From 6acd8e9271cdeaec458fd4eec4a6765d16e0e61c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 25 Nov 2015 16:36:54 +0100 Subject: perf stat: Clear sample_(type|period) for counting Clear sample_(type|period) for counting, as it only confuses debug output with unwanted sampling details: Before: $ sudo perf stat -e 'raw_syscalls:sys_enter' -vv ls ------------------------------------------------------------ perf_event_attr: type 2 size 112 config 0x11 { sample_period, sample_freq } 1 sample_type TIME|CPU|PERIOD|RAW read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING disabled 1 inherit 1 enable_on_exec 1 exclude_guest 1 ... After: $ sudo perf stat -e 'raw_syscalls:sys_enter' -vv ls ------------------------------------------------------------ perf_event_attr: type 2 size 112 config 0x11 read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING disabled 1 inherit 1 enable_on_exec 1 exclude_guest 1 ... Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448465815-27404-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e77880b5094d..df2fbf046ee2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -161,6 +161,13 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->inherit = !no_inherit; + /* + * Some events get initialized with sample_(period/type) set, + * like tracepoints. Clear it up for counting. + */ + attr->sample_period = 0; + attr->sample_type = 0; + if (target__has_cpu(&target)) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); -- cgit v1.2.3 From 5cd95fc3f8d84a8bb256838fa3b6b59e9095eaa2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:40 +0100 Subject: perf evsel: Use event maps directly in perf_evsel__enable All events now share proper cpu and thread maps. There's no need to pass those maps from evlist, it's safe to use evsel maps for enabling event. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +---- tools/perf/util/evsel.c | 5 ++++- tools/perf/util/evsel.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index df2fbf046ee2..813c52ad9303 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -256,12 +256,9 @@ static void handle_initial_delay(void) struct perf_evsel *counter; if (initial_delay) { - const int ncpus = cpu_map__nr(evsel_list->cpus), - nthreads = thread_map__nr(evsel_list->threads); - usleep(initial_delay * 1000); evlist__for_each(evsel_list, counter) - perf_evsel__enable(counter, ncpus, nthreads); + perf_evsel__enable(counter); } } diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0a1f4d9e52fc..3a9b5068667d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -981,8 +981,11 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, return -1; } -int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) +int perf_evsel__enable(struct perf_evsel *evsel) { + int nthreads = thread_map__nr(evsel->threads); + int ncpus = cpu_map__nr(evsel->cpus); + return perf_evsel__run_ioctl(evsel, ncpus, nthreads, PERF_EVENT_IOC_ENABLE, 0); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0e49bd742c63..a721592a3200 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -227,7 +227,7 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, const char *op, const char *filter); int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); -int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); +int perf_evsel__enable(struct perf_evsel *evsel); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); -- cgit v1.2.3 From ab46db0a3325a064bb24e826b12995d157565efb Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:43 +0100 Subject: perf stat: Use perf_evlist__enable in handle_initial_delay No need to mimic the behaviour of perf_evlist__enable, we can use it directly. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-5-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 813c52ad9303..8ca40deaa728 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -253,12 +253,9 @@ static void process_interval(void) static void handle_initial_delay(void) { - struct perf_evsel *counter; - if (initial_delay) { usleep(initial_delay * 1000); - evlist__for_each(evsel_list, counter) - perf_evsel__enable(counter); + perf_evlist__enable(evsel_list); } } -- cgit v1.2.3 From 67ccdecd09cac818146b1e153ff901cb67570012 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:44 +0100 Subject: perf stat: Create events as disabled Currently we have 2 kinds of stat counters based on when the event is enabled: 1) tracee command events, which are enable once the tracee executes exec syscall (enable_on_exec bit) 2) all other events which get alive within the perf_event_open syscall And 2) case could raise a problem in case we want additional filter to be attached for event. In this case we want the event to be enabled after it's configured with filter. Changing the behaviour of 2) events, so they all are created as disabled (disabled bit). Adding extra enable call to make them alive once they finish setup. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 8ca40deaa728..2e70610649a1 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -168,11 +168,18 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->sample_period = 0; attr->sample_type = 0; + /* + * Disabling all counters initially, they will be enabled + * either manually by us or by kernel via enable_on_exec + * set later. + */ + if (perf_evsel__is_group_leader(evsel)) + attr->disabled = 1; + if (target__has_cpu(&target)) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) { - attr->disabled = 1; if (!initial_delay) attr->enable_on_exec = 1; } @@ -251,12 +258,18 @@ static void process_interval(void) print_counters(&rs, 0, NULL); } -static void handle_initial_delay(void) +static void enable_counters(void) { - if (initial_delay) { + if (initial_delay) usleep(initial_delay * 1000); + + /* + * We need to enable counters only if: + * - we don't have tracee (attaching to task or cpu) + * - we have initial delay configured + */ + if (!target__none(&target) || initial_delay) perf_evlist__enable(evsel_list); - } } static volatile int workload_exec_errno; @@ -353,7 +366,7 @@ static int __run_perf_stat(int argc, const char **argv) if (forks) { perf_evlist__start_workload(evsel_list); - handle_initial_delay(); + enable_counters(); if (interval) { while (!waitpid(child_pid, &status, WNOHANG)) { @@ -372,7 +385,7 @@ static int __run_perf_stat(int argc, const char **argv) if (WIFSIGNALED(status)) psignal(WTERMSIG(status), argv[0]); } else { - handle_initial_delay(); + enable_counters(); while (!done) { nanosleep(&ts, NULL); if (interval) -- cgit v1.2.3 From c8280cec2a196f2ffea83dd755b17eb020ca1b83 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:45 +0100 Subject: perf stat: Move enable_on_exec setup under earlier code It's more readable this way and we can save one perf_evsel__is_group_leader condition in current code. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2e70610649a1..e74712dee242 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -173,17 +173,20 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * either manually by us or by kernel via enable_on_exec * set later. */ - if (perf_evsel__is_group_leader(evsel)) + if (perf_evsel__is_group_leader(evsel)) { attr->disabled = 1; - if (target__has_cpu(&target)) - return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); - - if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) { - if (!initial_delay) + /* + * In case of initial_delay we enable tracee + * events manually. + */ + if (target__none(&target) && !initial_delay) attr->enable_on_exec = 1; } + if (target__has_cpu(&target)) + return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); + return perf_evsel__open_per_thread(evsel, evsel_list->threads); } -- cgit v1.2.3 From 544c2ae7b1a794ad0bc5ec24d832ab5658d5aef6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:27 +0900 Subject: perf stat: Fix cmd_stat to release cpu_map Fix cmd_stat() to release cpu_map objects (aggr_map and cpus_aggr_map) afterwards. refcnt debugger shows that the cmd_stat initializes cpu_map but not puts it. ---- # ./perf stat -v ls .... REFCNT: BUG: Unreclaimed objects found. ==== [0] ==== Unreclaimed cpu_map@0x29339c0 Refcount +1 => 1 at ./perf(cpu_map__empty_new+0x6d) [0x4e64bd] ./perf(cmd_stat+0x5fe) [0x43594e] ./perf() [0x47b785] ./perf(main+0x617) [0x422587] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2dff420af5] ./perf() [0x4226fd] REFCNT: Total 1 objects are not reclaimed. "cpu_map" leaks 1 objects ---- Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021127.10245.93697.stgit@localhost.localdomain [ Remove NULL checks before calling the put operation, it checks it already ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e74712dee242..25a95f49c36e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1094,6 +1094,14 @@ static int perf_stat_init_aggr_mode(void) return cpus_aggr_map ? 0 : -ENOMEM; } +static void perf_stat__exit_aggr_mode(void) +{ + cpu_map__put(aggr_map); + cpu_map__put(cpus_aggr_map); + aggr_map = NULL; + cpus_aggr_map = NULL; +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1442,6 +1450,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: perf_evlist__delete(evsel_list); -- cgit v1.2.3 From 4b6ab94eabe4f55371cff4569750bb3996c55db6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:39 -0600 Subject: perf subcmd: Create subcmd library Move the subcommand-related files from perf to a new library named libsubcmd.a. Since we're moving files anyway, go ahead and rename 'exec_cmd.*' to 'exec-cmd.*' to be consistent with the naming of all the other files. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/c0a838d4c878ab17fee50998811612b2281355c1.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/subcmd/Build | 7 + tools/lib/subcmd/Makefile | 48 ++ tools/lib/subcmd/exec-cmd.c | 209 +++++++ tools/lib/subcmd/exec-cmd.h | 16 + tools/lib/subcmd/help.c | 268 +++++++++ tools/lib/subcmd/help.h | 34 ++ tools/lib/subcmd/pager.c | 100 ++++ tools/lib/subcmd/pager.h | 9 + tools/lib/subcmd/parse-options.c | 983 +++++++++++++++++++++++++++++++++ tools/lib/subcmd/parse-options.h | 228 ++++++++ tools/lib/subcmd/run-command.c | 227 ++++++++ tools/lib/subcmd/run-command.h | 60 ++ tools/lib/subcmd/sigchain.c | 53 ++ tools/lib/subcmd/sigchain.h | 10 + tools/lib/subcmd/subcmd-config.c | 11 + tools/lib/subcmd/subcmd-config.h | 14 + tools/lib/subcmd/subcmd-util.h | 91 +++ tools/perf/MANIFEST | 1 + tools/perf/Makefile.perf | 20 +- tools/perf/arch/x86/util/intel-pt.c | 2 +- tools/perf/bench/futex-hash.c | 2 +- tools/perf/bench/futex-lock-pi.c | 2 +- tools/perf/bench/futex-requeue.c | 2 +- tools/perf/bench/futex-wake-parallel.c | 2 +- tools/perf/bench/futex-wake.c | 2 +- tools/perf/bench/mem-functions.c | 2 +- tools/perf/bench/numa.c | 2 +- tools/perf/bench/sched-messaging.c | 2 +- tools/perf/bench/sched-pipe.c | 2 +- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-bench.c | 2 +- tools/perf/builtin-buildid-cache.c | 2 +- tools/perf/builtin-buildid-list.c | 2 +- tools/perf/builtin-config.c | 2 +- tools/perf/builtin-data.c | 2 +- tools/perf/builtin-evlist.c | 2 +- tools/perf/builtin-help.c | 8 +- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-list.c | 2 +- tools/perf/builtin-lock.c | 2 +- tools/perf/builtin-mem.c | 2 +- tools/perf/builtin-probe.c | 2 +- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-sched.c | 2 +- tools/perf/builtin-script.c | 4 +- tools/perf/builtin-stat.c | 2 +- tools/perf/builtin-timechart.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 4 +- tools/perf/perf.c | 6 +- tools/perf/tests/attr.c | 2 +- tools/perf/tests/builtin-test.c | 2 +- tools/perf/util/Build | 7 - tools/perf/util/auxtrace.c | 2 +- tools/perf/util/cache.h | 2 +- tools/perf/util/cgroup.c | 2 +- tools/perf/util/config.c | 2 +- tools/perf/util/evlist.c | 2 +- tools/perf/util/exec_cmd.c | 209 ------- tools/perf/util/exec_cmd.h | 16 - tools/perf/util/help-unknown-cmd.c | 2 +- tools/perf/util/help.c | 268 --------- tools/perf/util/help.h | 34 -- tools/perf/util/pager.c | 100 ---- tools/perf/util/pager.h | 9 - tools/perf/util/parse-branch-options.c | 2 +- tools/perf/util/parse-events.c | 4 +- tools/perf/util/parse-options.c | 983 --------------------------------- tools/perf/util/parse-options.h | 228 -------- tools/perf/util/parse-regs-options.c | 2 +- tools/perf/util/run-command.c | 227 -------- tools/perf/util/run-command.h | 60 -- tools/perf/util/sigchain.c | 53 -- tools/perf/util/sigchain.h | 10 - tools/perf/util/sort.h | 2 +- tools/perf/util/subcmd-config.c | 11 - tools/perf/util/subcmd-config.h | 14 - tools/perf/util/subcmd-util.h | 91 --- 81 files changed, 2439 insertions(+), 2378 deletions(-) create mode 100644 tools/lib/subcmd/Build create mode 100644 tools/lib/subcmd/Makefile create mode 100644 tools/lib/subcmd/exec-cmd.c create mode 100644 tools/lib/subcmd/exec-cmd.h create mode 100644 tools/lib/subcmd/help.c create mode 100644 tools/lib/subcmd/help.h create mode 100644 tools/lib/subcmd/pager.c create mode 100644 tools/lib/subcmd/pager.h create mode 100644 tools/lib/subcmd/parse-options.c create mode 100644 tools/lib/subcmd/parse-options.h create mode 100644 tools/lib/subcmd/run-command.c create mode 100644 tools/lib/subcmd/run-command.h create mode 100644 tools/lib/subcmd/sigchain.c create mode 100644 tools/lib/subcmd/sigchain.h create mode 100644 tools/lib/subcmd/subcmd-config.c create mode 100644 tools/lib/subcmd/subcmd-config.h create mode 100644 tools/lib/subcmd/subcmd-util.h delete mode 100644 tools/perf/util/exec_cmd.c delete mode 100644 tools/perf/util/exec_cmd.h delete mode 100644 tools/perf/util/help.c delete mode 100644 tools/perf/util/help.h delete mode 100644 tools/perf/util/pager.c delete mode 100644 tools/perf/util/pager.h delete mode 100644 tools/perf/util/parse-options.c delete mode 100644 tools/perf/util/parse-options.h delete mode 100644 tools/perf/util/run-command.c delete mode 100644 tools/perf/util/run-command.h delete mode 100644 tools/perf/util/sigchain.c delete mode 100644 tools/perf/util/sigchain.h delete mode 100644 tools/perf/util/subcmd-config.c delete mode 100644 tools/perf/util/subcmd-config.h delete mode 100644 tools/perf/util/subcmd-util.h (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/lib/subcmd/Build b/tools/lib/subcmd/Build new file mode 100644 index 000000000000..ee31288788c1 --- /dev/null +++ b/tools/lib/subcmd/Build @@ -0,0 +1,7 @@ +libsubcmd-y += exec-cmd.o +libsubcmd-y += help.o +libsubcmd-y += pager.o +libsubcmd-y += parse-options.o +libsubcmd-y += run-command.o +libsubcmd-y += sigchain.o +libsubcmd-y += subcmd-config.o diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile new file mode 100644 index 000000000000..629cf8c14e68 --- /dev/null +++ b/tools/lib/subcmd/Makefile @@ -0,0 +1,48 @@ +include ../../scripts/Makefile.include +include ../../perf/config/utilities.mak # QUIET_CLEAN + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar +RM = rm -f + +MAKEFLAGS += --no-print-directory + +LIBFILE = $(OUTPUT)libsubcmd.a + +CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE + +CFLAGS += -I$(srctree)/tools/include/ +CFLAGS += -I$(srctree)/include/uapi +CFLAGS += -I$(srctree)/include + +SUBCMD_IN := $(OUTPUT)libsubcmd-in.o + +all: + +export srctree OUTPUT CC LD CFLAGS V +include $(srctree)/tools/build/Makefile.include + +all: fixdep $(LIBFILE) + +$(SUBCMD_IN): FORCE + @$(MAKE) $(build)=libsubcmd + +$(LIBFILE): $(SUBCMD_IN) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(SUBCMD_IN) + +clean: + $(call QUIET_CLEAN, libsubcmd) $(RM) $(LIBFILE); \ + find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o -or -name \*.o.cmd -or -name \*.o.d | xargs $(RM) + +FORCE: + +.PHONY: clean FORCE diff --git a/tools/lib/subcmd/exec-cmd.c b/tools/lib/subcmd/exec-cmd.c new file mode 100644 index 000000000000..1ae833af1a4a --- /dev/null +++ b/tools/lib/subcmd/exec-cmd.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" +#include "exec-cmd.h" +#include "subcmd-config.h" + +#define MAX_ARGS 32 +#define PATH_MAX 4096 + +static const char *argv_exec_path; +static const char *argv0_path; + +void exec_cmd_init(const char *exec_name, const char *prefix, + const char *exec_path, const char *exec_path_env) +{ + subcmd_config.exec_name = exec_name; + subcmd_config.prefix = prefix; + subcmd_config.exec_path = exec_path; + subcmd_config.exec_path_env = exec_path_env; +} + +#define is_dir_sep(c) ((c) == '/') + +static int is_absolute_path(const char *path) +{ + return path[0] == '/'; +} + +static const char *get_pwd_cwd(void) +{ + static char cwd[PATH_MAX + 1]; + char *pwd; + struct stat cwd_stat, pwd_stat; + if (getcwd(cwd, PATH_MAX) == NULL) + return NULL; + pwd = getenv("PWD"); + if (pwd && strcmp(pwd, cwd)) { + stat(cwd, &cwd_stat); + if (!stat(pwd, &pwd_stat) && + pwd_stat.st_dev == cwd_stat.st_dev && + pwd_stat.st_ino == cwd_stat.st_ino) { + strlcpy(cwd, pwd, PATH_MAX); + } + } + return cwd; +} + +static const char *make_nonrelative_path(const char *path) +{ + static char buf[PATH_MAX + 1]; + + if (is_absolute_path(path)) { + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } else { + const char *cwd = get_pwd_cwd(); + if (!cwd) + die("Cannot determine the current working directory"); + if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } + return buf; +} + +char *system_path(const char *path) +{ + char *buf = NULL; + + if (is_absolute_path(path)) + return strdup(path); + + astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); + + return buf; +} + +const char *extract_argv0_path(const char *argv0) +{ + const char *slash; + + if (!argv0 || !*argv0) + return NULL; + slash = argv0 + strlen(argv0); + + while (argv0 <= slash && !is_dir_sep(*slash)) + slash--; + + if (slash >= argv0) { + argv0_path = strndup(argv0, slash - argv0); + return argv0_path ? slash + 1 : NULL; + } + + return argv0; +} + +void set_argv_exec_path(const char *exec_path) +{ + argv_exec_path = exec_path; + /* + * Propagate this setting to external programs. + */ + setenv(subcmd_config.exec_path_env, exec_path, 1); +} + + +/* Returns the highest-priority location to look for subprograms. */ +char *get_argv_exec_path(void) +{ + char *env; + + if (argv_exec_path) + return strdup(argv_exec_path); + + env = getenv(subcmd_config.exec_path_env); + if (env && *env) + return strdup(env); + + return system_path(subcmd_config.exec_path); +} + +static void add_path(char **out, const char *path) +{ + if (path && *path) { + if (is_absolute_path(path)) + astrcat(out, path); + else + astrcat(out, make_nonrelative_path(path)); + + astrcat(out, ":"); + } +} + +void setup_path(void) +{ + const char *old_path = getenv("PATH"); + char *new_path = NULL; + char *tmp = get_argv_exec_path(); + + add_path(&new_path, tmp); + add_path(&new_path, argv0_path); + free(tmp); + + if (old_path) + astrcat(&new_path, old_path); + else + astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); + + setenv("PATH", new_path, 1); + + free(new_path); +} + +static const char **prepare_exec_cmd(const char **argv) +{ + int argc; + const char **nargv; + + for (argc = 0; argv[argc]; argc++) + ; /* just counting */ + nargv = malloc(sizeof(*nargv) * (argc + 2)); + + nargv[0] = subcmd_config.exec_name; + for (argc = 0; argv[argc]; argc++) + nargv[argc + 1] = argv[argc]; + nargv[argc + 1] = NULL; + return nargv; +} + +int execv_cmd(const char **argv) { + const char **nargv = prepare_exec_cmd(argv); + + /* execvp() can only ever return if it fails */ + execvp(subcmd_config.exec_name, (char **)nargv); + + free(nargv); + return -1; +} + + +int execl_cmd(const char *cmd,...) +{ + int argc; + const char *argv[MAX_ARGS + 1]; + const char *arg; + va_list param; + + va_start(param, cmd); + argv[0] = cmd; + argc = 1; + while (argc < MAX_ARGS) { + arg = argv[argc++] = va_arg(param, char *); + if (!arg) + break; + } + va_end(param); + if (MAX_ARGS <= argc) { + fprintf(stderr, " Error: too many args to run %s\n", cmd); + return -1; + } + + argv[argc] = NULL; + return execv_cmd(argv); +} diff --git a/tools/lib/subcmd/exec-cmd.h b/tools/lib/subcmd/exec-cmd.h new file mode 100644 index 000000000000..f1bd3436ad5f --- /dev/null +++ b/tools/lib/subcmd/exec-cmd.h @@ -0,0 +1,16 @@ +#ifndef __PERF_EXEC_CMD_H +#define __PERF_EXEC_CMD_H + +extern void exec_cmd_init(const char *exec_name, const char *prefix, + const char *exec_path, const char *exec_path_env); + +extern void set_argv_exec_path(const char *exec_path); +extern const char *extract_argv0_path(const char *path); +extern void setup_path(void); +extern int execv_cmd(const char **argv); /* NULL terminated */ +extern int execl_cmd(const char *cmd, ...); +/* get_argv_exec_path and system_path return malloc'd string, caller must free it */ +extern char *get_argv_exec_path(void); +extern char *system_path(const char *path); + +#endif /* __PERF_EXEC_CMD_H */ diff --git a/tools/lib/subcmd/help.c b/tools/lib/subcmd/help.c new file mode 100644 index 000000000000..e228c3cb3716 --- /dev/null +++ b/tools/lib/subcmd/help.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" +#include "help.h" +#include "exec-cmd.h" + +void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) +{ + struct cmdname *ent = malloc(sizeof(*ent) + len + 1); + + ent->len = len; + memcpy(ent->name, name, len); + ent->name[len] = 0; + + ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); + cmds->names[cmds->cnt++] = ent; +} + +void clean_cmdnames(struct cmdnames *cmds) +{ + unsigned int i; + + for (i = 0; i < cmds->cnt; ++i) + zfree(&cmds->names[i]); + zfree(&cmds->names); + cmds->cnt = 0; + cmds->alloc = 0; +} + +int cmdname_compare(const void *a_, const void *b_) +{ + struct cmdname *a = *(struct cmdname **)a_; + struct cmdname *b = *(struct cmdname **)b_; + return strcmp(a->name, b->name); +} + +void uniq(struct cmdnames *cmds) +{ + unsigned int i, j; + + if (!cmds->cnt) + return; + + for (i = j = 1; i < cmds->cnt; i++) + if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) + cmds->names[j++] = cmds->names[i]; + + cmds->cnt = j; +} + +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) +{ + size_t ci, cj, ei; + int cmp; + + ci = cj = ei = 0; + while (ci < cmds->cnt && ei < excludes->cnt) { + cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); + if (cmp < 0) + cmds->names[cj++] = cmds->names[ci++]; + else if (cmp == 0) + ci++, ei++; + else if (cmp > 0) + ei++; + } + + while (ci < cmds->cnt) + cmds->names[cj++] = cmds->names[ci++]; + + cmds->cnt = cj; +} + +static void get_term_dimensions(struct winsize *ws) +{ + char *s = getenv("LINES"); + + if (s != NULL) { + ws->ws_row = atoi(s); + s = getenv("COLUMNS"); + if (s != NULL) { + ws->ws_col = atoi(s); + if (ws->ws_row && ws->ws_col) + return; + } + } +#ifdef TIOCGWINSZ + if (ioctl(1, TIOCGWINSZ, ws) == 0 && + ws->ws_row && ws->ws_col) + return; +#endif + ws->ws_row = 25; + ws->ws_col = 80; +} + +static void pretty_print_string_list(struct cmdnames *cmds, int longest) +{ + int cols = 1, rows; + int space = longest + 1; /* min 1 SP between words */ + struct winsize win; + int max_cols; + int i, j; + + get_term_dimensions(&win); + max_cols = win.ws_col - 1; /* don't print *on* the edge */ + + if (space < max_cols) + cols = max_cols / space; + rows = (cmds->cnt + cols - 1) / cols; + + for (i = 0; i < rows; i++) { + printf(" "); + + for (j = 0; j < cols; j++) { + unsigned int n = j * rows + i; + unsigned int size = space; + + if (n >= cmds->cnt) + break; + if (j == cols-1 || n + rows >= cmds->cnt) + size = 1; + printf("%-*s", size, cmds->names[n]->name); + } + putchar('\n'); + } +} + +static int is_executable(const char *name) +{ + struct stat st; + + if (stat(name, &st) || /* stat, not lstat */ + !S_ISREG(st.st_mode)) + return 0; + + return st.st_mode & S_IXUSR; +} + +static int has_extension(const char *filename, const char *ext) +{ + size_t len = strlen(filename); + size_t extlen = strlen(ext); + + return len > extlen && !memcmp(filename + len - extlen, ext, extlen); +} + +static void list_commands_in_dir(struct cmdnames *cmds, + const char *path, + const char *prefix) +{ + int prefix_len; + DIR *dir = opendir(path); + struct dirent *de; + char *buf = NULL; + + if (!dir) + return; + if (!prefix) + prefix = "perf-"; + prefix_len = strlen(prefix); + + astrcatf(&buf, "%s/", path); + + while ((de = readdir(dir)) != NULL) { + int entlen; + + if (prefixcmp(de->d_name, prefix)) + continue; + + astrcat(&buf, de->d_name); + if (!is_executable(buf)) + continue; + + entlen = strlen(de->d_name) - prefix_len; + if (has_extension(de->d_name, ".exe")) + entlen -= 4; + + add_cmdname(cmds, de->d_name + prefix_len, entlen); + } + closedir(dir); + free(buf); +} + +void load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds) +{ + const char *env_path = getenv("PATH"); + char *exec_path = get_argv_exec_path(); + + if (exec_path) { + list_commands_in_dir(main_cmds, exec_path, prefix); + qsort(main_cmds->names, main_cmds->cnt, + sizeof(*main_cmds->names), cmdname_compare); + uniq(main_cmds); + } + + if (env_path) { + char *paths, *path, *colon; + path = paths = strdup(env_path); + while (1) { + if ((colon = strchr(path, ':'))) + *colon = 0; + if (!exec_path || strcmp(path, exec_path)) + list_commands_in_dir(other_cmds, path, prefix); + + if (!colon) + break; + path = colon + 1; + } + free(paths); + + qsort(other_cmds->names, other_cmds->cnt, + sizeof(*other_cmds->names), cmdname_compare); + uniq(other_cmds); + } + free(exec_path); + exclude_cmds(other_cmds, main_cmds); +} + +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds) +{ + unsigned int i, longest = 0; + + for (i = 0; i < main_cmds->cnt; i++) + if (longest < main_cmds->names[i]->len) + longest = main_cmds->names[i]->len; + for (i = 0; i < other_cmds->cnt; i++) + if (longest < other_cmds->names[i]->len) + longest = other_cmds->names[i]->len; + + if (main_cmds->cnt) { + char *exec_path = get_argv_exec_path(); + printf("available %s in '%s'\n", title, exec_path); + printf("----------------"); + mput_char('-', strlen(title) + strlen(exec_path)); + putchar('\n'); + pretty_print_string_list(main_cmds, longest); + putchar('\n'); + free(exec_path); + } + + if (other_cmds->cnt) { + printf("%s available from elsewhere on your $PATH\n", title); + printf("---------------------------------------"); + mput_char('-', strlen(title)); + putchar('\n'); + pretty_print_string_list(other_cmds, longest); + putchar('\n'); + } +} + +int is_in_cmdlist(struct cmdnames *c, const char *s) +{ + unsigned int i; + + for (i = 0; i < c->cnt; i++) + if (!strcmp(s, c->names[i]->name)) + return 1; + return 0; +} diff --git a/tools/lib/subcmd/help.h b/tools/lib/subcmd/help.h new file mode 100644 index 000000000000..096c8bc45cd7 --- /dev/null +++ b/tools/lib/subcmd/help.h @@ -0,0 +1,34 @@ +#ifndef __PERF_HELP_H +#define __PERF_HELP_H + +#include + +struct cmdnames { + size_t alloc; + size_t cnt; + struct cmdname { + size_t len; /* also used for similarity index in help.c */ + char name[]; + } **names; +}; + +static inline void mput_char(char c, unsigned int num) +{ + while(num--) + putchar(c); +} + +void load_command_list(const char *prefix, + struct cmdnames *main_cmds, + struct cmdnames *other_cmds); +void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); +void clean_cmdnames(struct cmdnames *cmds); +int cmdname_compare(const void *a, const void *b); +void uniq(struct cmdnames *cmds); +/* Here we require that excludes is a sorted list. */ +void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); +int is_in_cmdlist(struct cmdnames *c, const char *s); +void list_commands(const char *title, struct cmdnames *main_cmds, + struct cmdnames *other_cmds); + +#endif /* __PERF_HELP_H */ diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c new file mode 100644 index 000000000000..d50f3b58606b --- /dev/null +++ b/tools/lib/subcmd/pager.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include "pager.h" +#include "run-command.h" +#include "sigchain.h" +#include "subcmd-config.h" + +/* + * This is split up from the rest of git so that we can do + * something different on Windows. + */ + +static int spawned_pager; + +void pager_init(const char *pager_env) +{ + subcmd_config.pager_env = pager_env; +} + +static void pager_preexec(void) +{ + /* + * Work around bug in "less" by not starting it until we + * have real input + */ + fd_set in; + + FD_ZERO(&in); + FD_SET(0, &in); + select(1, &in, NULL, &in, NULL); + + setenv("LESS", "FRSX", 0); +} + +static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; +static struct child_process pager_process; + +static void wait_for_pager(void) +{ + fflush(stdout); + fflush(stderr); + /* signal EOF to pager */ + close(1); + close(2); + finish_command(&pager_process); +} + +static void wait_for_pager_signal(int signo) +{ + wait_for_pager(); + sigchain_pop(signo); + raise(signo); +} + +void setup_pager(void) +{ + const char *pager = getenv(subcmd_config.pager_env); + + if (!isatty(1)) + return; + if (!pager) + pager = getenv("PAGER"); + if (!(pager || access("/usr/bin/pager", X_OK))) + pager = "/usr/bin/pager"; + if (!(pager || access("/usr/bin/less", X_OK))) + pager = "/usr/bin/less"; + if (!pager) + pager = "cat"; + if (!*pager || !strcmp(pager, "cat")) + return; + + spawned_pager = 1; /* means we are emitting to terminal */ + + /* spawn the pager */ + pager_argv[2] = pager; + pager_process.argv = pager_argv; + pager_process.in = -1; + pager_process.preexec_cb = pager_preexec; + + if (start_command(&pager_process)) + return; + + /* original process continues, but writes to the pipe */ + dup2(pager_process.in, 1); + if (isatty(2)) + dup2(pager_process.in, 2); + close(pager_process.in); + + /* this makes sure that the parent terminates after the pager */ + sigchain_push_common(wait_for_pager_signal); + atexit(wait_for_pager); +} + +int pager_in_use(void) +{ + return spawned_pager; +} diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h new file mode 100644 index 000000000000..d6a591a4c017 --- /dev/null +++ b/tools/lib/subcmd/pager.h @@ -0,0 +1,9 @@ +#ifndef __PERF_PAGER_H +#define __PERF_PAGER_H + +extern void pager_init(const char *pager_env); + +extern void setup_pager(void); +extern int pager_in_use(void); + +#endif /* __PERF_PAGER_H */ diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c new file mode 100644 index 000000000000..981bb4481fd5 --- /dev/null +++ b/tools/lib/subcmd/parse-options.c @@ -0,0 +1,983 @@ +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" +#include "parse-options.h" +#include "subcmd-config.h" +#include "pager.h" + +#define OPT_SHORT 1 +#define OPT_UNSET 2 + +char *error_buf; + +static int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); + else if (flags & OPT_UNSET) + fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); + else + fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); + + return -1; +} + +static const char *skip_prefix(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static void optwarning(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); + else if (flags & OPT_UNSET) + fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); + else + fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); +} + +static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, + int flags, const char **arg) +{ + const char *res; + + if (p->opt) { + res = p->opt; + p->opt = NULL; + } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || + **(p->argv + 1) == '-')) { + res = (const char *)opt->defval; + } else if (p->argc > 1) { + p->argc--; + res = *++p->argv; + } else + return opterror(opt, "requires a value", flags); + if (arg) + *arg = res; + return 0; +} + +static int get_value(struct parse_opt_ctx_t *p, + const struct option *opt, int flags) +{ + const char *s, *arg = NULL; + const int unset = flags & OPT_UNSET; + int err; + + if (unset && p->opt) + return opterror(opt, "takes no value", flags); + if (unset && (opt->flags & PARSE_OPT_NONEG)) + return opterror(opt, "isn't available", flags); + if (opt->flags & PARSE_OPT_DISABLED) + return opterror(opt, "is not usable", flags); + + if (opt->flags & PARSE_OPT_EXCLUSIVE) { + if (p->excl_opt && p->excl_opt != opt) { + char msg[128]; + + if (((flags & OPT_SHORT) && p->excl_opt->short_name) || + p->excl_opt->long_name == NULL) { + snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", + p->excl_opt->short_name); + } else { + snprintf(msg, sizeof(msg), "cannot be used with %s", + p->excl_opt->long_name); + } + opterror(opt, msg, flags); + return -3; + } + p->excl_opt = opt; + } + if (!(flags & OPT_SHORT) && p->opt) { + switch (opt->type) { + case OPTION_CALLBACK: + if (!(opt->flags & PARSE_OPT_NOARG)) + break; + /* FALLTHROUGH */ + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_BIT: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + return opterror(opt, "takes no value", flags); + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: + case OPTION_STRING: + case OPTION_INTEGER: + case OPTION_UINTEGER: + case OPTION_LONG: + case OPTION_U64: + default: + break; + } + } + + if (opt->flags & PARSE_OPT_NOBUILD) { + char reason[128]; + bool noarg = false; + + err = snprintf(reason, sizeof(reason), + opt->flags & PARSE_OPT_CANSKIP ? + "is being ignored because %s " : + "is not available because %s", + opt->build_opt); + reason[sizeof(reason) - 1] = '\0'; + + if (err < 0) + strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? + "is being ignored" : + "is not available", + sizeof(reason)); + + if (!(opt->flags & PARSE_OPT_CANSKIP)) + return opterror(opt, reason, flags); + + err = 0; + if (unset) + noarg = true; + if (opt->flags & PARSE_OPT_NOARG) + noarg = true; + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + noarg = true; + + switch (opt->type) { + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_BIT: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: + noarg = true; + break; + case OPTION_CALLBACK: + case OPTION_STRING: + case OPTION_INTEGER: + case OPTION_UINTEGER: + case OPTION_LONG: + case OPTION_U64: + default: + break; + } + + if (!noarg) + err = get_arg(p, opt, flags, NULL); + if (err) + return err; + + optwarning(opt, reason, flags); + return 0; + } + + switch (opt->type) { + case OPTION_BIT: + if (unset) + *(int *)opt->value &= ~opt->defval; + else + *(int *)opt->value |= opt->defval; + return 0; + + case OPTION_BOOLEAN: + *(bool *)opt->value = unset ? false : true; + if (opt->set) + *(bool *)opt->set = true; + return 0; + + case OPTION_INCR: + *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; + return 0; + + case OPTION_SET_UINT: + *(unsigned int *)opt->value = unset ? 0 : opt->defval; + return 0; + + case OPTION_SET_PTR: + *(void **)opt->value = unset ? NULL : (void *)opt->defval; + return 0; + + case OPTION_STRING: + err = 0; + if (unset) + *(const char **)opt->value = NULL; + else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + *(const char **)opt->value = (const char *)opt->defval; + else + err = get_arg(p, opt, flags, (const char **)opt->value); + + /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */ + if (opt->flags & PARSE_OPT_NOEMPTY) { + const char *val = *(const char **)opt->value; + + if (!val) + return err; + + /* Similar to unset if we are given an empty string. */ + if (val[0] == '\0') { + *(const char **)opt->value = NULL; + return 0; + } + } + + return err; + + case OPTION_CALLBACK: + if (unset) + return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; + if (opt->flags & PARSE_OPT_NOARG) + return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; + if (get_arg(p, opt, flags, &arg)) + return -1; + return (*opt->callback)(opt, arg, 0) ? (-1) : 0; + + case OPTION_INTEGER: + if (unset) { + *(int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(int *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(int *)opt->value = strtol(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + case OPTION_UINTEGER: + if (unset) { + *(unsigned int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(unsigned int *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + case OPTION_LONG: + if (unset) { + *(long *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(long *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(long *)opt->value = strtol(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + case OPTION_U64: + if (unset) { + *(u64 *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { + *(u64 *)opt->value = opt->defval; + return 0; + } + if (get_arg(p, opt, flags, &arg)) + return -1; + *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: + default: + die("should not happen, someone must be hit on the forehead"); + } +} + +static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + if (options->short_name == *p->opt) { + p->opt = p->opt[1] ? p->opt + 1 : NULL; + return get_value(p, options, OPT_SHORT); + } + } + return -2; +} + +static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, + const struct option *options) +{ + const char *arg_end = strchr(arg, '='); + const struct option *abbrev_option = NULL, *ambiguous_option = NULL; + int abbrev_flags = 0, ambiguous_flags = 0; + + if (!arg_end) + arg_end = arg + strlen(arg); + + for (; options->type != OPTION_END; options++) { + const char *rest; + int flags = 0; + + if (!options->long_name) + continue; + + rest = skip_prefix(arg, options->long_name); + if (options->type == OPTION_ARGUMENT) { + if (!rest) + continue; + if (*rest == '=') + return opterror(options, "takes no value", flags); + if (*rest) + continue; + p->out[p->cpidx++] = arg - 2; + return 0; + } + if (!rest) { + if (!prefixcmp(options->long_name, "no-")) { + /* + * The long name itself starts with "no-", so + * accept the option without "no-" so that users + * do not have to enter "no-no-" to get the + * negation. + */ + rest = skip_prefix(arg, options->long_name + 3); + if (rest) { + flags |= OPT_UNSET; + goto match; + } + /* Abbreviated case */ + if (!prefixcmp(options->long_name + 3, arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + } + /* abbreviated? */ + if (!strncmp(options->long_name, arg, arg_end - arg)) { +is_abbreviated: + if (abbrev_option) { + /* + * If this is abbreviated, it is + * ambiguous. So when there is no + * exact match later, we need to + * error out. + */ + ambiguous_option = abbrev_option; + ambiguous_flags = abbrev_flags; + } + if (!(flags & OPT_UNSET) && *arg_end) + p->opt = arg_end + 1; + abbrev_option = options; + abbrev_flags = flags; + continue; + } + /* negated and abbreviated very much? */ + if (!prefixcmp("no-", arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + /* negated? */ + if (strncmp(arg, "no-", 3)) + continue; + flags |= OPT_UNSET; + rest = skip_prefix(arg + 3, options->long_name); + /* abbreviated and negated? */ + if (!rest && !prefixcmp(options->long_name, arg + 3)) + goto is_abbreviated; + if (!rest) + continue; + } +match: + if (*rest) { + if (*rest != '=') + continue; + p->opt = rest + 1; + } + return get_value(p, options, flags); + } + + if (ambiguous_option) { + fprintf(stderr, + " Error: Ambiguous option: %s (could be --%s%s or --%s%s)", + arg, + (ambiguous_flags & OPT_UNSET) ? "no-" : "", + ambiguous_option->long_name, + (abbrev_flags & OPT_UNSET) ? "no-" : "", + abbrev_option->long_name); + return -1; + } + if (abbrev_option) + return get_value(p, abbrev_option, abbrev_flags); + return -2; +} + +static void check_typos(const char *arg, const struct option *options) +{ + if (strlen(arg) < 3) + return; + + if (!prefixcmp(arg, "no-")) { + fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); + exit(129); + } + + for (; options->type != OPTION_END; options++) { + if (!options->long_name) + continue; + if (!prefixcmp(options->long_name, arg)) { + fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); + exit(129); + } + } +} + +static void parse_options_start(struct parse_opt_ctx_t *ctx, + int argc, const char **argv, int flags) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->argc = argc - 1; + ctx->argv = argv + 1; + ctx->out = argv; + ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); + ctx->flags = flags; + if ((flags & PARSE_OPT_KEEP_UNKNOWN) && + (flags & PARSE_OPT_STOP_AT_NON_OPTION)) + die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); +} + +static int usage_with_options_internal(const char * const *, + const struct option *, int, + struct parse_opt_ctx_t *); + +static int parse_options_step(struct parse_opt_ctx_t *ctx, + const struct option *options, + const char * const usagestr[]) +{ + int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); + int excl_short_opt = 1; + const char *arg; + + /* we must reset ->opt, unknown short option leave it dangling */ + ctx->opt = NULL; + + for (; ctx->argc; ctx->argc--, ctx->argv++) { + arg = ctx->argv[0]; + if (*arg != '-' || !arg[1]) { + if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) + break; + ctx->out[ctx->cpidx++] = ctx->argv[0]; + continue; + } + + if (arg[1] != '-') { + ctx->opt = ++arg; + if (internal_help && *ctx->opt == 'h') { + return usage_with_options_internal(usagestr, options, 0, ctx); + } + switch (parse_short_opt(ctx, options)) { + case -1: + return parse_options_usage(usagestr, options, arg, 1); + case -2: + goto unknown; + case -3: + goto exclusive; + default: + break; + } + if (ctx->opt) + check_typos(arg, options); + while (ctx->opt) { + if (internal_help && *ctx->opt == 'h') + return usage_with_options_internal(usagestr, options, 0, ctx); + arg = ctx->opt; + switch (parse_short_opt(ctx, options)) { + case -1: + return parse_options_usage(usagestr, options, arg, 1); + case -2: + /* fake a short option thing to hide the fact that we may have + * started to parse aggregated stuff + * + * This is leaky, too bad. + */ + ctx->argv[0] = strdup(ctx->opt - 1); + *(char *)ctx->argv[0] = '-'; + goto unknown; + case -3: + goto exclusive; + default: + break; + } + } + continue; + } + + if (!arg[2]) { /* "--" */ + if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { + ctx->argc--; + ctx->argv++; + } + break; + } + + arg += 2; + if (internal_help && !strcmp(arg, "help-all")) + return usage_with_options_internal(usagestr, options, 1, ctx); + if (internal_help && !strcmp(arg, "help")) + return usage_with_options_internal(usagestr, options, 0, ctx); + if (!strcmp(arg, "list-opts")) + return PARSE_OPT_LIST_OPTS; + if (!strcmp(arg, "list-cmds")) + return PARSE_OPT_LIST_SUBCMDS; + switch (parse_long_opt(ctx, arg, options)) { + case -1: + return parse_options_usage(usagestr, options, arg, 0); + case -2: + goto unknown; + case -3: + excl_short_opt = 0; + goto exclusive; + default: + break; + } + continue; +unknown: + if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) + return PARSE_OPT_UNKNOWN; + ctx->out[ctx->cpidx++] = ctx->argv[0]; + ctx->opt = NULL; + } + return PARSE_OPT_DONE; + +exclusive: + parse_options_usage(usagestr, options, arg, excl_short_opt); + if ((excl_short_opt && ctx->excl_opt->short_name) || + ctx->excl_opt->long_name == NULL) { + char opt = ctx->excl_opt->short_name; + parse_options_usage(NULL, options, &opt, 1); + } else { + parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); + } + return PARSE_OPT_HELP; +} + +static int parse_options_end(struct parse_opt_ctx_t *ctx) +{ + memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); + ctx->out[ctx->cpidx + ctx->argc] = NULL; + return ctx->cpidx + ctx->argc; +} + +int parse_options_subcommand(int argc, const char **argv, const struct option *options, + const char *const subcommands[], const char *usagestr[], int flags) +{ + struct parse_opt_ctx_t ctx; + + /* build usage string if it's not provided */ + if (subcommands && !usagestr[0]) { + char *buf = NULL; + + astrcatf(&buf, "%s %s [] {", subcmd_config.exec_name, argv[0]); + + for (int i = 0; subcommands[i]; i++) { + if (i) + astrcat(&buf, "|"); + astrcat(&buf, subcommands[i]); + } + astrcat(&buf, "}"); + + usagestr[0] = buf; + } + + parse_options_start(&ctx, argc, argv, flags); + switch (parse_options_step(&ctx, options, usagestr)) { + case PARSE_OPT_HELP: + exit(129); + case PARSE_OPT_DONE: + break; + case PARSE_OPT_LIST_OPTS: + while (options->type != OPTION_END) { + if (options->long_name) + printf("--%s ", options->long_name); + options++; + } + putchar('\n'); + exit(130); + case PARSE_OPT_LIST_SUBCMDS: + if (subcommands) { + for (int i = 0; subcommands[i]; i++) + printf("%s ", subcommands[i]); + } + putchar('\n'); + exit(130); + default: /* PARSE_OPT_UNKNOWN */ + if (ctx.argv[0][1] == '-') + astrcatf(&error_buf, "unknown option `%s'", + ctx.argv[0] + 2); + else + astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); + usage_with_options(usagestr, options); + } + + return parse_options_end(&ctx); +} + +int parse_options(int argc, const char **argv, const struct option *options, + const char * const usagestr[], int flags) +{ + return parse_options_subcommand(argc, argv, options, NULL, + (const char **) usagestr, flags); +} + +#define USAGE_OPTS_WIDTH 24 +#define USAGE_GAP 2 + +static void print_option_help(const struct option *opts, int full) +{ + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + fputc('\n', stderr); + if (*opts->help) + fprintf(stderr, "%s\n", opts->help); + return; + } + if (!full && (opts->flags & PARSE_OPT_HIDDEN)) + return; + if (opts->flags & PARSE_OPT_DISABLED) + return; + + pos = fprintf(stderr, " "); + if (opts->short_name) + pos += fprintf(stderr, "-%c", opts->short_name); + else + pos += fprintf(stderr, " "); + + if (opts->long_name && opts->short_name) + pos += fprintf(stderr, ", "); + if (opts->long_name) + pos += fprintf(stderr, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_ARGUMENT: + break; + case OPTION_LONG: + case OPTION_U64: + case OPTION_INTEGER: + case OPTION_UINTEGER: + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=]"); + else + pos += fprintf(stderr, "[]"); + else + pos += fprintf(stderr, " "); + break; + case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=<%s>]", opts->argh); + else + pos += fprintf(stderr, "[<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + pos += fprintf(stderr, "[=...]"); + else + pos += fprintf(stderr, "[...]"); + else + pos += fprintf(stderr, " ..."); + } + break; + default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ + case OPTION_END: + case OPTION_GROUP: + case OPTION_BIT: + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + break; + } + + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', stderr); + pad = USAGE_OPTS_WIDTH; + } + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + if (opts->flags & PARSE_OPT_NOBUILD) + fprintf(stderr, "%*s(not built-in because %s)\n", + USAGE_OPTS_WIDTH + USAGE_GAP, "", + opts->build_opt); +} + +static int option__cmp(const void *va, const void *vb) +{ + const struct option *a = va, *b = vb; + int sa = tolower(a->short_name), sb = tolower(b->short_name), ret; + + if (sa == 0) + sa = 'z' + 1; + if (sb == 0) + sb = 'z' + 1; + + ret = sa - sb; + + if (ret == 0) { + const char *la = a->long_name ?: "", + *lb = b->long_name ?: ""; + ret = strcmp(la, lb); + } + + return ret; +} + +static struct option *options__order(const struct option *opts) +{ + int nr_opts = 0, len; + const struct option *o = opts; + struct option *ordered; + + for (o = opts; o->type != OPTION_END; o++) + ++nr_opts; + + len = sizeof(*o) * (nr_opts + 1); + ordered = malloc(len); + if (!ordered) + goto out; + memcpy(ordered, opts, len); + + qsort(ordered, nr_opts, sizeof(*o), option__cmp); +out: + return ordered; +} + +static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx) +{ + int i; + + for (i = 1; i < ctx->argc; ++i) { + const char *arg = ctx->argv[i]; + + if (arg[0] != '-') { + if (arg[1] == '\0') { + if (arg[0] == opt->short_name) + return true; + continue; + } + + if (opt->long_name && strcmp(opt->long_name, arg) == 0) + return true; + + if (opt->help && strcasestr(opt->help, arg) != NULL) + return true; + + continue; + } + + if (arg[1] == opt->short_name || + (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0)) + return true; + } + + return false; +} + +static int usage_with_options_internal(const char * const *usagestr, + const struct option *opts, int full, + struct parse_opt_ctx_t *ctx) +{ + struct option *ordered; + + if (!usagestr) + return PARSE_OPT_HELP; + + setup_pager(); + + if (error_buf) { + fprintf(stderr, " Error: %s\n", error_buf); + zfree(&error_buf); + } + + fprintf(stderr, "\n Usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) { + fprintf(stderr, "%s%s\n", + **usagestr ? " " : "", + *usagestr); + usagestr++; + } + + if (opts->type != OPTION_GROUP) + fputc('\n', stderr); + + ordered = options__order(opts); + if (ordered) + opts = ordered; + + for ( ; opts->type != OPTION_END; opts++) { + if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx)) + continue; + print_option_help(opts, full); + } + + fputc('\n', stderr); + + free(ordered); + + return PARSE_OPT_HELP; +} + +void usage_with_options(const char * const *usagestr, + const struct option *opts) +{ + usage_with_options_internal(usagestr, opts, 0, NULL); + exit(129); +} + +void usage_with_options_msg(const char * const *usagestr, + const struct option *opts, const char *fmt, ...) +{ + va_list ap; + char *tmp = error_buf; + + va_start(ap, fmt); + if (vasprintf(&error_buf, fmt, ap) == -1) + die("vasprintf failed"); + va_end(ap); + + free(tmp); + + usage_with_options_internal(usagestr, opts, 0, NULL); + exit(129); +} + +int parse_options_usage(const char * const *usagestr, + const struct option *opts, + const char *optstr, bool short_opt) +{ + if (!usagestr) + goto opt; + + fprintf(stderr, "\n Usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) { + fprintf(stderr, "%s%s\n", + **usagestr ? " " : "", + *usagestr); + usagestr++; + } + fputc('\n', stderr); + +opt: + for ( ; opts->type != OPTION_END; opts++) { + if (short_opt) { + if (opts->short_name == *optstr) { + print_option_help(opts, 0); + break; + } + continue; + } + + if (opts->long_name == NULL) + continue; + + if (!prefixcmp(opts->long_name, optstr)) + print_option_help(opts, 0); + if (!prefixcmp("no-", optstr) && + !prefixcmp(opts->long_name, optstr + 3)) + print_option_help(opts, 0); + } + + return PARSE_OPT_HELP; +} + + +int parse_opt_verbosity_cb(const struct option *opt, + const char *arg __maybe_unused, + int unset) +{ + int *target = opt->value; + + if (unset) + /* --no-quiet, --no-verbose */ + *target = 0; + else if (opt->short_name == 'v') { + if (*target >= 0) + (*target)++; + else + *target = 1; + } else { + if (*target <= 0) + (*target)--; + else + *target = -1; + } + return 0; +} + +static struct option * +find_option(struct option *opts, int shortopt, const char *longopt) +{ + for (; opts->type != OPTION_END; opts++) { + if ((shortopt && opts->short_name == shortopt) || + (opts->long_name && longopt && + !strcmp(opts->long_name, longopt))) + return opts; + } + return NULL; +} + +void set_option_flag(struct option *opts, int shortopt, const char *longopt, + int flag) +{ + struct option *opt = find_option(opts, shortopt, longopt); + + if (opt) + opt->flags |= flag; + return; +} + +void set_option_nobuild(struct option *opts, int shortopt, + const char *longopt, + const char *build_opt, + bool can_skip) +{ + struct option *opt = find_option(opts, shortopt, longopt); + + if (!opt) + return; + + opt->flags |= PARSE_OPT_NOBUILD; + opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; + opt->build_opt = build_opt; +} diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h new file mode 100644 index 000000000000..dec893f10477 --- /dev/null +++ b/tools/lib/subcmd/parse-options.h @@ -0,0 +1,228 @@ +#ifndef __PERF_PARSE_OPTIONS_H +#define __PERF_PARSE_OPTIONS_H + +#include +#include + +enum parse_opt_type { + /* special types */ + OPTION_END, + OPTION_ARGUMENT, + OPTION_GROUP, + /* options with no arguments */ + OPTION_BIT, + OPTION_BOOLEAN, + OPTION_INCR, + OPTION_SET_UINT, + OPTION_SET_PTR, + /* options with arguments (usually) */ + OPTION_STRING, + OPTION_INTEGER, + OPTION_LONG, + OPTION_CALLBACK, + OPTION_U64, + OPTION_UINTEGER, +}; + +enum parse_opt_flags { + PARSE_OPT_KEEP_DASHDASH = 1, + PARSE_OPT_STOP_AT_NON_OPTION = 2, + PARSE_OPT_KEEP_ARGV0 = 4, + PARSE_OPT_KEEP_UNKNOWN = 8, + PARSE_OPT_NO_INTERNAL_HELP = 16, +}; + +enum parse_opt_option_flags { + PARSE_OPT_OPTARG = 1, + PARSE_OPT_NOARG = 2, + PARSE_OPT_NONEG = 4, + PARSE_OPT_HIDDEN = 8, + PARSE_OPT_LASTARG_DEFAULT = 16, + PARSE_OPT_DISABLED = 32, + PARSE_OPT_EXCLUSIVE = 64, + PARSE_OPT_NOEMPTY = 128, + PARSE_OPT_NOBUILD = 256, + PARSE_OPT_CANSKIP = 512, +}; + +struct option; +typedef int parse_opt_cb(const struct option *, const char *arg, int unset); + +/* + * `type`:: + * holds the type of the option, you must have an OPTION_END last in your + * array. + * + * `short_name`:: + * the character to use as a short option name, '\0' if none. + * + * `long_name`:: + * the long option name, without the leading dashes, NULL if none. + * + * `value`:: + * stores pointers to the values to be filled. + * + * `argh`:: + * token to explain the kind of argument this option wants. Keep it + * homogenous across the repository. + * + * `help`:: + * the short help associated to what the option does. + * Must never be NULL (except for OPTION_END). + * OPTION_GROUP uses this pointer to store the group header. + * + * `flags`:: + * mask of parse_opt_option_flags. + * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) + * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs + * PARSE_OPT_NONEG: says that this option cannot be negated + * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in + * the long one. + * + * `callback`:: + * pointer to the callback to use for OPTION_CALLBACK. + * + * `defval`:: + * default value to fill (*->value) with for PARSE_OPT_OPTARG. + * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in + * the value when met. + * CALLBACKS can use it like they want. + * + * `set`:: + * whether an option was set by the user + */ +struct option { + enum parse_opt_type type; + int short_name; + const char *long_name; + void *value; + const char *argh; + const char *help; + const char *build_opt; + + int flags; + parse_opt_cb *callback; + intptr_t defval; + bool *set; + void *data; +}; + +#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) + +#define OPT_END() { .type = OPTION_END } +#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } +#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } +#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } +#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_FLAG(s, l, v, h, f) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ + { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, bool *), .help = (h), \ + .set = check_vtype(os, bool *)} +#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } +#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } +#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } +#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } +#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } +#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } +#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } +#define OPT_STRING_OPTARG(s, l, v, a, h, d) \ + { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, const char **), (a), .help = (h), \ + .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } +#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} +#define OPT_DATE(s, l, v, h) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } +#define OPT_CALLBACK(s, l, v, a, h, f) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } +#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } +#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } +#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ + .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ + .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} +#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ + .value = (v), (a), .help = (h), .callback = (f), \ + .flags = PARSE_OPT_OPTARG, .data = (d) } + +/* parse_options() will filter out the processed options and leave the + * non-option argments in argv[]. + * Returns the number of arguments left in argv[]. + * + * NOTE: parse_options() and parse_options_subcommand() may call exit() in the + * case of an error (or for 'special' options like --list-cmds or --list-opts). + */ +extern int parse_options(int argc, const char **argv, + const struct option *options, + const char * const usagestr[], int flags); + +extern int parse_options_subcommand(int argc, const char **argv, + const struct option *options, + const char *const subcommands[], + const char *usagestr[], int flags); + +extern NORETURN void usage_with_options(const char * const *usagestr, + const struct option *options); +extern NORETURN __attribute__((format(printf,3,4))) +void usage_with_options_msg(const char * const *usagestr, + const struct option *options, + const char *fmt, ...); + +/*----- incremantal advanced APIs -----*/ + +enum { + PARSE_OPT_HELP = -1, + PARSE_OPT_DONE, + PARSE_OPT_LIST_OPTS, + PARSE_OPT_LIST_SUBCMDS, + PARSE_OPT_UNKNOWN, +}; + +/* + * It's okay for the caller to consume argv/argc in the usual way. + * Other fields of that structure are private to parse-options and should not + * be modified in any way. + */ +struct parse_opt_ctx_t { + const char **argv; + const char **out; + int argc, cpidx; + const char *opt; + const struct option *excl_opt; + int flags; +}; + +extern int parse_options_usage(const char * const *usagestr, + const struct option *opts, + const char *optstr, + bool short_opt); + + +/*----- some often used options -----*/ +extern int parse_opt_abbrev_cb(const struct option *, const char *, int); +extern int parse_opt_approxidate_cb(const struct option *, const char *, int); +extern int parse_opt_verbosity_cb(const struct option *, const char *, int); + +#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") +#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") +#define OPT__VERBOSITY(var) \ + { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ + PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ + { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ + PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } +#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") +#define OPT__ABBREV(var) \ + { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ + "use digits to display SHA-1s", \ + PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } + +extern const char *parse_options_fix_filename(const char *prefix, const char *file); + +void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); +void set_option_nobuild(struct option *opts, int shortopt, const char *longopt, + const char *build_opt, bool can_skip); +#endif /* __PERF_PARSE_OPTIONS_H */ diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c new file mode 100644 index 000000000000..f4f6c9eb8e59 --- /dev/null +++ b/tools/lib/subcmd/run-command.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" +#include "run-command.h" +#include "exec-cmd.h" + +#define STRERR_BUFSIZE 128 + +static inline void close_pair(int fd[2]) +{ + close(fd[0]); + close(fd[1]); +} + +static inline void dup_devnull(int to) +{ + int fd = open("/dev/null", O_RDWR); + dup2(fd, to); + close(fd); +} + +int start_command(struct child_process *cmd) +{ + int need_in, need_out, need_err; + int fdin[2], fdout[2], fderr[2]; + char sbuf[STRERR_BUFSIZE]; + + /* + * In case of errors we must keep the promise to close FDs + * that have been passed in via ->in and ->out. + */ + + need_in = !cmd->no_stdin && cmd->in < 0; + if (need_in) { + if (pipe(fdin) < 0) { + if (cmd->out > 0) + close(cmd->out); + return -ERR_RUN_COMMAND_PIPE; + } + cmd->in = fdin[1]; + } + + need_out = !cmd->no_stdout + && !cmd->stdout_to_stderr + && cmd->out < 0; + if (need_out) { + if (pipe(fdout) < 0) { + if (need_in) + close_pair(fdin); + else if (cmd->in) + close(cmd->in); + return -ERR_RUN_COMMAND_PIPE; + } + cmd->out = fdout[0]; + } + + need_err = !cmd->no_stderr && cmd->err < 0; + if (need_err) { + if (pipe(fderr) < 0) { + if (need_in) + close_pair(fdin); + else if (cmd->in) + close(cmd->in); + if (need_out) + close_pair(fdout); + else if (cmd->out) + close(cmd->out); + return -ERR_RUN_COMMAND_PIPE; + } + cmd->err = fderr[0]; + } + + fflush(NULL); + cmd->pid = fork(); + if (!cmd->pid) { + if (cmd->no_stdin) + dup_devnull(0); + else if (need_in) { + dup2(fdin[0], 0); + close_pair(fdin); + } else if (cmd->in) { + dup2(cmd->in, 0); + close(cmd->in); + } + + if (cmd->no_stderr) + dup_devnull(2); + else if (need_err) { + dup2(fderr[1], 2); + close_pair(fderr); + } + + if (cmd->no_stdout) + dup_devnull(1); + else if (cmd->stdout_to_stderr) + dup2(2, 1); + else if (need_out) { + dup2(fdout[1], 1); + close_pair(fdout); + } else if (cmd->out > 1) { + dup2(cmd->out, 1); + close(cmd->out); + } + + if (cmd->dir && chdir(cmd->dir)) + die("exec %s: cd to %s failed (%s)", cmd->argv[0], + cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); + if (cmd->env) { + for (; *cmd->env; cmd->env++) { + if (strchr(*cmd->env, '=')) + putenv((char*)*cmd->env); + else + unsetenv(*cmd->env); + } + } + if (cmd->preexec_cb) + cmd->preexec_cb(); + if (cmd->exec_cmd) { + execv_cmd(cmd->argv); + } else { + execvp(cmd->argv[0], (char *const*) cmd->argv); + } + exit(127); + } + + if (cmd->pid < 0) { + int err = errno; + if (need_in) + close_pair(fdin); + else if (cmd->in) + close(cmd->in); + if (need_out) + close_pair(fdout); + else if (cmd->out) + close(cmd->out); + if (need_err) + close_pair(fderr); + return err == ENOENT ? + -ERR_RUN_COMMAND_EXEC : + -ERR_RUN_COMMAND_FORK; + } + + if (need_in) + close(fdin[0]); + else if (cmd->in) + close(cmd->in); + + if (need_out) + close(fdout[1]); + else if (cmd->out) + close(cmd->out); + + if (need_err) + close(fderr[1]); + + return 0; +} + +static int wait_or_whine(pid_t pid) +{ + char sbuf[STRERR_BUFSIZE]; + + for (;;) { + int status, code; + pid_t waiting = waitpid(pid, &status, 0); + + if (waiting < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, " Error: waitpid failed (%s)", + strerror_r(errno, sbuf, sizeof(sbuf))); + return -ERR_RUN_COMMAND_WAITPID; + } + if (waiting != pid) + return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; + if (WIFSIGNALED(status)) + return -ERR_RUN_COMMAND_WAITPID_SIGNAL; + + if (!WIFEXITED(status)) + return -ERR_RUN_COMMAND_WAITPID_NOEXIT; + code = WEXITSTATUS(status); + switch (code) { + case 127: + return -ERR_RUN_COMMAND_EXEC; + case 0: + return 0; + default: + return -code; + } + } +} + +int finish_command(struct child_process *cmd) +{ + return wait_or_whine(cmd->pid); +} + +int run_command(struct child_process *cmd) +{ + int code = start_command(cmd); + if (code) + return code; + return finish_command(cmd); +} + +static void prepare_run_command_v_opt(struct child_process *cmd, + const char **argv, + int opt) +{ + memset(cmd, 0, sizeof(*cmd)); + cmd->argv = argv; + cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; + cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; + cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; +} + +int run_command_v_opt(const char **argv, int opt) +{ + struct child_process cmd; + prepare_run_command_v_opt(&cmd, argv, opt); + return run_command(&cmd); +} diff --git a/tools/lib/subcmd/run-command.h b/tools/lib/subcmd/run-command.h new file mode 100644 index 000000000000..4a55393a6547 --- /dev/null +++ b/tools/lib/subcmd/run-command.h @@ -0,0 +1,60 @@ +#ifndef __PERF_RUN_COMMAND_H +#define __PERF_RUN_COMMAND_H + +#include + +enum { + ERR_RUN_COMMAND_FORK = 10000, + ERR_RUN_COMMAND_EXEC, + ERR_RUN_COMMAND_PIPE, + ERR_RUN_COMMAND_WAITPID, + ERR_RUN_COMMAND_WAITPID_WRONG_PID, + ERR_RUN_COMMAND_WAITPID_SIGNAL, + ERR_RUN_COMMAND_WAITPID_NOEXIT, +}; +#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) + +struct child_process { + const char **argv; + pid_t pid; + /* + * Using .in, .out, .err: + * - Specify 0 for no redirections (child inherits stdin, stdout, + * stderr from parent). + * - Specify -1 to have a pipe allocated as follows: + * .in: returns the writable pipe end; parent writes to it, + * the readable pipe end becomes child's stdin + * .out, .err: returns the readable pipe end; parent reads from + * it, the writable pipe end becomes child's stdout/stderr + * The caller of start_command() must close the returned FDs + * after it has completed reading from/writing to it! + * - Specify > 0 to set a channel to a particular FD as follows: + * .in: a readable FD, becomes child's stdin + * .out: a writable FD, becomes child's stdout/stderr + * .err > 0 not supported + * The specified FD is closed by start_command(), even in case + * of errors! + */ + int in; + int out; + int err; + const char *dir; + const char *const *env; + unsigned no_stdin:1; + unsigned no_stdout:1; + unsigned no_stderr:1; + unsigned exec_cmd:1; /* if this is to be external sub-command */ + unsigned stdout_to_stderr:1; + void (*preexec_cb)(void); +}; + +int start_command(struct child_process *); +int finish_command(struct child_process *); +int run_command(struct child_process *); + +#define RUN_COMMAND_NO_STDIN 1 +#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */ +#define RUN_COMMAND_STDOUT_TO_STDERR 4 +int run_command_v_opt(const char **argv, int opt); + +#endif /* __PERF_RUN_COMMAND_H */ diff --git a/tools/lib/subcmd/sigchain.c b/tools/lib/subcmd/sigchain.c new file mode 100644 index 000000000000..3537c348a18e --- /dev/null +++ b/tools/lib/subcmd/sigchain.c @@ -0,0 +1,53 @@ +#include +#include "subcmd-util.h" +#include "sigchain.h" + +#define SIGCHAIN_MAX_SIGNALS 32 + +struct sigchain_signal { + sigchain_fun *old; + int n; + int alloc; +}; +static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; + +static void check_signum(int sig) +{ + if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) + die("BUG: signal out of range: %d", sig); +} + +static int sigchain_push(int sig, sigchain_fun f) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + + ALLOC_GROW(s->old, s->n + 1, s->alloc); + s->old[s->n] = signal(sig, f); + if (s->old[s->n] == SIG_ERR) + return -1; + s->n++; + return 0; +} + +int sigchain_pop(int sig) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + if (s->n < 1) + return 0; + + if (signal(sig, s->old[s->n - 1]) == SIG_ERR) + return -1; + s->n--; + return 0; +} + +void sigchain_push_common(sigchain_fun f) +{ + sigchain_push(SIGINT, f); + sigchain_push(SIGHUP, f); + sigchain_push(SIGTERM, f); + sigchain_push(SIGQUIT, f); + sigchain_push(SIGPIPE, f); +} diff --git a/tools/lib/subcmd/sigchain.h b/tools/lib/subcmd/sigchain.h new file mode 100644 index 000000000000..959d64eb5557 --- /dev/null +++ b/tools/lib/subcmd/sigchain.h @@ -0,0 +1,10 @@ +#ifndef __PERF_SIGCHAIN_H +#define __PERF_SIGCHAIN_H + +typedef void (*sigchain_fun)(int); + +int sigchain_pop(int sig); + +void sigchain_push_common(sigchain_fun f); + +#endif /* __PERF_SIGCHAIN_H */ diff --git a/tools/lib/subcmd/subcmd-config.c b/tools/lib/subcmd/subcmd-config.c new file mode 100644 index 000000000000..d017c728bd1b --- /dev/null +++ b/tools/lib/subcmd/subcmd-config.c @@ -0,0 +1,11 @@ +#include "subcmd-config.h" + +#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED" + +struct subcmd_config subcmd_config = { + .exec_name = UNDEFINED, + .prefix = UNDEFINED, + .exec_path = UNDEFINED, + .exec_path_env = UNDEFINED, + .pager_env = UNDEFINED, +}; diff --git a/tools/lib/subcmd/subcmd-config.h b/tools/lib/subcmd/subcmd-config.h new file mode 100644 index 000000000000..cc8514030b5c --- /dev/null +++ b/tools/lib/subcmd/subcmd-config.h @@ -0,0 +1,14 @@ +#ifndef __PERF_SUBCMD_CONFIG_H +#define __PERF_SUBCMD_CONFIG_H + +struct subcmd_config { + const char *exec_name; + const char *prefix; + const char *exec_path; + const char *exec_path_env; + const char *pager_env; +}; + +extern struct subcmd_config subcmd_config; + +#endif /* __PERF_SUBCMD_CONFIG_H */ diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h new file mode 100644 index 000000000000..321aeb11a381 --- /dev/null +++ b/tools/lib/subcmd/subcmd-util.h @@ -0,0 +1,91 @@ +#ifndef __PERF_SUBCMD_UTIL_H +#define __PERF_SUBCMD_UTIL_H + +#include +#include +#include + +#define NORETURN __attribute__((__noreturn__)) + +static inline void report(const char *prefix, const char *err, va_list params) +{ + char msg[1024]; + vsnprintf(msg, sizeof(msg), err, params); + fprintf(stderr, " %s%s\n", prefix, msg); +} + +static NORETURN inline void die(const char *err, ...) +{ + va_list params; + + va_start(params, err); + report(" Fatal: ", err, params); + exit(128); + va_end(params); +} + +#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) + +#define alloc_nr(x) (((x)+16)*3/2) + +/* + * Realloc the buffer pointed at by variable 'x' so that it can hold + * at least 'nr' entries; the number of entries currently allocated + * is 'alloc', using the standard growing factor alloc_nr() macro. + * + * DO NOT USE any expression with side-effect for 'x' or 'alloc'. + */ +#define ALLOC_GROW(x, nr, alloc) \ + do { \ + if ((nr) > alloc) { \ + if (alloc_nr(alloc) < (nr)) \ + alloc = (nr); \ + else \ + alloc = alloc_nr(alloc); \ + x = xrealloc((x), alloc * sizeof(*(x))); \ + } \ + } while(0) + +static inline void *xrealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) { + ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) + die("Out of memory, realloc failed"); + } + return ret; +} + +#define astrcatf(out, fmt, ...) \ +({ \ + char *tmp = *(out); \ + if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \ + die("asprintf failed"); \ + free(tmp); \ +}) + +static inline void astrcat(char **out, const char *add) +{ + char *tmp = *out; + + if (asprintf(out, "%s%s", tmp ?: "", add) == -1) + die("asprintf failed"); + + free(tmp); +} + +static inline int prefixcmp(const char *str, const char *prefix) +{ + for (; ; str++, prefix++) + if (!*prefix) + return 0; + else if (*str != *prefix) + return (unsigned char)*prefix - (unsigned char)*str; +} + +#endif /* __PERF_SUBCMD_UTIL_H */ diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 2562eac6451d..ce3932ee4893 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -20,6 +20,7 @@ tools/lib/traceevent tools/lib/bpf tools/lib/api tools/lib/bpf +tools/lib/subcmd tools/lib/hweight.c tools/lib/rbtree.c tools/lib/string.c diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 388ec64fa39b..569fcf022531 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -145,9 +145,10 @@ BISON = bison STRIP = strip AWK = awk -LIB_DIR = $(srctree)/tools/lib/api/ +LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ -BPF_DIR = $(srctree)/tools/lib/bpf/ +BPF_DIR = $(srctree)/tools/lib/bpf/ +SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ # include config/Makefile by default and rule out # non-config cases @@ -184,6 +185,7 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) BPF_PATH=$(OUTPUT) + SUBCMD_PATH=$(OUTPUT) ifneq ($(subdir),) API_PATH=$(OUTPUT)/../lib/api/ else @@ -193,6 +195,7 @@ else TE_PATH=$(TRACE_EVENT_DIR) API_PATH=$(LIB_DIR) BPF_PATH=$(BPF_DIR) + SUBCMD_PATH=$(SUBCMD_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a @@ -206,6 +209,8 @@ export LIBAPI LIBBPF = $(BPF_PATH)libbpf.a +LIBSUBCMD = $(SUBCMD_PATH)libsubcmd.a + # python extension build directories PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ @@ -257,7 +262,7 @@ export PERL_PATH LIB_FILE=$(OUTPUT)libperf.a -PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) +PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) $(LIBSUBCMD) ifndef NO_LIBBPF PERFLIBS += $(LIBBPF) endif @@ -437,6 +442,13 @@ $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null +$(LIBSUBCMD): fixdep FORCE + $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) $(OUTPUT)libsubcmd.a + +$(LIBSUBCMD)-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) clean + help: @echo 'Perf make targets:' @echo ' doc - make *all* documentation (see below)' @@ -584,7 +596,7 @@ config-clean: $(call QUIET_CLEAN, config) $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null -clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean +clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT).config-detected diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index b02af064f0f9..b64d46285ebb 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -26,7 +26,7 @@ #include "../../util/evlist.h" #include "../../util/evsel.h" #include "../../util/cpumap.h" -#include "../../util/parse-options.h" +#include #include "../../util/parse-events.h" #include "../../util/pmu.h" #include "../../util/debug.h" diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index fc9bebd2cca0..0999ac536d86 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c index bc6a16adbca8..6a18ce21f865 100644 --- a/tools/perf/bench/futex-lock-pi.c +++ b/tools/perf/bench/futex-lock-pi.c @@ -5,7 +5,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index ad0d9b5342fb..718238683013 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c index 6d8c9fa2a16c..91aaf2a1fa90 100644 --- a/tools/perf/bench/futex-wake-parallel.c +++ b/tools/perf/bench/futex-wake-parallel.c @@ -10,7 +10,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index e5e41d3bdce7..f416bd705f66 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c index 9419b944220f..a91aa85d80ff 100644 --- a/tools/perf/bench/mem-functions.c +++ b/tools/perf/bench/mem-functions.c @@ -8,7 +8,7 @@ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "../util/cloexec.h" #include "bench.h" diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 492df2752a2d..5049d6357a46 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -7,7 +7,7 @@ #include "../perf.h" #include "../builtin.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../util/cloexec.h" #include "bench.h" diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index d4ff1b539cfd..bfaf9503de8e 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../builtin.h" #include "bench.h" diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 005cc283790c..1dc2d13cc272 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -10,7 +10,7 @@ */ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../builtin.h" #include "bench.h" diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1f00dc7cecba..e18f1b995ffd 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -21,7 +21,7 @@ #include "util/evsel.h" #include "util/annotate.h" #include "util/event.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/thread.h" #include "util/sort.h" diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index b17aed36ca16..a1cddc6bbf0f 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -16,7 +16,7 @@ */ #include "perf.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "builtin.h" #include "bench/bench.h" diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 7b8450cd33c2..d93bff7fc0e4 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -16,7 +16,7 @@ #include "util/cache.h" #include "util/debug.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/strlist.h" #include "util/build-id.h" #include "util/session.h" diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 6419f57b0850..5e914ee79eb3 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -12,7 +12,7 @@ #include "util/build-id.h" #include "util/cache.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/session.h" #include "util/symbol.h" #include "util/data.h" diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c index 427ea7a705b8..f04e804a9fad 100644 --- a/tools/perf/builtin-config.c +++ b/tools/perf/builtin-config.c @@ -9,7 +9,7 @@ #include "perf.h" #include "util/cache.h" -#include "util/parse-options.h" +#include #include "util/util.h" #include "util/debug.h" diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c index d6525bc54d13..b97bc1518b44 100644 --- a/tools/perf/builtin-data.c +++ b/tools/perf/builtin-data.c @@ -2,7 +2,7 @@ #include "builtin.h" #include "perf.h" #include "debug.h" -#include "parse-options.h" +#include #include "data-convert-bt.h" typedef int (*data_cmd_fn_t)(int argc, const char **argv, const char *prefix); diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index f4d62510acbb..08a7d36a2cf8 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -12,7 +12,7 @@ #include "util/evlist.h" #include "util/evsel.h" #include "util/parse-events.h" -#include "util/parse-options.h" +#include #include "util/session.h" #include "util/data.h" #include "util/debug.h" diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 275aa641c31c..96c1a4cfbbbf 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -6,11 +6,11 @@ #include "perf.h" #include "util/cache.h" #include "builtin.h" -#include "util/exec_cmd.h" +#include #include "common-cmds.h" -#include "util/parse-options.h" -#include "util/run-command.h" -#include "util/help.h" +#include +#include +#include #include "util/debug.h" static struct man_viewer_list { diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 99d127fe9c35..0022e02ed31a 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -18,7 +18,7 @@ #include "util/data.h" #include "util/auxtrace.h" -#include "util/parse-options.h" +#include #include diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 93ce665f976f..118010553d0c 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -12,7 +12,7 @@ #include "util/tool.h" #include "util/callchain.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/data.h" #include "util/cpumap.h" diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 031f9f55c281..4418d9214872 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -10,7 +10,7 @@ #include "util/header.h" #include "util/session.h" #include "util/intlist.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" #include "util/tool.h" diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index bf679e2c978b..5e22db4684b8 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -14,7 +14,7 @@ #include "util/parse-events.h" #include "util/cache.h" #include "util/pmu.h" -#include "util/parse-options.h" +#include int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) { diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index de16aaed516e..ce3bfb48b26f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -9,7 +9,7 @@ #include "util/thread.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 80170aace5d4..390170041696 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -1,7 +1,7 @@ #include "builtin.h" #include "perf.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/tool.h" #include "util/session.h" diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index dbe2ea5a2932..9af859b28b15 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@ #include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/probe-finder.h" #include "util/probe-event.h" #include "util/probe-file.h" diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a3b4930737c6..1435ef6265b6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -11,7 +11,7 @@ #include "util/build-id.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/callchain.h" diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5a454669d075..2a7330b99b82 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -27,7 +27,7 @@ #include "util/session.h" #include "util/tool.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/thread.h" diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index e3d3e32c0a93..871b55ae22a4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -12,7 +12,7 @@ #include "util/tool.h" #include "util/cloexec.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 571016f16c5a..bcc3542d9df5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3,9 +3,9 @@ #include "perf.h" #include "util/cache.h" #include "util/debug.h" -#include "util/exec_cmd.h" +#include #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/perf_regs.h" #include "util/session.h" #include "util/tool.h" diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 25a95f49c36e..bbf42eefd5e5 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -45,7 +45,7 @@ #include "builtin.h" #include "util/cgroup.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/pmu.h" #include "util/event.h" diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 30e59620179d..bd7a7757176f 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -30,7 +30,7 @@ #include "perf.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/event.h" #include "util/session.h" diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 92fe963e43c4..9ebd67a42ede 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -34,7 +34,7 @@ #include "util/top.h" #include "util/util.h" #include -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/cpumap.h" #include "util/xyarray.h" diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c783d8fd3a80..20916dd77aac 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -22,11 +22,11 @@ #include "util/color.h" #include "util/debug.h" #include "util/evlist.h" -#include "util/exec_cmd.h" +#include #include "util/machine.h" #include "util/session.h" #include "util/thread.h" -#include "util/parse-options.h" +#include #include "util/strlist.h" #include "util/intlist.h" #include "util/thread_map.h" diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6894325fe921..cb1d2499c45c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -9,12 +9,12 @@ #include "builtin.h" #include "util/env.h" -#include "util/exec_cmd.h" +#include #include "util/cache.h" #include "util/quote.h" -#include "util/run-command.h" +#include #include "util/parse-events.h" -#include "util/parse-options.h" +#include #include "util/bpf-loader.h" #include "util/debug.h" #include diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 6337f1c07f02..28d1605b0338 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -24,7 +24,7 @@ #include #include "../perf.h" #include "util.h" -#include "exec_cmd.h" +#include #include "tests.h" #define ENV "PERF_TEST_ATTR" diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index fa98406c92e2..0372d5945910 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -11,7 +11,7 @@ #include "tests.h" #include "debug.h" #include "color.h" -#include "parse-options.h" +#include #include "symbol.h" struct test __weak arch_tests[] = { diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 196beefa16a9..94b1099f2c22 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -9,13 +9,10 @@ libperf-y += env.o libperf-y += event.o libperf-y += evlist.o libperf-y += evsel.o -libperf-y += exec_cmd.o libperf-y += find_next_bit.o -libperf-y += help.o libperf-y += kallsyms.o libperf-y += levenshtein.o libperf-y += llvm-utils.o -libperf-y += parse-options.o libperf-y += parse-events.o libperf-y += perf_regs.o libperf-y += path.o @@ -23,7 +20,6 @@ libperf-y += rbtree.o libperf-y += libstring.o libperf-y += bitmap.o libperf-y += hweight.o -libperf-y += run-command.o libperf-y += quote.o libperf-y += strbuf.o libperf-y += string.o @@ -32,11 +28,9 @@ libperf-y += strfilter.o libperf-y += top.o libperf-y += usage.o libperf-y += wrapper.o -libperf-y += sigchain.o libperf-y += dso.o libperf-y += symbol.o libperf-y += color.o -libperf-y += pager.o libperf-y += header.o libperf-y += callchain.o libperf-y += values.o @@ -88,7 +82,6 @@ libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-y += term.o libperf-y += help-unknown-cmd.o -libperf-y += subcmd-config.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 7f10430af39c..360fda01f3b0 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -45,7 +45,7 @@ #include "event.h" #include "session.h" #include "debug.h" -#include "parse-options.h" +#include #include "intel-pt.h" #include "intel-bts.h" diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index fc6a745d2ec6..07b5d63947b1 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -4,7 +4,7 @@ #include #include "util.h" #include "strbuf.h" -#include "pager.h" +#include #include "../perf.h" #include "../ui/ui.h" diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 32e12ecfe9c5..90aa1b46b2e5 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -1,6 +1,6 @@ #include "util.h" #include "../perf.h" -#include "parse-options.h" +#include #include "evsel.h" #include "cgroup.h" #include "evlist.h" diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 2e452ac1353d..d3e12e30e1d5 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -10,7 +10,7 @@ */ #include "util.h" #include "cache.h" -#include "exec_cmd.h" +#include #include "util/hist.h" /* perf_hist_config */ #include "util/llvm-utils.h" /* perf_llvm_config */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d1b6c206bb93..8c44aadb9810 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -18,7 +18,7 @@ #include #include "parse-events.h" -#include "parse-options.h" +#include #include diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c deleted file mode 100644 index e7f9ed7943e3..000000000000 --- a/tools/perf/util/exec_cmd.c +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "exec_cmd.h" -#include "subcmd-config.h" - -#define MAX_ARGS 32 -#define PATH_MAX 4096 - -static const char *argv_exec_path; -static const char *argv0_path; - -void exec_cmd_init(const char *exec_name, const char *prefix, - const char *exec_path, const char *exec_path_env) -{ - subcmd_config.exec_name = exec_name; - subcmd_config.prefix = prefix; - subcmd_config.exec_path = exec_path; - subcmd_config.exec_path_env = exec_path_env; -} - -#define is_dir_sep(c) ((c) == '/') - -static int is_absolute_path(const char *path) -{ - return path[0] == '/'; -} - -static const char *get_pwd_cwd(void) -{ - static char cwd[PATH_MAX + 1]; - char *pwd; - struct stat cwd_stat, pwd_stat; - if (getcwd(cwd, PATH_MAX) == NULL) - return NULL; - pwd = getenv("PWD"); - if (pwd && strcmp(pwd, cwd)) { - stat(cwd, &cwd_stat); - if (!stat(pwd, &pwd_stat) && - pwd_stat.st_dev == cwd_stat.st_dev && - pwd_stat.st_ino == cwd_stat.st_ino) { - strlcpy(cwd, pwd, PATH_MAX); - } - } - return cwd; -} - -static const char *make_nonrelative_path(const char *path) -{ - static char buf[PATH_MAX + 1]; - - if (is_absolute_path(path)) { - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die("Too long path: %.*s", 60, path); - } else { - const char *cwd = get_pwd_cwd(); - if (!cwd) - die("Cannot determine the current working directory"); - if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) - die("Too long path: %.*s", 60, path); - } - return buf; -} - -char *system_path(const char *path) -{ - char *buf = NULL; - - if (is_absolute_path(path)) - return strdup(path); - - astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); - - return buf; -} - -const char *extract_argv0_path(const char *argv0) -{ - const char *slash; - - if (!argv0 || !*argv0) - return NULL; - slash = argv0 + strlen(argv0); - - while (argv0 <= slash && !is_dir_sep(*slash)) - slash--; - - if (slash >= argv0) { - argv0_path = strndup(argv0, slash - argv0); - return argv0_path ? slash + 1 : NULL; - } - - return argv0; -} - -void set_argv_exec_path(const char *exec_path) -{ - argv_exec_path = exec_path; - /* - * Propagate this setting to external programs. - */ - setenv(subcmd_config.exec_path_env, exec_path, 1); -} - - -/* Returns the highest-priority location to look for subprograms. */ -char *get_argv_exec_path(void) -{ - char *env; - - if (argv_exec_path) - return strdup(argv_exec_path); - - env = getenv(subcmd_config.exec_path_env); - if (env && *env) - return strdup(env); - - return system_path(subcmd_config.exec_path); -} - -static void add_path(char **out, const char *path) -{ - if (path && *path) { - if (is_absolute_path(path)) - astrcat(out, path); - else - astrcat(out, make_nonrelative_path(path)); - - astrcat(out, ":"); - } -} - -void setup_path(void) -{ - const char *old_path = getenv("PATH"); - char *new_path = NULL; - char *tmp = get_argv_exec_path(); - - add_path(&new_path, tmp); - add_path(&new_path, argv0_path); - free(tmp); - - if (old_path) - astrcat(&new_path, old_path); - else - astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); - - setenv("PATH", new_path, 1); - - free(new_path); -} - -static const char **prepare_exec_cmd(const char **argv) -{ - int argc; - const char **nargv; - - for (argc = 0; argv[argc]; argc++) - ; /* just counting */ - nargv = malloc(sizeof(*nargv) * (argc + 2)); - - nargv[0] = subcmd_config.exec_name; - for (argc = 0; argv[argc]; argc++) - nargv[argc + 1] = argv[argc]; - nargv[argc + 1] = NULL; - return nargv; -} - -int execv_cmd(const char **argv) { - const char **nargv = prepare_exec_cmd(argv); - - /* execvp() can only ever return if it fails */ - execvp(subcmd_config.exec_name, (char **)nargv); - - free(nargv); - return -1; -} - - -int execl_cmd(const char *cmd,...) -{ - int argc; - const char *argv[MAX_ARGS + 1]; - const char *arg; - va_list param; - - va_start(param, cmd); - argv[0] = cmd; - argc = 1; - while (argc < MAX_ARGS) { - arg = argv[argc++] = va_arg(param, char *); - if (!arg) - break; - } - va_end(param); - if (MAX_ARGS <= argc) { - fprintf(stderr, " Error: too many args to run %s\n", cmd); - return -1; - } - - argv[argc] = NULL; - return execv_cmd(argv); -} diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h deleted file mode 100644 index f1bd3436ad5f..000000000000 --- a/tools/perf/util/exec_cmd.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __PERF_EXEC_CMD_H -#define __PERF_EXEC_CMD_H - -extern void exec_cmd_init(const char *exec_name, const char *prefix, - const char *exec_path, const char *exec_path_env); - -extern void set_argv_exec_path(const char *exec_path); -extern const char *extract_argv0_path(const char *path); -extern void setup_path(void); -extern int execv_cmd(const char **argv); /* NULL terminated */ -extern int execl_cmd(const char *cmd, ...); -/* get_argv_exec_path and system_path return malloc'd string, caller must free it */ -extern char *get_argv_exec_path(void); -extern char *system_path(const char *path); - -#endif /* __PERF_EXEC_CMD_H */ diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c index a0820f16f511..dc1e41c9b054 100644 --- a/tools/perf/util/help-unknown-cmd.c +++ b/tools/perf/util/help-unknown-cmd.c @@ -1,5 +1,5 @@ #include "cache.h" -#include "help.h" +#include #include "../builtin.h" #include "levenshtein.h" diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c deleted file mode 100644 index 8169480066c6..000000000000 --- a/tools/perf/util/help.c +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "help.h" -#include "exec_cmd.h" - -void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) -{ - struct cmdname *ent = malloc(sizeof(*ent) + len + 1); - - ent->len = len; - memcpy(ent->name, name, len); - ent->name[len] = 0; - - ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); - cmds->names[cmds->cnt++] = ent; -} - -void clean_cmdnames(struct cmdnames *cmds) -{ - unsigned int i; - - for (i = 0; i < cmds->cnt; ++i) - zfree(&cmds->names[i]); - zfree(&cmds->names); - cmds->cnt = 0; - cmds->alloc = 0; -} - -int cmdname_compare(const void *a_, const void *b_) -{ - struct cmdname *a = *(struct cmdname **)a_; - struct cmdname *b = *(struct cmdname **)b_; - return strcmp(a->name, b->name); -} - -void uniq(struct cmdnames *cmds) -{ - unsigned int i, j; - - if (!cmds->cnt) - return; - - for (i = j = 1; i < cmds->cnt; i++) - if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) - cmds->names[j++] = cmds->names[i]; - - cmds->cnt = j; -} - -void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) -{ - size_t ci, cj, ei; - int cmp; - - ci = cj = ei = 0; - while (ci < cmds->cnt && ei < excludes->cnt) { - cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); - if (cmp < 0) - cmds->names[cj++] = cmds->names[ci++]; - else if (cmp == 0) - ci++, ei++; - else if (cmp > 0) - ei++; - } - - while (ci < cmds->cnt) - cmds->names[cj++] = cmds->names[ci++]; - - cmds->cnt = cj; -} - -static void get_term_dimensions(struct winsize *ws) -{ - char *s = getenv("LINES"); - - if (s != NULL) { - ws->ws_row = atoi(s); - s = getenv("COLUMNS"); - if (s != NULL) { - ws->ws_col = atoi(s); - if (ws->ws_row && ws->ws_col) - return; - } - } -#ifdef TIOCGWINSZ - if (ioctl(1, TIOCGWINSZ, ws) == 0 && - ws->ws_row && ws->ws_col) - return; -#endif - ws->ws_row = 25; - ws->ws_col = 80; -} - -static void pretty_print_string_list(struct cmdnames *cmds, int longest) -{ - int cols = 1, rows; - int space = longest + 1; /* min 1 SP between words */ - struct winsize win; - int max_cols; - int i, j; - - get_term_dimensions(&win); - max_cols = win.ws_col - 1; /* don't print *on* the edge */ - - if (space < max_cols) - cols = max_cols / space; - rows = (cmds->cnt + cols - 1) / cols; - - for (i = 0; i < rows; i++) { - printf(" "); - - for (j = 0; j < cols; j++) { - unsigned int n = j * rows + i; - unsigned int size = space; - - if (n >= cmds->cnt) - break; - if (j == cols-1 || n + rows >= cmds->cnt) - size = 1; - printf("%-*s", size, cmds->names[n]->name); - } - putchar('\n'); - } -} - -static int is_executable(const char *name) -{ - struct stat st; - - if (stat(name, &st) || /* stat, not lstat */ - !S_ISREG(st.st_mode)) - return 0; - - return st.st_mode & S_IXUSR; -} - -static int has_extension(const char *filename, const char *ext) -{ - size_t len = strlen(filename); - size_t extlen = strlen(ext); - - return len > extlen && !memcmp(filename + len - extlen, ext, extlen); -} - -static void list_commands_in_dir(struct cmdnames *cmds, - const char *path, - const char *prefix) -{ - int prefix_len; - DIR *dir = opendir(path); - struct dirent *de; - char *buf = NULL; - - if (!dir) - return; - if (!prefix) - prefix = "perf-"; - prefix_len = strlen(prefix); - - astrcatf(&buf, "%s/", path); - - while ((de = readdir(dir)) != NULL) { - int entlen; - - if (prefixcmp(de->d_name, prefix)) - continue; - - astrcat(&buf, de->d_name); - if (!is_executable(buf)) - continue; - - entlen = strlen(de->d_name) - prefix_len; - if (has_extension(de->d_name, ".exe")) - entlen -= 4; - - add_cmdname(cmds, de->d_name + prefix_len, entlen); - } - closedir(dir); - free(buf); -} - -void load_command_list(const char *prefix, - struct cmdnames *main_cmds, - struct cmdnames *other_cmds) -{ - const char *env_path = getenv("PATH"); - char *exec_path = get_argv_exec_path(); - - if (exec_path) { - list_commands_in_dir(main_cmds, exec_path, prefix); - qsort(main_cmds->names, main_cmds->cnt, - sizeof(*main_cmds->names), cmdname_compare); - uniq(main_cmds); - } - - if (env_path) { - char *paths, *path, *colon; - path = paths = strdup(env_path); - while (1) { - if ((colon = strchr(path, ':'))) - *colon = 0; - if (!exec_path || strcmp(path, exec_path)) - list_commands_in_dir(other_cmds, path, prefix); - - if (!colon) - break; - path = colon + 1; - } - free(paths); - - qsort(other_cmds->names, other_cmds->cnt, - sizeof(*other_cmds->names), cmdname_compare); - uniq(other_cmds); - } - free(exec_path); - exclude_cmds(other_cmds, main_cmds); -} - -void list_commands(const char *title, struct cmdnames *main_cmds, - struct cmdnames *other_cmds) -{ - unsigned int i, longest = 0; - - for (i = 0; i < main_cmds->cnt; i++) - if (longest < main_cmds->names[i]->len) - longest = main_cmds->names[i]->len; - for (i = 0; i < other_cmds->cnt; i++) - if (longest < other_cmds->names[i]->len) - longest = other_cmds->names[i]->len; - - if (main_cmds->cnt) { - char *exec_path = get_argv_exec_path(); - printf("available %s in '%s'\n", title, exec_path); - printf("----------------"); - mput_char('-', strlen(title) + strlen(exec_path)); - putchar('\n'); - pretty_print_string_list(main_cmds, longest); - putchar('\n'); - free(exec_path); - } - - if (other_cmds->cnt) { - printf("%s available from elsewhere on your $PATH\n", title); - printf("---------------------------------------"); - mput_char('-', strlen(title)); - putchar('\n'); - pretty_print_string_list(other_cmds, longest); - putchar('\n'); - } -} - -int is_in_cmdlist(struct cmdnames *c, const char *s) -{ - unsigned int i; - - for (i = 0; i < c->cnt; i++) - if (!strcmp(s, c->names[i]->name)) - return 1; - return 0; -} diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h deleted file mode 100644 index 096c8bc45cd7..000000000000 --- a/tools/perf/util/help.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __PERF_HELP_H -#define __PERF_HELP_H - -#include - -struct cmdnames { - size_t alloc; - size_t cnt; - struct cmdname { - size_t len; /* also used for similarity index in help.c */ - char name[]; - } **names; -}; - -static inline void mput_char(char c, unsigned int num) -{ - while(num--) - putchar(c); -} - -void load_command_list(const char *prefix, - struct cmdnames *main_cmds, - struct cmdnames *other_cmds); -void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); -void clean_cmdnames(struct cmdnames *cmds); -int cmdname_compare(const void *a, const void *b); -void uniq(struct cmdnames *cmds); -/* Here we require that excludes is a sorted list. */ -void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); -int is_in_cmdlist(struct cmdnames *c, const char *s); -void list_commands(const char *title, struct cmdnames *main_cmds, - struct cmdnames *other_cmds); - -#endif /* __PERF_HELP_H */ diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c deleted file mode 100644 index d50f3b58606b..000000000000 --- a/tools/perf/util/pager.c +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include -#include -#include "pager.h" -#include "run-command.h" -#include "sigchain.h" -#include "subcmd-config.h" - -/* - * This is split up from the rest of git so that we can do - * something different on Windows. - */ - -static int spawned_pager; - -void pager_init(const char *pager_env) -{ - subcmd_config.pager_env = pager_env; -} - -static void pager_preexec(void) -{ - /* - * Work around bug in "less" by not starting it until we - * have real input - */ - fd_set in; - - FD_ZERO(&in); - FD_SET(0, &in); - select(1, &in, NULL, &in, NULL); - - setenv("LESS", "FRSX", 0); -} - -static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; -static struct child_process pager_process; - -static void wait_for_pager(void) -{ - fflush(stdout); - fflush(stderr); - /* signal EOF to pager */ - close(1); - close(2); - finish_command(&pager_process); -} - -static void wait_for_pager_signal(int signo) -{ - wait_for_pager(); - sigchain_pop(signo); - raise(signo); -} - -void setup_pager(void) -{ - const char *pager = getenv(subcmd_config.pager_env); - - if (!isatty(1)) - return; - if (!pager) - pager = getenv("PAGER"); - if (!(pager || access("/usr/bin/pager", X_OK))) - pager = "/usr/bin/pager"; - if (!(pager || access("/usr/bin/less", X_OK))) - pager = "/usr/bin/less"; - if (!pager) - pager = "cat"; - if (!*pager || !strcmp(pager, "cat")) - return; - - spawned_pager = 1; /* means we are emitting to terminal */ - - /* spawn the pager */ - pager_argv[2] = pager; - pager_process.argv = pager_argv; - pager_process.in = -1; - pager_process.preexec_cb = pager_preexec; - - if (start_command(&pager_process)) - return; - - /* original process continues, but writes to the pipe */ - dup2(pager_process.in, 1); - if (isatty(2)) - dup2(pager_process.in, 2); - close(pager_process.in); - - /* this makes sure that the parent terminates after the pager */ - sigchain_push_common(wait_for_pager_signal); - atexit(wait_for_pager); -} - -int pager_in_use(void) -{ - return spawned_pager; -} diff --git a/tools/perf/util/pager.h b/tools/perf/util/pager.h deleted file mode 100644 index d6a591a4c017..000000000000 --- a/tools/perf/util/pager.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __PERF_PAGER_H -#define __PERF_PAGER_H - -extern void pager_init(const char *pager_env); - -extern void setup_pager(void); -extern int pager_in_use(void); - -#endif /* __PERF_PAGER_H */ diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index 355eecf6bf59..afc088dd7d20 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c @@ -1,7 +1,7 @@ #include "perf.h" #include "util/util.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/parse-branch-options.h" #define BRANCH_OPT(n, m) \ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6fc8cd753e1a..4f7b0efdde2f 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -4,9 +4,9 @@ #include "../perf.h" #include "evlist.h" #include "evsel.h" -#include "parse-options.h" +#include #include "parse-events.h" -#include "exec_cmd.h" +#include #include "string.h" #include "symbol.h" #include "cache.h" diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c deleted file mode 100644 index 981bb4481fd5..000000000000 --- a/tools/perf/util/parse-options.c +++ /dev/null @@ -1,983 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "parse-options.h" -#include "subcmd-config.h" -#include "pager.h" - -#define OPT_SHORT 1 -#define OPT_UNSET 2 - -char *error_buf; - -static int opterror(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); - else if (flags & OPT_UNSET) - fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); - else - fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); - - return -1; -} - -static const char *skip_prefix(const char *str, const char *prefix) -{ - size_t len = strlen(prefix); - return strncmp(str, prefix, len) ? NULL : str + len; -} - -static void optwarning(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); - else if (flags & OPT_UNSET) - fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); - else - fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); -} - -static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, - int flags, const char **arg) -{ - const char *res; - - if (p->opt) { - res = p->opt; - p->opt = NULL; - } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || - **(p->argv + 1) == '-')) { - res = (const char *)opt->defval; - } else if (p->argc > 1) { - p->argc--; - res = *++p->argv; - } else - return opterror(opt, "requires a value", flags); - if (arg) - *arg = res; - return 0; -} - -static int get_value(struct parse_opt_ctx_t *p, - const struct option *opt, int flags) -{ - const char *s, *arg = NULL; - const int unset = flags & OPT_UNSET; - int err; - - if (unset && p->opt) - return opterror(opt, "takes no value", flags); - if (unset && (opt->flags & PARSE_OPT_NONEG)) - return opterror(opt, "isn't available", flags); - if (opt->flags & PARSE_OPT_DISABLED) - return opterror(opt, "is not usable", flags); - - if (opt->flags & PARSE_OPT_EXCLUSIVE) { - if (p->excl_opt && p->excl_opt != opt) { - char msg[128]; - - if (((flags & OPT_SHORT) && p->excl_opt->short_name) || - p->excl_opt->long_name == NULL) { - snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", - p->excl_opt->short_name); - } else { - snprintf(msg, sizeof(msg), "cannot be used with %s", - p->excl_opt->long_name); - } - opterror(opt, msg, flags); - return -3; - } - p->excl_opt = opt; - } - if (!(flags & OPT_SHORT) && p->opt) { - switch (opt->type) { - case OPTION_CALLBACK: - if (!(opt->flags & PARSE_OPT_NOARG)) - break; - /* FALLTHROUGH */ - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_BIT: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - return opterror(opt, "takes no value", flags); - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - case OPTION_STRING: - case OPTION_INTEGER: - case OPTION_UINTEGER: - case OPTION_LONG: - case OPTION_U64: - default: - break; - } - } - - if (opt->flags & PARSE_OPT_NOBUILD) { - char reason[128]; - bool noarg = false; - - err = snprintf(reason, sizeof(reason), - opt->flags & PARSE_OPT_CANSKIP ? - "is being ignored because %s " : - "is not available because %s", - opt->build_opt); - reason[sizeof(reason) - 1] = '\0'; - - if (err < 0) - strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? - "is being ignored" : - "is not available", - sizeof(reason)); - - if (!(opt->flags & PARSE_OPT_CANSKIP)) - return opterror(opt, reason, flags); - - err = 0; - if (unset) - noarg = true; - if (opt->flags & PARSE_OPT_NOARG) - noarg = true; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - noarg = true; - - switch (opt->type) { - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_BIT: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - noarg = true; - break; - case OPTION_CALLBACK: - case OPTION_STRING: - case OPTION_INTEGER: - case OPTION_UINTEGER: - case OPTION_LONG: - case OPTION_U64: - default: - break; - } - - if (!noarg) - err = get_arg(p, opt, flags, NULL); - if (err) - return err; - - optwarning(opt, reason, flags); - return 0; - } - - switch (opt->type) { - case OPTION_BIT: - if (unset) - *(int *)opt->value &= ~opt->defval; - else - *(int *)opt->value |= opt->defval; - return 0; - - case OPTION_BOOLEAN: - *(bool *)opt->value = unset ? false : true; - if (opt->set) - *(bool *)opt->set = true; - return 0; - - case OPTION_INCR: - *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; - return 0; - - case OPTION_SET_UINT: - *(unsigned int *)opt->value = unset ? 0 : opt->defval; - return 0; - - case OPTION_SET_PTR: - *(void **)opt->value = unset ? NULL : (void *)opt->defval; - return 0; - - case OPTION_STRING: - err = 0; - if (unset) - *(const char **)opt->value = NULL; - else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - *(const char **)opt->value = (const char *)opt->defval; - else - err = get_arg(p, opt, flags, (const char **)opt->value); - - /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */ - if (opt->flags & PARSE_OPT_NOEMPTY) { - const char *val = *(const char **)opt->value; - - if (!val) - return err; - - /* Similar to unset if we are given an empty string. */ - if (val[0] == '\0') { - *(const char **)opt->value = NULL; - return 0; - } - } - - return err; - - case OPTION_CALLBACK: - if (unset) - return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; - if (opt->flags & PARSE_OPT_NOARG) - return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; - if (get_arg(p, opt, flags, &arg)) - return -1; - return (*opt->callback)(opt, arg, 0) ? (-1) : 0; - - case OPTION_INTEGER: - if (unset) { - *(int *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(int *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(int *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_UINTEGER: - if (unset) { - *(unsigned int *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(unsigned int *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_LONG: - if (unset) { - *(long *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(long *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(long *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_U64: - if (unset) { - *(u64 *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(u64 *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - default: - die("should not happen, someone must be hit on the forehead"); - } -} - -static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) -{ - for (; options->type != OPTION_END; options++) { - if (options->short_name == *p->opt) { - p->opt = p->opt[1] ? p->opt + 1 : NULL; - return get_value(p, options, OPT_SHORT); - } - } - return -2; -} - -static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, - const struct option *options) -{ - const char *arg_end = strchr(arg, '='); - const struct option *abbrev_option = NULL, *ambiguous_option = NULL; - int abbrev_flags = 0, ambiguous_flags = 0; - - if (!arg_end) - arg_end = arg + strlen(arg); - - for (; options->type != OPTION_END; options++) { - const char *rest; - int flags = 0; - - if (!options->long_name) - continue; - - rest = skip_prefix(arg, options->long_name); - if (options->type == OPTION_ARGUMENT) { - if (!rest) - continue; - if (*rest == '=') - return opterror(options, "takes no value", flags); - if (*rest) - continue; - p->out[p->cpidx++] = arg - 2; - return 0; - } - if (!rest) { - if (!prefixcmp(options->long_name, "no-")) { - /* - * The long name itself starts with "no-", so - * accept the option without "no-" so that users - * do not have to enter "no-no-" to get the - * negation. - */ - rest = skip_prefix(arg, options->long_name + 3); - if (rest) { - flags |= OPT_UNSET; - goto match; - } - /* Abbreviated case */ - if (!prefixcmp(options->long_name + 3, arg)) { - flags |= OPT_UNSET; - goto is_abbreviated; - } - } - /* abbreviated? */ - if (!strncmp(options->long_name, arg, arg_end - arg)) { -is_abbreviated: - if (abbrev_option) { - /* - * If this is abbreviated, it is - * ambiguous. So when there is no - * exact match later, we need to - * error out. - */ - ambiguous_option = abbrev_option; - ambiguous_flags = abbrev_flags; - } - if (!(flags & OPT_UNSET) && *arg_end) - p->opt = arg_end + 1; - abbrev_option = options; - abbrev_flags = flags; - continue; - } - /* negated and abbreviated very much? */ - if (!prefixcmp("no-", arg)) { - flags |= OPT_UNSET; - goto is_abbreviated; - } - /* negated? */ - if (strncmp(arg, "no-", 3)) - continue; - flags |= OPT_UNSET; - rest = skip_prefix(arg + 3, options->long_name); - /* abbreviated and negated? */ - if (!rest && !prefixcmp(options->long_name, arg + 3)) - goto is_abbreviated; - if (!rest) - continue; - } -match: - if (*rest) { - if (*rest != '=') - continue; - p->opt = rest + 1; - } - return get_value(p, options, flags); - } - - if (ambiguous_option) { - fprintf(stderr, - " Error: Ambiguous option: %s (could be --%s%s or --%s%s)", - arg, - (ambiguous_flags & OPT_UNSET) ? "no-" : "", - ambiguous_option->long_name, - (abbrev_flags & OPT_UNSET) ? "no-" : "", - abbrev_option->long_name); - return -1; - } - if (abbrev_option) - return get_value(p, abbrev_option, abbrev_flags); - return -2; -} - -static void check_typos(const char *arg, const struct option *options) -{ - if (strlen(arg) < 3) - return; - - if (!prefixcmp(arg, "no-")) { - fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); - exit(129); - } - - for (; options->type != OPTION_END; options++) { - if (!options->long_name) - continue; - if (!prefixcmp(options->long_name, arg)) { - fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); - exit(129); - } - } -} - -static void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags) -{ - memset(ctx, 0, sizeof(*ctx)); - ctx->argc = argc - 1; - ctx->argv = argv + 1; - ctx->out = argv; - ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); - ctx->flags = flags; - if ((flags & PARSE_OPT_KEEP_UNKNOWN) && - (flags & PARSE_OPT_STOP_AT_NON_OPTION)) - die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); -} - -static int usage_with_options_internal(const char * const *, - const struct option *, int, - struct parse_opt_ctx_t *); - -static int parse_options_step(struct parse_opt_ctx_t *ctx, - const struct option *options, - const char * const usagestr[]) -{ - int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); - int excl_short_opt = 1; - const char *arg; - - /* we must reset ->opt, unknown short option leave it dangling */ - ctx->opt = NULL; - - for (; ctx->argc; ctx->argc--, ctx->argv++) { - arg = ctx->argv[0]; - if (*arg != '-' || !arg[1]) { - if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) - break; - ctx->out[ctx->cpidx++] = ctx->argv[0]; - continue; - } - - if (arg[1] != '-') { - ctx->opt = ++arg; - if (internal_help && *ctx->opt == 'h') { - return usage_with_options_internal(usagestr, options, 0, ctx); - } - switch (parse_short_opt(ctx, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 1); - case -2: - goto unknown; - case -3: - goto exclusive; - default: - break; - } - if (ctx->opt) - check_typos(arg, options); - while (ctx->opt) { - if (internal_help && *ctx->opt == 'h') - return usage_with_options_internal(usagestr, options, 0, ctx); - arg = ctx->opt; - switch (parse_short_opt(ctx, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 1); - case -2: - /* fake a short option thing to hide the fact that we may have - * started to parse aggregated stuff - * - * This is leaky, too bad. - */ - ctx->argv[0] = strdup(ctx->opt - 1); - *(char *)ctx->argv[0] = '-'; - goto unknown; - case -3: - goto exclusive; - default: - break; - } - } - continue; - } - - if (!arg[2]) { /* "--" */ - if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { - ctx->argc--; - ctx->argv++; - } - break; - } - - arg += 2; - if (internal_help && !strcmp(arg, "help-all")) - return usage_with_options_internal(usagestr, options, 1, ctx); - if (internal_help && !strcmp(arg, "help")) - return usage_with_options_internal(usagestr, options, 0, ctx); - if (!strcmp(arg, "list-opts")) - return PARSE_OPT_LIST_OPTS; - if (!strcmp(arg, "list-cmds")) - return PARSE_OPT_LIST_SUBCMDS; - switch (parse_long_opt(ctx, arg, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 0); - case -2: - goto unknown; - case -3: - excl_short_opt = 0; - goto exclusive; - default: - break; - } - continue; -unknown: - if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) - return PARSE_OPT_UNKNOWN; - ctx->out[ctx->cpidx++] = ctx->argv[0]; - ctx->opt = NULL; - } - return PARSE_OPT_DONE; - -exclusive: - parse_options_usage(usagestr, options, arg, excl_short_opt); - if ((excl_short_opt && ctx->excl_opt->short_name) || - ctx->excl_opt->long_name == NULL) { - char opt = ctx->excl_opt->short_name; - parse_options_usage(NULL, options, &opt, 1); - } else { - parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); - } - return PARSE_OPT_HELP; -} - -static int parse_options_end(struct parse_opt_ctx_t *ctx) -{ - memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); - ctx->out[ctx->cpidx + ctx->argc] = NULL; - return ctx->cpidx + ctx->argc; -} - -int parse_options_subcommand(int argc, const char **argv, const struct option *options, - const char *const subcommands[], const char *usagestr[], int flags) -{ - struct parse_opt_ctx_t ctx; - - /* build usage string if it's not provided */ - if (subcommands && !usagestr[0]) { - char *buf = NULL; - - astrcatf(&buf, "%s %s [] {", subcmd_config.exec_name, argv[0]); - - for (int i = 0; subcommands[i]; i++) { - if (i) - astrcat(&buf, "|"); - astrcat(&buf, subcommands[i]); - } - astrcat(&buf, "}"); - - usagestr[0] = buf; - } - - parse_options_start(&ctx, argc, argv, flags); - switch (parse_options_step(&ctx, options, usagestr)) { - case PARSE_OPT_HELP: - exit(129); - case PARSE_OPT_DONE: - break; - case PARSE_OPT_LIST_OPTS: - while (options->type != OPTION_END) { - if (options->long_name) - printf("--%s ", options->long_name); - options++; - } - putchar('\n'); - exit(130); - case PARSE_OPT_LIST_SUBCMDS: - if (subcommands) { - for (int i = 0; subcommands[i]; i++) - printf("%s ", subcommands[i]); - } - putchar('\n'); - exit(130); - default: /* PARSE_OPT_UNKNOWN */ - if (ctx.argv[0][1] == '-') - astrcatf(&error_buf, "unknown option `%s'", - ctx.argv[0] + 2); - else - astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); - usage_with_options(usagestr, options); - } - - return parse_options_end(&ctx); -} - -int parse_options(int argc, const char **argv, const struct option *options, - const char * const usagestr[], int flags) -{ - return parse_options_subcommand(argc, argv, options, NULL, - (const char **) usagestr, flags); -} - -#define USAGE_OPTS_WIDTH 24 -#define USAGE_GAP 2 - -static void print_option_help(const struct option *opts, int full) -{ - size_t pos; - int pad; - - if (opts->type == OPTION_GROUP) { - fputc('\n', stderr); - if (*opts->help) - fprintf(stderr, "%s\n", opts->help); - return; - } - if (!full && (opts->flags & PARSE_OPT_HIDDEN)) - return; - if (opts->flags & PARSE_OPT_DISABLED) - return; - - pos = fprintf(stderr, " "); - if (opts->short_name) - pos += fprintf(stderr, "-%c", opts->short_name); - else - pos += fprintf(stderr, " "); - - if (opts->long_name && opts->short_name) - pos += fprintf(stderr, ", "); - if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); - - switch (opts->type) { - case OPTION_ARGUMENT: - break; - case OPTION_LONG: - case OPTION_U64: - case OPTION_INTEGER: - case OPTION_UINTEGER: - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=]"); - else - pos += fprintf(stderr, "[]"); - else - pos += fprintf(stderr, " "); - break; - case OPTION_CALLBACK: - if (opts->flags & PARSE_OPT_NOARG) - break; - /* FALLTHROUGH */ - case OPTION_STRING: - if (opts->argh) { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=<%s>]", opts->argh); - else - pos += fprintf(stderr, "[<%s>]", opts->argh); - else - pos += fprintf(stderr, " <%s>", opts->argh); - } else { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=...]"); - else - pos += fprintf(stderr, "[...]"); - else - pos += fprintf(stderr, " ..."); - } - break; - default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ - case OPTION_END: - case OPTION_GROUP: - case OPTION_BIT: - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - break; - } - - if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', stderr); - pad = USAGE_OPTS_WIDTH; - } - fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); - if (opts->flags & PARSE_OPT_NOBUILD) - fprintf(stderr, "%*s(not built-in because %s)\n", - USAGE_OPTS_WIDTH + USAGE_GAP, "", - opts->build_opt); -} - -static int option__cmp(const void *va, const void *vb) -{ - const struct option *a = va, *b = vb; - int sa = tolower(a->short_name), sb = tolower(b->short_name), ret; - - if (sa == 0) - sa = 'z' + 1; - if (sb == 0) - sb = 'z' + 1; - - ret = sa - sb; - - if (ret == 0) { - const char *la = a->long_name ?: "", - *lb = b->long_name ?: ""; - ret = strcmp(la, lb); - } - - return ret; -} - -static struct option *options__order(const struct option *opts) -{ - int nr_opts = 0, len; - const struct option *o = opts; - struct option *ordered; - - for (o = opts; o->type != OPTION_END; o++) - ++nr_opts; - - len = sizeof(*o) * (nr_opts + 1); - ordered = malloc(len); - if (!ordered) - goto out; - memcpy(ordered, opts, len); - - qsort(ordered, nr_opts, sizeof(*o), option__cmp); -out: - return ordered; -} - -static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx) -{ - int i; - - for (i = 1; i < ctx->argc; ++i) { - const char *arg = ctx->argv[i]; - - if (arg[0] != '-') { - if (arg[1] == '\0') { - if (arg[0] == opt->short_name) - return true; - continue; - } - - if (opt->long_name && strcmp(opt->long_name, arg) == 0) - return true; - - if (opt->help && strcasestr(opt->help, arg) != NULL) - return true; - - continue; - } - - if (arg[1] == opt->short_name || - (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0)) - return true; - } - - return false; -} - -static int usage_with_options_internal(const char * const *usagestr, - const struct option *opts, int full, - struct parse_opt_ctx_t *ctx) -{ - struct option *ordered; - - if (!usagestr) - return PARSE_OPT_HELP; - - setup_pager(); - - if (error_buf) { - fprintf(stderr, " Error: %s\n", error_buf); - zfree(&error_buf); - } - - fprintf(stderr, "\n Usage: %s\n", *usagestr++); - while (*usagestr && **usagestr) - fprintf(stderr, " or: %s\n", *usagestr++); - while (*usagestr) { - fprintf(stderr, "%s%s\n", - **usagestr ? " " : "", - *usagestr); - usagestr++; - } - - if (opts->type != OPTION_GROUP) - fputc('\n', stderr); - - ordered = options__order(opts); - if (ordered) - opts = ordered; - - for ( ; opts->type != OPTION_END; opts++) { - if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx)) - continue; - print_option_help(opts, full); - } - - fputc('\n', stderr); - - free(ordered); - - return PARSE_OPT_HELP; -} - -void usage_with_options(const char * const *usagestr, - const struct option *opts) -{ - usage_with_options_internal(usagestr, opts, 0, NULL); - exit(129); -} - -void usage_with_options_msg(const char * const *usagestr, - const struct option *opts, const char *fmt, ...) -{ - va_list ap; - char *tmp = error_buf; - - va_start(ap, fmt); - if (vasprintf(&error_buf, fmt, ap) == -1) - die("vasprintf failed"); - va_end(ap); - - free(tmp); - - usage_with_options_internal(usagestr, opts, 0, NULL); - exit(129); -} - -int parse_options_usage(const char * const *usagestr, - const struct option *opts, - const char *optstr, bool short_opt) -{ - if (!usagestr) - goto opt; - - fprintf(stderr, "\n Usage: %s\n", *usagestr++); - while (*usagestr && **usagestr) - fprintf(stderr, " or: %s\n", *usagestr++); - while (*usagestr) { - fprintf(stderr, "%s%s\n", - **usagestr ? " " : "", - *usagestr); - usagestr++; - } - fputc('\n', stderr); - -opt: - for ( ; opts->type != OPTION_END; opts++) { - if (short_opt) { - if (opts->short_name == *optstr) { - print_option_help(opts, 0); - break; - } - continue; - } - - if (opts->long_name == NULL) - continue; - - if (!prefixcmp(opts->long_name, optstr)) - print_option_help(opts, 0); - if (!prefixcmp("no-", optstr) && - !prefixcmp(opts->long_name, optstr + 3)) - print_option_help(opts, 0); - } - - return PARSE_OPT_HELP; -} - - -int parse_opt_verbosity_cb(const struct option *opt, - const char *arg __maybe_unused, - int unset) -{ - int *target = opt->value; - - if (unset) - /* --no-quiet, --no-verbose */ - *target = 0; - else if (opt->short_name == 'v') { - if (*target >= 0) - (*target)++; - else - *target = 1; - } else { - if (*target <= 0) - (*target)--; - else - *target = -1; - } - return 0; -} - -static struct option * -find_option(struct option *opts, int shortopt, const char *longopt) -{ - for (; opts->type != OPTION_END; opts++) { - if ((shortopt && opts->short_name == shortopt) || - (opts->long_name && longopt && - !strcmp(opts->long_name, longopt))) - return opts; - } - return NULL; -} - -void set_option_flag(struct option *opts, int shortopt, const char *longopt, - int flag) -{ - struct option *opt = find_option(opts, shortopt, longopt); - - if (opt) - opt->flags |= flag; - return; -} - -void set_option_nobuild(struct option *opts, int shortopt, - const char *longopt, - const char *build_opt, - bool can_skip) -{ - struct option *opt = find_option(opts, shortopt, longopt); - - if (!opt) - return; - - opt->flags |= PARSE_OPT_NOBUILD; - opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; - opt->build_opt = build_opt; -} diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h deleted file mode 100644 index dec893f10477..000000000000 --- a/tools/perf/util/parse-options.h +++ /dev/null @@ -1,228 +0,0 @@ -#ifndef __PERF_PARSE_OPTIONS_H -#define __PERF_PARSE_OPTIONS_H - -#include -#include - -enum parse_opt_type { - /* special types */ - OPTION_END, - OPTION_ARGUMENT, - OPTION_GROUP, - /* options with no arguments */ - OPTION_BIT, - OPTION_BOOLEAN, - OPTION_INCR, - OPTION_SET_UINT, - OPTION_SET_PTR, - /* options with arguments (usually) */ - OPTION_STRING, - OPTION_INTEGER, - OPTION_LONG, - OPTION_CALLBACK, - OPTION_U64, - OPTION_UINTEGER, -}; - -enum parse_opt_flags { - PARSE_OPT_KEEP_DASHDASH = 1, - PARSE_OPT_STOP_AT_NON_OPTION = 2, - PARSE_OPT_KEEP_ARGV0 = 4, - PARSE_OPT_KEEP_UNKNOWN = 8, - PARSE_OPT_NO_INTERNAL_HELP = 16, -}; - -enum parse_opt_option_flags { - PARSE_OPT_OPTARG = 1, - PARSE_OPT_NOARG = 2, - PARSE_OPT_NONEG = 4, - PARSE_OPT_HIDDEN = 8, - PARSE_OPT_LASTARG_DEFAULT = 16, - PARSE_OPT_DISABLED = 32, - PARSE_OPT_EXCLUSIVE = 64, - PARSE_OPT_NOEMPTY = 128, - PARSE_OPT_NOBUILD = 256, - PARSE_OPT_CANSKIP = 512, -}; - -struct option; -typedef int parse_opt_cb(const struct option *, const char *arg, int unset); - -/* - * `type`:: - * holds the type of the option, you must have an OPTION_END last in your - * array. - * - * `short_name`:: - * the character to use as a short option name, '\0' if none. - * - * `long_name`:: - * the long option name, without the leading dashes, NULL if none. - * - * `value`:: - * stores pointers to the values to be filled. - * - * `argh`:: - * token to explain the kind of argument this option wants. Keep it - * homogenous across the repository. - * - * `help`:: - * the short help associated to what the option does. - * Must never be NULL (except for OPTION_END). - * OPTION_GROUP uses this pointer to store the group header. - * - * `flags`:: - * mask of parse_opt_option_flags. - * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) - * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs - * PARSE_OPT_NONEG: says that this option cannot be negated - * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in - * the long one. - * - * `callback`:: - * pointer to the callback to use for OPTION_CALLBACK. - * - * `defval`:: - * default value to fill (*->value) with for PARSE_OPT_OPTARG. - * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in - * the value when met. - * CALLBACKS can use it like they want. - * - * `set`:: - * whether an option was set by the user - */ -struct option { - enum parse_opt_type type; - int short_name; - const char *long_name; - void *value; - const char *argh; - const char *help; - const char *build_opt; - - int flags; - parse_opt_cb *callback; - intptr_t defval; - bool *set; - void *data; -}; - -#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) - -#define OPT_END() { .type = OPTION_END } -#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } -#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } -#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } -#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } -#define OPT_BOOLEAN_FLAG(s, l, v, h, f) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) } -#define OPT_BOOLEAN_SET(s, l, v, os, h) \ - { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, bool *), .help = (h), \ - .set = check_vtype(os, bool *)} -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } -#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } -#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } -#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } -#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } -#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } -#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } -#define OPT_STRING_OPTARG(s, l, v, a, h, d) \ - { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, const char **), (a), .help = (h), \ - .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } -#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} -#define OPT_DATE(s, l, v, h) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } -#define OPT_CALLBACK(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } -#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } -#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } -#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ - .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ - .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} -#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ - .value = (v), (a), .help = (h), .callback = (f), \ - .flags = PARSE_OPT_OPTARG, .data = (d) } - -/* parse_options() will filter out the processed options and leave the - * non-option argments in argv[]. - * Returns the number of arguments left in argv[]. - * - * NOTE: parse_options() and parse_options_subcommand() may call exit() in the - * case of an error (or for 'special' options like --list-cmds or --list-opts). - */ -extern int parse_options(int argc, const char **argv, - const struct option *options, - const char * const usagestr[], int flags); - -extern int parse_options_subcommand(int argc, const char **argv, - const struct option *options, - const char *const subcommands[], - const char *usagestr[], int flags); - -extern NORETURN void usage_with_options(const char * const *usagestr, - const struct option *options); -extern NORETURN __attribute__((format(printf,3,4))) -void usage_with_options_msg(const char * const *usagestr, - const struct option *options, - const char *fmt, ...); - -/*----- incremantal advanced APIs -----*/ - -enum { - PARSE_OPT_HELP = -1, - PARSE_OPT_DONE, - PARSE_OPT_LIST_OPTS, - PARSE_OPT_LIST_SUBCMDS, - PARSE_OPT_UNKNOWN, -}; - -/* - * It's okay for the caller to consume argv/argc in the usual way. - * Other fields of that structure are private to parse-options and should not - * be modified in any way. - */ -struct parse_opt_ctx_t { - const char **argv; - const char **out; - int argc, cpidx; - const char *opt; - const struct option *excl_opt; - int flags; -}; - -extern int parse_options_usage(const char * const *usagestr, - const struct option *opts, - const char *optstr, - bool short_opt); - - -/*----- some often used options -----*/ -extern int parse_opt_abbrev_cb(const struct option *, const char *, int); -extern int parse_opt_approxidate_cb(const struct option *, const char *, int); -extern int parse_opt_verbosity_cb(const struct option *, const char *, int); - -#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") -#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") -#define OPT__VERBOSITY(var) \ - { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ - PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ - { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ - PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } -#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") -#define OPT__ABBREV(var) \ - { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ - "use digits to display SHA-1s", \ - PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } - -extern const char *parse_options_fix_filename(const char *prefix, const char *file); - -void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); -void set_option_nobuild(struct option *opts, int shortopt, const char *longopt, - const char *build_opt, bool can_skip); -#endif /* __PERF_PARSE_OPTIONS_H */ diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c index 4f2c1c255d81..646ecf736aad 100644 --- a/tools/perf/util/parse-regs-options.c +++ b/tools/perf/util/parse-regs-options.c @@ -1,7 +1,7 @@ #include "perf.h" #include "util/util.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/parse-regs-options.h" int diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c deleted file mode 100644 index fed37d6ae070..000000000000 --- a/tools/perf/util/run-command.c +++ /dev/null @@ -1,227 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "run-command.h" -#include "exec_cmd.h" - -#define STRERR_BUFSIZE 128 - -static inline void close_pair(int fd[2]) -{ - close(fd[0]); - close(fd[1]); -} - -static inline void dup_devnull(int to) -{ - int fd = open("/dev/null", O_RDWR); - dup2(fd, to); - close(fd); -} - -int start_command(struct child_process *cmd) -{ - int need_in, need_out, need_err; - int fdin[2], fdout[2], fderr[2]; - char sbuf[STRERR_BUFSIZE]; - - /* - * In case of errors we must keep the promise to close FDs - * that have been passed in via ->in and ->out. - */ - - need_in = !cmd->no_stdin && cmd->in < 0; - if (need_in) { - if (pipe(fdin) < 0) { - if (cmd->out > 0) - close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->in = fdin[1]; - } - - need_out = !cmd->no_stdout - && !cmd->stdout_to_stderr - && cmd->out < 0; - if (need_out) { - if (pipe(fdout) < 0) { - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->out = fdout[0]; - } - - need_err = !cmd->no_stderr && cmd->err < 0; - if (need_err) { - if (pipe(fderr) < 0) { - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - if (need_out) - close_pair(fdout); - else if (cmd->out) - close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->err = fderr[0]; - } - - fflush(NULL); - cmd->pid = fork(); - if (!cmd->pid) { - if (cmd->no_stdin) - dup_devnull(0); - else if (need_in) { - dup2(fdin[0], 0); - close_pair(fdin); - } else if (cmd->in) { - dup2(cmd->in, 0); - close(cmd->in); - } - - if (cmd->no_stderr) - dup_devnull(2); - else if (need_err) { - dup2(fderr[1], 2); - close_pair(fderr); - } - - if (cmd->no_stdout) - dup_devnull(1); - else if (cmd->stdout_to_stderr) - dup2(2, 1); - else if (need_out) { - dup2(fdout[1], 1); - close_pair(fdout); - } else if (cmd->out > 1) { - dup2(cmd->out, 1); - close(cmd->out); - } - - if (cmd->dir && chdir(cmd->dir)) - die("exec %s: cd to %s failed (%s)", cmd->argv[0], - cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); - if (cmd->env) { - for (; *cmd->env; cmd->env++) { - if (strchr(*cmd->env, '=')) - putenv((char*)*cmd->env); - else - unsetenv(*cmd->env); - } - } - if (cmd->preexec_cb) - cmd->preexec_cb(); - if (cmd->exec_cmd) { - execv_cmd(cmd->argv); - } else { - execvp(cmd->argv[0], (char *const*) cmd->argv); - } - exit(127); - } - - if (cmd->pid < 0) { - int err = errno; - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - if (need_out) - close_pair(fdout); - else if (cmd->out) - close(cmd->out); - if (need_err) - close_pair(fderr); - return err == ENOENT ? - -ERR_RUN_COMMAND_EXEC : - -ERR_RUN_COMMAND_FORK; - } - - if (need_in) - close(fdin[0]); - else if (cmd->in) - close(cmd->in); - - if (need_out) - close(fdout[1]); - else if (cmd->out) - close(cmd->out); - - if (need_err) - close(fderr[1]); - - return 0; -} - -static int wait_or_whine(pid_t pid) -{ - char sbuf[STRERR_BUFSIZE]; - - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); - - if (waiting < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, " Error: waitpid failed (%s)", - strerror_r(errno, sbuf, sizeof(sbuf))); - return -ERR_RUN_COMMAND_WAITPID; - } - if (waiting != pid) - return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; - if (WIFSIGNALED(status)) - return -ERR_RUN_COMMAND_WAITPID_SIGNAL; - - if (!WIFEXITED(status)) - return -ERR_RUN_COMMAND_WAITPID_NOEXIT; - code = WEXITSTATUS(status); - switch (code) { - case 127: - return -ERR_RUN_COMMAND_EXEC; - case 0: - return 0; - default: - return -code; - } - } -} - -int finish_command(struct child_process *cmd) -{ - return wait_or_whine(cmd->pid); -} - -int run_command(struct child_process *cmd) -{ - int code = start_command(cmd); - if (code) - return code; - return finish_command(cmd); -} - -static void prepare_run_command_v_opt(struct child_process *cmd, - const char **argv, - int opt) -{ - memset(cmd, 0, sizeof(*cmd)); - cmd->argv = argv; - cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; - cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; -} - -int run_command_v_opt(const char **argv, int opt) -{ - struct child_process cmd; - prepare_run_command_v_opt(&cmd, argv, opt); - return run_command(&cmd); -} diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h deleted file mode 100644 index 4a55393a6547..000000000000 --- a/tools/perf/util/run-command.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __PERF_RUN_COMMAND_H -#define __PERF_RUN_COMMAND_H - -#include - -enum { - ERR_RUN_COMMAND_FORK = 10000, - ERR_RUN_COMMAND_EXEC, - ERR_RUN_COMMAND_PIPE, - ERR_RUN_COMMAND_WAITPID, - ERR_RUN_COMMAND_WAITPID_WRONG_PID, - ERR_RUN_COMMAND_WAITPID_SIGNAL, - ERR_RUN_COMMAND_WAITPID_NOEXIT, -}; -#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) - -struct child_process { - const char **argv; - pid_t pid; - /* - * Using .in, .out, .err: - * - Specify 0 for no redirections (child inherits stdin, stdout, - * stderr from parent). - * - Specify -1 to have a pipe allocated as follows: - * .in: returns the writable pipe end; parent writes to it, - * the readable pipe end becomes child's stdin - * .out, .err: returns the readable pipe end; parent reads from - * it, the writable pipe end becomes child's stdout/stderr - * The caller of start_command() must close the returned FDs - * after it has completed reading from/writing to it! - * - Specify > 0 to set a channel to a particular FD as follows: - * .in: a readable FD, becomes child's stdin - * .out: a writable FD, becomes child's stdout/stderr - * .err > 0 not supported - * The specified FD is closed by start_command(), even in case - * of errors! - */ - int in; - int out; - int err; - const char *dir; - const char *const *env; - unsigned no_stdin:1; - unsigned no_stdout:1; - unsigned no_stderr:1; - unsigned exec_cmd:1; /* if this is to be external sub-command */ - unsigned stdout_to_stderr:1; - void (*preexec_cb)(void); -}; - -int start_command(struct child_process *); -int finish_command(struct child_process *); -int run_command(struct child_process *); - -#define RUN_COMMAND_NO_STDIN 1 -#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */ -#define RUN_COMMAND_STDOUT_TO_STDERR 4 -int run_command_v_opt(const char **argv, int opt); - -#endif /* __PERF_RUN_COMMAND_H */ diff --git a/tools/perf/util/sigchain.c b/tools/perf/util/sigchain.c deleted file mode 100644 index 3537c348a18e..000000000000 --- a/tools/perf/util/sigchain.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "subcmd-util.h" -#include "sigchain.h" - -#define SIGCHAIN_MAX_SIGNALS 32 - -struct sigchain_signal { - sigchain_fun *old; - int n; - int alloc; -}; -static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; - -static void check_signum(int sig) -{ - if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) - die("BUG: signal out of range: %d", sig); -} - -static int sigchain_push(int sig, sigchain_fun f) -{ - struct sigchain_signal *s = signals + sig; - check_signum(sig); - - ALLOC_GROW(s->old, s->n + 1, s->alloc); - s->old[s->n] = signal(sig, f); - if (s->old[s->n] == SIG_ERR) - return -1; - s->n++; - return 0; -} - -int sigchain_pop(int sig) -{ - struct sigchain_signal *s = signals + sig; - check_signum(sig); - if (s->n < 1) - return 0; - - if (signal(sig, s->old[s->n - 1]) == SIG_ERR) - return -1; - s->n--; - return 0; -} - -void sigchain_push_common(sigchain_fun f) -{ - sigchain_push(SIGINT, f); - sigchain_push(SIGHUP, f); - sigchain_push(SIGTERM, f); - sigchain_push(SIGQUIT, f); - sigchain_push(SIGPIPE, f); -} diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h deleted file mode 100644 index 959d64eb5557..000000000000 --- a/tools/perf/util/sigchain.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __PERF_SIGCHAIN_H -#define __PERF_SIGCHAIN_H - -typedef void (*sigchain_fun)(int); - -int sigchain_pop(int sig); - -void sigchain_push_common(sigchain_fun f); - -#endif /* __PERF_SIGCHAIN_H */ diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 31228851e397..86f05e7a5566 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -18,7 +18,7 @@ #include "debug.h" #include "header.h" -#include "parse-options.h" +#include #include "parse-events.h" #include "hist.h" #include "thread.h" diff --git a/tools/perf/util/subcmd-config.c b/tools/perf/util/subcmd-config.c deleted file mode 100644 index d017c728bd1b..000000000000 --- a/tools/perf/util/subcmd-config.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "subcmd-config.h" - -#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED" - -struct subcmd_config subcmd_config = { - .exec_name = UNDEFINED, - .prefix = UNDEFINED, - .exec_path = UNDEFINED, - .exec_path_env = UNDEFINED, - .pager_env = UNDEFINED, -}; diff --git a/tools/perf/util/subcmd-config.h b/tools/perf/util/subcmd-config.h deleted file mode 100644 index cc8514030b5c..000000000000 --- a/tools/perf/util/subcmd-config.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __PERF_SUBCMD_CONFIG_H -#define __PERF_SUBCMD_CONFIG_H - -struct subcmd_config { - const char *exec_name; - const char *prefix; - const char *exec_path; - const char *exec_path_env; - const char *pager_env; -}; - -extern struct subcmd_config subcmd_config; - -#endif /* __PERF_SUBCMD_CONFIG_H */ diff --git a/tools/perf/util/subcmd-util.h b/tools/perf/util/subcmd-util.h deleted file mode 100644 index 321aeb11a381..000000000000 --- a/tools/perf/util/subcmd-util.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef __PERF_SUBCMD_UTIL_H -#define __PERF_SUBCMD_UTIL_H - -#include -#include -#include - -#define NORETURN __attribute__((__noreturn__)) - -static inline void report(const char *prefix, const char *err, va_list params) -{ - char msg[1024]; - vsnprintf(msg, sizeof(msg), err, params); - fprintf(stderr, " %s%s\n", prefix, msg); -} - -static NORETURN inline void die(const char *err, ...) -{ - va_list params; - - va_start(params, err); - report(" Fatal: ", err, params); - exit(128); - va_end(params); -} - -#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) - -#define alloc_nr(x) (((x)+16)*3/2) - -/* - * Realloc the buffer pointed at by variable 'x' so that it can hold - * at least 'nr' entries; the number of entries currently allocated - * is 'alloc', using the standard growing factor alloc_nr() macro. - * - * DO NOT USE any expression with side-effect for 'x' or 'alloc'. - */ -#define ALLOC_GROW(x, nr, alloc) \ - do { \ - if ((nr) > alloc) { \ - if (alloc_nr(alloc) < (nr)) \ - alloc = (nr); \ - else \ - alloc = alloc_nr(alloc); \ - x = xrealloc((x), alloc * sizeof(*(x))); \ - } \ - } while(0) - -static inline void *xrealloc(void *ptr, size_t size) -{ - void *ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) { - ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) - die("Out of memory, realloc failed"); - } - return ret; -} - -#define astrcatf(out, fmt, ...) \ -({ \ - char *tmp = *(out); \ - if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \ - die("asprintf failed"); \ - free(tmp); \ -}) - -static inline void astrcat(char **out, const char *add) -{ - char *tmp = *out; - - if (asprintf(out, "%s%s", tmp ?: "", add) == -1) - die("asprintf failed"); - - free(tmp); -} - -static inline int prefixcmp(const char *str, const char *prefix) -{ - for (; ; str++, prefix++) - if (!*prefix) - return 0; - else if (*str != *prefix) - return (unsigned char)*prefix - (unsigned char)*str; -} - -#endif /* __PERF_SUBCMD_UTIL_H */ -- cgit v1.2.3 From 4979d0c7d0c73a3e799d4dcfbacd3cd11cc55638 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:46 +0100 Subject: perf stat record: Add record command Add 'perf stat record' command support. It creates simple (header only) perf.data file ATM. The record command could be specified anywhere among stat options. All stat command options are valid for stat record command with '-o' option exception. If specified for record command it denotes the perf data file name. Committer note: Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless while avoiding that older tools show confusing messages, for instance, with sample_type = 0, we get: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.630237 task-clock (msec) # 0.528 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.083 M/sec 978,312 cycles # 1.552 GHz 671,931 stalled-cycles-frontend # 68.68% frontend cycles idle stalled-cycles-backend 646,379 instructions # 0.66 insns per cycle # 1.04 stalled cycles per insn 131,046 branches # 207.931 M/sec 7,073 branch-misses # 5.40% of all branches 0.001193240 seconds time elapsed $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? non matching sample_type $ While with sample_type set to PERF_SAMPLE_IDENTIFIER, after we re-run 'perf stat record usleep' we get: $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ Which at least shows the names of the events in the perf.data file. Additionally, such files, when passed to 'perf report' will produce: $ oldperf report --stdio WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? Warning: Kernel address maps (/proc/{kallsyms,modules}) were restricted. Check /proc/sys/kernel/kptr_restrict before running 'perf record'. As no suitable kallsyms nor vmlinux was found, kernel samples can't be resolved. Samples in kernel modules can't be resolved as well. Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ Which is confusing and can be solved by just adding the kernel mmap record, which will also remove that warning about the data size field being equal to zero, after generating the mmap record: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.600796 task-clock (msec) # 0.478 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 54 page-faults # 0.090 M/sec 886,844 cycles # 1.476 GHz 582,169 stalled-cycles-frontend # 65.65% frontend cycles idle stalled-cycles-backend 638,344 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 130,204 branches # 216.719 M/sec 7,500 branch-misses # 5.76% of all branches 0.001255897 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # [acme@zoo linux]$ No warnings, sensible output about what are the events in the perf.data file and also a "file has no samples" message, which indeed it doesn't. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: htp://lkml.kernel.org/r/1446734469-11352-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 ++++ tools/perf/builtin-stat.c | 120 +++++++++++++++++++++++++++++++-- 2 files changed, 128 insertions(+), 4 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 4e074a660826..70eee1c2c444 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] +'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] DESCRIPTION ----------- @@ -22,6 +23,8 @@ OPTIONS ...:: Any command you can specify in a shell. +record:: + See STAT RECORD. -e:: --event=:: @@ -159,6 +162,15 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +STAT RECORD +----------- +Stores stat data into perf data file. + +-o file:: +--output file:: +Output file name. + + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bbf42eefd5e5..af2a3bf659f7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -59,6 +59,7 @@ #include "util/thread.h" #include "util/thread_map.h" #include "util/counts.h" +#include "util/session.h" #include #include @@ -126,6 +127,16 @@ static bool append_file; static const char *output_name; static int output_fd; +struct perf_stat { + bool record; + struct perf_data_file file; + struct perf_session *session; + u64 bytes_written; +}; + +static struct perf_stat perf_stat; +#define STAT_RECORD perf_stat.record + static volatile int done = 0; static struct perf_stat_config stat_config = { @@ -166,7 +177,11 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; - attr->sample_type = 0; + /* + * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless + * while avoiding that older tools show confusing messages. + */ + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled @@ -202,6 +217,26 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } +static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) +{ + if (perf_data_file__write(stat->session->file, bf, size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + stat->bytes_written += size; + return 0; +} + +static int process_synthesized_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct perf_stat *stat = (void *)tool; + return perf_stat__write(stat, event, event->header.size); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -361,6 +396,15 @@ static int __run_perf_stat(int argc, const char **argv) return -1; } + if (STAT_RECORD) { + int err, fd = perf_data_file__fd(&perf_stat.file); + + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + if (err < 0) + return err; + } + /* * Enable counters and exec the command: */ @@ -1261,6 +1305,38 @@ static int add_default_attributes(void) return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); } +static const char * const recort_usage[] = { + "perf stat record []", + NULL, +}; + +static int __cmd_record(int argc, const char **argv) +{ + struct perf_session *session; + struct perf_data_file *file = &perf_stat.file; + + argc = parse_options(argc, argv, stat_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (output_name) + file->path = output_name; + + session = perf_session__new(file, false, NULL); + if (session == NULL) { + pr_err("Perf session creation failed.\n"); + return -1; + } + + /* No pipe support ATM */ + if (perf_stat.file.is_pipe) + return -EINVAL; + + session->evlist = evsel_list; + perf_stat.session = session; + perf_stat.record = true; + return argc; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1271,6 +1347,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; + const char * const stat_subcommands[] = { "record" }; setlocale(LC_ALL, ""); @@ -1278,12 +1355,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (evsel_list == NULL) return -ENOMEM; - argc = parse_options(argc, argv, stat_options, stat_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, + (const char **) stat_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = __cmd_record(argc, argv); + if (argc < 0) + return -1; + } interval = stat_config.interval; - if (output_name && strcmp(output_name, "-")) + /* + * For record command the -o is already taken care of. + */ + if (!STAT_RECORD && output_name && strcmp(output_name, "-")) output = NULL; if (output_name && output_fd) { @@ -1450,6 +1537,31 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + if (STAT_RECORD) { + /* + * We synthesize the kernel mmap record just so that older tools + * don't emit warnings about not being able to resolve symbols + * due to /proc/sys/kernel/kptr_restrict settings and instear provide + * a saner message about no samples being in the perf.data file. + * + * This also serves to suppress a warning about f_header.data.size == 0 + * in header.c. -acme + */ + int fd = perf_data_file__fd(&perf_stat.file); + int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, + process_synthesized_event, + &perf_stat.session->machines.host); + if (err) { + pr_warning("Couldn't synthesize the kernel mmap record, harmless, " + "older tools may produce warnings about this file\n."); + } + + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + + perf_session__delete(perf_stat.session); + } + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: -- cgit v1.2.3 From 3ba78bd00e508bf46a6aa2b8e296dc8287ea4c29 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:47 +0100 Subject: perf stat record: Initialize record features Disabling all non stat related features. Also as we now enable STAT feature in the data file, adding code to instruct session open to skip sample type checking for stat data files. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 15 +++++++++++++++ tools/perf/util/session.c | 3 +++ 2 files changed, 18 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index af2a3bf659f7..c9c896a94fee 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1310,6 +1310,19 @@ static const char * const recort_usage[] = { NULL, }; +static void init_features(struct perf_session *session) +{ + int feat; + + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); +} + static int __cmd_record(int argc, const char **argv) { struct perf_session *session; @@ -1331,6 +1344,8 @@ static int __cmd_record(int argc, const char **argv) if (perf_stat.file.is_pipe) return -EINVAL; + init_features(session); + session->evlist = evsel_list; perf_stat.session = session; perf_stat.record = true; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a90c74b67e43..d5636ba94b20 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -37,6 +37,9 @@ static int perf_session__open(struct perf_session *session) if (perf_data_file__is_pipe(file)) return 0; + if (perf_header__has_feat(&session->header, HEADER_STAT)) + return 0; + if (!perf_evlist__valid_sample_type(session->evlist)) { pr_err("non matching sample_type\n"); return -1; -- cgit v1.2.3 From 8b99b1a4e0b082ea6a277766982dac84483d4d3c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:48 +0100 Subject: perf stat record: Synthesize stat record data Synthesizing needed stat record data for report/script: - cpu/thread maps - stat config Committer note: New records generated on a perf.data file with this patch: $ perf report -D | grep PERF_RECORD_ 0x568 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 29097 0x590 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5a2 [0x40]: PERF_RECORD_STAT_CONFIG $ Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-5-git-send-email-jolsa@kernel.org [ Adjusted wrt kernel PERF_RECORD_MMAP added when introducing 'perf stat record' ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 56 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c9c896a94fee..45bf4d2caebe 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -217,26 +217,20 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } -static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) +static int process_synthesized_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) { - if (perf_data_file__write(stat->session->file, bf, size) < 0) { + if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) { pr_err("failed to write perf data, error: %m\n"); return -1; } - stat->bytes_written += size; + perf_stat.bytes_written += event->header.size; return 0; } -static int process_synthesized_event(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - struct perf_stat *stat = (void *)tool; - return perf_stat__write(stat, event, event->header.size); -} - /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -323,6 +317,35 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static int perf_stat_synthesize_config(void) +{ + int err; + + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, + process_synthesized_event, + NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_stat_config(NULL, &stat_config, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize config.\n"); + return err; + } + + return 0; +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -403,6 +426,10 @@ static int __run_perf_stat(int argc, const char **argv) fd, false); if (err < 0) return err; + + err = perf_stat_synthesize_config(); + if (err < 0) + return err; } /* @@ -1560,7 +1587,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) * a saner message about no samples being in the perf.data file. * * This also serves to suppress a warning about f_header.data.size == 0 - * in header.c. -acme + * in header.c at the moment 'perf stat record' gets introduced, which + * is not really needed once we start adding the stat specific PERF_RECORD_ + * records, but the need to suppress the kptr_restrict messages in older + * tools remain -acme */ int fd = perf_data_file__fd(&perf_stat.file); int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, -- cgit v1.2.3 From 2af4646d1041ee590b0032d2b0103fa81aa43174 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: perf stat record: Store events IDs in perf data file Store event IDs in evlist object so it get stored into perf.data file. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 45bf4d2caebe..39d0c30f0f59 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -346,6 +346,38 @@ static int perf_stat_synthesize_config(void) return 0; } +#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) + +static int __store_counter_ids(struct perf_evsel *counter, + struct cpu_map *cpus, + struct thread_map *threads) +{ + int cpu, thread; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd = FD(counter, cpu, thread); + + if (perf_evlist__id_add_fd(evsel_list, counter, + cpu, thread, fd) < 0) + return -1; + } + } + + return 0; +} + +static int store_counter_ids(struct perf_evsel *counter) +{ + struct cpu_map *cpus = counter->cpus; + struct thread_map *threads = counter->threads; + + if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) + return -ENOMEM; + + return __store_counter_ids(counter, cpus, threads); +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -410,6 +442,9 @@ static int __run_perf_stat(int argc, const char **argv) l = strlen(counter->unit); if (l > unit_width) unit_width = l; + + if (STAT_RECORD && store_counter_ids(counter)) + return -1; } if (perf_evlist__apply_filters(evsel_list, &counter)) { -- cgit v1.2.3 From 664c98d4e1c2ff60627d78d4c8ae81cd2df13783 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:50 +0100 Subject: perf stat record: Add pipe support for record command Allowing storing stat record data into pipe, so report tools (report/script) could read data directly from record. Committer note: Before this patch: $ perf stat record -o - usleep 1 | perf report -i - incompatible file format (rerun with -v to learn more) $ perf stat record -o - usleep 1 | perf script -i - incompatible file format (rerun with -v to learn more) $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ After: $ perf stat record -o - usleep 1 | perf report -i - # To display the perf.data header info, please use # --header/--header-only options. # Error: The - file has no samples! $ perf stat record -o - usleep 1 | perf script -i - Display of symbols requested but neither sample IP nor sample address is selected. Hence, no addresses to convert to symbols. 0 [0x80]: failed to process type: 64 $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 39d0c30f0f59..8a2f9ce677e7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -317,10 +317,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } -static int perf_stat_synthesize_config(void) +static int perf_stat_synthesize_config(bool is_pipe) { int err; + if (is_pipe) { + err = perf_event__synthesize_attrs(NULL, perf_stat.session, + process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize attrs.\n"); + return err; + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); @@ -388,6 +397,7 @@ static int __run_perf_stat(int argc, const char **argv) size_t l; int status = 0; const bool forks = (argc > 0); + bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false; if (interval) { ts.tv_sec = interval / 1000; @@ -398,7 +408,7 @@ static int __run_perf_stat(int argc, const char **argv) } if (forks) { - if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, + if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe, workload_exec_failed_signal) < 0) { perror("failed to prepare workload"); return -1; @@ -457,12 +467,17 @@ static int __run_perf_stat(int argc, const char **argv) if (STAT_RECORD) { int err, fd = perf_data_file__fd(&perf_stat.file); - err = perf_session__write_header(perf_stat.session, evsel_list, - fd, false); + if (is_pipe) { + err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file)); + } else { + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + } + if (err < 0) return err; - err = perf_stat_synthesize_config(); + err = perf_stat_synthesize_config(is_pipe); if (err < 0) return err; } @@ -970,6 +985,10 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) struct perf_evsel *counter; char buf[64], *prefix = NULL; + /* Do not print anything if we record to the pipe. */ + if (STAT_RECORD && perf_stat.file.is_pipe) + return; + if (interval) print_interval(prefix = buf, ts); else @@ -1402,10 +1421,6 @@ static int __cmd_record(int argc, const char **argv) return -1; } - /* No pipe support ATM */ - if (perf_stat.file.is_pipe) - return -EINVAL; - init_features(session); session->evlist = evsel_list; @@ -1636,8 +1651,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } - perf_stat.session->header.data_size += perf_stat.bytes_written; - perf_session__write_header(perf_stat.session, evsel_list, fd, true); + if (!perf_stat.file.is_pipe) { + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + } perf_session__delete(perf_stat.session); } -- cgit v1.2.3 From 5a6ea81b8f9ce2736759d256ac4d63be65751199 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:51 +0100 Subject: perf stat record: Write stat events on record Writing stat events on 'perf stat record' at the time we read counter values from kernel. Committer note: After the patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.598006 task-clock (msec) # 0.484 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.087 M/sec 882,744 cycles # 1.476 GHz 581,416 stalled-cycles-frontend # 65.86% frontend cycles idle stalled-cycles-backend 636,479 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 129,334 branches # 216.275 M/sec 7,512 branch-misses # 5.81% of all branches 0.001235157 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ perf report -D | grep PERF_RECORD 0x5b0 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 5504 0x5d8 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5ea [0x40]: PERF_RECORD_STAT_CONFIG 0x62a [0x30]: PERF_RECORD_STAT 0x65a [0x30]: PERF_RECORD_STAT 0x68a [0x30]: PERF_RECORD_STAT 0x6ba [0x30]: PERF_RECORD_STAT 0x6ea [0x30]: PERF_RECORD_STAT 0x71a [0x30]: PERF_RECORD_STAT 0x74a [0x30]: PERF_RECORD_STAT 0x77a [0x30]: PERF_RECORD_STAT 0x7aa [0x30]: PERF_RECORD_STAT -1 -1 0x7da [0x40]: PERF_RECORD_MMAP -1/0: [0xffffffff81000000(0x1f000000) @ 0xffffffff81000000]: x [kernel.kallsyms]_text $ Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-8-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 8a2f9ce677e7..32aa2ea1c553 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,18 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) + +static int +perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread, + struct perf_counts_values *count) +{ + struct perf_sample_id *sid = SID(counter, cpu, thread); + + return perf_event__synthesize_stat(NULL, cpu, thread, sid->id, count, + process_synthesized_event, NULL); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -254,6 +266,13 @@ static int read_counter(struct perf_evsel *counter) count = perf_counts(counter->counts, cpu, thread); if (perf_evsel__read(counter, cpu, thread, count)) return -1; + + if (STAT_RECORD) { + if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { + pr_err("failed to write stat event\n"); + return -1; + } + } } } -- cgit v1.2.3 From 7aad0c32bb6aaa39aab596264ddc49d44c8088f3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:52 +0100 Subject: perf stat record: Write stat round events on record Writing stat round events on 'perf stat record' for each interval round. In non interval mode we store round event after the last stat event. Committer note: After the patch: $ perf report -D | grep PERF_RECORD | grep ROUND 0x852 [0x18]: PERF_RECORD_STAT_ROUND $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 32aa2ea1c553..fcece42c2611 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,16 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +static int write_stat_round_event(u64 time, u64 type) +{ + return perf_event__synthesize_stat_round(NULL, time, type, + process_synthesized_event, + NULL); +} + +#define WRITE_STAT_ROUND_EVENT(time, interval) \ + write_stat_round_event(time, PERF_STAT_ROUND_TYPE__ ## interval) + #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) static int @@ -306,6 +316,11 @@ static void process_interval(void) clock_gettime(CLOCK_MONOTONIC, &ts); diff_timespec(&rs, &ts, &ref_time); + if (STAT_RECORD) { + if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSECS_PER_SEC + rs.tv_nsec, INTERVAL)) + pr_err("failed to write stat round event\n"); + } + print_counters(&rs, 0, NULL); } @@ -1670,6 +1685,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } + if (!interval) { + if (WRITE_STAT_ROUND_EVENT(walltime_nsecs_stats.max, FINAL)) + pr_err("failed to write stat round event\n"); + } + if (!perf_stat.file.is_pipe) { perf_stat.session->header.data_size += perf_stat.bytes_written; perf_session__write_header(perf_stat.session, evsel_list, fd, true); -- cgit v1.2.3 From e9d6db8e8df42a38f79f264ab58c104e1678b12c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:53 +0100 Subject: perf stat record: Do not allow record with multiple runs mode We currently don't support storing multiple session in perf.data, so we can't allow -r option in stat record. $ perf stat -e cycles -r 2 record ls Cannot use -r option with perf stat record. Committer note: Before this patch we would a perf.data file such as: $ perf stat -e cycles -r 2 record ls Performance counter stats for 'ls' (2 runs): 3,935,236 cycles 0.002353261 seconds time elapsed ( +- 4.76% ) $ perf report -D | grep PERF_RECORD | grep ROUND 0xf0 [0]: failed to process type: 16 Error: failed to process sample $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index fcece42c2611..10f86a6d7b15 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1449,6 +1449,11 @@ static int __cmd_record(int argc, const char **argv) if (output_name) file->path = output_name; + if (run_count != 1 || forever) { + pr_err("Cannot use -r option with perf stat record.\n"); + return -1; + } + session = perf_session__new(file, false, NULL); if (session == NULL) { pr_err("Perf session creation failed.\n"); -- cgit v1.2.3 From 7b60a7e3a687481553d2b6ec7e6390a6e82f1849 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:54 +0100 Subject: perf stat record: Synthesize event update events Synthesize other events stuff not carried within attr event - unit, scale, name. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 10f86a6d7b15..575e2535ea03 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -351,8 +351,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static bool has_unit(struct perf_evsel *counter) +{ + return counter->unit && *counter->unit; +} + +static bool has_scale(struct perf_evsel *counter) +{ + return counter->scale != 1; +} + static int perf_stat_synthesize_config(bool is_pipe) { + struct perf_evsel *counter; int err; if (is_pipe) { @@ -364,6 +375,54 @@ static int perf_stat_synthesize_config(bool is_pipe) } } + /* + * Synthesize other events stuff not carried within + * attr event - unit, scale, name + */ + evlist__for_each(evsel_list, counter) { + if (!counter->supported) + continue; + + /* + * Synthesize unit and scale only if it's defined. + */ + if (has_unit(counter)) { + err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel unit.\n"); + return err; + } + } + + if (has_scale(counter)) { + err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + if (counter->own_cpus) { + err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + /* + * Name is needed only for pipe output, + * perf.data carries event names. + */ + if (is_pipe) { + err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel name.\n"); + return err; + } + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); -- cgit v1.2.3 From ba6039b6c8fcc24de7d6ab7b0bada4becaf84a2c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:55 +0100 Subject: perf stat report: Add report command Adding 'perf stat report' command support. ATM it only processes attr events and display nothing. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-12-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 +++++++ tools/perf/builtin-stat.c | 61 +++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 4 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 70eee1c2c444..95f492828657 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -11,6 +11,7 @@ SYNOPSIS 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] 'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] +'perf stat' report [-i file] DESCRIPTION ----------- @@ -26,6 +27,9 @@ OPTIONS record:: See STAT RECORD. +report:: + See STAT REPORT. + -e:: --event=:: Select the PMU event. Selection can be: @@ -170,6 +174,14 @@ Stores stat data into perf data file. --output file:: Output file name. +STAT REPORT +----------- +Reads and reports stat data from perf data file. + +-i file:: +--input file:: +Input file name. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 575e2535ea03..abba49b847d2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -60,6 +60,8 @@ #include "util/thread_map.h" #include "util/counts.h" #include "util/session.h" +#include "util/tool.h" +#include "asm/bug.h" #include #include @@ -132,6 +134,7 @@ struct perf_stat { struct perf_data_file file; struct perf_session *session; u64 bytes_written; + struct perf_tool tool; }; static struct perf_stat perf_stat; @@ -1041,8 +1044,8 @@ static void print_header(int argc, const char **argv) else if (target.cpu_list) fprintf(output, "\'CPU(s) %s", target.cpu_list); else if (!target__has_task(&target)) { - fprintf(output, "\'%s", argv[0]); - for (i = 1; i < argc; i++) + fprintf(output, "\'%s", argv ? argv[0] : "pipe"); + for (i = 1; argv && (i < argc); i++) fprintf(output, " %s", argv[i]); } else if (target.pid) fprintf(output, "process id \'%s", target.pid); @@ -1527,6 +1530,55 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static const char * const report_usage[] = { + "perf stat report []", + NULL, +}; + +static struct perf_stat perf_stat = { + .tool = { + .attr = perf_event__process_attr, + }, +}; + +static int __cmd_report(int argc, const char **argv) +{ + struct perf_session *session; + const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_END() + }; + struct stat st; + int ret; + + argc = parse_options(argc, argv, options, report_usage, 0); + + if (!input_name || !strlen(input_name)) { + if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) + input_name = "-"; + else + input_name = "perf.data"; + } + + perf_stat.file.path = input_name; + perf_stat.file.mode = PERF_DATA_MODE_READ; + + session = perf_session__new(&perf_stat.file, false, &perf_stat.tool); + if (session == NULL) + return -1; + + perf_stat.session = session; + stat_config.output = stderr; + evsel_list = session->evlist; + + ret = perf_session__process_events(session); + if (ret) + return ret; + + perf_session__delete(session); + return 0; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1537,7 +1589,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; - const char * const stat_subcommands[] = { "record" }; + const char * const stat_subcommands[] = { "record", "report" }; setlocale(LC_ALL, ""); @@ -1553,7 +1605,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) argc = __cmd_record(argc, argv); if (argc < 0) return -1; - } + } else if (argc && !strncmp(argv[0], "rep", 3)) + return __cmd_report(argc, argv); interval = stat_config.interval; -- cgit v1.2.3 From 1975d36e14b3314d1d0c7a428946ec0c27fd6e95 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:56 +0100 Subject: perf stat report: Process cpu/threads maps Adding processing of cpu/threads maps. Configuring session's evlist with these maps. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-13-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/time/tm/g parameters to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index abba49b847d2..0a1cfdd70df0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -135,6 +135,9 @@ struct perf_stat { struct perf_session *session; u64 bytes_written; struct perf_tool tool; + bool maps_allocated; + struct cpu_map *cpus; + struct thread_map *threads; }; static struct perf_stat perf_stat; @@ -234,9 +237,9 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } -static int write_stat_round_event(u64 time, u64 type) +static int write_stat_round_event(u64 tm, u64 type) { - return perf_event__synthesize_stat_round(NULL, time, type, + return perf_event__synthesize_stat_round(NULL, tm, type, process_synthesized_event, NULL); } @@ -1530,6 +1533,63 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int set_maps(struct perf_stat *st) +{ + if (!st->cpus || !st->threads) + return 0; + + if (WARN_ONCE(st->maps_allocated, "stats double allocation\n")) + return -EINVAL; + + perf_evlist__set_maps(evsel_list, st->cpus, st->threads); + + if (perf_evlist__alloc_stats(evsel_list, true)) + return -ENOMEM; + + st->maps_allocated = true; + return 0; +} + +static +int process_thread_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + + if (st->threads) { + pr_warning("Extra thread map event, ignoring.\n"); + return 0; + } + + st->threads = thread_map__new_event(&event->thread_map); + if (!st->threads) + return -ENOMEM; + + return set_maps(st); +} + +static +int process_cpu_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + struct cpu_map *cpus; + + if (st->cpus) { + pr_warning("Extra cpu map event, ignoring.\n"); + return 0; + } + + cpus = cpu_map__new_data(&event->cpu_map.data); + if (!cpus) + return -ENOMEM; + + st->cpus = cpus; + return set_maps(st); +} + static const char * const report_usage[] = { "perf stat report []", NULL, @@ -1538,6 +1598,8 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .thread_map = process_thread_map_event, + .cpu_map = process_cpu_map_event, }, }; -- cgit v1.2.3 From 62ba18ba938a8740ab18e02342b282d7378986f7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:57 +0100 Subject: perf stat report: Process stat config event Adding processing of stat config event and initialize stat_config object. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-14-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0a1cfdd70df0..1e5db50dab9e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1533,6 +1533,15 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static +int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + perf_event__read_stat_config(&stat_config, &event->stat_config); + return 0; +} + static int set_maps(struct perf_stat *st) { if (!st->cpus || !st->threads) @@ -1600,6 +1609,7 @@ static struct perf_stat perf_stat = { .attr = perf_event__process_attr, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, + .stat_config = process_stat_config_event, }, }; -- cgit v1.2.3 From 68d702f7a1202dd39d9fa01b7bea92ba9e5785d9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:58 +0100 Subject: perf stat report: Add support to initialize aggr_map from file Using perf.data's perf_env data to initialize aggregate config. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-15-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/socket/socket_id/g to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1e5db50dab9e..c78052580023 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1326,6 +1326,101 @@ static void perf_stat__exit_aggr_mode(void) cpus_aggr_map = NULL; } +static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) +{ + int cpu; + + if (idx > map->nr) + return -1; + + cpu = map->map[idx]; + + if (cpu >= env->nr_cpus_online) + return -1; + + return cpu; +} + +static int perf_env__get_socket(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int cpu = perf_env__get_cpu(env, map, idx); + + return cpu == -1 ? -1 : env->cpu[cpu].socket_id; +} + +static int perf_env__get_core(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int core = -1, cpu = perf_env__get_cpu(env, map, idx); + + if (cpu != -1) { + int socket_id = env->cpu[cpu].socket_id; + + /* + * Encode socket in upper 16 bits + * core_id is relative to socket, and + * we need a global id. So we combine + * socket + core id. + */ + core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff); + } + + return core; +} + +static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **sockp) +{ + return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env); +} + +static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **corep) +{ + return cpu_map__build_map(cpus, corep, perf_env__get_core, env); +} + +static int perf_stat__get_socket_file(struct cpu_map *map, int idx) +{ + return perf_env__get_socket(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat__get_core_file(struct cpu_map *map, int idx) +{ + return perf_env__get_core(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat_init_aggr_mode_file(struct perf_stat *st) +{ + struct perf_env *env = &st->session->header.env; + + switch (stat_config.aggr_mode) { + case AGGR_SOCKET: + if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build socket map"); + return -1; + } + aggr_get_id = perf_stat__get_socket_file; + break; + case AGGR_CORE: + if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build core map"); + return -1; + } + aggr_get_id = perf_stat__get_core_file; + break; + case AGGR_NONE: + case AGGR_GLOBAL: + case AGGR_THREAD: + case AGGR_UNSET: + default: + break; + } + + return 0; +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1538,7 +1633,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_session *session __maybe_unused) { + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + perf_event__read_stat_config(&stat_config, &event->stat_config); + + if (perf_stat.file.is_pipe) + perf_stat_init_aggr_mode(); + else + perf_stat_init_aggr_mode_file(st); + return 0; } -- cgit v1.2.3 From 6edb78a2178fd85d07b1a7fbb3629be56b860224 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:01 +0100 Subject: perf stat report: Move csv_sep initialization before report command So we have csv_sep properly initialized before report command leg. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-18-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c78052580023..f9d4e0963ac7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1776,6 +1776,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (csv_sep) { + csv_output = true; + if (!strcmp(csv_sep, "\\t")) + csv_sep = "\t"; + } else + csv_sep = DEFAULT_SEPARATOR; + if (argc && !strncmp(argv[0], "rec", 3)) { argc = __cmd_record(argc, argv); if (argc < 0) @@ -1826,13 +1833,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) stat_config.output = output; - if (csv_sep) { - csv_output = true; - if (!strcmp(csv_sep, "\\t")) - csv_sep = "\t"; - } else - csv_sep = DEFAULT_SEPARATOR; - /* * let the spreadsheet do the pretty-printing */ -- cgit v1.2.3 From a56f9390aa9d9b1c782c3dbd5ca2c4245eb265fc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:59 +0100 Subject: perf stat report: Process stat and stat round events Adding processing of stat and stat round events. The stat data com in stat events, using generic function process_stat_round_event to store data under perf_evsel object. The stat-round events comes each interval or as last event in non interval mode. The function process_stat_round_event process stored data for each perf_evsel object and print it out. Committer note: After this patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ $ perf stat report Performance counter stats for '/home/acme/bin/perf stat record usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-16-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f9d4e0963ac7..d27d1b921efa 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1628,6 +1628,32 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct stat_round_event *round = &event->stat_round; + struct perf_evsel *counter; + struct timespec tsh, *ts = NULL; + const char **argv = session->header.env.cmdline_argv; + int argc = session->header.env.nr_cmdline; + + evlist__for_each(evsel_list, counter) + perf_stat_process_counter(&stat_config, counter); + + if (round->type == PERF_STAT_ROUND_TYPE__FINAL) + update_stats(&walltime_nsecs_stats, round->time); + + if (stat_config.interval && round->time) { + tsh.tv_sec = round->time / NSECS_PER_SEC; + tsh.tv_nsec = round->time % NSECS_PER_SEC; + ts = &tsh; + } + + print_counters(ts, argc, argv); + return 0; +} + static int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -1713,6 +1739,8 @@ static struct perf_stat perf_stat = { .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, + .stat = perf_event__process_stat_event, + .stat_round = process_stat_round_event, }, }; -- cgit v1.2.3 From fa6ea7817db3839b58d46649b7834320257e7702 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:00 +0100 Subject: perf stat report: Process event update events Adding processing of event update events, so perf stat report can store additional info for events - unit,scale,name. Committer note: Before: # perf stat record -e power/energy-cores/ -a ^C Performance counter stats for 'system wide': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 332,488,114,176 power/energy-cores/ 1.597176695 seconds time elapsed # After, using the same perf.data file generated in the "Before" case above: # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # Reported-by: Kan Liang Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-17-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d27d1b921efa..3ccf5a9dab33 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1736,6 +1736,7 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .event_update = perf_event__process_event_update, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, -- cgit v1.2.3 From 89af4e05c21d68f22e07fe66940ea675615a49ed Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:02 +0100 Subject: perf stat report: Allow to override aggr_mode Allowing to override record aggr_mode. It's possible to use perf stat like: $ perf stat report -A $ perf stat report --per-core $ perf stat report --per-socket To customize the recorded aggregate mode regardless what was used during the stat record command. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-19-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 10 ++++++++++ tools/perf/builtin-stat.c | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 95f492828657..52ef7a9d50aa 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -182,6 +182,16 @@ Reads and reports stat data from perf data file. --input file:: Input file name. +--per-socket:: +Aggregate counts per processor socket for system-wide mode measurements. + +--per-core:: +Aggregate counts per physical processor for system-wide mode measurements. + +-A:: +--no-aggr:: +Do not aggregate counts across all monitored CPUs. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3ccf5a9dab33..9805e03ab163 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -138,6 +138,7 @@ struct perf_stat { bool maps_allocated; struct cpu_map *cpus; struct thread_map *threads; + enum aggr_mode aggr_mode; }; static struct perf_stat perf_stat; @@ -1663,6 +1664,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, perf_event__read_stat_config(&stat_config, &event->stat_config); + if (cpu_map__empty(st->cpus)) { + if (st->aggr_mode != AGGR_UNSET) + pr_warning("warning: processing task data, aggregation mode not set\n"); + return 0; + } + + if (st->aggr_mode != AGGR_UNSET) + stat_config.aggr_mode = st->aggr_mode; + if (perf_stat.file.is_pipe) perf_stat_init_aggr_mode(); else @@ -1743,6 +1753,7 @@ static struct perf_stat perf_stat = { .stat = perf_event__process_stat_event, .stat_round = process_stat_round_event, }, + .aggr_mode = AGGR_UNSET, }; static int __cmd_report(int argc, const char **argv) @@ -1750,6 +1761,12 @@ static int __cmd_report(int argc, const char **argv) struct perf_session *session; const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode, + "aggregate counts per processor socket", AGGR_SOCKET), + OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode, + "aggregate counts per physical processor core", AGGR_CORE), + OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode, + "disable CPU count aggregation", AGGR_NONE), OPT_END() }; struct stat st; -- cgit v1.2.3 From 6db1a5c190d6abe416ea36aa28a6c53e0b3bbd5e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:05 +0100 Subject: perf stat record: Keep sample_type 0 for pipe session For pipe sessions we need to keep sample_type zero, because script's perf_evsel__check_attr is triggered by sample_type != 0, and the check would fail on stat session. I was tempted to keep it zero unconditionally, but the pipe session is sufficient. In perf.data session we are guarded by HEADER_STAT feature. Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452028152-26762-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9805e03ab163..7f568244662b 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -184,11 +184,18 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; + /* * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless * while avoiding that older tools show confusing messages. + * + * However for pipe sessions we need to keep it zero, + * because script's perf_evsel__check_attr is triggered + * by attr->sample_type != 0, and we can't run it on + * stat sessions. */ - attr->sample_type = PERF_SAMPLE_IDENTIFIER; + if (!(STAT_RECORD && perf_stat.file.is_pipe)) + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled -- cgit v1.2.3 From 8a59f3ccbc11fcb222b046a64929dd473f7dff54 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 12 Jan 2016 10:35:29 +0100 Subject: perf stat: Fix recort_usage typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Markus reported gcc 6 complains when compiling perf stat command: builtin-stat.c:1591:27: error: ‘recort_usage’ defined but not used [-Werror=unused-const-variable] static const char * const recort_usage[] = { ^~~~~~~~~~~~ I fixed the typo and realized we already export record_usage, so I also prefixed it with stat (and included report_usage). Reported-by: Markus Trippelsdorf Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452591329-27620-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/perf/builtin-stat.c') diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 7f568244662b..038e877081b6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1588,7 +1588,7 @@ static int add_default_attributes(void) return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); } -static const char * const recort_usage[] = { +static const char * const stat_record_usage[] = { "perf stat record []", NULL, }; @@ -1611,7 +1611,7 @@ static int __cmd_record(int argc, const char **argv) struct perf_session *session; struct perf_data_file *file = &perf_stat.file; - argc = parse_options(argc, argv, stat_options, record_usage, + argc = parse_options(argc, argv, stat_options, stat_record_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (output_name) @@ -1745,7 +1745,7 @@ int process_cpu_map_event(struct perf_tool *tool __maybe_unused, return set_maps(st); } -static const char * const report_usage[] = { +static const char * const stat_report_usage[] = { "perf stat report []", NULL, }; @@ -1779,7 +1779,7 @@ static int __cmd_report(int argc, const char **argv) struct stat st; int ret; - argc = parse_options(argc, argv, options, report_usage, 0); + argc = parse_options(argc, argv, options, stat_report_usage, 0); if (!input_name || !strlen(input_name)) { if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) -- cgit v1.2.3