Commit | Line | Data |
---|---|---|
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__ |
16 | extern 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> | |
94ddf5ca MJ |
316 | #elif (defined(__sun__) || defined(__CYGWIN__)) |
317 | /* Solaris and Cygwin 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 | |
333 | ||
334 | #define INLINE _BT_LOG_INLINE | |
335 | #define VAR_UNUSED(var) (void)var | |
336 | #define RETVAL_UNUSED(expr) do { while(expr) break; } while(0) | |
337 | #define STATIC_ASSERT(name, cond) \ | |
338 | typedef char assert_##name[(cond)? 1: -1] | |
339 | #define ASSERT_UNREACHABLE(why) assert(!sizeof(why)) | |
340 | #ifndef _countof | |
341 | #define _countof(xs) (sizeof(xs) / sizeof((xs)[0])) | |
342 | #endif | |
343 | ||
344 | #if BT_LOG_INSTRUMENTED | |
345 | #define INSTRUMENTED_CONST | |
346 | #else | |
347 | #define INSTRUMENTED_CONST const | |
348 | #endif | |
349 | ||
350 | #define _PP_PASTE_2(a, b) a ## b | |
351 | #define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b) | |
352 | ||
353 | #define _PP_PASTE_3(a, b, c) a ## b ## c | |
354 | #define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c) | |
355 | ||
356 | /* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__ | |
357 | * as a single token and requires additional expansion to realize that it's | |
358 | * actually a list. If not for it, there would be no need in this extra | |
359 | * expansion. | |
360 | */ | |
361 | #define _PP_ID(x) x | |
362 | #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 | |
363 | #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)) | |
364 | ||
365 | /* There is a more efficient way to implement this, but it requires | |
366 | * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't | |
367 | * have one. | |
368 | */ | |
369 | #define _PP_HEAD__(x, ...) x | |
370 | #define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~)) | |
371 | #define _PP_HEAD(xs) _PP_HEAD_ xs | |
372 | #define _PP_TAIL_(x, ...) (__VA_ARGS__) | |
373 | #define _PP_TAIL(xs) _PP_TAIL_ xs | |
374 | #define _PP_UNTUPLE_(...) __VA_ARGS__ | |
375 | #define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs | |
376 | ||
377 | /* Apply function macro to each element in tuple. Output is not | |
378 | * enforced to be a tuple. | |
379 | */ | |
380 | #define _PP_MAP_1(f, xs) f(_PP_HEAD(xs)) | |
381 | #define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs)) | |
382 | #define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs)) | |
383 | #define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs)) | |
384 | #define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs)) | |
385 | #define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs)) | |
386 | #define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs)) | |
387 | #define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs)) | |
388 | #define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs)) | |
389 | #define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs)) | |
390 | #define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs)) | |
391 | #define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs)) | |
392 | #define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs)) | |
393 | #define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs)) | |
394 | #define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs)) | |
395 | #define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs)) | |
396 | #define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs)) | |
397 | #define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs)) | |
398 | #define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs)) | |
399 | #define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs)) | |
400 | #define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs)) | |
401 | #define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs)) | |
402 | #define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs)) | |
403 | #define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs)) | |
404 | #define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs) | |
405 | ||
406 | /* Apply function macro to each element in tuple in reverse order. | |
407 | * Output is not enforced to be a tuple. | |
408 | */ | |
409 | #define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs)) | |
410 | #define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
411 | #define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
412 | #define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
413 | #define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
414 | #define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
415 | #define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
416 | #define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
417 | #define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
418 | #define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
419 | #define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
420 | #define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
421 | #define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
422 | #define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
423 | #define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
424 | #define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
425 | #define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
426 | #define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
427 | #define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
428 | #define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
429 | #define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
430 | #define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
431 | #define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
432 | #define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs)) | |
433 | #define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs) | |
434 | ||
435 | /* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible | |
436 | * fields must be mentioned here. Not counting F_INIT() here because it's | |
437 | * somewhat special and is handled spearatly (at least for now). | |
438 | */ | |
439 | #define _BT_LOG_MESSAGE_FORMAT_MASK__ (0<<0) | |
440 | #define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR (1<<1) | |
441 | #define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH (1<<2) | |
442 | #define _BT_LOG_MESSAGE_FORMAT_MASK__DAY (1<<3) | |
443 | #define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR (1<<4) | |
444 | #define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE (1<<5) | |
445 | #define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND (1<<6) | |
446 | #define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND (1<<7) | |
447 | #define _BT_LOG_MESSAGE_FORMAT_MASK__PID (1<<8) | |
448 | #define _BT_LOG_MESSAGE_FORMAT_MASK__TID (1<<9) | |
449 | #define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL (1<<10) | |
450 | #define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts) (1<<11) | |
451 | #define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION (1<<12) | |
452 | #define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME (1<<13) | |
453 | #define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE (1<<14) | |
454 | #define _BT_LOG_MESSAGE_FORMAT_MASK__S(s) (1<<15) | |
455 | #define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16) | |
456 | #define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17) | |
457 | #define _BT_LOG_MESSAGE_FORMAT_MASK(field) \ | |
458 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field) | |
459 | ||
460 | /* Logical "or" of masks of fields used in specified format specification. | |
461 | */ | |
462 | #define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \ | |
463 | (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format)) | |
464 | ||
465 | /* Expands to expressions that evaluates to true if field is used in | |
466 | * specified format specification. Example: | |
467 | * | |
468 | * #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT) | |
469 | * ... | |
470 | * #endif | |
471 | */ | |
472 | #define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \ | |
473 | (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format)) | |
474 | ||
475 | /* Same, but checks all supported format specifications. | |
476 | */ | |
477 | #define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \ | |
478 | (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
479 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \ | |
480 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT)) | |
481 | ||
482 | #define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \ | |
483 | (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
484 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
485 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
486 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
487 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
488 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
489 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT)) | |
490 | ||
491 | #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) | |
492 | #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ | |
493 | #define memccpy _memccpy | |
494 | #endif | |
495 | ||
994e8fa3 MJ |
496 | #if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || \ |
497 | (defined(__MINGW64__) && !defined(__USE_MINGW_ANSI_STDIO)) | |
beb0fb75 PP |
498 | #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va) |
499 | static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap) | |
500 | { | |
501 | const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap); | |
502 | return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */ | |
503 | } | |
504 | #if BT_LOG_OPTIMIZE_SIZE | |
505 | #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__) | |
506 | static int fake_snprintf(char *s, size_t sz, const char *fmt, ...) | |
507 | { | |
508 | va_list va; | |
509 | va_start(va, fmt); | |
510 | const int n = fake_vsnprintf(s, sz, fmt, va); | |
511 | va_end(va); | |
512 | return n; | |
513 | } | |
514 | #endif | |
515 | #endif | |
516 | ||
517 | typedef void (*time_cb)(struct tm *const tm, unsigned *const usec); | |
518 | typedef void (*pid_cb)(int *const pid, int *const tid); | |
519 | typedef void (*buffer_cb)(bt_log_message *msg, char *buf); | |
520 | ||
521 | typedef struct src_location | |
522 | { | |
523 | const char *const func; | |
524 | const char *const file; | |
525 | const unsigned line; | |
526 | } | |
527 | src_location; | |
528 | ||
529 | typedef struct mem_block | |
530 | { | |
531 | const void *const d; | |
532 | const unsigned d_sz; | |
533 | } | |
534 | mem_block; | |
535 | ||
536 | static void time_callback(struct tm *const tm, unsigned *const usec); | |
537 | static void pid_callback(int *const pid, int *const tid); | |
538 | static void buffer_callback(bt_log_message *msg, char *buf); | |
539 | ||
540 | STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ); | |
541 | STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ); | |
542 | STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ); | |
beb0fb75 PP |
543 | static const char c_hex[] = "0123456789abcdef"; |
544 | ||
c9fe7f23 | 545 | static __thread char logging_buf[4 * 4096]; |
deaa6f85 | 546 | |
beb0fb75 PP |
547 | static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ; |
548 | static INSTRUMENTED_CONST time_cb g_time_cb = time_callback; | |
549 | static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback; | |
550 | static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback; | |
551 | ||
552 | #if BT_LOG_USE_ANDROID_LOG | |
553 | #include <android/log.h> | |
554 | ||
555 | static INLINE int android_lvl(const int lvl) | |
556 | { | |
557 | switch (lvl) | |
558 | { | |
ef267d12 | 559 | case BT_LOG_TRACE: |
beb0fb75 PP |
560 | return ANDROID_LOG_VERBOSE; |
561 | case BT_LOG_DEBUG: | |
562 | return ANDROID_LOG_DEBUG; | |
563 | case BT_LOG_INFO: | |
564 | return ANDROID_LOG_INFO; | |
770538dd | 565 | case BT_LOG_WARNING: |
beb0fb75 PP |
566 | return ANDROID_LOG_WARN; |
567 | case BT_LOG_ERROR: | |
568 | return ANDROID_LOG_ERROR; | |
569 | case BT_LOG_FATAL: | |
570 | return ANDROID_LOG_FATAL; | |
571 | default: | |
572 | ASSERT_UNREACHABLE("Bad log level"); | |
573 | return ANDROID_LOG_UNKNOWN; | |
574 | } | |
575 | } | |
576 | ||
577 | static void out_android_callback(const bt_log_message *const msg, void *arg) | |
578 | { | |
579 | VAR_UNUSED(arg); | |
580 | *msg->p = 0; | |
581 | const char *tag = msg->p; | |
582 | if (msg->tag_e != msg->tag_b) | |
583 | { | |
584 | tag = msg->tag_b; | |
585 | *msg->tag_e = 0; | |
586 | } | |
587 | __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b); | |
588 | } | |
589 | ||
590 | enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; | |
591 | #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback | |
592 | #endif | |
593 | ||
594 | #if BT_LOG_USE_NSLOG | |
595 | #include <CoreFoundation/CoreFoundation.h> | |
596 | CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...); | |
597 | ||
598 | static INLINE int apple_lvl(const int lvl) | |
599 | { | |
600 | switch (lvl) | |
601 | { | |
ef267d12 | 602 | case BT_LOG_TRACE: |
beb0fb75 PP |
603 | return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; |
604 | case BT_LOG_DEBUG: | |
605 | return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */; | |
606 | case BT_LOG_INFO: | |
607 | return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */; | |
770538dd | 608 | case BT_LOG_WARNING: |
beb0fb75 PP |
609 | return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */; |
610 | case BT_LOG_ERROR: | |
611 | return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */; | |
612 | case BT_LOG_FATAL: | |
613 | return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; | |
614 | default: | |
615 | ASSERT_UNREACHABLE("Bad log level"); | |
616 | return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */; | |
617 | } | |
618 | } | |
619 | ||
620 | static void out_nslog_callback(const bt_log_message *const msg, void *arg) | |
621 | { | |
622 | VAR_UNUSED(arg); | |
623 | *msg->p = 0; | |
624 | CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b); | |
625 | } | |
626 | ||
627 | enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX }; | |
628 | #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback | |
629 | #endif | |
630 | ||
631 | #if BT_LOG_USE_DEBUGSTRING | |
632 | #include <windows.h> | |
633 | ||
634 | static void out_debugstring_callback(const bt_log_message *const msg, void *arg) | |
635 | { | |
636 | VAR_UNUSED(arg); | |
637 | msg->p[0] = '\n'; | |
638 | msg->p[1] = '\0'; | |
639 | OutputDebugStringA(msg->buf); | |
640 | } | |
641 | ||
642 | enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD }; | |
643 | #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback | |
644 | #endif | |
645 | ||
646 | BT_HIDDEN | |
647 | void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg) | |
648 | { | |
649 | VAR_UNUSED(arg); | |
650 | const size_t eol_len = sizeof(BT_LOG_EOL) - 1; | |
651 | memcpy(msg->p, BT_LOG_EOL, eol_len); | |
652 | #if defined(_WIN32) || defined(_WIN64) | |
653 | /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and | |
654 | without FILE_WRITE_DATA */ | |
655 | WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf, | |
656 | (DWORD)(msg->p - msg->buf + eol_len), 0, 0); | |
657 | #else | |
658 | /* write() is atomic for buffers less than or equal to PIPE_BUF. */ | |
659 | RETVAL_UNUSED(write(STDERR_FILENO, msg->buf, | |
660 | (size_t)(msg->p - msg->buf) + eol_len)); | |
661 | #endif | |
662 | } | |
663 | ||
664 | static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR}; | |
665 | ||
666 | #if !BT_LOG_EXTERN_TAG_PREFIX | |
667 | BT_LOG_DEFINE_TAG_PREFIX = 0; | |
668 | #endif | |
669 | ||
670 | #if !BT_LOG_EXTERN_GLOBAL_FORMAT | |
671 | BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH}; | |
672 | #endif | |
673 | ||
674 | #if !BT_LOG_EXTERN_GLOBAL_OUTPUT | |
675 | #if BT_LOG_USE_ANDROID_LOG | |
676 | BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID}; | |
677 | #elif BT_LOG_USE_NSLOG | |
678 | BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG}; | |
679 | #elif BT_LOG_USE_DEBUGSTRING | |
680 | BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING}; | |
681 | #else | |
682 | BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR}; | |
683 | #endif | |
684 | #endif | |
685 | ||
686 | #if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL | |
687 | BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0; | |
688 | #endif | |
689 | ||
4f1c7f2e | 690 | BT_HIDDEN |
beb0fb75 PP |
691 | const bt_log_spec _bt_log_stderr_spec = |
692 | { | |
693 | BT_LOG_GLOBAL_FORMAT, | |
694 | &out_stderr, | |
695 | }; | |
696 | ||
697 | static const bt_log_spec global_spec = | |
698 | { | |
699 | BT_LOG_GLOBAL_FORMAT, | |
700 | BT_LOG_GLOBAL_OUTPUT, | |
701 | }; | |
702 | ||
703 | #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT) | |
704 | static char lvl_char(const int lvl) | |
705 | { | |
706 | switch (lvl) | |
707 | { | |
ef267d12 PP |
708 | case BT_LOG_TRACE: |
709 | return 'T'; | |
beb0fb75 PP |
710 | case BT_LOG_DEBUG: |
711 | return 'D'; | |
712 | case BT_LOG_INFO: | |
713 | return 'I'; | |
770538dd | 714 | case BT_LOG_WARNING: |
beb0fb75 PP |
715 | return 'W'; |
716 | case BT_LOG_ERROR: | |
717 | return 'E'; | |
718 | case BT_LOG_FATAL: | |
719 | return 'F'; | |
720 | default: | |
721 | ASSERT_UNREACHABLE("Bad log level"); | |
722 | return '?'; | |
723 | } | |
724 | } | |
725 | #endif | |
726 | ||
727 | #define GCCVER_LESS(MAJOR, MINOR, PATCH) \ | |
728 | (__GNUC__ < MAJOR || \ | |
729 | (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \ | |
730 | (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH)))) | |
731 | ||
732 | #if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0) | |
733 | #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0) | |
734 | #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n) | |
735 | #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n) | |
736 | #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n) | |
737 | #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n) | |
738 | /* Note: will not store old value of *vp in *ep (non-standard behaviour) */ | |
739 | #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \ | |
740 | __sync_bool_compare_and_swap(vp, *(ep), d) | |
741 | #endif | |
742 | ||
743 | #if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64) | |
744 | #define TCACHE | |
745 | #define TCACHE_STALE (0x40000000) | |
746 | #define TCACHE_FLUID (0x40000000 | 0x80000000) | |
747 | static unsigned g_tcache_mode = TCACHE_STALE; | |
748 | static struct timeval g_tcache_tv = {0, 0}; | |
f833fd04 | 749 | static struct tm g_tcache_tm = {0}; |
beb0fb75 PP |
750 | |
751 | static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm) | |
752 | { | |
753 | unsigned mode; | |
754 | mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED); | |
755 | if (0 == (mode & TCACHE_FLUID)) | |
756 | { | |
757 | mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE); | |
758 | if (0 == (mode & TCACHE_FLUID)) | |
759 | { | |
760 | if (g_tcache_tv.tv_sec == tv->tv_sec) | |
761 | { | |
762 | *tm = g_tcache_tm; | |
763 | __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); | |
764 | return !0; | |
765 | } | |
766 | __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED); | |
767 | } | |
768 | __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE); | |
769 | } | |
770 | return 0; | |
771 | } | |
772 | ||
773 | static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm) | |
774 | { | |
775 | unsigned stale = TCACHE_STALE; | |
776 | if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID, | |
777 | 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) | |
778 | { | |
779 | g_tcache_tv = *tv; | |
780 | g_tcache_tm = *tm; | |
781 | __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE); | |
782 | } | |
783 | } | |
784 | #endif | |
785 | ||
786 | static void time_callback(struct tm *const tm, unsigned *const msec) | |
787 | { | |
788 | #if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED | |
789 | VAR_UNUSED(tm); | |
790 | VAR_UNUSED(msec); | |
791 | #else | |
792 | #if defined(_WIN32) || defined(_WIN64) | |
793 | SYSTEMTIME st; | |
794 | GetLocalTime(&st); | |
795 | tm->tm_year = st.wYear; | |
796 | tm->tm_mon = st.wMonth; | |
797 | tm->tm_mday = st.wDay; | |
798 | tm->tm_wday = st.wDayOfWeek; | |
799 | tm->tm_hour = st.wHour; | |
800 | tm->tm_min = st.wMinute; | |
801 | tm->tm_sec = st.wSecond; | |
802 | *msec = st.wMilliseconds; | |
803 | #else | |
804 | struct timeval tv; | |
805 | gettimeofday(&tv, 0); | |
806 | #ifndef TCACHE | |
807 | localtime_r(&tv.tv_sec, tm); | |
808 | #else | |
809 | if (!tcache_get(&tv, tm)) | |
810 | { | |
811 | localtime_r(&tv.tv_sec, tm); | |
812 | tcache_set(&tv, tm); | |
813 | } | |
814 | #endif | |
815 | *msec = (unsigned)tv.tv_usec / 1000; | |
816 | #endif | |
817 | #endif | |
818 | } | |
819 | ||
820 | static void pid_callback(int *const pid, int *const tid) | |
821 | { | |
822 | #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) | |
823 | VAR_UNUSED(pid); | |
824 | #else | |
825 | #if defined(_WIN32) || defined(_WIN64) | |
826 | *pid = GetCurrentProcessId(); | |
827 | #else | |
828 | *pid = getpid(); | |
829 | #endif | |
830 | #endif | |
831 | ||
832 | #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) | |
833 | VAR_UNUSED(tid); | |
834 | #else | |
835 | #if defined(_WIN32) || defined(_WIN64) | |
836 | *tid = GetCurrentThreadId(); | |
f61ad754 MJ |
837 | #elif defined(__CYGWIN__) |
838 | pthread_t thr = pthread_self(); | |
839 | *tid = (int)pthread_getsequence_np(&thr); | |
c0c0c8b4 MJ |
840 | #elif defined(__sun__) |
841 | *tid = (int)pthread_self(); | |
beb0fb75 PP |
842 | #elif defined(__ANDROID__) |
843 | *tid = gettid(); | |
844 | #elif defined(__linux__) | |
845 | *tid = syscall(SYS_gettid); | |
846 | #elif defined(__MACH__) | |
847 | *tid = (int)pthread_mach_thread_np(pthread_self()); | |
848 | #else | |
849 | #define Platform not supported | |
850 | #endif | |
851 | #endif | |
852 | } | |
853 | ||
854 | static void buffer_callback(bt_log_message *msg, char *buf) | |
855 | { | |
856 | msg->e = (msg->p = msg->buf = buf) + g_buf_sz; | |
857 | } | |
858 | ||
859 | #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) | |
860 | static const char *funcname(const char *func) | |
861 | { | |
862 | return func? func: ""; | |
863 | } | |
864 | #endif | |
865 | ||
866 | #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) | |
867 | static const char *filename(const char *file) | |
868 | { | |
869 | const char *f = file; | |
870 | for (const char *p = file; 0 != *p; ++p) | |
871 | { | |
872 | if ('/' == *p || '\\' == *p) | |
873 | { | |
874 | f = p + 1; | |
875 | } | |
876 | } | |
877 | return f; | |
878 | } | |
879 | #endif | |
880 | ||
881 | static INLINE size_t nprintf_size(bt_log_message *const msg) | |
882 | { | |
883 | // *nprintf() always puts 0 in the end when input buffer is not empty. This | |
884 | // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which | |
885 | // leaves space for one more character. Some put_xxx() functions don't use | |
886 | // *nprintf() and could use that last character. In that case log line will | |
887 | // have multiple (two) half-written parts which is confusing. To workaround | |
888 | // that we allow *nprintf() to write its 0 in the eol area (which is always | |
889 | // not empty). | |
890 | return (size_t)(msg->e - msg->p + 1); | |
891 | } | |
892 | ||
893 | static INLINE void put_nprintf(bt_log_message *const msg, const int n) | |
894 | { | |
895 | if (0 < n) | |
896 | { | |
897 | msg->p = n < msg->e - msg->p? msg->p + n: msg->e; | |
898 | } | |
899 | } | |
900 | ||
901 | static INLINE char *put_padding_r(const unsigned w, const char wc, | |
902 | char *p, char *e) | |
903 | { | |
904 | for (char *const b = e - w; b < p; *--p = wc) {} | |
905 | return p; | |
906 | } | |
907 | ||
908 | static char *put_integer_r(unsigned v, const int sign, | |
909 | const unsigned w, const char wc, char *const e) | |
910 | { | |
911 | static const char _signs[] = {'-', '0', '+'}; | |
ee07b1e9 | 912 | const char *const signs = _signs + 1; |
beb0fb75 PP |
913 | char *p = e; |
914 | do { *--p = '0' + v % 10; } while (0 != (v /= 10)); | |
915 | if (0 == sign) return put_padding_r(w, wc, p, e); | |
916 | if ('0' != wc) | |
917 | { | |
918 | *--p = signs[sign]; | |
919 | return put_padding_r(w, wc, p, e); | |
920 | } | |
921 | p = put_padding_r(w, wc, p, e + 1); | |
922 | *--p = signs[sign]; | |
923 | return p; | |
924 | } | |
925 | ||
926 | static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc, | |
927 | char *const e) | |
928 | { | |
929 | return put_integer_r(v, 0, w, wc, e); | |
930 | } | |
931 | ||
932 | static INLINE char *put_int_r(const int v, const unsigned w, const char wc, | |
933 | char *const e) | |
934 | { | |
935 | return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e) | |
936 | : put_integer_r((unsigned)-v, -1, w, wc, e); | |
937 | } | |
938 | ||
939 | static INLINE char *put_stringn(const char *const s_p, const char *const s_e, | |
940 | char *const p, char *const e) | |
941 | { | |
942 | const ptrdiff_t m = e - p; | |
943 | ptrdiff_t n = s_e - s_p; | |
944 | if (n > m) | |
945 | { | |
946 | n = m; | |
947 | } | |
948 | memcpy(p, s_p, n); | |
949 | return p + n; | |
950 | } | |
951 | ||
952 | static INLINE char *put_string(const char *s, char *p, char *const e) | |
953 | { | |
954 | const ptrdiff_t n = e - p; | |
955 | char *const c = (char *)memccpy(p, s, '\0', n); | |
956 | return 0 != c? c - 1: e; | |
957 | } | |
958 | ||
959 | static INLINE char *put_uint(unsigned v, const unsigned w, const char wc, | |
960 | char *const p, char *const e) | |
961 | { | |
962 | char buf[16]; | |
963 | char *const se = buf + _countof(buf); | |
964 | char *sp = put_uint_r(v, w, wc, se); | |
965 | return put_stringn(sp, se, p, e); | |
966 | } | |
967 | ||
968 | #define PUT_CSTR_R(p, STR) \ | |
969 | do { \ | |
970 | for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \ | |
971 | *--(p) = (STR)[i]; \ | |
972 | } \ | |
973 | } _BT_LOG_ONCE | |
974 | ||
975 | #define PUT_CSTR_CHECKED(p, e, STR) \ | |
976 | do { \ | |
977 | for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \ | |
978 | *(p)++ = (STR)[i]; \ | |
979 | } \ | |
980 | } _BT_LOG_ONCE | |
981 | ||
982 | /* F_INIT field support. | |
983 | */ | |
984 | #define _BT_LOG_MESSAGE_FORMAT_INIT__ | |
985 | #define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR | |
986 | #define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH | |
987 | #define _BT_LOG_MESSAGE_FORMAT_INIT__DAY | |
988 | #define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR | |
989 | #define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE | |
990 | #define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND | |
991 | #define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND | |
992 | #define _BT_LOG_MESSAGE_FORMAT_INIT__PID | |
993 | #define _BT_LOG_MESSAGE_FORMAT_INIT__TID | |
994 | #define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL | |
995 | #define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts) | |
996 | #define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION | |
997 | #define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME | |
998 | #define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE | |
999 | #define _BT_LOG_MESSAGE_FORMAT_INIT__S(s) | |
1000 | #define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr); | |
1001 | #define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v) | |
1002 | #define _BT_LOG_MESSAGE_FORMAT_INIT(field) \ | |
1003 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field) | |
1004 | ||
1005 | /* Implements generation of printf-like format string for log message | |
1006 | * format specification. | |
1007 | */ | |
1008 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__ "" | |
1009 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR "%04u" | |
1010 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH "%02u" | |
1011 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY "%02u" | |
1012 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR "%02u" | |
1013 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE "%02u" | |
1014 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND "%02u" | |
1015 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND "%03u" | |
1016 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID "%5i" | |
1017 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID "%5i" | |
1018 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL "%c" | |
1019 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG UNDEFINED | |
1020 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION "%s" | |
1021 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME "%s" | |
1022 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE "%u" | |
1023 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s) s | |
1024 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) "" | |
1025 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u" | |
1026 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \ | |
1027 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field) | |
1028 | ||
1029 | /* Implements generation of printf-like format parameters for log message | |
1030 | * format specification. | |
1031 | */ | |
1032 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__ | |
1033 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR ,(unsigned)(tm.tm_year + 1900) | |
1034 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH ,(unsigned)(tm.tm_mon + 1) | |
1035 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY ,(unsigned)tm.tm_mday | |
1036 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR ,(unsigned)tm.tm_hour | |
1037 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE ,(unsigned)tm.tm_min | |
1038 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND ,(unsigned)tm.tm_sec | |
1039 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND ,(unsigned)msec | |
1040 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID ,pid | |
1041 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID ,tid | |
1042 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL ,(char)lvl_char(msg->lvl) | |
1043 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG UNDEFINED | |
1044 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION ,funcname(src->func) | |
1045 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME ,filename(src->file) | |
1046 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE ,src->line | |
1047 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s) | |
1048 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr) | |
1049 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v | |
1050 | #define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \ | |
1051 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field) | |
1052 | ||
1053 | /* Implements generation of put_xxx_t statements for log message specification. | |
1054 | */ | |
1055 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__ | |
1056 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR p = put_uint_r(tm.tm_year + 1900, 4, '0', p); | |
1057 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p); | |
1058 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p); | |
1059 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p); | |
1060 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE p = put_uint_r((unsigned)tm.tm_min, 2, '0', p); | |
1061 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p); | |
1062 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND p = put_uint_r(msec, 3, '0', p); | |
1063 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID p = put_int_r(pid, 5, ' ', p); | |
1064 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID p = put_int_r(tid, 5, ' ', p); | |
1065 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL *--p = lvl_char(msg->lvl); | |
1066 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG UNDEFINED | |
1067 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION UNDEFINED | |
1068 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME UNDEFINED | |
1069 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE UNDEFINED | |
1070 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s) PUT_CSTR_R(p, s); | |
1071 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr) | |
1072 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p); | |
1073 | #define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \ | |
1074 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field) | |
1075 | ||
1076 | static void put_ctx(bt_log_message *const msg) | |
1077 | { | |
1078 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT) | |
1079 | #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT) | |
1080 | VAR_UNUSED(msg); | |
1081 | #else | |
1082 | #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED | |
1083 | struct tm tm; | |
1084 | unsigned msec; | |
1085 | g_time_cb(&tm, &msec); | |
1086 | #endif | |
1087 | #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \ | |
1088 | _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT) | |
1089 | int pid, tid; | |
1090 | g_pid_cb(&pid, &tid); | |
1091 | #endif | |
1092 | ||
1093 | #if BT_LOG_OPTIMIZE_SIZE | |
1094 | int n; | |
1095 | n = snprintf(msg->p, nprintf_size(msg), | |
1096 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT) | |
1097 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT)); | |
1098 | put_nprintf(msg, n); | |
1099 | #else | |
1100 | char buf[64]; | |
1101 | char *const e = buf + sizeof(buf); | |
1102 | char *p = e; | |
1103 | _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT) | |
1104 | msg->p = put_stringn(p, e, msg->p, msg->e); | |
1105 | #endif | |
1106 | #endif | |
1107 | } | |
1108 | ||
1109 | #define PUT_TAG(msg, tag, prefix_delim, tag_delim) \ | |
1110 | do { \ | |
1111 | const char *ch; \ | |
1112 | msg->tag_b = msg->p; \ | |
1113 | if (0 != (ch = _bt_log_tag_prefix)) { \ | |
1114 | for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ | |
1115 | } \ | |
1116 | if (0 != (ch = tag) && 0 != tag[0]) { \ | |
1117 | if (msg->tag_b != msg->p) { \ | |
1118 | PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \ | |
1119 | } \ | |
1120 | for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \ | |
1121 | } \ | |
1122 | msg->tag_e = msg->p; \ | |
1123 | if (msg->tag_b != msg->p) { \ | |
1124 | PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \ | |
1125 | } \ | |
1126 | } _BT_LOG_ONCE | |
1127 | ||
1128 | /* Implements simple put statements for log message specification. | |
1129 | */ | |
1130 | #define _BT_LOG_MESSAGE_FORMAT_PUT__ | |
1131 | #define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR UNDEFINED | |
1132 | #define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH UNDEFINED | |
1133 | #define _BT_LOG_MESSAGE_FORMAT_PUT__DAY UNDEFINED | |
1134 | #define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR UNDEFINED | |
1135 | #define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE UNDEFINED | |
1136 | #define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND UNDEFINED | |
1137 | #define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND UNDEFINED | |
1138 | #define _BT_LOG_MESSAGE_FORMAT_PUT__PID UNDEFINED | |
1139 | #define _BT_LOG_MESSAGE_FORMAT_PUT__TID UNDEFINED | |
1140 | #define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL UNDEFINED | |
1141 | #define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td) PUT_TAG(msg, tag, pd, td); | |
1142 | #define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION msg->p = put_string(funcname(src->func), msg->p, msg->e); | |
1143 | #define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME msg->p = put_string(filename(src->file), msg->p, msg->e); | |
1144 | #define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e); | |
1145 | #define _BT_LOG_MESSAGE_FORMAT_PUT__S(s) PUT_CSTR_CHECKED(msg->p, msg->e, s); | |
1146 | #define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr) | |
1147 | #define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e); | |
1148 | #define _BT_LOG_MESSAGE_FORMAT_PUT(field) \ | |
1149 | _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field) | |
1150 | ||
1151 | static void put_tag(bt_log_message *const msg, const char *const tag) | |
1152 | { | |
1153 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT) | |
156184d6 SM |
1154 | |
1155 | /* | |
1156 | * This generates a -Wundef warning. The issue was reported upstream: | |
1157 | * | |
1158 | * https://github.com/wonder-mice/zf_log/issues/40 | |
1159 | * | |
1160 | * but there's not much we can do here, so just silence it. | |
1161 | */ | |
1162 | #pragma GCC diagnostic push | |
1163 | #pragma GCC diagnostic ignored "-Wundef" | |
beb0fb75 PP |
1164 | #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT) |
1165 | VAR_UNUSED(tag); | |
1166 | #endif | |
156184d6 SM |
1167 | #pragma GCC diagnostic pop |
1168 | ||
beb0fb75 PP |
1169 | #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT) |
1170 | VAR_UNUSED(msg); | |
1171 | #else | |
1172 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT) | |
1173 | #endif | |
1174 | } | |
1175 | ||
1176 | static void put_src(bt_log_message *const msg, const src_location *const src) | |
1177 | { | |
1178 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT) | |
1179 | #if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \ | |
1180 | !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \ | |
1181 | !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT) | |
1182 | VAR_UNUSED(src); | |
1183 | #endif | |
1184 | #if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT) | |
1185 | VAR_UNUSED(msg); | |
1186 | #else | |
1187 | #if BT_LOG_OPTIMIZE_SIZE | |
1188 | int n; | |
1189 | n = snprintf(msg->p, nprintf_size(msg), | |
1190 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT) | |
1191 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT)); | |
1192 | put_nprintf(msg, n); | |
1193 | #else | |
1194 | _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT) | |
1195 | #endif | |
1196 | #endif | |
1197 | } | |
1198 | ||
8b305066 SM |
1199 | static _BT_LOG_PRINTFLIKE(2, 0) |
1200 | void put_msg(bt_log_message *const msg, | |
beb0fb75 PP |
1201 | const char *const fmt, va_list va) |
1202 | { | |
1203 | int n; | |
1204 | msg->msg_b = msg->p; | |
1205 | n = vsnprintf(msg->p, nprintf_size(msg), fmt, va); | |
1206 | put_nprintf(msg, n); | |
1207 | } | |
1208 | ||
1209 | static void output_mem(const bt_log_spec *log, bt_log_message *const msg, | |
1210 | const mem_block *const mem) | |
1211 | { | |
1212 | if (0 == mem->d || 0 == mem->d_sz) | |
1213 | { | |
1214 | return; | |
1215 | } | |
1216 | const unsigned char *mem_p = (const unsigned char *)mem->d; | |
1217 | const unsigned char *const mem_e = mem_p + mem->d_sz; | |
1218 | const unsigned char *mem_cut; | |
1219 | const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width; | |
1220 | char *const hex_b = msg->msg_b; | |
1221 | char *const ascii_b = hex_b + 2 * mem_width + 2; | |
1222 | char *const ascii_e = ascii_b + mem_width; | |
1223 | if (msg->e < ascii_e) | |
1224 | { | |
1225 | return; | |
1226 | } | |
1227 | while (mem_p != mem_e) | |
1228 | { | |
1229 | char *hex = hex_b; | |
1230 | char *ascii = ascii_b; | |
1231 | for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e; | |
1232 | mem_cut != mem_p; ++mem_p) | |
1233 | { | |
1234 | const unsigned char ch = *mem_p; | |
1235 | *hex++ = c_hex[(0xf0 & ch) >> 4]; | |
1236 | *hex++ = c_hex[(0x0f & ch)]; | |
1237 | *ascii++ = isprint(ch)? (char)ch: '?'; | |
1238 | } | |
1239 | while (hex != ascii_b) | |
1240 | { | |
1241 | *hex++ = ' '; | |
1242 | } | |
1243 | msg->p = ascii; | |
1244 | log->output->callback(msg, log->output->arg); | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | BT_HIDDEN | |
1249 | void bt_log_set_tag_prefix(const char *const prefix) | |
1250 | { | |
1251 | _bt_log_tag_prefix = prefix; | |
1252 | } | |
1253 | ||
1254 | BT_HIDDEN | |
1255 | void bt_log_set_mem_width(const unsigned w) | |
1256 | { | |
1257 | _bt_log_global_format.mem_width = w; | |
1258 | } | |
1259 | ||
1260 | BT_HIDDEN | |
1261 | void bt_log_set_output_level(const int lvl) | |
1262 | { | |
1263 | _bt_log_global_output_lvl = lvl; | |
1264 | } | |
1265 | ||
1266 | BT_HIDDEN | |
1267 | void bt_log_set_output_v(const unsigned mask, void *const arg, | |
1268 | const bt_log_output_cb callback) | |
1269 | { | |
1270 | _bt_log_global_output.mask = mask; | |
1271 | _bt_log_global_output.arg = arg; | |
1272 | _bt_log_global_output.callback = callback; | |
1273 | } | |
1274 | ||
8b305066 SM |
1275 | static _BT_LOG_PRINTFLIKE(6, 0) |
1276 | void _bt_log_write_imp( | |
beb0fb75 PP |
1277 | const bt_log_spec *log, |
1278 | const src_location *const src, const mem_block *const mem, | |
1279 | const int lvl, const char *const tag, const char *const fmt, va_list va) | |
1280 | { | |
1281 | bt_log_message msg; | |
deaa6f85 | 1282 | char *buf = logging_buf; |
beb0fb75 PP |
1283 | const unsigned mask = log->output->mask; |
1284 | msg.lvl = lvl; | |
1285 | msg.tag = tag; | |
1286 | g_buffer_cb(&msg, buf); | |
557d8f39 PP |
1287 | const char *rst_color_p = bt_common_color_reset(); |
1288 | const char *rst_color_e = rst_color_p + strlen(rst_color_p); | |
1289 | const char *color_p = ""; | |
1290 | const char *color_e = color_p; | |
1291 | ||
1292 | switch (lvl) { | |
1293 | case BT_LOG_INFO: | |
1294 | color_p = bt_common_color_fg_blue(); | |
1295 | color_e = color_p + strlen(color_p); | |
1296 | break; | |
770538dd | 1297 | case BT_LOG_WARNING: |
557d8f39 PP |
1298 | color_p = bt_common_color_fg_yellow(); |
1299 | color_e = color_p + strlen(color_p); | |
1300 | break; | |
1301 | case BT_LOG_ERROR: | |
1302 | case BT_LOG_FATAL: | |
1303 | color_p = bt_common_color_fg_red(); | |
1304 | color_e = color_p + strlen(color_p); | |
1305 | break; | |
1306 | default: | |
1307 | break; | |
1308 | } | |
1309 | ||
1310 | msg.p = put_stringn(color_p, color_e, msg.p, msg.e); | |
1311 | ||
beb0fb75 PP |
1312 | if (BT_LOG_PUT_CTX & mask) |
1313 | { | |
1314 | put_ctx(&msg); | |
1315 | } | |
1316 | if (BT_LOG_PUT_TAG & mask) | |
1317 | { | |
1318 | put_tag(&msg, tag); | |
1319 | } | |
1320 | if (0 != src && BT_LOG_PUT_SRC & mask) | |
1321 | { | |
1322 | put_src(&msg, src); | |
1323 | } | |
1324 | if (BT_LOG_PUT_MSG & mask) | |
1325 | { | |
1326 | put_msg(&msg, fmt, va); | |
1327 | } | |
557d8f39 | 1328 | msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e); |
beb0fb75 PP |
1329 | log->output->callback(&msg, log->output->arg); |
1330 | if (0 != mem && BT_LOG_PUT_MSG & mask) | |
1331 | { | |
1332 | output_mem(log, &msg, mem); | |
1333 | } | |
1334 | } | |
1335 | ||
1336 | BT_HIDDEN | |
1337 | void _bt_log_write_d( | |
1338 | const char *const func, const char *const file, const unsigned line, | |
1339 | const int lvl, const char *const tag, | |
1340 | const char *const fmt, ...) | |
1341 | { | |
1342 | const src_location src = {func, file, line}; | |
1343 | va_list va; | |
1344 | va_start(va, fmt); | |
1345 | _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va); | |
1346 | va_end(va); | |
1347 | } | |
1348 | ||
1349 | BT_HIDDEN | |
1350 | void _bt_log_write_aux_d( | |
1351 | const char *const func, const char *const file, const unsigned line, | |
1352 | const bt_log_spec *const log, const int lvl, const char *const tag, | |
1353 | const char *const fmt, ...) | |
1354 | { | |
1355 | const src_location src = {func, file, line}; | |
1356 | va_list va; | |
1357 | va_start(va, fmt); | |
1358 | _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va); | |
1359 | va_end(va); | |
1360 | } | |
1361 | ||
1362 | BT_HIDDEN | |
1363 | void _bt_log_write(const int lvl, const char *const tag, | |
1364 | const char *const fmt, ...) | |
1365 | { | |
1366 | va_list va; | |
1367 | va_start(va, fmt); | |
1368 | _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va); | |
1369 | va_end(va); | |
1370 | } | |
1371 | ||
1372 | BT_HIDDEN | |
1373 | void _bt_log_write_aux( | |
1374 | const bt_log_spec *const log, const int lvl, const char *const tag, | |
1375 | const char *const fmt, ...) | |
1376 | { | |
1377 | va_list va; | |
1378 | va_start(va, fmt); | |
1379 | _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va); | |
1380 | va_end(va); | |
1381 | } | |
1382 | ||
1383 | BT_HIDDEN | |
1384 | void _bt_log_write_mem_d( | |
1385 | const char *const func, const char *const file, const unsigned line, | |
1386 | const int lvl, const char *const tag, | |
1387 | const void *const d, const unsigned d_sz, | |
1388 | const char *const fmt, ...) | |
1389 | { | |
1390 | const src_location src = {func, file, line}; | |
1391 | const mem_block mem = {d, d_sz}; | |
1392 | va_list va; | |
1393 | va_start(va, fmt); | |
1394 | _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va); | |
1395 | va_end(va); | |
1396 | } | |
1397 | ||
1398 | BT_HIDDEN | |
1399 | void _bt_log_write_mem_aux_d( | |
1400 | const char *const func, const char *const file, const unsigned line, | |
1401 | const bt_log_spec *const log, const int lvl, const char *const tag, | |
1402 | const void *const d, const unsigned d_sz, | |
1403 | const char *const fmt, ...) | |
1404 | { | |
1405 | const src_location src = {func, file, line}; | |
1406 | const mem_block mem = {d, d_sz}; | |
1407 | va_list va; | |
1408 | va_start(va, fmt); | |
1409 | _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va); | |
1410 | va_end(va); | |
1411 | } | |
1412 | ||
1413 | BT_HIDDEN | |
1414 | void _bt_log_write_mem(const int lvl, const char *const tag, | |
1415 | const void *const d, const unsigned d_sz, | |
1416 | const char *const fmt, ...) | |
1417 | { | |
1418 | const mem_block mem = {d, d_sz}; | |
1419 | va_list va; | |
1420 | va_start(va, fmt); | |
1421 | _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va); | |
1422 | va_end(va); | |
1423 | } | |
1424 | ||
1425 | BT_HIDDEN | |
1426 | void _bt_log_write_mem_aux( | |
1427 | const bt_log_spec *const log, const int lvl, const char *const tag, | |
1428 | const void *const d, const unsigned d_sz, | |
1429 | const char *const fmt, ...) | |
1430 | { | |
1431 | const mem_block mem = {d, d_sz}; | |
1432 | va_list va; | |
1433 | va_start(va, fmt); | |
1434 | _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va); | |
1435 | va_end(va); | |
1436 | } |