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(dso
))
37 d_al
->dso
= dso__long_name(dso
);
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
;
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
;
59 d_al
->sym_binding
= sym
->binding
;
65 d_al
->sym_binding
= 0;
67 d_al
->addr
= al
->addr
;
73 static struct addr_location
*get_al(struct dlfilter
*d
)
75 struct addr_location
*al
= d
->al
;
77 if (!al
->thread
&& machine__resolve(d
->machine
, al
, d
->sample
) < 0)
82 static struct thread
*get_thread(struct dlfilter
*d
)
84 struct addr_location
*al
= get_al(d
);
86 return al
? al
->thread
: NULL
;
89 static const struct perf_dlfilter_al
*dlfilter__resolve_ip(void *ctx
)
91 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
92 struct perf_dlfilter_al
*d_al
= d
->d_ip_al
;
93 struct addr_location
*al
;
98 /* 'size' is also used to indicate already initialized */
106 al_to_d_al(al
, d_al
);
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
;
115 static const struct perf_dlfilter_al
*dlfilter__resolve_addr(void *ctx
)
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
;
121 if (!d
->ctx_valid
|| !d
->d_sample
->addr_correlates_sym
)
124 /* 'size' is also used to indicate already initialized */
128 if (!addr_al
->thread
) {
129 struct thread
*thread
= get_thread(d
);
133 thread__resolve(thread
, addr_al
, d
->sample
);
136 al_to_d_al(addr_al
, d_addr_al
);
138 d_addr_al
->is_kernel_ip
= machine__kernel_ip(d
->machine
, d
->sample
->addr
);
143 static char **dlfilter__args(void *ctx
, int *dlargc
)
145 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
152 if (!d
->ctx_valid
&& !d
->in_start
&& !d
->in_stop
)
159 static bool has_priv(struct perf_dlfilter_al
*d_al_p
)
161 return d_al_p
->size
>= offsetof(struct perf_dlfilter_al
, priv
) + sizeof(d_al_p
->priv
);
164 static __s32
dlfilter__resolve_address(void *ctx
, __u64 address
, struct perf_dlfilter_al
*d_al_p
)
166 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
167 struct perf_dlfilter_al d_al
;
168 struct addr_location al
;
169 struct thread
*thread
;
172 if (!d
->ctx_valid
|| !d_al_p
)
175 thread
= get_thread(d
);
179 addr_location__init(&al
);
180 thread__find_symbol_fb(thread
, d
->sample
->cpumode
, address
, &al
);
182 al_to_d_al(&al
, &d_al
);
184 d_al
.is_kernel_ip
= machine__kernel_ip(d
->machine
, address
);
187 memcpy(d_al_p
, &d_al
, min((size_t)sz
, sizeof(d_al
)));
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
);
198 static void dlfilter__al_cleanup(void *ctx __maybe_unused
, struct perf_dlfilter_al
*d_al_p
)
200 struct addr_location
*al
;
202 /* Ensure backward compatibility */
203 if (!has_priv(d_al_p
) || !d_al_p
->priv
)
210 addr_location__exit(al
);
215 static const __u8
*dlfilter__insn(void *ctx
, __u32
*len
)
217 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
227 if (d
->sample
->ip
&& !d
->sample
->insn_len
) {
228 struct addr_location
*al
= d
->al
;
230 if (!al
->thread
&& machine__resolve(d
->machine
, al
, d
->sample
) < 0)
233 if (thread__maps(al
->thread
)) {
234 struct machine
*machine
= maps__machine(thread__maps(al
->thread
));
237 script_fetch_insn(d
->sample
, al
->thread
, machine
);
241 if (!d
->sample
->insn_len
)
244 *len
= d
->sample
->insn_len
;
246 return (__u8
*)d
->sample
->insn
;
249 static const char *dlfilter__srcline(void *ctx
, __u32
*line_no
)
251 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
252 struct addr_location
*al
;
253 unsigned int line
= 0;
254 char *srcfile
= NULL
;
259 if (!d
->ctx_valid
|| !line_no
)
268 dso
= map
? map__dso(map
) : NULL
;
271 srcfile
= get_srcline_split(dso
, map__rip_2objdump(map
, addr
), &line
);
277 static struct perf_event_attr
*dlfilter__attr(void *ctx
)
279 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
284 return &d
->evsel
->core
.attr
;
287 static __s32
code_read(__u64 ip
, struct map
*map
, struct machine
*machine
, void *buf
, __u32 len
)
289 u64 offset
= map__map_ip(map
, ip
);
291 if (ip
+ len
>= map__end(map
))
292 len
= map__end(map
) - ip
;
294 return dso__data_read_offset(map__dso(map
), machine
, offset
, buf
, len
);
297 static __s32
dlfilter__object_code(void *ctx
, __u64 ip
, void *buf
, __u32 len
)
299 struct dlfilter
*d
= (struct dlfilter
*)ctx
;
300 struct addr_location
*al
;
301 struct addr_location a
;
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
);
315 addr_location__init(&a
);
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;
320 addr_location__exit(&a
);
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
,
337 static char *find_dlfilter(const char *file
)
342 if (strchr(file
, '/'))
345 if (!access(file
, R_OK
)) {
347 * Prepend "./" so that dlopen will find the file in the
350 snprintf(path
, sizeof(path
), "./%s", file
);
355 exec_path
= get_argv_exec_path();
358 snprintf(path
, sizeof(path
), "%s/dlfilters/%s", exec_path
, file
);
360 if (!access(path
, R_OK
))
366 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
368 static int dlfilter__init(struct dlfilter
*d
, const char *file
, int dlargc
, char **dlargv
)
373 CHECK_FLAG(CONDITIONAL
);
374 CHECK_FLAG(SYSCALLRET
);
376 CHECK_FLAG(INTERRUPT
);
377 CHECK_FLAG(TX_ABORT
);
378 CHECK_FLAG(TRACE_BEGIN
);
379 CHECK_FLAG(TRACE_END
);
384 memset(d
, 0, sizeof(*d
));
385 d
->file
= find_dlfilter(file
);
393 static void dlfilter__exit(struct dlfilter
*d
)
398 static int dlfilter__open(struct dlfilter
*d
)
400 d
->handle
= dlopen(d
->file
, RTLD_NOW
);
402 pr_err("dlopen failed for: '%s'\n", d
->file
);
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");
411 memcpy(d
->fns
, &perf_dlfilter_fns
, sizeof(struct perf_dlfilter_fns
));
415 static int dlfilter__close(struct dlfilter
*d
)
417 return dlclose(d
->handle
);
420 struct dlfilter
*dlfilter__new(const char *file
, int dlargc
, char **dlargv
)
422 struct dlfilter
*d
= malloc(sizeof(*d
));
427 if (dlfilter__init(d
, file
, dlargc
, dlargv
))
430 if (dlfilter__open(d
))
442 static void dlfilter__free(struct dlfilter
*d
)
450 int dlfilter__start(struct dlfilter
*d
, struct perf_session
*session
)
453 d
->session
= session
;
458 ret
= d
->start(&d
->data
, d
);
466 static int dlfilter__stop(struct dlfilter
*d
)
472 ret
= d
->stop(d
->data
, d
);
479 void dlfilter__cleanup(struct dlfilter
*d
)
488 #define ASSIGN(x) d_sample.x = sample->x
490 int dlfilter__do_filter_event(struct dlfilter
*d
,
491 union perf_event
*event
,
492 struct perf_sample
*sample
,
494 struct machine
*machine
,
495 struct addr_location
*al
,
496 struct addr_location
*addr_al
,
499 struct perf_dlfilter_sample d_sample
;
500 struct perf_dlfilter_al d_ip_al
;
501 struct perf_dlfilter_al d_addr_al
;
507 d
->machine
= machine
;
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
;
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 */
536 ASSIGN(data_page_size
);
537 ASSIGN(code_page_size
);
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
);
550 d_sample
.brstack_nr
= 0;
551 d_sample
.brstack
= NULL
;
554 if (sample
->callchain
) {
555 d_sample
.raw_callchain_nr
= sample
->callchain
->nr
;
556 d_sample
.raw_callchain
= (__u64
*)sample
->callchain
->ips
;
558 d_sample
.raw_callchain_nr
= 0;
559 d_sample
.raw_callchain
= NULL
;
562 d_sample
.addr_correlates_sym
=
563 (evsel
->core
.attr
.sample_type
& PERF_SAMPLE_ADDR
) &&
564 sample_addr_correlates_sym(&evsel
->core
.attr
);
566 d_sample
.event
= evsel__name(evsel
);
571 ret
= d
->filter_event_early(d
->data
, &d_sample
, d
);
573 ret
= d
->filter_event(d
->data
, &d_sample
, d
);
575 d
->ctx_valid
= false;
580 bool get_filter_desc(const char *dirname
, const char *name
, char **desc
,
585 const char *(*desc_fn
)(const char **long_description
);
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")))
591 desc_fn
= dlsym(handle
, "filter_description");
594 const char *long_dsc
;
596 dsc
= desc_fn(&long_dsc
);
600 *long_desc
= strdup(long_dsc
);
606 static void list_filters(const char *dirname
)
608 struct dirent
*entry
;
611 dir
= opendir(dirname
);
615 while ((entry
= readdir(dir
)) != NULL
)
617 size_t n
= strlen(entry
->d_name
);
618 char *long_desc
= NULL
;
621 if (entry
->d_type
== DT_DIR
|| n
< 4 ||
622 strcmp(".so", entry
->d_name
+ n
- 3))
624 if (!get_filter_desc(dirname
, entry
->d_name
, &desc
, &long_desc
))
626 printf(" %-36s %s\n", entry
->d_name
, desc
? desc
: "");
631 while ((line
= strsep(&p
, "\n")) != NULL
)
632 printf("%39s%s\n", "", line
);
641 int list_available_dlfilters(const struct option
*opt __maybe_unused
,
642 const char *s __maybe_unused
,
643 int unset __maybe_unused
)
648 printf("List of available dlfilters:\n");
652 exec_path
= get_argv_exec_path();
655 snprintf(path
, sizeof(path
), "%s/dlfilters", exec_path
);