xref: /openbmc/linux/tools/perf/util/dlfilter.c (revision 7a48b58e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * dlfilter.c: Interface to perf script --dlfilter shared object
4  * Copyright (c) 2021, Intel Corporation.
5  */
6 #include <dlfcn.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <dirent.h>
10 #include <subcmd/exec-cmd.h>
11 #include <linux/zalloc.h>
12 #include <linux/build_bug.h>
13 #include <linux/kernel.h>
14 #include <linux/string.h>
15 
16 #include "debug.h"
17 #include "event.h"
18 #include "evsel.h"
19 #include "dso.h"
20 #include "map.h"
21 #include "thread.h"
22 #include "trace-event.h"
23 #include "symbol.h"
24 #include "srcline.h"
25 #include "dlfilter.h"
26 #include "../include/perf/perf_dlfilter.h"
27 
al_to_d_al(struct addr_location * al,struct perf_dlfilter_al * d_al)28 static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
29 {
30 	struct symbol *sym = al->sym;
31 
32 	d_al->size = sizeof(*d_al);
33 	if (al->map) {
34 		struct dso *dso = map__dso(al->map);
35 
36 		if (symbol_conf.show_kernel_path && dso->long_name)
37 			d_al->dso = dso->long_name;
38 		else
39 			d_al->dso = dso->name;
40 		d_al->is_64_bit = dso->is_64_bit;
41 		d_al->buildid_size = dso->bid.size;
42 		d_al->buildid = dso->bid.data;
43 	} else {
44 		d_al->dso = NULL;
45 		d_al->is_64_bit = 0;
46 		d_al->buildid_size = 0;
47 		d_al->buildid = NULL;
48 	}
49 	if (sym) {
50 		d_al->sym = sym->name;
51 		d_al->sym_start = sym->start;
52 		d_al->sym_end = sym->end;
53 		if (al->addr < sym->end)
54 			d_al->symoff = al->addr - sym->start;
55 		else
56 			d_al->symoff = al->addr - map__start(al->map) - sym->start;
57 		d_al->sym_binding = sym->binding;
58 	} else {
59 		d_al->sym = NULL;
60 		d_al->sym_start = 0;
61 		d_al->sym_end = 0;
62 		d_al->symoff = 0;
63 		d_al->sym_binding = 0;
64 	}
65 	d_al->addr = al->addr;
66 	d_al->comm = NULL;
67 	d_al->filtered = 0;
68 	d_al->priv = NULL;
69 }
70 
get_al(struct dlfilter * d)71 static struct addr_location *get_al(struct dlfilter *d)
72 {
73 	struct addr_location *al = d->al;
74 
75 	if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
76 		return NULL;
77 	return al;
78 }
79 
get_thread(struct dlfilter * d)80 static struct thread *get_thread(struct dlfilter *d)
81 {
82 	struct addr_location *al = get_al(d);
83 
84 	return al ? al->thread : NULL;
85 }
86 
dlfilter__resolve_ip(void * ctx)87 static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
88 {
89 	struct dlfilter *d = (struct dlfilter *)ctx;
90 	struct perf_dlfilter_al *d_al = d->d_ip_al;
91 	struct addr_location *al;
92 
93 	if (!d->ctx_valid)
94 		return NULL;
95 
96 	/* 'size' is also used to indicate already initialized */
97 	if (d_al->size)
98 		return d_al;
99 
100 	al = get_al(d);
101 	if (!al)
102 		return NULL;
103 
104 	al_to_d_al(al, d_al);
105 
106 	d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip);
107 	d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1";
108 	d_al->filtered = al->filtered;
109 
110 	return d_al;
111 }
112 
dlfilter__resolve_addr(void * ctx)113 static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
114 {
115 	struct dlfilter *d = (struct dlfilter *)ctx;
116 	struct perf_dlfilter_al *d_addr_al = d->d_addr_al;
117 	struct addr_location *addr_al = d->addr_al;
118 
119 	if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
120 		return NULL;
121 
122 	/* 'size' is also used to indicate already initialized */
123 	if (d_addr_al->size)
124 		return d_addr_al;
125 
126 	if (!addr_al->thread) {
127 		struct thread *thread = get_thread(d);
128 
129 		if (!thread)
130 			return NULL;
131 		thread__resolve(thread, addr_al, d->sample);
132 	}
133 
134 	al_to_d_al(addr_al, d_addr_al);
135 
136 	d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
137 
138 	return d_addr_al;
139 }
140 
dlfilter__args(void * ctx,int * dlargc)141 static char **dlfilter__args(void *ctx, int *dlargc)
142 {
143 	struct dlfilter *d = (struct dlfilter *)ctx;
144 
145 	if (dlargc)
146 		*dlargc = 0;
147 	else
148 		return NULL;
149 
150 	if (!d->ctx_valid && !d->in_start && !d->in_stop)
151 		return NULL;
152 
153 	*dlargc = d->dlargc;
154 	return d->dlargv;
155 }
156 
has_priv(struct perf_dlfilter_al * d_al_p)157 static bool has_priv(struct perf_dlfilter_al *d_al_p)
158 {
159 	return d_al_p->size >= offsetof(struct perf_dlfilter_al, priv) + sizeof(d_al_p->priv);
160 }
161 
dlfilter__resolve_address(void * ctx,__u64 address,struct perf_dlfilter_al * d_al_p)162 static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
163 {
164 	struct dlfilter *d = (struct dlfilter *)ctx;
165 	struct perf_dlfilter_al d_al;
166 	struct addr_location al;
167 	struct thread *thread;
168 	__u32 sz;
169 
170 	if (!d->ctx_valid || !d_al_p)
171 		return -1;
172 
173 	thread = get_thread(d);
174 	if (!thread)
175 		return -1;
176 
177 	addr_location__init(&al);
178 	thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
179 
180 	al_to_d_al(&al, &d_al);
181 
182 	d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
183 
184 	sz = d_al_p->size;
185 	memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
186 	d_al_p->size = sz;
187 
188 	if (has_priv(d_al_p))
189 		d_al_p->priv = memdup(&al, sizeof(al));
190 	else /* Avoid leak for v0 API */
191 		addr_location__exit(&al);
192 
193 	return 0;
194 }
195 
dlfilter__al_cleanup(void * ctx __maybe_unused,struct perf_dlfilter_al * d_al_p)196 static void dlfilter__al_cleanup(void *ctx __maybe_unused, struct perf_dlfilter_al *d_al_p)
197 {
198 	struct addr_location *al;
199 
200 	/* Ensure backward compatibility */
201 	if (!has_priv(d_al_p) || !d_al_p->priv)
202 		return;
203 
204 	al = d_al_p->priv;
205 
206 	d_al_p->priv = NULL;
207 
208 	addr_location__exit(al);
209 
210 	free(al);
211 }
212 
dlfilter__insn(void * ctx,__u32 * len)213 static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
214 {
215 	struct dlfilter *d = (struct dlfilter *)ctx;
216 
217 	if (!len)
218 		return NULL;
219 
220 	*len = 0;
221 
222 	if (!d->ctx_valid)
223 		return NULL;
224 
225 	if (d->sample->ip && !d->sample->insn_len) {
226 		struct addr_location *al = d->al;
227 
228 		if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
229 			return NULL;
230 
231 		if (thread__maps(al->thread)) {
232 			struct machine *machine = maps__machine(thread__maps(al->thread));
233 
234 			if (machine)
235 				script_fetch_insn(d->sample, al->thread, machine);
236 		}
237 	}
238 
239 	if (!d->sample->insn_len)
240 		return NULL;
241 
242 	*len = d->sample->insn_len;
243 
244 	return (__u8 *)d->sample->insn;
245 }
246 
dlfilter__srcline(void * ctx,__u32 * line_no)247 static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
248 {
249 	struct dlfilter *d = (struct dlfilter *)ctx;
250 	struct addr_location *al;
251 	unsigned int line = 0;
252 	char *srcfile = NULL;
253 	struct map *map;
254 	struct dso *dso;
255 	u64 addr;
256 
257 	if (!d->ctx_valid || !line_no)
258 		return NULL;
259 
260 	al = get_al(d);
261 	if (!al)
262 		return NULL;
263 
264 	map = al->map;
265 	addr = al->addr;
266 	dso = map ? map__dso(map) : NULL;
267 
268 	if (dso)
269 		srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
270 
271 	*line_no = line;
272 	return srcfile;
273 }
274 
dlfilter__attr(void * ctx)275 static struct perf_event_attr *dlfilter__attr(void *ctx)
276 {
277 	struct dlfilter *d = (struct dlfilter *)ctx;
278 
279 	if (!d->ctx_valid)
280 		return NULL;
281 
282 	return &d->evsel->core.attr;
283 }
284 
code_read(__u64 ip,struct map * map,struct machine * machine,void * buf,__u32 len)285 static __s32 code_read(__u64 ip, struct map *map, struct machine *machine, void *buf, __u32 len)
286 {
287 	u64 offset = map__map_ip(map, ip);
288 
289 	if (ip + len >= map__end(map))
290 		len = map__end(map) - ip;
291 
292 	return dso__data_read_offset(map__dso(map), machine, offset, buf, len);
293 }
294 
dlfilter__object_code(void * ctx,__u64 ip,void * buf,__u32 len)295 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
296 {
297 	struct dlfilter *d = (struct dlfilter *)ctx;
298 	struct addr_location *al;
299 	struct addr_location a;
300 	__s32 ret;
301 
302 	if (!d->ctx_valid)
303 		return -1;
304 
305 	al = get_al(d);
306 	if (!al)
307 		return -1;
308 
309 	if (al->map && ip >= map__start(al->map) && ip < map__end(al->map) &&
310 	    machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
311 		return code_read(ip, al->map, d->machine, buf, len);
312 
313 	addr_location__init(&a);
314 
315 	thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
316 	ret = a.map ? code_read(ip, a.map, d->machine, buf, len) : -1;
317 
318 	addr_location__exit(&a);
319 
320 	return ret;
321 }
322 
323 static const struct perf_dlfilter_fns perf_dlfilter_fns = {
324 	.resolve_ip      = dlfilter__resolve_ip,
325 	.resolve_addr    = dlfilter__resolve_addr,
326 	.args            = dlfilter__args,
327 	.resolve_address = dlfilter__resolve_address,
328 	.al_cleanup      = dlfilter__al_cleanup,
329 	.insn            = dlfilter__insn,
330 	.srcline         = dlfilter__srcline,
331 	.attr            = dlfilter__attr,
332 	.object_code     = dlfilter__object_code,
333 };
334 
find_dlfilter(const char * file)335 static char *find_dlfilter(const char *file)
336 {
337 	char path[PATH_MAX];
338 	char *exec_path;
339 
340 	if (strchr(file, '/'))
341 		goto out;
342 
343 	if (!access(file, R_OK)) {
344 		/*
345 		 * Prepend "./" so that dlopen will find the file in the
346 		 * current directory.
347 		 */
348 		snprintf(path, sizeof(path), "./%s", file);
349 		file = path;
350 		goto out;
351 	}
352 
353 	exec_path = get_argv_exec_path();
354 	if (!exec_path)
355 		goto out;
356 	snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
357 	free(exec_path);
358 	if (!access(path, R_OK))
359 		file = path;
360 out:
361 	return strdup(file);
362 }
363 
364 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
365 
dlfilter__init(struct dlfilter * d,const char * file,int dlargc,char ** dlargv)366 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
367 {
368 	CHECK_FLAG(BRANCH);
369 	CHECK_FLAG(CALL);
370 	CHECK_FLAG(RETURN);
371 	CHECK_FLAG(CONDITIONAL);
372 	CHECK_FLAG(SYSCALLRET);
373 	CHECK_FLAG(ASYNC);
374 	CHECK_FLAG(INTERRUPT);
375 	CHECK_FLAG(TX_ABORT);
376 	CHECK_FLAG(TRACE_BEGIN);
377 	CHECK_FLAG(TRACE_END);
378 	CHECK_FLAG(IN_TX);
379 	CHECK_FLAG(VMENTRY);
380 	CHECK_FLAG(VMEXIT);
381 
382 	memset(d, 0, sizeof(*d));
383 	d->file = find_dlfilter(file);
384 	if (!d->file)
385 		return -1;
386 	d->dlargc = dlargc;
387 	d->dlargv = dlargv;
388 	return 0;
389 }
390 
dlfilter__exit(struct dlfilter * d)391 static void dlfilter__exit(struct dlfilter *d)
392 {
393 	zfree(&d->file);
394 }
395 
dlfilter__open(struct dlfilter * d)396 static int dlfilter__open(struct dlfilter *d)
397 {
398 	d->handle = dlopen(d->file, RTLD_NOW);
399 	if (!d->handle) {
400 		pr_err("dlopen failed for: '%s'\n", d->file);
401 		return -1;
402 	}
403 	d->start = dlsym(d->handle, "start");
404 	d->filter_event = dlsym(d->handle, "filter_event");
405 	d->filter_event_early = dlsym(d->handle, "filter_event_early");
406 	d->stop = dlsym(d->handle, "stop");
407 	d->fns = dlsym(d->handle, "perf_dlfilter_fns");
408 	if (d->fns)
409 		memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
410 	return 0;
411 }
412 
dlfilter__close(struct dlfilter * d)413 static int dlfilter__close(struct dlfilter *d)
414 {
415 	return dlclose(d->handle);
416 }
417 
dlfilter__new(const char * file,int dlargc,char ** dlargv)418 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
419 {
420 	struct dlfilter *d = malloc(sizeof(*d));
421 
422 	if (!d)
423 		return NULL;
424 
425 	if (dlfilter__init(d, file, dlargc, dlargv))
426 		goto err_free;
427 
428 	if (dlfilter__open(d))
429 		goto err_exit;
430 
431 	return d;
432 
433 err_exit:
434 	dlfilter__exit(d);
435 err_free:
436 	free(d);
437 	return NULL;
438 }
439 
dlfilter__free(struct dlfilter * d)440 static void dlfilter__free(struct dlfilter *d)
441 {
442 	if (d) {
443 		dlfilter__exit(d);
444 		free(d);
445 	}
446 }
447 
dlfilter__start(struct dlfilter * d,struct perf_session * session)448 int dlfilter__start(struct dlfilter *d, struct perf_session *session)
449 {
450 	if (d) {
451 		d->session = session;
452 		if (d->start) {
453 			int ret;
454 
455 			d->in_start = true;
456 			ret = d->start(&d->data, d);
457 			d->in_start = false;
458 			return ret;
459 		}
460 	}
461 	return 0;
462 }
463 
dlfilter__stop(struct dlfilter * d)464 static int dlfilter__stop(struct dlfilter *d)
465 {
466 	if (d && d->stop) {
467 		int ret;
468 
469 		d->in_stop = true;
470 		ret = d->stop(d->data, d);
471 		d->in_stop = false;
472 		return ret;
473 	}
474 	return 0;
475 }
476 
dlfilter__cleanup(struct dlfilter * d)477 void dlfilter__cleanup(struct dlfilter *d)
478 {
479 	if (d) {
480 		dlfilter__stop(d);
481 		dlfilter__close(d);
482 		dlfilter__free(d);
483 	}
484 }
485 
486 #define ASSIGN(x) d_sample.x = sample->x
487 
dlfilter__do_filter_event(struct dlfilter * d,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine,struct addr_location * al,struct addr_location * addr_al,bool early)488 int dlfilter__do_filter_event(struct dlfilter *d,
489 			      union perf_event *event,
490 			      struct perf_sample *sample,
491 			      struct evsel *evsel,
492 			      struct machine *machine,
493 			      struct addr_location *al,
494 			      struct addr_location *addr_al,
495 			      bool early)
496 {
497 	struct perf_dlfilter_sample d_sample;
498 	struct perf_dlfilter_al d_ip_al;
499 	struct perf_dlfilter_al d_addr_al;
500 	int ret;
501 
502 	d->event       = event;
503 	d->sample      = sample;
504 	d->evsel       = evsel;
505 	d->machine     = machine;
506 	d->al          = al;
507 	d->addr_al     = addr_al;
508 	d->d_sample    = &d_sample;
509 	d->d_ip_al     = &d_ip_al;
510 	d->d_addr_al   = &d_addr_al;
511 
512 	d_sample.size  = sizeof(d_sample);
513 	d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
514 	d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
515 
516 	ASSIGN(ip);
517 	ASSIGN(pid);
518 	ASSIGN(tid);
519 	ASSIGN(time);
520 	ASSIGN(addr);
521 	ASSIGN(id);
522 	ASSIGN(stream_id);
523 	ASSIGN(period);
524 	ASSIGN(weight);
525 	ASSIGN(ins_lat);
526 	ASSIGN(p_stage_cyc);
527 	ASSIGN(transaction);
528 	ASSIGN(insn_cnt);
529 	ASSIGN(cyc_cnt);
530 	ASSIGN(cpu);
531 	ASSIGN(flags);
532 	ASSIGN(data_src);
533 	ASSIGN(phys_addr);
534 	ASSIGN(data_page_size);
535 	ASSIGN(code_page_size);
536 	ASSIGN(cgroup);
537 	ASSIGN(cpumode);
538 	ASSIGN(misc);
539 	ASSIGN(raw_size);
540 	ASSIGN(raw_data);
541 	ASSIGN(machine_pid);
542 	ASSIGN(vcpu);
543 
544 	if (sample->branch_stack) {
545 		d_sample.brstack_nr = sample->branch_stack->nr;
546 		d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
547 	} else {
548 		d_sample.brstack_nr = 0;
549 		d_sample.brstack = NULL;
550 	}
551 
552 	if (sample->callchain) {
553 		d_sample.raw_callchain_nr = sample->callchain->nr;
554 		d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
555 	} else {
556 		d_sample.raw_callchain_nr = 0;
557 		d_sample.raw_callchain = NULL;
558 	}
559 
560 	d_sample.addr_correlates_sym =
561 		(evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
562 		sample_addr_correlates_sym(&evsel->core.attr);
563 
564 	d_sample.event = evsel__name(evsel);
565 
566 	d->ctx_valid = true;
567 
568 	if (early)
569 		ret = d->filter_event_early(d->data, &d_sample, d);
570 	else
571 		ret = d->filter_event(d->data, &d_sample, d);
572 
573 	d->ctx_valid = false;
574 
575 	return ret;
576 }
577 
get_filter_desc(const char * dirname,const char * name,char ** desc,char ** long_desc)578 bool get_filter_desc(const char *dirname, const char *name, char **desc,
579 		     char **long_desc)
580 {
581 	char path[PATH_MAX];
582 	void *handle;
583 	const char *(*desc_fn)(const char **long_description);
584 
585 	snprintf(path, sizeof(path), "%s/%s", dirname, name);
586 	handle = dlopen(path, RTLD_NOW);
587 	if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
588 		return false;
589 	desc_fn = dlsym(handle, "filter_description");
590 	if (desc_fn) {
591 		const char *dsc;
592 		const char *long_dsc;
593 
594 		dsc = desc_fn(&long_dsc);
595 		if (dsc)
596 			*desc = strdup(dsc);
597 		if (long_dsc)
598 			*long_desc = strdup(long_dsc);
599 	}
600 	dlclose(handle);
601 	return true;
602 }
603 
list_filters(const char * dirname)604 static void list_filters(const char *dirname)
605 {
606 	struct dirent *entry;
607 	DIR *dir;
608 
609 	dir = opendir(dirname);
610 	if (!dir)
611 		return;
612 
613 	while ((entry = readdir(dir)) != NULL)
614 	{
615 		size_t n = strlen(entry->d_name);
616 		char *long_desc = NULL;
617 		char *desc = NULL;
618 
619 		if (entry->d_type == DT_DIR || n < 4 ||
620 		    strcmp(".so", entry->d_name + n - 3))
621 			continue;
622 		if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
623 			continue;
624 		printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
625 		if (verbose > 0) {
626 			char *p = long_desc;
627 			char *line;
628 
629 			while ((line = strsep(&p, "\n")) != NULL)
630 				printf("%39s%s\n", "", line);
631 		}
632 		free(long_desc);
633 		free(desc);
634 	}
635 
636 	closedir(dir);
637 }
638 
list_available_dlfilters(const struct option * opt __maybe_unused,const char * s __maybe_unused,int unset __maybe_unused)639 int list_available_dlfilters(const struct option *opt __maybe_unused,
640 			     const char *s __maybe_unused,
641 			     int unset __maybe_unused)
642 {
643 	char path[PATH_MAX];
644 	char *exec_path;
645 
646 	printf("List of available dlfilters:\n");
647 
648 	list_filters(".");
649 
650 	exec_path = get_argv_exec_path();
651 	if (!exec_path)
652 		goto out;
653 	snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
654 
655 	list_filters(path);
656 
657 	free(exec_path);
658 out:
659 	exit(0);
660 }
661