perf script: Add option to list dlfilters
authorAdrian Hunter <adrian.hunter@intel.com>
Sun, 27 Jun 2021 13:18:11 +0000 (16:18 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 1 Jul 2021 19:14:37 +0000 (16:14 -0300)
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 <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20210627131818.810-4-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-dlfilter.txt
tools/perf/Documentation/perf-script.txt
tools/perf/builtin-script.c
tools/perf/util/dlfilter.c
tools/perf/util/dlfilter.h
tools/perf/util/perf_dlfilter.h

index aef1c32..8bc219f 100644 (file)
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 2306c81..d2705d6 100644 (file)
@@ -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 <command>
         normally use -a by default, while scripts run with a <command>
index e47affe..4ffba1d 100644 (file)
@@ -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),
index e93c49c..288a2b5 100644 (file)
@@ -6,6 +6,8 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
+#include <dirent.h>
+#include <subcmd/exec-cmd.h>
 #include <linux/zalloc.h>
 #include <linux/build_bug.h>
 
@@ -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);
+}
index a994560..a1ed38d 100644 (file)
@@ -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
index f7a847f..31ad4c1 100644 (file)
@@ -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