diff --git a/Makefile.in b/Makefile.in index ba6dd763..3cb3161e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -139,6 +139,7 @@ C_SRCS := $(srcroot)src/jemalloc.c \ $(srcroot)src/prof_data.c \ $(srcroot)src/prof_log.c \ $(srcroot)src/prof_recent.c \ + $(srcroot)src/prof_stats.c \ $(srcroot)src/prof_sys.c \ $(srcroot)src/psset.c \ $(srcroot)src/rtree.c \ @@ -248,6 +249,7 @@ TESTS_UNIT := \ $(srcroot)test/unit/prof_mdump.c \ $(srcroot)test/unit/prof_recent.c \ $(srcroot)test/unit/prof_reset.c \ + $(srcroot)test/unit/prof_stats.c \ $(srcroot)test/unit/prof_tctx.c \ $(srcroot)test/unit/prof_thread_name.c \ $(srcroot)test/unit/prof_sys_thread_name.c \ diff --git a/include/jemalloc/internal/prof_externs.h b/include/jemalloc/internal/prof_externs.h index b94fbed3..671ac9b8 100644 --- a/include/jemalloc/internal/prof_externs.h +++ b/include/jemalloc/internal/prof_externs.h @@ -27,6 +27,9 @@ extern ssize_t opt_prof_recent_alloc_max; /* Whether to use thread name provided by the system or by mallctl. */ extern bool opt_prof_sys_thread_name; +/* Whether to record per size class counts and request size totals. */ +extern bool opt_prof_stats; + /* Accessed via prof_active_[gs]et{_unlocked,}(). */ extern bool prof_active; diff --git a/include/jemalloc/internal/prof_stats.h b/include/jemalloc/internal/prof_stats.h new file mode 100644 index 00000000..7954e82d --- /dev/null +++ b/include/jemalloc/internal/prof_stats.h @@ -0,0 +1,17 @@ +#ifndef JEMALLOC_INTERNAL_PROF_STATS_H +#define JEMALLOC_INTERNAL_PROF_STATS_H + +typedef struct prof_stats_s prof_stats_t; +struct prof_stats_s { + uint64_t req_sum; + uint64_t count; +}; + +extern malloc_mutex_t prof_stats_mtx; + +void prof_stats_inc(tsd_t *tsd, szind_t ind, size_t size); +void prof_stats_dec(tsd_t *tsd, szind_t ind, size_t size); +void prof_stats_get_live(tsd_t *tsd, szind_t ind, prof_stats_t *stats); +void prof_stats_get_accum(tsd_t *tsd, szind_t ind, prof_stats_t *stats); + +#endif /* JEMALLOC_INTERNAL_PROF_STATS_H */ diff --git a/include/jemalloc/internal/witness.h b/include/jemalloc/internal/witness.h index 662907c8..66dcf664 100644 --- a/include/jemalloc/internal/witness.h +++ b/include/jemalloc/internal/witness.h @@ -73,6 +73,7 @@ enum witness_rank_e { WITNESS_RANK_PROF_GDUMP = WITNESS_RANK_LEAF, WITNESS_RANK_PROF_NEXT_THR_UID = WITNESS_RANK_LEAF, WITNESS_RANK_PROF_RECENT_ALLOC = WITNESS_RANK_LEAF, + WITNESS_RANK_PROF_STATS = WITNESS_RANK_LEAF, WITNESS_RANK_PROF_THREAD_ACTIVE_INIT = WITNESS_RANK_LEAF, }; typedef enum witness_rank_e witness_rank_t; diff --git a/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj b/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj index 531dd9a6..9443ac55 100644 --- a/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj +++ b/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj @@ -80,6 +80,7 @@ + diff --git a/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters b/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters index f031fb10..3c4bff62 100644 --- a/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters +++ b/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters @@ -124,6 +124,9 @@ Source Files + + Source Files + Source Files diff --git a/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj b/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj index bc64de5c..fafb4914 100644 --- a/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj +++ b/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj @@ -80,6 +80,7 @@ + diff --git a/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters b/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters index f031fb10..3c4bff62 100644 --- a/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters +++ b/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters @@ -124,6 +124,9 @@ Source Files + + Source Files + Source Files diff --git a/src/ctl.c b/src/ctl.c index 8f6aff3e..598759cd 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -136,6 +136,7 @@ CTL_PROTO(opt_prof_final) CTL_PROTO(opt_prof_leak) CTL_PROTO(opt_prof_accum) CTL_PROTO(opt_prof_recent_alloc_max) +CTL_PROTO(opt_prof_stats) CTL_PROTO(opt_prof_sys_thread_name) CTL_PROTO(opt_prof_time_res) CTL_PROTO(opt_zero_realloc) @@ -415,6 +416,7 @@ static const ctl_named_node_t opt_node[] = { {NAME("prof_leak"), CTL(opt_prof_leak)}, {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)}, {NAME("prof_sys_thread_name"), CTL(opt_prof_sys_thread_name)}, {NAME("prof_time_resolution"), CTL(opt_prof_time_res)}, {NAME("zero_realloc"), CTL(opt_zero_realloc)} @@ -2057,6 +2059,7 @@ 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_recent_alloc_max, opt_prof_recent_alloc_max, ssize_t) +CTL_RO_NL_CGEN(config_prof, opt_prof_stats, opt_prof_stats, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_sys_thread_name, opt_prof_sys_thread_name, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_time_res, diff --git a/src/jemalloc.c b/src/jemalloc.c index b0a3b76b..02714158 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c @@ -1552,6 +1552,7 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], CONF_HANDLE_BOOL(opt_prof_log, "prof_log") CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max, "prof_recent_alloc_max", -1, SSIZE_MAX) + CONF_HANDLE_BOOL(opt_prof_stats, "prof_stats") CONF_HANDLE_BOOL(opt_prof_sys_thread_name, "prof_sys_thread_name") if (CONF_MATCH("prof_time_resolution")) { diff --git a/src/prof.c b/src/prof.c index 258b5f2d..0f1f7a71 100644 --- a/src/prof.c +++ b/src/prof.c @@ -8,6 +8,7 @@ #include "jemalloc/internal/prof_data.h" #include "jemalloc/internal/prof_log.h" #include "jemalloc/internal/prof_recent.h" +#include "jemalloc/internal/prof_stats.h" #include "jemalloc/internal/prof_sys.h" #include "jemalloc/internal/thread_event.h" @@ -131,6 +132,10 @@ prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size, assert(tctx == edata_prof_tctx_get(edata)); prof_recent_alloc(tsd, edata, size, usize); } + + if (opt_prof_stats) { + prof_stats_inc(tsd, szind, size); + } } void @@ -160,6 +165,10 @@ prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info) { prof_try_log(tsd, usize, prof_info); prof_tctx_try_destroy(tsd, tctx); + + if (opt_prof_stats) { + prof_stats_dec(tsd, szind, prof_info->alloc_size); + } } prof_tctx_t * @@ -587,7 +596,13 @@ prof_boot2(tsd_t *tsd, base_t *base) { next_thr_uid = 0; if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid", - WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) { + WITNESS_RANK_PROF_NEXT_THR_UID, + malloc_mutex_rank_exclusive)) { + return true; + } + + if (malloc_mutex_init(&prof_stats_mtx, "prof_stats", + WITNESS_RANK_PROF_STATS, malloc_mutex_rank_exclusive)) { return true; } @@ -595,8 +610,9 @@ prof_boot2(tsd_t *tsd, base_t *base) { return true; } - if (malloc_mutex_init(&prof_dump_filename_mtx, "prof_dump_filename", - WITNESS_RANK_PROF_DUMP_FILENAME, malloc_mutex_rank_exclusive)) { + if (malloc_mutex_init(&prof_dump_filename_mtx, + "prof_dump_filename", WITNESS_RANK_PROF_DUMP_FILENAME, + malloc_mutex_rank_exclusive)) { return true; } if (malloc_mutex_init(&prof_dump_mtx, "prof_dump", @@ -681,9 +697,10 @@ prof_prefork1(tsdn_t *tsdn) { malloc_mutex_prefork(tsdn, &prof_active_mtx); malloc_mutex_prefork(tsdn, &prof_dump_filename_mtx); malloc_mutex_prefork(tsdn, &prof_gdump_mtx); + malloc_mutex_prefork(tsdn, &prof_recent_alloc_mtx); + malloc_mutex_prefork(tsdn, &prof_stats_mtx); malloc_mutex_prefork(tsdn, &next_thr_uid_mtx); malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx); - malloc_mutex_prefork(tsdn, &prof_recent_alloc_mtx); } } @@ -692,10 +709,11 @@ prof_postfork_parent(tsdn_t *tsdn) { if (config_prof && opt_prof) { unsigned i; - malloc_mutex_postfork_parent(tsdn, &prof_recent_alloc_mtx); malloc_mutex_postfork_parent(tsdn, &prof_thread_active_init_mtx); malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx); + malloc_mutex_postfork_parent(tsdn, &prof_stats_mtx); + malloc_mutex_postfork_parent(tsdn, &prof_recent_alloc_mtx); malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx); malloc_mutex_postfork_parent(tsdn, &prof_dump_filename_mtx); malloc_mutex_postfork_parent(tsdn, &prof_active_mtx); @@ -719,9 +737,10 @@ prof_postfork_child(tsdn_t *tsdn) { if (config_prof && opt_prof) { unsigned i; - malloc_mutex_postfork_child(tsdn, &prof_recent_alloc_mtx); malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx); malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx); + malloc_mutex_postfork_child(tsdn, &prof_stats_mtx); + malloc_mutex_postfork_child(tsdn, &prof_recent_alloc_mtx); malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx); malloc_mutex_postfork_child(tsdn, &prof_dump_filename_mtx); malloc_mutex_postfork_child(tsdn, &prof_active_mtx); diff --git a/src/prof_stats.c b/src/prof_stats.c new file mode 100644 index 00000000..5d1a506b --- /dev/null +++ b/src/prof_stats.c @@ -0,0 +1,57 @@ +#include "jemalloc/internal/jemalloc_preamble.h" +#include "jemalloc/internal/jemalloc_internal_includes.h" + +#include "jemalloc/internal/prof_stats.h" + +bool opt_prof_stats = false; +malloc_mutex_t prof_stats_mtx; +static prof_stats_t prof_stats_live[PROF_SC_NSIZES]; +static prof_stats_t prof_stats_accum[PROF_SC_NSIZES]; + +static void +prof_stats_enter(tsd_t *tsd, szind_t ind) { + assert(opt_prof && opt_prof_stats); + assert(ind < SC_NSIZES); + malloc_mutex_lock(tsd_tsdn(tsd), &prof_stats_mtx); +} + +static void +prof_stats_leave(tsd_t *tsd) { + malloc_mutex_unlock(tsd_tsdn(tsd), &prof_stats_mtx); +} + +void +prof_stats_inc(tsd_t *tsd, szind_t ind, size_t size) { + cassert(config_prof); + prof_stats_enter(tsd, ind); + prof_stats_live[ind].req_sum += size; + prof_stats_live[ind].count++; + prof_stats_accum[ind].req_sum += size; + prof_stats_accum[ind].count++; + prof_stats_leave(tsd); +} + +void +prof_stats_dec(tsd_t *tsd, szind_t ind, size_t size) { + cassert(config_prof); + prof_stats_enter(tsd, ind); + prof_stats_live[ind].req_sum -= size; + prof_stats_live[ind].count--; + prof_stats_leave(tsd); +} + +void +prof_stats_get_live(tsd_t *tsd, szind_t ind, prof_stats_t *stats) { + cassert(config_prof); + prof_stats_enter(tsd, ind); + memcpy(stats, &prof_stats_live[ind], sizeof(prof_stats_t)); + prof_stats_leave(tsd); +} + +void +prof_stats_get_accum(tsd_t *tsd, szind_t ind, prof_stats_t *stats) { + cassert(config_prof); + prof_stats_enter(tsd, ind); + memcpy(stats, &prof_stats_accum[ind], sizeof(prof_stats_t)); + prof_stats_leave(tsd); +} diff --git a/test/unit/mallctl.c b/test/unit/mallctl.c index 3d5b2788..85dcb4e2 100644 --- a/test/unit/mallctl.c +++ b/test/unit/mallctl.c @@ -317,6 +317,7 @@ TEST_BEGIN(test_mallctl_opt) { TEST_MALLCTL_OPT(bool, prof_final, prof); TEST_MALLCTL_OPT(bool, prof_leak, 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); #undef TEST_MALLCTL_OPT diff --git a/test/unit/prof_stats.c b/test/unit/prof_stats.c new file mode 100644 index 00000000..555b69e3 --- /dev/null +++ b/test/unit/prof_stats.c @@ -0,0 +1,80 @@ +#include "test/jemalloc_test.h" + +#include "jemalloc/internal/prof_stats.h" + +static void +test_wrapper(szind_t ind) { +#define N_PTRS 3 + assert(opt_prof && opt_prof_stats); + + tsd_t *tsd = tsd_fetch(); + + prof_stats_t live_stats_orig; + prof_stats_get_live(tsd, ind, &live_stats_orig); + prof_stats_t accum_stats_orig; + prof_stats_get_accum(tsd, ind, &accum_stats_orig); + + void *ptrs[N_PTRS]; + + uint64_t live_req_sum = 0; + uint64_t live_count = 0; + uint64_t accum_req_sum = 0; + uint64_t accum_count = 0; + + for (size_t i = 0, sz = sz_index2size(ind) - N_PTRS; i < N_PTRS; + ++i, ++sz) { + void *p = malloc(sz); + assert_ptr_not_null(p, "malloc() failed"); + ptrs[i] = p; + live_req_sum += sz; + live_count++; + accum_req_sum += sz; + accum_count++; + prof_stats_t live_stats; + prof_stats_get_live(tsd, ind, &live_stats); + expect_u64_eq(live_stats.req_sum - live_stats_orig.req_sum, + live_req_sum, ""); + expect_u64_eq(live_stats.count - live_stats_orig.count, + live_count, ""); + prof_stats_t accum_stats; + prof_stats_get_accum(tsd, ind, &accum_stats); + expect_u64_eq(accum_stats.req_sum - accum_stats_orig.req_sum, + accum_req_sum, ""); + expect_u64_eq(accum_stats.count - accum_stats_orig.count, + accum_count, ""); + } + + for (size_t i = 0, sz = sz_index2size(ind) - N_PTRS; i < N_PTRS; + ++i, ++sz) { + free(ptrs[i]); + live_req_sum -= sz; + live_count--; + prof_stats_t live_stats; + prof_stats_get_live(tsd, ind, &live_stats); + expect_u64_eq(live_stats.req_sum - live_stats_orig.req_sum, + live_req_sum, ""); + expect_u64_eq(live_stats.count - live_stats_orig.count, + live_count, ""); + prof_stats_t accum_stats; + prof_stats_get_accum(tsd, ind, &accum_stats); + expect_u64_eq(accum_stats.req_sum - accum_stats_orig.req_sum, + accum_req_sum, ""); + expect_u64_eq(accum_stats.count - accum_stats_orig.count, + accum_count, ""); + } +#undef N_PTRS +} + +TEST_BEGIN(test_prof_stats) { + test_skip_if(!config_prof); + test_wrapper(0); + test_wrapper(1); + test_wrapper(2); +} +TEST_END + +int +main(void) { + return test( + test_prof_stats); +} diff --git a/test/unit/prof_stats.sh b/test/unit/prof_stats.sh new file mode 100644 index 00000000..b01dfd45 --- /dev/null +++ b/test/unit/prof_stats.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ "x${enable_prof}" = "x1" ] ; then + export MALLOC_CONF="prof:true,lg_prof_sample:0,prof_stats:true" +fi