diff --git a/include/jemalloc/internal/arena_externs.h b/include/jemalloc/internal/arena_externs.h index e6fceaaf..9f5c1958 100644 --- a/include/jemalloc/internal/arena_externs.h +++ b/include/jemalloc/internal/arena_externs.h @@ -88,6 +88,8 @@ ehooks_t *arena_get_ehooks(arena_t *arena); extent_hooks_t *arena_set_extent_hooks(tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks); bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec); +void arena_name_get(arena_t *arena, char *name); +void arena_name_set(arena_t *arena, const char *name); ssize_t arena_dirty_decay_ms_default_get(void); bool arena_dirty_decay_ms_default_set(ssize_t decay_ms); ssize_t arena_muzzy_decay_ms_default_get(void); diff --git a/include/jemalloc/internal/arena_structs.h b/include/jemalloc/internal/arena_structs.h index e2a5a408..e6868fce 100644 --- a/include/jemalloc/internal/arena_structs.h +++ b/include/jemalloc/internal/arena_structs.h @@ -91,6 +91,9 @@ struct arena_s { /* Used to determine uptime. Read-only after initialization. */ nstime_t create_time; + /* The name of the arena. */ + char name[ARENA_NAME_LEN]; + /* * The arena is allocated alongside its bins; really this is a * dynamically sized array determined by the binshard settings. diff --git a/include/jemalloc/internal/arena_types.h b/include/jemalloc/internal/arena_types.h index d0e12917..45eec69f 100644 --- a/include/jemalloc/internal/arena_types.h +++ b/include/jemalloc/internal/arena_types.h @@ -8,6 +8,8 @@ #define MUZZY_DECAY_MS_DEFAULT (0) /* Number of event ticks between time checks. */ #define ARENA_DECAY_NTICKS_PER_UPDATE 1000 +/* Maximum length of the arena name. */ +#define ARENA_NAME_LEN 32 typedef struct arena_decay_s arena_decay_t; typedef struct arena_s arena_t; diff --git a/src/arena.c b/src/arena.c index 1ab2775e..25ab41af 100644 --- a/src/arena.c +++ b/src/arena.c @@ -1547,6 +1547,22 @@ arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) { return false; } +void +arena_name_get(arena_t *arena, char *name) { + char *end = (char *)memchr((void *)arena->name, '\0', ARENA_NAME_LEN); + assert(end != NULL); + size_t len = (uintptr_t)end - (uintptr_t)arena->name + 1; + assert(len > 0 && len <= ARENA_NAME_LEN); + + strncpy(name, arena->name, len); +} + +void +arena_name_set(arena_t *arena, const char *name) { + strncpy(arena->name, name, ARENA_NAME_LEN); + arena->name[ARENA_NAME_LEN - 1] = '\0'; +} + ssize_t arena_dirty_decay_ms_default_get(void) { return atomic_load_zd(&dirty_decay_ms_default, ATOMIC_RELAXED); @@ -1670,6 +1686,11 @@ arena_new(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) { arena_set(ind, arena); arena->ind = ind; + /* Init the name. */ + malloc_snprintf(arena->name, sizeof(arena->name), "%s_%u", + arena_is_auto(arena) ? "auto" : "manual", arena->ind); + arena->name[ARENA_NAME_LEN - 1] = '\0'; + nstime_init_update(&arena->create_time); /* diff --git a/src/ctl.c b/src/ctl.c index 6b03f986..acf5d366 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -170,6 +170,7 @@ CTL_PROTO(arena_i_dirty_decay_ms) CTL_PROTO(arena_i_muzzy_decay_ms) CTL_PROTO(arena_i_extent_hooks) CTL_PROTO(arena_i_retain_grow_limit) +CTL_PROTO(arena_i_name) INDEX_PROTO(arena_i) CTL_PROTO(arenas_bin_i_size) CTL_PROTO(arenas_bin_i_nregs) @@ -504,11 +505,12 @@ static const ctl_named_node_t arena_i_node[] = { * Undocumented for now, since we anticipate an arena API in flux after * we cut the last 5-series release. */ - {NAME("oversize_threshold"), CTL(arena_i_oversize_threshold)}, - {NAME("dirty_decay_ms"), CTL(arena_i_dirty_decay_ms)}, - {NAME("muzzy_decay_ms"), CTL(arena_i_muzzy_decay_ms)}, - {NAME("extent_hooks"), CTL(arena_i_extent_hooks)}, - {NAME("retain_grow_limit"), CTL(arena_i_retain_grow_limit)} + {NAME("oversize_threshold"), CTL(arena_i_oversize_threshold)}, + {NAME("dirty_decay_ms"), CTL(arena_i_dirty_decay_ms)}, + {NAME("muzzy_decay_ms"), CTL(arena_i_muzzy_decay_ms)}, + {NAME("extent_hooks"), CTL(arena_i_extent_hooks)}, + {NAME("retain_grow_limit"), CTL(arena_i_retain_grow_limit)}, + {NAME("name"), CTL(arena_i_name)} }; static const ctl_named_node_t super_arena_i_node[] = { {NAME(""), CHILD(named, arena_i)} @@ -2983,6 +2985,61 @@ label_return: return ret; } +/* + * When writing, newp should point to a char array storing the name to be set. + * A name longer than ARENA_NAME_LEN will be arbitrarily cut. When reading, + * oldp should point to a char array whose length is no shorter than + * ARENA_NAME_LEN or the length of the name when it was set. + */ +static int +arena_i_name_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, + void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + int ret; + unsigned arena_ind; + char *name; + + malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); + MIB_UNSIGNED(arena_ind, 1); + if (arena_ind == MALLCTL_ARENAS_ALL || arena_ind >= + ctl_arenas->narenas) { + ret = EINVAL; + goto label_return; + } + arena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false); + if (arena == NULL) { + ret = EFAULT; + goto label_return; + } + + if (oldp != NULL && oldlenp != NULL) { + /* + * Read the arena name. When reading, the input oldp should + * point to an array with a length no shorter than + * ARENA_NAME_LEN or the length when it was set. + */ + if (*oldlenp != sizeof(char *)) { + ret = EINVAL; + goto label_return; + } + name = *(char **)oldp; + arena_name_get(arena, name); + } + + if (newp != NULL) { + /* Write the arena name. */ + WRITE(name, char *); + if (name == NULL) { + ret = EINVAL; + goto label_return; + } + arena_name_set(arena, name); + } + ret = 0; +label_return: + malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); + return ret; +} + static const ctl_named_node_t * arena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) { diff --git a/src/stats.c b/src/stats.c index 5bb1a346..701a6c86 100644 --- a/src/stats.c +++ b/src/stats.c @@ -42,15 +42,18 @@ const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = { assert(miblen_new == miblen + 1); \ } while (0) -#define CTL_M2_GET(n, i, v, t) do { \ +#define CTL_MIB_GET(n, i, v, t, ind) do { \ size_t mib[CTL_MAX_DEPTH]; \ size_t miblen = sizeof(mib) / sizeof(size_t); \ size_t sz = sizeof(t); \ xmallctlnametomib(n, mib, &miblen); \ - mib[2] = (i); \ + mib[(ind)] = (i); \ xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \ } while (0) +#define CTL_M1_GET(n, i, v, t) CTL_MIB_GET(n, i, v, t, 1) +#define CTL_M2_GET(n, i, v, t) CTL_MIB_GET(n, i, v, t, 2) + /******************************************************************************/ /* Data. */ @@ -1042,6 +1045,8 @@ JEMALLOC_COLD static void stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large, bool mutex, bool extents, bool hpa) { + char name[ARENA_NAME_LEN]; + char *namep = name; unsigned nthreads; const char *dss; ssize_t dirty_decay_ms, muzzy_decay_ms; @@ -1059,6 +1064,10 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large, uint64_t uptime; CTL_GET("arenas.page", &page, size_t); + if (i != MALLCTL_ARENAS_ALL && i != MALLCTL_ARENAS_DESTROYED) { + CTL_M1_GET("arena.0.name", i, (void *)&namep, const char *); + emitter_kv(emitter, "name", "name", emitter_type_string, &namep); + } CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned); emitter_kv(emitter, "nthreads", "assigned threads", diff --git a/test/include/test/test.h b/test/include/test/test.h index d4b65912..54610dab 100644 --- a/test/include/test/test.h +++ b/test/include/test/test.h @@ -266,7 +266,7 @@ #define expect_false(a, ...) expect_b_eq(a, false, __VA_ARGS__) #define verify_str_eq(may_abort, a, b, ...) do { \ - if (strcmp((a), (b))) { \ + if (strcmp((a), (b)) != 0) { \ char prefix[ASSERT_BUFSIZE]; \ char message[ASSERT_BUFSIZE]; \ malloc_snprintf(prefix, sizeof(prefix), \ @@ -284,7 +284,7 @@ } while (0) #define verify_str_ne(may_abort, a, b, ...) do { \ - if (!strcmp((a), (b))) { \ + if (strcmp((a), (b)) == 0) { \ char prefix[ASSERT_BUFSIZE]; \ char message[ASSERT_BUFSIZE]; \ malloc_snprintf(prefix, sizeof(prefix), \ diff --git a/test/unit/mallctl.c b/test/unit/mallctl.c index 14fe7993..244d4c96 100644 --- a/test/unit/mallctl.c +++ b/test/unit/mallctl.c @@ -711,6 +711,48 @@ TEST_BEGIN(test_arena_i_dss) { } TEST_END +TEST_BEGIN(test_arena_i_name) { + unsigned arena_ind; + size_t ind_sz = sizeof(arena_ind); + size_t mib[3]; + size_t miblen; + char name_old[ARENA_NAME_LEN]; + char *name_oldp = name_old; + size_t sz = sizeof(name_oldp); + char default_name[ARENA_NAME_LEN]; + const char *name_new = "test name"; + const char *super_long_name = "A name longer than ARENA_NAME_LEN"; + size_t super_long_name_len = strlen(super_long_name); + assert(super_long_name_len > ARENA_NAME_LEN); + + miblen = sizeof(mib)/sizeof(size_t); + expect_d_eq(mallctlnametomib("arena.0.name", mib, &miblen), 0, + "Unexpected mallctlnametomib() error"); + + expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &ind_sz, NULL, + 0), 0, "Unexpected mallctl() failure"); + mib[1] = arena_ind; + + malloc_snprintf(default_name, sizeof(default_name), "manual_%u", + arena_ind); + expect_d_eq(mallctlbymib(mib, miblen, (void *)&name_oldp, &sz, + (void *)&name_new, sizeof(name_new)), 0, + "Unexpected mallctl() failure"); + expect_str_eq(name_old, default_name, + "Unexpected default value for arena name"); + + expect_d_eq(mallctlbymib(mib, miblen, (void *)&name_oldp, &sz, + (void *)&super_long_name, sizeof(super_long_name)), 0, + "Unexpected mallctl() failure"); + expect_str_eq(name_old, name_new, "Unexpected value for arena name"); + + expect_d_eq(mallctlbymib(mib, miblen, (void *)&name_oldp, &sz, + NULL, 0), 0, "Unexpected mallctl() failure"); + int cmp = strncmp(name_old, super_long_name, ARENA_NAME_LEN - 1); + expect_true(cmp == 0, "Unexpected value for long arena name "); +} +TEST_END + TEST_BEGIN(test_arena_i_retain_grow_limit) { size_t old_limit, new_limit, default_limit; size_t mib[3]; @@ -1258,6 +1300,7 @@ main(void) { test_arena_i_purge, test_arena_i_decay, test_arena_i_dss, + test_arena_i_name, test_arena_i_retain_grow_limit, test_arenas_dirty_decay_ms, test_arenas_muzzy_decay_ms,