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