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