Skip to content

Commit 0d2997f

Browse files
namhyungacmel
authored andcommitted
perf lock: Look up callchain for the contended locks
The lock contention tracepoints don't provide lock names. All we can do is to get stack traces and show the caller instead. To minimize the overhead it's limited to up to 8 stack traces and display the first non-lock function symbol name as a caller. $ perf lock report -F acquired,contended,avg_wait,wait_total Name acquired contended avg wait total wait update_blocked_a... 40 40 3.61 us 144.45 us kernfs_fop_open+... 5 5 3.64 us 18.18 us _nohz_idle_balance 3 3 2.65 us 7.95 us tick_do_update_j... 1 1 6.04 us 6.04 us ep_scan_ready_list 1 1 3.93 us 3.93 us ... Signed-off-by: Namhyung Kim <[email protected]> Acked-by: Ian Rogers <[email protected]> Cc: Boqun Feng <[email protected]> Cc: Davidlohr Bueso <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Waiman Long <[email protected]> Cc: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 7cb2a53 commit 0d2997f

File tree

1 file changed

+156
-4
lines changed

1 file changed

+156
-4
lines changed

tools/perf/builtin-lock.c

+156-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "util/symbol.h"
1010
#include "util/thread.h"
1111
#include "util/header.h"
12+
#include "util/callchain.h"
1213

1314
#include <subcmd/pager.h>
1415
#include <subcmd/parse-options.h>
@@ -19,6 +20,7 @@
1920
#include "util/tool.h"
2021
#include "util/data.h"
2122
#include "util/string2.h"
23+
#include "util/map.h"
2224

2325
#include <sys/types.h>
2426
#include <sys/prctl.h>
@@ -32,6 +34,7 @@
3234
#include <linux/kernel.h>
3335
#include <linux/zalloc.h>
3436
#include <linux/err.h>
37+
#include <linux/stringify.h>
3538

3639
static struct perf_session *session;
3740

@@ -120,6 +123,24 @@ static struct rb_root thread_stats;
120123
static bool combine_locks;
121124
static bool show_thread_stats;
122125

