Skip to content

Commit ebab291

Browse files
namhyungacmel
authored andcommitted
perf lock contention: Support filters for different aggregation
It'd be useful to filter other than the current aggregation mode. For example, users may want to see callstacks for specific locks only. Or they may want tasks from a certain callstack. The tracepoints already collected the information but it needs to check the condition again when processing the event. And it needs to change BPF to allow the key combinations. The lock contentions on 'rcu_state' spinlock can be monitored: $ sudo perf lock con -abv -L rcu_state sleep 1 ... contended total wait max wait avg wait type caller 4 151.39 us 62.57 us 37.85 us spinlock rcu_core+0xcb 0xffffffff81fd1666 _raw_spin_lock_irqsave+0x46 0xffffffff8172d76b rcu_core+0xcb 0xffffffff822000eb __softirqentry_text_start+0xeb 0xffffffff816a0ba9 __irq_exit_rcu+0xc9 0xffffffff81fc0112 sysvec_apic_timer_interrupt+0xa2 0xffffffff82000e46 asm_sysvec_apic_timer_interrupt+0x16 0xffffffff81d49f78 cpuidle_enter_state+0xd8 0xffffffff81d4a259 cpuidle_enter+0x29 1 30.21 us 30.21 us 30.21 us spinlock rcu_core+0xcb 0xffffffff81fd1666 _raw_spin_lock_irqsave+0x46 0xffffffff8172d76b rcu_core+0xcb 0xffffffff822000eb __softirqentry_text_start+0xeb 0xffffffff816a0ba9 __irq_exit_rcu+0xc9 0xffffffff81fc00c4 sysvec_apic_timer_interrupt+0x54 0xffffffff82000e46 asm_sysvec_apic_timer_interrupt+0x16 1 28.84 us 28.84 us 28.84 us spinlock rcu_accelerate_cbs_unlocked+0x40 0xffffffff81fd1c60 _raw_spin_lock+0x30 0xffffffff81728cf0 rcu_accelerate_cbs_unlocked+0x40 0xffffffff8172da82 rcu_core+0x3e2 0xffffffff822000eb __softirqentry_text_start+0xeb 0xffffffff816a0ba9 __irq_exit_rcu+0xc9 0xffffffff81fc0112 sysvec_apic_timer_interrupt+0xa2 0xffffffff82000e46 asm_sysvec_apic_timer_interrupt+0x16 0xffffffff81d49f78 cpuidle_enter_state+0xd8 ... To see tasks calling 'rcu_core' function: $ sudo perf lock con -abt -S rcu_core sleep 1 contended total wait max wait avg wait pid comm 19 23.46 us 2.21 us 1.23 us 0 swapper 2 18.37 us 17.01 us 9.19 us 2061859 ThreadPoolForeg 3 5.76 us 1.97 us 1.92 us 3909 pipewire-pulse 1 2.26 us 2.26 us 2.26 us 1809271 MediaSu~isor #2 1 1.97 us 1.97 us 1.97 us 1514882 Chrome_ChildIOT 1 987 ns 987 ns 987 ns 3740 pipewire-pulse Signed-off-by: Namhyung Kim <[email protected]> Cc: Adrian Hunter <[email protected]> Cc: Hao Luo <[email protected]> Cc: Ian Rogers <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Juri Lelli <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Song Liu <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent 16cad1d commit ebab291

File tree

5 files changed

+89
-51
lines changed

5 files changed

+89
-51
lines changed

tools/perf/builtin-lock.c

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,34 @@ struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
509509
return NULL;
510510
}
511511

