Skip to content

Commit 8ab12a2

Browse files
captain5050acmel
authored andcommitted
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 <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Ali Saidi <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Athira Rajeev <[email protected]> Cc: Brian Robbins <[email protected]> Cc: Changbin Du <[email protected]> Cc: Dmitrii Dolgov <[email protected]> Cc: Fangrui Song <[email protected]> Cc: German Gomez <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Ivan Babrou <[email protected]> Cc: James Clark <[email protected]> Cc: Jing Zhang <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: John Garry <[email protected]> Cc: K Prateek Nayak <[email protected]> Cc: Kan Liang <[email protected]> Cc: Leo Yan <[email protected]> Cc: Liam Howlett <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Miguel Ojeda <[email protected]> Cc: Mike Leach <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Naveen N. Rao <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Ravi Bangoria <[email protected]> Cc: Sean Christopherson <[email protected]> Cc: Steinar H. Gunderson <[email protected]> Cc: Suzuki Poulouse <[email protected]> Cc: Wenyu Liu <[email protected]> Cc: Will Deacon <[email protected]> Cc: Yang Jihong <[email protected]> Cc: Ye Xingchen <[email protected]> Cc: Yuan Can <[email protected]> Cc: [email protected] Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent d7ba60a commit 8ab12a2

15 files changed

+170
-61
lines changed

tools/perf/builtin-c2c.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
284284
struct hist_entry *he;
285285
struct addr_location al;
286286
struct mem_info *mi, *mi_dup;
287+
struct callchain_cursor *cursor;
287288
int ret;
288289

289290
addr_location__init(&al);
@@ -297,7 +298,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
297298
if (c2c.stitch_lbr)
298299
thread__set_lbr_stitch_enable(al.thread, true);
299300

300-
ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
301+
cursor = get_tls_callchain_cursor();
302+
ret = sample__resolve_callchain(sample, cursor, NULL,
301303
evsel, &al, sysctl_perf_event_max_stack);
302304
if (ret)
303305
goto out;

tools/perf/builtin-kmem.c

+10-4
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
399399
struct addr_location al;
400400
struct machine *machine = &kmem_session->machines.host;
401401
struct callchain_cursor_node *node;
402+
struct callchain_cursor *cursor;
402403
u64 result = sample->ip;
403404

404405
addr_location__init(&al);
@@ -408,14 +409,19 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
408409
}
409410

410411
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
411-
sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16);
412412

413-
callchain_cursor_commit(&callchain_cursor);
413+
cursor = get_tls_callchain_cursor();
414+
if (cursor == NULL)
415+
goto out;
416+
417+
sample__resolve_callchain(sample, cursor, NULL, evsel, &al, 16);
418+
419+
callchain_cursor_commit(cursor);
414420
while (true) {
415421
struct alloc_func key, *caller;
416422
u64 addr;
417423

418-
node = callchain_cursor_current(&callchain_cursor);
424+
node = callchain_cursor_current(cursor);
419425
if (node == NULL)
420426
break;
421427

@@ -434,7 +440,7 @@ static u64 find_callsite(struct evsel *evsel, struct perf_sample *sample)
434440
} else
435441
pr_debug3("skipping alloc function: %s\n", caller->name);
436442

437-
callchain_cursor_advance(&callchain_cursor);
443+
callchain_cursor_advance(cursor);
438444
}
439445

440446
pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);

tools/perf/builtin-kwork.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
589589
struct symbol *sym;
590590
struct thread *thread;
591591
struct callchain_cursor_node *node;
592-
struct callchain_cursor *cursor = &callchain_cursor;
592+
struct callchain_cursor *cursor;
593593

594594
if (!kwork->show_callchain || sample->callchain == NULL)
595595
return;
@@ -601,6 +601,8 @@ static void timehist_save_callchain(struct perf_kwork *kwork,
601601
return;
602602
}
603603

