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