perf callchain: Use pthread keys for tls callchain_cursor
Pthread keys are more portable than __thread and allow the association of a destructor with the key. Use the destructor to clean up TLS callchain cursors to aid understanding memory leaks. Committer notes: Had to fixup a series of unconverted places and also check for the return of get_tls_callchain_cursor() as it may fail and return NULL. In that unlikely case we now either print something to a file, if the caller was expecting to print a callchain, or return an error code to state that resolving the callchain isn't possible. In some cases this was made easier because thread__resolve_callchain() already can fail for other reasons, so this new one (cursor == NULL) can be added and the callers don't have to explicitely check for this new condition. Signed-off-by: Ian Rogers <irogers@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ali Saidi <alisaidi@amazon.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com> Cc: Brian Robbins <brianrob@linux.microsoft.com> Cc: Changbin Du <changbin.du@huawei.com> Cc: Dmitrii Dolgov <9erthalion6@gmail.com> Cc: Fangrui Song <maskray@google.com> Cc: German Gomez <german.gomez@arm.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Ivan Babrou <ivan@cloudflare.com> Cc: James Clark <james.clark@arm.com> Cc: Jing Zhang <renyu.zj@linux.alibaba.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: John Garry <john.g.garry@oracle.com> Cc: K Prateek Nayak <kprateek.nayak@amd.com> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Leo Yan <leo.yan@linaro.org> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Miguel Ojeda <ojeda@kernel.org> Cc: Mike Leach <mike.leach@linaro.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Bangoria <ravi.bangoria@amd.com> Cc: Sean Christopherson <seanjc@google.com> Cc: Steinar H. Gunderson <sesse@google.com> Cc: Suzuki Poulouse <suzuki.poulose@arm.com> Cc: Wenyu Liu <liuwenyu7@huawei.com> Cc: Will Deacon <will@kernel.org> Cc: Yang Jihong <yangjihong1@huawei.com> Cc: Ye Xingchen <ye.xingchen@zte.com.cn> Cc: Yuan Can <yuancan@huawei.com> Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20230608232823.4027869-25-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
committed by
Arnaldo Carvalho de Melo
parent
d7ba60a4e5
commit
8ab12a2038
@@ -284,6 +284,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||||||
struct hist_entry *he;
|
struct hist_entry *he;
|
||||||
struct addr_location al;
|
struct addr_location al;
|
||||||
struct mem_info *mi, *mi_dup;
|
struct mem_info *mi, *mi_dup;
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
addr_location__init(&al);
|
addr_location__init(&al);
|
||||||
@@ -297,7 +298,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||||||
if (c2c.stitch_lbr)
|
if (c2c.stitch_lbr)
|
||||||
thread__set_lbr_stitch_enable(al.thread, true);
|
thread__set_lbr_stitch_enable(al.thread, true);
|
||||||
|
|
||||||
ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
|
cursor = get_tls_callchain_cursor();
|
||||||
|
ret = sample__resolve_callchain(sample, cursor, NULL,
|
||||||
evsel, &al, sysctl_perf_event_max_stack);
|
evsel, &al, sysctl_perf_event_max_stack);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@@ -399,6 +399,7 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
|
|||||||
struct addr_location al;
|
struct addr_location al;
|
||||||
struct machine *machine = &kmem_session->machines.host;
|
struct machine *machine = &kmem_session->machines.host;
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
u64 result = sample->ip;
|
u64 result = sample->ip;
|
||||||
|
|
||||||
addr_location__init(&al);
|
addr_location__init(&al);
|
||||||
@@ -408,14 +409,19 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
|
|||||||
}
|
}
|
||||||
|
|
||||||
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
||||||
sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16);
|
|
||||||
|
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
cursor = get_tls_callchain_cursor();
|
||||||
|
if (cursor == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16);
|
||||||
|
|
||||||
|
callchain_cursor_commit(cursor);
|
||||||
while (true) {
|
while (true) {
|
||||||
struct alloc_func key, *caller;
|
struct alloc_func key, *caller;
|
||||||
u64 addr;
|
u64 addr;
|
||||||
|
|
||||||
node = callchain_cursor_current(&callchain_cursor);
|
node = callchain_cursor_current(cursor);
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -434,7 +440,7 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
|
|||||||
} else
|
} else
|
||||||
pr_debug3("skipping alloc function: %s\n", caller->name);
|
pr_debug3("skipping alloc function: %s\n", caller->name);
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
callchain_cursor_advance(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
|
pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
|
||||||
|
|||||||
@@ -589,7 +589,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
|
|||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
struct callchain_cursor *cursor = &callchain_cursor;
|
struct callchain_cursor *cursor;
|
||||||
|
|
||||||
if (!kwork->show_callchain || sample->callchain == NULL)
|
if (!kwork->show_callchain || sample->callchain == NULL)
|
||||||
return;
|
return;
|
||||||
@@ -601,6 +601,8 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
if (thread__resolve_callchain(thread, cursor, evsel, sample,
|
if (thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||||
NULL, NULL, kwork->max_stack + 2) != 0) {
|
NULL, NULL, kwork->max_stack + 2) != 0) {
|
||||||
pr_debug("Failed to resolve callchain, skipping\n");
|
pr_debug("Failed to resolve callchain, skipping\n");
|
||||||
@@ -686,12 +688,18 @@ static void timehist_print_event(struct perf_kwork *kwork,
|
|||||||
* callchain
|
* callchain
|
||||||
*/
|
*/
|
||||||
if (kwork->show_callchain) {
|
if (kwork->show_callchain) {
|
||||||
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
|
if (cursor == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
printf(" ");
|
printf(" ");
|
||||||
|
|
||||||
sample__fprintf_sym(sample, al, 0,
|
sample__fprintf_sym(sample, al, 0,
|
||||||
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
|
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
|
||||||
EVSEL__PRINT_CALLCHAIN_ARROW |
|
EVSEL__PRINT_CALLCHAIN_ARROW |
|
||||||
EVSEL__PRINT_SKIP_IGNORED,
|
EVSEL__PRINT_SKIP_IGNORED,
|
||||||
&callchain_cursor, symbol_conf.bt_stop_list,
|
cursor, symbol_conf.bt_stop_list,
|
||||||
stdout);
|
stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -911,7 +911,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
|
|||||||
char *buf, int size)
|
char *buf, int size)
|
||||||
{
|
{
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
struct callchain_cursor *cursor = &callchain_cursor;
|
struct callchain_cursor *cursor;
|
||||||
struct machine *machine = &session->machines.host;
|
struct machine *machine = &session->machines.host;
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
int skip = 0;
|
int skip = 0;
|
||||||
@@ -925,6 +925,8 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
|
|||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
/* use caller function name from the callchain */
|
/* use caller function name from the callchain */
|
||||||
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
|
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||||
NULL, NULL, max_stack_depth);
|
NULL, NULL, max_stack_depth);
|
||||||
@@ -962,7 +964,7 @@ next:
|
|||||||
|
|
||||||
static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
|
static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
|
||||||
{
|
{
|
||||||
struct callchain_cursor *cursor = &callchain_cursor;
|
struct callchain_cursor *cursor;
|
||||||
struct machine *machine = &session->machines.host;
|
struct machine *machine = &session->machines.host;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
u64 hash = 0;
|
u64 hash = 0;
|
||||||
@@ -973,6 +975,7 @@ static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
|
|||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
cursor = get_tls_callchain_cursor();
|
||||||
/* use caller function name from the callchain */
|
/* use caller function name from the callchain */
|
||||||
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
|
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||||
NULL, NULL, max_stack_depth);
|
NULL, NULL, max_stack_depth);
|
||||||
|
|||||||
@@ -2111,7 +2111,7 @@ static void timehist_print_sample(struct perf_sched *sched,
|
|||||||
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
|
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
|
||||||
EVSEL__PRINT_CALLCHAIN_ARROW |
|
EVSEL__PRINT_CALLCHAIN_ARROW |
|
||||||
EVSEL__PRINT_SKIP_IGNORED,
|
EVSEL__PRINT_SKIP_IGNORED,
|
||||||
&callchain_cursor, symbol_conf.bt_stop_list, stdout);
|
get_tls_callchain_cursor(), symbol_conf.bt_stop_list, stdout);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -2196,7 +2196,7 @@ static void save_task_callchain(struct perf_sched *sched,
|
|||||||
struct evsel *evsel,
|
struct evsel *evsel,
|
||||||
struct machine *machine)
|
struct machine *machine)
|
||||||
{
|
{
|
||||||
struct callchain_cursor *cursor = &callchain_cursor;
|
struct callchain_cursor *cursor;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
|
|
||||||
/* want main thread for process - has maps */
|
/* want main thread for process - has maps */
|
||||||
@@ -2209,6 +2209,8 @@ static void save_task_callchain(struct perf_sched *sched,
|
|||||||
if (!sched->show_callchain || sample->callchain == NULL)
|
if (!sched->show_callchain || sample->callchain == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
if (thread__resolve_callchain(thread, cursor, evsel, sample,
|
if (thread__resolve_callchain(thread, cursor, evsel, sample,
|
||||||
NULL, NULL, sched->max_stack + 2) != 0) {
|
NULL, NULL, sched->max_stack + 2) != 0) {
|
||||||
if (verbose > 0)
|
if (verbose > 0)
|
||||||
@@ -2338,10 +2340,16 @@ static void save_idle_callchain(struct perf_sched *sched,
|
|||||||
struct idle_thread_runtime *itr,
|
struct idle_thread_runtime *itr,
|
||||||
struct perf_sample *sample)
|
struct perf_sample *sample)
|
||||||
{
|
{
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
|
|
||||||
if (!sched->show_callchain || sample->callchain == NULL)
|
if (!sched->show_callchain || sample->callchain == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
|
cursor = get_tls_callchain_cursor();
|
||||||
|
if (cursor == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
callchain_cursor__copy(&itr->cursor, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct thread *timehist_get_thread(struct perf_sched *sched,
|
static struct thread *timehist_get_thread(struct perf_sched *sched,
|
||||||
|
|||||||
+14
-10
@@ -1557,11 +1557,13 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
|
|||||||
unsigned int print_opts = output[type].print_ip_opts;
|
unsigned int print_opts = output[type].print_ip_opts;
|
||||||
struct callchain_cursor *cursor = NULL;
|
struct callchain_cursor *cursor = NULL;
|
||||||
|
|
||||||
if (symbol_conf.use_callchain && sample->callchain &&
|
if (symbol_conf.use_callchain && sample->callchain) {
|
||||||
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
cursor = get_tls_callchain_cursor();
|
||||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
if (thread__resolve_callchain(al->thread, cursor, evsel,
|
||||||
cursor = &callchain_cursor;
|
sample, NULL, NULL,
|
||||||
|
scripting_max_stack))
|
||||||
|
cursor = NULL;
|
||||||
|
}
|
||||||
if (cursor == NULL) {
|
if (cursor == NULL) {
|
||||||
printed += fprintf(fp, " ");
|
printed += fprintf(fp, " ");
|
||||||
if (print_opts & EVSEL__PRINT_SRCLINE) {
|
if (print_opts & EVSEL__PRINT_SRCLINE) {
|
||||||
@@ -2203,11 +2205,13 @@ static void process_event(struct perf_script *script,
|
|||||||
if (script->stitch_lbr)
|
if (script->stitch_lbr)
|
||||||
thread__set_lbr_stitch_enable(al->thread, true);
|
thread__set_lbr_stitch_enable(al->thread, true);
|
||||||
|
|
||||||
if (symbol_conf.use_callchain && sample->callchain &&
|
if (symbol_conf.use_callchain && sample->callchain) {
|
||||||
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
cursor = get_tls_callchain_cursor();
|
||||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
if (thread__resolve_callchain(al->thread, cursor, evsel,
|
||||||
cursor = &callchain_cursor;
|
sample, NULL, NULL,
|
||||||
|
scripting_max_stack))
|
||||||
|
cursor = NULL;
|
||||||
|
}
|
||||||
fputc(cursor ? '\n' : ' ', fp);
|
fputc(cursor ? '\n' : ' ', fp);
|
||||||
sample__fprintf_sym(sample, al, 0, output[type].print_ip_opts, cursor,
|
sample__fprintf_sym(sample, al, 0, output[type].print_ip_opts, cursor,
|
||||||
symbol_conf.bt_stop_list, fp);
|
symbol_conf.bt_stop_list, fp);
|
||||||
|
|||||||
@@ -2437,7 +2437,7 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam
|
|||||||
EVSEL__PRINT_DSO |
|
EVSEL__PRINT_DSO |
|
||||||
EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||||
|
|
||||||
return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, symbol_conf.bt_stop_list, trace->output);
|
return sample__fprintf_callchain(sample, 38, print_opts, get_tls_callchain_cursor(), symbol_conf.bt_stop_list, trace->output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *errno_to_name(struct evsel *evsel, int err)
|
static const char *errno_to_name(struct evsel *evsel, int err)
|
||||||
@@ -2491,9 +2491,11 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (sample->callchain) {
|
if (sample->callchain) {
|
||||||
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
|
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
|
||||||
if (callchain_ret == 0) {
|
if (callchain_ret == 0) {
|
||||||
if (callchain_cursor.nr < trace->min_stack)
|
if (cursor->nr < trace->min_stack)
|
||||||
goto out;
|
goto out;
|
||||||
callchain_ret = 1;
|
callchain_ret = 1;
|
||||||
}
|
}
|
||||||
@@ -2795,9 +2797,11 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel,
|
|||||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||||
|
|
||||||
if (sample->callchain) {
|
if (sample->callchain) {
|
||||||
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
|
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
|
||||||
if (callchain_ret == 0) {
|
if (callchain_ret == 0) {
|
||||||
if (callchain_cursor.nr < trace->min_stack)
|
if (cursor->nr < trace->min_stack)
|
||||||
goto out;
|
goto out;
|
||||||
callchain_ret = 1;
|
callchain_ret = 1;
|
||||||
}
|
}
|
||||||
@@ -2899,9 +2903,11 @@ static int trace__pgfault(struct trace *trace,
|
|||||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||||
|
|
||||||
if (sample->callchain) {
|
if (sample->callchain) {
|
||||||
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
|
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
|
||||||
if (callchain_ret == 0) {
|
if (callchain_ret == 0) {
|
||||||
if (callchain_cursor.nr < trace->min_stack)
|
if (cursor->nr < trace->min_stack)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
callchain_ret = 1;
|
callchain_ret = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ struct callchain_param callchain_param_default = {
|
|||||||
CALLCHAIN_PARAM_DEFAULT
|
CALLCHAIN_PARAM_DEFAULT
|
||||||
};
|
};
|
||||||
|
|
||||||
__thread struct callchain_cursor callchain_cursor;
|
/* Used for thread-local struct callchain_cursor. */
|
||||||
|
static pthread_key_t callchain_cursor;
|
||||||
|
|
||||||
int parse_callchain_record_opt(const char *arg, struct callchain_param *param)
|
int parse_callchain_record_opt(const char *arg, struct callchain_param *param)
|
||||||
{
|
{
|
||||||
@@ -986,6 +987,9 @@ int callchain_append(struct callchain_root *root,
|
|||||||
struct callchain_cursor *cursor,
|
struct callchain_cursor *cursor,
|
||||||
u64 period)
|
u64 period)
|
||||||
{
|
{
|
||||||
|
if (cursor == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (!cursor->nr)
|
if (!cursor->nr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -1116,7 +1120,7 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
|
|||||||
if ((!symbol_conf.use_callchain || sample->callchain == NULL) &&
|
if ((!symbol_conf.use_callchain || sample->callchain == NULL) &&
|
||||||
!symbol_conf.show_branchflag_count)
|
!symbol_conf.show_branchflag_count)
|
||||||
return 0;
|
return 0;
|
||||||
return callchain_append(he->callchain, &callchain_cursor, sample->period);
|
return callchain_append(he->callchain, get_tls_callchain_cursor(), sample->period);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
|
int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
|
||||||
@@ -1570,6 +1574,43 @@ out:
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void callchain_cursor__delete(void *vcursor)
|
||||||
|
{
|
||||||
|
struct callchain_cursor *cursor = vcursor;
|
||||||
|
struct callchain_cursor_node *node, *next;
|
||||||
|
|
||||||
|
callchain_cursor_reset(cursor);
|
||||||
|
for (node = cursor->first; node != NULL; node = next) {
|
||||||
|
next = node->next;
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
free(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_callchain_cursor_key(void)
|
||||||
|
{
|
||||||
|
if (pthread_key_create(&callchain_cursor, callchain_cursor__delete)) {
|
||||||
|
pr_err("callchain cursor creation failed");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct callchain_cursor *get_tls_callchain_cursor(void)
|
||||||
|
{
|
||||||
|
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
|
|
||||||
|
pthread_once(&once_control, init_callchain_cursor_key);
|
||||||
|
cursor = pthread_getspecific(callchain_cursor);
|
||||||
|
if (!cursor) {
|
||||||
|
cursor = zalloc(sizeof(*cursor));
|
||||||
|
if (!cursor)
|
||||||
|
pr_debug3("%s: not enough memory\n", __func__);
|
||||||
|
pthread_setspecific(callchain_cursor, cursor);
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
int callchain_cursor__copy(struct callchain_cursor *dst,
|
int callchain_cursor__copy(struct callchain_cursor *dst,
|
||||||
struct callchain_cursor *src)
|
struct callchain_cursor *src)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -168,8 +168,6 @@ struct callchain_cursor {
|
|||||||
struct callchain_cursor_node *curr;
|
struct callchain_cursor_node *curr;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern __thread struct callchain_cursor callchain_cursor;
|
|
||||||
|
|
||||||
static inline void callchain_init(struct callchain_root *root)
|
static inline void callchain_init(struct callchain_root *root)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&root->node.val);
|
INIT_LIST_HEAD(&root->node.val);
|
||||||
@@ -211,6 +209,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
|
|||||||
/* Close a cursor writing session. Initialize for the reader */
|
/* Close a cursor writing session. Initialize for the reader */
|
||||||
static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
|
static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
|
||||||
{
|
{
|
||||||
|
if (cursor == NULL)
|
||||||
|
return;
|
||||||
cursor->curr = cursor->first;
|
cursor->curr = cursor->first;
|
||||||
cursor->pos = 0;
|
cursor->pos = 0;
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
|
|||||||
static inline struct callchain_cursor_node *
|
static inline struct callchain_cursor_node *
|
||||||
callchain_cursor_current(struct callchain_cursor *cursor)
|
callchain_cursor_current(struct callchain_cursor *cursor)
|
||||||
{
|
{
|
||||||
if (cursor->pos == cursor->nr)
|
if (cursor == NULL || cursor->pos == cursor->nr)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return cursor->curr;
|
return cursor->curr;
|
||||||
@@ -231,6 +231,8 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
|
|||||||
cursor->pos++;
|
cursor->pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct callchain_cursor *get_tls_callchain_cursor(void);
|
||||||
|
|
||||||
int callchain_cursor__copy(struct callchain_cursor *dst,
|
int callchain_cursor__copy(struct callchain_cursor *dst,
|
||||||
struct callchain_cursor *src);
|
struct callchain_cursor *src);
|
||||||
|
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
|
|||||||
u64 kernel_start = machine__kernel_start(machine);
|
u64 kernel_start = machine__kernel_start(machine);
|
||||||
struct call_path *current = &dbe->cpr->call_path;
|
struct call_path *current = &dbe->cpr->call_path;
|
||||||
enum chain_order saved_order = callchain_param.order;
|
enum chain_order saved_order = callchain_param.order;
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||||
@@ -226,13 +227,14 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
|
|||||||
* the callchain starting with the root node and ending with the leaf.
|
* the callchain starting with the root node and ending with the leaf.
|
||||||
*/
|
*/
|
||||||
callchain_param.order = ORDER_CALLER;
|
callchain_param.order = ORDER_CALLER;
|
||||||
err = thread__resolve_callchain(thread, &callchain_cursor, evsel,
|
cursor = get_tls_callchain_cursor();
|
||||||
|
err = thread__resolve_callchain(thread, cursor, evsel,
|
||||||
sample, NULL, NULL, PERF_MAX_STACK_DEPTH);
|
sample, NULL, NULL, PERF_MAX_STACK_DEPTH);
|
||||||
if (err) {
|
if (err) {
|
||||||
callchain_param.order = saved_order;
|
callchain_param.order = saved_order;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
callchain_cursor_commit(cursor);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
@@ -240,7 +242,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
|
|||||||
u64 dso_db_id = 0, sym_db_id = 0, offset = 0;
|
u64 dso_db_id = 0, sym_db_id = 0, offset = 0;
|
||||||
|
|
||||||
|
|
||||||
node = callchain_cursor_current(&callchain_cursor);
|
node = callchain_cursor_current(cursor);
|
||||||
if (!node)
|
if (!node)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -265,7 +267,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
|
|||||||
al.sym, node->ip,
|
al.sym, node->ip,
|
||||||
kernel_start);
|
kernel_start);
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
callchain_cursor_advance(cursor);
|
||||||
addr_location__exit(&al);
|
addr_location__exit(&al);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
|||||||
char s = print_oneline ? ' ' : '\t';
|
char s = print_oneline ? ' ' : '\t';
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
|
if (cursor == NULL)
|
||||||
|
return fprintf(fp, "<not enough memory for the callchain cursor>%s", print_oneline ? "" : "\n");
|
||||||
|
|
||||||
if (sample->callchain) {
|
if (sample->callchain) {
|
||||||
callchain_cursor_commit(cursor);
|
callchain_cursor_commit(cursor);
|
||||||
|
|
||||||
|
|||||||
+31
-15
@@ -1029,15 +1029,19 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
struct addr_location *al __maybe_unused)
|
struct addr_location *al __maybe_unused)
|
||||||
{
|
{
|
||||||
struct hist_entry **he_cache;
|
struct hist_entry **he_cache;
|
||||||
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
if (cursor == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
callchain_cursor_commit(cursor);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is for detecting cycles or recursions so that they're
|
* This is for detecting cycles or recursions so that they're
|
||||||
* cumulated only one time to prevent entries more than 100%
|
* cumulated only one time to prevent entries more than 100%
|
||||||
* overhead.
|
* overhead.
|
||||||
*/
|
*/
|
||||||
he_cache = malloc(sizeof(*he_cache) * (callchain_cursor.nr + 1));
|
he_cache = malloc(sizeof(*he_cache) * (cursor->nr + 1));
|
||||||
if (he_cache == NULL)
|
if (he_cache == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@@ -1072,7 +1076,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
* We need to re-initialize the cursor since callchain_append()
|
* We need to re-initialize the cursor since callchain_append()
|
||||||
* advanced the cursor to the end.
|
* advanced the cursor to the end.
|
||||||
*/
|
*/
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
callchain_cursor_commit(get_tls_callchain_cursor());
|
||||||
|
|
||||||
hists__inc_nr_samples(hists, he->filtered);
|
hists__inc_nr_samples(hists, he->filtered);
|
||||||
|
|
||||||
@@ -1085,7 +1089,7 @@ iter_next_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
{
|
{
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
|
|
||||||
node = callchain_cursor_current(&callchain_cursor);
|
node = callchain_cursor_current(get_tls_callchain_cursor());
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -1131,12 +1135,15 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
|||||||
.raw_size = sample->raw_size,
|
.raw_size = sample->raw_size,
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
struct callchain_cursor cursor;
|
struct callchain_cursor cursor, *tls_cursor = get_tls_callchain_cursor();
|
||||||
bool fast = hists__has(he_tmp.hists, sym);
|
bool fast = hists__has(he_tmp.hists, sym);
|
||||||
|
|
||||||
callchain_cursor_snapshot(&cursor, &callchain_cursor);
|
if (tls_cursor == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
callchain_cursor_snapshot(&cursor, tls_cursor);
|
||||||
|
|
||||||
|
callchain_cursor_advance(tls_cursor);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if there's duplicate entries in the callchain.
|
* Check if there's duplicate entries in the callchain.
|
||||||
@@ -1222,7 +1229,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
|
|||||||
if (al)
|
if (al)
|
||||||
alm = map__get(al->map);
|
alm = map__get(al->map);
|
||||||
|
|
||||||
err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent,
|
err = sample__resolve_callchain(iter->sample, get_tls_callchain_cursor(), &iter->parent,
|
||||||
iter->evsel, al, max_stack_depth);
|
iter->evsel, al, max_stack_depth);
|
||||||
if (err) {
|
if (err) {
|
||||||
map__put(alm);
|
map__put(alm);
|
||||||
@@ -1568,8 +1575,13 @@ static int hists__hierarchy_insert_entry(struct hists *hists,
|
|||||||
|
|
||||||
if (hist_entry__has_callchains(new_he) &&
|
if (hist_entry__has_callchains(new_he) &&
|
||||||
symbol_conf.use_callchain) {
|
symbol_conf.use_callchain) {
|
||||||
callchain_cursor_reset(&callchain_cursor);
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
if (callchain_merge(&callchain_cursor,
|
|
||||||
|
if (cursor == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
callchain_cursor_reset(cursor);
|
||||||
|
if (callchain_merge(cursor,
|
||||||
new_he->callchain,
|
new_he->callchain,
|
||||||
he->callchain) < 0)
|
he->callchain) < 0)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@@ -1610,11 +1622,15 @@ static int hists__collapse_insert_entry(struct hists *hists,
|
|||||||
he_stat__add_stat(iter->stat_acc, he->stat_acc);
|
he_stat__add_stat(iter->stat_acc, he->stat_acc);
|
||||||
|
|
||||||
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
|
||||||
callchain_cursor_reset(&callchain_cursor);
|
struct callchain_cursor *cursor = get_tls_callchain_cursor();
|
||||||
if (callchain_merge(&callchain_cursor,
|
|
||||||
iter->callchain,
|
if (cursor != NULL) {
|
||||||
he->callchain) < 0)
|
callchain_cursor_reset(cursor);
|
||||||
ret = -1;
|
if (callchain_merge(cursor, iter->callchain, he->callchain) < 0)
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hist_entry__delete(he);
|
hist_entry__delete(he);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -3180,6 +3180,9 @@ int thread__resolve_callchain(struct thread *thread,
|
|||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (cursor == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
callchain_cursor_reset(cursor);
|
callchain_cursor_reset(cursor);
|
||||||
|
|
||||||
if (callchain_param.order == ORDER_CALLEE) {
|
if (callchain_param.order == ORDER_CALLEE) {
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ static SV *perl_process_callchain(struct perf_sample *sample,
|
|||||||
struct evsel *evsel,
|
struct evsel *evsel,
|
||||||
struct addr_location *al)
|
struct addr_location *al)
|
||||||
{
|
{
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
AV *list;
|
AV *list;
|
||||||
|
|
||||||
list = newAV();
|
list = newAV();
|
||||||
@@ -269,18 +270,20 @@ static SV *perl_process_callchain(struct perf_sample *sample,
|
|||||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
cursor = get_tls_callchain_cursor();
|
||||||
|
|
||||||
|
if (thread__resolve_callchain(al->thread, cursor, evsel,
|
||||||
sample, NULL, NULL, scripting_max_stack) != 0) {
|
sample, NULL, NULL, scripting_max_stack) != 0) {
|
||||||
pr_err("Failed to resolve callchain. Skipping\n");
|
pr_err("Failed to resolve callchain. Skipping\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
callchain_cursor_commit(cursor);
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
HV *elem;
|
HV *elem;
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
node = callchain_cursor_current(&callchain_cursor);
|
node = callchain_cursor_current(cursor);
|
||||||
if (!node)
|
if (!node)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -328,7 +331,7 @@ static SV *perl_process_callchain(struct perf_sample *sample,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
callchain_cursor_advance(cursor);
|
||||||
av_push(list, newRV_noinc((SV*)elem));
|
av_push(list, newRV_noinc((SV*)elem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -417,6 +417,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
|
|||||||
struct addr_location *al)
|
struct addr_location *al)
|
||||||
{
|
{
|
||||||
PyObject *pylist;
|
PyObject *pylist;
|
||||||
|
struct callchain_cursor *cursor;
|
||||||
|
|
||||||
pylist = PyList_New(0);
|
pylist = PyList_New(0);
|
||||||
if (!pylist)
|
if (!pylist)
|
||||||
@@ -425,19 +426,20 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
|
|||||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
cursor = get_tls_callchain_cursor();
|
||||||
|
if (thread__resolve_callchain(al->thread, cursor, evsel,
|
||||||
sample, NULL, NULL,
|
sample, NULL, NULL,
|
||||||
scripting_max_stack) != 0) {
|
scripting_max_stack) != 0) {
|
||||||
pr_err("Failed to resolve callchain. Skipping\n");
|
pr_err("Failed to resolve callchain. Skipping\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
callchain_cursor_commit(&callchain_cursor);
|
callchain_cursor_commit(cursor);
|
||||||
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
PyObject *pyelem;
|
PyObject *pyelem;
|
||||||
struct callchain_cursor_node *node;
|
struct callchain_cursor_node *node;
|
||||||
node = callchain_cursor_current(&callchain_cursor);
|
node = callchain_cursor_current(cursor);
|
||||||
if (!node)
|
if (!node)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -493,7 +495,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
|
|||||||
_PyUnicode_FromString(dsoname));
|
_PyUnicode_FromString(dsoname));
|
||||||
}
|
}
|
||||||
|
|
||||||
callchain_cursor_advance(&callchain_cursor);
|
callchain_cursor_advance(cursor);
|
||||||
PyList_Append(pylist, pyelem);
|
PyList_Append(pylist, pyelem);
|
||||||
Py_DECREF(pyelem);
|
Py_DECREF(pyelem);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user