604+
cursor = get_tls_callchain_cursor();
605+
604606
if (thread__resolve_callchain(thread, cursor, evsel, sample,
605607
NULL, NULL, kwork->max_stack + 2) != 0) {
606608
pr_debug("Failed to resolve callchain, skipping\n");
@@ -686,12 +688,18 @@ static void timehist_print_event(struct perf_kwork *kwork,
686688
* callchain
687689
*/
688690
if (kwork->show_callchain) {
691+
struct callchain_cursor *cursor = get_tls_callchain_cursor();
692+
693+
if (cursor == NULL)
694+
return;
695+
689696
printf(" ");
697+
690698
sample__fprintf_sym(sample, al, 0,
691699
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
692700
EVSEL__PRINT_CALLCHAIN_ARROW |
693701
EVSEL__PRINT_SKIP_IGNORED,
694-
&callchain_cursor, symbol_conf.bt_stop_list,
702+
cursor, symbol_conf.bt_stop_list,
695703
stdout);
696704
}
697705

tools/perf/builtin-lock.c

+5-2
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
911911
char *buf, int size)
912912
{
913913
struct thread *thread;
914-
struct callchain_cursor *cursor = &callchain_cursor;
914+
struct callchain_cursor *cursor;
915915
struct machine *machine = &session->machines.host;
916916
struct symbol *sym;
917917
int skip = 0;
@@ -925,6 +925,8 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
925925
if (thread == NULL)
926926
return -1;
927927

928+
cursor = get_tls_callchain_cursor();
929+
928930
/* use caller function name from the callchain */
929931
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
930932
NULL, NULL, max_stack_depth);
@@ -962,7 +964,7 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
962964

963965
static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
964966
{
965-
struct callchain_cursor *cursor = &callchain_cursor;
967+
struct callchain_cursor *cursor;
966968
struct machine *machine = &session->machines.host;
967969
struct thread *thread;
968970
u64 hash = 0;
@@ -973,6 +975,7 @@ static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
973975
if (thread == NULL)
974976
return -1;
975977

978+
cursor = get_tls_callchain_cursor();
976979
/* use caller function name from the callchain */
977980
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
978981
NULL, NULL, max_stack_depth);

tools/perf/builtin-sched.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -2111,7 +2111,7 @@ static void timehist_print_sample(struct perf_sched *sched,
21112111
EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
21122112
EVSEL__PRINT_CALLCHAIN_ARROW |
21132113
EVSEL__PRINT_SKIP_IGNORED,
2114-
&callchain_cursor, symbol_conf.bt_stop_list, stdout);
2114+
get_tls_callchain_cursor(), symbol_conf.bt_stop_list, stdout);
21152115

