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 <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>
This commit is contained in:
Adrian Hunter 2021-06-27 16:18:11 +03:00 committed by Arnaldo Carvalho de Melo
parent 9bde93a79a
commit 638e2b9984
6 changed files with 134 additions and 1 deletions

View 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View 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>

View 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),

View 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);
}

View 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

View 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