From 2256ef896177faf8af7b199595382348be054250 Mon Sep 17 00:00:00 2001 From: Yinan Zhang Date: Tue, 24 Mar 2020 17:53:41 -0700 Subject: [PATCH] Add option to fetch system thread name on each prof sample --- Makefile.in | 1 + include/jemalloc/internal/prof_externs.h | 5 + src/ctl.c | 5 + src/jemalloc.c | 3 + src/prof.c | 155 ++++++++++++++--------- test/unit/mallctl.c | 1 + test/unit/prof_use_sys_thread_name.c | 75 +++++++++++ test/unit/prof_use_sys_thread_name.sh | 5 + 8 files changed, 192 insertions(+), 58 deletions(-) create mode 100644 test/unit/prof_use_sys_thread_name.c create mode 100644 test/unit/prof_use_sys_thread_name.sh diff --git a/Makefile.in b/Makefile.in index 7eca2f5f..7300cb98 100644 --- a/Makefile.in +++ b/Makefile.in @@ -227,6 +227,7 @@ TESTS_UNIT := \ $(srcroot)test/unit/prof_reset.c \ $(srcroot)test/unit/prof_tctx.c \ $(srcroot)test/unit/prof_thread_name.c \ + $(srcroot)test/unit/prof_use_sys_thread_name.c \ $(srcroot)test/unit/ql.c \ $(srcroot)test/unit/qr.c \ $(srcroot)test/unit/rb.c \ diff --git a/include/jemalloc/internal/prof_externs.h b/include/jemalloc/internal/prof_externs.h index 5a32754e..35181671 100644 --- a/include/jemalloc/internal/prof_externs.h +++ b/include/jemalloc/internal/prof_externs.h @@ -28,6 +28,9 @@ extern char opt_prof_prefix[ extern ssize_t opt_prof_recent_alloc_max; extern malloc_mutex_t prof_recent_alloc_mtx; +/* Whether to use thread name provided by the system or by mallctl. */ +extern bool opt_prof_experimental_use_sys_thread_name; + /* Accessed via prof_active_[gs]et{_unlocked,}(). */ extern bool prof_active; @@ -59,6 +62,8 @@ void prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size, void prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info); prof_tctx_t *prof_tctx_create(tsd_t *tsd); #ifdef JEMALLOC_JET +typedef int (prof_read_sys_thread_name_t)(char *buf, size_t limit); +extern prof_read_sys_thread_name_t *JET_MUTABLE prof_read_sys_thread_name; size_t prof_tdata_count(void); size_t prof_bt_count(void); #endif diff --git a/src/ctl.c b/src/ctl.c index d149ce6d..86ac83e1 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -117,6 +117,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_experimental_use_sys_thread_name) CTL_PROTO(opt_zero_realloc) CTL_PROTO(tcache_create) CTL_PROTO(tcache_flush) @@ -353,6 +354,8 @@ 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_experimental_use_sys_thread_name"), + CTL(opt_prof_experimental_use_sys_thread_name)}, {NAME("zero_realloc"), CTL(opt_zero_realloc)} }; @@ -1829,6 +1832,8 @@ 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_experimental_use_sys_thread_name, + opt_prof_experimental_use_sys_thread_name, bool) CTL_RO_NL_GEN(opt_zero_realloc, zero_realloc_mode_names[opt_zero_realloc_action], const char *) diff --git a/src/jemalloc.c b/src/jemalloc.c index 8561ef40..ea331f82 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c @@ -1426,6 +1426,9 @@ 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_experimental_use_sys_thread_name, + "prof_experimental_use_sys_thread_name") } if (config_log) { if (CONF_MATCH("log")) { diff --git a/src/prof.c b/src/prof.c index 73e6d914..e68694a8 100644 --- a/src/prof.c +++ b/src/prof.c @@ -48,6 +48,7 @@ bool opt_prof_final = false; bool opt_prof_leak = false; bool opt_prof_accum = false; char opt_prof_prefix[PROF_DUMP_FILENAME_LEN]; +bool opt_prof_experimental_use_sys_thread_name = false; /* Accessed via prof_idump_[accum/rollback](). */ static counter_accum_t prof_idump_accumulated; @@ -133,9 +134,101 @@ prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx) { } } +static char * +prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) { + char *ret; + size_t size; + + if (thread_name == NULL) { + return NULL; + } + + size = strlen(thread_name) + 1; + if (size == 1) { + return ""; + } + + ret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true, + arena_get(TSDN_NULL, 0, true), true); + if (ret == NULL) { + return NULL; + } + memcpy(ret, thread_name, size); + return ret; +} + +static int +prof_thread_name_set_impl(tsd_t *tsd, const char *thread_name) { + assert(tsd_reentrancy_level_get(tsd) == 0); + + prof_tdata_t *tdata; + unsigned i; + char *s; + + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) { + return EAGAIN; + } + + /* Validate input. */ + if (thread_name == NULL) { + return EFAULT; + } + for (i = 0; thread_name[i] != '\0'; i++) { + char c = thread_name[i]; + if (!isgraph(c) && !isblank(c)) { + return EFAULT; + } + } + + s = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name); + if (s == NULL) { + return EAGAIN; + } + + if (tdata->thread_name != NULL) { + idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true, + true); + tdata->thread_name = NULL; + } + if (strlen(s) > 0) { + tdata->thread_name = s; + } + return 0; +} + +static int +prof_read_sys_thread_name_impl(char *buf, size_t limit) { +#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP + return pthread_getname_np(pthread_self(), buf, limit); +#else + return ENOSYS; +#endif +} +#ifdef JEMALLOC_JET +prof_read_sys_thread_name_t *JET_MUTABLE prof_read_sys_thread_name = + prof_read_sys_thread_name_impl; +#else +#define prof_read_sys_thread_name prof_read_sys_thread_name_impl +#endif + +static void +prof_fetch_sys_thread_name(tsd_t *tsd) { +#define THREAD_NAME_MAX_LEN 16 + char buf[THREAD_NAME_MAX_LEN]; + if (!prof_read_sys_thread_name(buf, THREAD_NAME_MAX_LEN)) { + prof_thread_name_set_impl(tsd, buf); + } +#undef THREAD_NAME_MAX_LEN +} + void prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size, size_t usize, prof_tctx_t *tctx) { + if (opt_prof_experimental_use_sys_thread_name) { + prof_fetch_sys_thread_name(tsd); + } + edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd), &emap_global, ptr); prof_info_set(tsd, edata, tctx); @@ -710,29 +803,6 @@ prof_tdata_init(tsd_t *tsd) { NULL, prof_thread_active_init_get(tsd_tsdn(tsd)), false); } -static char * -prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) { - char *ret; - size_t size; - - if (thread_name == NULL) { - return NULL; - } - - size = strlen(thread_name) + 1; - if (size == 1) { - return ""; - } - - ret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true, - arena_get(TSDN_NULL, 0, true), true); - if (ret == NULL) { - return NULL; - } - memcpy(ret, thread_name, size); - return ret; -} - prof_tdata_t * prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) { uint64_t thr_uid = tdata->thr_uid; @@ -799,42 +869,11 @@ prof_thread_name_get(tsd_t *tsd) { int prof_thread_name_set(tsd_t *tsd, const char *thread_name) { - assert(tsd_reentrancy_level_get(tsd) == 0); - - prof_tdata_t *tdata; - unsigned i; - char *s; - - tdata = prof_tdata_get(tsd, true); - if (tdata == NULL) { - return EAGAIN; + if (opt_prof_experimental_use_sys_thread_name) { + return ENOENT; + } else { + return prof_thread_name_set_impl(tsd, thread_name); } - - /* Validate input. */ - if (thread_name == NULL) { - return EFAULT; - } - for (i = 0; thread_name[i] != '\0'; i++) { - char c = thread_name[i]; - if (!isgraph(c) && !isblank(c)) { - return EFAULT; - } - } - - s = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name); - if (s == NULL) { - return EAGAIN; - } - - if (tdata->thread_name != NULL) { - idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true, - true); - tdata->thread_name = NULL; - } - if (strlen(s) > 0) { - tdata->thread_name = s; - } - return 0; } bool diff --git a/test/unit/mallctl.c b/test/unit/mallctl.c index e38723f6..cc1d5313 100644 --- a/test/unit/mallctl.c +++ b/test/unit/mallctl.c @@ -192,6 +192,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_experimental_use_sys_thread_name, prof); #undef TEST_MALLCTL_OPT } diff --git a/test/unit/prof_use_sys_thread_name.c b/test/unit/prof_use_sys_thread_name.c new file mode 100644 index 00000000..60cb55bf --- /dev/null +++ b/test/unit/prof_use_sys_thread_name.c @@ -0,0 +1,75 @@ +#include "test/jemalloc_test.h" + +static const char *test_thread_name = "test_name"; + +static int +test_prof_read_sys_thread_name_error(char *buf, size_t limit) { + return ENOSYS; +} + +static int +test_prof_read_sys_thread_name(char *buf, size_t limit) { + assert(strlen(test_thread_name) < limit); + strncpy(buf, test_thread_name, limit); + return 0; +} + +static int +test_prof_read_sys_thread_name_clear(char *buf, size_t limit) { + assert(limit > 0); + buf[0] = '\0'; + return 0; +} + +TEST_BEGIN(test_prof_experimental_use_sys_thread_name) { + test_skip_if(!config_prof); + + bool oldval; + size_t sz = sizeof(oldval); + assert_d_eq(mallctl("opt.prof_experimental_use_sys_thread_name", + &oldval, &sz, NULL, 0), 0, "mallctl failed"); + assert_true(oldval, "option was not set correctly"); + + const char *thread_name; + sz = sizeof(thread_name); + assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0, + "mallctl read for thread name should not fail"); + expect_str_eq(thread_name, "", "Initial thread name should be empty"); + + thread_name = test_thread_name; + assert_d_eq(mallctl("thread.prof.name", NULL, NULL, &thread_name, sz), + ENOENT, "mallctl write for thread name should fail"); + assert_ptr_eq(thread_name, test_thread_name, + "Thread name should not be touched"); + + prof_read_sys_thread_name = test_prof_read_sys_thread_name_error; + void *p = malloc(1); + free(p); + assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0, + "mallctl read for thread name should not fail"); + assert_str_eq(thread_name, "", + "Thread name should stay the same if the system call fails"); + + prof_read_sys_thread_name = test_prof_read_sys_thread_name; + p = malloc(1); + free(p); + assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0, + "mallctl read for thread name should not fail"); + assert_str_eq(thread_name, test_thread_name, + "Thread name should be changed if the system call succeeds"); + + prof_read_sys_thread_name = test_prof_read_sys_thread_name_clear; + p = malloc(1); + free(p); + assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0, + "mallctl read for thread name should not fail"); + expect_str_eq(thread_name, "", "Thread name should be updated if the " + "system call returns a different name"); +} +TEST_END + +int +main(void) { + return test( + test_prof_experimental_use_sys_thread_name); +} diff --git a/test/unit/prof_use_sys_thread_name.sh b/test/unit/prof_use_sys_thread_name.sh new file mode 100644 index 00000000..0e0e0d99 --- /dev/null +++ b/test/unit/prof_use_sys_thread_name.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ "x${enable_prof}" = "x1" ] ; then + export MALLOC_CONF="prof:true,lg_prof_sample:0,prof_experimental_use_sys_thread_name:true" +fi