From 638e2b9984ee1b8d485366f74d579467880283b4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Sun, 27 Jun 2021 16:18:11 +0300 Subject: [PATCH] perf script: Add option to list dlfilters Add option --list-dlfilters to list dlfilters in the current directory or the exec-path e.g. ~/libexec/perf-core/dlfilters. Use with option -v (must come before option --list-dlfilters) to show long descriptions. Signed-off-by: Adrian Hunter Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20210627131818.810-4-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-dlfilter.txt | 4 + tools/perf/Documentation/perf-script.txt | 4 + tools/perf/builtin-script.c | 2 + tools/perf/util/dlfilter.c | 117 ++++++++++++++++++++++++++++- tools/perf/util/dlfilter.h | 2 + tools/perf/util/perf_dlfilter.h | 6 ++ 6 files changed, 134 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt index aef1c32..8bc219f 100644 --- a/tools/perf/Documentation/perf-dlfilter.txt +++ b/tools/perf/Documentation/perf-dlfilter.txt @@ -37,6 +37,7 @@ int start(void **data, void *ctx); int stop(void *data, void *ctx); int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ctx); int filter_event_early(void *data, const struct perf_dlfilter_sample *sample, void *ctx); +const char *filter_description(const char **long_description); ---- If implemented, 'start' will be called at the beginning, before any @@ -59,6 +60,9 @@ error code. 'data' is set by 'start'. 'ctx' is needed for calls to 'filter_event_early' is the same as 'filter_event' except it is called before internal filtering. +If implemented, 'filter_description' should return a one-line description +of the filter, and optionally a longer description. + The perf_dlfilter_sample structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 2306c81..d2705d6 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -102,6 +102,10 @@ OPTIONS Filter sample events using the given shared object file. Refer linkperf:perf-dlfilter[1] +--list-dlfilters=:: + Display a list of available dlfilters. Use with option -v (must come + before option --list-dlfilters) to show long descriptions. + -a:: Force system-wide collection. Scripts run without a normally use -a by default, while scripts run with a diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index e47affe..4ffba1d 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3631,6 +3631,8 @@ int cmd_script(int argc, const char **argv) "show latency attributes (irqs/preemption disabled, etc)"), OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", list_available_scripts), + OPT_CALLBACK_NOOPT(0, "list-dlfilters", NULL, NULL, "list available dlfilters", + list_available_dlfilters), OPT_CALLBACK('s', "script", NULL, "name", "script file name (lang:script name, script name, or *)", parse_scriptname), diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c index e93c49c..288a2b5 100644 --- a/tools/perf/util/dlfilter.c +++ b/tools/perf/util/dlfilter.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -136,6 +138,35 @@ static const struct perf_dlfilter_fns perf_dlfilter_fns = { .resolve_addr = dlfilter__resolve_addr, }; +static char *find_dlfilter(const char *file) +{ + char path[PATH_MAX]; + char *exec_path; + + if (strchr(file, '/')) + goto out; + + if (!access(file, R_OK)) { + /* + * Prepend "./" so that dlopen will find the file in the + * current directory. + */ + snprintf(path, sizeof(path), "./%s", file); + file = path; + goto out; + } + + exec_path = get_argv_exec_path(); + if (!exec_path) + goto out; + snprintf(path, sizeof(path), "%s/dlfilters/%s", exec_path, file); + free(exec_path); + if (!access(path, R_OK)) + file = path; +out: + return strdup(file); +} + #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) @@ -155,7 +186,7 @@ static int dlfilter__init(struct dlfilter *d, const char *file) CHECK_FLAG(VMEXIT); memset(d, 0, sizeof(*d)); - d->file = strdup(file); + d->file = find_dlfilter(file); if (!d->file) return -1; return 0; @@ -333,3 +364,87 @@ int dlfilter__do_filter_event(struct dlfilter *d, return ret; } + +static bool get_filter_desc(const char *dirname, const char *name, + char **desc, char **long_desc) +{ + char path[PATH_MAX]; + void *handle; + const char *(*desc_fn)(const char **long_description); + + snprintf(path, sizeof(path), "%s/%s", dirname, name); + handle = dlopen(path, RTLD_NOW); + if (!handle || !(dlsym(handle, "filter_event") || dlsym(handle, "filter_event_early"))) + return false; + desc_fn = dlsym(handle, "filter_description"); + if (desc_fn) { + const char *dsc; + const char *long_dsc; + + dsc = desc_fn(&long_dsc); + if (dsc) + *desc = strdup(dsc); + if (long_dsc) + *long_desc = strdup(long_dsc); + } + dlclose(handle); + return true; +} + +static void list_filters(const char *dirname) +{ + struct dirent *entry; + DIR *dir; + + dir = opendir(dirname); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL) + { + size_t n = strlen(entry->d_name); + char *long_desc = NULL; + char *desc = NULL; + + if (entry->d_type == DT_DIR || n < 4 || + strcmp(".so", entry->d_name + n - 3)) + continue; + if (!get_filter_desc(dirname, entry->d_name, &desc, &long_desc)) + continue; + printf(" %-36s %s\n", entry->d_name, desc ? desc : ""); + if (verbose) { + char *p = long_desc; + char *line; + + while ((line = strsep(&p, "\n")) != NULL) + printf("%39s%s\n", "", line); + } + free(long_desc); + free(desc); + } + + closedir(dir); +} + +int list_available_dlfilters(const struct option *opt __maybe_unused, + const char *s __maybe_unused, + int unset __maybe_unused) +{ + char path[PATH_MAX]; + char *exec_path; + + printf("List of available dlfilters:\n"); + + list_filters("."); + + exec_path = get_argv_exec_path(); + if (!exec_path) + goto out; + snprintf(path, sizeof(path), "%s/dlfilters", exec_path); + + list_filters(path); + + free(exec_path); +out: + exit(0); +} diff --git a/tools/perf/util/dlfilter.h b/tools/perf/util/dlfilter.h index a994560..a1ed38d 100644 --- a/tools/perf/util/dlfilter.h +++ b/tools/perf/util/dlfilter.h @@ -88,4 +88,6 @@ static inline int dlfilter__filter_event_early(struct dlfilter *d, return dlfilter__do_filter_event(d, event, sample, evsel, machine, al, addr_al, true); } +int list_available_dlfilters(const struct option *opt, const char *s, int unset); + #endif diff --git a/tools/perf/util/perf_dlfilter.h b/tools/perf/util/perf_dlfilter.h index f7a847f..31ad4c1 100644 --- a/tools/perf/util/perf_dlfilter.h +++ b/tools/perf/util/perf_dlfilter.h @@ -126,4 +126,10 @@ int filter_event(void *data, const struct perf_dlfilter_sample *sample, void *ct */ int filter_event_early(void *data, const struct perf_dlfilter_sample *sample, void *ctx); +/* + * If implemented, return a one-line description of the filter, and optionally + * a longer description. + */ +const char *filter_description(const char **long_description); + #endif -- 2.7.4