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