21162116
out:
21172117
printf("\n");
@@ -2196,7 +2196,7 @@ static void save_task_callchain(struct perf_sched *sched,
21962196
struct evsel *evsel,
21972197
struct machine *machine)
21982198
{
2199-
struct callchain_cursor *cursor = &callchain_cursor;
2199+
struct callchain_cursor *cursor;
22002200
struct thread *thread;
22012201

22022202
/* want main thread for process - has maps */
@@ -2209,6 +2209,8 @@ static void save_task_callchain(struct perf_sched *sched,
22092209
if (!sched->show_callchain || sample->callchain == NULL)
22102210
return;
22112211

2212+
cursor = get_tls_callchain_cursor();
2213+
22122214
if (thread__resolve_callchain(thread, cursor, evsel, sample,
22132215
NULL, NULL, sched->max_stack + 2) != 0) {
22142216
if (verbose > 0)
@@ -2338,10 +2340,16 @@ static void save_idle_callchain(struct perf_sched *sched,
23382340
struct idle_thread_runtime *itr,
23392341
struct perf_sample *sample)
23402342
{
2343+
struct callchain_cursor *cursor;
2344+
23412345
if (!sched->show_callchain || sample->callchain == NULL)
23422346
return;
23432347

2344-
callchain_cursor__copy(&itr->cursor, &callchain_cursor);
2348+
cursor = get_tls_callchain_cursor();
2349+
if (cursor == NULL)
2350+
return;
2351+
2352+
callchain_cursor__copy(&itr->cursor, cursor);
23452353
}
23462354

23472355
static struct thread *timehist_get_thread(struct perf_sched *sched,

tools/perf/builtin-script.c

+14-10
Original file line numberDiff line numberDiff line change
@@ -1557,11 +1557,13 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
15571557
unsigned int print_opts = output[type].print_ip_opts;
15581558
struct callchain_cursor *cursor = NULL;
15591559

1560-
if (symbol_conf.use_callchain && sample->callchain &&
1561-
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
1562-
sample, NULL, NULL, scripting_max_stack) == 0)
1563-
cursor = &callchain_cursor;
1564-
1560+
if (symbol_conf.use_callchain && sample->callchain) {
1561+
cursor = get_tls_callchain_cursor();
1562+
if (thread__resolve_callchain(al->thread, cursor, evsel,
1563+
sample, NULL, NULL,
1564+
scripting_max_stack))
1565+
cursor = NULL;
1566+
}
15651567
if (cursor == NULL) {
15661568
printed += fprintf(fp, " ");
15671569
if (print_opts & EVSEL__PRINT_SRCLINE) {
@@ -2203,11 +2205,13 @@ static void process_event(struct perf_script *script,
22032205
if (script->stitch_lbr)
22042206
thread__set_lbr_stitch_enable(al->thread, true);
22052207

2206-
if (symbol_conf.use_callchain && sample->callchain &&
2207-
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
2208-
sample, NULL, NULL, scripting_max_stack) == 0)
2209-
cursor = &callchain_cursor;
2210-
2208+
if (symbol_conf.use_callchain && sample->callchain) {
2209+
cursor = get_tls_callchain_cursor();
2210+
if (thread__resolve_callchain(al->thread, cursor, evsel,
2211+
sample, NULL, NULL,
2212+
scripting_max_stack))
2213+
cursor = NULL;
2214+
}
22112215
fputc(cursor ? '\n' : ' ', fp);
22122216
sample__fprintf_sym(sample, al, 0, output[type].print_ip_opts, cursor,
22132217
symbol_conf.bt_stop_list, fp);

tools/perf/builtin-trace.c

+13-7
Original file line numberDiff line numberDiff line change
@@ -2437,7 +2437,7 @@ static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sam
24372437
EVSEL__PRINT_DSO |
24382438
EVSEL__PRINT_UNKNOWN_AS_ADDR;
24392439

2440-
return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, symbol_conf.bt_stop_list, trace->output);
2440+
return sample__fprintf_callchain(sample, 38, print_opts, get_tls_callchain_cursor(), symbol_conf.bt_stop_list, trace->output);
24412441
}
24422442

24432443
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,
24912491
goto out;
24922492

24932493
if (sample->callchain) {
2494-
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
2494+
struct callchain_cursor *cursor = get_tls_callchain_cursor();
2495+
2496+
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
24952497
if (callchain_ret == 0) {
2496-
if (callchain_cursor.nr < trace->min_stack)
2498+
if (cursor->nr < trace->min_stack)
24972499
goto out;
24982500
callchain_ret = 1;
24992501
}
@@ -2795,9 +2797,11 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel,
27952797
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
27962798

27972799
if (sample->callchain) {
2798-
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
2800+
struct callchain_cursor *cursor = get_tls_callchain_cursor();
2801+
2802+
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
27992803
if (callchain_ret == 0) {
2800-
if (callchain_cursor.nr < trace->min_stack)
2804+
if (cursor->nr < trace->min_stack)
28012805
goto out;
28022806
callchain_ret = 1;
28032807
}
@@ -2899,9 +2903,11 @@ static int trace__pgfault(struct trace *trace,
28992903
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
29002904

29012905
if (sample->callchain) {
2902-
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
2906+
struct callchain_cursor *cursor = get_tls_callchain_cursor();
2907+
2908+
callchain_ret = trace__resolve_callchain(trace, evsel, sample, cursor);
29032909
if (callchain_ret == 0) {
2904-
if (callchain_cursor.nr < trace->min_stack)
2910+
if (cursor->nr < trace->min_stack)
29052911
goto out_put;
29062912
callchain_ret = 1;
29072913
}

tools/perf/util/callchain.c

+43-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ struct callchain_param callchain_param_default = {
5858
CALLCHAIN_PARAM_DEFAULT
5959
};
6060

61-
__thread struct callchain_cursor callchain_cursor;
61+
/* Used for thread-local struct callchain_cursor. */
62+
static pthread_key_t callchain_cursor;
6263

6364
int parse_callchain_record_opt(const char *arg, struct callchain_param *param)
6465
{
@@ -986,6 +987,9 @@ int callchain_append(struct callchain_root *root,
986987
struct callchain_cursor *cursor,
987988
u64 period)
988989
{
990+
if (cursor == NULL)
991+
return -1;
992+
989993
if (!cursor->nr)
990994
return 0;
991995

@@ -1116,7 +1120,7 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
11161120
if ((!symbol_conf.use_callchain || sample->callchain == NULL) &&
11171121
!symbol_conf.show_branchflag_count)
11181122
return 0;
1119-
return callchain_append(he->callchain, &callchain_cursor, sample->period);
1123+
return callchain_append(he->callchain, get_tls_callchain_cursor(), sample->period);
11201124
}
11211125

11221126
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)
15701574
return -ENOMEM;
15711575
}
15721576

1577+
static void callchain_cursor__delete(void *vcursor)
1578+
{
1579+
struct callchain_cursor *cursor = vcursor;
1580+
struct callchain_cursor_node *node, *next;
1581+
1582+
callchain_cursor_reset(cursor);
1583+
for (node = cursor->first; node != NULL; node = next) {
1584+
next = node->next;
1585+
free(node);
1586+
}
1587+
free(cursor);
1588+
}
1589+
1590+
static void init_callchain_cursor_key(void)
1591+
{
1592+
if (pthread_key_create(&callchain_cursor, callchain_cursor__delete)) {
1593+
pr_err("callchain cursor creation failed");
1594+
abort();
1595+
}
1596+
}
1597+
1598+
struct callchain_cursor *get_tls_callchain_cursor(void)
1599+
{
1600+
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
1601+
struct callchain_cursor *cursor;
1602+
1603+
pthread_once(&once_control, init_callchain_cursor_key);
1604+
cursor = pthread_getspecific(callchain_cursor);
1605+
if (!cursor) {
1606+
cursor = zalloc(sizeof(*cursor));
1607+
if (!cursor)
1608+
pr_debug3("%s: not enough memory\n", __func__);
1609+
pthread_setspecific(callchain_cursor, cursor);
1610+
}
1611+
return cursor;
1612+
}
1613+
15731614
int callchain_cursor__copy(struct callchain_cursor *dst,
15741615
struct callchain_cursor *src)
15751616
{

tools/perf/util/callchain.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,6 @@ struct callchain_cursor {
168168
struct callchain_cursor_node *curr;
169169
};
170170

171-
extern __thread struct callchain_cursor callchain_cursor;
172-
173171
static inline void callchain_init(struct callchain_root *root)
174172
{
175173
INIT_LIST_HEAD(&root->node.val);
@@ -211,6 +209,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
211209
/* Close a cursor writing session. Initialize for the reader */
212210
static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
213211
{
212+
if (cursor == NULL)
213+
return;
214214
cursor->curr = cursor->first;
215215
cursor->pos = 0;
216216
}
@@ -219,7 +219,7 @@ static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
219219
static inline struct callchain_cursor_node *
220220
callchain_cursor_current(struct callchain_cursor *cursor)
221221
{
222-
if (cursor->pos == cursor->nr)
222+
if (cursor == NULL || cursor->pos == cursor->nr)
223223
return NULL;
224224

225225
return cursor->curr;
@@ -231,6 +231,8 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor)
231231
cursor->pos++;
232232
}
233233

234+
struct callchain_cursor *get_tls_callchain_cursor(void);
235+
234236
int callchain_cursor__copy(struct callchain_cursor *dst,
235237
struct callchain_cursor *src);
236238

0 commit comments

Comments
 (0)