Inline the storage for thread name in prof_tdata_t.

The previous approach managed the thread name in a separate buffer, which causes
races because the thread name update (triggered by new samples) can happen at
the same time as prof dumping (which reads the thread names) -- these two
operations are under separate locks to avoid blocking each other.  Implemented
the thread name storage as part of the tdata struct, which resolves the lifetime
issue and also avoids internal alloc / dalloc during prof_sample.
This commit is contained in:
Qi Wang
2023-03-28 18:02:34 -07:00
committed by Qi Wang
parent 6cab460a45
commit ce0b7ab6c8
11 changed files with 120 additions and 103 deletions

View File

@@ -14,8 +14,6 @@ mallctl_thread_name_get_impl(const char *thread_name_expected, const char *func,
expect_str_eq(thread_name_old, thread_name_expected,
"%s():%d: Unexpected thread.prof.name value", func, line);
}
#define mallctl_thread_name_get(a) \
mallctl_thread_name_get_impl(a, __func__, __LINE__)
static void
mallctl_thread_name_set_impl(const char *thread_name, const char *func,
@@ -26,51 +24,59 @@ mallctl_thread_name_set_impl(const char *thread_name, const char *func,
func, line);
mallctl_thread_name_get_impl(thread_name, func, line);
}
#define mallctl_thread_name_get(a) \
mallctl_thread_name_get_impl(a, __func__, __LINE__)
#define mallctl_thread_name_set(a) \
mallctl_thread_name_set_impl(a, __func__, __LINE__)
TEST_BEGIN(test_prof_thread_name_validation) {
const char *thread_name;
test_skip_if(!config_prof);
test_skip_if(opt_prof_sys_thread_name);
mallctl_thread_name_get("");
mallctl_thread_name_set("hi there");
const char *test_name1 = "test case1";
mallctl_thread_name_set(test_name1);
/* Test name longer than the max len. */
char long_name[] =
"test case longer than expected; test case longer than expected";
expect_zu_gt(strlen(long_name), PROF_THREAD_NAME_MAX_LEN,
"Long test name not long enough");
const char *test_name_long = long_name;
expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&test_name_long, sizeof(test_name_long)), 0,
"Unexpected mallctl failure from thread.prof.name");
/* Long name cut to match. */
long_name[PROF_THREAD_NAME_MAX_LEN - 1] = '\0';
mallctl_thread_name_get(test_name_long);
/* NULL input shouldn't be allowed. */
thread_name = NULL;
const char *test_name2 = NULL;
expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&thread_name, sizeof(thread_name)), EFAULT,
"Unexpected mallctl result writing \"%s\" to thread.prof.name",
thread_name);
(void *)&test_name2, sizeof(test_name2)), EINVAL,
"Unexpected mallctl result writing to thread.prof.name");
/* '\n' shouldn't be allowed. */
thread_name = "hi\nthere";
const char *test_name3 = "test\ncase";
expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&thread_name, sizeof(thread_name)), EFAULT,
(void *)&test_name3, sizeof(test_name3)), EINVAL,
"Unexpected mallctl result writing \"%s\" to thread.prof.name",
thread_name);
test_name3);
/* Simultaneous read/write shouldn't be allowed. */
{
const char *thread_name_old;
size_t sz;
sz = sizeof(thread_name_old);
expect_d_eq(mallctl("thread.prof.name",
(void *)&thread_name_old, &sz, (void *)&thread_name,
sizeof(thread_name)), EPERM,
"Unexpected mallctl result writing \"%s\" to "
"thread.prof.name", thread_name);
}
const char *thread_name_old;
size_t sz = sizeof(thread_name_old);
expect_d_eq(mallctl("thread.prof.name", (void *)&thread_name_old, &sz,
(void *)&test_name1, sizeof(test_name1)), EPERM,
"Unexpected mallctl result from thread.prof.name");
mallctl_thread_name_set("");
}
TEST_END
#define NTHREADS 4
#define NRESET 25
static void *
thd_start(void *varg) {
unsigned thd_ind = *(unsigned *)varg;
@@ -82,6 +88,7 @@ thd_start(void *varg) {
mallctl_thread_name_get("");
mallctl_thread_name_set(thread_name);
#define NRESET 25
for (i = 0; i < NRESET; i++) {
expect_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
"Unexpected error while resetting heap profile data");
@@ -92,12 +99,14 @@ thd_start(void *varg) {
mallctl_thread_name_set("");
return NULL;
#undef NRESET
}
TEST_BEGIN(test_prof_thread_name_threaded) {
test_skip_if(!config_prof);
test_skip_if(opt_prof_sys_thread_name);
#define NTHREADS 4
thd_t thds[NTHREADS];
unsigned thd_args[NTHREADS];
unsigned i;
@@ -109,10 +118,9 @@ TEST_BEGIN(test_prof_thread_name_threaded) {
for (i = 0; i < NTHREADS; i++) {
thd_join(thds[i], NULL);
}
#undef NTHREADS
}
TEST_END
#undef NTHREADS
#undef NRESET
int
main(void) {