1 // SPDX-License-Identifier: GPL-2.0
3 * dlfilter.c: Interface to perf script --dlfilter shared object
4 * Copyright (c) 2021, Intel Corporation.
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>
22 #include "trace-event.h"
26 #include "../include/perf/perf_dlfilter.h"
28 static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
30 struct symbol *sym = al->sym;
32 d_al->size = sizeof(*d_al);
34 struct dso *dso = map__dso(al->map);
36 if (symbol_conf.show_kernel_path && dso->long_name)
37 d_al->dso = dso->long_name;
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;
46 d_al->buildid_size = 0;
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;
56 d_al->symoff = al->addr - map__start(al->map) - sym->start;
57 d_al->sym_binding = sym->binding;
63 d_al->sym_binding = 0;
65 d_al->addr = al->addr;
71 static struct addr_location *get_al(struct dlfilter *d)
73 struct addr_location *al = d->al;
75 if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
80 static struct thread *get_thread(struct dlfilter *d)
82 struct addr_location *al = get_al(d);
84 return al ? al->thread : NULL;
87 static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
89 struct dlfilter *d = (struct dlfilter *)ctx;
90 struct perf_dlfilter_al *d_al = d->d_ip_al;
91 struct addr_location *al;
96 /* 'size' is also used to indicate already initialized */
104 al_to_d_al(al, d_al);
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;
113 static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
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;
119 if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
122 /* 'size' is also used to indicate already initialized */
126 if (!addr_al->thread) {
127 struct thread *thread = get_thread(d);
131 thread__resolve(thread, addr_al, d->sample);
134 al_to_d_al(addr_al, d_addr_al);
136 d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
141 static char **dlfilter__args(void *ctx, int *dlargc)
143 struct dlfilter *d = (struct dlfilter *)ctx;
150 if (!d->ctx_valid && !d->in_start && !d->in_stop)
157 static bool has_priv(struct perf_dlfilter_al *d_al_p)
159 return d_al_p->size >= offsetof(struct perf_dlfilter_al, priv) + sizeof(d_al_p->priv);
162 static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
164 struct dlfilter *d = (struct dlfilter *)ctx;
165 struct perf_dlfilter_al d_al;
166 struct addr_location al;
167 struct thread *thread;
170 if (!d->ctx_valid || !d_al_p)
173 thread = get_thread(d);
177 addr_location__init(&al);
178 thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
180 al_to_d_al(&al, &d_al);
182 d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
185 memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
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);
196 static void dlfilter__al_cleanup(void *ctx __maybe_unused, struct perf_dlfilter_al *d_al_p)
198 struct addr_location *al;
200 /* Ensure backward compatibility */
201 if (!has_priv(d_al_p) || !d_al_p->priv)
208 addr_location__exit(al);
213 static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
215 struct dlfilter *d = (struct dlfilter *)ctx;
225 if (d->sample->ip && !d->sample->insn_len) {
226 struct addr_location *al = d->al;
228 if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
231 if (thread__maps(al->thread)) {
232 struct machine *machine = maps__machine(thread__maps(al->thread));
235 script_fetch_insn(d->sample, al->thread, machine);
239 if (!d->sample->insn_len)
242 *len = d->sample->insn_len;
244 return (__u8 *)d->sample->insn;
247 static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
249 struct dlfilter *d = (struct dlfilter *)ctx;
250 struct addr_location *al;
251 unsigned int line = 0;
252 char *srcfile = NULL;
257 if (!d->ctx_valid || !line_no)
266 dso = map ? map__dso(map) : NULL;
269 srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
275 static struct perf_event_attr *dlfilter__attr(void *ctx)
277 struct dlfilter *d = (struct dlfilter *)ctx;
282 return &d->evsel->core.attr;
285 static __s32 code_read(__u64 ip, struct map *map, struct machine *machine, void *buf, __u32 len)
287 u64 offset = map__map_ip(map, ip);
289 if (ip + len >= map__end(map))
290 len = map__end(map) - ip;
292 return dso__data_read_offset(map__dso(map), machine, offset, buf, len);
295 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
297 struct dlfilter *d = (struct dlfilter *)ctx;
298 struct addr_location *al;
299 struct addr_location a;
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);
313 addr_location__init(&a);
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;
318 addr_location__exit(&a);
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,
335 static char *find_dlfilter(const char *file)
340 if (strchr(file, '/'))
343 if (!access(file, R_OK)) {
345 * Prepend "./" so that dlopen will find the file in the
348 snprintf(path, sizeof(path), "./%s", file);
353 exec_path = get_argv_exec_path();
356 snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
358 if (!access(path, R_OK))
364 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
366 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
371 CHECK_FLAG(CONDITIONAL);
372 CHECK_FLAG(SYSCALLRET);
374 CHECK_FLAG(INTERRUPT);
375 CHECK_FLAG(TX_ABORT);
376 CHECK_FLAG(TRACE_BEGIN);
377 CHECK_FLAG(TRACE_END);
382 memset(d, 0, sizeof(*d));
383 d->file = find_dlfilter(file);
391 static void dlfilter__exit(struct dlfilter *d)
396 static int dlfilter__open(struct dlfilter *d)
398 d->handle = dlopen(d->file, RTLD_NOW);
400 pr_err("dlopen failed for: '%s'\n", d->file);
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");
409 memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
413 static int dlfilter__close(struct dlfilter *d)
415 return dlclose(d->handle);
418 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
420 struct dlfilter *d = malloc(sizeof(*d));
425 if (dlfilter__init(d, file, dlargc, dlargv))
428 if (dlfilter__open(d))
440 static void dlfilter__free(struct dlfilter *d)
448 int dlfilter__start(struct dlfilter *d, struct perf_session *session)
451 d->session = session;
456 ret = d->start(&d->data, d);
464 static int dlfilter__stop(struct dlfilter *d)
470 ret = d->stop(d->data, d);
477 void dlfilter__cleanup(struct dlfilter *d)
486 #define ASSIGN(x) d_sample.x = sample->x
488 int dlfilter__do_filter_event(struct dlfilter *d,
489 union perf_event *event,
490 struct perf_sample *sample,
492 struct machine *machine,
493 struct addr_location *al,
494 struct addr_location *addr_al,
497 struct perf_dlfilter_sample d_sample;
498 struct perf_dlfilter_al d_ip_al;
499 struct perf_dlfilter_al d_addr_al;
505 d->machine = machine;
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;
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 */
534 ASSIGN(data_page_size);
535 ASSIGN(code_page_size);
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);
548 d_sample.brstack_nr = 0;
549 d_sample.brstack = NULL;
552 if (sample->callchain) {
553 d_sample.raw_callchain_nr = sample->callchain->nr;
554 d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
556 d_sample.raw_callchain_nr = 0;
557 d_sample.raw_callchain = NULL;
560 d_sample.addr_correlates_sym =
561 (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
562 sample_addr_correlates_sym(&evsel->core.attr);
564 d_sample.event = evsel__name(evsel);
569 ret = d->filter_event_early(d->data, &d_sample, d);
571 ret = d->filter_event(d->data, &d_sample, d);
573 d->ctx_valid = false;
578 bool get_filter_desc(const char *dirname, const char *name, char **desc,
583 const char *(*desc_fn)(const char **long_description);
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")))
589 desc_fn = dlsym(handle, "filter_description");
592 const char *long_dsc;
594 dsc = desc_fn(&long_dsc);
598 *long_desc = strdup(long_dsc);
604 static void list_filters(const char *dirname)
606 struct dirent *entry;
609 dir = opendir(dirname);
613 while ((entry = readdir(dir)) != NULL)
615 size_t n = strlen(entry->d_name);
616 char *long_desc = NULL;
619 if (entry->d_type == DT_DIR || n < 4 ||
620 strcmp(".so", entry->d_name + n - 3))
622 if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
624 printf(" %-36s %s\n", entry->d_name, desc ? desc : "");
629 while ((line = strsep(&p, "\n")) != NULL)
630 printf("%39s%s\n", "", line);
639 int list_available_dlfilters(const struct option *opt __maybe_unused,
640 const char *s __maybe_unused,
641 int unset __maybe_unused)
646 printf("List of available dlfilters:\n");
650 exec_path = get_argv_exec_path();
653 snprintf(path, sizeof(path), "%s/dlfilters", exec_path);