From b798fabdf7c86288f303b1e0bcf877c9ded67c18 Mon Sep 17 00:00:00 2001 From: yunxu Date: Wed, 12 Jan 2022 18:46:34 +0800 Subject: [PATCH] Add prof_leak_error option The option makes the process to exit with error code 1 if a memory leak is detected. This is useful for implementing automated tools that rely on leak detection. --- doc/jemalloc.xml.in | 19 +++++++++++++++++++ include/jemalloc/internal/prof_externs.h | 1 + src/ctl.c | 3 +++ src/jemalloc.c | 20 ++++++++++++++++++++ src/prof.c | 1 + src/prof_data.c | 10 ++++++++++ src/stats.c | 1 + test/unit/mallctl.c | 1 + 8 files changed, 56 insertions(+) diff --git a/doc/jemalloc.xml.in b/doc/jemalloc.xml.in index cba0b3f6..6e2099ad 100644 --- a/doc/jemalloc.xml.in +++ b/doc/jemalloc.xml.in @@ -1553,6 +1553,25 @@ malloc_conf = "xmalloc:true";]]> + + + opt.prof_leak_error + (bool) + r- + [] + + Similar to + opt.prof_leak, but makes the process exit with error + code 1 if a memory leak is detected. This option supersedes + opt.prof_leak, + meaning that if both are specified, this option takes precedence. When + enabled, also enables + opt.prof_leak. Works only when combined with + opt.prof_final, + otherwise does nothing. This option is disabled by default. + + + opt.zero_realloc diff --git a/include/jemalloc/internal/prof_externs.h b/include/jemalloc/internal/prof_externs.h index 953192f4..bdff1349 100644 --- a/include/jemalloc/internal/prof_externs.h +++ b/include/jemalloc/internal/prof_externs.h @@ -12,6 +12,7 @@ extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */ extern bool opt_prof_gdump; /* High-water memory dumping. */ extern bool opt_prof_final; /* Final profile dumping. */ extern bool opt_prof_leak; /* Dump leak summary at exit. */ +extern bool opt_prof_leak_error; /* Exit with error code if memory leaked */ extern bool opt_prof_accum; /* Report cumulative bytes. */ extern bool opt_prof_log; /* Turn logging on at boot. */ extern char opt_prof_prefix[ diff --git a/src/ctl.c b/src/ctl.c index 6e0088f6..54d33aed 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -145,6 +145,7 @@ CTL_PROTO(opt_lg_prof_interval) CTL_PROTO(opt_prof_gdump) CTL_PROTO(opt_prof_final) CTL_PROTO(opt_prof_leak) +CTL_PROTO(opt_prof_leak_error) CTL_PROTO(opt_prof_accum) CTL_PROTO(opt_prof_recent_alloc_max) CTL_PROTO(opt_prof_stats) @@ -469,6 +470,7 @@ static const ctl_named_node_t opt_node[] = { {NAME("prof_gdump"), CTL(opt_prof_gdump)}, {NAME("prof_final"), CTL(opt_prof_final)}, {NAME("prof_leak"), CTL(opt_prof_leak)}, + {NAME("prof_leak_error"), CTL(opt_prof_leak_error)}, {NAME("prof_accum"), CTL(opt_prof_accum)}, {NAME("prof_recent_alloc_max"), CTL(opt_prof_recent_alloc_max)}, {NAME("prof_stats"), CTL(opt_prof_stats)}, @@ -2201,6 +2203,7 @@ CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool) +CTL_RO_NL_CGEN(config_prof, opt_prof_leak_error, opt_prof_leak_error, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_recent_alloc_max, opt_prof_recent_alloc_max, ssize_t) CTL_RO_NL_CGEN(config_prof, opt_prof_stats, opt_prof_stats, bool) diff --git a/src/jemalloc.c b/src/jemalloc.c index 17a27ae0..117a005c 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c @@ -1578,6 +1578,26 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump") CONF_HANDLE_BOOL(opt_prof_final, "prof_final") CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak") + if (CONF_MATCH("prof_leak_error")) { + if (CONF_MATCH_VALUE("true")) { + if (!opt_prof_final) { + CONF_ERROR( + "prof_leak_error is" + " not allowed" + " without" + " prof_leak_final", + k, klen, v, vlen); + } else { + opt_prof_leak = true; + opt_prof_leak_error = + true; + } + } else if (!CONF_MATCH_VALUE("false")) { + CONF_ERROR("Invalid conf value", + k, klen, v, vlen); + } + CONF_CONTINUE; + } CONF_HANDLE_BOOL(opt_prof_log, "prof_log") CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max, "prof_recent_alloc_max", -1, SSIZE_MAX) diff --git a/src/prof.c b/src/prof.c index f708d108..cbfc7409 100644 --- a/src/prof.c +++ b/src/prof.c @@ -31,6 +31,7 @@ ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT; bool opt_prof_gdump = false; bool opt_prof_final = false; bool opt_prof_leak = false; +bool opt_prof_leak_error = false; bool opt_prof_accum = false; char opt_prof_prefix[PROF_DUMP_FILENAME_LEN]; bool opt_prof_sys_thread_name = false; diff --git a/src/prof_data.c b/src/prof_data.c index 3ef0100d..bfa55be1 100644 --- a/src/prof_data.c +++ b/src/prof_data.c @@ -1037,6 +1037,16 @@ prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx) { 1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : ""); malloc_printf( ": Run jeprof on dump output for leak detail\n"); + if (opt_prof_leak_error) { + malloc_printf( + ": Exiting with error code because memory" + " leaks were detected\n"); + /* + * Use _exit() with underscore to avoid calling atexit() + * and entering endless cycle. + */ + _exit(1); + } } #endif } diff --git a/src/stats.c b/src/stats.c index bed585b1..efc70fd3 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1530,6 +1530,7 @@ stats_general_print(emitter_t *emitter) { OPT_WRITE_BOOL("prof_gdump") OPT_WRITE_BOOL("prof_final") OPT_WRITE_BOOL("prof_leak") + OPT_WRITE_BOOL("prof_leak_error") OPT_WRITE_BOOL("stats_print") OPT_WRITE_CHAR_P("stats_print_opts") OPT_WRITE_BOOL("stats_print") diff --git a/test/unit/mallctl.c b/test/unit/mallctl.c index bd5ef9e5..6efc8f1b 100644 --- a/test/unit/mallctl.c +++ b/test/unit/mallctl.c @@ -320,6 +320,7 @@ TEST_BEGIN(test_mallctl_opt) { TEST_MALLCTL_OPT(bool, prof_gdump, prof); TEST_MALLCTL_OPT(bool, prof_final, prof); TEST_MALLCTL_OPT(bool, prof_leak, prof); + TEST_MALLCTL_OPT(bool, prof_leak_error, prof); TEST_MALLCTL_OPT(ssize_t, prof_recent_alloc_max, prof); TEST_MALLCTL_OPT(bool, prof_stats, prof); TEST_MALLCTL_OPT(bool, prof_sys_thread_name, prof);