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