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