512+
bool match_callstack_filter(struct machine *machine, u64 *callstack)
513+
{
514+
struct map *kmap;
515+
struct symbol *sym;
516+
u64 ip;
517+
518+
if (list_empty(&callstack_filters))
519+
return true;
520+
521+
for (int i = 0; i < max_stack_depth; i++) {
522+
struct callstack_filter *filter;
523+
524+
if (!callstack || !callstack[i])
525+
break;
526+
527+
ip = callstack[i];
528+
sym = machine__find_kernel_symbol(machine, ip, &kmap);
529+
if (sym == NULL)
530+
continue;
531+
532+
list_for_each_entry(filter, &callstack_filters, list) {
533+
if (strstr(sym->name, filter->name))
534+
return true;
535+
}
536+
}
537+
return false;
538+
}
539+
512540
struct trace_lock_handler {
513541
/* it's used on CONFIG_LOCKDEP */
514542
int (*acquire_event)(struct evsel *evsel,
@@ -1070,12 +1098,6 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
10701098
ls = lock_stat_findnew(key, name, flags);
10711099
if (!ls)
10721100
return -ENOMEM;
1073-
1074-
if (aggr_mode == LOCK_AGGR_CALLER && needs_callstack()) {
1075-
ls->callstack = get_callstack(sample, max_stack_depth);
1076-
if (ls->callstack == NULL)
1077-
return -ENOMEM;
1078-
}
10791101
}
10801102

10811103
if (filters.nr_types) {
@@ -1106,6 +1128,22 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
11061128
return 0;
11071129
}
11081130

1131+
if (needs_callstack()) {
1132+
u64 *callstack = get_callstack(sample, max_stack_depth);
1133+
if (callstack == NULL)
1134+
return -ENOMEM;
1135+
1136+
if (!match_callstack_filter(machine, callstack)) {
1137+
free(callstack);
1138+
return 0;
1139+
}
1140+
1141+
if (ls->callstack == NULL)
1142+
ls->callstack = callstack;
1143+
else
1144+
free(callstack);
1145+
}
1146+
11091147
ts = thread_stat_findnew(sample->tid);
11101148
if (!ts)
11111149
return -ENOMEM;
@@ -1606,31 +1644,6 @@ static void print_contention_result(struct lock_contention *con)
16061644
if (!st->wait_time_total)
16071645
continue;
16081646

1609-
if (aggr_mode == LOCK_AGGR_CALLER && !list_empty(&callstack_filters)) {
1610-
struct map *kmap;
1611-
struct symbol *sym;
1612-
u64 ip;
1613-
1614-
for (int i = 0; i < max_stack_depth; i++) {
1615-
struct callstack_filter *filter;
1616-
1617-
if (!st->callstack || !st->callstack[i])
1618-
break;
1619-
1620-
ip = st->callstack[i];
1621-
sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
1622-
if (sym == NULL)
1623-
continue;
1624-
1625-
list_for_each_entry(filter, &callstack_filters, list) {
1626-
if (strstr(sym->name, filter->name))
1627-
goto found;
1628-
}
1629-
}
1630-
continue;
1631-
}
1632-
1633-
found:
16341647
list_for_each_entry(key, &lock_keys, list) {
16351648
key->print(key, st);
16361649
pr_info(" ");

tools/perf/util/bpf_lock_contention.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ int lock_contention_prepare(struct lock_contention *con)
3434
bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries);
3535
bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries);
3636

37-
if (con->aggr_mode == LOCK_AGGR_TASK) {
37+
if (con->aggr_mode == LOCK_AGGR_TASK)
3838
bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries);
39-
bpf_map__set_max_entries(skel->maps.stacks, 1);
40-
} else {
39+
else
4140
bpf_map__set_max_entries(skel->maps.task_data, 1);
41+
42+
if (con->save_callstack)
4243
bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries);
43-
}
44+
else
45+
bpf_map__set_max_entries(skel->maps.stacks, 1);
4446

4547
if (target__has_cpu(target))
4648
ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus);
@@ -146,6 +148,7 @@ int lock_contention_prepare(struct lock_contention *con)
146148
/* these don't work well if in the rodata section */
147149
skel->bss->stack_skip = con->stack_skip;
148150
skel->bss->aggr_mode = con->aggr_mode;
151+
skel->bss->needs_callstack = con->save_callstack;
149152

150153
lock_contention_bpf__attach(skel);
151154
return 0;
@@ -177,7 +180,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
177180

178181
if (con->aggr_mode == LOCK_AGGR_TASK) {
179182
struct contention_task_data task;
180-
int pid = key->aggr_key;
183+
int pid = key->pid;
181184
int task_fd = bpf_map__fd(skel->maps.task_data);
182185

183186
/* do not update idle comm which contains CPU number */
@@ -194,7 +197,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
194197
}
195198

196199
if (con->aggr_mode == LOCK_AGGR_ADDR) {
197-
sym = machine__find_kernel_symbol(machine, key->aggr_key, &kmap);
200+
sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap);
198201
if (sym)
199202
name = sym->name;
200203
return name;
@@ -255,20 +258,35 @@ int lock_contention_read(struct lock_contention *con)
255258

