Messages in this thread | | | Subject | Re: [PATCH RFC 04/11] perf script: Add API for filtering via dynamically loaded shared object | From | Adrian Hunter <> | Date | Wed, 23 Jun 2021 12:25:13 +0300 |
| |
On 22/06/21 9:32 pm, Arnaldo Carvalho de Melo wrote: > Em Mon, Jun 21, 2021 at 06:05:07PM +0300, Adrian Hunter escreveu: >> In some cases, users want to filter very large amounts of data (e.g. from >> AUX area tracing like Intel PT) looking for something specific. While >> scripting such as Python can be used, Python is 10 to 20 times slower than >> C. So define a C API so that custom filters can be written and loaded. > > Statically linking with perf would be even faster 8-) But yeah, I think > its something useful, some notes below > > A first question, can this be combined with pre-existing filters? I.e. > > perf script -C 0 --dlfilter file.so > > ?
Thanks for looking at the patches.
Yes. The filter_event() callback is called after internal filtering. Patch 5 introduces filter_event_early() which is called before internal filtering, but in either case all internal filtering is still done.
> > Also in the docs we can state that after these custom filters get > polished and mature, it would be interesting to send them to be merged > upstream, where they could even be statically linked with perf, if a > profile session with it linked statically proves that. > > Having a collection of such filters upstream we could then have > a similar interface as for the script collection to describe filters, > i.e. the .so file would have to have a description and perhaps a man > page: > > ⬢[acme@toolbox perf]$ perf script -l > List of available trace scripts: > compaction-times [-h] [-u] [-p|-pv] [-t | [-m] [-fs] [-ms]] [pid|pid-range|comm-regex] display time taken by mm compaction > event_analyzing_sample analyze all perf samples > export-to-postgresql [database name] [columns] [calls] export perf data to a postgresql database > export-to-sqlite [database name] [columns] [calls] export perf data to a sqlite3 database > failed-syscalls-by-pid [comm] system-wide failed syscalls, by pid > flamegraph create flame graphs > futex-contention futext contention measurement > intel-pt-events print Intel PT Events including Power Events and PTWRITE > mem-phys-addr resolve physical address samples > net_dropmonitor display a table of dropped frames > netdev-times [tx] [rx] [dev=] [debug] display a process of packet and processing time > powerpc-hcalls > sched-migration sched migration overview > sctop [comm] [interval] syscall top > stackcollapse produce callgraphs in short form for scripting use > syscall-counts-by-pid [comm] system-wide syscall counts, by pid > syscall-counts [comm] system-wide syscall counts > failed-syscalls [comm] system-wide failed syscalls > rw-by-file <comm> r/w activity for a program, by file > rw-by-pid system-wide r/w activity > rwtop [interval] system-wide r/w top > wakeup-latency system-wide min/max/avg wakeup latency > ⬢[acme@toolbox perf]$ > > I.e. some 'perf script --list-dlfilters'
Yes but we would also need some way to store and retrieve a description of the filter otherwise all we have is the file name. I guess it could be another API function so the filter .so could be loaded and the "const char* filter_description(const char **title)" function called to get the description.
> >> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> >> --- >> tools/perf/Documentation/perf-dlfilter.txt | 214 +++++++++++++ >> tools/perf/Documentation/perf-script.txt | 7 +- >> tools/perf/builtin-script.c | 25 +- >> tools/perf/util/Build | 1 + >> tools/perf/util/dlfilter.c | 330 +++++++++++++++++++++ >> tools/perf/util/dlfilter.h | 74 +++++ >> tools/perf/util/perf_dlfilter.h | 120 ++++++++ >> 7 files changed, 769 insertions(+), 2 deletions(-) >> create mode 100644 tools/perf/Documentation/perf-dlfilter.txt >> create mode 100644 tools/perf/util/dlfilter.c >> create mode 100644 tools/perf/util/dlfilter.h >> create mode 100644 tools/perf/util/perf_dlfilter.h >> >> diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt >> new file mode 100644 >> index 000000000000..d8f80998790f >> --- /dev/null >> +++ b/tools/perf/Documentation/perf-dlfilter.txt >> @@ -0,0 +1,214 @@ >> +perf-dlfilter(1) >> +================ >> + >> +NAME >> +---- >> +perf-dlfilter - Filter sample events using a dynamically loaded shared >> +object file >> + >> +SYNOPSIS >> +-------- >> +[verse] >> +'perf script' [--dlfilter file.so ] >> + >> +DESCRIPTION >> +----------- >> + >> +This option is used to process data through a custom filter provided by a >> +dynamically loaded shared object file. >> + >> +API >> +--- >> + >> +The API for filtering consists of the following: >> + >> +[source,c] >> +---- >> +#include <perf/perf_dlfilter.h> >> + >> +const struct perf_dlfilter_fns perf_dlfilter_fns; >> + >> +int start(void **data); >> +int stop(void *data); >> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx); >> +---- >> + >> +If implemented, 'start' will be called at the beginning, before any >> +calls to 'filter_event' . Return 0 to indicate success, >> +or return a negative error code. '*data' can be assigned for use by other >> +functions. >> + >> +If implemented, 'stop' will be called at the end, after any calls to >> +'filter_event'. Return 0 to indicate success, or >> +return a negative error code. 'data' is set by 'start'. >> + >> +If implemented, 'filter_event' will be called for each sample event. >> +Return 0 to keep the sample event, 1 to filter it out, or return a negative >> +error code. 'data' is set by 'start'. 'ctx' is needed for calls to >> +'perf_dlfilter_fns'. >> + >> +The perf_dlfilter_sample structure >> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> + >> +'filter_event' is passed a perf_dlfilter_sample >> +structure, which contains the following fields: >> +[source,c] >> +---- >> +/* >> + * perf sample event information (as per perf script and <linux/perf_event.h>) >> + */ >> +struct perf_dlfilter_sample { >> + __u32 size; /* Size of this structure (for compatibility checking) */ > > There is a 4-byte hole here, > >> + __u64 ip; >> + __s32 pid; >> + __s32 tid; >> + __u64 time; >> + __u64 addr; >> + __u64 id; >> + __u64 stream_id; >> + __u64 period; >> + __u64 weight; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ >> + __u16 ins_lat; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ >> + __u16 p_stage_cyc; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ > > Another > > Can you move these last two __u16 to right after the 'size' field?
Sure
> >> + __u64 transaction; /* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */ >> + __u64 insn_cnt; /* For instructions-per-cycle (IPC) */ >> + __u64 cyc_cnt; /* For instructions-per-cycle (IPC) */ >> + __s32 cpu; >> + __u32 flags; /* Refer PERF_DLFILTER_FLAG_* above */ >> + __u64 data_src; /* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */ >> + __u64 phys_addr; /* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */ >> + __u64 data_page_size; /* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */ >> + __u64 code_page_size; /* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */ >> + __u64 cgroup; /* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */ >> + __u8 cpumode; /* Refer CPUMODE_MASK etc in <linux/perf_event.h> */ >> + __u8 addr_correlates_sym; /* True => resolve_addr() can be called */ >> + __u16 misc; /* Refer perf_event_header in <linux/perf_event.h> */ >> + __u32 raw_size; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ >> + const void *raw_data; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ >> + __u64 brstack_nr; /* Number of brstack entries */ >> + const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */ >> + __u64 raw_callchain_nr; /* Number of raw_callchain entries */ >> + const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */ >> + const char *event; >> +}; >> +---- >> + >> +The perf_dlfilter_fns structure >> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> + >> +The 'perf_dlfilter_fns' structure is populated with function pointers when the >> +file is loaded. The functions can be called by 'filter_event'. >> + >> +[source,c] >> +---- >> +struct perf_dlfilter_fns { >> + const struct perf_dlfilter_al *(*resolve_ip)(void *ctx); >> + const struct perf_dlfilter_al *(*resolve_addr)(void *ctx); >> + void *(*reserved[126])(void *); >> +}; >> +---- >> + >> +'resolve_ip' returns information about ip. >> + >> +'resolve_addr' returns information about addr (if addr_correlates_sym). >> + >> +The perf_dlfilter_al structure >> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> + >> +The 'perf_dlfilter_al' structure contains information about an address. >> + >> +[source,c] >> +---- >> +/* >> + * Address location (as per perf script) >> + */ >> +struct perf_dlfilter_al { >> + __u32 size; /* Size of this structure (for compatibility checking) */ >> + __u32 symoff; >> + const char *sym; >> + __u64 addr; /* Mapped address (from dso) */ >> + __u64 sym_start; >> + __u64 sym_end; >> + const char *dso; >> + __u8 sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */ >> + __u8 is_64_bit; /* Only valid if dso is not NULL */ >> + __u8 is_kernel_ip; /* True if in kernel space */ >> + __u32 buildid_size; >> + __u8 *buildid; >> + /* Below members are only populated by resolve_ip() */ >> + __u8 filtered; /* true if this sample event will be filtered out */ >> + const char *comm; >> +}; >> +---- >> + >> +perf_dlfilter_sample flags >> +~~~~~~~~~~~~~~~~~~~~~~~~~~ >> + >> +The 'flags' member of 'perf_dlfilter_sample' corresponds with the flags field >> +of perf script. The bits of the flags are as follows: >> + >> +[source,c] >> +---- >> +/* Definitions for perf_dlfilter_sample flags */ >> +enum { >> + PERF_DLFILTER_FLAG_BRANCH = 1ULL << 0, >> + PERF_DLFILTER_FLAG_CALL = 1ULL << 1, >> + PERF_DLFILTER_FLAG_RETURN = 1ULL << 2, >> + PERF_DLFILTER_FLAG_CONDITIONAL = 1ULL << 3, >> + PERF_DLFILTER_FLAG_SYSCALLRET = 1ULL << 4, >> + PERF_DLFILTER_FLAG_ASYNC = 1ULL << 5, >> + PERF_DLFILTER_FLAG_INTERRUPT = 1ULL << 6, >> + PERF_DLFILTER_FLAG_TX_ABORT = 1ULL << 7, >> + PERF_DLFILTER_FLAG_TRACE_BEGIN = 1ULL << 8, >> + PERF_DLFILTER_FLAG_TRACE_END = 1ULL << 9, >> + PERF_DLFILTER_FLAG_IN_TX = 1ULL << 10, >> + PERF_DLFILTER_FLAG_VMENTRY = 1ULL << 11, >> + PERF_DLFILTER_FLAG_VMEXIT = 1ULL << 12, >> +}; >> +---- >> + >> +EXAMPLE >> +------- >> + >> +Filter out everything except branches from "foo" to "bar": >> + >> +[source,c] >> +---- >> +#include <perf/perf_dlfilter.h> >> +#include <string.h> >> + >> +const struct perf_dlfilter_fns perf_dlfilter_fns; >> + >> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx) >> +{ >> + const struct perf_dlfilter_al *al; >> + const struct perf_dlfilter_al *addr_al; >> + >> + if (!sample->ip || !sample->addr_correlates_sym) >> + return 1; >> + >> + al = perf_dlfilter_fns.resolve_ip(ctx); >> + if (!al || !al->sym || strcmp(al->sym, "foo")) >> + return 1; >> + >> + addr_al = perf_dlfilter_fns.resolve_addr(ctx); >> + if (!addr_al || !addr_al->sym || strcmp(addr_al->sym, "bar")) >> + return 1; >> + >> + return 0; >> +} >> +---- >> + >> +To build the shared object, assuming perf has been installed for the local user >> +i.e. perf_dlfilter.h is in ~/include/perf : >> + >> + gcc -c -I ~/include -fpic dlfilter-example.c >> + gcc -shared -o dlfilter-example.so dlfilter-example.o >> + >> +To use the filter with perf script: >> + >> + perf script --dlfilter ./dlfilter-example.so >> + >> +SEE ALSO >> +-------- >> +linkperf:perf-script[1] >> diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt >> index 48a5f5b26dd4..2306c81b606b 100644 >> --- a/tools/perf/Documentation/perf-script.txt >> +++ b/tools/perf/Documentation/perf-script.txt >> @@ -98,6 +98,10 @@ OPTIONS >> Generate perf-script.[ext] starter script for given language, >> using current perf.data. >> >> +--dlfilter=<file>:: >> + Filter sample events using the given shared object file. >> + Refer linkperf:perf-dlfilter[1] >> + >> -a:: >> Force system-wide collection. Scripts run without a <command> >> normally use -a by default, while scripts run with a <command> >> @@ -483,4 +487,5 @@ include::itrace.txt[] >> SEE ALSO >> -------- >> linkperf:perf-record[1], linkperf:perf-script-perl[1], >> -linkperf:perf-script-python[1], linkperf:perf-intel-pt[1] >> +linkperf:perf-script-python[1], linkperf:perf-intel-pt[1], >> +linkperf:perf-dlfilter[1] >> diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c >> index d2771a997e26..aaf2922643a0 100644 >> --- a/tools/perf/builtin-script.c >> +++ b/tools/perf/builtin-script.c >> @@ -55,6 +55,7 @@ >> #include <subcmd/pager.h> >> #include <perf/evlist.h> >> #include <linux/err.h> >> +#include "util/dlfilter.h" >> #include "util/record.h" >> #include "util/util.h" >> #include "perf.h" >> @@ -79,6 +80,7 @@ static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); >> static struct perf_stat_config stat_config; >> static int max_blocks; >> static bool native_arch; >> +static struct dlfilter *dlfilter; >> >> unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; >> >> @@ -2175,6 +2177,7 @@ static int process_sample_event(struct perf_tool *tool, >> struct perf_script *scr = container_of(tool, struct perf_script, tool); >> struct addr_location al; >> struct addr_location addr_al; >> + int ret = 0; >> >> if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num, >> sample->time)) { >> @@ -2213,6 +2216,13 @@ static int process_sample_event(struct perf_tool *tool, >> if (evswitch__discard(&scr->evswitch, evsel)) >> goto out_put; >> >> + ret = dlfilter__filter_event(dlfilter, event, sample, evsel, machine, &al, &addr_al); >> + if (ret) { >> + if (ret > 0) >> + ret = 0; >> + goto out_put; >> + } >> + >> if (scripting_ops) { >> struct addr_location *addr_al_ptr = NULL; >> >> @@ -2229,7 +2239,7 @@ static int process_sample_event(struct perf_tool *tool, >> >> out_put: >> addr_location__put(&al); >> - return 0; >> + return ret; >> } >> >> static int process_attr(struct perf_tool *tool, union perf_event *event, >> @@ -3568,6 +3578,7 @@ int cmd_script(int argc, const char **argv) >> }; >> struct utsname uts; >> char *script_path = NULL; >> + const char *dlfilter_file = NULL; >> const char **__argv; >> int i, j, err = 0; >> struct perf_script script = { >> @@ -3615,6 +3626,7 @@ int cmd_script(int argc, const char **argv) >> parse_scriptname), >> OPT_STRING('g', "gen-script", &generate_script_lang, "lang", >> "generate perf-script.xx script in specified language"), >> + OPT_STRING(0, "dlfilter", &dlfilter_file, "file", "filter .so file name"), >> OPT_STRING('i', "input", &input_name, "file", "input file name"), >> OPT_BOOLEAN('d', "debug-mode", &debug_mode, >> "do various checks like samples ordering and lost events"), >> @@ -3933,6 +3945,12 @@ int cmd_script(int argc, const char **argv) >> exit(-1); >> } >> >> + if (dlfilter_file) { >> + dlfilter = dlfilter__new(dlfilter_file); >> + if (!dlfilter) >> + return -1; >> + } >> + >> if (!script_name) { >> setup_pager(); >> use_browser = 0; >> @@ -4032,6 +4050,10 @@ int cmd_script(int argc, const char **argv) >> goto out_delete; >> } >> >> + err = dlfilter__start(dlfilter, session); >> + if (err) >> + goto out_delete; >> + >> if (script_name) { >> err = scripting_ops->start_script(script_name, argc, argv, session); >> if (err) >> @@ -4081,6 +4103,7 @@ int cmd_script(int argc, const char **argv) >> >> if (script_started) >> cleanup_scripting(); >> + dlfilter__cleanup(dlfilter); >> out: >> return err; >> } >> diff --git a/tools/perf/util/Build b/tools/perf/util/Build >> index 95e15d1035ab..1a909b53dc15 100644 >> --- a/tools/perf/util/Build >> +++ b/tools/perf/util/Build >> @@ -126,6 +126,7 @@ perf-y += parse-regs-options.o >> perf-y += parse-sublevel-options.o >> perf-y += term.o >> perf-y += help-unknown-cmd.o >> +perf-y += dlfilter.o >> perf-y += mem-events.o >> perf-y += vsprintf.o >> perf-y += units.o >> diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c >> new file mode 100644 >> index 000000000000..15cb9de13a4b >> --- /dev/null >> +++ b/tools/perf/util/dlfilter.c >> @@ -0,0 +1,330 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * dlfilter.c: Interface to perf script --dlfilter shared object >> + * Copyright (c) 2021, Intel Corporation. >> + */ >> +#include <dlfcn.h> >> +#include <stdlib.h> >> +#include <string.h> >> +#include <linux/zalloc.h> >> +#include <linux/build_bug.h> >> + >> +#include "debug.h" >> +#include "event.h" >> +#include "evsel.h" >> +#include "dso.h" >> +#include "map.h" >> +#include "thread.h" >> +#include "symbol.h" >> +#include "dlfilter.h" >> +#include "perf_dlfilter.h" >> + >> +static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al) >> +{ >> + struct symbol *sym = al->sym; >> + >> + d_al->size = sizeof(*d_al); >> + if (al->map) { >> + struct dso *dso = al->map->dso; >> + >> + if (symbol_conf.show_kernel_path && dso->long_name) >> + d_al->dso = dso->long_name; >> + else >> + d_al->dso = dso->name; >> + d_al->is_64_bit = dso->is_64_bit; >> + d_al->buildid_size = dso->bid.size; >> + d_al->buildid = dso->bid.data; >> + } else { >> + d_al->dso = NULL; >> + d_al->is_64_bit = 0; >> + d_al->buildid_size = 0; >> + d_al->buildid = NULL; >> + } >> + if (sym) { >> + d_al->sym = sym->name; >> + d_al->sym_start = sym->start; >> + d_al->sym_end = sym->end; >> + if (al->addr < sym->end) >> + d_al->symoff = al->addr - sym->start; >> + else >> + d_al->symoff = al->addr - al->map->start - sym->start; >> + d_al->sym_binding = sym->binding; >> + } else { >> + d_al->sym = NULL; >> + d_al->sym_start = 0; >> + d_al->sym_end = 0; >> + d_al->symoff = 0; >> + d_al->sym_binding = 0; >> + } >> + d_al->addr = al->addr; >> + d_al->comm = NULL; >> + d_al->filtered = 0; >> +} >> + >> +static struct addr_location *get_al(struct dlfilter *d) >> +{ >> + struct addr_location *al = d->al; >> + >> + if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0) >> + return NULL; >> + return al; >> +} >> + >> +static struct thread *get_thread(struct dlfilter *d) >> +{ >> + struct addr_location *al = get_al(d); >> + >> + return al ? al->thread : NULL; >> +} >> + >> +static const struct perf_dlfilter_al *dlfilter__resolve_ip(void *ctx) >> +{ >> + struct dlfilter *d = (struct dlfilter *)ctx; >> + struct perf_dlfilter_al *d_al = d->d_ip_al; >> + struct addr_location *al; >> + >> + if (!d->ctx_valid) >> + return NULL; >> + >> + /* 'size' is also used to indicate already initialized */ >> + if (d_al->size) >> + return d_al; >> + >> + al = get_al(d); >> + if (!al) >> + return NULL; >> + >> + al_to_d_al(al, d_al); >> + >> + d_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->ip); >> + d_al->comm = al->thread ? thread__comm_str(al->thread) : ":-1"; >> + d_al->filtered = al->filtered; >> + >> + return d_al; >> +} >> + >> +static const struct perf_dlfilter_al *dlfilter__resolve_addr(void *ctx) >> +{ >> + struct dlfilter *d = (struct dlfilter *)ctx; >> + struct perf_dlfilter_al *d_addr_al = d->d_addr_al; >> + struct addr_location *addr_al = d->addr_al; >> + >> + if (!d->ctx_valid || !d->d_sample->addr_correlates_sym) >> + return NULL; >> + >> + /* 'size' is also used to indicate already initialized */ >> + if (d_addr_al->size) >> + return d_addr_al; >> + >> + if (!addr_al->thread) { >> + struct thread *thread = get_thread(d); >> + >> + if (!thread) >> + return NULL; >> + thread__resolve(thread, addr_al, d->sample); >> + } >> + >> + al_to_d_al(addr_al, d_addr_al); >> + >> + d_addr_al->is_kernel_ip = machine__kernel_ip(d->machine, d->sample->addr); >> + >> + return d_addr_al; >> +} >> + >> +static const struct perf_dlfilter_fns perf_dlfilter_fns = { >> + .resolve_ip = dlfilter__resolve_ip, >> + .resolve_addr = dlfilter__resolve_addr, >> +}; >> + >> +#define CHECK_FLAG(x) BUILD_BUG_ON((u64)PERF_DLFILTER_FLAG_ ## x != (u64)PERF_IP_FLAG_ ## x) >> + >> +static int dlfilter__init(struct dlfilter *d, const char *file) >> +{ >> + CHECK_FLAG(BRANCH); >> + CHECK_FLAG(CALL); >> + CHECK_FLAG(RETURN); >> + CHECK_FLAG(CONDITIONAL); >> + CHECK_FLAG(SYSCALLRET); >> + CHECK_FLAG(ASYNC); >> + CHECK_FLAG(INTERRUPT); >> + CHECK_FLAG(TX_ABORT); >> + CHECK_FLAG(TRACE_BEGIN); >> + CHECK_FLAG(TRACE_END); >> + CHECK_FLAG(IN_TX); >> + CHECK_FLAG(VMENTRY); >> + CHECK_FLAG(VMEXIT); >> + >> + memset(d, 0, sizeof(*d)); >> + d->file = strdup(file); >> + if (!d->file) >> + return -1; >> + return 0; >> +} >> + >> +static void dlfilter__exit(struct dlfilter *d) >> +{ >> + zfree(&d->file); >> +} >> + >> +static int dlfilter__open(struct dlfilter *d) >> +{ >> + d->handle = dlopen(d->file, RTLD_NOW); >> + if (!d->handle) { >> + pr_err("dlopen failed for: '%s'\n", d->file); >> + return -1; >> + } >> + d->start = dlsym(d->handle, "start"); >> + d->filter_event = dlsym(d->handle, "filter_event"); >> + d->stop = dlsym(d->handle, "stop"); >> + d->fns = dlsym(d->handle, "perf_dlfilter_fns"); >> + if (d->fns) >> + memcpy(d->fns, &perf_dlfilter_fns, sizeof(struct perf_dlfilter_fns)); >> + return 0; >> +} >> + >> +static int dlfilter__close(struct dlfilter *d) >> +{ >> + return dlclose(d->handle); >> +} >> + >> +struct dlfilter *dlfilter__new(const char *file) >> +{ >> + struct dlfilter *d = malloc(sizeof(*d)); >> + >> + if (!d) >> + return NULL; >> + >> + if (dlfilter__init(d, file)) >> + goto err_free; >> + >> + if (dlfilter__open(d)) >> + goto err_exit; >> + >> + return d; >> + >> +err_exit: >> + dlfilter__exit(d); >> +err_free: >> + free(d); >> + return NULL; >> +} >> + >> +static void dlfilter__free(struct dlfilter *d) >> +{ >> + if (d) { >> + dlfilter__exit(d); >> + free(d); >> + } >> +} >> + >> +int dlfilter__start(struct dlfilter *d, struct perf_session *session) >> +{ >> + if (d) { >> + d->session = session; >> + if (d->start) >> + return d->start(&d->data); >> + } >> + return 0; >> +} >> + >> +static int dlfilter__stop(struct dlfilter *d) >> +{ >> + if (d && d->stop) >> + return d->stop(d->data); >> + return 0; >> +} >> + >> +void dlfilter__cleanup(struct dlfilter *d) >> +{ >> + if (d) { >> + dlfilter__stop(d); >> + dlfilter__close(d); >> + dlfilter__free(d); >> + } >> +} >> + >> +#define ASSIGN(x) d_sample.x = sample->x >> + >> +int dlfilter__do_filter_event(struct dlfilter *d, >> + union perf_event *event, >> + struct perf_sample *sample, >> + struct evsel *evsel, >> + struct machine *machine, >> + struct addr_location *al, >> + struct addr_location *addr_al) >> +{ >> + struct perf_dlfilter_sample d_sample; >> + struct perf_dlfilter_al d_ip_al; >> + struct perf_dlfilter_al d_addr_al; >> + int ret; >> + >> + d->event = event; >> + d->sample = sample; >> + d->evsel = evsel; >> + d->machine = machine; >> + d->al = al; >> + d->addr_al = addr_al; >> + d->d_sample = &d_sample; >> + d->d_ip_al = &d_ip_al; >> + d->d_addr_al = &d_addr_al; >> + >> + d_sample.size = sizeof(d_sample); >> + d_ip_al.size = 0; /* To indicate d_ip_al is not initialized */ >> + d_addr_al.size = 0; /* To indicate d_addr_al is not initialized */ >> + >> + ASSIGN(ip); >> + ASSIGN(pid); >> + ASSIGN(tid); >> + ASSIGN(time); >> + ASSIGN(addr); >> + ASSIGN(id); >> + ASSIGN(stream_id); >> + ASSIGN(period); >> + ASSIGN(weight); >> + ASSIGN(ins_lat); >> + ASSIGN(p_stage_cyc); >> + ASSIGN(transaction); >> + ASSIGN(insn_cnt); >> + ASSIGN(cyc_cnt); >> + ASSIGN(cpu); >> + ASSIGN(flags); >> + ASSIGN(data_src); >> + ASSIGN(phys_addr); >> + ASSIGN(data_page_size); >> + ASSIGN(code_page_size); >> + ASSIGN(cgroup); >> + ASSIGN(cpumode); >> + ASSIGN(misc); >> + ASSIGN(raw_size); >> + ASSIGN(raw_data); >> + >> + if (sample->branch_stack) { >> + d_sample.brstack_nr = sample->branch_stack->nr; >> + d_sample.brstack = (struct perf_branch_entry *)perf_sample__branch_entries(sample); >> + } else { >> + d_sample.brstack_nr = 0; >> + d_sample.brstack = NULL; >> + } >> + >> + if (sample->callchain) { >> + d_sample.raw_callchain_nr = sample->callchain->nr; >> + d_sample.raw_callchain = (__u64 *)sample->callchain->ips; >> + } else { >> + d_sample.raw_callchain_nr = 0; >> + d_sample.raw_callchain = NULL; >> + } >> + >> + d_sample.addr_correlates_sym = >> + (evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) && >> + sample_addr_correlates_sym(&evsel->core.attr); >> + >> + d_sample.event = evsel__name(evsel); >> + >> + d->ctx_valid = true; >> + >> + ret = d->filter_event(d->data, &d_sample, d); >> + >> + d->ctx_valid = false; >> + >> + return ret; >> +} >> diff --git a/tools/perf/util/dlfilter.h b/tools/perf/util/dlfilter.h >> new file mode 100644 >> index 000000000000..671e2d3d5a06 >> --- /dev/null >> +++ b/tools/perf/util/dlfilter.h >> @@ -0,0 +1,74 @@ >> +/* SPDX-License-Identifier: GPL-2.0 */ >> +/* >> + * dlfilter.h: Interface to perf script --dlfilter shared object >> + * Copyright (c) 2021, Intel Corporation. >> + */ >> + >> +#ifndef PERF_UTIL_DLFILTER_H >> +#define PERF_UTIL_DLFILTER_H >> + >> +struct perf_session; >> +union perf_event; >> +struct perf_sample; >> +struct evsel; >> +struct machine; >> +struct addr_location; >> +struct perf_dlfilter_fns; >> +struct perf_dlfilter_sample; >> +struct perf_dlfilter_al; >> + >> +struct dlfilter { >> + char *file; >> + void *handle; >> + void *data; >> + struct perf_session *session; >> + bool ctx_valid; >> + >> + union perf_event *event; >> + struct perf_sample *sample; >> + struct evsel *evsel; >> + struct machine *machine; >> + struct addr_location *al; >> + struct addr_location *addr_al; >> + struct perf_dlfilter_sample *d_sample; >> + struct perf_dlfilter_al *d_ip_al; >> + struct perf_dlfilter_al *d_addr_al; >> + >> + int (*start)(void **data); >> + int (*stop)(void *data); >> + >> + int (*filter_event)(void *data, >> + const struct perf_dlfilter_sample *sample, >> + void *ctx); >> + >> + struct perf_dlfilter_fns *fns; >> +}; >> + >> +struct dlfilter *dlfilter__new(const char *file); >> + >> +int dlfilter__start(struct dlfilter *d, struct perf_session *session); >> + >> +int dlfilter__do_filter_event(struct dlfilter *d, >> + union perf_event *event, >> + struct perf_sample *sample, >> + struct evsel *evsel, >> + struct machine *machine, >> + struct addr_location *al, >> + struct addr_location *addr_al); >> + >> +void dlfilter__cleanup(struct dlfilter *d); >> + >> +static inline int dlfilter__filter_event(struct dlfilter *d, >> + union perf_event *event, >> + struct perf_sample *sample, >> + struct evsel *evsel, >> + struct machine *machine, >> + struct addr_location *al, >> + struct addr_location *addr_al) >> +{ >> + if (!d || !d->filter_event) >> + return 0; >> + return dlfilter__do_filter_event(d, event, sample, evsel, machine, al, addr_al); >> +} >> + >> +#endif >> diff --git a/tools/perf/util/perf_dlfilter.h b/tools/perf/util/perf_dlfilter.h >> new file mode 100644 >> index 000000000000..132f833f0a0b >> --- /dev/null >> +++ b/tools/perf/util/perf_dlfilter.h >> @@ -0,0 +1,120 @@ >> +/* SPDX-License-Identifier: GPL-2.0 */ >> +/* >> + * perf_dlfilter.h: API for perf --dlfilter shared object >> + * Copyright (c) 2021, Intel Corporation. >> + */ >> +#ifndef _LINUX_PERF_DLFILTER_H >> +#define _LINUX_PERF_DLFILTER_H >> + >> +#include <linux/perf_event.h> >> +#include <linux/types.h> >> + >> +/* Definitions for perf_dlfilter_sample flags */ >> +enum { >> + PERF_DLFILTER_FLAG_BRANCH = 1ULL << 0, >> + PERF_DLFILTER_FLAG_CALL = 1ULL << 1, >> + PERF_DLFILTER_FLAG_RETURN = 1ULL << 2, >> + PERF_DLFILTER_FLAG_CONDITIONAL = 1ULL << 3, >> + PERF_DLFILTER_FLAG_SYSCALLRET = 1ULL << 4, >> + PERF_DLFILTER_FLAG_ASYNC = 1ULL << 5, >> + PERF_DLFILTER_FLAG_INTERRUPT = 1ULL << 6, >> + PERF_DLFILTER_FLAG_TX_ABORT = 1ULL << 7, >> + PERF_DLFILTER_FLAG_TRACE_BEGIN = 1ULL << 8, >> + PERF_DLFILTER_FLAG_TRACE_END = 1ULL << 9, >> + PERF_DLFILTER_FLAG_IN_TX = 1ULL << 10, >> + PERF_DLFILTER_FLAG_VMENTRY = 1ULL << 11, >> + PERF_DLFILTER_FLAG_VMEXIT = 1ULL << 12, >> +}; >> + >> +/* >> + * perf sample event information (as per perf script and <linux/perf_event.h>) >> + */ >> +struct perf_dlfilter_sample { >> + __u32 size; /* Size of this structure (for compatibility checking) */ >> + __u64 ip; >> + __s32 pid; >> + __s32 tid; >> + __u64 time; >> + __u64 addr; >> + __u64 id; >> + __u64 stream_id; >> + __u64 period; >> + __u64 weight; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ >> + __u16 ins_lat; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ >> + __u16 p_stage_cyc; /* Refer PERF_SAMPLE_WEIGHT_TYPE in <linux/perf_event.h> */ >> + __u64 transaction; /* Refer PERF_SAMPLE_TRANSACTION in <linux/perf_event.h> */ >> + __u64 insn_cnt; /* For instructions-per-cycle (IPC) */ >> + __u64 cyc_cnt; /* For instructions-per-cycle (IPC) */ >> + __s32 cpu; >> + __u32 flags; /* Refer PERF_DLFILTER_FLAG_* above */ >> + __u64 data_src; /* Refer PERF_SAMPLE_DATA_SRC in <linux/perf_event.h> */ >> + __u64 phys_addr; /* Refer PERF_SAMPLE_PHYS_ADDR in <linux/perf_event.h> */ >> + __u64 data_page_size; /* Refer PERF_SAMPLE_DATA_PAGE_SIZE in <linux/perf_event.h> */ >> + __u64 code_page_size; /* Refer PERF_SAMPLE_CODE_PAGE_SIZE in <linux/perf_event.h> */ >> + __u64 cgroup; /* Refer PERF_SAMPLE_CGROUP in <linux/perf_event.h> */ >> + __u8 cpumode; /* Refer CPUMODE_MASK etc in <linux/perf_event.h> */ >> + __u8 addr_correlates_sym; /* True => resolve_addr() can be called */ >> + __u16 misc; /* Refer perf_event_header in <linux/perf_event.h> */ >> + __u32 raw_size; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ >> + const void *raw_data; /* Refer PERF_SAMPLE_RAW in <linux/perf_event.h> */ >> + __u64 brstack_nr; /* Number of brstack entries */ >> + const struct perf_branch_entry *brstack; /* Refer <linux/perf_event.h> */ >> + __u64 raw_callchain_nr; /* Number of raw_callchain entries */ >> + const __u64 *raw_callchain; /* Refer <linux/perf_event.h> */ >> + const char *event; >> +}; >> + >> +/* >> + * Address location (as per perf script) >> + */ >> +struct perf_dlfilter_al { >> + __u32 size; /* Size of this structure (for compatibility checking) */ >> + __u32 symoff; >> + const char *sym; >> + __u64 addr; /* Mapped address (from dso) */ >> + __u64 sym_start; >> + __u64 sym_end; >> + const char *dso; >> + __u8 sym_binding; /* STB_LOCAL, STB_GLOBAL or STB_WEAK, refer <elf.h> */ >> + __u8 is_64_bit; /* Only valid if dso is not NULL */ >> + __u8 is_kernel_ip; /* True if in kernel space */ >> + __u32 buildid_size; >> + __u8 *buildid; >> + /* Below members are only populated by resolve_ip() */ >> + __u8 filtered; /* True if this sample event will be filtered out */ >> + const char *comm; >> +}; >> + >> +struct perf_dlfilter_fns { >> + /* Return information about ip */ >> + const struct perf_dlfilter_al *(*resolve_ip)(void *ctx); >> + /* Return information about addr (if addr_correlates_sym) */ >> + const struct perf_dlfilter_al *(*resolve_addr)(void *ctx); >> + /* Reserved */ >> + void *(*reserved[126])(void *); >> +}; >> + >> +/* >> + * If implemented, 'start' will be called at the beginning, >> + * before any calls to 'filter_event'. Return 0 to indicate success, >> + * or return a negative error code. '*data' can be assigned for use >> + * by other functions. >> + */ >> +int start(void **data); >> + >> +/* >> + * If implemented, 'stop' will be called at the end, >> + * after any calls to 'filter_event'. Return 0 to indicate success, or >> + * return a negative error code. 'data' is set by start(). >> + */ >> +int stop(void *data); >> + >> +/* >> + * If implemented, 'filter_event' will be called for each sample >> + * event. Return 0 to keep the sample event, 1 to filter it out, or >> + * return a negative error code. 'data' is set by start(). 'ctx' is >> + * needed for calls to perf_dlfilter_fns. >> + */ >> +int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx); >> + >> +#endif >> -- >> 2.17.1 >> >
| |