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 /*native_arch=*/true);
239 }
240 }
241
242 if (!d->sample->insn_len)
243 return NULL;
244
245 *len = d->sample->insn_len;
246
247 return (__u8 *)d->sample->insn;
248 }
249
dlfilter__srcline(void * ctx,__u32 * line_no)250 static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
251 {
252 struct dlfilter *d = (struct dlfilter *)ctx;
253 struct addr_location *al;
254 unsigned int line = 0;
255 char *srcfile = NULL;
256 struct map *map;
257 struct dso *dso;
258 u64 addr;
259
260 if (!d->ctx_valid || !line_no)
261 return NULL;
262
263 al = get_al(d);
264 if (!al)
265 return NULL;
266
267 map = al->map;
268 addr = al->addr;
269 dso = map ? map__dso(map) : NULL;
270
271 if (dso)
272 srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
273
274 *line_no = line;
275 return srcfile;
276 }
277
dlfilter__attr(void * ctx)278 static struct perf_event_attr *dlfilter__attr(void *ctx)
279 {
280 struct dlfilter *d = (struct dlfilter *)ctx;
281
282 if (!d->ctx_valid)
283 return NULL;
284
285 return &d->evsel->core.attr;
286 }
287
code_read(__u64 ip,struct map * map,struct machine * machine,void * buf,__u32 len)288 static __s32 code_read(__u64 ip, struct map *map, struct machine *machine, void *buf, __u32 len)
289 {
290 u64 offset = map__map_ip(map, ip);
291
292 if (ip + len >= map__end(map))
293 len = map__end(map) - ip;
294
295 return dso__data_read_offset(map__dso(map), machine, offset, buf, len);
296 }
297
dlfilter__object_code(void * ctx,__u64 ip,void * buf,__u32 len)298 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
299 {
300 struct dlfilter *d = (struct dlfilter *)ctx;
301 struct addr_location *al;
302 struct addr_location a;
303 __s32 ret;
304
305 if (!d->ctx_valid)
306 return -1;
307
308 al = get_al(d);
309 if (!al)
310 return -1;
311
312 if (al->map && ip >= map__start(al->map) && ip < map__end(al->map) &&
313 machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
314 return code_read(ip, al->map, d->machine, buf, len);
315
316 addr_location__init(&a);
317
318 thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
319 ret = a.map ? code_read(ip, a.map, d->machine, buf, len) : -1;
320
321 addr_location__exit(&a);
322
323 return ret;
324 }
325
326 static const struct perf_dlfilter_fns perf_dlfilter_fns = {
327 .resolve_ip = dlfilter__resolve_ip,
328 .resolve_addr = dlfilter__resolve_addr,
329 .args = dlfilter__args,
330 .resolve_address = dlfilter__resolve_address,
331 .al_cleanup = dlfilter__al_cleanup,
332 .insn = dlfilter__insn,
333 .srcline = dlfilter__srcline,
334 .attr = dlfilter__attr,
335 .object_code = dlfilter__object_code,
336 };
337
find_dlfilter(const char * file)338 static char *find_dlfilter(const char *file)
339 {
340 char path[PATH_MAX];
341 char *exec_path;
342
343 if (strchr(file, '/'))
344 goto out;
345
346 if (!access(file, R_OK)) {
347 /*
348 * Prepend "./" so that dlopen will find the file in the
349 * current directory.
350 */
351 snprintf(path, sizeof(path), "./%s", file);
352 file = path;
353 goto out;
354 }
355
356 exec_path = get_argv_exec_path();
357 if (!exec_path)
358 goto out;
359 snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
360 free(exec_path);
361 if (!access(path, R_OK))
362 file = path;
363 out:
364 return strdup(file);
365 }
366
367 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
368
dlfilter__init(struct dlfilter * d,const char * file,int dlargc,char ** dlargv)369 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
370 {
371 CHECK_FLAG(BRANCH);
372 CHECK_FLAG(CALL);
373 CHECK_FLAG(RETURN);
374 CHECK_FLAG(CONDITIONAL);
375 CHECK_FLAG(SYSCALLRET);
376 CHECK_FLAG(ASYNC);
377 CHECK_FLAG(INTERRUPT);
378 CHECK_FLAG(TX_ABORT);
379 CHECK_FLAG(TRACE_BEGIN);
380 CHECK_FLAG(TRACE_END);
381 CHECK_FLAG(IN_TX);
382 CHECK_FLAG(VMENTRY);
383 CHECK_FLAG(VMEXIT);
384
385 memset(d, 0, sizeof(*d));
386 d->file = find_dlfilter(file);
387 if (!d->file)
388 return -1;
389 d->dlargc = dlargc;
390 d->dlargv = dlargv;
391 return 0;
392 }
393
dlfilter__exit(struct dlfilter * d)394 static void dlfilter__exit(struct dlfilter *d)
395 {
396 zfree(&d->file);
397 }
398
dlfilter__open(struct dlfilter * d)399 static int dlfilter__open(struct dlfilter *d)
400 {
401 d->handle = dlopen(d->file, RTLD_NOW);
402 if (!d->handle) {
403 pr_err("dlopen failed for: '%s'\n", d->file);
404 return -1;
405 }
406 d->start = dlsym(d->handle, "start");
407 d->filter_event = dlsym(d->handle, "filter_event");
408 d->filter_event_early = dlsym(d->handle, "filter_event_early");
409 d->stop = dlsym(d->handle, "stop");
410 d->fns = dlsym(d->handle, "perf_dlfilter_fns");
411 if (d->fns)
412 memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
413 return 0;
414 }
415
dlfilter__close(struct dlfilter * d)416 static int dlfilter__close(struct dlfilter *d)
417 {
418 return dlclose(d->handle);
419 }
420
dlfilter__new(const char * file,int dlargc,char ** dlargv)421 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
422 {
423 struct dlfilter *d = malloc(sizeof(*d));
424
425 if (!d)
426 return NULL;
427
428 if (dlfilter__init(d, file, dlargc, dlargv))
429 goto err_free;
430
431 if (dlfilter__open(d))
432 goto err_exit;
433
434 return d;
435
436 err_exit:
437 dlfilter__exit(d);
438 err_free:
439 free(d);
440 return NULL;
441 }
442
dlfilter__free(struct dlfilter * d)443 static void dlfilter__free(struct dlfilter *d)
444 {
445 if (d) {
446 dlfilter__exit(d);
447 free(d);
448 }
449 }
450
dlfilter__start(struct dlfilter * d,struct perf_session * session)451 int dlfilter__start(struct dlfilter *d, struct perf_session *session)
452 {
453 if (d) {
454 d->session = session;
455 if (d->start) {
456 int ret;
457
458 d->in_start = true;
459 ret = d->start(&d->data, d);
460 d->in_start = false;
461 return ret;
462 }
463 }
464 return 0;
465 }
466
dlfilter__stop(struct dlfilter * d)467 static int dlfilter__stop(struct dlfilter *d)
468 {
469 if (d && d->stop) {
470 int ret;
471
472 d->in_stop = true;
473 ret = d->stop(d->data, d);
474 d->in_stop = false;
475 return ret;
476 }
477 return 0;
478 }
479
dlfilter__cleanup(struct dlfilter * d)480 void dlfilter__cleanup(struct dlfilter *d)
481 {
482 if (d) {
483 dlfilter__stop(d);
484 dlfilter__close(d);
485 dlfilter__free(d);
486 }
487 }
488
489 #define ASSIGN(x) d_sample.x = sample->x
490
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)491 int dlfilter__do_filter_event(struct dlfilter *d,
492 union perf_event *event,
493 struct perf_sample *sample,
494 struct evsel *evsel,
495 struct machine *machine,
496 struct addr_location *al,
497 struct addr_location *addr_al,
498 bool early)
499 {
500 struct perf_dlfilter_sample d_sample;
501 struct perf_dlfilter_al d_ip_al;
502 struct perf_dlfilter_al d_addr_al;
503 int ret;
504
505 d->event = event;
506 d->sample = sample;
507 d->evsel = evsel;
508 d->machine = machine;
509 d->al = al;
510 d->addr_al = addr_al;
511 d->d_sample = &d_sample;
512 d->d_ip_al = &d_ip_al;
513 d->d_addr_al = &d_addr_al;
514
515 d_sample.size = sizeof(d_sample);
516 d_ip_al.size = 0; /* To indicate d_ip_al is not initialized */
517 d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
518
519 ASSIGN(ip);
520 ASSIGN(pid);
521 ASSIGN(tid);
522 ASSIGN(time);
523 ASSIGN(addr);
524 ASSIGN(id);
525 ASSIGN(stream_id);
526 ASSIGN(period);
527 ASSIGN(weight);
528 ASSIGN(ins_lat);
529 ASSIGN(p_stage_cyc);
530 ASSIGN(transaction);
531 ASSIGN(insn_cnt);
532 ASSIGN(cyc_cnt);
533 ASSIGN(cpu);
534 ASSIGN(flags);
535 ASSIGN(data_src);
536 ASSIGN(phys_addr);
537 ASSIGN(data_page_size);
538 ASSIGN(code_page_size);
539 ASSIGN(cgroup);
540 ASSIGN(cpumode);
541 ASSIGN(misc);
542 ASSIGN(raw_size);
543 ASSIGN(raw_data);
544 ASSIGN(machine_pid);
545 ASSIGN(vcpu);
546
547 if (sample->branch_stack) {
548 d_sample.brstack_nr = sample->branch_stack->nr;
549 d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
550 } else {
551 d_sample.brstack_nr = 0;
552 d_sample.brstack = NULL;
553 }
554
555 if (sample->callchain) {
556 d_sample.raw_callchain_nr = sample->callchain->nr;
557 d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
558 } else {
559 d_sample.raw_callchain_nr = 0;
560 d_sample.raw_callchain = NULL;
561 }
562
563 d_sample.addr_correlates_sym =
564 (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
565 sample_addr_correlates_sym(&evsel->core.attr);
566
567 d_sample.event = evsel__name(evsel);
568
569 d->ctx_valid = true;
570
571 if (early)
572 ret = d->filter_event_early(d->data, &d_sample, d);
573 else
574 ret = d->filter_event(d->data, &d_sample, d);
575
576 d->ctx_valid = false;
577
578 return ret;
579 }
580
get_filter_desc(const char * dirname,const char * name,char ** desc,char ** long_desc)581 bool get_filter_desc(const char *dirname, const char *name, char **desc,
582 char **long_desc)
583 {
584 char path[PATH_MAX];
585 void *handle;
586 const char *(*desc_fn)(const char **long_description);
587
588 snprintf(path, sizeof(path), "%s/%s", dirname, name);
589 handle = dlopen(path, RTLD_NOW);
590 if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
591 return false;
592 desc_fn = dlsym(handle, "filter_description");
593 if (desc_fn) {
594 const char *dsc;
595 const char *long_dsc;
596
597 dsc = desc_fn(&long_dsc);
598 if (dsc)
599 *desc = strdup(dsc);
600 if (long_dsc)
601 *long_desc = strdup(long_dsc);
602 }
603 dlclose(handle);
604 return true;
605 }
606
list_filters(const char * dirname)607 static void list_filters(const char *dirname)
608 {
609 struct dirent *entry;
610 DIR *dir;
611
612 dir = opendir(dirname);
613 if (!dir)
614 return;
615
616 while ((entry = readdir(dir)) != NULL)
617 {
618 size_t n = strlen(entry->d_name);
619 char *long_desc = NULL;
620 char *desc = NULL;
621
622 if (entry->d_type == DT_DIR || n < 4 ||
623 strcmp(".so", entry->d_name + n - 3))
624 continue;
625 if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
626 continue;
627 printf(" %-36s %s\n", entry->d_name, desc ? desc : "");
628 if (verbose > 0) {
629 char *p = long_desc;
630 char *line;
631
632 while ((line = strsep(&p, "\n")) != NULL)
633 printf("%39s%s\n", "", line);
634 }
635 free(long_desc);
636 free(desc);
637 }
638
639 closedir(dir);
640 }
641
list_available_dlfilters(const struct option * opt __maybe_unused,const char * s __maybe_unused,int unset __maybe_unused)642 int list_available_dlfilters(const struct option *opt __maybe_unused,
643 const char *s __maybe_unused,
644 int unset __maybe_unused)
645 {
646 char path[PATH_MAX];
647 char *exec_path;
648
649 printf("List of available dlfilters:\n");
650
651 list_filters(".");
652
653 exec_path = get_argv_exec_path();
654 if (!exec_path)
655 goto out;
656 snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
657
658 list_filters(path);
659
660 free(exec_path);
661 out:
662 exit(0);
663 }
664