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
);