Skip to content

Commit 528b9ca

Browse files
namhyungacmel
authored andcommitted
perf lock: Add 'contention' subcommand
The 'perf lock contention' processes the lock contention events and displays the result like perf lock report. Right now, there's not much difference between the two but the lock contention specific features will come soon. $ perf lock contention contended total wait max wait avg wait type caller 238 1.41 ms 29.20 us 5.94 us spinlock update_blocked_averages+0x4c 1 902.08 us 902.08 us 902.08 us rwsem:R do_user_addr_fault+0x1dd 81 330.30 us 17.24 us 4.08 us spinlock _nohz_idle_balance+0x172 2 89.54 us 61.26 us 44.77 us spinlock do_anonymous_page+0x16d 24 78.36 us 12.27 us 3.27 us mutex pipe_read+0x56 2 71.58 us 59.56 us 35.79 us spinlock __handle_mm_fault+0x6aa 6 25.68 us 6.89 us 4.28 us spinlock do_idle+0x28d 1 18.46 us 18.46 us 18.46 us rtmutex exec_fw_cmd+0x21b 3 15.25 us 6.26 us 5.08 us spinlock tick_do_update_jiffies64+0x2c Signed-off-by: Namhyung Kim <namhyung@kernel.org> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Waiman Long <longman@redhat.com> Cc: Will Deacon <will@kernel.org> Link: https://lore.kernel.org/r/20220725183124.368304-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent f9c695a commit 528b9ca

File tree

2 files changed

+214
-2
lines changed

2 files changed

+214
-2
lines changed

tools/perf/Documentation/perf-lock.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ perf-lock - Analyze lock events
88
SYNOPSIS
99
--------
1010
[verse]
11-
'perf lock' {record|report|script|info}
11+
'perf lock' {record|report|script|info|contention}
1212

1313
DESCRIPTION
1414
-----------
@@ -27,6 +27,8 @@ and statistics with this 'perf lock' command.
2727
'perf lock info' shows metadata like threads or addresses
2828
of lock instances.
2929

30+
'perf lock contention' shows contention statistics.
31+
3032
COMMON OPTIONS
3133
--------------
3234

tools/perf/builtin-lock.c

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ static enum {
145145
*/
146146
#define CONTENTION_STACK_SKIP 3
147147

148+
/*
149+
* flags for lock:contention_begin
150+
* Imported from include/trace/events/lock.h.
151+
*/
152+
#define LCB_F_SPIN (1U << 0)
153+
#define LCB_F_READ (1U << 1)
154+
#define LCB_F_WRITE (1U << 2)
155+
#define LCB_F_RT (1U << 3)
156+
#define LCB_F_PERCPU (1U << 4)
157+
#define LCB_F_MUTEX (1U << 5)
158+
159+
148160
static u64 sched_text_start;
149161
static u64 sched_text_end;
150162
static u64 lock_text_start;
@@ -1022,6 +1034,51 @@ static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sampl
10221034
return -1;
10231035
}
10241036

