Add mallctl for dumping last-N profiling records

This commit is contained in:
Yinan Zhang 2020-02-04 16:05:11 -08:00 committed by Qi Wang
parent bc05ecebf6
commit 68e8ddcaff
2 changed files with 225 additions and 0 deletions

View File

@ -237,6 +237,7 @@ CTL_PROTO(experimental_utilization_batch_query)
CTL_PROTO(experimental_arenas_i_pactivep) CTL_PROTO(experimental_arenas_i_pactivep)
INDEX_PROTO(experimental_arenas_i) INDEX_PROTO(experimental_arenas_i)
CTL_PROTO(experimental_prof_recent_alloc_max) CTL_PROTO(experimental_prof_recent_alloc_max)
CTL_PROTO(experimental_prof_recent_alloc_dump)
#define MUTEX_STATS_CTL_PROTO_GEN(n) \ #define MUTEX_STATS_CTL_PROTO_GEN(n) \
CTL_PROTO(stats_##n##_num_ops) \ CTL_PROTO(stats_##n##_num_ops) \
@ -631,6 +632,7 @@ static const ctl_indexed_node_t experimental_arenas_node[] = {
static const ctl_named_node_t experimental_prof_recent_node[] = { static const ctl_named_node_t experimental_prof_recent_node[] = {
{NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)}, {NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)},
{NAME("alloc_dump"), CTL(experimental_prof_recent_alloc_dump)},
}; };
static const ctl_named_node_t experimental_node[] = { static const ctl_named_node_t experimental_node[] = {
@ -3549,3 +3551,34 @@ experimental_prof_recent_alloc_max_ctl(tsd_t *tsd, const size_t *mib,
label_return: label_return:
return ret; return ret;
} }
typedef struct write_cb_packet_s write_cb_packet_t;
struct write_cb_packet_s {
void (*write_cb)(void *, const char *);
void *cbopaque;
};
static int
experimental_prof_recent_alloc_dump_ctl(tsd_t *tsd, const size_t *mib,
size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
int ret;
if (!(config_prof && opt_prof)) {
ret = ENOENT;
goto label_return;
}
assert(sizeof(write_cb_packet_t) == sizeof(void *) * 2);
WRITEONLY();
write_cb_packet_t write_cb_packet;
ASSURED_WRITE(write_cb_packet, write_cb_packet_t);
prof_recent_alloc_dump(tsd, write_cb_packet.write_cb,
write_cb_packet.cbopaque);
ret = 0;
label_return:
return ret;
}

View File

@ -381,6 +381,197 @@ TEST_END
#undef NTH_REQ_SIZE #undef NTH_REQ_SIZE
#define DUMP_OUT_SIZE 4096
static char dump_out[DUMP_OUT_SIZE];
static size_t dump_out_len = 0;
static void test_dump_write_cb(void *not_used, const char *str) {
size_t len = strlen(str);
assert(dump_out_len + len < DUMP_OUT_SIZE);
memcpy(dump_out + dump_out_len, str, len + 1);
dump_out_len += len;
}
static void call_dump() {
static void *in[2] = {test_dump_write_cb, NULL};
dump_out_len = 0;
assert_d_eq(mallctl("experimental.prof_recent.alloc_dump",
NULL, NULL, in, sizeof(in)), 0, "Dump mallctl raised error");
}
typedef struct {
size_t size;
bool released;
} confirm_record_t;
#define DUMP_ERROR "Dump output is wrong"
static void confirm_record(const char *template,
const confirm_record_t *records, const size_t n_records) {
static const char *types[2] = {"alloc", "dalloc"};
static char buf[64];
/*
* The template string would be in the form of:
* "{\"recent_alloc_max\":XYZ,\"recent_alloc\":[]}",
* and dump_out would be in the form of:
* "{\"recent_alloc_max\":XYZ,\"recent_alloc\":[...]}".
* Using "- 2" serves to cut right before the ending "]}".
*/
assert_d_eq(memcmp(dump_out, template, strlen(template) - 2), 0,
DUMP_ERROR);
assert_d_eq(memcmp(dump_out + strlen(dump_out) - 2,
template + strlen(template) - 2, 2), 0, DUMP_ERROR);
const char *start = dump_out + strlen(template) - 2;
const char *end = dump_out + strlen(dump_out) - 2;
const confirm_record_t *record;
for (record = records; record < records + n_records; ++record) {
#define ASSERT_CHAR(c) do { \
assert_true(start < end, DUMP_ERROR); \
assert_c_eq(*start++, c, DUMP_ERROR); \
} while (0)
#define ASSERT_STR(s) do { \
const size_t len = strlen(s); \
assert_true(start + len <= end, DUMP_ERROR); \
assert_d_eq(memcmp(start, s, len), 0, DUMP_ERROR); \
start += len; \
} while (0)
#define ASSERT_FORMATTED_STR(s, ...) do { \
malloc_snprintf(buf, sizeof(buf), s, __VA_ARGS__); \
ASSERT_STR(buf); \
} while (0)
if (record != records) {
ASSERT_CHAR(',');
}
ASSERT_CHAR('{');
ASSERT_STR("\"size\"");
ASSERT_CHAR(':');
ASSERT_FORMATTED_STR("%zu", record->size);
ASSERT_CHAR(',');
ASSERT_STR("\"usize\"");
ASSERT_CHAR(':');
ASSERT_FORMATTED_STR("%zu", sz_s2u(record->size));
ASSERT_CHAR(',');
ASSERT_STR("\"released\"");
ASSERT_CHAR(':');
ASSERT_STR(record->released ? "true" : "false");
ASSERT_CHAR(',');
const char **type = types;
while (true) {
ASSERT_FORMATTED_STR("\"%s_thread_uid\"", *type);
ASSERT_CHAR(':');
while (isdigit(*start)) {
++start;
}
ASSERT_CHAR(',');
ASSERT_FORMATTED_STR("\"%s_time\"", *type);
ASSERT_CHAR(':');
while (isdigit(*start)) {
++start;
}
ASSERT_CHAR(',');
ASSERT_FORMATTED_STR("\"%s_trace\"", *type);
ASSERT_CHAR(':');
ASSERT_CHAR('[');
while (isdigit(*start) || *start == 'x' ||
(*start >= 'a' && *start <= 'f') ||
*start == '\"' || *start == ',') {
++start;
}
ASSERT_CHAR(']');
if (strcmp(*type, "dalloc") == 0) {
break;
}
assert(strcmp(*type, "alloc") == 0);
if (!record->released) {
break;
}
ASSERT_CHAR(',');
++type;
}
ASSERT_CHAR('}');
#undef ASSERT_FORMATTED_STR
#undef ASSERT_STR
#undef ASSERT_CHAR
}
assert_ptr_eq(record, records + n_records, DUMP_ERROR);
assert_ptr_eq(start, end, DUMP_ERROR);
}
TEST_BEGIN(test_prof_recent_alloc_dump) {
test_skip_if(!config_prof);
tsd_t *tsd = tsd_fetch();
confirm_prof_setup(tsd);
ssize_t future;
void *p, *q;
confirm_record_t records[2];
future = 0;
assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
call_dump();
assert_str_eq(dump_out, "{\"recent_alloc_max\":0,\"recent_alloc\":[]}",
DUMP_ERROR);
future = 2;
assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
call_dump();
const char *template = "{\"recent_alloc_max\":2,\"recent_alloc\":[]}";
assert_str_eq(dump_out, template, DUMP_ERROR);
p = malloc(7);
call_dump();
records[0].size = 7;
records[0].released = false;
confirm_record(template, records, 1);
q = malloc(17);
call_dump();
records[1].size = 17;
records[1].released = false;
confirm_record(template, records, 2);
free(q);
call_dump();
records[1].released = true;
confirm_record(template, records, 2);
free(p);
call_dump();
records[0].released = true;
confirm_record(template, records, 2);
future = OPT_ALLOC_MAX;
assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
confirm_prof_setup(tsd);
}
TEST_END
#undef DUMP_ERROR
#undef DUMP_OUT_SIZE
#define N_THREADS 16 #define N_THREADS 16
#define N_PTRS 512 #define N_PTRS 512
#define N_CTLS 8 #define N_CTLS 8
@ -500,5 +691,6 @@ main(void) {
test_prof_recent_off, test_prof_recent_off,
test_prof_recent_on, test_prof_recent_on,
test_prof_recent_alloc, test_prof_recent_alloc,
test_prof_recent_alloc_dump,
test_prof_recent_stress); test_prof_recent_stress);
} }