Replace assert() -> BT_ASSERT() and some preconditions with BT_ASSERT_PRE()
[babeltrace.git] / logging / log.c
1 /*
2 * This is zf_log.c, modified with Babeltrace prefixes.
3 * See <https://github.com/wonder-mice/zf_log/>.
4 * See LICENSE.
5 */
6
7 #include <babeltrace/babeltrace-internal.h>
8 #include <babeltrace/common-internal.h>
9 #include <pthread.h>
10
11 #ifdef __CYGWIN__
12 extern unsigned long pthread_getsequence_np(pthread_t *);
13 #endif
14
15 /* When defined, Android log (android/log.h) will be used by default instead of
16 * stderr (ignored on non-Android platforms). Date, time, pid and tid (context)
17 * will be provided by Android log. Android log features will be used to output
18 * log level and tag.
19 */
20 #ifdef BT_LOG_USE_ANDROID_LOG
21 #undef BT_LOG_USE_ANDROID_LOG
22 #if defined(__ANDROID__)
23 #define BT_LOG_USE_ANDROID_LOG 1
24 #else
25 #define BT_LOG_USE_ANDROID_LOG 0
26 #endif
27 #else
28 #define BT_LOG_USE_ANDROID_LOG 0
29 #endif
30 /* When defined, NSLog (uses Apple System Log) will be used instead of stderr
31 * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be
32 * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on
33 * non-public CFLog() function. Both use Apple System Log internally, but it's
34 * easier to call CFLog() from C than NSLog(). Current implementation doesn't
35 * support "%@" format specifier.
36 */
37 #ifdef BT_LOG_USE_NSLOG
38 #undef BT_LOG_USE_NSLOG
39 #if defined(__APPLE__) && defined(__MACH__)
40 #define BT_LOG_USE_NSLOG 1
41 #else
42 #define BT_LOG_USE_NSLOG 0
43 #endif
44 #else
45 #define BT_LOG_USE_NSLOG 0
46 #endif
47 /* When defined, OutputDebugString() will be used instead of stderr (ignored on
48 * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with
49 * UTF-8 data.
50 */
51 #ifdef BT_LOG_USE_DEBUGSTRING
52 #undef BT_LOG_USE_DEBUGSTRING
53 #if defined(_WIN32) || defined(_WIN64)
54 #define BT_LOG_USE_DEBUGSTRING 1
55 #else
56 #define BT_LOG_USE_DEBUGSTRING 0
57 #endif
58 #else
59 #define BT_LOG_USE_DEBUGSTRING 0
60 #endif
61 /* When defined, bt_log library will not contain definition of tag prefix
62 * variable. In that case it must be defined elsewhere using
63 * BT_LOG_DEFINE_TAG_PREFIX macro, for example:
64 *
65 * BT_LOG_DEFINE_TAG_PREFIX = "ProcessName";
66 *
67 * This allows to specify custom value for static initialization and avoid
68 * overhead of setting this value in runtime.
69 */
70 #ifdef BT_LOG_EXTERN_TAG_PREFIX
71 #undef BT_LOG_EXTERN_TAG_PREFIX
72 #define BT_LOG_EXTERN_TAG_PREFIX 1
73 #else
74 #define BT_LOG_EXTERN_TAG_PREFIX 0
75 #endif
76 /* When defined, bt_log library will not contain definition of global format
77 * variable. In that case it must be defined elsewhere using
78 * BT_LOG_DEFINE_GLOBAL_FORMAT macro, for example:
79 *
80 * BT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH};
81 *
82 * This allows to specify custom value for static initialization and avoid
83 * overhead of setting this value in runtime.
84 */
85 #ifdef BT_LOG_EXTERN_GLOBAL_FORMAT
86 #undef BT_LOG_EXTERN_GLOBAL_FORMAT
87 #define BT_LOG_EXTERN_GLOBAL_FORMAT 1
88 #else
89 #define BT_LOG_EXTERN_GLOBAL_FORMAT 0
90 #endif
91 /* When defined, bt_log library will not contain definition of global output
92 * variable. In that case it must be defined elsewhere using
93 * BT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example:
94 *
95 * BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback};
96 *
97 * This allows to specify custom value for static initialization and avoid
98 * overhead of setting this value in runtime.
99 */
100 #ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT
101 #undef BT_LOG_EXTERN_GLOBAL_OUTPUT
102 #define BT_LOG_EXTERN_GLOBAL_OUTPUT 1
103 #else
104 #define BT_LOG_EXTERN_GLOBAL_OUTPUT 0
105 #endif
106 /* When defined, bt_log library will not contain definition of global output
107 * level variable. In that case it must be defined elsewhere using
108 * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example:
109 *
110 * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_WARN;
111 *
112 * This allows to specify custom value for static initialization and avoid
113 * overhead of setting this value in runtime.
114 */
115 #ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
116 #undef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
117 #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1
118 #else
119 #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0
120 #endif
121 /* When defined, implementation will prefer smaller code size over speed.
122 * Very rough estimate is that code will be up to 2x smaller and up to 2x
123 * slower. Disabled by default.
124 */
125 #ifdef BT_LOG_OPTIMIZE_SIZE
126 #undef BT_LOG_OPTIMIZE_SIZE
127 #define BT_LOG_OPTIMIZE_SIZE 1
128 #else
129 #define BT_LOG_OPTIMIZE_SIZE 0
130 #endif
131 /* Size of the log line buffer. The buffer is a globally allocated per thread.
132 */
133 #ifndef BT_LOG_BUF_SZ
134 #define BT_LOG_BUF_SZ (4 * 4096)
135 #endif
136 /* Default number of bytes in one line of memory output. For large values
137 * BT_LOG_BUF_SZ also must be increased.
138 */
139 #ifndef BT_LOG_MEM_WIDTH
140 #define BT_LOG_MEM_WIDTH 32
141 #endif
142 /* String to put in the end of each log line (can be empty). Its value used by
143 * stderr output callback. Its size used as a default value for BT_LOG_EOL_SZ.
144 */
145 #ifndef BT_LOG_EOL
146 #define BT_LOG_EOL "\n"
147 #endif
148 /* Default delimiter that separates parts of log message. Can NOT contain '%'
149 * or '\0'.
150 *
151 * Log message format specifications can override (or ignore) this value. For
152 * more details see BT_LOG_MESSAGE_CTX_FORMAT, BT_LOG_MESSAGE_SRC_FORMAT and
153 * BT_LOG_MESSAGE_TAG_FORMAT.
154 */
155 #ifndef BT_LOG_DEF_DELIMITER
156 #define BT_LOG_DEF_DELIMITER " "
157 #endif
158 /* Specifies log message context format. Log message context includes date,
159 * time, process id, thread id and message's log level. Custom information can
160 * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
161 * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements),
162 * F_UINT(width, value).
163 *
164 * Must be defined as a tuple, for example:
165 *
166 * #define BT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, S(" > "))
167 *
168 * In that case, resulting log message will be:
169 *
170 * 2016.12.22 > TAG function@filename.c:line Message text
171 *
172 * Note, that tag, source location and message text are not impacted by
173 * this setting. See BT_LOG_MESSAGE_TAG_FORMAT and BT_LOG_MESSAGE_SRC_FORMAT.
174 *
175 * If message context must be visually separated from the rest of the message,
176 * it must be reflected in context format (notice trailing S(" > ") in the
177 * example above).
178 *
179 * S(str) adds constant string str. String can NOT contain '%' or '\0'.
180 *
181 * F_INIT(statements) adds initialization statement(s) that will be evaluated
182 * once for each log message. All statements are evaluated in specified order.
183 * Several F_INIT() fields can be used in every log message format
184 * specification. Fields, like F_UINT(width, value), are allowed to use results
185 * of initialization statements. If statement introduces variables (or other
186 * names, like structures) they must be prefixed with "f_". Statements must be
187 * enclosed into additional "()". Example:
188 *
189 * #define BT_LOG_MESSAGE_CTX_FORMAT \
190 * (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \
191 * YEAR, S("."), MONTH, S("."), DAY, S(" "), \
192 * F_UINT(5, f_ru.ru_nsignals), \
193 * S(" "))
194 *
195 * F_UINT(width, value) adds unsigned integer value extended with up to width
196 * spaces (for alignment purposes). Value can be any expression that evaluates
197 * to unsigned integer. If expression contains non-standard functions, they
198 * must be declared with F_INIT(). Example:
199 *
200 * #define BT_LOG_MESSAGE_CTX_FORMAT \
201 * (YEAR, S("."), MONTH, S("."), DAY, S(" "), \
202 * F_INIT(( unsigned tickcount(); )), \
203 * F_UINT(5, tickcount()), \
204 * S(" "))
205 *
206 * Other log message format specifications follow same rules, but have a
207 * different set of supported fields.
208 */
209 #ifndef BT_LOG_MESSAGE_CTX_FORMAT
210 #define BT_LOG_MESSAGE_CTX_FORMAT \
211 (MONTH, S("-"), DAY, S(BT_LOG_DEF_DELIMITER), \
212 HOUR, S(":"), MINUTE, S(":"), SECOND, S("."), MILLISECOND, S(BT_LOG_DEF_DELIMITER), \
213 PID, S(BT_LOG_DEF_DELIMITER), TID, S(BT_LOG_DEF_DELIMITER), \
214 LEVEL, S(BT_LOG_DEF_DELIMITER))
215 #endif
216 /* Example:
217 */
218 /* Specifies log message tag format. It includes tag prefix and tag. Custom
219 * information can be added as well. Supported fields:
220 * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements),
221 * F_UINT(width, value).
222 *
223 * TAG(prefix_delimiter, tag_delimiter) adds following string to log message:
224 *
225 * PREFIX<prefix_delimiter>TAG<tag_delimiter>
226 *
227 * Prefix delimiter will be used only when prefix is not empty. Tag delimiter
228 * will be used only when prefixed tag is not empty. Example:
229 *
230 * #define BT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] "))
231 *
232 * See BT_LOG_MESSAGE_CTX_FORMAT for details.
233 */
234 #ifndef BT_LOG_MESSAGE_TAG_FORMAT
235 #define BT_LOG_MESSAGE_TAG_FORMAT \
236 (TAG(".", BT_LOG_DEF_DELIMITER))
237 #endif
238 /* Specifies log message source location format. It includes function name,
239 * file name and file line. Custom information can be added as well. Supported
240 * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements),
241 * F_UINT(width, value).
242 *
243 * See BT_LOG_MESSAGE_CTX_FORMAT for details.
244 */
245 #ifndef BT_LOG_MESSAGE_SRC_FORMAT
246 #define BT_LOG_MESSAGE_SRC_FORMAT \
247 (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(BT_LOG_DEF_DELIMITER))
248 #endif
249 /* Fields that can be used in log message format specifications (see above).
250 * Mentioning them here explicitly, so we know that nobody else defined them
251 * before us. See BT_LOG_MESSAGE_CTX_FORMAT for details.
252 */
253 #define YEAR YEAR
254 #define MONTH MONTH
255 #define DAY DAY
256 #define MINUTE MINUTE
257 #define SECOND SECOND
258 #define MILLISECOND MILLISECOND
259 #define PID PID
260 #define TID TID
261 #define LEVEL LEVEL
262 #define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim)
263 #define FUNCTION FUNCTION
264 #define FILENAME FILENAME
265 #define FILELINE FILELINE
266 #define S(str) S(str)
267 #define F_INIT(statements) F_INIT(statements)
268 #define F_UINT(width, value) F_UINT(width, value)
269 /* Number of bytes to reserve for EOL in the log line buffer (must be >0).
270 * Must be larger than or equal to length of BT_LOG_EOL with terminating null.
271 */
272 #ifndef BT_LOG_EOL_SZ
273 #define BT_LOG_EOL_SZ sizeof(BT_LOG_EOL)
274 #endif
275 /* Compile instrumented version of the library to facilitate unit testing.
276 */
277 #ifndef BT_LOG_INSTRUMENTED
278 #define BT_LOG_INSTRUMENTED 0
279 #endif
280
281 #if defined(__linux__)
282 #if !defined(__ANDROID__) && !defined(_GNU_SOURCE)
283 #define _GNU_SOURCE
284 #endif
285 #endif
286 #if defined(__MINGW32__)
287 #ifdef __STRICT_ANSI__
288 #undef __STRICT_ANSI__
289 #endif
290 #endif
291 #include <babeltrace/assert-internal.h>
292 #include <ctype.h>
293 #include <string.h>
294 #include <time.h>
295 #include <stdarg.h>
296 #include <stddef.h>
297 #include <stdlib.h>
298 #include <stdio.h>
299
300 #define BT_LOG_OUTPUT_LEVEL dummy
301
302 #include <babeltrace/logging-internal.h>
303 #include <babeltrace/logging.h>
304
305 #if defined(_WIN32) || defined(_WIN64)
306 #include <windows.h>
307 #else
308 #include <unistd.h>
309 #include <sys/time.h>
310 #if defined(__linux__)
311 #include <linux/limits.h>
312 #elif (defined(__sun__) || defined(__CYGWIN__))
313 /* Solaris and Cygwin have no sys/syslimits.h */
314 #else
315 #include <sys/syslimits.h>
316 #endif
317 #endif
318
319 #if defined(__linux__)
320 #include <sys/prctl.h>
321 #include <sys/types.h>
322 #if !defined(__ANDROID__)
323 #include <sys/syscall.h>
324 #endif
325 #endif
326 #if defined(__MACH__)
327 #include <pthread.h>
328 #endif
329
330 #define INLINE _BT_LOG_INLINE
331 #define VAR_UNUSED(var) (void)var
332 #define RETVAL_UNUSED(expr) do { while(expr) break; } while(0)
333 #define STATIC_ASSERT(name, cond) \
334 typedef char assert_##name[(cond)? 1: -1]
335 #define ASSERT_UNREACHABLE(why) assert(!sizeof(why))
336 #ifndef _countof
337 #define _countof(xs) (sizeof(xs) / sizeof((xs)[0]))
338 #endif
339
340 #if BT_LOG_INSTRUMENTED
341 #define INSTRUMENTED_CONST
342 #else
343 #define INSTRUMENTED_CONST const
344 #endif
345
346 #define _PP_PASTE_2(a, b) a ## b
347 #define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b)
348
349 #define _PP_PASTE_3(a, b, c) a ## b ## c
350 #define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c)
351
352 /* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__
353 * as a single token and requires additional expansion to realize that it's
354 * actually a list. If not for it, there would be no need in this extra
355 * expansion.
356 */
357 #define _PP_ID(x) x
358 #define _PP_NARGS_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,...) _24
359 #define _PP_NARGS(...) _PP_ID(_PP_NARGS_N(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
360
361 /* There is a more efficient way to implement this, but it requires
362 * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't
363 * have one.
364 */
365 #define _PP_HEAD__(x, ...) x
366 #define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~))
367 #define _PP_HEAD(xs) _PP_HEAD_ xs
368 #define _PP_TAIL_(x, ...) (__VA_ARGS__)
369 #define _PP_TAIL(xs) _PP_TAIL_ xs
370 #define _PP_UNTUPLE_(...) __VA_ARGS__
371 #define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs
372
373 /* Apply function macro to each element in tuple. Output is not
374 * enforced to be a tuple.
375 */
376 #define _PP_MAP_1(f, xs) f(_PP_HEAD(xs))
377 #define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs))
378 #define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs))
379 #define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs))
380 #define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs))
381 #define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs))
382 #define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs))
383 #define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs))
384 #define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs))
385 #define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs))
386 #define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs))
387 #define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs))
388 #define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs))
389 #define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs))
390 #define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs))
391 #define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs))
392 #define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs))
393 #define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs))
394 #define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs))
395 #define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs))
396 #define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs))
397 #define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs))
398 #define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs))
399 #define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs))
400 #define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs)
401
402 /* Apply function macro to each element in tuple in reverse order.
403 * Output is not enforced to be a tuple.
404 */
405 #define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs))
406 #define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
407 #define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
408 #define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
409 #define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
410 #define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
411 #define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
412 #define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
413 #define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
414 #define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
415 #define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
416 #define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
417 #define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
418 #define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
419 #define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
420 #define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
421 #define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
422 #define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
423 #define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
424 #define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
425 #define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
426 #define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
427 #define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
428 #define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
429 #define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs)
430
431 /* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible
432 * fields must be mentioned here. Not counting F_INIT() here because it's
433 * somewhat special and is handled spearatly (at least for now).
434 */
435 #define _BT_LOG_MESSAGE_FORMAT_MASK__ (0<<0)
436 #define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR (1<<1)
437 #define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH (1<<2)
438 #define _BT_LOG_MESSAGE_FORMAT_MASK__DAY (1<<3)
439 #define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR (1<<4)
440 #define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1<<5)
441 #define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND (1<<6)
442 #define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1<<7)
443 #define _BT_LOG_MESSAGE_FORMAT_MASK__PID (1<<8)
444 #define _BT_LOG_MESSAGE_FORMAT_MASK__TID (1<<9)
445 #define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1<<10)
446 #define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1<<11)
447 #define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1<<12)
448 #define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1<<13)
449 #define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1<<14)
450 #define _BT_LOG_MESSAGE_FORMAT_MASK__S(s) (1<<15)
451 #define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16)
452 #define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17)
453 #define _BT_LOG_MESSAGE_FORMAT_MASK(field) \
454 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field)
455
456 /* Logical "or" of masks of fields used in specified format specification.
457 */
458 #define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \
459 (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format))
460
461 /* Expands to expressions that evaluates to true if field is used in
462 * specified format specification. Example:
463 *
464 * #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT)
465 * ...
466 * #endif
467 */
468 #define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \
469 (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format))
470
471 /* Same, but checks all supported format specifications.
472 */
473 #define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \
474 (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \
475 _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \
476 _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT))
477
478 #define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \
479 (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \
480 _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \
481 _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \
482 _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \
483 _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \
484 _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \
485 _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT))
486
487 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
488 #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
489 #define memccpy _memccpy
490 #endif
491
492 #if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || \
493 (defined(__MINGW64__) && !defined(__USE_MINGW_ANSI_STDIO))
494 #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va)
495 static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap)
496 {
497 const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap);
498 return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */
499 }
500 #if BT_LOG_OPTIMIZE_SIZE
501 #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__)
502 static int fake_snprintf(char *s, size_t sz, const char *fmt, ...)
503 {
504 va_list va;
505 va_start(va, fmt);
506 const int n = fake_vsnprintf(s, sz, fmt, va);
507 va_end(va);
508 return n;
509 }
510 #endif
511 #endif
512
513 typedef void (*time_cb)(struct tm *const tm, unsigned *const usec);
514 typedef void (*pid_cb)(int *const pid, int *const tid);
515 typedef void (*buffer_cb)(bt_log_message *msg, char *buf);
516
517 typedef struct src_location
518 {
519 const char *const func;
520 const char *const file;
521 const unsigned line;
522 }
523 src_location;
524
525 typedef struct mem_block
526 {
527 const void *const d;
528 const unsigned d_sz;
529 }
530 mem_block;
531
532 static void time_callback(struct tm *const tm, unsigned *const usec);
533 static void pid_callback(int *const pid, int *const tid);
534 static void buffer_callback(bt_log_message *msg, char *buf);
535
536 STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ);
537 STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ);
538 STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ);
539 static const char c_hex[] = "0123456789abcdef";
540
541 static char __thread logging_buf[4 * 4096];
542
543 static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ;
544 static INSTRUMENTED_CONST time_cb g_time_cb = time_callback;
545 static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback;
546 static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback;
547
548 #if BT_LOG_USE_ANDROID_LOG
549 #include <android/log.h>
550
551 static INLINE int android_lvl(const int lvl)
552 {
553 switch (lvl)
554 {
555 case BT_LOG_VERBOSE:
556 return ANDROID_LOG_VERBOSE;
557 case BT_LOG_DEBUG:
558 return ANDROID_LOG_DEBUG;
559 case BT_LOG_INFO:
560 return ANDROID_LOG_INFO;
561 case BT_LOG_WARN:
562 return ANDROID_LOG_WARN;
563 case BT_LOG_ERROR:
564 return ANDROID_LOG_ERROR;
565 case BT_LOG_FATAL:
566 return ANDROID_LOG_FATAL;
567 default:
568 ASSERT_UNREACHABLE("Bad log level");
569 return ANDROID_LOG_UNKNOWN;
570 }
571 }
572
573 static void out_android_callback(const bt_log_message *const msg, void *arg)
574 {
575 VAR_UNUSED(arg);
576 *msg->p = 0;
577 const char *tag = msg->p;
578 if (msg->tag_e != msg->tag_b)
579 {
580 tag = msg->tag_b;
581 *msg->tag_e = 0;
582 }
583 __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b);
584 }
585
586 enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
587 #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback
588 #endif
589
590 #if BT_LOG_USE_NSLOG
591 #include <CoreFoundation/CoreFoundation.h>
592 CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...);
593
594 static INLINE int apple_lvl(const int lvl)
595 {
596 switch (lvl)
597 {
598 case BT_LOG_VERBOSE:
599 return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
600 case BT_LOG_DEBUG:
601 return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
602 case BT_LOG_INFO:
603 return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */;
604 case BT_LOG_WARN:
605 return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */;
606 case BT_LOG_ERROR:
607 return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */;
608 case BT_LOG_FATAL:
609 return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
610 default:
611 ASSERT_UNREACHABLE("Bad log level");
612 return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
613 }
614 }
615
616 static void out_nslog_callback(const bt_log_message *const msg, void *arg)
617 {
618 VAR_UNUSED(arg);
619 *msg->p = 0;
620 CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b);
621 }
622
623 enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
624 #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback
625 #endif
626
627 #if BT_LOG_USE_DEBUGSTRING
628 #include <windows.h>
629
630 static void out_debugstring_callback(const bt_log_message *const msg, void *arg)
631 {
632 VAR_UNUSED(arg);
633 msg->p[0] = '\n';
634 msg->p[1] = '\0';
635 OutputDebugStringA(msg->buf);
636 }
637
638 enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD };
639 #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback
640 #endif
641
642 BT_HIDDEN
643 void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg)
644 {
645 VAR_UNUSED(arg);
646 const size_t eol_len = sizeof(BT_LOG_EOL) - 1;
647 memcpy(msg->p, BT_LOG_EOL, eol_len);
648 #if defined(_WIN32) || defined(_WIN64)
649 /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and
650 without FILE_WRITE_DATA */
651 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf,
652 (DWORD)(msg->p - msg->buf + eol_len), 0, 0);
653 #else
654 /* write() is atomic for buffers less than or equal to PIPE_BUF. */
655 RETVAL_UNUSED(write(STDERR_FILENO, msg->buf,
656 (size_t)(msg->p - msg->buf) + eol_len));
657 #endif
658 }
659
660 static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR};
661
662 #if !BT_LOG_EXTERN_TAG_PREFIX
663 BT_LOG_DEFINE_TAG_PREFIX = 0;
664 #endif
665
666 #if !BT_LOG_EXTERN_GLOBAL_FORMAT
667 BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH};
668 #endif
669
670 #if !BT_LOG_EXTERN_GLOBAL_OUTPUT
671 #if BT_LOG_USE_ANDROID_LOG
672 BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID};
673 #elif BT_LOG_USE_NSLOG
674 BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG};
675 #elif BT_LOG_USE_DEBUGSTRING
676 BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING};
677 #else
678 BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR};
679 #endif
680 #endif
681
682 #if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
683 BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0;
684 #endif
685
686 BT_HIDDEN
687 const bt_log_spec _bt_log_stderr_spec =
688 {
689 BT_LOG_GLOBAL_FORMAT,
690 &out_stderr,
691 };
692
693 static const bt_log_spec global_spec =
694 {
695 BT_LOG_GLOBAL_FORMAT,
696 BT_LOG_GLOBAL_OUTPUT,
697 };
698
699 #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT)
700 static char lvl_char(const int lvl)
701 {
702 switch (lvl)
703 {
704 case BT_LOG_VERBOSE:
705 return 'V';
706 case BT_LOG_DEBUG:
707 return 'D';
708 case BT_LOG_INFO:
709 return 'I';
710 case BT_LOG_WARN:
711 return 'W';
712 case BT_LOG_ERROR:
713 return 'E';
714 case BT_LOG_FATAL:
715 return 'F';
716 default:
717 ASSERT_UNREACHABLE("Bad log level");
718 return '?';
719 }
720 }
721 #endif
722
723 #define GCCVER_LESS(MAJOR, MINOR, PATCH) \
724 (__GNUC__ < MAJOR || \
725 (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \
726 (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH))))
727
728 #if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0)
729 #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0)
730 #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n)
731 #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n)
732 #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n)
733 #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n)
734 /* Note: will not store old value of *vp in *ep (non-standard behaviour) */
735 #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \
736 __sync_bool_compare_and_swap(vp, *(ep), d)
737 #endif
738
739 #if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64)
740 #define TCACHE
741 #define TCACHE_STALE (0x40000000)
742 #define TCACHE_FLUID (0x40000000 | 0x80000000)
743 static unsigned g_tcache_mode = TCACHE_STALE;
744 static struct timeval g_tcache_tv = {0, 0};
745 static struct tm g_tcache_tm = {0};
746
747 static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm)
748 {
749 unsigned mode;
750 mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED);
751 if (0 == (mode & TCACHE_FLUID))
752 {
753 mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE);
754 if (0 == (mode & TCACHE_FLUID))
755 {
756 if (g_tcache_tv.tv_sec == tv->tv_sec)
757 {
758 *tm = g_tcache_tm;
759 __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
760 return !0;
761 }
762 __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED);
763 }
764 __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
765 }
766 return 0;
767 }
768
769 static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm)
770 {
771 unsigned stale = TCACHE_STALE;
772 if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID,
773 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
774 {
775 g_tcache_tv = *tv;
776 g_tcache_tm = *tm;
777 __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE);
778 }
779 }
780 #endif
781
782 static void time_callback(struct tm *const tm, unsigned *const msec)
783 {
784 #if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED
785 VAR_UNUSED(tm);
786 VAR_UNUSED(msec);
787 #else
788 #if defined(_WIN32) || defined(_WIN64)
789 SYSTEMTIME st;
790 GetLocalTime(&st);
791 tm->tm_year = st.wYear;
792 tm->tm_mon = st.wMonth;
793 tm->tm_mday = st.wDay;
794 tm->tm_wday = st.wDayOfWeek;
795 tm->tm_hour = st.wHour;
796 tm->tm_min = st.wMinute;
797 tm->tm_sec = st.wSecond;
798 *msec = st.wMilliseconds;
799 #else
800 struct timeval tv;
801 gettimeofday(&tv, 0);
802 #ifndef TCACHE
803 localtime_r(&tv.tv_sec, tm);
804 #else
805 if (!tcache_get(&tv, tm))
806 {
807 localtime_r(&tv.tv_sec, tm);
808 tcache_set(&tv, tm);
809 }
810 #endif
811 *msec = (unsigned)tv.tv_usec / 1000;
812 #endif
813 #endif
814 }
815
816 static void pid_callback(int *const pid, int *const tid)
817 {
818 #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT)
819 VAR_UNUSED(pid);
820 #else
821 #if defined(_WIN32) || defined(_WIN64)
822 *pid = GetCurrentProcessId();
823 #else
824 *pid = getpid();
825 #endif
826 #endif
827
828 #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
829 VAR_UNUSED(tid);
830 #else
831 #if defined(_WIN32) || defined(_WIN64)
832 *tid = GetCurrentThreadId();
833 #elif defined(__CYGWIN__)
834 pthread_t thr = pthread_self();
835 *tid = (int)pthread_getsequence_np(&thr);
836 #elif defined(__sun__)
837 *tid = (int)pthread_self();
838 #elif defined(__ANDROID__)
839 *tid = gettid();
840 #elif defined(__linux__)
841 *tid = syscall(SYS_gettid);
842 #elif defined(__MACH__)
843 *tid = (int)pthread_mach_thread_np(pthread_self());
844 #else
845 #define Platform not supported
846 #endif
847 #endif
848 }
849
850 static void buffer_callback(bt_log_message *msg, char *buf)
851 {
852 msg->e = (msg->p = msg->buf = buf) + g_buf_sz;
853 }
854
855 #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT)
856 static const char *funcname(const char *func)
857 {
858 return func? func: "";
859 }
860 #endif
861
862 #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT)
863 static const char *filename(const char *file)
864 {
865 const char *f = file;
866 for (const char *p = file; 0 != *p; ++p)
867 {
868 if ('/' == *p || '\\' == *p)
869 {
870 f = p + 1;
871 }
872 }
873 return f;
874 }
875 #endif
876
877 static INLINE size_t nprintf_size(bt_log_message *const msg)
878 {
879 // *nprintf() always puts 0 in the end when input buffer is not empty. This
880 // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which
881 // leaves space for one more character. Some put_xxx() functions don't use
882 // *nprintf() and could use that last character. In that case log line will
883 // have multiple (two) half-written parts which is confusing. To workaround
884 // that we allow *nprintf() to write its 0 in the eol area (which is always
885 // not empty).
886 return (size_t)(msg->e - msg->p + 1);
887 }
888
889 static INLINE void put_nprintf(bt_log_message *const msg, const int n)
890 {
891 if (0 < n)
892 {
893 msg->p = n < msg->e - msg->p? msg->p + n: msg->e;
894 }
895 }
896
897 static INLINE char *put_padding_r(const unsigned w, const char wc,
898 char *p, char *e)
899 {
900 for (char *const b = e - w; b < p; *--p = wc) {}
901 return p;
902 }
903
904 static char *put_integer_r(unsigned v, const int sign,
905 const unsigned w, const char wc, char *const e)
906 {
907 static const char _signs[] = {'-', '0', '+'};
908 const char *const signs = _signs + 1;
909 char *p = e;
910 do { *--p = '0' + v % 10; } while (0 != (v /= 10));
911 if (0 == sign) return put_padding_r(w, wc, p, e);
912 if ('0' != wc)
913 {
914 *--p = signs[sign];
915 return put_padding_r(w, wc, p, e);
916 }
917 p = put_padding_r(w, wc, p, e + 1);
918 *--p = signs[sign];
919 return p;
920 }
921
922 static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc,
923 char *const e)
924 {
925 return put_integer_r(v, 0, w, wc, e);
926 }
927
928 static INLINE char *put_int_r(const int v, const unsigned w, const char wc,
929 char *const e)
930 {
931 return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e)
932 : put_integer_r((unsigned)-v, -1, w, wc, e);
933 }
934
935 static INLINE char *put_stringn(const char *const s_p, const char *const s_e,
936 char *const p, char *const e)
937 {
938 const ptrdiff_t m = e - p;
939 ptrdiff_t n = s_e - s_p;
940 if (n > m)
941 {
942 n = m;
943 }
944 memcpy(p, s_p, n);
945 return p + n;
946 }
947
948 static INLINE char *put_string(const char *s, char *p, char *const e)
949 {
950 const ptrdiff_t n = e - p;
951 char *const c = (char *)memccpy(p, s, '\0', n);
952 return 0 != c? c - 1: e;
953 }
954
955 static INLINE char *put_uint(unsigned v, const unsigned w, const char wc,
956 char *const p, char *const e)
957 {
958 char buf[16];
959 char *const se = buf + _countof(buf);
960 char *sp = put_uint_r(v, w, wc, se);
961 return put_stringn(sp, se, p, e);
962 }
963
964 #define PUT_CSTR_R(p, STR) \
965 do { \
966 for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \
967 *--(p) = (STR)[i]; \
968 } \
969 } _BT_LOG_ONCE
970
971 #define PUT_CSTR_CHECKED(p, e, STR) \
972 do { \
973 for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \
974 *(p)++ = (STR)[i]; \
975 } \
976 } _BT_LOG_ONCE
977
978 /* F_INIT field support.
979 */
980 #define _BT_LOG_MESSAGE_FORMAT_INIT__
981 #define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR
982 #define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH
983 #define _BT_LOG_MESSAGE_FORMAT_INIT__DAY
984 #define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR
985 #define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE
986 #define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND
987 #define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND
988 #define _BT_LOG_MESSAGE_FORMAT_INIT__PID
989 #define _BT_LOG_MESSAGE_FORMAT_INIT__TID
990 #define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL
991 #define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts)
992 #define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION
993 #define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME
994 #define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE
995 #define _BT_LOG_MESSAGE_FORMAT_INIT__S(s)
996 #define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr);
997 #define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v)
998 #define _BT_LOG_MESSAGE_FORMAT_INIT(field) \
999 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field)
1000
1001 /* Implements generation of printf-like format string for log message
1002 * format specification.
1003 */
1004 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ ""
1005 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u"
1006 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u"
1007 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u"
1008 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u"
1009 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u"
1010 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u"
1011 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u"
1012 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i"
1013 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i"
1014 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c"
1015 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED
1016 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s"
1017 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s"
1018 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u"
1019 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s
1020 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) ""
1021 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u"
1022 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \
1023 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field)
1024
1025 /* Implements generation of printf-like format parameters for log message
1026 * format specification.
1027 */
1028 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__
1029 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR ,(unsigned)(tm.tm_year + 1900)
1030 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH ,(unsigned)(tm.tm_mon + 1)
1031 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY ,(unsigned)tm.tm_mday
1032 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR ,(unsigned)tm.tm_hour
1033 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE ,(unsigned)tm.tm_min
1034 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND ,(unsigned)tm.tm_sec
1035 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND ,(unsigned)msec
1036 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID ,pid
1037 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID ,tid
1038 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL ,(char)lvl_char(msg->lvl)
1039 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED
1040 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION ,funcname(src->func)
1041 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME ,filename(src->file)
1042 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE ,src->line
1043 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s)
1044 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr)
1045 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v
1046 #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \
1047 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field)
1048
1049 /* Implements generation of put_xxx_t statements for log message specification.
1050 */
1051 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__
1052 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR p = put_uint_r(tm.tm_year + 1900, 4, '0', p);
1053 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p);
1054 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p);
1055 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p);
1056 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE p = put_uint_r((unsigned)tm.tm_min, 2, '0', p);
1057 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p);
1058 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND p = put_uint_r(msec, 3, '0', p);
1059 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p);
1060 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p);
1061 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl);
1062 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED
1063 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED
1064 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED
1065 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED
1066 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s);
1067 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr)
1068 #define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p);
1069 #define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \
1070 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field)
1071
1072 static void put_ctx(bt_log_message *const msg)
1073 {
1074 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT)
1075 #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT)
1076 VAR_UNUSED(msg);
1077 #else
1078 #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED
1079 struct tm tm;
1080 unsigned msec;
1081 g_time_cb(&tm, &msec);
1082 #endif
1083 #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \
1084 _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
1085 int pid, tid;
1086 g_pid_cb(&pid, &tid);
1087 #endif
1088
1089 #if BT_LOG_OPTIMIZE_SIZE
1090 int n;
1091 n = snprintf(msg->p, nprintf_size(msg),
1092 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT)
1093 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT));
1094 put_nprintf(msg, n);
1095 #else
1096 char buf[64];
1097 char *const e = buf + sizeof(buf);
1098 char *p = e;
1099 _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT)
1100 msg->p = put_stringn(p, e, msg->p, msg->e);
1101 #endif
1102 #endif
1103 }
1104
1105 #define PUT_TAG(msg, tag, prefix_delim, tag_delim) \
1106 do { \
1107 const char *ch; \
1108 msg->tag_b = msg->p; \
1109 if (0 != (ch = _bt_log_tag_prefix)) { \
1110 for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
1111 } \
1112 if (0 != (ch = tag) && 0 != tag[0]) { \
1113 if (msg->tag_b != msg->p) { \
1114 PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \
1115 } \
1116 for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
1117 } \
1118 msg->tag_e = msg->p; \
1119 if (msg->tag_b != msg->p) { \
1120 PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \
1121 } \
1122 } _BT_LOG_ONCE
1123
1124 /* Implements simple put statements for log message specification.
1125 */
1126 #define _BT_LOG_MESSAGE_FORMAT_PUT__
1127 #define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED
1128 #define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED
1129 #define _BT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED
1130 #define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED
1131 #define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED
1132 #define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED
1133 #define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED
1134 #define _BT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED
1135 #define _BT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED
1136 #define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED
1137 #define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) PUT_TAG(msg, tag, pd, td);
1138 #define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION msg->p = put_string(funcname(src->func), msg->p, msg->e);
1139 #define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME msg->p = put_string(filename(src->file), msg->p, msg->e);
1140 #define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e);
1141 #define _BT_LOG_MESSAGE_FORMAT_PUT__S(s) PUT_CSTR_CHECKED(msg->p, msg->e, s);
1142 #define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr)
1143 #define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e);
1144 #define _BT_LOG_MESSAGE_FORMAT_PUT(field) \
1145 _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field)
1146
1147 static void put_tag(bt_log_message *const msg, const char *const tag)
1148 {
1149 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT)
1150 #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT)
1151 VAR_UNUSED(tag);
1152 #endif
1153 #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT)
1154 VAR_UNUSED(msg);
1155 #else
1156 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT)
1157 #endif
1158 }
1159
1160 static void put_src(bt_log_message *const msg, const src_location *const src)
1161 {
1162 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT)
1163 #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \
1164 !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \
1165 !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT)
1166 VAR_UNUSED(src);
1167 #endif
1168 #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT)
1169 VAR_UNUSED(msg);
1170 #else
1171 #if BT_LOG_OPTIMIZE_SIZE
1172 int n;
1173 n = snprintf(msg->p, nprintf_size(msg),
1174 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT)
1175 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT));
1176 put_nprintf(msg, n);
1177 #else
1178 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT)
1179 #endif
1180 #endif
1181 }
1182
1183 static void put_msg(bt_log_message *const msg,
1184 const char *const fmt, va_list va)
1185 {
1186 int n;
1187 msg->msg_b = msg->p;
1188 n = vsnprintf(msg->p, nprintf_size(msg), fmt, va);
1189 put_nprintf(msg, n);
1190 }
1191
1192 static void output_mem(const bt_log_spec *log, bt_log_message *const msg,
1193 const mem_block *const mem)
1194 {
1195 if (0 == mem->d || 0 == mem->d_sz)
1196 {
1197 return;
1198 }
1199 const unsigned char *mem_p = (const unsigned char *)mem->d;
1200 const unsigned char *const mem_e = mem_p + mem->d_sz;
1201 const unsigned char *mem_cut;
1202 const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width;
1203 char *const hex_b = msg->msg_b;
1204 char *const ascii_b = hex_b + 2 * mem_width + 2;
1205 char *const ascii_e = ascii_b + mem_width;
1206 if (msg->e < ascii_e)
1207 {
1208 return;
1209 }
1210 while (mem_p != mem_e)
1211 {
1212 char *hex = hex_b;
1213 char *ascii = ascii_b;
1214 for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e;
1215 mem_cut != mem_p; ++mem_p)
1216 {
1217 const unsigned char ch = *mem_p;
1218 *hex++ = c_hex[(0xf0 & ch) >> 4];
1219 *hex++ = c_hex[(0x0f & ch)];
1220 *ascii++ = isprint(ch)? (char)ch: '?';
1221 }
1222 while (hex != ascii_b)
1223 {
1224 *hex++ = ' ';
1225 }
1226 msg->p = ascii;
1227 log->output->callback(msg, log->output->arg);
1228 }
1229 }
1230
1231 BT_HIDDEN
1232 void bt_log_set_tag_prefix(const char *const prefix)
1233 {
1234 _bt_log_tag_prefix = prefix;
1235 }
1236
1237 BT_HIDDEN
1238 void bt_log_set_mem_width(const unsigned w)
1239 {
1240 _bt_log_global_format.mem_width = w;
1241 }
1242
1243 BT_HIDDEN
1244 void bt_log_set_output_level(const int lvl)
1245 {
1246 _bt_log_global_output_lvl = lvl;
1247 }
1248
1249 BT_HIDDEN
1250 void bt_log_set_output_v(const unsigned mask, void *const arg,
1251 const bt_log_output_cb callback)
1252 {
1253 _bt_log_global_output.mask = mask;
1254 _bt_log_global_output.arg = arg;
1255 _bt_log_global_output.callback = callback;
1256 }
1257
1258 static void _bt_log_write_imp(
1259 const bt_log_spec *log,
1260 const src_location *const src, const mem_block *const mem,
1261 const int lvl, const char *const tag, const char *const fmt, va_list va)
1262 {
1263 bt_log_message msg;
1264 char *buf = logging_buf;
1265 const unsigned mask = log->output->mask;
1266 msg.lvl = lvl;
1267 msg.tag = tag;
1268 g_buffer_cb(&msg, buf);
1269 const char *rst_color_p = bt_common_color_reset();
1270 const char *rst_color_e = rst_color_p + strlen(rst_color_p);
1271 const char *color_p = "";
1272 const char *color_e = color_p;
1273
1274 switch (lvl) {
1275 case BT_LOG_INFO:
1276 color_p = bt_common_color_fg_blue();
1277 color_e = color_p + strlen(color_p);
1278 break;
1279 case BT_LOG_WARN:
1280 color_p = bt_common_color_fg_yellow();
1281 color_e = color_p + strlen(color_p);
1282 break;
1283 case BT_LOG_ERROR:
1284 case BT_LOG_FATAL:
1285 color_p = bt_common_color_fg_red();
1286 color_e = color_p + strlen(color_p);
1287 break;
1288 default:
1289 break;
1290 }
1291
1292 msg.p = put_stringn(color_p, color_e, msg.p, msg.e);
1293
1294 if (BT_LOG_PUT_CTX & mask)
1295 {
1296 put_ctx(&msg);
1297 }
1298 if (BT_LOG_PUT_TAG & mask)
1299 {
1300 put_tag(&msg, tag);
1301 }
1302 if (0 != src && BT_LOG_PUT_SRC & mask)
1303 {
1304 put_src(&msg, src);
1305 }
1306 if (BT_LOG_PUT_MSG & mask)
1307 {
1308 put_msg(&msg, fmt, va);
1309 }
1310 msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e);
1311 log->output->callback(&msg, log->output->arg);
1312 if (0 != mem && BT_LOG_PUT_MSG & mask)
1313 {
1314 output_mem(log, &msg, mem);
1315 }
1316 }
1317
1318 BT_HIDDEN
1319 void _bt_log_write_d(
1320 const char *const func, const char *const file, const unsigned line,
1321 const int lvl, const char *const tag,
1322 const char *const fmt, ...)
1323 {
1324 const src_location src = {func, file, line};
1325 va_list va;
1326 va_start(va, fmt);
1327 _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va);
1328 va_end(va);
1329 }
1330
1331 BT_HIDDEN
1332 void _bt_log_write_aux_d(
1333 const char *const func, const char *const file, const unsigned line,
1334 const bt_log_spec *const log, const int lvl, const char *const tag,
1335 const char *const fmt, ...)
1336 {
1337 const src_location src = {func, file, line};
1338 va_list va;
1339 va_start(va, fmt);
1340 _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va);
1341 va_end(va);
1342 }
1343
1344 BT_HIDDEN
1345 void _bt_log_write(const int lvl, const char *const tag,
1346 const char *const fmt, ...)
1347 {
1348 va_list va;
1349 va_start(va, fmt);
1350 _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va);
1351 va_end(va);
1352 }
1353
1354 BT_HIDDEN
1355 void _bt_log_write_aux(
1356 const bt_log_spec *const log, const int lvl, const char *const tag,
1357 const char *const fmt, ...)
1358 {
1359 va_list va;
1360 va_start(va, fmt);
1361 _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va);
1362 va_end(va);
1363 }
1364
1365 BT_HIDDEN
1366 void _bt_log_write_mem_d(
1367 const char *const func, const char *const file, const unsigned line,
1368 const int lvl, const char *const tag,
1369 const void *const d, const unsigned d_sz,
1370 const char *const fmt, ...)
1371 {
1372 const src_location src = {func, file, line};
1373 const mem_block mem = {d, d_sz};
1374 va_list va;
1375 va_start(va, fmt);
1376 _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va);
1377 va_end(va);
1378 }
1379
1380 BT_HIDDEN
1381 void _bt_log_write_mem_aux_d(
1382 const char *const func, const char *const file, const unsigned line,
1383 const bt_log_spec *const log, const int lvl, const char *const tag,
1384 const void *const d, const unsigned d_sz,
1385 const char *const fmt, ...)
1386 {
1387 const src_location src = {func, file, line};
1388 const mem_block mem = {d, d_sz};
1389 va_list va;
1390 va_start(va, fmt);
1391 _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va);
1392 va_end(va);
1393 }
1394
1395 BT_HIDDEN
1396 void _bt_log_write_mem(const int lvl, const char *const tag,
1397 const void *const d, const unsigned d_sz,
1398 const char *const fmt, ...)
1399 {
1400 const mem_block mem = {d, d_sz};
1401 va_list va;
1402 va_start(va, fmt);
1403 _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va);
1404 va_end(va);
1405 }
1406
1407 BT_HIDDEN
1408 void _bt_log_write_mem_aux(
1409 const bt_log_spec *const log, const int lvl, const char *const tag,
1410 const void *const d, const unsigned d_sz,
1411 const char *const fmt, ...)
1412 {
1413 const mem_block mem = {d, d_sz};
1414 va_list va;
1415 va_start(va, fmt);
1416 _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va);
1417 va_end(va);
1418 }
This page took 0.057615 seconds and 4 git commands to generate.