1 #ifndef JEMALLOC_INTERNAL_LOG_H 2 #define JEMALLOC_INTERNAL_LOG_H 3 4 #include "jemalloc/internal/atomic.h" 5 #include "jemalloc/internal/malloc_io.h" 6 #include "jemalloc/internal/mutex.h" 7 8 #ifdef JEMALLOC_LOG 9 # define JEMALLOC_LOG_VAR_BUFSIZE 1000 10 #else 11 # define JEMALLOC_LOG_VAR_BUFSIZE 1 12 #endif 13 14 #define JEMALLOC_LOG_BUFSIZE 4096 15 16 /* 17 * The log malloc_conf option is a '|'-delimited list of log_var name segments 18 * which should be logged. The names are themselves hierarchical, with '.' as 19 * the delimiter (a "segment" is just a prefix in the log namespace). So, if 20 * you have: 21 * 22 * log("arena", "log msg for arena"); // 1 23 * log("arena.a", "log msg for arena.a"); // 2 24 * log("arena.b", "log msg for arena.b"); // 3 25 * log("arena.a.a", "log msg for arena.a.a"); // 4 26 * log("extent.a", "log msg for extent.a"); // 5 27 * log("extent.b", "log msg for extent.b"); // 6 28 * 29 * And your malloc_conf option is "log=arena.a|extent", then lines 2, 4, 5, and 30 * 6 will print at runtime. You can enable logging from all log vars by 31 * writing "log=.". 32 * 33 * None of this should be regarded as a stable API for right now. It's intended 34 * as a debugging interface, to let us keep around some of our printf-debugging 35 * statements. 36 */ 37 38 extern char log_var_names[JEMALLOC_LOG_VAR_BUFSIZE]; 39 extern atomic_b_t log_init_done; 40 41 typedef struct log_var_s log_var_t; 42 struct log_var_s { 43 /* 44 * Lowest bit is "inited", second lowest is "enabled". Putting them in 45 * a single word lets us avoid any fences on weak architectures. 46 */ 47 atomic_u_t state; 48 const char *name; 49 }; 50 51 #define LOG_NOT_INITIALIZED 0U 52 #define LOG_INITIALIZED_NOT_ENABLED 1U 53 #define LOG_ENABLED 2U 54 55 #define LOG_VAR_INIT(name_str) {ATOMIC_INIT(LOG_NOT_INITIALIZED), name_str} 56 57 /* 58 * Returns the value we should assume for state (which is not necessarily 59 * accurate; if logging is done before logging has finished initializing, then 60 * we default to doing the safe thing by logging everything). 61 */ 62 unsigned log_var_update_state(log_var_t *log_var); 63 64 /* We factor out the metadata management to allow us to test more easily. */ 65 #define log_do_begin(log_var) \ 66 if (config_log) { \ 67 unsigned log_state = atomic_load_u(&(log_var).state, \ 68 ATOMIC_RELAXED); \ 69 if (unlikely(log_state == LOG_NOT_INITIALIZED)) { \ 70 log_state = log_var_update_state(&(log_var)); \ 71 assert(log_state != LOG_NOT_INITIALIZED); \ 72 } \ 73 if (log_state == LOG_ENABLED) { \ 74 { 75 /* User code executes here. */ 76 #define log_do_end(log_var) \ 77 } \ 78 } \ 79 } 80 81 /* 82 * MSVC has some preprocessor bugs in its expansion of __VA_ARGS__ during 83 * preprocessing. To work around this, we take all potential extra arguments in 84 * a var-args functions. Since a varargs macro needs at least one argument in 85 * the "...", we accept the format string there, and require that the first 86 * argument in this "..." is a const char *. 87 */ 88 static inline void 89 log_impl_varargs(const char *name, ...) { 90 char buf[JEMALLOC_LOG_BUFSIZE]; 91 va_list ap; 92 93 va_start(ap, name); 94 const char *format = va_arg(ap, const char *); 95 size_t dst_offset = 0; 96 dst_offset += malloc_snprintf(buf, JEMALLOC_LOG_BUFSIZE, "%s: ", name); 97 dst_offset += malloc_vsnprintf(buf + dst_offset, 98 JEMALLOC_LOG_BUFSIZE - dst_offset, format, ap); 99 dst_offset += malloc_snprintf(buf + dst_offset, 100 JEMALLOC_LOG_BUFSIZE - dst_offset, "\n"); 101 va_end(ap); 102 103 malloc_write(buf); 104 } 105 106 /* Call as log("log.var.str", "format_string %d", arg_for_format_string); */ 107 #define LOG(log_var_str, ...) \ 108 do { \ 109 static log_var_t log_var = LOG_VAR_INIT(log_var_str); \ 110 log_do_begin(log_var) \ 111 log_impl_varargs((log_var).name, __VA_ARGS__); \ 112 log_do_end(log_var) \ 113 } while (0) 114 115 #endif /* JEMALLOC_INTERNAL_LOG_H */ 116