Adding perf bpf command to allow to provide some fucs over ebpf objects, like compile, disassembly and loading, which is comming in following patches.
Link: http://lkml.kernel.org/n/tip-51vi69jgn3nfa00azjlik...@git.kernel.org Signed-off-by: Jiri Olsa <jo...@kernel.org> --- tools/perf/Build | 1 + tools/perf/builtin-bpf.c | 199 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/command-list.txt | 1 + tools/perf/perf.c | 1 + 5 files changed, 203 insertions(+) create mode 100644 tools/perf/builtin-bpf.c diff --git a/tools/perf/Build b/tools/perf/Build index e5232d567611..7f521ac16466 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -24,6 +24,7 @@ perf-y += builtin-mem.o perf-y += builtin-data.o perf-y += builtin-version.o perf-y += builtin-c2c.o +perf-y += builtin-bpf.o perf-$(CONFIG_TRACE) += builtin-trace.o perf-$(CONFIG_LIBELF) += builtin-probe.o diff --git a/tools/perf/builtin-bpf.c b/tools/perf/builtin-bpf.c new file mode 100644 index 000000000000..6f02352caf79 --- /dev/null +++ b/tools/perf/builtin-bpf.c @@ -0,0 +1,199 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <time.h> +#include <linux/compiler.h> +#include <subcmd/parse-options.h> +#include "builtin.h" +#include "perf.h" +#include "target.h" +#include "debug.h" +#include "parse-events.h" +#include "evlist.h" +#include "evsel.h" +#include "bpf-loader.h" + +struct perf_bpf { + struct target target; + struct perf_evlist *evlist; +}; + +struct perf_bpf bpf = { + .target = { .uid = UINT_MAX, }, +}; + +static volatile int done; +static volatile int workload_exec_errno; + +static void sig_handler(int sig __maybe_unused) +{ + done = 1; +} + +/* + * perf_evlist__prepare_workload will send a SIGUSR1 + * if the fork fails, since we asked by setting its + * want_signal to true. + */ +static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *info, + void *ucontext __maybe_unused) +{ + workload_exec_errno = info->si_value.sival_int; +} + +static int create_perf_bpf_counter(struct perf_evsel *evsel) +{ + if (target__has_cpu(&bpf.target) && !target__has_per_thread(&bpf.target)) + return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); + + return perf_evsel__open_per_thread(evsel, bpf.evlist->threads); +} + +static int __cmd_bpf(int argc , const char **argv) +{ + struct perf_evsel *evsel; + bool forks = argc > 0; + int err, status; + int child_pid = -1; + char msg[BUFSIZ]; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 500 }; + + if (forks) { + err = perf_evlist__prepare_workload(bpf.evlist, &bpf.target, + argv, true, + workload_exec_failed_signal); + if (err < 0) { + pr_err("Couldn't run the workload!\n"); + status = err; + goto out; + } + + child_pid = bpf.evlist->workload.pid; + } + + evlist__for_each_entry(bpf.evlist, evsel) { + err =create_perf_bpf_counter(evsel); + if (err < 0) { + perf_evsel__open_strerror(evsel, &bpf.target, + errno, msg, sizeof(msg)); + pr_err("%s\n", msg); + goto out_child; + } + } + + err = bpf__apply_obj_config(); + if (err) { + char errbuf[BUFSIZ]; + + bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); + pr_err("ERROR: Apply config to BPF failed: %s\n", + errbuf); + goto out_child; + } + + if (forks) { + perf_evlist__start_workload(bpf.evlist); + + if (!target__none(&bpf.target)) + perf_evlist__enable(bpf.evlist); + + waitpid(child_pid, &status, 0); + } else { + if (!target__none(&bpf.target)) + perf_evlist__enable(bpf.evlist); + + while (!done) { + nanosleep(&ts, NULL); + } + } + + if (!target__none(&bpf.target)) + perf_evlist__disable(bpf.evlist); + + child_pid = -1; + +out_child: + if (forks) { + if (workload_exec_errno) { + const char *emsg = str_error_r(workload_exec_errno, msg, sizeof(msg)); + pr_err("Workload failed: %s\n", emsg); + return -1; + } + + if (child_pid != -1) { + kill(child_pid, SIGTERM); + waitpid(child_pid, &status, 0); + } + + if (WIFSIGNALED(status)) + psignal(WTERMSIG(status), argv[0]); + } + +out: + perf_evlist__close(bpf.evlist); + + if (err) + status = err; + return WEXITSTATUS(status); +} + +int cmd_bpf(int argc, const char **argv) +{ + int err = -1; + const char * const bpf_usage[] = { + "perf bpf [<options>] [<command>]", + "perf bpf [<options>] -- <command> [<options>]", + NULL + }; + const struct option bpf_options[] = { + OPT_CALLBACK('e', "event", &bpf.evlist, "event", + "event selector. use 'perf list' to list available events", + parse_events_option), + OPT_STRING('C', "cpu", &bpf.target.cpu_list, "cpu", + "list of cpus to monitor"), + OPT_BOOLEAN('a', "all-cpus", &bpf.target.system_wide, + "system-wide collection from all CPUs"), + OPT_STRING('p', "pid", &bpf.target.pid, "pid", + "record events on existing process id"), + OPT_STRING('t', "tid", &bpf.target.tid, "tid", + "record events on existing thread id"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose"), + OPT_END() + }; + + signal(SIGINT, sig_handler); + + bpf.evlist = perf_evlist__new(); + if (bpf.evlist == NULL) + return -ENOMEM; + + argc = parse_options(argc, argv, bpf_options, bpf_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (!argc && target__none(&bpf.target)) + usage_with_options(bpf_usage, bpf_options); + + if (bpf.evlist->nr_entries == 0) { + pr_err("failed: No event specified\n"); + goto out; + } + + if (perf_evlist__create_maps(bpf.evlist, &bpf.target) < 0) { + if (target__has_task(&bpf.target)) { + pr_err("Problems finding threads of monitor\n"); + parse_options_usage(bpf_usage, bpf_options, "p", 1); + parse_options_usage(NULL, bpf_options, "t", 1); + } else if (target__has_cpu(&bpf.target)) { + perror("failed to parse CPUs map"); + parse_options_usage(bpf_usage, bpf_options, "C", 1); + parse_options_usage(NULL, bpf_options, "a", 1); + } + } + + target__validate(&bpf.target); + + err = __cmd_bpf(argc, argv); +out: + perf_evlist__delete(bpf.evlist); + return err; +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 05745f3ce912..1805c65f4d01 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -39,6 +39,7 @@ int cmd_inject(int argc, const char **argv); int cmd_mem(int argc, const char **argv); int cmd_data(int argc, const char **argv); int cmd_ftrace(int argc, const char **argv); +int cmd_bpf(int argc, const char **argv); int find_scripts(char **scripts_array, char **scripts_path_array); #endif diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 2d0caf20ff3a..4e8e398d4f45 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -30,3 +30,4 @@ perf-test mainporcelain common perf-timechart mainporcelain common perf-top mainporcelain common perf-trace mainporcelain audit +perf-bpf mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 1b3fc8ec0fa2..144cf71af0f7 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -80,6 +80,7 @@ static struct cmd_struct commands[] = { { "mem", cmd_mem, 0 }, { "data", cmd_data, 0 }, { "ftrace", cmd_ftrace, 0 }, + { "bpf", cmd_bpf, 0 }, }; struct pager_config { -- 2.13.6