Commit 8ab12a20 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo

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: default avatarIan 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent d7ba60a4
...@@ -284,6 +284,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl ...@@ -962,7 +964,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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,
......
...@@ -1557,11 +1557,13 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, ...@@ -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, ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 = { ...@@ -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, ...@@ -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 ...@@ -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 @@ int callchain_node__make_parent_list(struct callchain_node *node) ...@@ -1570,6 +1574,43 @@ int callchain_node__make_parent_list(struct callchain_node *node)
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 { ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
......
...@@ -1029,15 +1029,19 @@ iter_prepare_cumulative_entry(struct hist_entry_iter *iter, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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_snapshot(&cursor, tls_cursor);
callchain_cursor_advance(&callchain_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, ...@@ -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, ...@@ -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, ...@@ -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);
if (callchain_merge(cursor, iter->callchain, he->callchain) < 0)
ret = -1; 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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment