xref: /linux/tools/perf/util/bpf_lock_contention.c (revision fd507d3e359c7e06d74321cd3d8a5ec8769d05a9)
1407b36f6SNamhyung Kim // SPDX-License-Identifier: GPL-2.0
2407b36f6SNamhyung Kim #include "util/debug.h"
36fda2405SNamhyung Kim #include "util/evlist.h"
4407b36f6SNamhyung Kim #include "util/machine.h"
5407b36f6SNamhyung Kim #include "util/map.h"
6407b36f6SNamhyung Kim #include "util/symbol.h"
76fda2405SNamhyung Kim #include "util/target.h"
86fda2405SNamhyung Kim #include "util/thread_map.h"
9407b36f6SNamhyung Kim #include "util/lock-contention.h"
10407b36f6SNamhyung Kim #include <linux/zalloc.h>
11a6eaf966SNamhyung Kim #include <linux/string.h>
12407b36f6SNamhyung Kim #include <bpf/bpf.h>
13407b36f6SNamhyung Kim 
14407b36f6SNamhyung Kim #include "bpf_skel/lock_contention.skel.h"
15*fd507d3eSNamhyung Kim #include "bpf_skel/lock_data.h"
16407b36f6SNamhyung Kim 
17407b36f6SNamhyung Kim static struct lock_contention_bpf *skel;
18407b36f6SNamhyung Kim 
19447ec4e5SNamhyung Kim int lock_contention_prepare(struct lock_contention *con)
20407b36f6SNamhyung Kim {
216fda2405SNamhyung Kim 	int i, fd;
226fda2405SNamhyung Kim 	int ncpus = 1, ntasks = 1;
23447ec4e5SNamhyung Kim 	struct evlist *evlist = con->evlist;
24447ec4e5SNamhyung Kim 	struct target *target = con->target;
256fda2405SNamhyung Kim 
26407b36f6SNamhyung Kim 	skel = lock_contention_bpf__open();
27407b36f6SNamhyung Kim 	if (!skel) {
28407b36f6SNamhyung Kim 		pr_err("Failed to open lock-contention BPF skeleton\n");
29407b36f6SNamhyung Kim 		return -1;
30407b36f6SNamhyung Kim 	}
31407b36f6SNamhyung Kim 
3296532a83SNamhyung Kim 	bpf_map__set_value_size(skel->maps.stacks, con->max_stack * sizeof(u64));
33ceb13bfcSNamhyung Kim 	bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries);
34ceb13bfcSNamhyung Kim 	bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries);
35c66a36afSNamhyung Kim 	bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries);
36ceb13bfcSNamhyung Kim 
376fda2405SNamhyung Kim 	if (target__has_cpu(target))
386fda2405SNamhyung Kim 		ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus);
396fda2405SNamhyung Kim 	if (target__has_task(target))
406fda2405SNamhyung Kim 		ntasks = perf_thread_map__nr(evlist->core.threads);
416fda2405SNamhyung Kim 
426fda2405SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus);
436fda2405SNamhyung Kim 	bpf_map__set_max_entries(skel->maps.task_filter, ntasks);
446fda2405SNamhyung Kim 
45407b36f6SNamhyung Kim 	if (lock_contention_bpf__load(skel) < 0) {
46407b36f6SNamhyung Kim 		pr_err("Failed to load lock-contention BPF skeleton\n");
47407b36f6SNamhyung Kim 		return -1;
48407b36f6SNamhyung Kim 	}
49407b36f6SNamhyung Kim 
506fda2405SNamhyung Kim 	if (target__has_cpu(target)) {
516fda2405SNamhyung Kim 		u32 cpu;
526fda2405SNamhyung Kim 		u8 val = 1;
536fda2405SNamhyung Kim 
546fda2405SNamhyung Kim 		skel->bss->has_cpu = 1;
556fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.cpu_filter);
566fda2405SNamhyung Kim 
576fda2405SNamhyung Kim 		for (i = 0; i < ncpus; i++) {
586fda2405SNamhyung Kim 			cpu = perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu;
596fda2405SNamhyung Kim 			bpf_map_update_elem(fd, &cpu, &val, BPF_ANY);
606fda2405SNamhyung Kim 		}
616fda2405SNamhyung Kim 	}
626fda2405SNamhyung Kim 
636fda2405SNamhyung Kim 	if (target__has_task(target)) {
646fda2405SNamhyung Kim 		u32 pid;
656fda2405SNamhyung Kim 		u8 val = 1;
666fda2405SNamhyung Kim 
676fda2405SNamhyung Kim 		skel->bss->has_task = 1;
686fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.task_filter);
696fda2405SNamhyung Kim 
706fda2405SNamhyung Kim 		for (i = 0; i < ntasks; i++) {
716fda2405SNamhyung Kim 			pid = perf_thread_map__pid(evlist->core.threads, i);
726fda2405SNamhyung Kim 			bpf_map_update_elem(fd, &pid, &val, BPF_ANY);
736fda2405SNamhyung Kim 		}
746fda2405SNamhyung Kim 	}
756fda2405SNamhyung Kim 
766fda2405SNamhyung Kim 	if (target__none(target) && evlist->workload.pid > 0) {
776fda2405SNamhyung Kim 		u32 pid = evlist->workload.pid;
786fda2405SNamhyung Kim 		u8 val = 1;
796fda2405SNamhyung Kim 
806fda2405SNamhyung Kim 		skel->bss->has_task = 1;
816fda2405SNamhyung Kim 		fd = bpf_map__fd(skel->maps.task_filter);
826fda2405SNamhyung Kim 		bpf_map_update_elem(fd, &pid, &val, BPF_ANY);
836fda2405SNamhyung Kim 	}
846fda2405SNamhyung Kim 
85c1da8dd5SNamhyung Kim 	skel->bss->stack_skip = con->stack_skip;
86c1da8dd5SNamhyung Kim 
87407b36f6SNamhyung Kim 	lock_contention_bpf__attach(skel);
88407b36f6SNamhyung Kim 	return 0;
89407b36f6SNamhyung Kim }
90407b36f6SNamhyung Kim 
91407b36f6SNamhyung Kim int lock_contention_start(void)
92407b36f6SNamhyung Kim {
93407b36f6SNamhyung Kim 	skel->bss->enabled = 1;
94407b36f6SNamhyung Kim 	return 0;
95407b36f6SNamhyung Kim }
96407b36f6SNamhyung Kim 
97407b36f6SNamhyung Kim int lock_contention_stop(void)
98407b36f6SNamhyung Kim {
99407b36f6SNamhyung Kim 	skel->bss->enabled = 0;
100407b36f6SNamhyung Kim 	return 0;
101407b36f6SNamhyung Kim }
102407b36f6SNamhyung Kim 
103447ec4e5SNamhyung Kim int lock_contention_read(struct lock_contention *con)
104407b36f6SNamhyung Kim {
1059e9c5f3cSNamhyung Kim 	int fd, stack, err = 0;
106*fd507d3eSNamhyung Kim 	struct contention_key *prev_key, key;
107*fd507d3eSNamhyung Kim 	struct contention_data data = {};
1089e9c5f3cSNamhyung Kim 	struct lock_stat *st = NULL;
109447ec4e5SNamhyung Kim 	struct machine *machine = con->machine;
1109e9c5f3cSNamhyung Kim 	u64 *stack_trace;
1119e9c5f3cSNamhyung Kim 	size_t stack_size = con->max_stack * sizeof(*stack_trace);
112407b36f6SNamhyung Kim 
113407b36f6SNamhyung Kim 	fd = bpf_map__fd(skel->maps.lock_stat);
114407b36f6SNamhyung Kim 	stack = bpf_map__fd(skel->maps.stacks);
115407b36f6SNamhyung Kim 
1166d499a6bSNamhyung Kim 	con->lost = skel->bss->lost;
1176d499a6bSNamhyung Kim 
1189e9c5f3cSNamhyung Kim 	stack_trace = zalloc(stack_size);
1199e9c5f3cSNamhyung Kim 	if (stack_trace == NULL)
1209e9c5f3cSNamhyung Kim 		return -1;
1219e9c5f3cSNamhyung Kim 
122*fd507d3eSNamhyung Kim 	prev_key = NULL;
123*fd507d3eSNamhyung Kim 	while (!bpf_map_get_next_key(fd, prev_key, &key)) {
124407b36f6SNamhyung Kim 		struct map *kmap;
125407b36f6SNamhyung Kim 		struct symbol *sym;
126c1da8dd5SNamhyung Kim 		int idx = 0;
127407b36f6SNamhyung Kim 
1289e9c5f3cSNamhyung Kim 		/* to handle errors in the loop body */
1299e9c5f3cSNamhyung Kim 		err = -1;
1309e9c5f3cSNamhyung Kim 
131407b36f6SNamhyung Kim 		bpf_map_lookup_elem(fd, &key, &data);
132407b36f6SNamhyung Kim 		st = zalloc(sizeof(*st));
133407b36f6SNamhyung Kim 		if (st == NULL)
1349e9c5f3cSNamhyung Kim 			break;
135407b36f6SNamhyung Kim 
136407b36f6SNamhyung Kim 		st->nr_contended = data.count;
137407b36f6SNamhyung Kim 		st->wait_time_total = data.total_time;
138407b36f6SNamhyung Kim 		st->wait_time_max = data.max_time;
139407b36f6SNamhyung Kim 		st->wait_time_min = data.min_time;
140407b36f6SNamhyung Kim 
141407b36f6SNamhyung Kim 		if (data.count)
142407b36f6SNamhyung Kim 			st->avg_wait_time = data.total_time / data.count;
143407b36f6SNamhyung Kim 
144407b36f6SNamhyung Kim 		st->flags = data.flags;
145407b36f6SNamhyung Kim 
146407b36f6SNamhyung Kim 		bpf_map_lookup_elem(stack, &key, stack_trace);
147407b36f6SNamhyung Kim 
148c1da8dd5SNamhyung Kim 		/* skip lock internal functions */
149cc2367eeSArnaldo Carvalho de Melo 		while (machine__is_lock_function(machine, stack_trace[idx]) &&
15096532a83SNamhyung Kim 		       idx < con->max_stack - 1)
151407b36f6SNamhyung Kim 			idx++;
152407b36f6SNamhyung Kim 
153407b36f6SNamhyung Kim 		st->addr = stack_trace[idx];
154407b36f6SNamhyung Kim 		sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
155407b36f6SNamhyung Kim 
156407b36f6SNamhyung Kim 		if (sym) {
157407b36f6SNamhyung Kim 			unsigned long offset;
158407b36f6SNamhyung Kim 			int ret = 0;
159407b36f6SNamhyung Kim 
160407b36f6SNamhyung Kim 			offset = kmap->map_ip(kmap, st->addr) - sym->start;
161407b36f6SNamhyung Kim 
162407b36f6SNamhyung Kim 			if (offset)
163407b36f6SNamhyung Kim 				ret = asprintf(&st->name, "%s+%#lx", sym->name, offset);
164407b36f6SNamhyung Kim 			else
165407b36f6SNamhyung Kim 				st->name = strdup(sym->name);
166407b36f6SNamhyung Kim 
167407b36f6SNamhyung Kim 			if (ret < 0 || st->name == NULL)
1689e9c5f3cSNamhyung Kim 				break;
169407b36f6SNamhyung Kim 		} else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) {
1709e9c5f3cSNamhyung Kim 			break;
171407b36f6SNamhyung Kim 		}
172407b36f6SNamhyung Kim 
173a6eaf966SNamhyung Kim 		if (verbose) {
1749e9c5f3cSNamhyung Kim 			st->callstack = memdup(stack_trace, stack_size);
1759e9c5f3cSNamhyung Kim 			if (st->callstack == NULL)
1769e9c5f3cSNamhyung Kim 				break;
177a6eaf966SNamhyung Kim 		}
178a6eaf966SNamhyung Kim 
179447ec4e5SNamhyung Kim 		hlist_add_head(&st->hash_entry, con->result);
180*fd507d3eSNamhyung Kim 		prev_key = &key;
1819e9c5f3cSNamhyung Kim 
1829e9c5f3cSNamhyung Kim 		/* we're fine now, reset the values */
1839e9c5f3cSNamhyung Kim 		st = NULL;
1849e9c5f3cSNamhyung Kim 		err = 0;
185407b36f6SNamhyung Kim 	}
186407b36f6SNamhyung Kim 
1879e9c5f3cSNamhyung Kim 	free(stack_trace);
1889e9c5f3cSNamhyung Kim 	if (st) {
1899e9c5f3cSNamhyung Kim 		free(st->name);
1909e9c5f3cSNamhyung Kim 		free(st);
1919e9c5f3cSNamhyung Kim 	}
1929e9c5f3cSNamhyung Kim 
1939e9c5f3cSNamhyung Kim 	return err;
194407b36f6SNamhyung Kim }
195407b36f6SNamhyung Kim 
196407b36f6SNamhyung Kim int lock_contention_finish(void)
197407b36f6SNamhyung Kim {
198407b36f6SNamhyung Kim 	if (skel) {
199407b36f6SNamhyung Kim 		skel->bss->enabled = 0;
200407b36f6SNamhyung Kim 		lock_contention_bpf__destroy(skel);
201407b36f6SNamhyung Kim 	}
202407b36f6SNamhyung Kim 
203407b36f6SNamhyung Kim 	return 0;
204407b36f6SNamhyung Kim }
205