From 4b76c684bb8d7f0b7960bfac84391e9fd51a234e Mon Sep 17 00:00:00 2001 From: zhxchen17 Date: Mon, 9 Sep 2019 20:18:41 -0700 Subject: [PATCH] Add "prof.dump_prefix" to override filename prefixes for dumps. --- doc/jemalloc.xml.in | 42 +++++++-- include/jemalloc/internal/ctl.h | 1 + include/jemalloc/internal/prof_externs.h | 2 + include/jemalloc/internal/prof_types.h | 7 ++ src/ctl.c | 27 ++++++ src/prof.c | 106 +++++++++++++++++------ src/prof_log.c | 6 +- test/unit/prof_idump.c | 13 +++ 8 files changed, 166 insertions(+), 38 deletions(-) diff --git a/doc/jemalloc.xml.in b/doc/jemalloc.xml.in index 5636fb90..e83bfbff 100644 --- a/doc/jemalloc.xml.in +++ b/doc/jemalloc.xml.in @@ -1344,7 +1344,10 @@ malloc_conf = "xmalloc:true";]]> set to the empty string, no automatic dumps will occur; this is primarily useful for disabling the automatic final heap dump (which also disables leak reporting, if enabled). The default prefix is - jeprof. + jeprof. This prefix value can be overriden by + prof.dump_prefix. + @@ -1423,8 +1426,10 @@ malloc_conf = "xmalloc:true";]]> <prefix>.<pid>.<seq>.i<iseq>.heap, where <prefix> is controlled by the opt.prof_prefix - option. By default, interval-triggered profile dumping is disabled + linkend="opt.prof_prefix">opt.prof_prefix and + prof.dump_prefix + options. By default, interval-triggered profile dumping is disabled (encoded as -1). @@ -1456,8 +1461,10 @@ malloc_conf = "xmalloc:true";]]> usage to a file named according to the pattern <prefix>.<pid>.<seq>.f.heap, where <prefix> is controlled by the opt.prof_prefix - option. Note that atexit() may allocate + linkend="opt.prof_prefix">opt.prof_prefix and + prof.dump_prefix + options. Note that atexit() may allocate memory during application initialization and then deadlock internally when jemalloc in turn calls atexit(), so this option is not universally usable (though the application can @@ -2224,8 +2231,25 @@ struct extent_hooks_s { <prefix>.<pid>.<seq>.m<mseq>.heap, where <prefix> is controlled by the opt.prof_prefix and + prof.dump_prefix + options. + + + + + prof.dump_prefix + (const char *) + -w + [] + + Set the filename prefix for profile dumps. See + opt.prof_prefix - option. + for the default setting. This can be useful to differentiate profile + dumps such as from forked processes. + @@ -2240,8 +2264,10 @@ struct extent_hooks_s { dumped to files named according to the pattern <prefix>.<pid>.<seq>.u<useq>.heap, where <prefix> is controlled by the opt.prof_prefix - option. + linkend="opt.prof_prefix">opt.prof_prefix and + prof.dump_prefix + options. diff --git a/include/jemalloc/internal/ctl.h b/include/jemalloc/internal/ctl.h index 1d1aacc6..8ddf7f86 100644 --- a/include/jemalloc/internal/ctl.h +++ b/include/jemalloc/internal/ctl.h @@ -103,6 +103,7 @@ bool ctl_boot(void); void ctl_prefork(tsdn_t *tsdn); void ctl_postfork_parent(tsdn_t *tsdn); void ctl_postfork_child(tsdn_t *tsdn); +void ctl_mtx_assert_held(tsdn_t *tsdn); #define xmallctl(name, oldp, oldlenp, newp, newlen) do { \ if (je_mallctl(name, oldp, oldlenp, newp, newlen) \ diff --git a/include/jemalloc/internal/prof_externs.h b/include/jemalloc/internal/prof_externs.h index c0471f52..7befad64 100644 --- a/include/jemalloc/internal/prof_externs.h +++ b/include/jemalloc/internal/prof_externs.h @@ -72,10 +72,12 @@ void prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs, uint64_t *accumbytes); #endif int prof_getpid(void); +void prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind); bool prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum); void prof_idump(tsdn_t *tsdn); bool prof_mdump(tsd_t *tsd, const char *filename); void prof_gdump(tsdn_t *tsdn); +bool prof_dump_prefix_set(tsdn_t *tsdn, const char *prefix); void prof_bt_hash(const void *key, size_t r_hash[2]); bool prof_bt_keycomp(const void *k1, const void *k2); diff --git a/include/jemalloc/internal/prof_types.h b/include/jemalloc/internal/prof_types.h index 1eff995e..a50653bb 100644 --- a/include/jemalloc/internal/prof_types.h +++ b/include/jemalloc/internal/prof_types.h @@ -53,4 +53,11 @@ typedef struct prof_tdata_s prof_tdata_t; #define PROF_TDATA_STATE_PURGATORY ((prof_tdata_t *)(uintptr_t)2) #define PROF_TDATA_STATE_MAX PROF_TDATA_STATE_PURGATORY +/* Minimize memory bloat for non-prof builds. */ +#ifdef JEMALLOC_PROF +#define PROF_DUMP_FILENAME_LEN (PATH_MAX + 1) +#else +#define PROF_DUMP_FILENAME_LEN 1 +#endif + #endif /* JEMALLOC_INTERNAL_PROF_TYPES_H */ diff --git a/src/ctl.c b/src/ctl.c index d6f803cd..0beef6e0 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -148,6 +148,7 @@ CTL_PROTO(prof_thread_active_init) CTL_PROTO(prof_active) CTL_PROTO(prof_dump) CTL_PROTO(prof_gdump) +CTL_PROTO(prof_dump_prefix) CTL_PROTO(prof_reset) CTL_PROTO(prof_interval) CTL_PROTO(lg_prof_sample) @@ -413,6 +414,7 @@ static const ctl_named_node_t prof_node[] = { {NAME("active"), CTL(prof_active)}, {NAME("dump"), CTL(prof_dump)}, {NAME("gdump"), CTL(prof_gdump)}, + {NAME("dump_prefix"), CTL(prof_dump_prefix)}, {NAME("reset"), CTL(prof_reset)}, {NAME("interval"), CTL(prof_interval)}, {NAME("lg_sample"), CTL(lg_prof_sample)}, @@ -1416,6 +1418,11 @@ ctl_postfork_child(tsdn_t *tsdn) { malloc_mutex_postfork_child(tsdn, &ctl_mtx); } +void +ctl_mtx_assert_held(tsdn_t *tsdn) { + malloc_mutex_assert_owner(tsdn, &ctl_mtx); +} + /******************************************************************************/ /* *_ctl() functions. */ @@ -2720,6 +2727,26 @@ label_return: return ret; } +static int +prof_dump_prefix_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, + void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + int ret; + const char *prefix = NULL; + + if (!config_prof) { + return ENOENT; + } + + malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); + WRITEONLY(); + WRITE(prefix, const char *); + + ret = prof_dump_prefix_set(tsd_tsdn(tsd), prefix) ? EFAULT : 0; +label_return: + malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); + return ret; +} + static int prof_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { diff --git a/src/prof.c b/src/prof.c index c7c91ef4..9ea4eda4 100644 --- a/src/prof.c +++ b/src/prof.c @@ -2,6 +2,7 @@ #include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/jemalloc_internal_includes.h" +#include "jemalloc/internal/ctl.h" #include "jemalloc/internal/assert.h" #include "jemalloc/internal/mutex.h" @@ -41,12 +42,7 @@ bool opt_prof_gdump = false; bool opt_prof_final = false; bool opt_prof_leak = false; bool opt_prof_accum = false; -char opt_prof_prefix[ - /* Minimize memory bloat for non-prof builds. */ -#ifdef JEMALLOC_PROF - PATH_MAX + -#endif - 1]; +char opt_prof_prefix[PROF_DUMP_FILENAME_LEN]; /* * Initialized as opt_prof_active, and accessed via @@ -106,6 +102,7 @@ static uint64_t prof_dump_mseq; static uint64_t prof_dump_useq; malloc_mutex_t prof_dump_mtx; +static char *prof_dump_prefix = NULL; /* Do not dump any profiles until bootstrapping is complete. */ bool prof_booted = false; @@ -514,26 +511,53 @@ prof_getpid(void) { #endif } +static const char * +prof_dump_prefix_get(tsdn_t* tsdn) { + malloc_mutex_assert_owner(tsdn, &prof_dump_filename_mtx); + + return prof_dump_prefix == NULL ? opt_prof_prefix : prof_dump_prefix; +} + +static bool +prof_dump_prefix_is_empty(tsdn_t *tsdn) { + malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); + bool ret = (prof_dump_prefix_get(tsdn)[0] == '\0'); + malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); + return ret; +} + #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1) #define VSEQ_INVALID UINT64_C(0xffffffffffffffff) static void -prof_dump_filename(char *filename, char v, uint64_t vseq) { +prof_dump_filename(tsd_t *tsd, char *filename, char v, uint64_t vseq) { cassert(config_prof); + assert(tsd_reentrancy_level_get(tsd) == 0); + const char *prof_prefix = prof_dump_prefix_get(tsd_tsdn(tsd)); + if (vseq != VSEQ_INVALID) { /* "...v.heap" */ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, "%s.%d.%"FMTu64".%c%"FMTu64".heap", - opt_prof_prefix, prof_getpid(), prof_dump_seq, v, vseq); + prof_prefix, prof_getpid(), prof_dump_seq, v, vseq); } else { /* "....heap" */ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, "%s.%d.%"FMTu64".%c.heap", - opt_prof_prefix, prof_getpid(), prof_dump_seq, v); + prof_prefix, prof_getpid(), prof_dump_seq, v); } prof_dump_seq++; } +void +prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind) { + malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); + malloc_snprintf(filename, PROF_DUMP_FILENAME_LEN, + "%s.%d.%"FMTu64".json", prof_dump_prefix_get(tsdn), prof_getpid(), + ind); + malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); +} + static void prof_fdump(void) { tsd_t *tsd; @@ -541,16 +565,16 @@ prof_fdump(void) { cassert(config_prof); assert(opt_prof_final); - assert(opt_prof_prefix[0] != '\0'); if (!prof_booted) { return; } tsd = tsd_fetch(); assert(tsd_reentrancy_level_get(tsd) == 0); + assert(!prof_dump_prefix_is_empty(tsd_tsdn(tsd))); malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump_filename(filename, 'f', VSEQ_INVALID); + prof_dump_filename(tsd, filename, 'f', VSEQ_INVALID); malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); prof_dump(tsd, false, filename, opt_prof_leak); } @@ -571,6 +595,31 @@ prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) { return false; } +bool +prof_dump_prefix_set(tsdn_t *tsdn, const char *prefix) { + cassert(config_prof); + ctl_mtx_assert_held(tsdn); + malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); + if (prof_dump_prefix == NULL) { + malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); + /* Everything is still guarded by ctl_mtx. */ + char *buffer = base_alloc(tsdn, b0get(), PROF_DUMP_FILENAME_LEN, + QUANTUM); + if (buffer == NULL) { + return true; + } + malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); + prof_dump_prefix = buffer; + } + assert(prof_dump_prefix != NULL); + + strncpy(prof_dump_prefix, prefix, PROF_DUMP_FILENAME_LEN - 1); + prof_dump_prefix[PROF_DUMP_FILENAME_LEN - 1] = '\0'; + malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); + + return false; +} + void prof_idump(tsdn_t *tsdn) { tsd_t *tsd; @@ -595,14 +644,16 @@ prof_idump(tsdn_t *tsdn) { return; } - if (opt_prof_prefix[0] != '\0') { - char filename[PATH_MAX + 1]; - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump_filename(filename, 'i', prof_dump_iseq); - prof_dump_iseq++; + malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); + if (prof_dump_prefix_get(tsd_tsdn(tsd))[0] == '\0') { malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump(tsd, false, filename, false); + return; } + char filename[PATH_MAX + 1]; + prof_dump_filename(tsd, filename, 'i', prof_dump_iseq); + prof_dump_iseq++; + malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); + prof_dump(tsd, false, filename, false); } bool @@ -616,11 +667,12 @@ prof_mdump(tsd_t *tsd, const char *filename) { char filename_buf[DUMP_FILENAME_BUFSIZE]; if (filename == NULL) { /* No filename specified, so automatically generate one. */ - if (opt_prof_prefix[0] == '\0') { + malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); + if (prof_dump_prefix_get(tsd_tsdn(tsd))[0] == '\0') { + malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); return true; } - malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx); - prof_dump_filename(filename_buf, 'm', prof_dump_mseq); + prof_dump_filename(tsd, filename_buf, 'm', prof_dump_mseq); prof_dump_mseq++; malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx); filename = filename_buf; @@ -652,14 +704,16 @@ prof_gdump(tsdn_t *tsdn) { return; } - if (opt_prof_prefix[0] != '\0') { - char filename[DUMP_FILENAME_BUFSIZE]; - malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); - prof_dump_filename(filename, 'u', prof_dump_useq); - prof_dump_useq++; + malloc_mutex_lock(tsdn, &prof_dump_filename_mtx); + if (prof_dump_prefix_get(tsdn)[0] == '\0') { malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); - prof_dump(tsd, false, filename, false); + return; } + char filename[DUMP_FILENAME_BUFSIZE]; + prof_dump_filename(tsd, filename, 'u', prof_dump_useq); + prof_dump_useq++; + malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx); + prof_dump(tsd, false, filename, false); } static uint64_t diff --git a/src/prof_log.c b/src/prof_log.c index 8274cfcf..af91af7d 100644 --- a/src/prof_log.c +++ b/src/prof_log.c @@ -405,7 +405,6 @@ prof_log_start(tsdn_t *tsdn, const char *filename) { } bool ret = false; - size_t buf_size = PATH_MAX + 1; malloc_mutex_lock(tsdn, &log_mtx); @@ -413,11 +412,10 @@ prof_log_start(tsdn_t *tsdn, const char *filename) { ret = true; } else if (filename == NULL) { /* Make default name. */ - malloc_snprintf(log_filename, buf_size, "%s.%d.%"FMTu64".json", - opt_prof_prefix, prof_getpid(), log_seq); + prof_get_default_filename(tsdn, log_filename, log_seq); log_seq++; prof_logging_state = prof_logging_state_started; - } else if (strlen(filename) >= buf_size) { + } else if (strlen(filename) >= PROF_DUMP_FILENAME_LEN) { ret = true; } else { strcpy(log_filename, filename); diff --git a/test/unit/prof_idump.c b/test/unit/prof_idump.c index 1cc6c98c..7a9b2882 100644 --- a/test/unit/prof_idump.c +++ b/test/unit/prof_idump.c @@ -1,5 +1,7 @@ #include "test/jemalloc_test.h" +#define TEST_PREFIX "test_prefix" + static bool did_prof_dump_open; static int @@ -8,6 +10,10 @@ prof_dump_open_intercept(bool propagate_err, const char *filename) { did_prof_dump_open = true; + const char filename_prefix[] = TEST_PREFIX "."; + assert_d_eq(strncmp(filename_prefix, filename, sizeof(filename_prefix) + - 1), 0, "Dump file name should start with \"" TEST_PREFIX ".\""); + fd = open("/dev/null", O_WRONLY); assert_d_ne(fd, -1, "Unexpected open() failure"); @@ -18,9 +24,16 @@ TEST_BEGIN(test_idump) { bool active; void *p; + const char *dump_prefix = TEST_PREFIX; + test_skip_if(!config_prof); active = true; + + assert_d_eq(mallctl("prof.dump_prefix", NULL, NULL, + (void *)&dump_prefix, sizeof(dump_prefix)), 0, + "Unexpected mallctl failure while overwriting dump prefix"); + assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active, sizeof(active)), 0, "Unexpected mallctl failure while activating profiling");