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