Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf lock contention: Implement -t/--threads option for BPF

The BPF didn't show the per-thread stat properly. Use task's thread id (PID)
as a key instead of stack_id and add a task_data map to save task comm names.

$ sudo ./perf lock con -abt -E 5 sleep 1
contended total wait max wait avg wait pid comm

1 740.66 ms 740.66 ms 740.66 ms 1950 nv_queue
3 305.50 ms 298.19 ms 101.83 ms 1884 nvidia-modeset/
1 25.14 us 25.14 us 25.14 us 2725038 EventManager_De
12 23.09 us 9.30 us 1.92 us 0 swapper
1 20.18 us 20.18 us 20.18 us 2725033 EventManager_De

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Blake Jones <blakejones@google.com>
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: Song Liu <song@kernel.org>
Cc: bpf@vger.kernel.org
Link: https://lore.kernel.org/r/20221209190727.759804-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
eca949b2 fd507d3e

+78 -17
+3 -10
tools/perf/builtin-lock.c
··· 12 12 #include "util/target.h" 13 13 #include "util/callchain.h" 14 14 #include "util/lock-contention.h" 15 + #include "util/bpf_skel/lock_data.h" 15 16 16 17 #include <subcmd/pager.h> 17 18 #include <subcmd/parse-options.h> ··· 62 61 static int stack_skip = CONTENTION_STACK_SKIP; 63 62 static int print_nr_entries = INT_MAX / 2; 64 63 65 - static enum { 66 - LOCK_AGGR_ADDR, 67 - LOCK_AGGR_TASK, 68 - LOCK_AGGR_CALLER, 69 - } aggr_mode = LOCK_AGGR_ADDR; 64 + static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR; 70 65 71 66 static struct thread_stat *thread_stat_find(u32 tid) 72 67 { ··· 1616 1619 .map_nr_entries = bpf_map_entries, 1617 1620 .max_stack = max_stack_depth, 1618 1621 .stack_skip = stack_skip, 1622 + .aggr_mode = show_thread_stats ? LOCK_AGGR_TASK : LOCK_AGGR_CALLER, 1619 1623 }; 1620 1624 1621 1625 session = perf_session__new(use_bpf ? NULL : &data, &eops); ··· 1688 1690 1689 1691 if (select_key(true)) 1690 1692 goto out_delete; 1691 - 1692 - if (show_thread_stats) 1693 - aggr_mode = LOCK_AGGR_TASK; 1694 - else 1695 - aggr_mode = LOCK_AGGR_CALLER; 1696 1693 1697 1694 if (use_bpf) { 1698 1695 lock_contention_start();
+37 -3
tools/perf/util/bpf_lock_contention.c
··· 5 5 #include "util/map.h" 6 6 #include "util/symbol.h" 7 7 #include "util/target.h" 8 + #include "util/thread.h" 8 9 #include "util/thread_map.h" 9 10 #include "util/lock-contention.h" 10 11 #include <linux/zalloc.h> ··· 31 30 } 32 31 33 32 bpf_map__set_value_size(skel->maps.stacks, con->max_stack * sizeof(u64)); 34 - bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries); 35 33 bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries); 36 34 bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries); 35 + 36 + if (con->aggr_mode == LOCK_AGGR_TASK) { 37 + bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries); 38 + bpf_map__set_max_entries(skel->maps.stacks, 1); 39 + } else { 40 + bpf_map__set_max_entries(skel->maps.task_data, 1); 41 + bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries); 42 + } 37 43 38 44 if (target__has_cpu(target)) 39 45 ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); ··· 90 82 bpf_map_update_elem(fd, &pid, &val, BPF_ANY); 91 83 } 92 84 85 + /* these don't work well if in the rodata section */ 93 86 skel->bss->stack_skip = con->stack_skip; 87 + skel->bss->aggr_mode = con->aggr_mode; 94 88 95 89 lock_contention_bpf__attach(skel); 96 90 return 0; ··· 112 102 113 103 int lock_contention_read(struct lock_contention *con) 114 104 { 115 - int fd, stack, err = 0; 105 + int fd, stack, task_fd, err = 0; 116 106 struct contention_key *prev_key, key; 117 107 struct contention_data data = {}; 118 108 struct lock_stat *st = NULL; ··· 122 112 123 113 fd = bpf_map__fd(skel->maps.lock_stat); 124 114 stack = bpf_map__fd(skel->maps.stacks); 115 + task_fd = bpf_map__fd(skel->maps.task_data); 125 116 126 117 con->lost = skel->bss->lost; 127 118 128 119 stack_trace = zalloc(stack_size); 129 120 if (stack_trace == NULL) 130 121 return -1; 122 + 123 + if (con->aggr_mode == LOCK_AGGR_TASK) { 124 + struct thread *idle = __machine__findnew_thread(machine, 125 + /*pid=*/0, 126 + /*tid=*/0); 127 + thread__set_comm(idle, "swapper", /*timestamp=*/0); 128 + } 131 129 132 130 prev_key = NULL; 133 131 while (!bpf_map_get_next_key(fd, prev_key, &key)) { ··· 160 142 st->avg_wait_time = data.total_time / data.count; 161 143 162 144 st->flags = data.flags; 145 + 146 + if (con->aggr_mode == LOCK_AGGR_TASK) { 147 + struct contention_task_data task; 148 + struct thread *t; 149 + 150 + st->addr = key.stack_or_task_id; 151 + 152 + /* do not update idle comm which contains CPU number */ 153 + if (st->addr) { 154 + bpf_map_lookup_elem(task_fd, &key, &task); 155 + t = __machine__findnew_thread(machine, /*pid=*/-1, 156 + key.stack_or_task_id); 157 + thread__set_comm(t, task.comm, /*timestamp=*/0); 158 + } 159 + goto next; 160 + } 163 161 164 162 bpf_map_lookup_elem(stack, &key, stack_trace); 165 163 ··· 209 175 if (st->callstack == NULL) 210 176 break; 211 177 } 212 - 178 + next: 213 179 hlist_add_head(&st->hash_entry, con->result); 214 180 prev_key = &key; 215 181
+37 -4
tools/perf/util/bpf_skel/lock_contention.bpf.c
··· 44 44 struct { 45 45 __uint(type, BPF_MAP_TYPE_HASH); 46 46 __uint(key_size, sizeof(__u32)); 47 + __uint(value_size, sizeof(struct contention_task_data)); 48 + __uint(max_entries, MAX_ENTRIES); 49 + } task_data SEC(".maps"); 50 + 51 + struct { 52 + __uint(type, BPF_MAP_TYPE_HASH); 53 + __uint(key_size, sizeof(__u32)); 47 54 __uint(value_size, sizeof(__u8)); 48 55 __uint(max_entries, 1); 49 56 } cpu_filter SEC(".maps"); ··· 67 60 int has_cpu; 68 61 int has_task; 69 62 int stack_skip; 63 + 64 + /* determine the key of lock stat */ 65 + int aggr_mode; 70 66 71 67 /* error stat */ 72 68 int lost; ··· 95 85 } 96 86 97 87 return 1; 88 + } 89 + 90 + static inline void update_task_data(__u32 pid) 91 + { 92 + struct contention_task_data *p; 93 + 94 + p = bpf_map_lookup_elem(&task_data, &pid); 95 + if (p == NULL) { 96 + struct contention_task_data data; 97 + 98 + bpf_get_current_comm(data.comm, sizeof(data.comm)); 99 + bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST); 100 + } 98 101 } 99 102 100 103 SEC("tp_btf/contention_begin") ··· 138 115 pelem->timestamp = bpf_ktime_get_ns(); 139 116 pelem->lock = (__u64)ctx[0]; 140 117 pelem->flags = (__u32)ctx[1]; 141 - pelem->stack_id = bpf_get_stackid(ctx, &stacks, BPF_F_FAST_STACK_CMP | stack_skip); 142 118 143 - if (pelem->stack_id < 0) 144 - lost++; 119 + if (aggr_mode == LOCK_AGGR_CALLER) { 120 + pelem->stack_id = bpf_get_stackid(ctx, &stacks, 121 + BPF_F_FAST_STACK_CMP | stack_skip); 122 + if (pelem->stack_id < 0) 123 + lost++; 124 + } 125 + 145 126 return 0; 146 127 } 147 128 ··· 168 141 169 142 duration = bpf_ktime_get_ns() - pelem->timestamp; 170 143 171 - key.stack_or_task_id = pelem->stack_id; 144 + if (aggr_mode == LOCK_AGGR_CALLER) { 145 + key.stack_or_task_id = pelem->stack_id; 146 + } else { 147 + key.stack_or_task_id = pid; 148 + update_task_data(pid); 149 + } 150 + 172 151 data = bpf_map_lookup_elem(&lock_stat, &key); 173 152 if (!data) { 174 153 struct contention_data first = {
+1
tools/perf/util/lock-contention.h
··· 117 117 int lost; 118 118 int max_stack; 119 119 int stack_skip; 120 + int aggr_mode; 120 121 }; 121 122 122 123 #ifdef HAVE_BPF_SKEL