1037+
static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
1038+
{
1039+
struct callchain_cursor *cursor = &callchain_cursor;
1040+
struct thread *thread;
1041+
u64 hash = 0;
1042+
int skip = 0;
1043+
int ret;
1044+
1045+
thread = machine__findnew_thread(&session->machines.host,
1046+
-1, sample->pid);
1047+
if (thread == NULL)
1048+
return -1;
1049+
1050+
/* use caller function name from the callchain */
1051+
ret = thread__resolve_callchain(thread, cursor, evsel, sample,
1052+
NULL, NULL, CONTENTION_STACK_DEPTH);
1053+
thread__put(thread);
1054+
1055+
if (ret != 0)
1056+
return -1;
1057+
1058+
callchain_cursor_commit(cursor);
1059+
1060+
while (true) {
1061+
struct callchain_cursor_node *node;
1062+
1063+
node = callchain_cursor_current(cursor);
1064+
if (node == NULL)
1065+
break;
1066+
1067+
/* skip first few entries - for lock functions */
1068+
if (++skip <= CONTENTION_STACK_SKIP)
1069+
goto next;
1070+
1071+
if (node->ms.sym && is_lock_function(node->ip))
1072+
goto next;
1073+
1074+
hash ^= hash_long((unsigned long)node->ip, 64);
1075+
1076+
next:
1077+
callchain_cursor_advance(cursor);
1078+
}
1079+
return hash;
1080+
}
1081+
10251082
static int report_lock_contention_begin_event(struct evsel *evsel,
10261083
struct perf_sample *sample)
10271084
{
@@ -1039,6 +1096,8 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
10391096
key = sample->tid;
10401097
break;
10411098
case LOCK_AGGR_CALLER:
1099+
key = callchain_id(evsel, sample);
1100+
break;
10421101
default:
10431102
pr_err("Invalid aggregation mode: %d\n", aggr_mode);
10441103
return -EINVAL;
@@ -1120,6 +1179,8 @@ static int report_lock_contention_end_event(struct evsel *evsel,
11201179
key = sample->tid;
11211180
break;
11221181
case LOCK_AGGR_CALLER:
1182+
key = callchain_id(evsel, sample);
1183+
break;
11231184
default:
11241185
pr_err("Invalid aggregation mode: %d\n", aggr_mode);
11251186
return -EINVAL;
@@ -1183,6 +1244,12 @@ static struct trace_lock_handler report_lock_ops = {
11831244
.contention_end_event = report_lock_contention_end_event,
11841245
};
11851246

1247+
static struct trace_lock_handler contention_lock_ops = {
1248+
.contention_begin_event = report_lock_contention_begin_event,
1249+
.contention_end_event = report_lock_contention_end_event,
1250+
};
1251+
1252+
11861253
static struct trace_lock_handler *trace_handler;
11871254

11881255
static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample)
@@ -1428,6 +1495,67 @@ static void sort_result(void)
14281495
}
14291496
}
14301497