256259
prev_key = NULL;
257260
while (!bpf_map_get_next_key(fd, prev_key, &key)) {
258-
s32 stack_id;
261+
s64 ls_key;
259262
const char *name;
260263

261264
/* to handle errors in the loop body */
262265
err = -1;
263266

264267
bpf_map_lookup_elem(fd, &key, &data);
265-
266268
if (con->save_callstack) {
267-
stack_id = key.aggr_key;
268-
bpf_map_lookup_elem(stack, &stack_id, stack_trace);
269+
bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
270+
271+
if (!match_callstack_filter(machine, stack_trace))
272+
goto next;
269273
}
270274

271-
st = lock_stat_find(key.aggr_key);
275+
switch (con->aggr_mode) {
276+
case LOCK_AGGR_CALLER:
277+
ls_key = key.stack_id;
278+
break;
279+
case LOCK_AGGR_TASK:
280+
ls_key = key.pid;
281+
break;
282+
case LOCK_AGGR_ADDR:
283+
ls_key = key.lock_addr;
284+
break;
285+
default:
286+
goto next;
287+
}
288+
289+
st = lock_stat_find(ls_key);
272290
if (st != NULL) {
273291
st->wait_time_total += data.total_time;
274292
if (st->wait_time_max < data.max_time)
@@ -283,7 +301,7 @@ int lock_contention_read(struct lock_contention *con)
283301
}
284302

285303
name = lock_contention_get_name(con, &key, stack_trace);
286-
st = lock_stat_findnew(key.aggr_key, name, data.flags);
304+
st = lock_stat_findnew(ls_key, name, data.flags);
287305
if (st == NULL)
288306
break;
289307

@@ -295,8 +313,6 @@ int lock_contention_read(struct lock_contention *con)
295313
if (data.count)
296314
st->avg_wait_time = data.total_time / data.count;
297315

298-
st->flags = data.flags;
299-
300316
if (con->save_callstack) {
301317
st->callstack = memdup(stack_trace, stack_size);
302318
if (st->callstack == NULL)

tools/perf/util/bpf_skel/lock_contention.bpf.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ int has_cpu;
8282
int has_task;
8383
int has_type;
8484
int has_addr;
85+
int needs_callstack;
8586
int stack_skip;
8687

8788
/* determine the key of lock stat */
@@ -173,7 +174,7 @@ int contention_begin(u64 *ctx)
173174
pelem->lock = (__u64)ctx[0];
174175
pelem->flags = (__u32)ctx[1];
175176

176-
if (aggr_mode == LOCK_AGGR_CALLER) {
177+
if (needs_callstack) {
177178
pelem->stack_id = bpf_get_stackid(ctx, &stacks,
178179
BPF_F_FAST_STACK_CMP | stack_skip);
179180
if (pelem->stack_id < 0)
@@ -188,7 +189,7 @@ int contention_end(u64 *ctx)
188189
{
189190
__u32 pid;
190191
struct tstamp_data *pelem;
191-
struct contention_key key;
192+
struct contention_key key = {};
192193
struct contention_data *data;
193194
__u64 duration;
194195

@@ -204,14 +205,18 @@ int contention_end(u64 *ctx)
204205

205206
switch (aggr_mode) {
206207
case LOCK_AGGR_CALLER:
207-
key.aggr_key = pelem->stack_id;
208+
key.stack_id = pelem->stack_id;
208209
break;
209210
case LOCK_AGGR_TASK:
210-
key.aggr_key = pid;
211+
key.pid = pid;
211212
update_task_data(pid);
213+
if (needs_callstack)
214+
key.stack_id = pelem->stack_id;
212215
break;
213216
case LOCK_AGGR_ADDR:
214-
key.aggr_key = pelem->lock;
217+
key.lock_addr = pelem->lock;
218+
if (needs_callstack)
219+
key.stack_id = pelem->stack_id;
215220
break;
216221
default:
217222
/* should not happen */

tools/perf/util/bpf_skel/lock_data.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#define UTIL_BPF_SKEL_LOCK_DATA_H
55

66
struct contention_key {
7-
u64 aggr_key; /* can be stack_id, pid or lock addr */
7+
u32 stack_id;
8+
u32 pid;
9+
u64 lock_addr;
810
};
911

1012
#define TASK_COMM_LEN 16

tools/perf/util/lock-contention.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct lock_stat {
6868
struct lock_stat *lock_stat_find(u64 addr);
6969
struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags);
7070

71+
bool match_callstack_filter(struct machine *machine, u64 *callstack);
72+
7173
/*
7274
* struct lock_seq_stat:
7375
* Place to put on state of one lock sequence

0 commit comments

Comments
 (0)