Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * dlfilter.c: Interface to perf script --dlfilter shared object
0004  * Copyright (c) 2021, Intel Corporation.
0005  */
0006 #include <dlfcn.h>
0007 #include <stdlib.h>
0008 #include <string.h>
0009 #include <dirent.h>
0010 #include <subcmd/exec-cmd.h>
0011 #include <linux/zalloc.h>
0012 #include <linux/build_bug.h>
0013 
0014 #include "debug.h"
0015 #include "event.h"
0016 #include "evsel.h"
0017 #include "dso.h"
0018 #include "map.h"
0019 #include "thread.h"
0020 #include "trace-event.h"
0021 #include "symbol.h"
0022 #include "srcline.h"
0023 #include "dlfilter.h"
0024 #include "../include/perf/perf_dlfilter.h"
0025 
0026 static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
0027 {
0028     struct symbol *sym = al->sym;
0029 
0030     d_al->size = sizeof(*d_al);
0031     if (al->map) {
0032         struct dso *dso = al->map->dso;
0033 
0034         if (symbol_conf.show_kernel_path && dso->long_name)
0035             d_al->dso = dso->long_name;
0036         else
0037             d_al->dso = dso->name;
0038         d_al->is_64_bit = dso->is_64_bit;
0039         d_al->buildid_size = dso->bid.size;
0040         d_al->buildid = dso->bid.data;
0041     } else {
0042         d_al->dso = NULL;
0043         d_al->is_64_bit = 0;
0044         d_al->buildid_size = 0;
0045         d_al->buildid = NULL;
0046     }
0047     if (sym) {
0048         d_al->sym = sym->name;
0049         d_al->sym_start = sym->start;
0050         d_al->sym_end = sym->end;
0051         if (al->addr < sym->end)
0052             d_al->symoff = al->addr - sym->start;
0053         else
0054             d_al->symoff = al->addr - al->map->start - sym->start;
0055         d_al->sym_binding = sym->binding;
0056     } else {
0057         d_al->sym = NULL;
0058         d_al->sym_start = 0;
0059         d_al->sym_end = 0;
0060         d_al->symoff = 0;
0061         d_al->sym_binding = 0;
0062     }
0063     d_al->addr = al->addr;
0064     d_al->comm = NULL;
0065     d_al->filtered = 0;
0066 }
0067 
0068 static struct addr_location *get_al(struct dlfilter *d)
0069 {
0070     struct addr_location *al = d->al;
0071 
0072     if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
0073         return NULL;
0074     return al;
0075 }
0076 
0077 static struct thread *get_thread(struct dlfilter *d)
0078 {
0079     struct addr_location *al = get_al(d);
0080 
0081     return al ? al->thread : NULL;
0082 }
0083 
0084 static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx)
0085 {
0086     struct dlfilter *d = (struct dlfilter *)ctx;
0087     struct perf_dlfilter_al *d_al = d->d_ip_al;
0088     struct addr_location *al;
0089 
0090     if (!d->ctx_valid)
0091         return NULL;
0092 
0093     /* 'size' is also used to indicate already initialized */
0094     if (d_al->size)
0095         return d_al;
0096 
0097     al = get_al(d);
0098     if (!al)
0099         return NULL;
0100 
0101     al_to_d_al(al, d_al);
0102 
0103     d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip);
0104     d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1";
0105     d_al->filtered = al->filtered;
0106 
0107     return d_al;
0108 }
0109 
0110 static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx)
0111 {
0112     struct dlfilter *d = (struct dlfilter *)ctx;
0113     struct perf_dlfilter_al *d_addr_al = d->d_addr_al;
0114     struct addr_location *addr_al = d->addr_al;
0115 
0116     if (!d->ctx_valid || !d->d_sample->addr_correlates_sym)
0117         return NULL;
0118 
0119     /* 'size' is also used to indicate already initialized */
0120     if (d_addr_al->size)
0121         return d_addr_al;
0122 
0123     if (!addr_al->thread) {
0124         struct thread *thread = get_thread(d);
0125 
0126         if (!thread)
0127             return NULL;
0128         thread__resolve(thread, addr_al, d->sample);
0129     }
0130 
0131     al_to_d_al(addr_al, d_addr_al);
0132 
0133     d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr);
0134 
0135     return d_addr_al;
0136 }
0137 
0138 static char **dlfilter__args(void *ctx, int *dlargc)
0139 {
0140     struct dlfilter *d = (struct dlfilter *)ctx;
0141 
0142     if (dlargc)
0143         *dlargc = 0;
0144     else
0145         return NULL;
0146 
0147     if (!d->ctx_valid && !d->in_start && !d->in_stop)
0148         return NULL;
0149 
0150     *dlargc = d->dlargc;
0151     return d->dlargv;
0152 }
0153 
0154 static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
0155 {
0156     struct dlfilter *d = (struct dlfilter *)ctx;
0157     struct perf_dlfilter_al d_al;
0158     struct addr_location al;
0159     struct thread *thread;
0160     __u32 sz;
0161 
0162     if (!d->ctx_valid || !d_al_p)
0163         return -1;
0164 
0165     thread = get_thread(d);
0166     if (!thread)
0167         return -1;
0168 
0169     thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
0170 
0171     al_to_d_al(&al, &d_al);
0172 
0173     d_al.is_kernel_ip = machine__kernel_ip(d->machine, address);
0174 
0175     sz = d_al_p->size;
0176     memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
0177     d_al_p->size = sz;
0178 
0179     return 0;
0180 }
0181 
0182 static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
0183 {
0184     struct dlfilter *d = (struct dlfilter *)ctx;
0185 
0186     if (!len)
0187         return NULL;
0188 
0189     *len = 0;
0190 
0191     if (!d->ctx_valid)
0192         return NULL;
0193 
0194     if (d->sample->ip && !d->sample->insn_len) {
0195         struct addr_location *al = d->al;
0196 
0197         if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
0198             return NULL;
0199 
0200         if (al->thread->maps && al->thread->maps->machine)
0201             script_fetch_insn(d->sample, al->thread, al->thread->maps->machine);
0202     }
0203 
0204     if (!d->sample->insn_len)
0205         return NULL;
0206 
0207     *len = d->sample->insn_len;
0208 
0209     return (__u8 *)d->sample->insn;
0210 }
0211 
0212 static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
0213 {
0214     struct dlfilter *d = (struct dlfilter *)ctx;
0215     struct addr_location *al;
0216     unsigned int line = 0;
0217     char *srcfile = NULL;
0218     struct map *map;
0219     u64 addr;
0220 
0221     if (!d->ctx_valid || !line_no)
0222         return NULL;
0223 
0224     al = get_al(d);
0225     if (!al)
0226         return NULL;
0227 
0228     map = al->map;
0229     addr = al->addr;
0230 
0231     if (map && map->dso)
0232         srcfile = get_srcline_split(map->dso, map__rip_2objdump(map, addr), &line);
0233 
0234     *line_no = line;
0235     return srcfile;
0236 }
0237 
0238 static struct perf_event_attr *dlfilter__attr(void *ctx)
0239 {
0240     struct dlfilter *d = (struct dlfilter *)ctx;
0241 
0242     if (!d->ctx_valid)
0243         return NULL;
0244 
0245     return &d->evsel->core.attr;
0246 }
0247 
0248 static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
0249 {
0250     struct dlfilter *d = (struct dlfilter *)ctx;
0251     struct addr_location *al;
0252     struct addr_location a;
0253     struct map *map;
0254     u64 offset;
0255 
0256     if (!d->ctx_valid)
0257         return -1;
0258 
0259     al = get_al(d);
0260     if (!al)
0261         return -1;
0262 
0263     map = al->map;
0264 
0265     if (map && ip >= map->start && ip < map->end &&
0266         machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
0267         goto have_map;
0268 
0269     thread__find_map_fb(al->thread, d->sample->cpumode, ip, &a);
0270     if (!a.map)
0271         return -1;
0272 
0273     map = a.map;
0274 have_map:
0275     offset = map->map_ip(map, ip);
0276     if (ip + len >= map->end)
0277         len = map->end - ip;
0278     return dso__data_read_offset(map->dso, d->machine, offset, buf, len);
0279 }
0280 
0281 static const struct perf_dlfilter_fns perf_dlfilter_fns = {
0282     .resolve_ip      = dlfilter__resolve_ip,
0283     .resolve_addr    = dlfilter__resolve_addr,
0284     .args            = dlfilter__args,
0285     .resolve_address = dlfilter__resolve_address,
0286     .insn            = dlfilter__insn,
0287     .srcline         = dlfilter__srcline,
0288     .attr            = dlfilter__attr,
0289     .object_code     = dlfilter__object_code,
0290 };
0291 
0292 static char *find_dlfilter(const char *file)
0293 {
0294     char path[PATH_MAX];
0295     char *exec_path;
0296 
0297     if (strchr(file, '/'))
0298         goto out;
0299 
0300     if (!access(file, R_OK)) {
0301         /*
0302          * Prepend "./" so that dlopen will find the file in the
0303          * current directory.
0304          */
0305         snprintf(path, sizeof(path), "./%s", file);
0306         file = path;
0307         goto out;
0308     }
0309 
0310     exec_path = get_argv_exec_path();
0311     if (!exec_path)
0312         goto out;
0313     snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file);
0314     free(exec_path);
0315     if (!access(path, R_OK))
0316         file = path;
0317 out:
0318     return strdup(file);
0319 }
0320 
0321 #define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x)
0322 
0323 static int dlfilter__init(struct dlfilter *d, const char *file, int dlargc, char **dlargv)
0324 {
0325     CHECK_FLAG(BRANCH);
0326     CHECK_FLAG(CALL);
0327     CHECK_FLAG(RETURN);
0328     CHECK_FLAG(CONDITIONAL);
0329     CHECK_FLAG(SYSCALLRET);
0330     CHECK_FLAG(ASYNC);
0331     CHECK_FLAG(INTERRUPT);
0332     CHECK_FLAG(TX_ABORT);
0333     CHECK_FLAG(TRACE_BEGIN);
0334     CHECK_FLAG(TRACE_END);
0335     CHECK_FLAG(IN_TX);
0336     CHECK_FLAG(VMENTRY);
0337     CHECK_FLAG(VMEXIT);
0338 
0339     memset(d, 0, sizeof(*d));
0340     d->file = find_dlfilter(file);
0341     if (!d->file)
0342         return -1;
0343     d->dlargc = dlargc;
0344     d->dlargv = dlargv;
0345     return 0;
0346 }
0347 
0348 static void dlfilter__exit(struct dlfilter *d)
0349 {
0350     zfree(&d->file);
0351 }
0352 
0353 static int dlfilter__open(struct dlfilter *d)
0354 {
0355     d->handle = dlopen(d->file, RTLD_NOW);
0356     if (!d->handle) {
0357         pr_err("dlopen failed for: '%s'\n", d->file);
0358         return -1;
0359     }
0360     d->start = dlsym(d->handle, "start");
0361     d->filter_event = dlsym(d->handle, "filter_event");
0362     d->filter_event_early = dlsym(d->handle, "filter_event_early");
0363     d->stop = dlsym(d->handle, "stop");
0364     d->fns = dlsym(d->handle, "perf_dlfilter_fns");
0365     if (d->fns)
0366         memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns));
0367     return 0;
0368 }
0369 
0370 static int dlfilter__close(struct dlfilter *d)
0371 {
0372     return dlclose(d->handle);
0373 }
0374 
0375 struct dlfilter *dlfilter__new(const char *file, int dlargc, char **dlargv)
0376 {
0377     struct dlfilter *d = malloc(sizeof(*d));
0378 
0379     if (!d)
0380         return NULL;
0381 
0382     if (dlfilter__init(d, file, dlargc, dlargv))
0383         goto err_free;
0384 
0385     if (dlfilter__open(d))
0386         goto err_exit;
0387 
0388     return d;
0389 
0390 err_exit:
0391     dlfilter__exit(d);
0392 err_free:
0393     free(d);
0394     return NULL;
0395 }
0396 
0397 static void dlfilter__free(struct dlfilter *d)
0398 {
0399     if (d) {
0400         dlfilter__exit(d);
0401         free(d);
0402     }
0403 }
0404 
0405 int dlfilter__start(struct dlfilter *d, struct perf_session *session)
0406 {
0407     if (d) {
0408         d->session = session;
0409         if (d->start) {
0410             int ret;
0411 
0412             d->in_start = true;
0413             ret = d->start(&d->data, d);
0414             d->in_start = false;
0415             return ret;
0416         }
0417     }
0418     return 0;
0419 }
0420 
0421 static int dlfilter__stop(struct dlfilter *d)
0422 {
0423     if (d && d->stop) {
0424         int ret;
0425 
0426         d->in_stop = true;
0427         ret = d->stop(d->data, d);
0428         d->in_stop = false;
0429         return ret;
0430     }
0431     return 0;
0432 }
0433 
0434 void dlfilter__cleanup(struct dlfilter *d)
0435 {
0436     if (d) {
0437         dlfilter__stop(d);
0438         dlfilter__close(d);
0439         dlfilter__free(d);
0440     }
0441 }
0442 
0443 #define ASSIGN(x) d_sample.x = sample->x
0444 
0445 int dlfilter__do_filter_event(struct dlfilter *d,
0446                   union perf_event *event,
0447                   struct perf_sample *sample,
0448                   struct evsel *evsel,
0449                   struct machine *machine,
0450                   struct addr_location *al,
0451                   struct addr_location *addr_al,
0452                   bool early)
0453 {
0454     struct perf_dlfilter_sample d_sample;
0455     struct perf_dlfilter_al d_ip_al;
0456     struct perf_dlfilter_al d_addr_al;
0457     int ret;
0458 
0459     d->event       = event;
0460     d->sample      = sample;
0461     d->evsel       = evsel;
0462     d->machine     = machine;
0463     d->al          = al;
0464     d->addr_al     = addr_al;
0465     d->d_sample    = &d_sample;
0466     d->d_ip_al     = &d_ip_al;
0467     d->d_addr_al   = &d_addr_al;
0468 
0469     d_sample.size  = sizeof(d_sample);
0470     d_ip_al.size   = 0; /* To indicate d_ip_al is not initialized */
0471     d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */
0472 
0473     ASSIGN(ip);
0474     ASSIGN(pid);
0475     ASSIGN(tid);
0476     ASSIGN(time);
0477     ASSIGN(addr);
0478     ASSIGN(id);
0479     ASSIGN(stream_id);
0480     ASSIGN(period);
0481     ASSIGN(weight);
0482     ASSIGN(ins_lat);
0483     ASSIGN(p_stage_cyc);
0484     ASSIGN(transaction);
0485     ASSIGN(insn_cnt);
0486     ASSIGN(cyc_cnt);
0487     ASSIGN(cpu);
0488     ASSIGN(flags);
0489     ASSIGN(data_src);
0490     ASSIGN(phys_addr);
0491     ASSIGN(data_page_size);
0492     ASSIGN(code_page_size);
0493     ASSIGN(cgroup);
0494     ASSIGN(cpumode);
0495     ASSIGN(misc);
0496     ASSIGN(raw_size);
0497     ASSIGN(raw_data);
0498     ASSIGN(machine_pid);
0499     ASSIGN(vcpu);
0500 
0501     if (sample->branch_stack) {
0502         d_sample.brstack_nr = sample->branch_stack->nr;
0503         d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample);
0504     } else {
0505         d_sample.brstack_nr = 0;
0506         d_sample.brstack = NULL;
0507     }
0508 
0509     if (sample->callchain) {
0510         d_sample.raw_callchain_nr = sample->callchain->nr;
0511         d_sample.raw_callchain = (__u64 *)sample->callchain->ips;
0512     } else {
0513         d_sample.raw_callchain_nr = 0;
0514         d_sample.raw_callchain = NULL;
0515     }
0516 
0517     d_sample.addr_correlates_sym =
0518         (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) &&
0519         sample_addr_correlates_sym(&evsel->core.attr);
0520 
0521     d_sample.event = evsel__name(evsel);
0522 
0523     d->ctx_valid = true;
0524 
0525     if (early)
0526         ret = d->filter_event_early(d->data, &d_sample, d);
0527     else
0528         ret = d->filter_event(d->data, &d_sample, d);
0529 
0530     d->ctx_valid = false;
0531 
0532     return ret;
0533 }
0534 
0535 bool get_filter_desc(const char *dirname, const char *name, char **desc,
0536              char **long_desc)
0537 {
0538     char path[PATH_MAX];
0539     void *handle;
0540     const char *(*desc_fn)(const char **long_description);
0541 
0542     snprintf(path, sizeof(path), "%s/%s", dirname, name);
0543     handle = dlopen(path, RTLD_NOW);
0544     if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early")))
0545         return false;
0546     desc_fn = dlsym(handle, "filter_description");
0547     if (desc_fn) {
0548         const char *dsc;
0549         const char *long_dsc;
0550 
0551         dsc = desc_fn(&long_dsc);
0552         if (dsc)
0553             *desc = strdup(dsc);
0554         if (long_dsc)
0555             *long_desc = strdup(long_dsc);
0556     }
0557     dlclose(handle);
0558     return true;
0559 }
0560 
0561 static void list_filters(const char *dirname)
0562 {
0563     struct dirent *entry;
0564     DIR *dir;
0565 
0566     dir = opendir(dirname);
0567     if (!dir)
0568         return;
0569 
0570     while ((entry = readdir(dir)) != NULL)
0571     {
0572         size_t n = strlen(entry->d_name);
0573         char *long_desc = NULL;
0574         char *desc = NULL;
0575 
0576         if (entry->d_type == DT_DIR || n < 4 ||
0577             strcmp(".so", entry->d_name + n - 3))
0578             continue;
0579         if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc))
0580             continue;
0581         printf("  %-36s %s\n", entry->d_name, desc ? desc : "");
0582         if (verbose) {
0583             char *p = long_desc;
0584             char *line;
0585 
0586             while ((line = strsep(&p, "\n")) != NULL)
0587                 printf("%39s%s\n", "", line);
0588         }
0589         free(long_desc);
0590         free(desc);
0591     }
0592 
0593     closedir(dir);
0594 }
0595 
0596 int list_available_dlfilters(const struct option *opt __maybe_unused,
0597                  const char *s __maybe_unused,
0598                  int unset __maybe_unused)
0599 {
0600     char path[PATH_MAX];
0601     char *exec_path;
0602 
0603     printf("List of available dlfilters:\n");
0604 
0605     list_filters(".");
0606 
0607     exec_path = get_argv_exec_path();
0608     if (!exec_path)
0609         goto out;
0610     snprintf(path, sizeof(path), "%s/dlfilters", exec_path);
0611 
0612     list_filters(path);
0613 
0614     free(exec_path);
0615 out:
0616     exit(0);
0617 }