126+
/*
127+
* CONTENTION_STACK_DEPTH
128+
* Number of stack trace entries to find callers
129+
*/
130+
#define CONTENTION_STACK_DEPTH 8
131+
132+
/*
133+
* CONTENTION_STACK_SKIP
134+
* Number of stack trace entries to skip when finding callers.
135+
* The first few entries belong to the locking implementation itself.
136+
*/
137+
#define CONTENTION_STACK_SKIP 3
138+
139+
static u64 sched_text_start;
140+
static u64 sched_text_end;
141+
static u64 lock_text_start;
142+
static u64 lock_text_end;
143+
123144
static struct thread_stat *thread_stat_find(u32 tid)
124145
{
125146
struct rb_node *node;
@@ -839,6 +860,116 @@ static int report_lock_release_event(struct evsel *evsel,
839860
return 0;
840861
}
841862

863+
static bool is_lock_function(u64 addr)
864+
{
865+
if (!sched_text_start) {
866+
struct machine *machine = &session->machines.host;
867+
struct map *kmap;
868+
struct symbol *sym;
869+
870+
sym = machine__find_kernel_symbol_by_name(machine,
871+
"__sched_text_start",
872+
&kmap);
873+
if (!sym) {
874+
/* to avoid retry */
875+
sched_text_start = 1;
876+
return false;
877+
}
878+
879+
sched_text_start = kmap->unmap_ip(kmap, sym->start);
880+
881+
/* should not fail from here */
882+
sym = machine__find_kernel_symbol_by_name(machine,
883+
"__sched_text_end",
884+
&kmap);
885+
sched_text_end = kmap->unmap_ip(kmap, sym->start);
886+
887+
sym = machine__find_kernel_symbol_by_name(machine,
888+
"__lock_text_start",
889+
&kmap);
890+
lock_text_start = kmap->unmap_ip(kmap, sym->start);
891+
892+
sym = machine__find_kernel_symbol_by_name(machine,
893+
"__lock_text_end",
894+
&kmap);
895+
lock_text_start = kmap->unmap_ip(kmap, sym->start);
896+
}
897+
898+
/* failed to get kernel symbols */
899+
if (sched_text_start == 1)
900+
return false;
901+
902+
/* mutex and rwsem functions are in sched text section */
903+
if (sched_text_start <= addr && addr < sched_text_end)
904+
return true;
905+
906+
/* spinlock functions are in lock text section */
907+
if (lock_text_start <= addr && addr < lock_text_end)
908+
return true;
909+
910+
return false;
911+
}
912+
913+
static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample,
914+
char *buf, int size)
915+
{
916+
struct thread *thread;
917+
struct callchain_cursor *cursor = &callchain_cursor;
918+
struct symbol *sym;
919+
int skip = 0;
920+
int ret;
921+
922+
/* lock names will be replaced to task name later */
923+
if (show_thread_stats)
924+
return -1;
925+
926+
thread = machine__findnew_thread(&session->machines.host,
927+
-1, sample->pid);
928+
if (thread == NULL)
929+
return -1;
930+
931+
/* use caller function name from the callchain */
932+
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
933+
NULL, NULL, CONTENTION_STACK_DEPTH);
934+
if (ret != 0) {
935+
thread__put(thread);
936+
return -1;
937+
}
938+
939+
callchain_cursor_commit(cursor);
940+
thread__put(thread);
941+
942+
while (true) {
943+
struct callchain_cursor_node *node;
944+
945+
node = callchain_cursor_current(cursor);
946+
if (node == NULL)
947+
break;
948+
949+
/* skip first few entries - for lock functions */
950+
if (++skip <= CONTENTION_STACK_SKIP)
951+
goto next;
952+
953+
sym = node->ms.sym;
954+
if (sym && !is_lock_function(node->ip)) {
955+
struct map *map = node->ms.map;
956+
u64 offset;
957+
958+
offset = map->map_ip(map, node->ip) - sym->start;
959+
960+
if (offset)
961+
scnprintf(buf, size, "%s+%#lx", sym->name, offset);
962+
else
963+
strlcpy(buf, sym->name, size);
964+
return 0;
965+
}
966+
967+
next:
968+
callchain_cursor_advance(cursor);
969+
}
970+
return -1;
971+
}
972+
842973
static int report_lock_contention_begin_event(struct evsel *evsel,
843974
struct perf_sample *sample)
844975
{
@@ -850,9 +981,18 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
850981
if (show_thread_stats)
851982
addr = sample->tid;
852983

853-
ls = lock_stat_findnew(addr, "No name");
854-
if (!ls)
855-
return -ENOMEM;
984+
ls = lock_stat_find(addr);
985+
if (!ls) {
986+
char buf[128];
987+
const char *caller = buf;
988+
989+
if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
990+
caller = "Unknown";
991+
992+
ls = lock_stat_findnew(addr, caller);
993+
if (!ls)
994+
return -ENOMEM;
995+
}
856996

857997
ts = thread_stat_findnew(sample->tid);
858998
if (!ts)
@@ -1233,6 +1373,7 @@ static int __cmd_report(bool display_info)
12331373
struct perf_tool eops = {
12341374
.sample = process_sample_event,
12351375
.comm = perf_event__process_comm,
1376+
.mmap = perf_event__process_mmap,
12361377
.namespaces = perf_event__process_namespaces,
12371378
.ordered_events = true,
12381379
};
@@ -1248,6 +1389,8 @@ static int __cmd_report(bool display_info)
12481389
return PTR_ERR(session);
12491390
}
12501391

1392+
/* for lock function check */
1393+
symbol_conf.sort_by_name = true;
12511394
symbol__init(&session->header.env);
12521395

12531396
if (!perf_session__has_traces(session, "lock record"))
@@ -1292,8 +1435,12 @@ static int __cmd_record(int argc, const char **argv)
12921435
const char *record_args[] = {
12931436
"record", "-R", "-m", "1024", "-c", "1", "--synth", "task",
12941437
};
1438+
const char *callgraph_args[] = {
1439+
"--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH),
1440+
};
12951441
unsigned int rec_argc, i, j, ret;
12961442
unsigned int nr_tracepoints;
1443+
unsigned int nr_callgraph_args = 0;
12971444
const char **rec_argv;
12981445
bool has_lock_stat = true;
12991446

@@ -1318,8 +1465,10 @@ static int __cmd_record(int argc, const char **argv)
13181465
}
13191466
}
13201467

1468+
nr_callgraph_args = ARRAY_SIZE(callgraph_args);
1469+
13211470
setup_args:
1322-
rec_argc = ARRAY_SIZE(record_args) + argc - 1;
1471+
rec_argc = ARRAY_SIZE(record_args) + nr_callgraph_args + argc - 1;
13231472

13241473
if (has_lock_stat)
13251474
nr_tracepoints = ARRAY_SIZE(lock_tracepoints);
@@ -1351,6 +1500,9 @@ static int __cmd_record(int argc, const char **argv)
13511500
rec_argv[i++] = ev_name;
13521501
}
13531502

1503+
for (j = 0; j < nr_callgraph_args; j++, i++)
1504+
rec_argv[i] = callgraph_args[j];
1505+
13541506
for (j = 1; j < (unsigned int)argc; j++, i++)
13551507
rec_argv[i] = argv[j];
13561508

0 commit comments

Comments
 (0)