1498+
static const char *get_type_str(struct lock_stat *st)
1499+
{
1500+
static const struct {
1501+
unsigned int flags;
1502+
const char *name;
1503+
} table[] = {
1504+
{ 0, "semaphore" },
1505+
{ LCB_F_SPIN, "spinlock" },
1506+
{ LCB_F_SPIN | LCB_F_READ, "rwlock:R" },
1507+
{ LCB_F_SPIN | LCB_F_WRITE, "rwlock:W"},
1508+
{ LCB_F_READ, "rwsem:R" },
1509+
{ LCB_F_WRITE, "rwsem:W" },
1510+
{ LCB_F_RT, "rtmutex" },
1511+
{ LCB_F_RT | LCB_F_READ, "rwlock-rt:R" },
1512+
{ LCB_F_RT | LCB_F_WRITE, "rwlock-rt:W"},
1513+
{ LCB_F_PERCPU | LCB_F_READ, "pcpu-sem:R" },
1514+
{ LCB_F_PERCPU | LCB_F_WRITE, "pcpu-sem:W" },
1515+
{ LCB_F_MUTEX, "mutex" },
1516+
{ LCB_F_MUTEX | LCB_F_SPIN, "mutex" },
1517+
};
1518+
1519+
for (unsigned int i = 0; i < ARRAY_SIZE(table); i++) {
1520+
if (table[i].flags == st->flags)
1521+
return table[i].name;
1522+
}
1523+
return "unknown";
1524+
}
1525+
1526+
static void sort_contention_result(void)
1527+
{
1528+
sort_result();
1529+
}
1530+
1531+
static void print_contention_result(void)
1532+
{
1533+
struct lock_stat *st;
1534+
struct lock_key *key;
1535+
int bad, total;
1536+
1537+
list_for_each_entry(key, &lock_keys, list)
1538+
pr_info("%*s ", key->len, key->header);
1539+
1540+
pr_info(" %10s %s\n\n", "type", "caller");
1541+
1542+
bad = total = 0;
1543+
while ((st = pop_from_result())) {
1544+
total++;
1545+
if (st->broken)
1546+
bad++;
1547+
1548+
list_for_each_entry(key, &lock_keys, list) {
1549+
key->print(key, st);
1550+
pr_info(" ");
1551+
}
1552+
1553+
pr_info(" %10s %s\n", get_type_str(st), st->name);
1554+
}
1555+
1556+
print_bad_events(bad, total);
1557+
}
1558+
14311559
static const struct evsel_str_handler lock_tracepoints[] = {
14321560
{ "lock:lock_acquire", evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
14331561
{ "lock:lock_acquired", evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
@@ -1508,6 +1636,68 @@ static int __cmd_report(bool display_info)
15081636
return err;
15091637
}
15101638

1639+
static int __cmd_contention(void)
1640+
{
1641+
int err = -EINVAL;
1642+
struct perf_tool eops = {
1643+
.sample = process_sample_event,
1644+
.comm = perf_event__process_comm,
1645+
.mmap = perf_event__process_mmap,
1646+
.ordered_events = true,
1647+
};
1648+
struct perf_data data = {
1649+
.path = input_name,
1650+
.mode = PERF_DATA_MODE_READ,
1651+
.force = force,
1652+
};
1653+
1654+
session = perf_session__new(&data, &eops);
1655+
if (IS_ERR(session)) {
1656+
pr_err("Initializing perf session failed\n");
1657+
return PTR_ERR(session);
1658+
}
1659+
1660+
/* for lock function check */
1661+
symbol_conf.sort_by_name = true;
1662+
symbol__init(&session->header.env);
1663+
1664+
if (!perf_session__has_traces(session, "lock record"))
1665+
goto out_delete;
1666+
1667+
if (!evlist__find_evsel_by_str(session->evlist, "lock:contention_begin")) {
1668+
pr_err("lock contention evsel not found\n");
1669+
goto out_delete;
1670+
}
1671+
1672+
if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) {
1673+
pr_err("Initializing perf session tracepoint handlers failed\n");
1674+
goto out_delete;
1675+
}
1676+
1677+
if (setup_output_field("contended,wait_total,wait_max,avg_wait"))
1678+
goto out_delete;
1679+
1680+
sort_key = "wait_total";
1681+
if (select_key())
1682+
goto out_delete;
1683+
1684+
aggr_mode = LOCK_AGGR_CALLER;
1685+
1686+
err = perf_session__process_events(session);
1687+
if (err)
1688+
goto out_delete;
1689+
1690+
setup_pager();
1691+
1692+
sort_contention_result();
1693+
print_contention_result();
1694+
1695+
out_delete:
1696+
perf_session__delete(session);
1697+
return err;
1698+
}
1699+
1700+
15111701
static int __cmd_record(int argc, const char **argv)
15121702
{
15131703
const char *record_args[] = {
@@ -1626,12 +1816,17 @@ int cmd_lock(int argc, const char **argv)
16261816
OPT_PARENT(lock_options)
16271817
};
16281818

1819+
const struct option contention_options[] = {
1820+
OPT_PARENT(lock_options)
1821+
};
1822+
16291823
const char * const info_usage[] = {
16301824
"perf lock info [<options>]",
16311825
NULL
16321826
};
16331827
const char *const lock_subcommands[] = { "record", "report", "script",
1634-
"info", NULL };
1828+
"info", "contention",
1829+
"contention", NULL };
16351830
const char *lock_usage[] = {
16361831
NULL,
16371832
NULL
@@ -1640,6 +1835,10 @@ int cmd_lock(int argc, const char **argv)
16401835
"perf lock report [<options>]",
16411836
NULL
16421837
};
1838+
const char * const contention_usage[] = {
1839+
"perf lock contention [<options>]",
1840+
NULL
1841+
};
16431842
unsigned int i;
16441843
int rc = 0;
16451844

@@ -1675,6 +1874,17 @@ int cmd_lock(int argc, const char **argv)
16751874
/* recycling report_lock_ops */
16761875
trace_handler = &report_lock_ops;
16771876
rc = __cmd_report(true);
1877+
} else if (strlen(argv[0]) > 2 && strstarts("contention", argv[0])) {
1878+
trace_handler = &contention_lock_ops;
1879+
if (argc) {
1880+
argc = parse_options(argc, argv, contention_options,
1881+
contention_usage, 0);
1882+
if (argc) {
1883+
usage_with_options(contention_usage,
1884+
contention_options);
1885+
}
1886+
}
1887+
rc = __cmd_contention();
16781888
} else {
16791889
usage_with_options(lock_usage, lock_options);
16801890
}

0 commit comments

Comments
 (0)