2 * SPDX-License-Identifier: MIT
4 * Copyright (c) 2016 wonder-mice
5 * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
7 * This is very inspired by zf_log.c (see
8 * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
9 * stripped down) for the use cases of Babeltrace.
22 #if defined(_WIN32) || defined(_WIN64)
27 # include <sys/prctl.h>
28 # include <sys/syscall.h>
39 #include "common/assert.h"
40 #include "common/common.h"
41 #include "common/macros.h"
42 #include "compat/time.h"
47 extern unsigned long pthread_getsequence_np(pthread_t
*);
51 * Thread-local logging message buffer to put the next message and write
52 * it with a single system call.
54 static __thread
char msg_buf
[4 * 4096];
57 * Returns the number of available bytes in `msg_buf` considering the
58 * writing position `at`.
61 size_t avail_msg_buf_bytes(char *at
)
63 return msg_buf
+ sizeof(msg_buf
) - at
;
67 * Appends the character `ch` to `msg_buf`.
70 void append_char_to_msg_buf(char ** const at
, const char ch
)
77 * Appends a space character to `msg_buf`.
80 void append_sp_to_msg_buf(char ** const at
)
82 append_char_to_msg_buf(at
, ' ');
86 * Appends the null-terminated string `str` to `msg_buf`.
89 void append_str_to_msg_buf(char ** const at
, const char * const str
)
91 const size_t len
= strlen(str
);
93 memcpy(*at
, str
, len
);
98 * Formats the unsigned integer `val` with sprintf() using `fmt` and
99 * appends the resulting string to `msg_buf`.
102 void append_uint_to_msg_buf(char ** const at
, const char * const fmt
,
103 const unsigned int val
)
105 #pragma GCC diagnostic push
106 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
107 const int written_len
= sprintf(*at
, fmt
, val
);
108 #pragma GCC diagnostic pop
110 BT_ASSERT_DBG(written_len
> 0);
111 *at
+= (size_t) written_len
;
115 * Thread-local cache of seconds and milliseconds to formatted date/time
116 * string (null terminated).
118 * This is often useful because many log messages may happen during the
121 * Does not need any kind of protection since it's TLS.
123 struct date_time_cache
{
129 static __thread
struct date_time_cache date_time_cache
= {0};
132 const char *date_time_cache_get(const struct timeval tv
)
134 const uint64_t s
= (uint64_t) tv
.tv_sec
;
135 const uint32_t ms
= (uint32_t) (tv
.tv_usec
/ 1000);
137 if (date_time_cache
.s
!= s
|| date_time_cache
.ms
!= ms
) {
138 /* Add to cache now */
140 const time_t time_s
= (time_t) tv
.tv_sec
;
142 date_time_cache
.s
= (uint64_t) tv
.tv_sec
;
143 date_time_cache
.ms
= (uint32_t) (tv
.tv_usec
/ 1000);
144 (void) bt_localtime_r(&time_s
, &tm
);
145 (void) sprintf(date_time_cache
.str
, "%02u-%02u %02u:%02u:%02u.%03u",
146 tm
.tm_mon
+ 1, tm
.tm_mday
,
147 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
, date_time_cache
.ms
);
150 return date_time_cache
.str
;
154 * Appends a formatted date/time for `tv` to `msg_buf`.
157 void append_date_time_to_msg_buf(char ** const at
, const struct timeval tv
)
159 const char *str
= date_time_cache_get(tv
);
161 append_str_to_msg_buf(at
, str
);
165 * Appends the PID and TID to `msg_buf`.
168 void append_pid_tid_to_msg_buf(char ** const at
)
170 const unsigned int pid
= (unsigned int) getpid();
173 /* Look at this beautiful portability */
174 #if defined(_WIN32) || defined(_WIN64)
175 tid
= (unsigned int) GetCurrentThreadId();
176 #elif defined(__CYGWIN__)
178 pthread_t thr
= pthread_self();
180 tid
= (unsigned int) pthread_getsequence_np(&thr
);
182 #elif defined(__sun__)
183 tid
= (unsigned int) pthread_self();
184 #elif defined(__linux__)
185 tid
= (unsigned int) syscall(SYS_gettid
);
186 #elif defined(__APPLE__) && defined(__MACH__)
187 tid
= (unsigned int) pthread_mach_thread_np(pthread_self());
188 #elif defined(__GNU__)
190 mach_port_t mach_port
= mach_thread_self();
192 mach_port_deallocate(mach_task_self(), mach_port
);
193 tid
= (unsigned int) mach_port
;
196 # error "Platform not supported"
199 /* Append them now */
200 append_uint_to_msg_buf(at
, "%u", pid
);
201 append_sp_to_msg_buf(at
);
202 append_uint_to_msg_buf(at
, "%u", tid
);
206 * Writes the initial part of the log message to `msg_buf`, without the
207 * message and without resetting the terminal color).
210 void common_write_init(char ** const at
, const char * const file_name
,
211 const char * const func_name
, const unsigned int line_no
,
212 const enum bt_log_level lvl
, const char * const tag
)
214 const char *color_p
= "";
217 /* Get time immediately */
218 gettimeofday(&tv
, 0);
220 /* Write the terminal color code to use, if any */
223 color_p
= bt_common_color_fg_blue();
226 color_p
= bt_common_color_fg_yellow();
230 color_p
= bt_common_color_fg_red();
236 append_str_to_msg_buf(at
, color_p
);
238 /* Write date/time */
239 append_date_time_to_msg_buf(at
, tv
);
240 append_sp_to_msg_buf(at
);
243 append_pid_tid_to_msg_buf(at
);
244 append_sp_to_msg_buf(at
);
246 /* Write log level letter */
247 append_char_to_msg_buf(at
, bt_log_get_letter_from_level(lvl
));
248 append_sp_to_msg_buf(at
);
252 append_str_to_msg_buf(at
, tag
);
253 append_sp_to_msg_buf(at
);
256 /* Write source location */
257 append_str_to_msg_buf(at
, func_name
);
258 append_char_to_msg_buf(at
, '@');
259 append_str_to_msg_buf(at
, file_name
);
260 append_uint_to_msg_buf(at
, ":%u", line_no
);
261 append_sp_to_msg_buf(at
);
265 * Writes the final part of the log message to `msg_buf` (resets the
266 * terminal color and appends a newline), and then writes the whole log
267 * message to the standard error.
270 void common_write_fini(char ** const at
)
272 append_str_to_msg_buf(at
, bt_common_color_reset());
273 append_char_to_msg_buf(at
, '\n');
274 (void) write(STDERR_FILENO
, msg_buf
, *at
- msg_buf
);
277 void bt_log_write(const char * const file_name
, const char * const func_name
,
278 const unsigned int line_no
, const enum bt_log_level lvl
,
279 const char * const tag
, const char * const msg
)
283 common_write_init(&at
, file_name
, func_name
, line_no
, lvl
, tag
);
284 append_str_to_msg_buf(&at
, msg
);
285 common_write_fini(&at
);
288 _BT_LOG_PRINTFLIKE(6, 0)
290 void write_vprintf(const char * const file_name
, const char * const func_name
,
291 const unsigned int line_no
, const enum bt_log_level lvl
,
292 const char * const tag
, const char * const fmt
, va_list args
)
297 common_write_init(&at
, file_name
, func_name
, line_no
, lvl
, tag
);
298 written_len
= vsnprintf(at
, avail_msg_buf_bytes(at
) - 16,
300 if (written_len
> 0) {
301 at
+= (size_t) written_len
;
304 common_write_fini(&at
);
307 void bt_log_write_printf(const char * const file_name
,
308 const char * const func_name
, const unsigned int line_no
,
309 const enum bt_log_level lvl
, const char * const tag
,
310 const char * const fmt
, ...)
315 write_vprintf(file_name
, func_name
, line_no
, lvl
, tag
, fmt
, va
);
320 * Writes the initial part of the log message to `msg_buf`, including
321 * the initial message and `errno` string, without the message and
322 * without resetting the terminal color).
325 void common_write_errno_init(char ** const at
, const char * const file_name
,
326 const char * const func_name
, const unsigned int line_no
,
327 const enum bt_log_level lvl
, const char * const tag
,
328 const char * const init_msg
)
330 BT_ASSERT_DBG(errno
!= 0);
331 const char * const errno_msg
= g_strerror(errno
);
333 common_write_init(at
, file_name
, func_name
, line_no
, lvl
, tag
);
334 append_str_to_msg_buf(at
, init_msg
);
335 append_char_to_msg_buf(at
, ':');
336 append_sp_to_msg_buf(at
);
337 append_str_to_msg_buf(at
, errno_msg
);
340 void bt_log_write_errno(const char * const file_name
,
341 const char * const func_name
, const unsigned int line_no
,
342 const enum bt_log_level lvl
, const char * const tag
,
343 const char * const init_msg
, const char * const msg
)
347 common_write_errno_init(&at
, file_name
, func_name
, line_no
, lvl
,
349 append_str_to_msg_buf(&at
, msg
);
350 common_write_fini(&at
);
353 void bt_log_write_errno_printf(const char * const file_name
,
354 const char * const func_name
, const unsigned int line_no
,
355 const enum bt_log_level lvl
, const char * const tag
,
356 const char * const init_msg
, const char * const fmt
, ...)
362 common_write_errno_init(&at
, file_name
, func_name
, line_no
, lvl
,
365 written_len
= vsnprintf(at
, avail_msg_buf_bytes(at
) - 16, fmt
, va
);
367 if (written_len
> 0) {
368 at
+= (size_t) written_len
;
371 common_write_fini(&at
);
375 * Logs `mem_len` bytes of `mem_data` on a single line.
378 void write_mem_line(const char * const file_name
, const char * const func_name
,
379 const unsigned int line_no
, const enum bt_log_level lvl
,
380 const char * const tag
, const uint8_t * const mem_data
,
381 const size_t mem_len
, const size_t max_mem_line_len
)
383 static const char * const hex_chars
= "0123456789abcdef";
387 common_write_init(&at
, file_name
, func_name
, line_no
, lvl
, tag
);
389 /* Write hexadecimal representation */
390 for (i
= 0; i
< mem_len
; i
++) {
391 const uint8_t byte
= mem_data
[i
];
394 append_char_to_msg_buf(&at
, hex_chars
[byte
>> 4]);
395 append_char_to_msg_buf(&at
, hex_chars
[byte
& 0xf]);
398 append_sp_to_msg_buf(&at
);
401 /* Insert spaces to align the following ASCII representation */
402 for (i
= 0; i
< max_mem_line_len
- mem_len
; i
++) {
403 append_sp_to_msg_buf(&at
);
404 append_sp_to_msg_buf(&at
);
405 append_sp_to_msg_buf(&at
);
408 /* Insert a vertical line between the representations */
409 append_str_to_msg_buf(&at
, "| ");
411 /* Write the ASCII representation */
412 for (i
= 0; i
< mem_len
; i
++) {
413 const uint8_t byte
= mem_data
[i
];
416 append_char_to_msg_buf(&at
, (char) byte
);
418 /* Non-printable character */
419 append_char_to_msg_buf(&at
, '.');
423 common_write_fini(&at
);
427 * Logs `mem_len` bytes of `mem_data` on one or more lines.
430 void write_mem_lines(const char * const file_name
, const char * const func_name
,
431 const unsigned int line_no
, const enum bt_log_level lvl
,
432 const char * const tag
, const uint8_t * const mem_data
,
433 const size_t mem_len
)
435 const uint8_t *mem_at
= mem_data
;
436 size_t rem_mem_len
= mem_len
;
438 if (!mem_data
|| mem_len
== 0) {
439 /* Nothing to write */
443 while (rem_mem_len
> 0) {
444 static const size_t max_mem_line_len
= 16;
446 /* Number of bytes to write on this line */
447 const size_t mem_line_len
= rem_mem_len
> max_mem_line_len
?
448 max_mem_line_len
: rem_mem_len
;
450 /* Log those bytes */
451 write_mem_line(file_name
, func_name
, line_no
, lvl
, tag
,
452 mem_at
, mem_line_len
, max_mem_line_len
);
454 /* Adjust for next iteration */
455 rem_mem_len
-= mem_line_len
;
456 mem_at
+= mem_line_len
;
463 void bt_log_write_mem(const char * const file_name
, const char * const func_name
,
464 const unsigned int line_no
, const enum bt_log_level lvl
,
465 const char * const tag
, const void * const mem_data
,
466 const size_t mem_len
, const char * const msg
)
468 bt_log_write(file_name
, func_name
, line_no
, lvl
, tag
, msg
);
469 write_mem_lines(file_name
, func_name
, line_no
, lvl
, tag
,
473 void bt_log_write_mem_printf(const char * const file_name
,
474 const char * const func_name
, const unsigned int line_no
,
475 const enum bt_log_level lvl
, const char * const tag
,
476 const void * const mem_data
, const size_t mem_len
,
477 const char * const fmt
, ...)
482 write_vprintf(file_name
, func_name
, line_no
, lvl
, tag
, fmt
, va
);
485 write_mem_lines(file_name
, func_name
, line_no
, lvl
, tag
,