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