Linux 6.9-rc1
[linux-2.6-microblaze.git] / tools / perf / util / dlfilter.c
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 #include <linux/kernel.h>
14 #include <linux/string.h>
15
16 #include "debug.h"
17 #include "event.h"
18 #include "evsel.h"
19 #include "dso.h"
20 #include "map.h"
21 #include "thread.h"
22 #include "trace-event.h"
23 #include "symbol.h"
24 #include "srcline.h"
25 #include "dlfilter.h"
26 #include "../include/perf/perf_dlfilter.h"
27
28 static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
29 {
30         struct symbol *sym = al->sym;
31
32         d_al->size = sizeof(*d_al);
33         if (al->map) {
34                 struct dso *dso = map__dso(al->map);
35
36                 if (symbol_conf.show_kernel_path && dso->long_name)
37                         d_al->dso = dso->long_name;
38                 else
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;
43         } else {
44                 d_al->dso = NULL;
45                 d_al->is_64_bit = 0;
46                 d_al->buildid_size = 0;
47                 d_al->buildid = NULL;
48         }
49         if (sym) {
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;
55                 else if (al->map)
56                         d_al->symoff = al->addr - map__start(al->map) - sym->start;
57                 else
58                         d_al->symoff = 0;
59                 d_al->sym_binding = sym->binding;
60         } else {
61                 d_al->sym = NULL;
62                 d_al->sym_start = 0;
63                 d_al->sym_end = 0;
64                 d_al->symoff = 0;
65                 d_al->sym_binding = 0;
66         }
67         d_al->addr = al->addr;
68         d_al->comm = NULL;
69         d_al->filtered = 0;
70         d_al->priv = NULL;
71 }
72
73 static struct addr_location *get_al(struct dlfilter *d)
74 {
75         struct addr_location *al = d->al;
76
77         if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
78                 return NULL;
79         return al;
80 }
81
82 static struct thread *get_thread(struct dlfilter *d)
83 {
84         struct addr_location *al = get_al(d);
85
86         return al ? al->thread : NULL;
87 }
88
89 static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
90 {
91         struct dlfilter *d = (struct dlfilter *)ctx;
92         struct perf_dlfilter_al *d_al = d->d_ip_al;
93         struct addr_location *al;
94
95         if (!d->ctx_valid)
96                 return NULL;
97
98         /* 'size' is also used to indicate already initialized */
99         if (d_al->size)
100                 return d_al;
101
102         al = get_al(d);
103         if (!al)
104                 return NULL;
105
106         al_to_d_al(al, d_al);
107
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;
111
112         return d_al;
113 }
114
115 static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
116 {
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;
120
121         if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
122                 return NULL;
123
124         /* 'size' is also used to indicate already initialized */
125         if (d_addr_al->size)
126                 return d_addr_al;
127
128         if (!addr_al->thread) {
129                 struct thread *thread = get_thread(d);
130
131                 if (!thread)
132                         return NULL;
133                 thread__resolve(thread, addr_al, d->sample);
134         }
135
136         al_to_d_al(addr_al, d_addr_al);
137
138         d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
139
140         return d_addr_al;
141 }
142
143 static char **dlfilter__args(void *ctx, int *dlargc)
144 {
145         struct dlfilter *d = (struct dlfilter *)ctx;
146
147         if (dlargc)
148                 *dlargc = 0;
149         else
150                 return NULL;
151
152         if (!d->ctx_valid && !d->in_start && !d->in_stop)
153                 return NULL;
154
155         *dlargc = d->dlargc;
156         return d->dlargv;
157 }
158
159 static bool has_priv(struct perf_dlfilter_al *d_al_p)
160 {
161         return d_al_p->size >= offsetof(struct perf_dlfilter_al, priv) + sizeof(d_al_p->priv);
162 }
163
164 static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
165 {
166         struct dlfilter *d = (struct dlfilter *)ctx;
167         struct perf_dlfilter_al d_al;
168         struct addr_location al;
169         struct thread *thread;
170         __u32 sz;
171
172         if (!d->ctx_valid || !d_al_p)
173                 return -1;
174
175         thread = get_thread(d);
176         if (!thread)
177                 return -1;
178
179         addr_location__init(&al);
180         thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
181
182         al_to_d_al(&al, &d_al);
183
184         d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
185
186         sz = d_al_p->size;
187         memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
188         d_al_p->size = sz;
189
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);
194
195         return 0;
196 }
197
198 static void dlfilter__al_cleanup(void *ctx __maybe_unused, struct perf_dlfilter_al *d_al_p)
199 {
200         struct addr_location *al;
201
202         /* Ensure backward compatibility */
203         if (!has_priv(d_al_p) || !d_al_p->priv)
204                 return;
205
206         al = d_al_p->priv;
207
208         d_al_p->priv = NULL;
209
210         addr_location__exit(al);
211
212         free(al);
213 }
214
215 static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
216 {
217         struct dlfilter *d = (struct dlfilter *)ctx;
218
219         if (!len)
220                 return NULL;
221
222         *len = 0;
223
224         if (!d->ctx_valid)
225                 return NULL;
226
227         if (d->sample->ip && !d->sample->insn_len) {
228                 struct addr_location *al = d->al;
229
230                 if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
231                         return NULL;
232
233                 if (thread__maps(al->thread)) {
234                         struct machine *machine = maps__machine(thread__maps(al->thread));
235
236                         if (machine)
237                                 script_fetch_insn(d->sample, al->thread, machine);
238                 }
239         }
240
241         if (!d->sample->insn_len)
242                 return NULL;
243
244         *len = d->sample->insn_len;
245
246         return (__u8 *)d->sample->insn;
247 }
248
249 static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
250 {
251         struct dlfilter *d = (struct dlfilter *)ctx;
252         struct addr_location *al;
253         unsigned int line = 0;
254         char *srcfile = NULL;
255         struct map *map;
256         struct dso *dso;
257         u64 addr;
258
259         if (!d->ctx_valid || !line_no)
260                 return NULL;
261
262         al = get_al(d);
263         if (!al)
264                 return NULL;
265
266         map = al->map;
267         addr = al->addr;
268         dso = map ? map__dso(map) : NULL;
269
270         if (dso)
271                 srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
272
273         *line_no = line;
274         return srcfile;
275 }
276
277 static struct perf_event_attr *dlfilter__attr(void *ctx)
278 {
279         struct dlfilter *d = (struct dlfilter *)ctx;
280
281         if (!d->ctx_valid)
282                 return NULL;
283
284         return &d->evsel->core.attr;
285 }
286
287 static __s32 code_read(__u64 ip, struct map *map, struct machine *machine, void *buf, __u32 len)
288 {
289         u64 offset = map__map_ip(map, ip);
290
291         if (ip + len >= map__end(map))
292                 len = map__end(map) - ip;
293
294         return dso__data_read_offset(map__dso(map), machine, offset, buf, len);
295 }
296
297 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
298 {
299         struct dlfilter *d = (struct dlfilter *)ctx;
300         struct addr_location *al;
301         struct addr_location a;
302         __s32 ret;
303
304         if (!d->ctx_valid)
305                 return -1;
306
307         al = get_al(d);
308         if (!al)
309                 return -1;
310
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);
314
315         addr_location__init(&a);
316
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;
319
320         addr_location__exit(&a);
321
322         return ret;
323 }
324
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,
335 };
336
337 static char *find_dlfilter(const char *file)
338 {
339         char path[PATH_MAX];
340         char *exec_path;
341
342         if (strchr(file, '/'))
343                 goto out;
344
345         if (!access(file, R_OK)) {
346                 /*
347                  * Prepend "./" so that dlopen will find the file in the
348                  * current directory.
349                  */
350                 snprintf(path, sizeof(path), "./%s", file);
351                 file = path;
352                 goto out;
353         }
354
355         exec_path = get_argv_exec_path();
356         if (!exec_path)
357                 goto out;
358         snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
359         free(exec_path);
360         if (!access(path, R_OK))
361                 file = path;
362 out:
363         return strdup(file);
364 }
365
366 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
367
368 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
369 {
370         CHECK_FLAG(BRANCH);
371         CHECK_FLAG(CALL);
372         CHECK_FLAG(RETURN);
373         CHECK_FLAG(CONDITIONAL);
374         CHECK_FLAG(SYSCALLRET);
375         CHECK_FLAG(ASYNC);
376         CHECK_FLAG(INTERRUPT);
377         CHECK_FLAG(TX_ABORT);
378         CHECK_FLAG(TRACE_BEGIN);
379         CHECK_FLAG(TRACE_END);
380         CHECK_FLAG(IN_TX);
381         CHECK_FLAG(VMENTRY);
382         CHECK_FLAG(VMEXIT);
383
384         memset(d, 0, sizeof(*d));
385         d->file = find_dlfilter(file);
386         if (!d->file)
387                 return -1;
388         d->dlargc = dlargc;
389         d->dlargv = dlargv;
390         return 0;
391 }
392
393 static void dlfilter__exit(struct dlfilter *d)
394 {
395         zfree(&d->file);
396 }
397
398 static int dlfilter__open(struct dlfilter *d)
399 {
400         d->handle = dlopen(d->file, RTLD_NOW);
401         if (!d->handle) {
402                 pr_err("dlopen failed for: '%s'\n", d->file);
403                 return -1;
404         }
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");
410         if (d->fns)
411                 memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
412         return 0;
413 }
414
415 static int dlfilter__close(struct dlfilter *d)
416 {
417         return dlclose(d->handle);
418 }
419
420 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
421 {
422         struct dlfilter *d = malloc(sizeof(*d));
423
424         if (!d)
425                 return NULL;
426
427         if (dlfilter__init(d, file, dlargc, dlargv))
428                 goto err_free;
429
430         if (dlfilter__open(d))
431                 goto err_exit;
432
433         return d;
434
435 err_exit:
436         dlfilter__exit(d);
437 err_free:
438         free(d);
439         return NULL;
440 }
441
442 static void dlfilter__free(struct dlfilter *d)
443 {
444         if (d) {
445                 dlfilter__exit(d);
446                 free(d);
447         }
448 }
449
450 int dlfilter__start(struct dlfilter *d, struct perf_session *session)
451 {
452         if (d) {
453                 d->session = session;
454                 if (d->start) {
455                         int ret;
456
457                         d->in_start = true;
458                         ret = d->start(&d->data, d);
459                         d->in_start = false;
460                         return ret;
461                 }
462         }
463         return 0;
464 }
465
466 static int dlfilter__stop(struct dlfilter *d)
467 {
468         if (d && d->stop) {
469                 int ret;
470
471                 d->in_stop = true;
472                 ret = d->stop(d->data, d);
473                 d->in_stop = false;
474                 return ret;
475         }
476         return 0;
477 }
478
479 void dlfilter__cleanup(struct dlfilter *d)
480 {
481         if (d) {
482                 dlfilter__stop(d);
483                 dlfilter__close(d);
484                 dlfilter__free(d);
485         }
486 }
487
488 #define ASSIGN(x) d_sample.x = sample->x
489
490 int dlfilter__do_filter_event(struct dlfilter *d,
491                               union perf_event *event,
492                               struct perf_sample *sample,
493                               struct evsel *evsel,
494                               struct machine *machine,
495                               struct addr_location *al,
496                               struct addr_location *addr_al,
497                               bool early)
498 {
499         struct perf_dlfilter_sample d_sample;
500         struct perf_dlfilter_al d_ip_al;
501         struct perf_dlfilter_al d_addr_al;
502         int ret;
503
504         d->event       = event;
505         d->sample      = sample;
506         d->evsel       = evsel;
507         d->machine     = machine;
508         d->al          = al;
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;
513
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 */
517
518         ASSIGN(ip);
519         ASSIGN(pid);
520         ASSIGN(tid);
521         ASSIGN(time);
522         ASSIGN(addr);
523         ASSIGN(id);
524         ASSIGN(stream_id);
525         ASSIGN(period);
526         ASSIGN(weight);
527         ASSIGN(ins_lat);
528         ASSIGN(p_stage_cyc);
529         ASSIGN(transaction);
530         ASSIGN(insn_cnt);
531         ASSIGN(cyc_cnt);
532         ASSIGN(cpu);
533         ASSIGN(flags);
534         ASSIGN(data_src);
535         ASSIGN(phys_addr);
536         ASSIGN(data_page_size);
537         ASSIGN(code_page_size);
538         ASSIGN(cgroup);
539         ASSIGN(cpumode);
540         ASSIGN(misc);
541         ASSIGN(raw_size);
542         ASSIGN(raw_data);
543         ASSIGN(machine_pid);
544         ASSIGN(vcpu);
545
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);
549         } else {
550                 d_sample.brstack_nr = 0;
551                 d_sample.brstack = NULL;
552         }
553
554         if (sample->callchain) {
555                 d_sample.raw_callchain_nr = sample->callchain->nr;
556                 d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
557         } else {
558                 d_sample.raw_callchain_nr = 0;
559                 d_sample.raw_callchain = NULL;
560         }
561
562         d_sample.addr_correlates_sym =
563                 (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
564                 sample_addr_correlates_sym(&evsel->core.attr);
565
566         d_sample.event = evsel__name(evsel);
567
568         d->ctx_valid = true;
569
570         if (early)
571                 ret = d->filter_event_early(d->data, &d_sample, d);
572         else
573                 ret = d->filter_event(d->data, &d_sample, d);
574
575         d->ctx_valid = false;
576
577         return ret;
578 }
579
580 bool get_filter_desc(const char *dirname, const char *name, char **desc,
581                      char **long_desc)
582 {
583         char path[PATH_MAX];
584         void *handle;
585         const char *(*desc_fn)(const char **long_description);
586
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")))
590                 return false;
591         desc_fn = dlsym(handle, "filter_description");
592         if (desc_fn) {
593                 const char *dsc;
594                 const char *long_dsc;
595
596                 dsc = desc_fn(&long_dsc);
597                 if (dsc)
598                         *desc = strdup(dsc);
599                 if (long_dsc)
600                         *long_desc = strdup(long_dsc);
601         }
602         dlclose(handle);
603         return true;
604 }
605
606 static void list_filters(const char *dirname)
607 {
608         struct dirent *entry;
609         DIR *dir;
610
611         dir = opendir(dirname);
612         if (!dir)
613                 return;
614
615         while ((entry = readdir(dir)) != NULL)
616         {
617                 size_t n = strlen(entry->d_name);
618                 char *long_desc = NULL;
619                 char *desc = NULL;
620
621                 if (entry->d_type == DT_DIR || n < 4 ||
622                     strcmp(".so", entry->d_name + n - 3))
623                         continue;
624                 if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
625                         continue;
626                 printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
627                 if (verbose > 0) {
628                         char *p = long_desc;
629                         char *line;
630
631                         while ((line = strsep(&p, "\n")) != NULL)
632                                 printf("%39s%s\n", "", line);
633                 }
634                 free(long_desc);
635                 free(desc);
636         }
637
638         closedir(dir);
639 }
640
641 int list_available_dlfilters(const struct option *opt __maybe_unused,
642                              const char *s __maybe_unused,
643                              int unset __maybe_unused)
644 {
645         char path[PATH_MAX];
646         char *exec_path;
647
648         printf("List of available dlfilters:\n");
649
650         list_filters(".");
651
652         exec_path = get_argv_exec_path();
653         if (!exec_path)
654                 goto out;
655         snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
656
657         list_filters(path);
658
659         free(exec_path);
660 out:
661         exit(0);
662 }