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:
@@ -2384,13 +2384,13 @@ thread_prof_name_ctl(tsd_t *tsd, const size_t *mib,
|
||||
READ_XOR_WRITE();
|
||||
|
||||
if (newp != NULL) {
|
||||
if (newlen != sizeof(const char *)) {
|
||||
const char *newval = *(const char **)newp;
|
||||
if (newlen != sizeof(const char *) || newval == NULL) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
if ((ret = prof_thread_name_set(tsd, *(const char **)newp)) !=
|
||||
0) {
|
||||
if ((ret = prof_thread_name_set(tsd, newval)) != 0) {
|
||||
goto label_return;
|
||||
}
|
||||
} else {
|
||||
|
19
src/prof.c
19
src/prof.c
@@ -415,11 +415,14 @@ prof_tdata_t *
|
||||
prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
|
||||
uint64_t thr_uid = tdata->thr_uid;
|
||||
uint64_t thr_discrim = tdata->thr_discrim + 1;
|
||||
char *thread_name = (tdata->thread_name != NULL) ?
|
||||
prof_thread_name_alloc(tsd, tdata->thread_name) : NULL;
|
||||
bool active = tdata->active;
|
||||
|
||||
/* Keep a local copy of the thread name, before detaching. */
|
||||
prof_thread_name_assert(tdata);
|
||||
char thread_name[PROF_THREAD_NAME_MAX_LEN];
|
||||
strncpy(thread_name, tdata->thread_name, PROF_THREAD_NAME_MAX_LEN);
|
||||
prof_tdata_detach(tsd, tdata);
|
||||
|
||||
return prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name,
|
||||
active);
|
||||
}
|
||||
@@ -464,15 +467,15 @@ prof_active_set(tsdn_t *tsdn, bool active) {
|
||||
|
||||
const char *
|
||||
prof_thread_name_get(tsd_t *tsd) {
|
||||
static const char *prof_thread_name_dummy = "";
|
||||
|
||||
assert(tsd_reentrancy_level_get(tsd) == 0);
|
||||
|
||||
prof_tdata_t *tdata;
|
||||
|
||||
tdata = prof_tdata_get(tsd, true);
|
||||
prof_tdata_t *tdata = prof_tdata_get(tsd, true);
|
||||
if (tdata == NULL) {
|
||||
return "";
|
||||
return prof_thread_name_dummy;
|
||||
}
|
||||
return (tdata->thread_name != NULL ? tdata->thread_name : "");
|
||||
|
||||
return tdata->thread_name;
|
||||
}
|
||||
|
||||
int
|
||||
|
@@ -441,64 +441,30 @@ prof_bt_count(void) {
|
||||
return bt_count;
|
||||
}
|
||||
|
||||
char *
|
||||
prof_thread_name_alloc(tsd_t *tsd, const char *thread_name) {
|
||||
char *ret;
|
||||
size_t size;
|
||||
|
||||
if (thread_name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = strlen(thread_name) + 1;
|
||||
ret = iallocztm(tsd_tsdn(tsd), size, sz_size2index(size), false, NULL,
|
||||
true, arena_get(TSDN_NULL, 0, true), true);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(ret, thread_name, size);
|
||||
ret[size - 1] = '\0';
|
||||
|
||||
return ret;
|
||||
static void
|
||||
prof_thread_name_write_tdata(prof_tdata_t *tdata, const char *thread_name) {
|
||||
strncpy(tdata->thread_name, thread_name, PROF_THREAD_NAME_MAX_LEN);
|
||||
tdata->thread_name[PROF_THREAD_NAME_MAX_LEN - 1] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
prof_thread_name_set_impl(tsd_t *tsd, const char *thread_name) {
|
||||
assert(tsd_reentrancy_level_get(tsd) == 0);
|
||||
assert(thread_name != NULL);
|
||||
|
||||
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++) {
|
||||
for (unsigned i = 0; thread_name[i] != '\0'; i++) {
|
||||
char c = thread_name[i];
|
||||
if (!isgraph(c) && !isblank(c)) {
|
||||
return EFAULT;
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
s = prof_thread_name_alloc(tsd, thread_name);
|
||||
if (s == NULL) {
|
||||
return EAGAIN;
|
||||
prof_tdata_t *tdata = prof_tdata_get(tsd, true);
|
||||
if (tdata == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
char *old_thread_name = tdata->thread_name;
|
||||
tdata->thread_name = s;
|
||||
if (old_thread_name != NULL) {
|
||||
idalloctm(tsd_tsdn(tsd), old_thread_name, /* tcache */ NULL,
|
||||
/* alloc_ctx */ NULL, /* is_internal */ true,
|
||||
/* slow_path */ true);
|
||||
}
|
||||
prof_thread_name_write_tdata(tdata, thread_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -949,7 +915,7 @@ prof_tdata_dump_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata,
|
||||
tdata->thr_uid);
|
||||
prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque,
|
||||
&tdata->cnt_summed);
|
||||
if (tdata->thread_name != NULL) {
|
||||
if (!prof_thread_name_empty(tdata)) {
|
||||
arg->prof_dump_write(arg->cbopaque, " ");
|
||||
arg->prof_dump_write(arg->cbopaque, tdata->thread_name);
|
||||
}
|
||||
@@ -1179,10 +1145,15 @@ prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
|
||||
tdata->lock = prof_tdata_mutex_choose(thr_uid);
|
||||
tdata->thr_uid = thr_uid;
|
||||
tdata->thr_discrim = thr_discrim;
|
||||
tdata->thread_name = thread_name;
|
||||
tdata->attached = true;
|
||||
tdata->expired = false;
|
||||
tdata->tctx_uid_next = 0;
|
||||
if (thread_name == NULL) {
|
||||
prof_thread_name_clear(tdata);
|
||||
} else {
|
||||
prof_thread_name_write_tdata(tdata, thread_name);
|
||||
}
|
||||
prof_thread_name_assert(tdata);
|
||||
|
||||
if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,
|
||||
prof_bt_keycomp)) {
|
||||
@@ -1230,13 +1201,8 @@ prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
|
||||
malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tdata->lock);
|
||||
|
||||
tdata_tree_remove(&tdatas, tdata);
|
||||
|
||||
assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));
|
||||
|
||||
if (tdata->thread_name != NULL) {
|
||||
idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
|
||||
true);
|
||||
}
|
||||
ckh_delete(tsd, &tdata->bt2tctx);
|
||||
idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
|
||||
}
|
||||
|
@@ -243,8 +243,7 @@ prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info) {
|
||||
iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
|
||||
arena_get(TSDN_NULL, 0, true), true);
|
||||
|
||||
const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
|
||||
"" : tctx->tdata->thread_name;
|
||||
const char *prod_thr_name = tctx->tdata->thread_name;
|
||||
const char *cons_thr_name = prof_thread_name_get(tsd);
|
||||
|
||||
prof_bt_t bt;
|
||||
|
@@ -495,7 +495,7 @@ prof_recent_alloc_dump_node(emitter_t *emitter, prof_recent_t *node) {
|
||||
&node->alloc_tctx->thr_uid);
|
||||
prof_tdata_t *alloc_tdata = node->alloc_tctx->tdata;
|
||||
assert(alloc_tdata != NULL);
|
||||
if (alloc_tdata->thread_name != NULL) {
|
||||
if (!prof_thread_name_empty(alloc_tdata)) {
|
||||
emitter_json_kv(emitter, "alloc_thread_name",
|
||||
emitter_type_string, &alloc_tdata->thread_name);
|
||||
}
|
||||
@@ -511,7 +511,7 @@ prof_recent_alloc_dump_node(emitter_t *emitter, prof_recent_t *node) {
|
||||
emitter_type_uint64, &node->dalloc_tctx->thr_uid);
|
||||
prof_tdata_t *dalloc_tdata = node->dalloc_tctx->tdata;
|
||||
assert(dalloc_tdata != NULL);
|
||||
if (dalloc_tdata->thread_name != NULL) {
|
||||
if (!prof_thread_name_empty(dalloc_tdata)) {
|
||||
emitter_json_kv(emitter, "dalloc_thread_name",
|
||||
emitter_type_string, &dalloc_tdata->thread_name);
|
||||
}
|
||||
|
@@ -462,12 +462,17 @@ prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read =
|
||||
|
||||
void
|
||||
prof_sys_thread_name_fetch(tsd_t *tsd) {
|
||||
#define THREAD_NAME_MAX_LEN 16
|
||||
char buf[THREAD_NAME_MAX_LEN];
|
||||
if (!prof_sys_thread_name_read(buf, THREAD_NAME_MAX_LEN)) {
|
||||
prof_thread_name_set_impl(tsd, buf);
|
||||
prof_tdata_t *tdata = prof_tdata_get(tsd, true);
|
||||
if (tdata == NULL) {
|
||||
return;
|
||||
}
|
||||
#undef THREAD_NAME_MAX_LEN
|
||||
|
||||
if (prof_sys_thread_name_read(tdata->thread_name,
|
||||
PROF_THREAD_NAME_MAX_LEN) != 0) {
|
||||
prof_thread_name_clear(tdata);
|
||||
}
|
||||
|
||||
tdata->thread_name[PROF_THREAD_NAME_MAX_LEN - 1] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
|
Reference in New Issue
Block a user