1*c43cad87SWarner Losh #define JEMALLOC_PROF_SYS_C_ 2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h" 3*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h" 4*c43cad87SWarner Losh 5*c43cad87SWarner Losh #include "jemalloc/internal/buf_writer.h" 6*c43cad87SWarner Losh #include "jemalloc/internal/ctl.h" 7*c43cad87SWarner Losh #include "jemalloc/internal/prof_data.h" 8*c43cad87SWarner Losh #include "jemalloc/internal/prof_sys.h" 9*c43cad87SWarner Losh 10*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBUNWIND 11*c43cad87SWarner Losh #define UNW_LOCAL_ONLY 12*c43cad87SWarner Losh #include <libunwind.h> 13*c43cad87SWarner Losh #endif 14*c43cad87SWarner Losh 15*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBGCC 16*c43cad87SWarner Losh /* 17*c43cad87SWarner Losh * We have a circular dependency -- jemalloc_internal.h tells us if we should 18*c43cad87SWarner Losh * use libgcc's unwinding functionality, but after we've included that, we've 19*c43cad87SWarner Losh * already hooked _Unwind_Backtrace. We'll temporarily disable hooking. 20*c43cad87SWarner Losh */ 21*c43cad87SWarner Losh #undef _Unwind_Backtrace 22*c43cad87SWarner Losh #include <unwind.h> 23*c43cad87SWarner Losh #define _Unwind_Backtrace JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook) 24*c43cad87SWarner Losh #endif 25*c43cad87SWarner Losh 26*c43cad87SWarner Losh /******************************************************************************/ 27*c43cad87SWarner Losh 28*c43cad87SWarner Losh malloc_mutex_t prof_dump_filename_mtx; 29*c43cad87SWarner Losh 30*c43cad87SWarner Losh bool prof_do_mock = false; 31*c43cad87SWarner Losh 32*c43cad87SWarner Losh static uint64_t prof_dump_seq; 33*c43cad87SWarner Losh static uint64_t prof_dump_iseq; 34*c43cad87SWarner Losh static uint64_t prof_dump_mseq; 35*c43cad87SWarner Losh static uint64_t prof_dump_useq; 36*c43cad87SWarner Losh 37*c43cad87SWarner Losh static char *prof_prefix = NULL; 38*c43cad87SWarner Losh 39*c43cad87SWarner Losh /* The fallback allocator profiling functionality will use. */ 40*c43cad87SWarner Losh base_t *prof_base; 41*c43cad87SWarner Losh 42*c43cad87SWarner Losh void 43*c43cad87SWarner Losh bt_init(prof_bt_t *bt, void **vec) { 44*c43cad87SWarner Losh cassert(config_prof); 45*c43cad87SWarner Losh 46*c43cad87SWarner Losh bt->vec = vec; 47*c43cad87SWarner Losh bt->len = 0; 48*c43cad87SWarner Losh } 49*c43cad87SWarner Losh 50*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBUNWIND 51*c43cad87SWarner Losh static void 52*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { 53*c43cad87SWarner Losh int nframes; 54*c43cad87SWarner Losh 55*c43cad87SWarner Losh cassert(config_prof); 56*c43cad87SWarner Losh assert(*len == 0); 57*c43cad87SWarner Losh assert(vec != NULL); 58*c43cad87SWarner Losh assert(max_len == PROF_BT_MAX); 59*c43cad87SWarner Losh 60*c43cad87SWarner Losh nframes = unw_backtrace(vec, PROF_BT_MAX); 61*c43cad87SWarner Losh if (nframes <= 0) { 62*c43cad87SWarner Losh return; 63*c43cad87SWarner Losh } 64*c43cad87SWarner Losh *len = nframes; 65*c43cad87SWarner Losh } 66*c43cad87SWarner Losh #elif (defined(JEMALLOC_PROF_LIBGCC)) 67*c43cad87SWarner Losh static _Unwind_Reason_Code 68*c43cad87SWarner Losh prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) { 69*c43cad87SWarner Losh cassert(config_prof); 70*c43cad87SWarner Losh 71*c43cad87SWarner Losh return _URC_NO_REASON; 72*c43cad87SWarner Losh } 73*c43cad87SWarner Losh 74*c43cad87SWarner Losh static _Unwind_Reason_Code 75*c43cad87SWarner Losh prof_unwind_callback(struct _Unwind_Context *context, void *arg) { 76*c43cad87SWarner Losh prof_unwind_data_t *data = (prof_unwind_data_t *)arg; 77*c43cad87SWarner Losh void *ip; 78*c43cad87SWarner Losh 79*c43cad87SWarner Losh cassert(config_prof); 80*c43cad87SWarner Losh 81*c43cad87SWarner Losh ip = (void *)_Unwind_GetIP(context); 82*c43cad87SWarner Losh if (ip == NULL) { 83*c43cad87SWarner Losh return _URC_END_OF_STACK; 84*c43cad87SWarner Losh } 85*c43cad87SWarner Losh data->vec[*data->len] = ip; 86*c43cad87SWarner Losh (*data->len)++; 87*c43cad87SWarner Losh if (*data->len == data->max) { 88*c43cad87SWarner Losh return _URC_END_OF_STACK; 89*c43cad87SWarner Losh } 90*c43cad87SWarner Losh 91*c43cad87SWarner Losh return _URC_NO_REASON; 92*c43cad87SWarner Losh } 93*c43cad87SWarner Losh 94*c43cad87SWarner Losh static void 95*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { 96*c43cad87SWarner Losh prof_unwind_data_t data = {vec, len, max_len}; 97*c43cad87SWarner Losh 98*c43cad87SWarner Losh cassert(config_prof); 99*c43cad87SWarner Losh assert(vec != NULL); 100*c43cad87SWarner Losh assert(max_len == PROF_BT_MAX); 101*c43cad87SWarner Losh 102*c43cad87SWarner Losh _Unwind_Backtrace(prof_unwind_callback, &data); 103*c43cad87SWarner Losh } 104*c43cad87SWarner Losh #elif (defined(JEMALLOC_PROF_GCC)) 105*c43cad87SWarner Losh static void 106*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { 107*c43cad87SWarner Losh #define BT_FRAME(i) \ 108*c43cad87SWarner Losh if ((i) < max_len) { \ 109*c43cad87SWarner Losh void *p; \ 110*c43cad87SWarner Losh if (__builtin_frame_address(i) == 0) { \ 111*c43cad87SWarner Losh return; \ 112*c43cad87SWarner Losh } \ 113*c43cad87SWarner Losh p = __builtin_return_address(i); \ 114*c43cad87SWarner Losh if (p == NULL) { \ 115*c43cad87SWarner Losh return; \ 116*c43cad87SWarner Losh } \ 117*c43cad87SWarner Losh vec[(i)] = p; \ 118*c43cad87SWarner Losh *len = (i) + 1; \ 119*c43cad87SWarner Losh } else { \ 120*c43cad87SWarner Losh return; \ 121*c43cad87SWarner Losh } 122*c43cad87SWarner Losh 123*c43cad87SWarner Losh cassert(config_prof); 124*c43cad87SWarner Losh assert(vec != NULL); 125*c43cad87SWarner Losh assert(max_len == PROF_BT_MAX); 126*c43cad87SWarner Losh 127*c43cad87SWarner Losh BT_FRAME(0) 128*c43cad87SWarner Losh BT_FRAME(1) 129*c43cad87SWarner Losh BT_FRAME(2) 130*c43cad87SWarner Losh BT_FRAME(3) 131*c43cad87SWarner Losh BT_FRAME(4) 132*c43cad87SWarner Losh BT_FRAME(5) 133*c43cad87SWarner Losh BT_FRAME(6) 134*c43cad87SWarner Losh BT_FRAME(7) 135*c43cad87SWarner Losh BT_FRAME(8) 136*c43cad87SWarner Losh BT_FRAME(9) 137*c43cad87SWarner Losh 138*c43cad87SWarner Losh BT_FRAME(10) 139*c43cad87SWarner Losh BT_FRAME(11) 140*c43cad87SWarner Losh BT_FRAME(12) 141*c43cad87SWarner Losh BT_FRAME(13) 142*c43cad87SWarner Losh BT_FRAME(14) 143*c43cad87SWarner Losh BT_FRAME(15) 144*c43cad87SWarner Losh BT_FRAME(16) 145*c43cad87SWarner Losh BT_FRAME(17) 146*c43cad87SWarner Losh BT_FRAME(18) 147*c43cad87SWarner Losh BT_FRAME(19) 148*c43cad87SWarner Losh 149*c43cad87SWarner Losh BT_FRAME(20) 150*c43cad87SWarner Losh BT_FRAME(21) 151*c43cad87SWarner Losh BT_FRAME(22) 152*c43cad87SWarner Losh BT_FRAME(23) 153*c43cad87SWarner Losh BT_FRAME(24) 154*c43cad87SWarner Losh BT_FRAME(25) 155*c43cad87SWarner Losh BT_FRAME(26) 156*c43cad87SWarner Losh BT_FRAME(27) 157*c43cad87SWarner Losh BT_FRAME(28) 158*c43cad87SWarner Losh BT_FRAME(29) 159*c43cad87SWarner Losh 160*c43cad87SWarner Losh BT_FRAME(30) 161*c43cad87SWarner Losh BT_FRAME(31) 162*c43cad87SWarner Losh BT_FRAME(32) 163*c43cad87SWarner Losh BT_FRAME(33) 164*c43cad87SWarner Losh BT_FRAME(34) 165*c43cad87SWarner Losh BT_FRAME(35) 166*c43cad87SWarner Losh BT_FRAME(36) 167*c43cad87SWarner Losh BT_FRAME(37) 168*c43cad87SWarner Losh BT_FRAME(38) 169*c43cad87SWarner Losh BT_FRAME(39) 170*c43cad87SWarner Losh 171*c43cad87SWarner Losh BT_FRAME(40) 172*c43cad87SWarner Losh BT_FRAME(41) 173*c43cad87SWarner Losh BT_FRAME(42) 174*c43cad87SWarner Losh BT_FRAME(43) 175*c43cad87SWarner Losh BT_FRAME(44) 176*c43cad87SWarner Losh BT_FRAME(45) 177*c43cad87SWarner Losh BT_FRAME(46) 178*c43cad87SWarner Losh BT_FRAME(47) 179*c43cad87SWarner Losh BT_FRAME(48) 180*c43cad87SWarner Losh BT_FRAME(49) 181*c43cad87SWarner Losh 182*c43cad87SWarner Losh BT_FRAME(50) 183*c43cad87SWarner Losh BT_FRAME(51) 184*c43cad87SWarner Losh BT_FRAME(52) 185*c43cad87SWarner Losh BT_FRAME(53) 186*c43cad87SWarner Losh BT_FRAME(54) 187*c43cad87SWarner Losh BT_FRAME(55) 188*c43cad87SWarner Losh BT_FRAME(56) 189*c43cad87SWarner Losh BT_FRAME(57) 190*c43cad87SWarner Losh BT_FRAME(58) 191*c43cad87SWarner Losh BT_FRAME(59) 192*c43cad87SWarner Losh 193*c43cad87SWarner Losh BT_FRAME(60) 194*c43cad87SWarner Losh BT_FRAME(61) 195*c43cad87SWarner Losh BT_FRAME(62) 196*c43cad87SWarner Losh BT_FRAME(63) 197*c43cad87SWarner Losh BT_FRAME(64) 198*c43cad87SWarner Losh BT_FRAME(65) 199*c43cad87SWarner Losh BT_FRAME(66) 200*c43cad87SWarner Losh BT_FRAME(67) 201*c43cad87SWarner Losh BT_FRAME(68) 202*c43cad87SWarner Losh BT_FRAME(69) 203*c43cad87SWarner Losh 204*c43cad87SWarner Losh BT_FRAME(70) 205*c43cad87SWarner Losh BT_FRAME(71) 206*c43cad87SWarner Losh BT_FRAME(72) 207*c43cad87SWarner Losh BT_FRAME(73) 208*c43cad87SWarner Losh BT_FRAME(74) 209*c43cad87SWarner Losh BT_FRAME(75) 210*c43cad87SWarner Losh BT_FRAME(76) 211*c43cad87SWarner Losh BT_FRAME(77) 212*c43cad87SWarner Losh BT_FRAME(78) 213*c43cad87SWarner Losh BT_FRAME(79) 214*c43cad87SWarner Losh 215*c43cad87SWarner Losh BT_FRAME(80) 216*c43cad87SWarner Losh BT_FRAME(81) 217*c43cad87SWarner Losh BT_FRAME(82) 218*c43cad87SWarner Losh BT_FRAME(83) 219*c43cad87SWarner Losh BT_FRAME(84) 220*c43cad87SWarner Losh BT_FRAME(85) 221*c43cad87SWarner Losh BT_FRAME(86) 222*c43cad87SWarner Losh BT_FRAME(87) 223*c43cad87SWarner Losh BT_FRAME(88) 224*c43cad87SWarner Losh BT_FRAME(89) 225*c43cad87SWarner Losh 226*c43cad87SWarner Losh BT_FRAME(90) 227*c43cad87SWarner Losh BT_FRAME(91) 228*c43cad87SWarner Losh BT_FRAME(92) 229*c43cad87SWarner Losh BT_FRAME(93) 230*c43cad87SWarner Losh BT_FRAME(94) 231*c43cad87SWarner Losh BT_FRAME(95) 232*c43cad87SWarner Losh BT_FRAME(96) 233*c43cad87SWarner Losh BT_FRAME(97) 234*c43cad87SWarner Losh BT_FRAME(98) 235*c43cad87SWarner Losh BT_FRAME(99) 236*c43cad87SWarner Losh 237*c43cad87SWarner Losh BT_FRAME(100) 238*c43cad87SWarner Losh BT_FRAME(101) 239*c43cad87SWarner Losh BT_FRAME(102) 240*c43cad87SWarner Losh BT_FRAME(103) 241*c43cad87SWarner Losh BT_FRAME(104) 242*c43cad87SWarner Losh BT_FRAME(105) 243*c43cad87SWarner Losh BT_FRAME(106) 244*c43cad87SWarner Losh BT_FRAME(107) 245*c43cad87SWarner Losh BT_FRAME(108) 246*c43cad87SWarner Losh BT_FRAME(109) 247*c43cad87SWarner Losh 248*c43cad87SWarner Losh BT_FRAME(110) 249*c43cad87SWarner Losh BT_FRAME(111) 250*c43cad87SWarner Losh BT_FRAME(112) 251*c43cad87SWarner Losh BT_FRAME(113) 252*c43cad87SWarner Losh BT_FRAME(114) 253*c43cad87SWarner Losh BT_FRAME(115) 254*c43cad87SWarner Losh BT_FRAME(116) 255*c43cad87SWarner Losh BT_FRAME(117) 256*c43cad87SWarner Losh BT_FRAME(118) 257*c43cad87SWarner Losh BT_FRAME(119) 258*c43cad87SWarner Losh 259*c43cad87SWarner Losh BT_FRAME(120) 260*c43cad87SWarner Losh BT_FRAME(121) 261*c43cad87SWarner Losh BT_FRAME(122) 262*c43cad87SWarner Losh BT_FRAME(123) 263*c43cad87SWarner Losh BT_FRAME(124) 264*c43cad87SWarner Losh BT_FRAME(125) 265*c43cad87SWarner Losh BT_FRAME(126) 266*c43cad87SWarner Losh BT_FRAME(127) 267*c43cad87SWarner Losh #undef BT_FRAME 268*c43cad87SWarner Losh } 269*c43cad87SWarner Losh #else 270*c43cad87SWarner Losh static void 271*c43cad87SWarner Losh prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) { 272*c43cad87SWarner Losh cassert(config_prof); 273*c43cad87SWarner Losh not_reached(); 274*c43cad87SWarner Losh } 275*c43cad87SWarner Losh #endif 276*c43cad87SWarner Losh 277*c43cad87SWarner Losh void 278*c43cad87SWarner Losh prof_backtrace(tsd_t *tsd, prof_bt_t *bt) { 279*c43cad87SWarner Losh cassert(config_prof); 280*c43cad87SWarner Losh prof_backtrace_hook_t prof_backtrace_hook = prof_backtrace_hook_get(); 281*c43cad87SWarner Losh assert(prof_backtrace_hook != NULL); 282*c43cad87SWarner Losh 283*c43cad87SWarner Losh pre_reentrancy(tsd, NULL); 284*c43cad87SWarner Losh prof_backtrace_hook(bt->vec, &bt->len, PROF_BT_MAX); 285*c43cad87SWarner Losh post_reentrancy(tsd); 286*c43cad87SWarner Losh } 287*c43cad87SWarner Losh 288*c43cad87SWarner Losh void 289*c43cad87SWarner Losh prof_hooks_init() { 290*c43cad87SWarner Losh prof_backtrace_hook_set(&prof_backtrace_impl); 291*c43cad87SWarner Losh prof_dump_hook_set(NULL); 292*c43cad87SWarner Losh } 293*c43cad87SWarner Losh 294*c43cad87SWarner Losh void 295*c43cad87SWarner Losh prof_unwind_init() { 296*c43cad87SWarner Losh #ifdef JEMALLOC_PROF_LIBGCC 297*c43cad87SWarner Losh /* 298*c43cad87SWarner Losh * Cause the backtracing machinery to allocate its internal 299*c43cad87SWarner Losh * state before enabling profiling. 300*c43cad87SWarner Losh */ 301*c43cad87SWarner Losh _Unwind_Backtrace(prof_unwind_init_callback, NULL); 302*c43cad87SWarner Losh #endif 303*c43cad87SWarner Losh } 304*c43cad87SWarner Losh 305*c43cad87SWarner Losh static int 306*c43cad87SWarner Losh prof_sys_thread_name_read_impl(char *buf, size_t limit) { 307*c43cad87SWarner Losh #if defined(JEMALLOC_HAVE_PTHREAD_GETNAME_NP) 308*c43cad87SWarner Losh return pthread_getname_np(pthread_self(), buf, limit); 309*c43cad87SWarner Losh #elif defined(JEMALLOC_HAVE_PTHREAD_GET_NAME_NP) 310*c43cad87SWarner Losh pthread_get_name_np(pthread_self(), buf, limit); 311*c43cad87SWarner Losh return 0; 312*c43cad87SWarner Losh #else 313*c43cad87SWarner Losh return ENOSYS; 314*c43cad87SWarner Losh #endif 315*c43cad87SWarner Losh } 316*c43cad87SWarner Losh prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read = 317*c43cad87SWarner Losh prof_sys_thread_name_read_impl; 318*c43cad87SWarner Losh 319*c43cad87SWarner Losh void 320*c43cad87SWarner Losh prof_sys_thread_name_fetch(tsd_t *tsd) { 321*c43cad87SWarner Losh #define THREAD_NAME_MAX_LEN 16 322*c43cad87SWarner Losh char buf[THREAD_NAME_MAX_LEN]; 323*c43cad87SWarner Losh if (!prof_sys_thread_name_read(buf, THREAD_NAME_MAX_LEN)) { 324*c43cad87SWarner Losh prof_thread_name_set_impl(tsd, buf); 325*c43cad87SWarner Losh } 326*c43cad87SWarner Losh #undef THREAD_NAME_MAX_LEN 327*c43cad87SWarner Losh } 328*c43cad87SWarner Losh 329*c43cad87SWarner Losh int 330*c43cad87SWarner Losh prof_getpid(void) { 331*c43cad87SWarner Losh #ifdef _WIN32 332*c43cad87SWarner Losh return GetCurrentProcessId(); 333*c43cad87SWarner Losh #else 334*c43cad87SWarner Losh return getpid(); 335*c43cad87SWarner Losh #endif 336*c43cad87SWarner Losh } 337*c43cad87SWarner Losh 338*c43cad87SWarner Losh /* 339*c43cad87SWarner Losh * This buffer is rather large for stack allocation, so use a single buffer for 340*c43cad87SWarner Losh * all profile dumps; protected by prof_dump_mtx. 341*c43cad87SWarner Losh */ 342*c43cad87SWarner Losh static char prof_dump_buf[PROF_DUMP_BUFSIZE]; 343*c43cad87SWarner Losh 344*c43cad87SWarner Losh typedef struct prof_dump_arg_s prof_dump_arg_t; 345*c43cad87SWarner Losh struct prof_dump_arg_s { 346*c43cad87SWarner Losh /* 347*c43cad87SWarner Losh * Whether error should be handled locally: if true, then we print out 348*c43cad87SWarner Losh * error message as well as abort (if opt_abort is true) when an error 349*c43cad87SWarner Losh * occurred, and we also report the error back to the caller in the end; 350*c43cad87SWarner Losh * if false, then we only report the error back to the caller in the 351*c43cad87SWarner Losh * end. 352*c43cad87SWarner Losh */ 353*c43cad87SWarner Losh const bool handle_error_locally; 354*c43cad87SWarner Losh /* 355*c43cad87SWarner Losh * Whether there has been an error in the dumping process, which could 356*c43cad87SWarner Losh * have happened either in file opening or in file writing. When an 357*c43cad87SWarner Losh * error has already occurred, we will stop further writing to the file. 358*c43cad87SWarner Losh */ 359*c43cad87SWarner Losh bool error; 360*c43cad87SWarner Losh /* File descriptor of the dump file. */ 361*c43cad87SWarner Losh int prof_dump_fd; 362*c43cad87SWarner Losh }; 363*c43cad87SWarner Losh 364*c43cad87SWarner Losh static void 365*c43cad87SWarner Losh prof_dump_check_possible_error(prof_dump_arg_t *arg, bool err_cond, 366*c43cad87SWarner Losh const char *format, ...) { 367*c43cad87SWarner Losh assert(!arg->error); 368*c43cad87SWarner Losh if (!err_cond) { 369*c43cad87SWarner Losh return; 370*c43cad87SWarner Losh } 371*c43cad87SWarner Losh 372*c43cad87SWarner Losh arg->error = true; 373*c43cad87SWarner Losh if (!arg->handle_error_locally) { 374*c43cad87SWarner Losh return; 375*c43cad87SWarner Losh } 376*c43cad87SWarner Losh 377*c43cad87SWarner Losh va_list ap; 378*c43cad87SWarner Losh char buf[PROF_PRINTF_BUFSIZE]; 379*c43cad87SWarner Losh va_start(ap, format); 380*c43cad87SWarner Losh malloc_vsnprintf(buf, sizeof(buf), format, ap); 381*c43cad87SWarner Losh va_end(ap); 382*c43cad87SWarner Losh malloc_write(buf); 383*c43cad87SWarner Losh 384*c43cad87SWarner Losh if (opt_abort) { 385*c43cad87SWarner Losh abort(); 386*c43cad87SWarner Losh } 387*c43cad87SWarner Losh } 388*c43cad87SWarner Losh 389*c43cad87SWarner Losh static int 390*c43cad87SWarner Losh prof_dump_open_file_impl(const char *filename, int mode) { 391*c43cad87SWarner Losh return creat(filename, mode); 392*c43cad87SWarner Losh } 393*c43cad87SWarner Losh prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file = 394*c43cad87SWarner Losh prof_dump_open_file_impl; 395*c43cad87SWarner Losh 396*c43cad87SWarner Losh static void 397*c43cad87SWarner Losh prof_dump_open(prof_dump_arg_t *arg, const char *filename) { 398*c43cad87SWarner Losh arg->prof_dump_fd = prof_dump_open_file(filename, 0644); 399*c43cad87SWarner Losh prof_dump_check_possible_error(arg, arg->prof_dump_fd == -1, 400*c43cad87SWarner Losh "<jemalloc>: failed to open \"%s\"\n", filename); 401*c43cad87SWarner Losh } 402*c43cad87SWarner Losh 403*c43cad87SWarner Losh prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file = malloc_write_fd; 404*c43cad87SWarner Losh 405*c43cad87SWarner Losh static void 406*c43cad87SWarner Losh prof_dump_flush(void *opaque, const char *s) { 407*c43cad87SWarner Losh cassert(config_prof); 408*c43cad87SWarner Losh prof_dump_arg_t *arg = (prof_dump_arg_t *)opaque; 409*c43cad87SWarner Losh if (!arg->error) { 410*c43cad87SWarner Losh ssize_t err = prof_dump_write_file(arg->prof_dump_fd, s, 411*c43cad87SWarner Losh strlen(s)); 412*c43cad87SWarner Losh prof_dump_check_possible_error(arg, err == -1, 413*c43cad87SWarner Losh "<jemalloc>: failed to write during heap profile flush\n"); 414*c43cad87SWarner Losh } 415*c43cad87SWarner Losh } 416*c43cad87SWarner Losh 417*c43cad87SWarner Losh static void 418*c43cad87SWarner Losh prof_dump_close(prof_dump_arg_t *arg) { 419*c43cad87SWarner Losh if (arg->prof_dump_fd != -1) { 420*c43cad87SWarner Losh close(arg->prof_dump_fd); 421*c43cad87SWarner Losh } 422*c43cad87SWarner Losh } 423*c43cad87SWarner Losh 424*c43cad87SWarner Losh #ifndef _WIN32 425*c43cad87SWarner Losh JEMALLOC_FORMAT_PRINTF(1, 2) 426*c43cad87SWarner Losh static int 427*c43cad87SWarner Losh prof_open_maps_internal(const char *format, ...) { 428*c43cad87SWarner Losh int mfd; 429*c43cad87SWarner Losh va_list ap; 430*c43cad87SWarner Losh char filename[PATH_MAX + 1]; 431*c43cad87SWarner Losh 432*c43cad87SWarner Losh va_start(ap, format); 433*c43cad87SWarner Losh malloc_vsnprintf(filename, sizeof(filename), format, ap); 434*c43cad87SWarner Losh va_end(ap); 435*c43cad87SWarner Losh 436*c43cad87SWarner Losh #if defined(O_CLOEXEC) 437*c43cad87SWarner Losh mfd = open(filename, O_RDONLY | O_CLOEXEC); 438*c43cad87SWarner Losh #else 439*c43cad87SWarner Losh mfd = open(filename, O_RDONLY); 440*c43cad87SWarner Losh if (mfd != -1) { 441*c43cad87SWarner Losh fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC); 442*c43cad87SWarner Losh } 443*c43cad87SWarner Losh #endif 444*c43cad87SWarner Losh 445*c43cad87SWarner Losh return mfd; 446*c43cad87SWarner Losh } 447*c43cad87SWarner Losh #endif 448*c43cad87SWarner Losh 449*c43cad87SWarner Losh static int 450*c43cad87SWarner Losh prof_dump_open_maps_impl() { 451*c43cad87SWarner Losh int mfd; 452*c43cad87SWarner Losh 453*c43cad87SWarner Losh cassert(config_prof); 454*c43cad87SWarner Losh #if defined(__FreeBSD__) || defined(__DragonFly__) 455*c43cad87SWarner Losh mfd = prof_open_maps_internal("/proc/curproc/map"); 456*c43cad87SWarner Losh #elif defined(_WIN32) 457*c43cad87SWarner Losh mfd = -1; // Not implemented 458*c43cad87SWarner Losh #else 459*c43cad87SWarner Losh int pid = prof_getpid(); 460*c43cad87SWarner Losh 461*c43cad87SWarner Losh mfd = prof_open_maps_internal("/proc/%d/task/%d/maps", pid, pid); 462*c43cad87SWarner Losh if (mfd == -1) { 463*c43cad87SWarner Losh mfd = prof_open_maps_internal("/proc/%d/maps", pid); 464*c43cad87SWarner Losh } 465*c43cad87SWarner Losh #endif 466*c43cad87SWarner Losh return mfd; 467*c43cad87SWarner Losh } 468*c43cad87SWarner Losh prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps = 469*c43cad87SWarner Losh prof_dump_open_maps_impl; 470*c43cad87SWarner Losh 471*c43cad87SWarner Losh static ssize_t 472*c43cad87SWarner Losh prof_dump_read_maps_cb(void *read_cbopaque, void *buf, size_t limit) { 473*c43cad87SWarner Losh int mfd = *(int *)read_cbopaque; 474*c43cad87SWarner Losh assert(mfd != -1); 475*c43cad87SWarner Losh return malloc_read_fd(mfd, buf, limit); 476*c43cad87SWarner Losh } 477*c43cad87SWarner Losh 478*c43cad87SWarner Losh static void 479*c43cad87SWarner Losh prof_dump_maps(buf_writer_t *buf_writer) { 480*c43cad87SWarner Losh int mfd = prof_dump_open_maps(); 481*c43cad87SWarner Losh if (mfd == -1) { 482*c43cad87SWarner Losh return; 483*c43cad87SWarner Losh } 484*c43cad87SWarner Losh 485*c43cad87SWarner Losh buf_writer_cb(buf_writer, "\nMAPPED_LIBRARIES:\n"); 486*c43cad87SWarner Losh buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd); 487*c43cad87SWarner Losh close(mfd); 488*c43cad87SWarner Losh } 489*c43cad87SWarner Losh 490*c43cad87SWarner Losh static bool 491*c43cad87SWarner Losh prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, 492*c43cad87SWarner Losh bool leakcheck) { 493*c43cad87SWarner Losh cassert(config_prof); 494*c43cad87SWarner Losh assert(tsd_reentrancy_level_get(tsd) == 0); 495*c43cad87SWarner Losh 496*c43cad87SWarner Losh prof_tdata_t * tdata = prof_tdata_get(tsd, true); 497*c43cad87SWarner Losh if (tdata == NULL) { 498*c43cad87SWarner Losh return true; 499*c43cad87SWarner Losh } 500*c43cad87SWarner Losh 501*c43cad87SWarner Losh prof_dump_arg_t arg = {/* handle_error_locally */ !propagate_err, 502*c43cad87SWarner Losh /* error */ false, /* prof_dump_fd */ -1}; 503*c43cad87SWarner Losh 504*c43cad87SWarner Losh pre_reentrancy(tsd, NULL); 505*c43cad87SWarner Losh malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx); 506*c43cad87SWarner Losh 507*c43cad87SWarner Losh prof_dump_open(&arg, filename); 508*c43cad87SWarner Losh buf_writer_t buf_writer; 509*c43cad87SWarner Losh bool err = buf_writer_init(tsd_tsdn(tsd), &buf_writer, prof_dump_flush, 510*c43cad87SWarner Losh &arg, prof_dump_buf, PROF_DUMP_BUFSIZE); 511*c43cad87SWarner Losh assert(!err); 512*c43cad87SWarner Losh prof_dump_impl(tsd, buf_writer_cb, &buf_writer, tdata, leakcheck); 513*c43cad87SWarner Losh prof_dump_maps(&buf_writer); 514*c43cad87SWarner Losh buf_writer_terminate(tsd_tsdn(tsd), &buf_writer); 515*c43cad87SWarner Losh prof_dump_close(&arg); 516*c43cad87SWarner Losh 517*c43cad87SWarner Losh prof_dump_hook_t dump_hook = prof_dump_hook_get(); 518*c43cad87SWarner Losh if (dump_hook != NULL) { 519*c43cad87SWarner Losh dump_hook(filename); 520*c43cad87SWarner Losh } 521*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx); 522*c43cad87SWarner Losh post_reentrancy(tsd); 523*c43cad87SWarner Losh 524*c43cad87SWarner Losh return arg.error; 525*c43cad87SWarner Losh } 526*c43cad87SWarner Losh 527*c43cad87SWarner Losh /* 528*c43cad87SWarner Losh * If profiling is off, then PROF_DUMP_FILENAME_LEN is 1, so we'll end up 529*c43cad87SWarner Losh * calling strncpy with a size of 0, which triggers a -Wstringop-truncation 530*c43cad87SWarner Losh * warning (strncpy can never actually be called in this case, since we bail out 531*c43cad87SWarner Losh * much earlier when config_prof is false). This function works around the 532*c43cad87SWarner Losh * warning to let us leave the warning on. 533*c43cad87SWarner Losh */ 534*c43cad87SWarner Losh static inline void 535*c43cad87SWarner Losh prof_strncpy(char *UNUSED dest, const char *UNUSED src, size_t UNUSED size) { 536*c43cad87SWarner Losh cassert(config_prof); 537*c43cad87SWarner Losh #ifdef JEMALLOC_PROF 538*c43cad87SWarner Losh strncpy(dest, src, size); 539*c43cad87SWarner Losh #endif 540*c43cad87SWarner Losh } 541*c43cad87SWarner Losh 542*c43cad87SWarner Losh static const char * 543*c43cad87SWarner Losh prof_prefix_get(tsdn_t* tsdn) { 544*c43cad87SWarner Losh malloc_mutex_assert_owner(tsdn, &prof_dump_filename_mtx); 545*c43cad87SWarner Losh 546*c43cad87SWarner Losh return prof_prefix == NULL ? opt_prof_prefix : prof_prefix; 547*c43cad87SWarner Losh } 548*c43cad87SWarner Losh 549*c43cad87SWarner Losh static bool 550*c43cad87SWarner Losh prof_prefix_is_empty(tsdn_t *tsdn) { 551*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); 552*c43cad87SWarner Losh bool ret = (prof_prefix_get(tsdn)[0] == '\0'); 553*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 554*c43cad87SWarner Losh return ret; 555*c43cad87SWarner Losh } 556*c43cad87SWarner Losh 557*c43cad87SWarner Losh #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1) 558*c43cad87SWarner Losh #define VSEQ_INVALID UINT64_C(0xffffffffffffffff) 559*c43cad87SWarner Losh static void 560*c43cad87SWarner Losh prof_dump_filename(tsd_t *tsd, char *filename, char v, uint64_t vseq) { 561*c43cad87SWarner Losh cassert(config_prof); 562*c43cad87SWarner Losh 563*c43cad87SWarner Losh assert(tsd_reentrancy_level_get(tsd) == 0); 564*c43cad87SWarner Losh const char *prefix = prof_prefix_get(tsd_tsdn(tsd)); 565*c43cad87SWarner Losh 566*c43cad87SWarner Losh if (vseq != VSEQ_INVALID) { 567*c43cad87SWarner Losh /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */ 568*c43cad87SWarner Losh malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, 569*c43cad87SWarner Losh "%s.%d.%"FMTu64".%c%"FMTu64".heap", prefix, prof_getpid(), 570*c43cad87SWarner Losh prof_dump_seq, v, vseq); 571*c43cad87SWarner Losh } else { 572*c43cad87SWarner Losh /* "<prefix>.<pid>.<seq>.<v>.heap" */ 573*c43cad87SWarner Losh malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, 574*c43cad87SWarner Losh "%s.%d.%"FMTu64".%c.heap", prefix, prof_getpid(), 575*c43cad87SWarner Losh prof_dump_seq, v); 576*c43cad87SWarner Losh } 577*c43cad87SWarner Losh prof_dump_seq++; 578*c43cad87SWarner Losh } 579*c43cad87SWarner Losh 580*c43cad87SWarner Losh void 581*c43cad87SWarner Losh prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind) { 582*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); 583*c43cad87SWarner Losh malloc_snprintf(filename, PROF_DUMP_FILENAME_LEN, 584*c43cad87SWarner Losh "%s.%d.%"FMTu64".json", prof_prefix_get(tsdn), prof_getpid(), ind); 585*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 586*c43cad87SWarner Losh } 587*c43cad87SWarner Losh 588*c43cad87SWarner Losh void 589*c43cad87SWarner Losh prof_fdump_impl(tsd_t *tsd) { 590*c43cad87SWarner Losh char filename[DUMP_FILENAME_BUFSIZE]; 591*c43cad87SWarner Losh 592*c43cad87SWarner Losh assert(!prof_prefix_is_empty(tsd_tsdn(tsd))); 593*c43cad87SWarner Losh malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 594*c43cad87SWarner Losh prof_dump_filename(tsd, filename, 'f', VSEQ_INVALID); 595*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 596*c43cad87SWarner Losh prof_dump(tsd, false, filename, opt_prof_leak); 597*c43cad87SWarner Losh } 598*c43cad87SWarner Losh 599*c43cad87SWarner Losh bool 600*c43cad87SWarner Losh prof_prefix_set(tsdn_t *tsdn, const char *prefix) { 601*c43cad87SWarner Losh cassert(config_prof); 602*c43cad87SWarner Losh ctl_mtx_assert_held(tsdn); 603*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); 604*c43cad87SWarner Losh if (prof_prefix == NULL) { 605*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 606*c43cad87SWarner Losh /* Everything is still guarded by ctl_mtx. */ 607*c43cad87SWarner Losh char *buffer = base_alloc(tsdn, prof_base, 608*c43cad87SWarner Losh PROF_DUMP_FILENAME_LEN, QUANTUM); 609*c43cad87SWarner Losh if (buffer == NULL) { 610*c43cad87SWarner Losh return true; 611*c43cad87SWarner Losh } 612*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); 613*c43cad87SWarner Losh prof_prefix = buffer; 614*c43cad87SWarner Losh } 615*c43cad87SWarner Losh assert(prof_prefix != NULL); 616*c43cad87SWarner Losh 617*c43cad87SWarner Losh prof_strncpy(prof_prefix, prefix, PROF_DUMP_FILENAME_LEN - 1); 618*c43cad87SWarner Losh prof_prefix[PROF_DUMP_FILENAME_LEN - 1] = '\0'; 619*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 620*c43cad87SWarner Losh 621*c43cad87SWarner Losh return false; 622*c43cad87SWarner Losh } 623*c43cad87SWarner Losh 624*c43cad87SWarner Losh void 625*c43cad87SWarner Losh prof_idump_impl(tsd_t *tsd) { 626*c43cad87SWarner Losh malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 627*c43cad87SWarner Losh if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') { 628*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 629*c43cad87SWarner Losh return; 630*c43cad87SWarner Losh } 631*c43cad87SWarner Losh char filename[PATH_MAX + 1]; 632*c43cad87SWarner Losh prof_dump_filename(tsd, filename, 'i', prof_dump_iseq); 633*c43cad87SWarner Losh prof_dump_iseq++; 634*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 635*c43cad87SWarner Losh prof_dump(tsd, false, filename, false); 636*c43cad87SWarner Losh } 637*c43cad87SWarner Losh 638*c43cad87SWarner Losh bool 639*c43cad87SWarner Losh prof_mdump_impl(tsd_t *tsd, const char *filename) { 640*c43cad87SWarner Losh char filename_buf[DUMP_FILENAME_BUFSIZE]; 641*c43cad87SWarner Losh if (filename == NULL) { 642*c43cad87SWarner Losh /* No filename specified, so automatically generate one. */ 643*c43cad87SWarner Losh malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 644*c43cad87SWarner Losh if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') { 645*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 646*c43cad87SWarner Losh return true; 647*c43cad87SWarner Losh } 648*c43cad87SWarner Losh prof_dump_filename(tsd, filename_buf, 'm', prof_dump_mseq); 649*c43cad87SWarner Losh prof_dump_mseq++; 650*c43cad87SWarner Losh malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); 651*c43cad87SWarner Losh filename = filename_buf; 652*c43cad87SWarner Losh } 653*c43cad87SWarner Losh return prof_dump(tsd, true, filename, false); 654*c43cad87SWarner Losh } 655*c43cad87SWarner Losh 656*c43cad87SWarner Losh void 657*c43cad87SWarner Losh prof_gdump_impl(tsd_t *tsd) { 658*c43cad87SWarner Losh tsdn_t *tsdn = tsd_tsdn(tsd); 659*c43cad87SWarner Losh malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); 660*c43cad87SWarner Losh if (prof_prefix_get(tsdn)[0] == '\0') { 661*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 662*c43cad87SWarner Losh return; 663*c43cad87SWarner Losh } 664*c43cad87SWarner Losh char filename[DUMP_FILENAME_BUFSIZE]; 665*c43cad87SWarner Losh prof_dump_filename(tsd, filename, 'u', prof_dump_useq); 666*c43cad87SWarner Losh prof_dump_useq++; 667*c43cad87SWarner Losh malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); 668*c43cad87SWarner Losh prof_dump(tsd, false, filename, false); 669*c43cad87SWarner Losh } 670