1*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h" 2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h" 3*c43cad87SWarner Losh 4*c43cad87SWarner Losh #include "jemalloc/internal/assert.h" 5*c43cad87SWarner Losh #include "jemalloc/internal/buf_writer.h" 6*c43cad87SWarner Losh #include "jemalloc/internal/ckh.h" 7*c43cad87SWarner Losh #include "jemalloc/internal/emitter.h" 8*c43cad87SWarner Losh #include "jemalloc/internal/hash.h" 9*c43cad87SWarner Losh #include "jemalloc/internal/malloc_io.h" 10*c43cad87SWarner Losh #include "jemalloc/internal/mutex.h" 11*c43cad87SWarner Losh #include "jemalloc/internal/prof_data.h" 12*c43cad87SWarner Losh #include "jemalloc/internal/prof_log.h" 13*c43cad87SWarner Losh #include "jemalloc/internal/prof_sys.h" 14*c43cad87SWarner Losh 15*c43cad87SWarner Losh bool opt_prof_log = false; 16*c43cad87SWarner Losh typedef enum prof_logging_state_e prof_logging_state_t; 17*c43cad87SWarner Losh enum prof_logging_state_e { 18*c43cad87SWarner Losh prof_logging_state_stopped, 19*c43cad87SWarner Losh prof_logging_state_started, 20*c43cad87SWarner Losh prof_logging_state_dumping 21*c43cad87SWarner Losh }; 22*c43cad87SWarner Losh 23*c43cad87SWarner Losh /* 24*c43cad87SWarner Losh * - stopped: log_start never called, or previous log_stop has completed. 25*c43cad87SWarner Losh * - started: log_start called, log_stop not called yet. Allocations are logged. 26*c43cad87SWarner Losh * - dumping: log_stop called but not finished; samples are not logged anymore. 27*c43cad87SWarner Losh */ 28*c43cad87SWarner Losh prof_logging_state_t prof_logging_state = prof_logging_state_stopped; 29*c43cad87SWarner Losh 30*c43cad87SWarner Losh /* Used in unit tests. */ 31*c43cad87SWarner Losh static bool prof_log_dummy = false; 32*c43cad87SWarner Losh 33*c43cad87SWarner Losh /* Incremented for every log file that is output. */ 34*c43cad87SWarner Losh static uint64_t log_seq = 0; 35*c43cad87SWarner Losh static char log_filename[ 36*c43cad87SWarner Losh /* Minimize memory bloat for non-prof builds. */ 37*c43cad87SWarner Losh #ifdef JEMALLOC_PROF 38*c43cad87SWarner Losh PATH_MAX + 39*c43cad87SWarner Losh #endif 40*c43cad87SWarner Losh 1]; 41*c43cad87SWarner Losh 42*c43cad87SWarner Losh /* Timestamp for most recent call to log_start(). */ 43*c43cad87SWarner Losh static nstime_t log_start_timestamp; 44*c43cad87SWarner Losh 45*c43cad87SWarner Losh /* Increment these when adding to the log_bt and log_thr linked lists. */ 46*c43cad87SWarner Losh static size_t log_bt_index = 0; 47*c43cad87SWarner Losh static size_t log_thr_index = 0; 48*c43cad87SWarner Losh 49*c43cad87SWarner Losh /* Linked list node definitions. These are only used in this file. */ 50*c43cad87SWarner Losh typedef struct prof_bt_node_s prof_bt_node_t; 51*c43cad87SWarner Losh 52*c43cad87SWarner Losh struct prof_bt_node_s { 53*c43cad87SWarner Losh prof_bt_node_t *next; 54*c43cad87SWarner Losh size_t index; 55*c43cad87SWarner Losh prof_bt_t bt; 56*c43cad87SWarner Losh /* Variable size backtrace vector pointed to by bt. */ 57*c43cad87SWarner Losh void *vec[1]; 58*c43cad87SWarner Losh }; 59*c43cad87SWarner Losh 60*c43cad87SWarner Losh typedef struct prof_thr_node_s prof_thr_node_t; 61*c43cad87SWarner Losh 62*c43cad87SWarner Losh struct prof_thr_node_s { 63*c43cad87SWarner Losh prof_thr_node_t *next; 64*c43cad87SWarner Losh size_t index; 65*c43cad87SWarner Losh uint64_t thr_uid; 66*c43cad87SWarner Losh /* Variable size based on thr_name_sz. */ 67*c43cad87SWarner Losh char name[1]; 68*c43cad87SWarner Losh }; 69*c43cad87SWarner Losh 70*c43cad87SWarner Losh typedef struct prof_alloc_node_s prof_alloc_node_t; 71*c43cad87SWarner Losh 72*c43cad87SWarner Losh /* This is output when logging sampled allocations. */ 73*c43cad87SWarner Losh struct prof_alloc_node_s { 74*c43cad87SWarner Losh prof_alloc_node_t *next; 75*c43cad87SWarner Losh /* Indices into an array of thread data. */ 76*c43cad87SWarner Losh size_t alloc_thr_ind; 77*c43cad87SWarner Losh size_t free_thr_ind; 78*c43cad87SWarner Losh 79*c43cad87SWarner Losh /* Indices into an array of backtraces. */ 80*c43cad87SWarner Losh size_t alloc_bt_ind; 81*c43cad87SWarner Losh size_t free_bt_ind; 82*c43cad87SWarner Losh 83*c43cad87SWarner Losh uint64_t alloc_time_ns; 84*c43cad87SWarner Losh uint64_t free_time_ns; 85*c43cad87SWarner Losh 86*c43cad87SWarner Losh size_t usize; 87*c43cad87SWarner Losh }; 88*c43cad87SWarner Losh 89*c43cad87SWarner Losh /* 90*c43cad87SWarner Losh * Created on the first call to prof_try_log and deleted on prof_log_stop. 91*c43cad87SWarner Losh * These are the backtraces and threads that have already been logged by an 92*c43cad87SWarner Losh * allocation. 93*c43cad87SWarner Losh */ 94*c43cad87SWarner Losh static bool log_tables_initialized = false; 95*c43cad87SWarner Losh static ckh_t log_bt_node_set; 96*c43cad87SWarner Losh static ckh_t log_thr_node_set; 97*c43cad87SWarner Losh 98*c43cad87SWarner Losh /* Store linked lists for logged data. */ 99*c43cad87SWarner Losh static prof_bt_node_t *log_bt_first = NULL; 100*c43cad87SWarner Losh static prof_bt_node_t *log_bt_last = NULL; 101*c43cad87SWarner Losh static prof_thr_node_t *log_thr_first = NULL; 102*c43cad87SWarner Losh static prof_thr_node_t *log_thr_last = NULL; 103*c43cad87SWarner Losh static prof_alloc_node_t *log_alloc_first = NULL; 104*c43cad87SWarner Losh static prof_alloc_node_t *log_alloc_last = NULL; 105*c43cad87SWarner Losh 106*c43cad87SWarner Losh /* Protects the prof_logging_state and any log_{...} variable. */ 107*c43cad87SWarner Losh malloc_mutex_t log_mtx; 108*c43cad87SWarner Losh 109*c43cad87SWarner Losh /******************************************************************************/ 110*c43cad87SWarner Losh /* 111*c43cad87SWarner Losh * Function prototypes for static functions that are referenced prior to 112*c43cad87SWarner Losh * definition. 113*c43cad87SWarner Losh */ 114*c43cad87SWarner Losh 115*c43cad87SWarner Losh /* Hashtable functions for log_bt_node_set and log_thr_node_set. */ 116*c43cad87SWarner Losh static void prof_thr_node_hash(const void *key, size_t r_hash[2]); 117*c43cad87SWarner Losh static bool prof_thr_node_keycomp(const void *k1, const void *k2); 118*c43cad87SWarner Losh static void prof_bt_node_hash(const void *key, size_t r_hash[2]); 119*c43cad87SWarner Losh static bool prof_bt_node_keycomp(const void *k1, const void *k2); 120*c43cad87SWarner Losh 121*c43cad87SWarner Losh /******************************************************************************/ 122*c43cad87SWarner Losh 123*c43cad87SWarner Losh static size_t 124*c43cad87SWarner Losh prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) { 125*c43cad87SWarner Losh assert(prof_logging_state == prof_logging_state_started); 126*c43cad87SWarner Losh malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx); 127*c43cad87SWarner Losh 128*c43cad87SWarner Losh prof_bt_node_t dummy_node; 129*c43cad87SWarner Losh dummy_node.bt = *bt; 130*c43cad87SWarner Losh prof_bt_node_t *node; 131*c43cad87SWarner Losh 132*c43cad87SWarner Losh /* See if this backtrace is already cached in the table. */ 133*c43cad87SWarner Losh if (ckh_search(&log_bt_node_set, (void *)(&dummy_node), 134*c43cad87SWarner Losh (void **)(&node), NULL)) { 135*c43cad87SWarner Losh size_t sz = offsetof(prof_bt_node_t, vec) + 136*c43cad87SWarner Losh (bt->len * sizeof(void *)); 137*c43cad87SWarner Losh prof_bt_node_t *new_node = (prof_bt_node_t *) 138*c43cad87SWarner Losh iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, 139*c43cad87SWarner Losh true, arena_get(TSDN_NULL, 0, true), true); 140*c43cad87SWarner Losh if (log_bt_first == NULL) { 141*c43cad87SWarner Losh log_bt_first = new_node; 142*c43cad87SWarner Losh log_bt_last = new_node; 143*c43cad87SWarner Losh } else { 144*c43cad87SWarner Losh log_bt_last->next = new_node; 145*c43cad87SWarner Losh log_bt_last = new_node; 146*c43cad87SWarner Losh } 147*c43cad87SWarner Losh 148*c43cad87SWarner Losh new_node->next = NULL; 149*c43cad87SWarner Losh new_node->index = log_bt_index; 150*c43cad87SWarner Losh /* 151*c43cad87SWarner Losh * Copy the backtrace: bt is inside a tdata or gctx, which 152*c43cad87SWarner Losh * might die before prof_log_stop is called. 153*c43cad87SWarner Losh */ 154*c43cad87SWarner Losh new_node->bt.len = bt->len; 155*c43cad87SWarner Losh memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *)); 156*c43cad87SWarner Losh new_node->bt.vec = new_node->vec; 157*c43cad87SWarner Losh 158*c43cad87SWarner Losh log_bt_index++; 159*c43cad87SWarner Losh ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL); 160*c43cad87SWarner Losh return new_node->index; 161*c43cad87SWarner Losh } else { 162*c43cad87SWarner Losh return node->index; 163*c43cad87SWarner Losh } 164*c43cad87SWarner Losh } 165*c43cad87SWarner Losh 166*c43cad87SWarner Losh static size_t 167*c43cad87SWarner Losh prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) { 168*c43cad87SWarner Losh assert(prof_logging_state == prof_logging_state_started); 169*c43cad87SWarner Losh malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx); 170*c43cad87SWarner Losh 171*c43cad87SWarner Losh prof_thr_node_t dummy_node; 172*c43cad87SWarner Losh dummy_node.thr_uid = thr_uid; 173*c43cad87SWarner Losh prof_thr_node_t *node; 174*c43cad87SWarner Losh 175*c43cad87SWarner Losh /* See if this thread is already cached in the table. */ 176*c43cad87SWarner Losh if (ckh_search(&log_thr_node_set, (void *)(&dummy_node), 177*c43cad87SWarner Losh (void **)(&node), NULL)) { 178*c43cad87SWarner Losh size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1; 179*c43cad87SWarner Losh prof_thr_node_t *new_node = (prof_thr_node_t *) 180*c43cad87SWarner Losh iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, 181*c43cad87SWarner Losh true, arena_get(TSDN_NULL, 0, true), true); 182*c43cad87SWarner Losh if (log_thr_first == NULL) { 183*c43cad87SWarner Losh log_thr_first = new_node; 184*c43cad87SWarner Losh log_thr_last = new_node; 185*c43cad87SWarner Losh } else { 186*c43cad87SWarner Losh log_thr_last->next = new_node; 187*c43cad87SWarner Losh log_thr_last = new_node; 188*c43cad87SWarner Losh } 189*c43cad87SWarner Losh 190*c43cad87SWarner Losh new_node->next = NULL; 191*c43cad87SWarner Losh new_node->index = log_thr_index; 192*c43cad87SWarner Losh new_node->thr_uid = thr_uid; 193*c43cad87SWarner Losh strcpy(new_node->name, name); 194*c43cad87SWarner Losh 195*c43cad87SWarner Losh log_thr_index++; 196*c43cad87SWarner Losh ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL); 197*c43cad87SWarner Losh return new_node->index; 198*c43cad87SWarner Losh } else { 199*c43cad87SWarner Losh return node->index; 200*c43cad87SWarner Losh } 201*c43cad87SWarner Losh } 202*c43cad87SWarner Losh 203*c43cad87SWarner Losh JEMALLOC_COLD 204*c43cad87SWarner Losh void 205*c43cad87SWarner Losh prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info) { 206*c43cad87SWarner Losh cassert(config_prof); 207*c43cad87SWarner Losh prof_tctx_t *tctx = prof_info->alloc_tctx; 208*c43cad87SWarner Losh malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock); 209*c43cad87SWarner Losh 210*c43cad87SWarner Losh prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false); 211*c43cad87SWarner Losh if (cons_tdata == NULL) { 212*c43cad87SWarner Losh /* 213*c43cad87SWarner Losh * We decide not to log these allocations. cons_tdata will be 214*c43cad87SWarner Losh * NULL only when the current thread is in a weird state (e.g. 215*c43cad87SWarner Losh * it's being destroyed). 216*c43cad87SWarner Losh */ 217*c43cad87SWarner Losh return; 218*c43cad87SWarner Losh } 219*c43cad87SWarner Losh 220*c43cad87SWarner Losh malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx); 221*c43cad87SWarner Losh 222*c43cad87SWarner Losh if (prof_logging_state != prof_logging_state_started) { 223*c43cad87SWarner Losh goto label_done; 224*c43cad87SWarner Losh } 225*c43cad87SWarner Losh 226*c43cad87SWarner Losh if (!log_tables_initialized) { 227*c43cad87SWarner Losh bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS, 228*c43cad87SWarner Losh prof_bt_node_hash, prof_bt_node_keycomp); 229*c43cad87SWarner Losh bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS, 230*c43cad87SWarner Losh prof_thr_node_hash, prof_thr_node_keycomp); 231*c43cad87SWarner Losh if (err1 || err2) { 232*c43cad87SWarner Losh goto label_done; 233*c43cad87SWarner Losh } 234*c43cad87SWarner Losh log_tables_initialized = true; 235*c43cad87SWarner Losh } 236*c43cad87SWarner Losh 237*c43cad87SWarner Losh nstime_t alloc_time = prof_info->alloc_time; 238*c43cad87SWarner Losh nstime_t free_time; 239*c43cad87SWarner Losh nstime_prof_init_update(&free_time); 240*c43cad87SWarner Losh 241*c43cad87SWarner Losh size_t sz = sizeof(prof_alloc_node_t); 242*c43cad87SWarner Losh prof_alloc_node_t *new_node = (prof_alloc_node_t *) 243*c43cad87SWarner Losh iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true, 244*c43cad87SWarner Losh arena_get(TSDN_NULL, 0, true), true); 245*c43cad87SWarner Losh 246*c43cad87SWarner Losh const char *prod_thr_name = (tctx->tdata->thread_name == NULL)? 247*c43cad87SWarner Losh "" : tctx->tdata->thread_name; 248*c43cad87SWarner Losh const char *cons_thr_name = prof_thread_name_get(tsd); 249*c43cad87SWarner Losh 250*c43cad87SWarner Losh prof_bt_t bt; 251*c43cad87SWarner Losh /* Initialize the backtrace, using the buffer in tdata to store it. */ 252*c43cad87SWarner Losh bt_init(&bt, cons_tdata->vec); 253*c43cad87SWarner Losh prof_backtrace(tsd, &bt); 254*c43cad87SWarner Losh prof_bt_t *cons_bt = &bt; 255*c43cad87SWarner Losh 256*c43cad87SWarner Losh /* We haven't destroyed tctx yet, so gctx should be good to read. */ 257*c43cad87SWarner Losh prof_bt_t *prod_bt = &tctx->gctx->bt; 258*c43cad87SWarner Losh 259*c43cad87SWarner Losh new_node->next = NULL; 260*c43cad87SWarner Losh new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid, 261*c43cad87SWarner Losh prod_thr_name); 262*c43cad87SWarner Losh new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid, 263*c43cad87SWarner Losh cons_thr_name); 264*c43cad87SWarner Losh new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt); 265*c43cad87SWarner Losh new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt); 266*c43cad87SWarner Losh new_node->alloc_time_ns = nstime_ns(&alloc_time); 267*c43cad87SWarner Losh new_node->free_time_ns = nstime_ns(&free_time); 268*c43cad87SWarner Losh new_node->usize = usize; 269*c43cad87SWarner Losh 270*c43cad87SWarner Losh if (log_alloc_first == NULL) { 271*c43cad87SWarner Losh log_alloc_first = new_node; 272*c43cad87SWarner Losh log_alloc_last = new_node; 273*c43cad87SWarner Losh } else { 274*c43cad87SWarner Losh log_alloc_last->next = new_node; 275*c43cad87SWarner Losh log_alloc_last = new_node; 276*c43cad87SWarner Losh } 277*c43cad87SWarner Losh 278*c43cad87SWarner Losh label_done: 279*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx); 280*c43cad87SWarner Losh } 281*c43cad87SWarner Losh 282*c43cad87SWarner Losh static void 283*c43cad87SWarner Losh prof_bt_node_hash(const void *key, size_t r_hash[2]) { 284*c43cad87SWarner Losh const prof_bt_node_t *bt_node = (prof_bt_node_t *)key; 285*c43cad87SWarner Losh prof_bt_hash((void *)(&bt_node->bt), r_hash); 286*c43cad87SWarner Losh } 287*c43cad87SWarner Losh 288*c43cad87SWarner Losh static bool 289*c43cad87SWarner Losh prof_bt_node_keycomp(const void *k1, const void *k2) { 290*c43cad87SWarner Losh const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1; 291*c43cad87SWarner Losh const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2; 292*c43cad87SWarner Losh return prof_bt_keycomp((void *)(&bt_node1->bt), 293*c43cad87SWarner Losh (void *)(&bt_node2->bt)); 294*c43cad87SWarner Losh } 295*c43cad87SWarner Losh 296*c43cad87SWarner Losh static void 297*c43cad87SWarner Losh prof_thr_node_hash(const void *key, size_t r_hash[2]) { 298*c43cad87SWarner Losh const prof_thr_node_t *thr_node = (prof_thr_node_t *)key; 299*c43cad87SWarner Losh hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash); 300*c43cad87SWarner Losh } 301*c43cad87SWarner Losh 302*c43cad87SWarner Losh static bool 303*c43cad87SWarner Losh prof_thr_node_keycomp(const void *k1, const void *k2) { 304*c43cad87SWarner Losh const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1; 305*c43cad87SWarner Losh const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2; 306*c43cad87SWarner Losh return thr_node1->thr_uid == thr_node2->thr_uid; 307*c43cad87SWarner Losh } 308*c43cad87SWarner Losh 309*c43cad87SWarner Losh /* Used in unit tests. */ 310*c43cad87SWarner Losh size_t 311*c43cad87SWarner Losh prof_log_bt_count(void) { 312*c43cad87SWarner Losh cassert(config_prof); 313*c43cad87SWarner Losh size_t cnt = 0; 314*c43cad87SWarner Losh prof_bt_node_t *node = log_bt_first; 315*c43cad87SWarner Losh while (node != NULL) { 316*c43cad87SWarner Losh cnt++; 317*c43cad87SWarner Losh node = node->next; 318*c43cad87SWarner Losh } 319*c43cad87SWarner Losh return cnt; 320*c43cad87SWarner Losh } 321*c43cad87SWarner Losh 322*c43cad87SWarner Losh /* Used in unit tests. */ 323*c43cad87SWarner Losh size_t 324*c43cad87SWarner Losh prof_log_alloc_count(void) { 325*c43cad87SWarner Losh cassert(config_prof); 326*c43cad87SWarner Losh size_t cnt = 0; 327*c43cad87SWarner Losh prof_alloc_node_t *node = log_alloc_first; 328*c43cad87SWarner Losh while (node != NULL) { 329*c43cad87SWarner Losh cnt++; 330*c43cad87SWarner Losh node = node->next; 331*c43cad87SWarner Losh } 332*c43cad87SWarner Losh return cnt; 333*c43cad87SWarner Losh } 334*c43cad87SWarner Losh 335*c43cad87SWarner Losh /* Used in unit tests. */ 336*c43cad87SWarner Losh size_t 337*c43cad87SWarner Losh prof_log_thr_count(void) { 338*c43cad87SWarner Losh cassert(config_prof); 339*c43cad87SWarner Losh size_t cnt = 0; 340*c43cad87SWarner Losh prof_thr_node_t *node = log_thr_first; 341*c43cad87SWarner Losh while (node != NULL) { 342*c43cad87SWarner Losh cnt++; 343*c43cad87SWarner Losh node = node->next; 344*c43cad87SWarner Losh } 345*c43cad87SWarner Losh return cnt; 346*c43cad87SWarner Losh } 347*c43cad87SWarner Losh 348*c43cad87SWarner Losh /* Used in unit tests. */ 349*c43cad87SWarner Losh bool 350*c43cad87SWarner Losh prof_log_is_logging(void) { 351*c43cad87SWarner Losh cassert(config_prof); 352*c43cad87SWarner Losh return prof_logging_state == prof_logging_state_started; 353*c43cad87SWarner Losh } 354*c43cad87SWarner Losh 355*c43cad87SWarner Losh /* Used in unit tests. */ 356*c43cad87SWarner Losh bool 357*c43cad87SWarner Losh prof_log_rep_check(void) { 358*c43cad87SWarner Losh cassert(config_prof); 359*c43cad87SWarner Losh if (prof_logging_state == prof_logging_state_stopped 360*c43cad87SWarner Losh && log_tables_initialized) { 361*c43cad87SWarner Losh return true; 362*c43cad87SWarner Losh } 363*c43cad87SWarner Losh 364*c43cad87SWarner Losh if (log_bt_last != NULL && log_bt_last->next != NULL) { 365*c43cad87SWarner Losh return true; 366*c43cad87SWarner Losh } 367*c43cad87SWarner Losh if (log_thr_last != NULL && log_thr_last->next != NULL) { 368*c43cad87SWarner Losh return true; 369*c43cad87SWarner Losh } 370*c43cad87SWarner Losh if (log_alloc_last != NULL && log_alloc_last->next != NULL) { 371*c43cad87SWarner Losh return true; 372*c43cad87SWarner Losh } 373*c43cad87SWarner Losh 374*c43cad87SWarner Losh size_t bt_count = prof_log_bt_count(); 375*c43cad87SWarner Losh size_t thr_count = prof_log_thr_count(); 376*c43cad87SWarner Losh size_t alloc_count = prof_log_alloc_count(); 377*c43cad87SWarner Losh 378*c43cad87SWarner Losh 379*c43cad87SWarner Losh if (prof_logging_state == prof_logging_state_stopped) { 380*c43cad87SWarner Losh if (bt_count != 0 || thr_count != 0 || alloc_count || 0) { 381*c43cad87SWarner Losh return true; 382*c43cad87SWarner Losh } 383*c43cad87SWarner Losh } 384*c43cad87SWarner Losh 385*c43cad87SWarner Losh prof_alloc_node_t *node = log_alloc_first; 386*c43cad87SWarner Losh while (node != NULL) { 387*c43cad87SWarner Losh if (node->alloc_bt_ind >= bt_count) { 388*c43cad87SWarner Losh return true; 389*c43cad87SWarner Losh } 390*c43cad87SWarner Losh if (node->free_bt_ind >= bt_count) { 391*c43cad87SWarner Losh return true; 392*c43cad87SWarner Losh } 393*c43cad87SWarner Losh if (node->alloc_thr_ind >= thr_count) { 394*c43cad87SWarner Losh return true; 395*c43cad87SWarner Losh } 396*c43cad87SWarner Losh if (node->free_thr_ind >= thr_count) { 397*c43cad87SWarner Losh return true; 398*c43cad87SWarner Losh } 399*c43cad87SWarner Losh if (node->alloc_time_ns > node->free_time_ns) { 400*c43cad87SWarner Losh return true; 401*c43cad87SWarner Losh } 402*c43cad87SWarner Losh node = node->next; 403*c43cad87SWarner Losh } 404*c43cad87SWarner Losh 405*c43cad87SWarner Losh return false; 406*c43cad87SWarner Losh } 407*c43cad87SWarner Losh 408*c43cad87SWarner Losh /* Used in unit tests. */ 409*c43cad87SWarner Losh void 410*c43cad87SWarner Losh prof_log_dummy_set(bool new_value) { 411*c43cad87SWarner Losh cassert(config_prof); 412*c43cad87SWarner Losh prof_log_dummy = new_value; 413*c43cad87SWarner Losh } 414*c43cad87SWarner Losh 415*c43cad87SWarner Losh /* Used as an atexit function to stop logging on exit. */ 416*c43cad87SWarner Losh static void 417*c43cad87SWarner Losh prof_log_stop_final(void) { 418*c43cad87SWarner Losh tsd_t *tsd = tsd_fetch(); 419*c43cad87SWarner Losh prof_log_stop(tsd_tsdn(tsd)); 420*c43cad87SWarner Losh } 421*c43cad87SWarner Losh 422*c43cad87SWarner Losh JEMALLOC_COLD 423*c43cad87SWarner Losh bool 424*c43cad87SWarner Losh prof_log_start(tsdn_t *tsdn, const char *filename) { 425*c43cad87SWarner Losh cassert(config_prof); 426*c43cad87SWarner Losh 427*c43cad87SWarner Losh if (!opt_prof) { 428*c43cad87SWarner Losh return true; 429*c43cad87SWarner Losh } 430*c43cad87SWarner Losh 431*c43cad87SWarner Losh bool ret = false; 432*c43cad87SWarner Losh 433*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &log_mtx); 434*c43cad87SWarner Losh 435*c43cad87SWarner Losh static bool prof_log_atexit_called = false; 436*c43cad87SWarner Losh if (!prof_log_atexit_called) { 437*c43cad87SWarner Losh prof_log_atexit_called = true; 438*c43cad87SWarner Losh if (atexit(prof_log_stop_final) != 0) { 439*c43cad87SWarner Losh malloc_write("<jemalloc>: Error in atexit() " 440*c43cad87SWarner Losh "for logging\n"); 441*c43cad87SWarner Losh if (opt_abort) { 442*c43cad87SWarner Losh abort(); 443*c43cad87SWarner Losh } 444*c43cad87SWarner Losh ret = true; 445*c43cad87SWarner Losh goto label_done; 446*c43cad87SWarner Losh } 447*c43cad87SWarner Losh } 448*c43cad87SWarner Losh 449*c43cad87SWarner Losh if (prof_logging_state != prof_logging_state_stopped) { 450*c43cad87SWarner Losh ret = true; 451*c43cad87SWarner Losh } else if (filename == NULL) { 452*c43cad87SWarner Losh /* Make default name. */ 453*c43cad87SWarner Losh prof_get_default_filename(tsdn, log_filename, log_seq); 454*c43cad87SWarner Losh log_seq++; 455*c43cad87SWarner Losh prof_logging_state = prof_logging_state_started; 456*c43cad87SWarner Losh } else if (strlen(filename) >= PROF_DUMP_FILENAME_LEN) { 457*c43cad87SWarner Losh ret = true; 458*c43cad87SWarner Losh } else { 459*c43cad87SWarner Losh strcpy(log_filename, filename); 460*c43cad87SWarner Losh prof_logging_state = prof_logging_state_started; 461*c43cad87SWarner Losh } 462*c43cad87SWarner Losh 463*c43cad87SWarner Losh if (!ret) { 464*c43cad87SWarner Losh nstime_prof_init_update(&log_start_timestamp); 465*c43cad87SWarner Losh } 466*c43cad87SWarner Losh label_done: 467*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &log_mtx); 468*c43cad87SWarner Losh 469*c43cad87SWarner Losh return ret; 470*c43cad87SWarner Losh } 471*c43cad87SWarner Losh 472*c43cad87SWarner Losh struct prof_emitter_cb_arg_s { 473*c43cad87SWarner Losh int fd; 474*c43cad87SWarner Losh ssize_t ret; 475*c43cad87SWarner Losh }; 476*c43cad87SWarner Losh 477*c43cad87SWarner Losh static void 478*c43cad87SWarner Losh prof_emitter_write_cb(void *opaque, const char *to_write) { 479*c43cad87SWarner Losh struct prof_emitter_cb_arg_s *arg = 480*c43cad87SWarner Losh (struct prof_emitter_cb_arg_s *)opaque; 481*c43cad87SWarner Losh size_t bytes = strlen(to_write); 482*c43cad87SWarner Losh if (prof_log_dummy) { 483*c43cad87SWarner Losh return; 484*c43cad87SWarner Losh } 485*c43cad87SWarner Losh arg->ret = malloc_write_fd(arg->fd, to_write, bytes); 486*c43cad87SWarner Losh } 487*c43cad87SWarner Losh 488*c43cad87SWarner Losh /* 489*c43cad87SWarner Losh * prof_log_emit_{...} goes through the appropriate linked list, emitting each 490*c43cad87SWarner Losh * node to the json and deallocating it. 491*c43cad87SWarner Losh */ 492*c43cad87SWarner Losh static void 493*c43cad87SWarner Losh prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) { 494*c43cad87SWarner Losh emitter_json_array_kv_begin(emitter, "threads"); 495*c43cad87SWarner Losh prof_thr_node_t *thr_node = log_thr_first; 496*c43cad87SWarner Losh prof_thr_node_t *thr_old_node; 497*c43cad87SWarner Losh while (thr_node != NULL) { 498*c43cad87SWarner Losh emitter_json_object_begin(emitter); 499*c43cad87SWarner Losh 500*c43cad87SWarner Losh emitter_json_kv(emitter, "thr_uid", emitter_type_uint64, 501*c43cad87SWarner Losh &thr_node->thr_uid); 502*c43cad87SWarner Losh 503*c43cad87SWarner Losh char *thr_name = thr_node->name; 504*c43cad87SWarner Losh 505*c43cad87SWarner Losh emitter_json_kv(emitter, "thr_name", emitter_type_string, 506*c43cad87SWarner Losh &thr_name); 507*c43cad87SWarner Losh 508*c43cad87SWarner Losh emitter_json_object_end(emitter); 509*c43cad87SWarner Losh thr_old_node = thr_node; 510*c43cad87SWarner Losh thr_node = thr_node->next; 511*c43cad87SWarner Losh idalloctm(tsd_tsdn(tsd), thr_old_node, NULL, NULL, true, true); 512*c43cad87SWarner Losh } 513*c43cad87SWarner Losh emitter_json_array_end(emitter); 514*c43cad87SWarner Losh } 515*c43cad87SWarner Losh 516*c43cad87SWarner Losh static void 517*c43cad87SWarner Losh prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) { 518*c43cad87SWarner Losh emitter_json_array_kv_begin(emitter, "stack_traces"); 519*c43cad87SWarner Losh prof_bt_node_t *bt_node = log_bt_first; 520*c43cad87SWarner Losh prof_bt_node_t *bt_old_node; 521*c43cad87SWarner Losh /* 522*c43cad87SWarner Losh * Calculate how many hex digits we need: twice number of bytes, two for 523*c43cad87SWarner Losh * "0x", and then one more for terminating '\0'. 524*c43cad87SWarner Losh */ 525*c43cad87SWarner Losh char buf[2 * sizeof(intptr_t) + 3]; 526*c43cad87SWarner Losh size_t buf_sz = sizeof(buf); 527*c43cad87SWarner Losh while (bt_node != NULL) { 528*c43cad87SWarner Losh emitter_json_array_begin(emitter); 529*c43cad87SWarner Losh size_t i; 530*c43cad87SWarner Losh for (i = 0; i < bt_node->bt.len; i++) { 531*c43cad87SWarner Losh malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]); 532*c43cad87SWarner Losh char *trace_str = buf; 533*c43cad87SWarner Losh emitter_json_value(emitter, emitter_type_string, 534*c43cad87SWarner Losh &trace_str); 535*c43cad87SWarner Losh } 536*c43cad87SWarner Losh emitter_json_array_end(emitter); 537*c43cad87SWarner Losh 538*c43cad87SWarner Losh bt_old_node = bt_node; 539*c43cad87SWarner Losh bt_node = bt_node->next; 540*c43cad87SWarner Losh idalloctm(tsd_tsdn(tsd), bt_old_node, NULL, NULL, true, true); 541*c43cad87SWarner Losh } 542*c43cad87SWarner Losh emitter_json_array_end(emitter); 543*c43cad87SWarner Losh } 544*c43cad87SWarner Losh 545*c43cad87SWarner Losh static void 546*c43cad87SWarner Losh prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) { 547*c43cad87SWarner Losh emitter_json_array_kv_begin(emitter, "allocations"); 548*c43cad87SWarner Losh prof_alloc_node_t *alloc_node = log_alloc_first; 549*c43cad87SWarner Losh prof_alloc_node_t *alloc_old_node; 550*c43cad87SWarner Losh while (alloc_node != NULL) { 551*c43cad87SWarner Losh emitter_json_object_begin(emitter); 552*c43cad87SWarner Losh 553*c43cad87SWarner Losh emitter_json_kv(emitter, "alloc_thread", emitter_type_size, 554*c43cad87SWarner Losh &alloc_node->alloc_thr_ind); 555*c43cad87SWarner Losh 556*c43cad87SWarner Losh emitter_json_kv(emitter, "free_thread", emitter_type_size, 557*c43cad87SWarner Losh &alloc_node->free_thr_ind); 558*c43cad87SWarner Losh 559*c43cad87SWarner Losh emitter_json_kv(emitter, "alloc_trace", emitter_type_size, 560*c43cad87SWarner Losh &alloc_node->alloc_bt_ind); 561*c43cad87SWarner Losh 562*c43cad87SWarner Losh emitter_json_kv(emitter, "free_trace", emitter_type_size, 563*c43cad87SWarner Losh &alloc_node->free_bt_ind); 564*c43cad87SWarner Losh 565*c43cad87SWarner Losh emitter_json_kv(emitter, "alloc_timestamp", 566*c43cad87SWarner Losh emitter_type_uint64, &alloc_node->alloc_time_ns); 567*c43cad87SWarner Losh 568*c43cad87SWarner Losh emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64, 569*c43cad87SWarner Losh &alloc_node->free_time_ns); 570*c43cad87SWarner Losh 571*c43cad87SWarner Losh emitter_json_kv(emitter, "usize", emitter_type_uint64, 572*c43cad87SWarner Losh &alloc_node->usize); 573*c43cad87SWarner Losh 574*c43cad87SWarner Losh emitter_json_object_end(emitter); 575*c43cad87SWarner Losh 576*c43cad87SWarner Losh alloc_old_node = alloc_node; 577*c43cad87SWarner Losh alloc_node = alloc_node->next; 578*c43cad87SWarner Losh idalloctm(tsd_tsdn(tsd), alloc_old_node, NULL, NULL, true, 579*c43cad87SWarner Losh true); 580*c43cad87SWarner Losh } 581*c43cad87SWarner Losh emitter_json_array_end(emitter); 582*c43cad87SWarner Losh } 583*c43cad87SWarner Losh 584*c43cad87SWarner Losh static void 585*c43cad87SWarner Losh prof_log_emit_metadata(emitter_t *emitter) { 586*c43cad87SWarner Losh emitter_json_object_kv_begin(emitter, "info"); 587*c43cad87SWarner Losh 588*c43cad87SWarner Losh nstime_t now; 589*c43cad87SWarner Losh 590*c43cad87SWarner Losh nstime_prof_init_update(&now); 591*c43cad87SWarner Losh uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp); 592*c43cad87SWarner Losh emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns); 593*c43cad87SWarner Losh 594*c43cad87SWarner Losh char *vers = JEMALLOC_VERSION; 595*c43cad87SWarner Losh emitter_json_kv(emitter, "version", 596*c43cad87SWarner Losh emitter_type_string, &vers); 597*c43cad87SWarner Losh 598*c43cad87SWarner Losh emitter_json_kv(emitter, "lg_sample_rate", 599*c43cad87SWarner Losh emitter_type_int, &lg_prof_sample); 600*c43cad87SWarner Losh 601*c43cad87SWarner Losh const char *res_type = prof_time_res_mode_names[opt_prof_time_res]; 602*c43cad87SWarner Losh emitter_json_kv(emitter, "prof_time_resolution", emitter_type_string, 603*c43cad87SWarner Losh &res_type); 604*c43cad87SWarner Losh 605*c43cad87SWarner Losh int pid = prof_getpid(); 606*c43cad87SWarner Losh emitter_json_kv(emitter, "pid", emitter_type_int, &pid); 607*c43cad87SWarner Losh 608*c43cad87SWarner Losh emitter_json_object_end(emitter); 609*c43cad87SWarner Losh } 610*c43cad87SWarner Losh 611*c43cad87SWarner Losh #define PROF_LOG_STOP_BUFSIZE PROF_DUMP_BUFSIZE 612*c43cad87SWarner Losh JEMALLOC_COLD 613*c43cad87SWarner Losh bool 614*c43cad87SWarner Losh prof_log_stop(tsdn_t *tsdn) { 615*c43cad87SWarner Losh cassert(config_prof); 616*c43cad87SWarner Losh if (!opt_prof || !prof_booted) { 617*c43cad87SWarner Losh return true; 618*c43cad87SWarner Losh } 619*c43cad87SWarner Losh 620*c43cad87SWarner Losh tsd_t *tsd = tsdn_tsd(tsdn); 621*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &log_mtx); 622*c43cad87SWarner Losh 623*c43cad87SWarner Losh if (prof_logging_state != prof_logging_state_started) { 624*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &log_mtx); 625*c43cad87SWarner Losh return true; 626*c43cad87SWarner Losh } 627*c43cad87SWarner Losh 628*c43cad87SWarner Losh /* 629*c43cad87SWarner Losh * Set the state to dumping. We'll set it to stopped when we're done. 630*c43cad87SWarner Losh * Since other threads won't be able to start/stop/log when the state is 631*c43cad87SWarner Losh * dumping, we don't have to hold the lock during the whole method. 632*c43cad87SWarner Losh */ 633*c43cad87SWarner Losh prof_logging_state = prof_logging_state_dumping; 634*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &log_mtx); 635*c43cad87SWarner Losh 636*c43cad87SWarner Losh 637*c43cad87SWarner Losh emitter_t emitter; 638*c43cad87SWarner Losh 639*c43cad87SWarner Losh /* Create a file. */ 640*c43cad87SWarner Losh 641*c43cad87SWarner Losh int fd; 642*c43cad87SWarner Losh if (prof_log_dummy) { 643*c43cad87SWarner Losh fd = 0; 644*c43cad87SWarner Losh } else { 645*c43cad87SWarner Losh fd = creat(log_filename, 0644); 646*c43cad87SWarner Losh } 647*c43cad87SWarner Losh 648*c43cad87SWarner Losh if (fd == -1) { 649*c43cad87SWarner Losh malloc_printf("<jemalloc>: creat() for log file \"%s\" " 650*c43cad87SWarner Losh " failed with %d\n", log_filename, errno); 651*c43cad87SWarner Losh if (opt_abort) { 652*c43cad87SWarner Losh abort(); 653*c43cad87SWarner Losh } 654*c43cad87SWarner Losh return true; 655*c43cad87SWarner Losh } 656*c43cad87SWarner Losh 657*c43cad87SWarner Losh struct prof_emitter_cb_arg_s arg; 658*c43cad87SWarner Losh arg.fd = fd; 659*c43cad87SWarner Losh 660*c43cad87SWarner Losh buf_writer_t buf_writer; 661*c43cad87SWarner Losh buf_writer_init(tsdn, &buf_writer, prof_emitter_write_cb, &arg, NULL, 662*c43cad87SWarner Losh PROF_LOG_STOP_BUFSIZE); 663*c43cad87SWarner Losh emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb, 664*c43cad87SWarner Losh &buf_writer); 665*c43cad87SWarner Losh 666*c43cad87SWarner Losh emitter_begin(&emitter); 667*c43cad87SWarner Losh prof_log_emit_metadata(&emitter); 668*c43cad87SWarner Losh prof_log_emit_threads(tsd, &emitter); 669*c43cad87SWarner Losh prof_log_emit_traces(tsd, &emitter); 670*c43cad87SWarner Losh prof_log_emit_allocs(tsd, &emitter); 671*c43cad87SWarner Losh emitter_end(&emitter); 672*c43cad87SWarner Losh 673*c43cad87SWarner Losh buf_writer_terminate(tsdn, &buf_writer); 674*c43cad87SWarner Losh 675*c43cad87SWarner Losh /* Reset global state. */ 676*c43cad87SWarner Losh if (log_tables_initialized) { 677*c43cad87SWarner Losh ckh_delete(tsd, &log_bt_node_set); 678*c43cad87SWarner Losh ckh_delete(tsd, &log_thr_node_set); 679*c43cad87SWarner Losh } 680*c43cad87SWarner Losh log_tables_initialized = false; 681*c43cad87SWarner Losh log_bt_index = 0; 682*c43cad87SWarner Losh log_thr_index = 0; 683*c43cad87SWarner Losh log_bt_first = NULL; 684*c43cad87SWarner Losh log_bt_last = NULL; 685*c43cad87SWarner Losh log_thr_first = NULL; 686*c43cad87SWarner Losh log_thr_last = NULL; 687*c43cad87SWarner Losh log_alloc_first = NULL; 688*c43cad87SWarner Losh log_alloc_last = NULL; 689*c43cad87SWarner Losh 690*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &log_mtx); 691*c43cad87SWarner Losh prof_logging_state = prof_logging_state_stopped; 692*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &log_mtx); 693*c43cad87SWarner Losh 694*c43cad87SWarner Losh if (prof_log_dummy) { 695*c43cad87SWarner Losh return false; 696*c43cad87SWarner Losh } 697*c43cad87SWarner Losh return close(fd) || arg.ret == -1; 698*c43cad87SWarner Losh } 699*c43cad87SWarner Losh #undef PROF_LOG_STOP_BUFSIZE 700*c43cad87SWarner Losh 701*c43cad87SWarner Losh JEMALLOC_COLD 702*c43cad87SWarner Losh bool 703*c43cad87SWarner Losh prof_log_init(tsd_t *tsd) { 704*c43cad87SWarner Losh cassert(config_prof); 705*c43cad87SWarner Losh if (malloc_mutex_init(&log_mtx, "prof_log", 706*c43cad87SWarner Losh WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) { 707*c43cad87SWarner Losh return true; 708*c43cad87SWarner Losh } 709*c43cad87SWarner Losh 710*c43cad87SWarner Losh if (opt_prof_log) { 711*c43cad87SWarner Losh prof_log_start(tsd_tsdn(tsd), NULL); 712*c43cad87SWarner Losh } 713*c43cad87SWarner Losh 714*c43cad87SWarner Losh return false; 715*c43cad87SWarner Losh } 716*c43cad87SWarner Losh 717*c43cad87SWarner Losh /******************************************************************************/ 718