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