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");