Allow prof.dump mallctl to specify filename.
This commit is contained in:
parent
74025c85bf
commit
22ca855e8f
@ -1016,9 +1016,10 @@ Total number of large size classes.
|
|||||||
Maximum size supported by this large size class.
|
Maximum size supported by this large size class.
|
||||||
.Ed
|
.Ed
|
||||||
.\"-----------------------------------------------------------------------------
|
.\"-----------------------------------------------------------------------------
|
||||||
@roff_prof@.It Sy "prof.dump (void) --"
|
@roff_prof@.It Sy "prof.dump (const char *) -w"
|
||||||
@roff_prof@.Bd -ragged -offset indent -compact
|
@roff_prof@.Bd -ragged -offset indent -compact
|
||||||
@roff_prof@Dump a memory profile to a file according to the pattern
|
@roff_prof@Dump a memory profile to the specified file, or if NULL is specified,
|
||||||
|
@roff_prof@to a file according to the pattern
|
||||||
@roff_prof@.Pa <prefix>.<pid>.<seq>.m<mseq>.heap ,
|
@roff_prof@.Pa <prefix>.<pid>.<seq>.m<mseq>.heap ,
|
||||||
@roff_prof@where
|
@roff_prof@where
|
||||||
@roff_prof@.Pa <prefix>
|
@roff_prof@.Pa <prefix>
|
||||||
|
@ -144,7 +144,7 @@ void prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
|
|||||||
size_t old_size, prof_thr_cnt_t *old_cnt);
|
size_t old_size, prof_thr_cnt_t *old_cnt);
|
||||||
void prof_free(const void *ptr);
|
void prof_free(const void *ptr);
|
||||||
void prof_idump(void);
|
void prof_idump(void);
|
||||||
void prof_mdump(void);
|
bool prof_mdump(const char *filename);
|
||||||
void prof_udump(void);
|
void prof_udump(void);
|
||||||
void prof_boot0(void);
|
void prof_boot0(void);
|
||||||
bool prof_boot1(void);
|
bool prof_boot1(void);
|
||||||
|
@ -830,14 +830,18 @@ ctl_boot(void)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define VOID() do { \
|
#define WRITEONLY() do { \
|
||||||
READONLY(); \
|
|
||||||
if (oldp != NULL || oldlenp != NULL) { \
|
if (oldp != NULL || oldlenp != NULL) { \
|
||||||
ret = EPERM; \
|
ret = EPERM; \
|
||||||
goto RETURN; \
|
goto RETURN; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define VOID() do { \
|
||||||
|
READONLY(); \
|
||||||
|
WRITEONLY(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define READ(v, t) do { \
|
#define READ(v, t) do { \
|
||||||
if (oldp != NULL && oldlenp != NULL) { \
|
if (oldp != NULL && oldlenp != NULL) { \
|
||||||
if (*oldlenp != sizeof(t)) { \
|
if (*oldlenp != sizeof(t)) { \
|
||||||
@ -1167,10 +1171,15 @@ prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
|||||||
void *newp, size_t newlen)
|
void *newp, size_t newlen)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
const char *filename = NULL;
|
||||||
|
|
||||||
VOID();
|
WRITEONLY();
|
||||||
|
WRITE(filename, const char *);
|
||||||
|
|
||||||
prof_mdump();
|
if (prof_mdump(filename)) {
|
||||||
|
ret = EFAULT;
|
||||||
|
goto RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
RETURN:
|
RETURN:
|
||||||
|
@ -99,13 +99,15 @@ static _Unwind_Reason_Code prof_unwind_callback(
|
|||||||
static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max);
|
static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max);
|
||||||
static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt);
|
static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt);
|
||||||
static void prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt);
|
static void prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt);
|
||||||
static void prof_flush(void);
|
static bool prof_flush(bool propagate_err);
|
||||||
static void prof_write(const char *s);
|
static bool prof_write(const char *s, bool propagate_err);
|
||||||
static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all,
|
static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all,
|
||||||
size_t *leak_nctx);
|
size_t *leak_nctx);
|
||||||
static void prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt);
|
static bool prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt,
|
||||||
static void prof_dump_maps(void);
|
bool propagate_err);
|
||||||
static void prof_dump(const char *filename, bool leakcheck);
|
static bool prof_dump_maps(bool propagate_err);
|
||||||
|
static bool prof_dump(const char *filename, bool leakcheck,
|
||||||
|
bool propagate_err);
|
||||||
static void prof_dump_filename(char *filename, char v, int64_t vseq);
|
static void prof_dump_filename(char *filename, char v, int64_t vseq);
|
||||||
static void prof_fdump(void);
|
static void prof_fdump(void);
|
||||||
static void prof_bt_hash(const void *key, unsigned minbits, size_t *hash1,
|
static void prof_bt_hash(const void *key, unsigned minbits, size_t *hash1,
|
||||||
@ -715,23 +717,30 @@ prof_free(const void *ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prof_flush(void)
|
prof_flush(bool propagate_err)
|
||||||
{
|
{
|
||||||
|
bool ret = false;
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
|
|
||||||
err = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
|
err = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
|
||||||
if (err == -1) {
|
if (err == -1) {
|
||||||
malloc_write4("<jemalloc>",
|
if (propagate_err == false) {
|
||||||
": write() failed during heap profile flush", "\n", "");
|
malloc_write4("<jemalloc>",
|
||||||
if (opt_abort)
|
": write() failed during heap profile flush", "\n",
|
||||||
abort();
|
"");
|
||||||
|
if (opt_abort)
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
}
|
}
|
||||||
prof_dump_buf_end = 0;
|
prof_dump_buf_end = 0;
|
||||||
|
|
||||||
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prof_write(const char *s)
|
prof_write(const char *s, bool propagate_err)
|
||||||
{
|
{
|
||||||
unsigned i, slen, n;
|
unsigned i, slen, n;
|
||||||
|
|
||||||
@ -740,7 +749,8 @@ prof_write(const char *s)
|
|||||||
while (i < slen) {
|
while (i < slen) {
|
||||||
/* Flush the buffer if it is full. */
|
/* Flush the buffer if it is full. */
|
||||||
if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE)
|
if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE)
|
||||||
prof_flush();
|
if (prof_flush(propagate_err) && propagate_err)
|
||||||
|
return (true);
|
||||||
|
|
||||||
if (prof_dump_buf_end + slen <= PROF_DUMP_BUF_SIZE) {
|
if (prof_dump_buf_end + slen <= PROF_DUMP_BUF_SIZE) {
|
||||||
/* Finish writing. */
|
/* Finish writing. */
|
||||||
@ -753,6 +763,8 @@ prof_write(const char *s)
|
|||||||
prof_dump_buf_end += n;
|
prof_dump_buf_end += n;
|
||||||
i += n;
|
i += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -799,31 +811,40 @@ prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx)
|
|||||||
malloc_mutex_unlock(&ctx->lock);
|
malloc_mutex_unlock(&ctx->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt)
|
prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt, bool propagate_err)
|
||||||
{
|
{
|
||||||
char buf[UMAX2S_BUFSIZE];
|
char buf[UMAX2S_BUFSIZE];
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
prof_write(umax2s(ctx->cnt_dump.curobjs, 10, buf));
|
if (prof_write(umax2s(ctx->cnt_dump.curobjs, 10, buf), propagate_err)
|
||||||
prof_write(": ");
|
|| prof_write(": ", propagate_err)
|
||||||
prof_write(umax2s(ctx->cnt_dump.curbytes, 10, buf));
|
|| prof_write(umax2s(ctx->cnt_dump.curbytes, 10, buf),
|
||||||
prof_write(" [");
|
propagate_err)
|
||||||
prof_write(umax2s(ctx->cnt_dump.accumobjs, 10, buf));
|
|| prof_write(" [", propagate_err)
|
||||||
prof_write(": ");
|
|| prof_write(umax2s(ctx->cnt_dump.accumobjs, 10, buf),
|
||||||
prof_write(umax2s(ctx->cnt_dump.accumbytes, 10, buf));
|
propagate_err)
|
||||||
prof_write("] @");
|
|| prof_write(": ", propagate_err)
|
||||||
|
|| prof_write(umax2s(ctx->cnt_dump.accumbytes, 10, buf),
|
||||||
|
propagate_err)
|
||||||
|
|| prof_write("] @", propagate_err))
|
||||||
|
return (true);
|
||||||
|
|
||||||
for (i = 0; i < bt->len; i++) {
|
for (i = 0; i < bt->len; i++) {
|
||||||
prof_write(" 0x");
|
if (prof_write(" 0x", propagate_err)
|
||||||
prof_write(umax2s((uintptr_t)bt->vec[i], 16, buf));
|
|| prof_write(umax2s((uintptr_t)bt->vec[i], 16, buf),
|
||||||
|
propagate_err))
|
||||||
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
prof_write("\n");
|
if (prof_write("\n", propagate_err))
|
||||||
|
return (true);
|
||||||
|
|
||||||
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prof_dump_maps(void)
|
prof_dump_maps(bool propagate_err)
|
||||||
{
|
{
|
||||||
int mfd;
|
int mfd;
|
||||||
char buf[UMAX2S_BUFSIZE];
|
char buf[UMAX2S_BUFSIZE];
|
||||||
@ -856,23 +877,29 @@ prof_dump_maps(void)
|
|||||||
if (mfd != -1) {
|
if (mfd != -1) {
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
|
|
||||||
prof_write("\nMAPPED_LIBRARIES:\n");
|
if (prof_write("\nMAPPED_LIBRARIES:\n", propagate_err) &&
|
||||||
|
propagate_err)
|
||||||
|
return (true);
|
||||||
nread = 0;
|
nread = 0;
|
||||||
do {
|
do {
|
||||||
prof_dump_buf_end += nread;
|
prof_dump_buf_end += nread;
|
||||||
if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE) {
|
if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE) {
|
||||||
/* Make space in prof_dump_buf before read(). */
|
/* Make space in prof_dump_buf before read(). */
|
||||||
prof_flush();
|
if (prof_flush(propagate_err) && propagate_err)
|
||||||
|
return (true);
|
||||||
}
|
}
|
||||||
nread = read(mfd, &prof_dump_buf[prof_dump_buf_end],
|
nread = read(mfd, &prof_dump_buf[prof_dump_buf_end],
|
||||||
PROF_DUMP_BUF_SIZE - prof_dump_buf_end);
|
PROF_DUMP_BUF_SIZE - prof_dump_buf_end);
|
||||||
} while (nread > 0);
|
} while (nread > 0);
|
||||||
close(mfd);
|
close(mfd);
|
||||||
}
|
} else
|
||||||
|
return (true);
|
||||||
|
|
||||||
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
prof_dump(const char *filename, bool leakcheck)
|
prof_dump(const char *filename, bool leakcheck, bool propagate_err)
|
||||||
{
|
{
|
||||||
prof_cnt_t cnt_all;
|
prof_cnt_t cnt_all;
|
||||||
size_t tabind;
|
size_t tabind;
|
||||||
@ -884,12 +911,14 @@ prof_dump(const char *filename, bool leakcheck)
|
|||||||
prof_enter();
|
prof_enter();
|
||||||
prof_dump_fd = creat(filename, 0644);
|
prof_dump_fd = creat(filename, 0644);
|
||||||
if (prof_dump_fd == -1) {
|
if (prof_dump_fd == -1) {
|
||||||
malloc_write4("<jemalloc>",
|
if (propagate_err == false) {
|
||||||
": creat(\"", filename, "\", 0644) failed\n");
|
malloc_write4("<jemalloc>",
|
||||||
if (opt_abort)
|
": creat(\"", filename, "\", 0644) failed\n");
|
||||||
abort();
|
if (opt_abort)
|
||||||
|
abort();
|
||||||
|
}
|
||||||
prof_leave();
|
prof_leave();
|
||||||
return;
|
goto ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Merge per thread profile stats, and sum them in cnt_all. */
|
/* Merge per thread profile stats, and sum them in cnt_all. */
|
||||||
@ -901,32 +930,40 @@ prof_dump(const char *filename, bool leakcheck)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Dump profile header. */
|
/* Dump profile header. */
|
||||||
prof_write("heap profile: ");
|
if (prof_write("heap profile: ", propagate_err)
|
||||||
prof_write(umax2s(cnt_all.curobjs, 10, buf));
|
|| prof_write(umax2s(cnt_all.curobjs, 10, buf), propagate_err)
|
||||||
prof_write(": ");
|
|| prof_write(": ", propagate_err)
|
||||||
prof_write(umax2s(cnt_all.curbytes, 10, buf));
|
|| prof_write(umax2s(cnt_all.curbytes, 10, buf), propagate_err)
|
||||||
prof_write(" [");
|
|| prof_write(" [", propagate_err)
|
||||||
prof_write(umax2s(cnt_all.accumobjs, 10, buf));
|
|| prof_write(umax2s(cnt_all.accumobjs, 10, buf), propagate_err)
|
||||||
prof_write(": ");
|
|| prof_write(": ", propagate_err)
|
||||||
prof_write(umax2s(cnt_all.accumbytes, 10, buf));
|
|| prof_write(umax2s(cnt_all.accumbytes, 10, buf), propagate_err))
|
||||||
if (opt_lg_prof_sample == 0)
|
goto ERROR;
|
||||||
prof_write("] @ heapprofile\n");
|
|
||||||
else {
|
if (opt_lg_prof_sample == 0) {
|
||||||
prof_write("] @ heap_v2/");
|
if (prof_write("] @ heapprofile\n", propagate_err))
|
||||||
prof_write(umax2s((uint64_t)1U << opt_lg_prof_sample, 10, buf));
|
goto ERROR;
|
||||||
prof_write("\n");
|
} else {
|
||||||
|
if (prof_write("] @ heap_v2/", propagate_err)
|
||||||
|
|| prof_write(umax2s((uint64_t)1U << opt_lg_prof_sample, 10,
|
||||||
|
buf), propagate_err)
|
||||||
|
|| prof_write("\n", propagate_err))
|
||||||
|
goto ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dump per ctx profile stats. */
|
/* Dump per ctx profile stats. */
|
||||||
for (tabind = 0; ckh_iter(&bt2ctx, &tabind, (void **)&bt, (void **)&ctx)
|
for (tabind = 0; ckh_iter(&bt2ctx, &tabind, (void **)&bt, (void **)&ctx)
|
||||||
== false;) {
|
== false;) {
|
||||||
prof_dump_ctx(ctx, bt);
|
if (prof_dump_ctx(ctx, bt, propagate_err))
|
||||||
|
goto ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dump /proc/<pid>/maps if possible. */
|
/* Dump /proc/<pid>/maps if possible. */
|
||||||
prof_dump_maps();
|
if (prof_dump_maps(propagate_err))
|
||||||
|
goto ERROR;
|
||||||
|
|
||||||
prof_flush();
|
if (prof_flush(propagate_err))
|
||||||
|
goto ERROR;
|
||||||
close(prof_dump_fd);
|
close(prof_dump_fd);
|
||||||
prof_leave();
|
prof_leave();
|
||||||
|
|
||||||
@ -941,6 +978,11 @@ prof_dump(const char *filename, bool leakcheck)
|
|||||||
malloc_write4("<jemalloc>: Run pprof on \"",
|
malloc_write4("<jemalloc>: Run pprof on \"",
|
||||||
filename, "\" for leak detail\n", "");
|
filename, "\" for leak detail\n", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (false);
|
||||||
|
ERROR:
|
||||||
|
prof_leave();
|
||||||
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DUMP_FILENAME_BUFSIZE (PATH_MAX+ UMAX2S_BUFSIZE \
|
#define DUMP_FILENAME_BUFSIZE (PATH_MAX+ UMAX2S_BUFSIZE \
|
||||||
@ -1034,7 +1076,7 @@ prof_fdump(void)
|
|||||||
malloc_mutex_lock(&prof_dump_seq_mtx);
|
malloc_mutex_lock(&prof_dump_seq_mtx);
|
||||||
prof_dump_filename(filename, 'f', 0xffffffffffffffffLLU);
|
prof_dump_filename(filename, 'f', 0xffffffffffffffffLLU);
|
||||||
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
||||||
prof_dump(filename, opt_prof_leak);
|
prof_dump(filename, opt_prof_leak, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1056,22 +1098,26 @@ prof_idump(void)
|
|||||||
prof_dump_filename(filename, 'i', prof_dump_iseq);
|
prof_dump_filename(filename, 'i', prof_dump_iseq);
|
||||||
prof_dump_iseq++;
|
prof_dump_iseq++;
|
||||||
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
||||||
prof_dump(filename, false);
|
prof_dump(filename, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
prof_mdump(void)
|
prof_mdump(const char *filename)
|
||||||
{
|
{
|
||||||
char filename[DUMP_FILENAME_BUFSIZE];
|
char filename_buf[DUMP_FILENAME_BUFSIZE];
|
||||||
|
|
||||||
if (prof_booted == false)
|
if (opt_prof == false || prof_booted == false)
|
||||||
return;
|
return (true);
|
||||||
|
|
||||||
malloc_mutex_lock(&prof_dump_seq_mtx);
|
if (filename == NULL) {
|
||||||
prof_dump_filename(filename, 'm', prof_dump_mseq);
|
/* No filename specified, so automatically generate one. */
|
||||||
prof_dump_mseq++;
|
malloc_mutex_lock(&prof_dump_seq_mtx);
|
||||||
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
prof_dump_filename(filename_buf, 'm', prof_dump_mseq);
|
||||||
prof_dump(filename, false);
|
prof_dump_mseq++;
|
||||||
|
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
||||||
|
filename = filename_buf;
|
||||||
|
}
|
||||||
|
return (prof_dump(filename, false, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1093,7 +1139,7 @@ prof_udump(void)
|
|||||||
prof_dump_filename(filename, 'u', prof_dump_useq);
|
prof_dump_filename(filename, 'u', prof_dump_useq);
|
||||||
prof_dump_useq++;
|
prof_dump_useq++;
|
||||||
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
malloc_mutex_unlock(&prof_dump_seq_mtx);
|
||||||
prof_dump(filename, false);
|
prof_dump(filename, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user