Commit | Line | Data |
---|---|---|
407b36f6 NK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "util/debug.h" | |
6fda2405 | 3 | #include "util/evlist.h" |
407b36f6 NK |
4 | #include "util/machine.h" |
5 | #include "util/map.h" | |
6 | #include "util/symbol.h" | |
6fda2405 NK |
7 | #include "util/target.h" |
8 | #include "util/thread_map.h" | |
407b36f6 NK |
9 | #include "util/lock-contention.h" |
10 | #include <linux/zalloc.h> | |
11 | #include <bpf/bpf.h> | |
12 | ||
13 | #include "bpf_skel/lock_contention.skel.h" | |
14 | ||
15 | static struct lock_contention_bpf *skel; | |
16 | ||
17 | /* should be same as bpf_skel/lock_contention.bpf.c */ | |
18 | struct lock_contention_key { | |
19 | u32 stack_id; | |
20 | }; | |
21 | ||
22 | struct lock_contention_data { | |
23 | u64 total_time; | |
24 | u64 min_time; | |
25 | u64 max_time; | |
26 | u32 count; | |
27 | u32 flags; | |
28 | }; | |
29 | ||
6fda2405 | 30 | int lock_contention_prepare(struct evlist *evlist, struct target *target) |
407b36f6 | 31 | { |
6fda2405 NK |
32 | int i, fd; |
33 | int ncpus = 1, ntasks = 1; | |
34 | ||
407b36f6 NK |
35 | skel = lock_contention_bpf__open(); |
36 | if (!skel) { | |
37 | pr_err("Failed to open lock-contention BPF skeleton\n"); | |
38 | return -1; | |
39 | } | |
40 | ||
6fda2405 NK |
41 | if (target__has_cpu(target)) |
42 | ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); | |
43 | if (target__has_task(target)) | |
44 | ntasks = perf_thread_map__nr(evlist->core.threads); | |
45 | ||
46 | bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); | |
47 | bpf_map__set_max_entries(skel->maps.task_filter, ntasks); | |
48 | ||
407b36f6 NK |
49 | if (lock_contention_bpf__load(skel) < 0) { |
50 | pr_err("Failed to load lock-contention BPF skeleton\n"); | |
51 | return -1; | |
52 | } | |
53 | ||
6fda2405 NK |
54 | if (target__has_cpu(target)) { |
55 | u32 cpu; | |
56 | u8 val = 1; | |
57 | ||
58 | skel->bss->has_cpu = 1; | |
59 | fd = bpf_map__fd(skel->maps.cpu_filter); | |
60 | ||
61 | for (i = 0; i < ncpus; i++) { | |
62 | cpu = perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu; | |
63 | bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); | |
64 | } | |
65 | } | |
66 | ||
67 | if (target__has_task(target)) { | |
68 | u32 pid; | |
69 | u8 val = 1; | |
70 | ||
71 | skel->bss->has_task = 1; | |
72 | fd = bpf_map__fd(skel->maps.task_filter); | |
73 | ||
74 | for (i = 0; i < ntasks; i++) { | |
75 | pid = perf_thread_map__pid(evlist->core.threads, i); | |
76 | bpf_map_update_elem(fd, &pid, &val, BPF_ANY); | |
77 | } | |
78 | } | |
79 | ||
80 | if (target__none(target) && evlist->workload.pid > 0) { | |
81 | u32 pid = evlist->workload.pid; | |
82 | u8 val = 1; | |
83 | ||
84 | skel->bss->has_task = 1; | |
85 | fd = bpf_map__fd(skel->maps.task_filter); | |
86 | bpf_map_update_elem(fd, &pid, &val, BPF_ANY); | |
87 | } | |
88 | ||
407b36f6 NK |
89 | lock_contention_bpf__attach(skel); |
90 | return 0; | |
91 | } | |
92 | ||
93 | int lock_contention_start(void) | |
94 | { | |
95 | skel->bss->enabled = 1; | |
96 | return 0; | |
97 | } | |
98 | ||
99 | int lock_contention_stop(void) | |
100 | { | |
101 | skel->bss->enabled = 0; | |
102 | return 0; | |
103 | } | |
104 | ||
105 | int lock_contention_read(struct machine *machine, struct hlist_head *head) | |
106 | { | |
107 | int fd, stack; | |
108 | u32 prev_key, key; | |
109 | struct lock_contention_data data; | |
110 | struct lock_stat *st; | |
111 | u64 stack_trace[CONTENTION_STACK_DEPTH]; | |
112 | ||
113 | fd = bpf_map__fd(skel->maps.lock_stat); | |
114 | stack = bpf_map__fd(skel->maps.stacks); | |
115 | ||
116 | prev_key = 0; | |
117 | while (!bpf_map_get_next_key(fd, &prev_key, &key)) { | |
118 | struct map *kmap; | |
119 | struct symbol *sym; | |
120 | int idx; | |
121 | ||
122 | bpf_map_lookup_elem(fd, &key, &data); | |
123 | st = zalloc(sizeof(*st)); | |
124 | if (st == NULL) | |
125 | return -1; | |
126 | ||
127 | st->nr_contended = data.count; | |
128 | st->wait_time_total = data.total_time; | |
129 | st->wait_time_max = data.max_time; | |
130 | st->wait_time_min = data.min_time; | |
131 | ||
132 | if (data.count) | |
133 | st->avg_wait_time = data.total_time / data.count; | |
134 | ||
135 | st->flags = data.flags; | |
136 | ||
137 | bpf_map_lookup_elem(stack, &key, stack_trace); | |
138 | ||
139 | /* skip BPF + lock internal functions */ | |
140 | idx = CONTENTION_STACK_SKIP; | |
141 | while (is_lock_function(machine, stack_trace[idx]) && | |
142 | idx < CONTENTION_STACK_DEPTH - 1) | |
143 | idx++; | |
144 | ||
145 | st->addr = stack_trace[idx]; | |
146 | sym = machine__find_kernel_symbol(machine, st->addr, &kmap); | |
147 | ||
148 | if (sym) { | |
149 | unsigned long offset; | |
150 | int ret = 0; | |
151 | ||
152 | offset = kmap->map_ip(kmap, st->addr) - sym->start; | |
153 | ||
154 | if (offset) | |
155 | ret = asprintf(&st->name, "%s+%#lx", sym->name, offset); | |
156 | else | |
157 | st->name = strdup(sym->name); | |
158 | ||
159 | if (ret < 0 || st->name == NULL) | |
160 | return -1; | |
161 | } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) { | |
162 | free(st); | |
163 | return -1; | |
164 | } | |
165 | ||
166 | hlist_add_head(&st->hash_entry, head); | |
167 | prev_key = key; | |
168 | } | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | int lock_contention_finish(void) | |
174 | { | |
175 | if (skel) { | |
176 | skel->bss->enabled = 0; | |
177 | lock_contention_bpf__destroy(skel); | |
178 | } | |
179 | ||
180 | return 0; | |
181 | } |