Blender V5.0
clog.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cassert>
10#include <cstdarg>
11#include <cstdint>
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15
16#include <mutex>
17#include <set>
18
19/* Disable for small single threaded programs
20 * to avoid having to link with pthreads. */
21#ifdef WITH_CLOG_PTHREADS
22# include "atomic_ops.h"
23# include <pthread.h>
24#endif
25
26/* For 'isatty' to check for color. */
27#if defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
28# include <sys/time.h>
29# include <unistd.h>
30#endif
31
32#if defined(_MSC_VER)
33# ifndef NOMINMAX
34# define NOMINMAX
35# endif
36# include <Windows.h>
37
38# include <VersionHelpers.h> /* This needs to be included after Windows.h. */
39# include <io.h>
40# if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
41# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
42# endif
43#endif
44
45/* For printing timestamp. */
46#define __STDC_FORMAT_MACROS
47#include <cinttypes>
48
49#include <algorithm>
50
51/* Only other dependency (could use regular malloc too). */
52#include "MEM_guardedalloc.h"
53
54/* own include. */
55#include "CLG_log.h"
56
57/* Local utility defines */
58#define STREQ(a, b) (strcmp(a, b) == 0)
59#define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
60
61#ifdef _WIN32
62# define PATHSEP_CHAR '\\'
63#else
64# define PATHSEP_CHAR '/'
65#endif
66
67/* -------------------------------------------------------------------- */
70
71static std::mutex LOG_MUTEX;
72
76 char match[0];
77};
78
82#ifdef WITH_CLOG_PTHREADS
83 pthread_mutex_t types_lock;
84#endif
85
86 /* exclude, include filters. */
93
95 int output;
97
100
102 struct {
105
106 struct {
107 void (*error_fn)(void *file_handle);
108 void (*fatal_fn)(void *file_handle);
109 void (*backtrace_fn)(void *file_handle);
111};
112
114
115/* -------------------------------------------------------------------- */
118
120{
121 /* Inside a function for correct static initialization order, otherwise
122 * all_refs might get null initialized only after logrefs are registered.*/
123 static CLG_LogRef *all_refs = nullptr;
124 return &all_refs;
125}
126
128
129/* -------------------------------------------------------------------- */
134
135#define CLOG_BUF_LEN_INIT 512
136
143
144static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
145{
146 cstr->data = buf_stack;
147 cstr->len_alloc = buf_stack_len;
148 cstr->len = 0;
149 cstr->is_alloc = false;
150}
151
152static void clg_str_free(CLogStringBuf *cstr)
153{
154 if (cstr->is_alloc) {
155 MEM_freeN(cstr->data);
156 }
157}
158
159static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
160{
161 if (len > cstr->len_alloc) {
162 cstr->len_alloc *= 2;
163 cstr->len_alloc = std::max(len, cstr->len_alloc);
164
165 if (cstr->is_alloc) {
166 cstr->data = static_cast<char *>(MEM_reallocN(cstr->data, cstr->len_alloc));
167 }
168 else {
169 /* Copy the static buffer. */
170 char *data = MEM_malloc_arrayN<char>(cstr->len_alloc, __func__);
171 memcpy(data, cstr->data, cstr->len);
172 cstr->data = data;
173 cstr->is_alloc = true;
174 }
175 }
176}
177
178static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
179{
180 uint len_next = cstr->len + len;
181 clg_str_reserve(cstr, len_next);
182 char *str_dst = cstr->data + cstr->len;
183 memcpy(str_dst, str, len);
184#if 0 /* no need. */
185 str_dst[len] = '\0';
186#endif
187 cstr->len = len_next;
188}
189
190static void clg_str_append(CLogStringBuf *cstr, const char *str)
191{
192 clg_str_append_with_len(cstr, str, strlen(str));
193}
194
195static void clg_str_append_char(CLogStringBuf *cstr, const char c, const uint len)
196{
197 uint len_next = cstr->len + len;
198 clg_str_reserve(cstr, len_next);
199 char *str_dst = cstr->data + cstr->len;
200 memset(str_dst, c, len);
201#if 0 /* no need. */
202 str_dst[len] = '\0';
203#endif
204 cstr->len = len_next;
205}
206
208static void clg_str_vappendf(CLogStringBuf *cstr, const char *format, va_list args)
209{
210 /* Use limit because windows may use '-1' for a formatting error. */
211 const uint len_max = 65535;
212 uint len_avail = cstr->len_alloc - cstr->len;
213 while (true) {
214 va_list args_cpy;
215 va_copy(args_cpy, args);
216 int retval = vsnprintf(cstr->data + cstr->len, len_avail, format, args_cpy);
217 va_end(args_cpy);
218
219 if (retval < 0) {
220 /* Some encoding error happened, not much we can do here, besides skipping/canceling this
221 * message. */
222 break;
223 }
224
225 if ((uint)retval <= len_avail) {
226 /* Copy was successful. */
227 cstr->len += (uint)retval;
228 break;
229 }
230
231 /* `vsnprintf` was not successful, due to lack of allocated space, `retval` contains expected
232 * length of the formatted string, use it to allocate required amount of memory. */
233 uint len_alloc = cstr->len + (uint)retval;
234 if (len_alloc >= len_max) {
235 /* Safe upper-limit, just in case... */
236 break;
237 }
238
240 len_avail = cstr->len_alloc - cstr->len;
241 }
242}
243
244static void clg_str_indent_multiline(CLogStringBuf *cstr, const uint indent_len)
245{
246 /* If there are multiple lines, indent them the same as the first line for readability. */
247 if (indent_len < 2) {
248 return;
249 }
250
251 uint num_newlines = 0;
252 for (uint i = 0; i < cstr->len; i++) {
253 if (cstr->data[i] == '\n') {
254 num_newlines++;
255 }
256 }
257 if (num_newlines == 0) {
258 return;
259 }
260
261 const char *old_data = cstr->data;
262 const uint old_len = cstr->len;
263 const bool old_is_alloc = cstr->is_alloc;
264
265 cstr->len_alloc = cstr->len + (num_newlines * indent_len);
266 cstr->len = 0;
267 cstr->data = MEM_malloc_arrayN<char>(cstr->len_alloc, __func__);
268 cstr->is_alloc = true;
269
270 for (uint i = 0; i < old_len; i++) {
271 cstr->data[cstr->len++] = old_data[i];
272 if (old_data[i] == '\n') {
273 memset(cstr->data + cstr->len, ' ', indent_len);
274 cstr->data[cstr->len + indent_len - 2] = '|';
275 cstr->len += indent_len;
276 }
277 }
278
279 if (old_is_alloc) {
280 MEM_freeN(old_data);
281 }
282}
283
285
286/* -------------------------------------------------------------------- */
289
299#define COLOR_LEN (COLOR_RESET + 1)
300
301static const char *clg_color_table[COLOR_LEN] = {nullptr};
302#ifdef _WIN32
303static DWORD clg_previous_console_mode = 0;
304#endif
305
306static void clg_color_table_init(bool use_color)
307{
308 for (int i = 0; i < COLOR_LEN; i++) {
309 clg_color_table[i] = "";
310 }
311 if (use_color) {
312 clg_color_table[COLOR_DEFAULT] = "\033[1;37m";
313 clg_color_table[COLOR_RED] = "\033[1;31m";
314 clg_color_table[COLOR_GREEN] = "\033[1;32m";
315 clg_color_table[COLOR_YELLOW] = "\033[1;33m";
316 clg_color_table[COLOR_DIM] = "\033[2;37m";
317 clg_color_table[COLOR_RESET] = "\033[0m";
318 }
319}
320
321static const char *clg_level_as_text(enum CLG_Level level)
322{
323 switch (level) {
324 case CLG_LEVEL_FATAL:
325 return "FATAL";
326 case CLG_LEVEL_ERROR:
327 return "ERROR";
328 case CLG_LEVEL_WARN:
329 return "WARNING";
330 case CLG_LEVEL_INFO:
331 return "INFO";
332 case CLG_LEVEL_DEBUG:
333 return "DEBUG";
334 case CLG_LEVEL_TRACE:
335 return "TRACE";
336 }
337
338 return "INVALID_LEVEL";
339}
340
342{
343 switch (level) {
344 case CLG_LEVEL_FATAL:
345 case CLG_LEVEL_ERROR:
346 return COLOR_RED;
347 case CLG_LEVEL_WARN:
348 return COLOR_YELLOW;
349 case CLG_LEVEL_INFO:
350 case CLG_LEVEL_DEBUG:
351 case CLG_LEVEL_TRACE:
352 return COLOR_DEFAULT;
353 }
354 /* should never get here. */
355 assert(false);
356 return COLOR_DEFAULT;
357}
358
360
361/* -------------------------------------------------------------------- */
364
371static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
372{
373 if (ctx->filters[0] == nullptr && ctx->filters[1] == nullptr &&
375 {
376 /* No filters but level specified? Match everything. */
377 return true;
378 }
379
380 const size_t identifier_len = strlen(identifier);
381 for (uint i = 0; i < 2; i++) {
382 const CLG_IDFilter *flt = ctx->filters[i];
383 while (flt != nullptr) {
384 const size_t len = strlen(flt->match);
385 if (STREQ(flt->match, "*") ||
386 ((len <= identifier_len) && (STREQLEN(identifier, flt->match, len))))
387 {
388 return (bool)i;
389 }
390 if (flt->match[0] == '*' && flt->match[len - 1] == '*') {
391 char *match = MEM_calloc_arrayN<char>(len - 1, __func__);
392 memcpy(match, flt->match + 1, len - 2);
393 const bool success = (strstr(identifier, match) != nullptr);
394 MEM_freeN(match);
395 if (success) {
396 return (bool)i;
397 }
398 }
399 else if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
400 /* `foo.*` and `foo.bar.*` support kept for backwards compatibility.
401 * `foo` and `foo.bar` now do the same thing. */
402 if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
403 ((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1)))
404 {
405 return (bool)i;
406 }
407 }
408 flt = flt->next;
409 }
410 }
411 return false;
412}
413
418static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
419{
420 for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
421 if (STREQ(identifier, ty->identifier)) {
422 return ty;
423 }
424 }
425 return nullptr;
426}
427
428static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
429{
430 assert(clg_ctx_type_find_by_name(ctx, identifier) == nullptr);
432 ty->next = ctx->types;
433 ctx->types = ty;
434 strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
435 ty->ctx = ctx;
436
437 if (clg_ctx_filter_check(ctx, ty->identifier)) {
438 ty->level = ctx->default_type.level;
439 }
440 else {
441 ty->level = std::min(ctx->default_type.level, CLG_LEVEL_WARN);
442 }
443
444 return ty;
445}
446
448{
449 if (ctx->callbacks.error_fn != nullptr) {
450 ctx->callbacks.error_fn(ctx->output_file);
451 }
452}
453
455{
456 if (ctx->callbacks.fatal_fn != nullptr) {
457 ctx->callbacks.fatal_fn(ctx->output_file);
458 }
459 fflush(ctx->output_file);
460 abort();
461}
462
464{
465 /* NOTE: we avoid writing to 'FILE', for back-trace we make an exception,
466 * if necessary we could have a version of the callback that writes to file
467 * descriptor all at once. */
469 fflush(ctx->output_file);
470}
471
473{
474 uint64_t tick;
475#if defined(_MSC_VER)
476 tick = GetTickCount64();
477#else
478 struct timeval tv;
479 gettimeofday(&tv, nullptr);
480 tick = tv.tv_sec * 1000 + tv.tv_usec / 1000;
481#endif
482 return tick;
483}
484
486
487/* -------------------------------------------------------------------- */
490
491static void write_timestamp(CLogStringBuf *cstr, const uint64_t timestamp_tick_start)
492{
493 char timestamp_str[128];
494 const uint64_t timestamp = clg_timestamp_ticks_get() - timestamp_tick_start;
495 const int h = int(timestamp / (1000 * 60 * 60));
496 const int m = int((timestamp / (1000 * 60)) % 60);
497 const int s = int((timestamp / 1000) % 60);
498 const int r = int(timestamp % 1000);
499
500 const uint timestamp_len =
501 (h > 0) ?
502 snprintf(timestamp_str, sizeof(timestamp_str), "%.2d:%.2d:%.2d.%.3d ", h, m, s, r) :
503 snprintf(timestamp_str, sizeof(timestamp_str), "%.2d:%.2d.%.3d ", m, s, r);
504
505 clg_str_append_with_len(cstr, timestamp_str, timestamp_len);
506}
507
508static void write_memory(CLogStringBuf *cstr)
509{
510 const uint64_t mem_in_use = MEM_get_memory_in_use() / (1024 * 1024);
511 char memory_str[128];
512 const uint len = snprintf(memory_str, sizeof(memory_str), "%dM", (int)mem_in_use);
513
514 clg_str_append_with_len(cstr, memory_str, len);
515
516 const uint memory_align_width = 5;
517 const uint num_spaces = (len < memory_align_width) ? memory_align_width - len : 0;
518 clg_str_append_char(cstr, ' ', num_spaces + 2);
519}
520
521static void write_level(CLogStringBuf *cstr, enum CLG_Level level, bool use_color)
522{
523 if (level >= CLG_LEVEL_INFO) {
524 return;
525 }
526
527 if (use_color) {
528 enum eCLogColor color = clg_level_to_color(level);
529 clg_str_append(cstr, clg_color_table[color]);
530 clg_str_append(cstr, clg_level_as_text(level));
532 }
533 else {
534 clg_str_append(cstr, clg_level_as_text(level));
535 }
536
537 clg_str_append(cstr, " ");
538}
539
540static void write_type(CLogStringBuf *cstr, const CLG_LogType *lg)
541{
542 const uint len = strlen(lg->identifier);
544
545 const uint type_align_width = 16;
546 const uint num_spaces = (len < type_align_width) ? type_align_width - len : 0;
547 clg_str_append_char(cstr, ' ', num_spaces + 1);
548}
549
551 const char *file_line,
552 const char *fn,
553 const bool use_basename,
554 const bool use_color)
555{
556 if (use_color) {
558 }
559
560 uint file_line_len = strlen(file_line);
561 if (use_basename) {
562 uint file_line_offset = file_line_len;
563 while (file_line_offset-- > 0) {
564 if (file_line[file_line_offset] == PATHSEP_CHAR) {
565 file_line_offset++;
566 break;
567 }
568 }
569 file_line += file_line_offset;
570 file_line_len -= file_line_offset;
571 }
572 clg_str_append_with_len(cstr, file_line, file_line_len);
573
574 clg_str_append(cstr, " ");
575 clg_str_append(cstr, fn);
576
577 if (use_color) {
579 }
580}
581
583 enum CLG_Level level,
584 const char *file_line,
585 const char *fn,
586 const char *message)
587{
588 CLogStringBuf cstr;
589 char cstr_stack_buf[CLOG_BUF_LEN_INIT];
590 clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
591
592 if (lg->ctx->use_timestamp) {
594 }
595 if (lg->ctx->use_memory) {
596 write_memory(&cstr);
597 }
598 write_type(&cstr, lg);
599
600 clg_str_append(&cstr, "| ");
601
602 const uint64_t multiline_indent_len = cstr.len;
603
604 write_level(&cstr, level, lg->ctx->use_color);
605
606 clg_str_append(&cstr, message);
607
608 if (lg->ctx->use_source) {
609 clg_str_append(&cstr, "\n");
610 write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename, lg->ctx->use_color);
611 }
612
613 clg_str_indent_multiline(&cstr, multiline_indent_len);
614
615 clg_str_append(&cstr, "\n");
616
617 /* Output could be optional. */
618 {
619 /* Mutex to avoid garbled output with threads and multi line output. */
620 std::scoped_lock lock(LOG_MUTEX);
621 int bytes_written = write(lg->ctx->output, cstr.data, cstr.len);
622 (void)bytes_written;
623 }
624
625 clg_str_free(&cstr);
626
627 if (lg->ctx->callbacks.backtrace_fn) {
629 }
630
631 if (level == CLG_LEVEL_FATAL) {
633 }
634}
635
636void CLG_logf(const CLG_LogType *lg,
637 enum CLG_Level level,
638 const char *file_line,
639 const char *fn,
640 const char *format,
641 ...)
642{
643 CLogStringBuf cstr;
644 char cstr_stack_buf[CLOG_BUF_LEN_INIT];
645 clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
646
647 if (lg->ctx->use_timestamp) {
649 }
650 if (lg->ctx->use_memory) {
651 write_memory(&cstr);
652 }
653 write_type(&cstr, lg);
654
655 clg_str_append(&cstr, "| ");
656
657 const uint64_t multiline_indent_len = cstr.len;
658
659 write_level(&cstr, level, lg->ctx->use_color);
660
661 {
662 va_list ap;
663 va_start(ap, format);
664 clg_str_vappendf(&cstr, format, ap);
665 va_end(ap);
666 }
667
668 if (lg->ctx->use_source) {
669 clg_str_append(&cstr, "\n");
670 write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename, lg->ctx->use_color);
671 }
672
673 clg_str_indent_multiline(&cstr, multiline_indent_len);
674
675 clg_str_append(&cstr, "\n");
676
677 /* Output could be optional. */
678 {
679 /* Mutex to avoid garbled output with threads and multi line output. */
680 std::scoped_lock lock(LOG_MUTEX);
681 int bytes_written = write(lg->ctx->output, cstr.data, cstr.len);
682 (void)bytes_written;
683 }
684
685 clg_str_free(&cstr);
686
687 if (lg->ctx->callbacks.backtrace_fn) {
689 }
690
691 if (level == CLG_LEVEL_ERROR) {
693 }
694
695 if (level == CLG_LEVEL_FATAL) {
697 }
698}
699
700void CLG_log_raw(const CLG_LogType *lg, const char *message)
701{
702 /* Write raw text without any formatting. */
703 int bytes_written = write(lg->ctx->output, message, strlen(message));
704 (void)bytes_written;
705}
706
708
709/* -------------------------------------------------------------------- */
712
713static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
714{
715 ctx->output_file = static_cast<FILE *>(file_handle);
716 ctx->output = fileno(ctx->output_file);
717#if defined(__unix__) || defined(__APPLE__)
718 ctx->use_color = isatty(ctx->output);
719#elif defined(WIN32)
720 /* As of Windows 10 build 18298 all the standard consoles supports color
721 * like the Linux Terminal do, but it needs to be turned on.
722 * To turn on colors we need to enable virtual terminal processing by passing the flag
723 * ENABLE_VIRTUAL_TERMINAL_PROCESSING into SetConsoleMode.
724 * If the system doesn't support virtual terminal processing it will fail silently and the flag
725 * will not be set. */
726
727 GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &clg_previous_console_mode);
728
729 ctx->use_color = 0;
730 if (IsWindows10OrGreater() && isatty(ctx->output)) {
731 DWORD mode = clg_previous_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
732 if (SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), mode)) {
733 ctx->use_color = 1;
734 }
735 }
736#endif
737}
738
739static void CLG_ctx_output_use_source_set(CLogContext *ctx, int value)
740{
741 ctx->use_source = (bool)value;
742}
743
745{
746 ctx->use_basename = (bool)value;
747}
748
750{
751 ctx->use_timestamp = (bool)value;
752 if (ctx->use_timestamp) {
754 }
755}
756
757static void CLG_ctx_output_use_memory_set(CLogContext *ctx, int value)
758{
759 ctx->use_memory = (bool)value;
760}
761
763static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle))
764{
765 ctx->callbacks.error_fn = error_fn;
766}
767
769static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
770{
771 ctx->callbacks.fatal_fn = fatal_fn;
772}
773
774static void CLG_ctx_backtrace_fn_set(CLogContext *ctx, void (*backtrace_fn)(void *file_handle))
775{
776 ctx->callbacks.backtrace_fn = backtrace_fn;
777}
778
780 const char *type_match,
781 int type_match_len)
782{
783 if (type_match_len == 0) {
784 return;
785 }
786 CLG_IDFilter *flt = static_cast<CLG_IDFilter *>(
787 MEM_callocN(sizeof(*flt) + type_match_len + 1, __func__));
788 flt->next = *flt_list;
789 *flt_list = flt;
790 memcpy(flt->match, type_match, type_match_len);
791 /* no need to null terminate since we calloc'd */
792}
793
795 const char *type_match,
796 int type_match_len)
797{
798 clg_ctx_type_filter_append(&ctx->filters[0], type_match, type_match_len);
799}
800
802 const char *type_match,
803 int type_match_len)
804{
805 clg_ctx_type_filter_append(&ctx->filters[1], type_match, type_match_len);
806 if (ctx->default_type.level <= CLG_LEVEL_WARN) {
808 }
809}
810
812{
813 ctx->default_type.level = level;
814 for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
815 ty->level = level;
816 }
817}
818
820{
821 CLogContext *ctx = MEM_callocN<CLogContext>(__func__);
822#ifdef WITH_CLOG_PTHREADS
823 pthread_mutex_init(&ctx->types_lock, nullptr);
824#endif
826 ctx->use_source = true;
827 CLG_ctx_output_set(ctx, stdout);
828
829 return ctx;
830}
831
832static void CLG_ctx_free(CLogContext *ctx)
833{
834#if defined(WIN32)
835 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), clg_previous_console_mode);
836#endif
837 while (ctx->types != nullptr) {
838 CLG_LogType *item = ctx->types;
839 ctx->types = item->next;
840 MEM_freeN(item);
841 }
842
843 for (CLG_LogRef *ref = *clg_all_refs_p(); ref; ref = ref->next) {
844 ref->type = nullptr;
845 }
846
847 for (uint i = 0; i < 2; i++) {
848 while (ctx->filters[i] != nullptr) {
849 CLG_IDFilter *item = ctx->filters[i];
850 ctx->filters[i] = item->next;
851 MEM_freeN(item);
852 }
853 }
854#ifdef WITH_CLOG_PTHREADS
855 pthread_mutex_destroy(&ctx->types_lock);
856#endif
857 MEM_freeN(ctx);
858}
859
861
862/* -------------------------------------------------------------------- */
867
868/* We could support multiple at once, for now this seems not needed. */
869static struct CLogContext *g_ctx = nullptr;
870/* Separate to preserve this after freeing context. */
871static bool g_quiet = false;
872
874{
876
877 clg_color_table_init(g_ctx->use_color);
878}
879
881{
883}
884
885void CLG_output_set(void *file_handle)
886{
887 CLG_ctx_output_set(g_ctx, file_handle);
888}
889
891{
893}
894
899
904
906{
908}
909
910void CLG_error_fn_set(void (*error_fn)(void *file_handle))
911{
913}
914
915void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
916{
918}
919
920void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle))
921{
923}
924
925void CLG_type_filter_exclude(const char *type_match, int type_match_len)
926{
927 CLG_ctx_type_filter_exclude(g_ctx, type_match, type_match_len);
928}
929
930void CLG_type_filter_include(const char *type_match, int type_match_len)
931{
932 CLG_ctx_type_filter_include(g_ctx, type_match, type_match_len);
933}
934
939
940void CLG_quiet_set(bool quiet)
941{
942 g_quiet = quiet;
943}
944
946{
947 return g_quiet;
948}
949
951
952/* -------------------------------------------------------------------- */
957
959{
960 /* Add to global list of refs, both for setting the type to null on CLG_exit()
961 * and so CLG_logref_list_all can be used to print all categories. */
962 static std::mutex mutex;
963 std::scoped_lock lock(mutex);
964 CLG_LogRef **all_refs = clg_all_refs_p();
965 clg_ref->next = *all_refs;
966 *all_refs = clg_ref;
967}
968
969void CLG_logref_list_all(void (*callback)(const char *identifier, void *user_data),
970 void *user_data)
971{
972 /* Generate sorted list of unique identifiers. */
973 auto cmp = [](const char *a, const char *b) { return std::strcmp(a, b) < 0; };
974 std::set<const char *, decltype(cmp)> identifiers(cmp);
975 for (CLG_LogRef *ref = *clg_all_refs_p(); ref; ref = ref->next) {
976 identifiers.insert(ref->identifier);
977 }
978
979 for (const char *identifier : identifiers) {
980 callback(identifier, user_data);
981 }
982}
983
985{
986#ifdef WITH_CLOG_PTHREADS
987 /* Only runs once when initializing a static type in most cases. */
988 pthread_mutex_lock(&g_ctx->types_lock);
989#endif
990 if (clg_ref->type == nullptr) {
992 if (clg_ty == nullptr) {
993 clg_ty = clg_ctx_type_register(g_ctx, clg_ref->identifier);
994 }
995#ifdef WITH_CLOG_PTHREADS
996 atomic_cas_ptr((void **)&clg_ref->type, clg_ref->type, clg_ty);
997#else
998 clg_ref->type = clg_ty;
999#endif
1000 }
1001#ifdef WITH_CLOG_PTHREADS
1002 pthread_mutex_unlock(&g_ctx->types_lock);
1003#endif
1004}
1005
1007{
1008 if (clg_ref->type == nullptr) {
1009 CLG_logref_init(clg_ref);
1010 }
1011 return clg_ref->type->ctx->use_color;
1012}
1013
#define ATTR_PRINTF_FORMAT(format_param, dots_param)
unsigned int uint
#define STREQLEN(a, b, n)
CLG_Level
Definition CLG_log.h:52
@ CLG_LEVEL_ERROR
Definition CLG_log.h:56
@ CLG_LEVEL_DEBUG
Definition CLG_log.h:62
@ CLG_LEVEL_INFO
Definition CLG_log.h:60
@ CLG_LEVEL_FATAL
Definition CLG_log.h:54
@ CLG_LEVEL_WARN
Definition CLG_log.h:58
@ CLG_LEVEL_TRACE
Definition CLG_log.h:64
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE void * atomic_cas_ptr(void **v, void *old, void *_new)
volatile int lock
BMesh const char void * data
unsigned long long int uint64_t
static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
Definition clog.cc:713
static void write_type(CLogStringBuf *cstr, const CLG_LogType *lg)
Definition clog.cc:540
static uint64_t clg_timestamp_ticks_get()
Definition clog.cc:472
static void clg_str_append_char(CLogStringBuf *cstr, const char c, const uint len)
Definition clog.cc:195
void CLG_type_filter_exclude(const char *type_match, int type_match_len)
Definition clog.cc:925
static void clg_str_vappendf(CLogStringBuf *cstr, const char *format, va_list args)
Definition clog.cc:208
static void clg_ctx_fatal_action(CLogContext *ctx)
Definition clog.cc:454
bool CLG_quiet_get()
Definition clog.cc:945
void CLG_output_set(void *file_handle)
Definition clog.cc:885
static void CLG_ctx_free(CLogContext *ctx)
Definition clog.cc:832
void CLG_output_use_basename_set(int value)
Definition clog.cc:895
void CLG_exit()
Definition clog.cc:880
void CLG_output_use_memory_set(int value)
Definition clog.cc:905
void CLG_log_raw(const CLG_LogType *lg, const char *message)
Definition clog.cc:700
void CLG_error_fn_set(void(*error_fn)(void *file_handle))
Definition clog.cc:910
void CLG_level_set(CLG_Level level)
Definition clog.cc:935
void CLG_output_use_source_set(int value)
Definition clog.cc:890
static void clg_ctx_error_action(CLogContext *ctx)
Definition clog.cc:447
static CLG_LogType * clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
Definition clog.cc:418
void CLG_backtrace_fn_set(void(*fatal_fn)(void *file_handle))
Definition clog.cc:920
static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value)
Definition clog.cc:749
static std::mutex LOG_MUTEX
Definition clog.cc:71
static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
Definition clog.cc:178
static void write_memory(CLogStringBuf *cstr)
Definition clog.cc:508
void CLG_log_str(const CLG_LogType *lg, enum CLG_Level level, const char *file_line, const char *fn, const char *message)
Definition clog.cc:582
static CLogContext * CLG_ctx_init()
Definition clog.cc:819
static void write_timestamp(CLogStringBuf *cstr, const uint64_t timestamp_tick_start)
Definition clog.cc:491
static void CLG_ctx_output_use_source_set(CLogContext *ctx, int value)
Definition clog.cc:739
#define CLOG_BUF_LEN_INIT
Definition clog.cc:135
void CLG_logf(const CLG_LogType *lg, enum CLG_Level level, const char *file_line, const char *fn, const char *format,...)
Definition clog.cc:636
#define STREQLEN(a, b, n)
Definition clog.cc:59
void CLG_fatal_fn_set(void(*fatal_fn)(void *file_handle))
Definition clog.cc:915
static void clg_str_append(CLogStringBuf *cstr, const char *str)
Definition clog.cc:190
void CLG_logref_list_all(void(*callback)(const char *identifier, void *user_data), void *user_data)
Definition clog.cc:969
void CLG_logref_register(CLG_LogRef *clg_ref)
Definition clog.cc:958
static void CLG_ctx_output_use_memory_set(CLogContext *ctx, int value)
Definition clog.cc:757
static const char * clg_color_table[COLOR_LEN]
Definition clog.cc:301
static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void(*fatal_fn)(void *file_handle))
Definition clog.cc:769
static bool g_quiet
Definition clog.cc:871
void CLG_type_filter_include(const char *type_match, int type_match_len)
Definition clog.cc:930
void CLG_logref_init(CLG_LogRef *clg_ref)
Definition clog.cc:984
static void clg_ctx_type_filter_append(CLG_IDFilter **flt_list, const char *type_match, int type_match_len)
Definition clog.cc:779
static struct CLogContext * g_ctx
Definition clog.cc:869
static void clg_str_free(CLogStringBuf *cstr)
Definition clog.cc:152
static void write_level(CLogStringBuf *cstr, enum CLG_Level level, bool use_color)
Definition clog.cc:521
void CLG_output_use_timestamp_set(int value)
Definition clog.cc:900
static void CLG_ctx_output_use_basename_set(CLogContext *ctx, int value)
Definition clog.cc:744
static CLG_LogType * clg_ctx_type_register(CLogContext *ctx, const char *identifier)
Definition clog.cc:428
static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
Definition clog.cc:144
static void clg_ctx_backtrace(CLogContext *ctx)
Definition clog.cc:463
static void CLG_ctx_backtrace_fn_set(CLogContext *ctx, void(*backtrace_fn)(void *file_handle))
Definition clog.cc:774
static CLG_LogRef ** clg_all_refs_p()
Definition clog.cc:119
#define COLOR_LEN
Definition clog.cc:299
static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
Definition clog.cc:159
void CLG_quiet_set(bool quiet)
Definition clog.cc:940
static void CLG_ctx_level_set(CLogContext *ctx, CLG_Level level)
Definition clog.cc:811
static void CLG_ctx_type_filter_exclude(CLogContext *ctx, const char *type_match, int type_match_len)
Definition clog.cc:794
static void clg_str_indent_multiline(CLogStringBuf *cstr, const uint indent_len)
Definition clog.cc:244
eCLogColor
Definition clog.cc:290
@ COLOR_DEFAULT
Definition clog.cc:291
@ COLOR_DIM
Definition clog.cc:295
@ COLOR_RED
Definition clog.cc:292
@ COLOR_YELLOW
Definition clog.cc:294
@ COLOR_GREEN
Definition clog.cc:293
@ COLOR_RESET
Definition clog.cc:297
static void clg_color_table_init(bool use_color)
Definition clog.cc:306
static const char * clg_level_as_text(enum CLG_Level level)
Definition clog.cc:321
static void write_file_line_fn(CLogStringBuf *cstr, const char *file_line, const char *fn, const bool use_basename, const bool use_color)
Definition clog.cc:550
int CLG_color_support_get(CLG_LogRef *clg_ref)
Definition clog.cc:1006
static enum eCLogColor clg_level_to_color(enum CLG_Level level)
Definition clog.cc:341
static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
Definition clog.cc:371
void CLG_init()
Definition clog.cc:873
#define STREQ(a, b)
Definition clog.cc:58
static void CLG_ctx_type_filter_include(CLogContext *ctx, const char *type_match, int type_match_len)
Definition clog.cc:801
static void CLT_ctx_error_fn_set(CLogContext *ctx, void(*error_fn)(void *file_handle))
Definition clog.cc:763
#define str(s)
ThreadMutex mutex
#define assert(assertion)
format
#define PATHSEP_CHAR
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
size_t(* MEM_get_memory_in_use)(void)
Definition mallocn.cc:70
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static size_t mem_in_use
char match[0]
Definition clog.cc:76
struct CLG_IDFilter * next
Definition clog.cc:74
const char * identifier
Definition CLG_log.h:81
struct CLG_LogRef * next
Definition CLG_log.h:83
CLG_LogType * type
Definition CLG_log.h:82
char identifier[64]
Definition CLG_log.h:71
CLG_Level level
Definition CLG_log.h:75
struct CLG_LogType * next
Definition CLG_log.h:70
struct CLogContext * ctx
Definition CLG_log.h:73
struct CLogContext::@150175052047236141236166345214312336306070316061 default_type
bool use_basename
Definition clog.cc:90
void(* fatal_fn)(void *file_handle)
Definition clog.cc:108
void(* error_fn)(void *file_handle)
Definition clog.cc:107
bool use_source
Definition clog.cc:89
bool use_timestamp
Definition clog.cc:91
uint64_t timestamp_tick_start
Definition clog.cc:99
FILE * output_file
Definition clog.cc:96
int output
Definition clog.cc:95
void(* backtrace_fn)(void *file_handle)
Definition clog.cc:109
bool use_memory
Definition clog.cc:92
bool use_color
Definition clog.cc:88
CLG_IDFilter * filters[2]
Definition clog.cc:87
CLG_LogType * types
Definition clog.cc:81
struct CLogContext::@363215326307130321371241047232307372132122233336 callbacks
CLG_Level level
Definition clog.cc:103
bool is_alloc
Definition clog.cc:141
char * data
Definition clog.cc:138
uint len_alloc
Definition clog.cc:140
i
Definition text_draw.cc:230
uint len_alloc
uint len