From bb91a073ed124d3f6224d8ac37ecb536c01970c1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 13 Oct 2019 17:14:25 +0200 Subject: [PATCH 01/57] perf tools: Allow to build with -ltcmalloc By using "make TCMALLOC=1" you can enable perf to be build for usage with libtcmalloc.so (gperftools). Get heap profile (tools/perf directory): $ $ make TCMALLOC=1 DEBUG=1 $ HEAPPROFILE=/tmp/heapprof ./perf ... $ pprof ./perf /tmp/heapprof.000* (pprof) top Total: 2335.5 MB 1735.1 74.3% 74.3% 1735.1 74.3% memdup 402.0 17.2% 91.5% 402.0 17.2% zalloc 140.2 6.0% 97.5% 145.8 6.2% map__new 33.6 1.4% 98.9% 33.6 1.4% symbol__new 12.4 0.5% 99.5% 12.4 0.5% alloc_event 6.2 0.3% 99.7% 6.2 0.3% nsinfo__new 5.5 0.2% 100.0% 5.5 0.2% nsinfo__copy 0.3 0.0% 100.0% 0.3 0.0% dso__new 0.1 0.0% 100.0% 0.1 0.0% do_read_string 0.0 0.0% 100.0% 0.0 0.0% __GI__IO_file_doallocate See callstack: $ pprof --pdf ./perf /tmp/heapprof.00* > callstack.pdf $ pprof --web ./perf /tmp/heapprof.00* Committer testing: Install gperftools, on fedora: # dnf install gperftools-devel Then build: $ make TCMALLOC=1 DEBUG=1 -C tools/perf O=/tmp/build/perf install-bin Verify that it linked against the right library: $ ldd ~/bin/perf | grep tcma libtcmalloc.so.4 => /lib64/libtcmalloc.so.4 (0x00007fb2953a7000) $ Run 'perf trace' system wide for 1 minute: # HEAPPROFILE=/tmp/heapprof perf trace -a sleep 1m 59985.524 ( 0.006 ms): Web Content/20354 recvmsg(fd: 9, msg: 0x7ffee5fdafb0) = -1 EAGAIN (Resource temporarily unavailable) 59985.536 ( 0.005 ms): Web Content/20354 recvmsg(fd: 9, msg: 0x7ffee5fdafc0) = -1 EAGAIN (Resource temporarily unavailable) 59981.956 (10.143 ms): SCTP timer/21716 ... [continued]: select()) = 0 (Timeout) 59985.549 ( ): Web Content/20354 poll(ufds: 0x7f1df38af180, nfds: 3, timeout_msecs: 4294967295) ... 0.926 (59999.481 ms): sleep/29764 ... [continued]: nanosleep()) = 0 59992.133 ( ): SCTP timer/21716 select(tvp: 0x7ff5bf7fee80) ... 60000.477 ( 0.009 ms): sleep/29764 close(fd: 1) = 0 60000.493 ( 0.005 ms): sleep/29764 close(fd: 2) = 0 60000.514 ( ): sleep/29764 exit_group() = ? Dumping heap profile to /tmp/heapprof.0001.heap (Exiting, 3 MB in use) [root@quaco ~]# Install pprof: # dnf install pprof And run it: # pprof ~/bin/perf /tmp/heapprof.0001.heap Using local file /root/bin/perf. Using local file /tmp/heapprof.0001.heap. Welcome to pprof! For help, type 'help'. (pprof) top Total: 4.0 MB 1.7 42.0% 42.0% 2.2 54.1% map__new 0.9 23.3% 65.3% 0.9 23.3% zalloc 0.5 11.4% 76.7% 0.5 11.4% dso__new 0.2 5.6% 82.3% 0.3 8.5% trace__sys_enter 0.2 4.9% 87.2% 0.2 4.9% __GI___strdup 0.2 3.8% 91.0% 0.2 3.8% new_term 0.1 2.2% 93.2% 0.4 10.1% __perf_pmu__new_alias 0.0 1.0% 94.3% 0.0 1.2% event_read_fields 0.0 0.8% 95.1% 0.0 0.8% nsinfo__new 0.0 0.7% 95.8% 0.1 3.2% trace__read_syscall_info (pprof) Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Andi Kleen Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191013151427.11941-2-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 5 +++++ tools/perf/Makefile.perf | 2 ++ 2 files changed, 7 insertions(+) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 063202c53b64..1783427da9b0 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -265,6 +265,11 @@ LDFLAGS += -Wl,-z,noexecstack EXTLIBS = -lpthread -lrt -lm -ldl +ifneq ($(TCMALLOC),) + CFLAGS += -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free + EXTLIBS += -ltcmalloc +endif + ifeq ($(FEATURES_DUMP),) include $(srctree)/tools/build/Makefile.feature else diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index a099a8a89447..8f1ba986d3bf 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -114,6 +114,8 @@ include ../scripts/utilities.mak # Define NO_LIBZSTD if you do not want support of Zstandard based runtime # trace compression in record mode. # +# Define TCMALLOC to enable tcmalloc heap profiling. +# # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL From b3509b6ed7a79ec49f6b64e4f3b780f259a2a468 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 11 Oct 2019 11:21:39 -0700 Subject: [PATCH 02/57] perf script: Fix --reltime with --time My earlier patch to just enable --reltime with --time was a little too optimistic. The --time parsing would accept absolute time, which is very confusing to the user. Support relative time in --time parsing too. This only works with recent perf record that records the first sample time. Otherwise we error out. Fixes: 3714437d3fcc ("perf script: Allow --time with --reltime") Signed-off-by: Andi Kleen Cc: Jiri Olsa Link: http://lore.kernel.org/lkml/20191011182140.8353-1-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 5 +++-- tools/perf/util/time-utils.c | 27 ++++++++++++++++++++++++--- tools/perf/util/time-utils.h | 5 +++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 1c797a948ada..f86c5cce5b2c 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3864,10 +3864,11 @@ int cmd_script(int argc, const char **argv) goto out_delete; if (script.time_str) { - err = perf_time__parse_for_ranges(script.time_str, session, + err = perf_time__parse_for_ranges_reltime(script.time_str, session, &script.ptime_range, &script.range_size, - &script.range_num); + &script.range_num, + reltime); if (err < 0) goto out_delete; diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 9796a2e43f67..302443921681 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -458,10 +458,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, return true; } -int perf_time__parse_for_ranges(const char *time_str, +int perf_time__parse_for_ranges_reltime(const char *time_str, struct perf_session *session, struct perf_time_interval **ranges, - int *range_size, int *range_num) + int *range_size, int *range_num, + bool reltime) { bool has_percent = strchr(time_str, '%'); struct perf_time_interval *ptime_range; @@ -471,7 +472,7 @@ int perf_time__parse_for_ranges(const char *time_str, if (!ptime_range) return -ENOMEM; - if (has_percent) { + if (has_percent || reltime) { if (session->evlist->first_sample_time == 0 && session->evlist->last_sample_time == 0) { pr_err("HINT: no first/last sample time found in perf data.\n" @@ -479,7 +480,9 @@ int perf_time__parse_for_ranges(const char *time_str, "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n"); goto error; } + } + if (has_percent) { num = perf_time__percent_parse_str( ptime_range, size, time_str, @@ -492,6 +495,15 @@ int perf_time__parse_for_ranges(const char *time_str, if (num < 0) goto error_invalid; + if (reltime) { + int i; + + for (i = 0; i < num; i++) { + ptime_range[i].start += session->evlist->first_sample_time; + ptime_range[i].end += session->evlist->first_sample_time; + } + } + *range_size = size; *range_num = num; *ranges = ptime_range; @@ -504,6 +516,15 @@ error: return ret; } +int perf_time__parse_for_ranges(const char *time_str, + struct perf_session *session, + struct perf_time_interval **ranges, + int *range_size, int *range_num) +{ + return perf_time__parse_for_ranges_reltime(time_str, session, ranges, + range_size, range_num, false); +} + int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) { u64 sec = timestamp / NSEC_PER_SEC; diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h index 4f42988eb2f7..1142b0bddd5e 100644 --- a/tools/perf/util/time-utils.h +++ b/tools/perf/util/time-utils.h @@ -26,6 +26,11 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf, struct perf_session; +int perf_time__parse_for_ranges_reltime(const char *str, struct perf_session *session, + struct perf_time_interval **ranges, + int *range_size, int *range_num, + bool reltime); + int perf_time__parse_for_ranges(const char *str, struct perf_session *session, struct perf_time_interval **ranges, int *range_size, int *range_num); From 5a40e1994815ab09c59614c6a13d94eef55d1a7f Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 11 Oct 2019 11:21:40 -0700 Subject: [PATCH 03/57] perf evlist: Fix fix for freed id arrays In the earlier fix for the memory overrun of id arrays I managed to typo the wrong event in the fix. Of course we need to close the current event in the loop, not the original failing event. The same test case as in the original patch still passes. Fixes: 7834fa948beb ("perf evlist: Fix access of freed id arrays") Signed-off-by: Andi Kleen Cc: Jiri Olsa Link: http://lore.kernel.org/lkml/20191011182140.8353-2-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 21b77efa802c..8793b4e322b0 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1599,7 +1599,7 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list, is_open = false; if (c2->leader == leader) { if (is_open) - perf_evsel__close(&evsel->core); + perf_evsel__close(&c2->core); c2->leader = c2; c2->core.nr_members = 0; } From 6add129c5d9210ada25217abc130df0b7096ee02 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 11 Oct 2019 17:19:41 +0800 Subject: [PATCH 04/57] perf test: Report failure for mmap events When fail to mmap events in task exit case, it misses to set 'err' to -1; thus the testing will not report failure for it. This patch sets 'err' to -1 when fails to mmap events, thus Perf tool can report correct result. Fixes: d723a55096b8 ("perf test: Add test case for checking number of EXIT events") Signed-off-by: Leo Yan Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191011091942.29841-1-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/task-exit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 4965f8b9055b..19fa7cb429fd 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -111,6 +111,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused if (evlist__mmap(evlist, 128) < 0) { pr_debug("failed to mmap events: %d (%s)\n", errno, str_error_r(errno, sbuf, sizeof(sbuf))); + err = -1; goto out_delete_evlist; } From 791ce9c48c79210d2ffcdbe69421e7783b32921f Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 11 Oct 2019 17:19:42 +0800 Subject: [PATCH 05/57] perf test: Avoid infinite loop for task exit case When executing the task exit testing case, perf gets stuck in an endless loop this case and doesn't return back on Arm64 Juno board. After digging into this issue, since Juno board has Arm's big.LITTLE CPUs, thus the PMUs are not compatible between the big CPUs and little CPUs. This leads to a PMU event that cannot be enabled properly when the traced task is migrated from one variant's CPU to another variant. Finally, the test case runs into infinite loop for cannot read out any event data after return from polling. Eventually, we need to work out formal solution to allow PMU events can be freely migrated from one CPU variant to another, but this is a difficult task and a different topic. This patch tries to fix the Perf test case to avoid infinite loop, when the testing detects 1000 times retrying for reading empty events, it will directly bail out and return failure. This allows the Perf tool can continue its other test cases. Signed-off-by: Leo Yan Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191011091942.29841-2-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/task-exit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 19fa7cb429fd..adaff9044331 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -54,6 +54,7 @@ int test__task_exit(struct test *test __maybe_unused, int subtest __maybe_unused struct perf_cpu_map *cpus; struct perf_thread_map *threads; struct mmap *md; + int retry_count = 0; signal(SIGCHLD, sig_handler); @@ -133,6 +134,13 @@ retry: out_init: if (!exited || !nr_exit) { evlist__poll(evlist, -1); + + if (retry_count++ > 1000) { + pr_debug("Failed after retrying 1000 times\n"); + err = -1; + goto out_free_maps; + } + goto retry; } From 800d3f561659b5436f8c57e7c26dd1f6928b5615 Mon Sep 17 00:00:00 2001 From: Jin Yao Date: Fri, 11 Oct 2019 10:21:22 +0800 Subject: [PATCH 06/57] perf report: Add warning when libunwind not compiled in We received a user report that call-graph DWARF mode was enabled in 'perf record' but 'perf report' didn't unwind the callstack correctly. The reason was, libunwind was not compiled in. We can use 'perf -vv' to check the compiled libraries but it would be valuable to report a warning to user directly (especially valuable for a perf newbie). The warning is: Warning: Please install libunwind development packages during the perf build. Both TUI and stdio are supported. Signed-off-by: Jin Yao Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191011022122.26369-1-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index aae0e57c60fb..7accaf8ef689 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -399,6 +399,13 @@ static int report__setup_sample_type(struct report *rep) PERF_SAMPLE_BRANCH_ANY)) rep->nonany_branch_mode = true; +#ifndef HAVE_LIBUNWIND_SUPPORT + if (dwarf_callchain_users) { + ui__warning("Please install libunwind development packages " + "during the perf build.\n"); + } +#endif + return 0; } From 353dcaa2f979a04f9397306ae3165ccf9fc731df Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Oct 2019 11:36:45 -0700 Subject: [PATCH 07/57] perf annotate: Avoid reallocation in objdump parsing Objdump output is parsed using getline which allocates memory for the read. Getline will realloc if the memory is too small, but currently the line is always freed after the call. Simplify parse_objdump_line by performing the reading in symbol__disassemble. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jin Yao Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191010183649.23768-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 2b856b6b46f6..f9c39a742418 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1489,24 +1489,17 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start * means that it's not a disassembly line so should be treated differently. * The ops.raw part will be parsed further according to type of the instruction. */ -static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, +static int symbol__parse_objdump_line(struct symbol *sym, struct annotate_args *args, - int *line_nr) + char *line, int *line_nr) { struct map *map = args->ms.map; struct annotation *notes = symbol__annotation(sym); struct disasm_line *dl; - char *line = NULL, *parsed_line, *tmp, *tmp2; - size_t line_len; + char *parsed_line, *tmp, *tmp2; s64 line_ip, offset = -1; regmatch_t match[2]; - if (getline(&line, &line_len, file) < 0) - return -1; - - if (!line) - return -1; - line_ip = -1; parsed_line = strim(line); @@ -1543,7 +1536,6 @@ static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, args->ms.sym = sym; dl = disasm_line__new(args); - free(line); (*line_nr)++; if (dl == NULL) @@ -1876,6 +1868,8 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) int lineno = 0; int nline; pid_t pid; + char *line; + size_t line_len; int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); if (err) @@ -1964,18 +1958,26 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) goto out_free_command; } + /* Storage for getline. */ + line = NULL; + line_len = 0; + nline = 0; while (!feof(file)) { + if (getline(&line, &line_len, file) < 0 || !line) + break; + /* * The source code line number (lineno) needs to be kept in * across calls to symbol__parse_objdump_line(), so that it * can associate it with the instructions till the next one. * See disasm_line__new() and struct disasm_line::line_nr. */ - if (symbol__parse_objdump_line(sym, file, args, &lineno) < 0) + if (symbol__parse_objdump_line(sym, args, line, &lineno) < 0) break; nline++; } + free(line); if (nline == 0) pr_err("No output from %s\n", command); From 4235949944d1bb244c85fd184cdc2f78e9df848b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Oct 2019 11:36:46 -0700 Subject: [PATCH 08/57] perf annotate: Use libsubcmd's run-command.h to fork objdump Reduce duplicated logic by using the subcmd library. Ensure when errors occur they are reported to the caller. Before this patch, if no lines are read the error status is 0. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jin Yao Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191010183649.23768-3-irogers@google.com Link: http://lore.kernel.org/lkml/20191015003418.62563-1-irogers@google.com [ merged follow up fix for NULL termination as in the 2nd link above ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 72 ++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f9c39a742418..9835666db5a7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -43,6 +43,7 @@ #include #include #include +#include /* FIXME: For the HE_COLORSET */ #include "ui/browser.h" @@ -1864,12 +1865,19 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) struct kcore_extract kce; bool delete_extract = false; bool decomp = false; - int stdout_fd[2]; int lineno = 0; int nline; - pid_t pid; char *line; size_t line_len; + const char *objdump_argv[] = { + "/bin/sh", + "-c", + NULL, /* Will be the objdump command to run. */ + "--", + NULL, /* Will be the symfs path. */ + NULL, + }; + struct child_process objdump_process; int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); if (err) @@ -1899,7 +1907,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0) - goto out; + return -1; decomp = true; strcpy(symfs_filename, tmp); @@ -1924,38 +1932,28 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) pr_debug("Executing: %s\n", command); - err = -1; - if (pipe(stdout_fd) < 0) { - pr_err("Failure creating the pipe to run %s\n", command); + objdump_argv[2] = command; + objdump_argv[4] = symfs_filename; + + /* Create a pipe to read from for stdout */ + memset(&objdump_process, 0, sizeof(objdump_process)); + objdump_process.argv = objdump_argv; + objdump_process.out = -1; + if (start_command(&objdump_process)) { + pr_err("Failure starting to run %s\n", command); + err = -1; goto out_free_command; } - pid = fork(); - if (pid < 0) { - pr_err("Failure forking to run %s\n", command); - goto out_close_stdout; - } - - if (pid == 0) { - close(stdout_fd[0]); - dup2(stdout_fd[1], 1); - close(stdout_fd[1]); - execl("/bin/sh", "sh", "-c", command, "--", symfs_filename, - NULL); - perror(command); - exit(-1); - } - - close(stdout_fd[1]); - - file = fdopen(stdout_fd[0], "r"); + file = fdopen(objdump_process.out, "r"); if (!file) { pr_err("Failure creating FILE stream for %s\n", command); /* * If we were using debug info should retry with * original binary. */ - goto out_free_command; + err = -1; + goto out_close_stdout; } /* Storage for getline. */ @@ -1979,8 +1977,14 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) } free(line); - if (nline == 0) + err = finish_command(&objdump_process); + if (err) + pr_err("Error running %s\n", command); + + if (nline == 0) { + err = -1; pr_err("No output from %s\n", command); + } /* * kallsyms does not have symbol sizes so there may a nop at the end. @@ -1990,23 +1994,21 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) delete_last_nop(sym); fclose(file); - err = 0; + +out_close_stdout: + close(objdump_process.out); + out_free_command: free(command); -out_remove_tmp: - close(stdout_fd[0]); +out_remove_tmp: if (decomp) unlink(symfs_filename); if (delete_extract) kcore_extract__delete(&kce); -out: - return err; -out_close_stdout: - close(stdout_fd[1]); - goto out_free_command; + return err; } static void calc_percent(struct sym_hist *sym_hist, From 7a675de428364a16038a8e6ed557daf0a009ce9c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Oct 2019 11:36:47 -0700 Subject: [PATCH 09/57] perf annotate: Don't pipe objdump output through 'grep' command Simplify the objdump command by not piping the output of objdump through grep. Instead, drop lines that match the grep pattern during the reading loop. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jin Yao Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191010183649.23768-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 9835666db5a7..0e052e253835 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1916,7 +1916,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C \"$1\" 2>/dev/null|grep -v \"$1:\"|expand", + " -l -d %s %s -C \"$1\" 2>/dev/null|expand", opts->objdump_path ?: "objdump", opts->disassembler_style ? "-M " : "", opts->disassembler_style ?: "", @@ -1962,9 +1962,16 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) nline = 0; while (!feof(file)) { + const char *match; + if (getline(&line, &line_len, file) < 0 || !line) break; + /* Skip lines containing "filename:" */ + match = strstr(line, symfs_filename); + if (match && match[strlen(symfs_filename)] == ':') + continue; + /* * The source code line number (lineno) needs to be kept in * across calls to symbol__parse_objdump_line(), so that it From b34b45eef16d814a93a52f2e76db803bb38939a0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Oct 2019 11:36:48 -0700 Subject: [PATCH 10/57] perf annotate: Don't pipe objdump output through 'expand' command Avoiding a pipe allows objdump command failures to surface. Move to the caller of symbol__parse_objdump_line the call to strim that removes leading and trailing tabs. Add a new expand_tabs function that if a tab is present allocate a new line in which tabs are expanded. In symbol__parse_objdump_line the line had no leading spaces, so simplify the line_ip processing. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Jin Yao Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191010183649.23768-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 95 ++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 0e052e253835..efc5bfef790a 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1492,35 +1492,24 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start */ static int symbol__parse_objdump_line(struct symbol *sym, struct annotate_args *args, - char *line, int *line_nr) + char *parsed_line, int *line_nr) { struct map *map = args->ms.map; struct annotation *notes = symbol__annotation(sym); struct disasm_line *dl; - char *parsed_line, *tmp, *tmp2; + char *tmp; s64 line_ip, offset = -1; regmatch_t match[2]; - line_ip = -1; - parsed_line = strim(line); - /* /filename:linenr ? Save line number and ignore. */ if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) { *line_nr = atoi(parsed_line + match[1].rm_so); return 0; } - tmp = skip_spaces(parsed_line); - if (*tmp) { - /* - * Parse hexa addresses followed by ':' - */ - line_ip = strtoull(tmp, &tmp2, 16); - if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') - line_ip = -1; - } - - if (line_ip != -1) { + /* Process hex address followed by ':'. */ + line_ip = strtoull(parsed_line, &tmp, 16); + if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') { u64 start = map__rip_2objdump(map, sym->start), end = map__rip_2objdump(map, sym->end); @@ -1528,7 +1517,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, if ((u64)line_ip < start || (u64)line_ip >= end) offset = -1; else - parsed_line = tmp2 + 1; + parsed_line = tmp + 1; } args->offset = offset; @@ -1854,6 +1843,67 @@ static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, } #endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +/* + * Possibly create a new version of line with tabs expanded. Returns the + * existing or new line, storage is updated if a new line is allocated. If + * allocation fails then NULL is returned. + */ +static char *expand_tabs(char *line, char **storage, size_t *storage_len) +{ + size_t i, src, dst, len, new_storage_len, num_tabs; + char *new_line; + size_t line_len = strlen(line); + + for (num_tabs = 0, i = 0; i < line_len; i++) + if (line[i] == '\t') + num_tabs++; + + if (num_tabs == 0) + return line; + + /* + * Space for the line and '\0', less the leading and trailing + * spaces. Each tab may introduce 7 additional spaces. + */ + new_storage_len = line_len + 1 + (num_tabs * 7); + + new_line = malloc(new_storage_len); + if (new_line == NULL) { + pr_err("Failure allocating memory for tab expansion\n"); + return NULL; + } + + /* + * Copy regions starting at src and expand tabs. If there are two + * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces + * are inserted. + */ + for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) { + if (line[i] == '\t') { + len = i - src; + memcpy(&new_line[dst], &line[src], len); + dst += len; + new_line[dst++] = ' '; + while (dst % 8 != 0) + new_line[dst++] = ' '; + src = i + 1; + num_tabs--; + } + } + + /* Expand the last region. */ + len = line_len + 1 - src; + memcpy(&new_line[dst], &line[src], len); + dst += len; + new_line[dst] = '\0'; + + free(*storage); + *storage = new_line; + *storage_len = new_storage_len; + return new_line; + +} + static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) { struct annotation_options *opts = args->options; @@ -1916,7 +1966,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C \"$1\" 2>/dev/null|expand", + " -l -d %s %s -C \"$1\" 2>/dev/null", opts->objdump_path ?: "objdump", opts->disassembler_style ? "-M " : "", opts->disassembler_style ?: "", @@ -1963,6 +2013,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) nline = 0; while (!feof(file)) { const char *match; + char *expanded_line; if (getline(&line, &line_len, file) < 0 || !line) break; @@ -1972,13 +2023,19 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) if (match && match[strlen(symfs_filename)] == ':') continue; + expanded_line = strim(line); + expanded_line = expand_tabs(expanded_line, &line, &line_len); + if (!expanded_line) + break; + /* * The source code line number (lineno) needs to be kept in * across calls to symbol__parse_objdump_line(), so that it * can associate it with the instructions till the next one. * See disasm_line__new() and struct disasm_line::line_nr. */ - if (symbol__parse_objdump_line(sym, args, line, &lineno) < 0) + if (symbol__parse_objdump_line(sym, args, expanded_line, + &lineno) < 0) break; nline++; } From c5baf9089246c1356705c9ba36d767ee8ce43dd2 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 10 Oct 2019 11:36:49 -0700 Subject: [PATCH 11/57] perf annotate: Fix objdump --no-show-raw-insn flag Remove redirection of objdump's stderr to /dev/null to help diagnose failures. Fix the '--no-show-raw' flag to be '--no-show-raw-insn' which binutils is permissive and allows, but fails with LLVM objdump. Signed-off-by: Ian Rogers Cc: Alexander Shishkin Cc: Jin Yao Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191010183649.23768-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index efc5bfef790a..eef8aa87db66 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1966,13 +1966,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C \"$1\" 2>/dev/null", + " -l -d %s %s -C \"$1\"", opts->objdump_path ?: "objdump", opts->disassembler_style ? "-M " : "", opts->disassembler_style ?: "", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), - opts->show_asm_raw ? "" : "--no-show-raw", + opts->show_asm_raw ? "" : "--no-show-raw-insn", opts->annotate_src ? "-S" : ""); if (err < 0) { From 5fb470bc29d8e2ff0e1cab4fbb580a06da11ab28 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Tue, 8 Oct 2019 11:38:41 +0200 Subject: [PATCH 12/57] perf jvmti: Link against tools/lib/ctype.h to have weak strlcpy() The build of file libperf-jvmti.so succeeds but the resulting object fails to load: # ~/linux/tools/perf/perf record -k mono -- java \ -XX:+PreserveFramePointer \ -agentpath:/root/linux/tools/perf/libperf-jvmti.so \ hog 100000 123450 Error occurred during initialization of VM Could not find agent library /root/linux/tools/perf/libperf-jvmti.so in absolute path, with error: /root/linux/tools/perf/libperf-jvmti.so: undefined symbol: _ctype Add the missing _ctype symbol into the build script. Fixes: 79743bc927f6 ("perf jvmti: Link against tools/lib/string.o to have weak strlcpy()") Signed-off-by: Thomas Richter Cc: Heiko Carstens Cc: Vasily Gorbik Link: http://lore.kernel.org/lkml/20191008093841.59387-1-tmricht@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/jvmti/Build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/jvmti/Build b/tools/perf/jvmti/Build index 1e148bbdf820..202cadaaf097 100644 --- a/tools/perf/jvmti/Build +++ b/tools/perf/jvmti/Build @@ -2,7 +2,7 @@ jvmti-y += libjvmti.o jvmti-y += jvmti_agent.o # For strlcpy -jvmti-y += libstring.o +jvmti-y += libstring.o libctype.o CFLAGS_jvmti = -fPIC -DPIC -I$(JDIR)/include -I$(JDIR)/include/linux CFLAGS_REMOVE_jvmti = -Wmissing-declarations @@ -15,3 +15,7 @@ CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PE $(OUTPUT)jvmti/libstring.o: ../lib/string.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) + +$(OUTPUT)jvmti/libctype.o: ../lib/ctype.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) From dd071024bf52156eed31deaf511c6e7a82a6f57b Mon Sep 17 00:00:00 2001 From: Jin Yao Date: Fri, 11 Oct 2019 13:05:45 +0800 Subject: [PATCH 13/57] perf stat: Support --all-kernel/--all-user 'perf record' has supported --all-kernel / --all-user to configure all used events to run in kernel space or run in user space. But 'perf stat' doesn't support these options. It would be useful to support these options in 'perf stat' too to keep the same semantics available in both tools. Signed-off-by: Jin Yao Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Kan Liang Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191011050545.3899-1-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 6 ++++++ tools/perf/builtin-stat.c | 6 ++++++ tools/perf/util/stat.c | 10 ++++++++++ tools/perf/util/stat.h | 2 ++ 4 files changed, 24 insertions(+) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 930c51c01201..a9af4e440e80 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -323,6 +323,12 @@ The output is SMI cycles%, equals to (aperf - unhalted core cycles) / aperf Users who wants to get the actual value can apply --no-metric-only. +--all-kernel:: +Configure all used events to run in kernel space. + +--all-user:: +Configure all used events to run in user space. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 468fc49420ce..c88d4e118409 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -803,6 +803,12 @@ static struct option stat_options[] = { OPT_CALLBACK('M', "metrics", &evsel_list, "metric/metric group list", "monitor specified metrics or metric groups (separated by ,)", parse_metric_groups), + OPT_BOOLEAN_FLAG(0, "all-kernel", &stat_config.all_kernel, + "Configure all used events to run in kernel space.", + PARSE_OPT_EXCLUSIVE), + OPT_BOOLEAN_FLAG(0, "all-user", &stat_config.all_user, + "Configure all used events to run in user space.", + PARSE_OPT_EXCLUSIVE), OPT_END() }; diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index ebdd130557fb..6822e4ffe224 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -490,6 +490,16 @@ int create_perf_stat_counter(struct evsel *evsel, if (config->identifier) attr->sample_type = PERF_SAMPLE_IDENTIFIER; + if (config->all_user) { + attr->exclude_kernel = 1; + attr->exclude_user = 0; + } + + if (config->all_kernel) { + attr->exclude_kernel = 0; + attr->exclude_user = 1; + } + /* * Disabling all counters initially, they will be enabled * either manually by us or by kernel via enable_on_exec diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index edbeb2f63e8d..081c4a5113c6 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -106,6 +106,8 @@ struct perf_stat_config { bool big_num; bool no_merge; bool walltime_run_table; + bool all_kernel; + bool all_user; FILE *output; unsigned int interval; unsigned int timeout; From 8eded45fcd3420e0f08b1e7869781f0e12927637 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2019 14:46:40 -0300 Subject: [PATCH 14/57] perf trace: Add syscall failure stats to -s/--summary and -S/--with-summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like strace has: # trace -s sleep 1 Summary of events: sleep (32370), 80 events, 93.0% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ nanosleep 1 0 1000.402 1000.402 1000.402 1000.402 0.00% mmap 8 0 0.023 0.002 0.003 0.004 8.49% close 5 0 0.015 0.001 0.003 0.009 51.39% mprotect 4 0 0.014 0.002 0.003 0.005 16.95% openat 3 0 0.013 0.003 0.004 0.005 14.29% munmap 1 0 0.010 0.010 0.010 0.010 0.00% read 4 0 0.005 0.001 0.001 0.002 16.83% brk 4 0 0.004 0.001 0.001 0.002 20.82% access 1 1 0.004 0.004 0.004 0.004 0.00% fstat 3 0 0.003 0.001 0.001 0.001 12.17% lseek 3 0 0.003 0.001 0.001 0.001 11.45% arch_prctl 2 1 0.002 0.001 0.001 0.001 2.30% execve 1 0 0.000 0.000 0.000 0.000 0.00% # # perf trace -S sleep 1 ? ... [continued]: execve()) = 0 0.028 brk(brk: NULL) = 0x559f5bd96000 0.033 arch_prctl(option: 0x3001, arg2: 0x7ffda8b715a0) = -1 EINVAL (Invalid argument) 0.046 access(filename: "/etc/ld.so.preload", mode: R) = -1 ENOENT (No such file or directory) 0.055 openat(dfd: CWD, filename: "/etc/ld.so.cache", flags: RDONLY|CLOEXEC) = 3 0.060 fstat(fd: 3, statbuf: 0x7ffda8b707a0) = 0 0.062 mmap(addr: NULL, len: 134346, prot: READ, flags: PRIVATE, fd: 3, off: 0) = 0x7f3aedfc4000 0.066 close(fd: 3) = 0 0.079 openat(dfd: CWD, filename: "/lib64/libc.so.6", flags: RDONLY|CLOEXEC) = 3 0.085 read(fd: 3, buf: 0x7ffda8b70948, count: 832) = 832 0.088 lseek(fd: 3, offset: 792, whence: SET) = 792 0.090 read(fd: 3, buf: 0x7ffda8b70810, count: 68) = 68 0.093 fstat(fd: 3, statbuf: 0x7ffda8b707f0) = 0 0.095 mmap(addr: NULL, len: 8192, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS) = 0x7f3aedfc2000 0.101 lseek(fd: 3, offset: 792, whence: SET) = 792 0.103 read(fd: 3, buf: 0x7ffda8b70450, count: 68) = 68 0.105 lseek(fd: 3, offset: 864, whence: SET) = 864 0.107 read(fd: 3, buf: 0x7ffda8b70470, count: 32) = 32 0.110 mmap(addr: NULL, len: 1857472, prot: READ, flags: PRIVATE|DENYWRITE, fd: 3, off: 0) = 0x7f3aeddfc000 0.114 mprotect(start: 0x7f3aede1e000, len: 1679360, prot: NONE) = 0 0.121 mmap(addr: 0x7f3aede1e000, len: 1363968, prot: READ|EXEC, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x22000) = 0x7f3aede1e000 0.127 mmap(addr: 0x7f3aedf6b000, len: 311296, prot: READ, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x16f000) = 0x7f3aedf6b000 0.131 mmap(addr: 0x7f3aedfb8000, len: 24576, prot: READ|WRITE, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x1bb000) = 0x7f3aedfb8000 0.138 mmap(addr: 0x7f3aedfbe000, len: 14272, prot: READ|WRITE, flags: PRIVATE|FIXED|ANONYMOUS) = 0x7f3aedfbe000 0.147 close(fd: 3) = 0 0.158 arch_prctl(option: SET_FS, arg2: 0x7f3aedfc3580) = 0 0.210 mprotect(start: 0x7f3aedfb8000, len: 16384, prot: READ) = 0 0.230 mprotect(start: 0x559f5b27d000, len: 4096, prot: READ) = 0 0.236 mprotect(start: 0x7f3aee00f000, len: 4096, prot: READ) = 0 0.240 munmap(addr: 0x7f3aedfc4000, len: 134346) = 0 0.300 brk(brk: NULL) = 0x559f5bd96000 0.302 brk(brk: 0x559f5bdb7000) = 0x559f5bdb7000 0.305 brk(brk: NULL) = 0x559f5bdb7000 0.310 openat(dfd: CWD, filename: "/usr/lib/locale/locale-archive", flags: RDONLY|CLOEXEC) = 3 0.315 fstat(fd: 3, statbuf: 0x7f3aedfbdac0) = 0 0.318 mmap(addr: NULL, len: 217750512, prot: READ, flags: PRIVATE, fd: 3, off: 0) = 0x7f3ae0e52000 0.325 close(fd: 3) = 0 0.358 nanosleep(rqtp: 0x7ffda8b714b0, rmtp: NULL) = 0 1000.622 close(fd: 1) = 0 1000.641 close(fd: 2) = 0 1000.664 exit_group(error_code: 0) = ? Summary of events: sleep (722), 80 events, 93.0% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ nanosleep 1 0 1000.194 1000.194 1000.194 1000.194 0.00% mmap 8 0 0.025 0.002 0.003 0.005 10.17% close 5 0 0.018 0.001 0.004 0.010 50.18% mprotect 4 0 0.016 0.003 0.004 0.006 16.81% openat 3 0 0.011 0.003 0.004 0.004 6.57% munmap 1 0 0.010 0.010 0.010 0.010 0.00% brk 4 0 0.005 0.001 0.001 0.002 20.72% read 4 0 0.005 0.001 0.001 0.002 16.71% access 1 1 0.005 0.005 0.005 0.005 0.00% fstat 3 0 0.004 0.001 0.001 0.002 14.82% lseek 3 0 0.003 0.001 0.001 0.001 11.66% arch_prctl 2 1 0.002 0.001 0.001 0.001 3.59% execve 1 0 0.000 0.000 0.000 0.000 0.00% # Works for system wide, e.g. for 1ms: # perf trace -s -a sleep 0.001 Summary of events: sleep (768), 94 events, 37.9% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ nanosleep 1 0 1.133 1.133 1.133 1.133 0.00% execve 7 6 0.351 0.003 0.050 0.316 88.53% mmap 8 0 0.024 0.002 0.003 0.004 8.86% mprotect 4 0 0.017 0.003 0.004 0.006 16.02% openat 3 0 0.013 0.004 0.004 0.005 8.34% munmap 1 0 0.010 0.010 0.010 0.010 0.00% brk 4 0 0.007 0.001 0.002 0.002 10.99% close 5 0 0.005 0.001 0.001 0.002 11.69% read 5 0 0.005 0.000 0.001 0.002 30.53% access 1 1 0.004 0.004 0.004 0.004 0.00% fstat 3 0 0.004 0.001 0.001 0.002 10.74% lseek 3 0 0.003 0.001 0.001 0.001 10.20% arch_prctl 2 1 0.002 0.001 0.001 0.001 3.34% Web Content (21258), 46 events, 18.5% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ recvmsg 12 12 0.015 0.001 0.001 0.002 8.50% futex 2 0 0.008 0.003 0.004 0.005 27.08% poll 6 0 0.006 0.000 0.001 0.002 22.14% read 2 0 0.006 0.002 0.003 0.003 26.08% write 1 0 0.002 0.002 0.002 0.002 0.00% Web Content (4365), 36 events, 14.5% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ recvmsg 10 10 0.015 0.001 0.002 0.003 11.83% poll 5 0 0.006 0.000 0.001 0.002 28.44% futex 2 0 0.005 0.001 0.003 0.004 48.29% read 1 0 0.003 0.003 0.003 0.003 0.00% Timer (21275), 14 events, 5.6% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ futex 6 1 0.240 0.000 0.040 0.149 64.58% write 1 0 0.008 0.008 0.008 0.008 0.00% Timer (4383), 14 events, 5.6% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ futex 6 2 0.186 0.000 0.031 0.181 96.45% write 1 0 0.010 0.010 0.010 0.010 0.00% Web Content (20354), 28 events, 11.3% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ recvmsg 8 8 0.010 0.001 0.001 0.002 15.24% poll 4 0 0.004 0.000 0.001 0.002 35.68% futex 1 0 0.003 0.003 0.003 0.003 0.00% read 1 0 0.003 0.003 0.003 0.003 0.00% Timer (20371), 10 events, 4.0% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ futex 4 1 0.077 0.000 0.019 0.075 95.46% write 1 0 0.005 0.005 0.005 0.005 0.00% [root@quaco ~]# Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-k7kh2muo5oeg56yx446hnw9v@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 58 ++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 144d417ddb22..56f2d72104a5 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1958,11 +1958,16 @@ out_cant_read: return NULL; } +struct syscall_stats { + struct stats stats; + u64 nr_failures; +}; + static void thread__update_stats(struct thread_trace *ttrace, - int id, struct perf_sample *sample) + int id, struct perf_sample *sample, long err) { struct int_node *inode; - struct stats *stats; + struct syscall_stats *stats; u64 duration = 0; inode = intlist__findnew(ttrace->syscall_stats, id); @@ -1971,17 +1976,22 @@ static void thread__update_stats(struct thread_trace *ttrace, stats = inode->priv; if (stats == NULL) { - stats = malloc(sizeof(struct stats)); + stats = malloc(sizeof(*stats)); if (stats == NULL) return; - init_stats(stats); + + stats->nr_failures = 0; + init_stats(&stats->stats); inode->priv = stats; } if (ttrace->entry_time && sample->time > ttrace->entry_time) duration = sample->time - ttrace->entry_time; - update_stats(stats, duration); + update_stats(&stats->stats, duration); + + if (err < 0) + ++stats->nr_failures; } static int trace__printf_interrupted_entry(struct trace *trace) @@ -2226,11 +2236,11 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, trace__fprintf_sample(trace, evsel, sample, thread); - if (trace->summary) - thread__update_stats(ttrace, id, sample); - ret = perf_evsel__sc_tp_uint(evsel, ret, sample); + if (trace->summary) + thread__update_stats(ttrace, id, sample, ret); + if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) { trace__set_fd_pathname(thread, ret, ttrace->filename.name); ttrace->filename.pending_open = false; @@ -4016,17 +4026,17 @@ static size_t trace__fprintf_threads_header(FILE *fp) } DEFINE_RESORT_RB(syscall_stats, a->msecs > b->msecs, - struct stats *stats; - double msecs; - int syscall; + struct syscall_stats *stats; + double msecs; + int syscall; ) { struct int_node *source = rb_entry(nd, struct int_node, rb_node); - struct stats *stats = source->priv; + struct syscall_stats *stats = source->priv; entry->syscall = source->i; entry->stats = stats; - entry->msecs = stats ? (u64)stats->n * (avg_stats(stats) / NSEC_PER_MSEC) : 0; + entry->msecs = stats ? (u64)stats->stats.n * (avg_stats(&stats->stats) / NSEC_PER_MSEC) : 0; } static size_t thread__dump_stats(struct thread_trace *ttrace, @@ -4042,26 +4052,26 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, printed += fprintf(fp, "\n"); - printed += fprintf(fp, " syscall calls total min avg max stddev\n"); - printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); - printed += fprintf(fp, " --------------- -------- --------- --------- --------- --------- ------\n"); + printed += fprintf(fp, " syscall calls errors total min avg max stddev\n"); + printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); + printed += fprintf(fp, " --------------- -------- ------ -------- --------- --------- --------- ------\n"); resort_rb__for_each_entry(nd, syscall_stats) { - struct stats *stats = syscall_stats_entry->stats; + struct syscall_stats *stats = syscall_stats_entry->stats; if (stats) { - double min = (double)(stats->min) / NSEC_PER_MSEC; - double max = (double)(stats->max) / NSEC_PER_MSEC; - double avg = avg_stats(stats); + double min = (double)(stats->stats.min) / NSEC_PER_MSEC; + double max = (double)(stats->stats.max) / NSEC_PER_MSEC; + double avg = avg_stats(&stats->stats); double pct; - u64 n = (u64) stats->n; + u64 n = (u64)stats->stats.n; - pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; + pct = avg ? 100.0 * stddev_stats(&stats->stats) / avg : 0.0; avg /= NSEC_PER_MSEC; sc = &trace->syscalls.table[syscall_stats_entry->syscall]; printed += fprintf(fp, " %-15s", sc->name); - printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f %9.3f", - n, syscall_stats_entry->msecs, min, avg); + printed += fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", + n, stats->nr_failures, syscall_stats_entry->msecs, min, avg); printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); } } From b88b14db21dbcd04a5d262b96613bfdd005341a7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2019 15:32:40 -0300 Subject: [PATCH 15/57] perf trace: Introduce --errno-summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be used with -S or -s, using just this new option implies -s, examples: # perf trace --errno-summary sleep 1 Summary of events: sleep (10793), 80 events, 93.0% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ nanosleep 1 0 1000.427 1000.427 1000.427 1000.427 0.00% mmap 8 0 0.026 0.002 0.003 0.005 9.18% close 5 0 0.018 0.001 0.004 0.009 48.97% mprotect 4 0 0.017 0.003 0.004 0.006 16.49% openat 3 0 0.012 0.003 0.004 0.005 9.41% munmap 1 0 0.010 0.010 0.010 0.010 0.00% brk 4 0 0.005 0.001 0.001 0.002 22.77% read 4 0 0.005 0.001 0.001 0.002 22.33% access 1 1 0.004 0.004 0.004 0.004 0.00% ENOENT: 1 fstat 3 0 0.004 0.001 0.001 0.002 17.18% lseek 3 0 0.003 0.001 0.001 0.001 11.62% arch_prctl 2 1 0.002 0.001 0.001 0.001 3.32% EINVAL: 1 execve 1 0 0.000 0.000 0.000 0.000 0.00% # Works as well together with --failure and -S, i.e. collect the stats and show just the syscalls that failed: # perf trace --failure -S --errno-summary sleep 1 0.032 arch_prctl(option: 0x3001, arg2: 0x7fffdb11b580) = -1 EINVAL (Invalid argument) 0.045 access(filename: "/etc/ld.so.preload", mode: R) = -1 ENOENT (No such file or directory) Summary of events: sleep (10806), 80 events, 93.0% syscall calls errors total min avg max stddev (msec) (msec) (msec) (msec) (%) --------------- -------- ------ -------- --------- --------- --------- ------ nanosleep 1 0 1000.094 1000.094 1000.094 1000.094 0.00% mmap 8 0 0.026 0.002 0.003 0.005 9.06% close 5 0 0.018 0.001 0.004 0.010 49.58% mprotect 4 0 0.017 0.003 0.004 0.006 17.56% openat 3 0 0.014 0.004 0.005 0.006 12.29% munmap 1 0 0.010 0.010 0.010 0.010 0.00% brk 4 0 0.005 0.001 0.001 0.002 22.75% read 4 0 0.005 0.001 0.001 0.002 17.19% access 1 1 0.005 0.005 0.005 0.005 0.00% ENOENT: 1 fstat 3 0 0.004 0.001 0.001 0.002 21.66% lseek 3 0 0.003 0.001 0.001 0.001 11.71% arch_prctl 2 1 0.002 0.001 0.001 0.001 2.66% EINVAL: 1 execve 1 0 0.000 0.000 0.000 0.000 0.00% # Suggested-by: Andi Kleen Cc: Adrian Hunter Cc: Brendan Gregg Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-l0mjwczkpouov7lss5zn8d9h@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-trace.txt | 4 ++ tools/perf/builtin-trace.c | 51 +++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3bb89c2e9020..abc9b5d83312 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -146,6 +146,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. Show all syscalls followed by a summary by thread with min, max, and average times (in msec) and relative stddev. +--errno-summary:: + To be used with -s or -S, to show stats for the errnos experienced by + syscalls, using only this option will trigger --summary. + --tool_stats:: Show tool stats such as number of times fd->pathname was discovered thru hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc. diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 56f2d72104a5..467e18e6f8ec 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -175,6 +175,7 @@ struct trace { bool multiple_threads; bool summary; bool summary_only; + bool errno_summary; bool failure_only; bool show_comm; bool print_sample; @@ -1961,10 +1962,12 @@ out_cant_read: struct syscall_stats { struct stats stats; u64 nr_failures; + int max_errno; + u32 *errnos; }; -static void thread__update_stats(struct thread_trace *ttrace, - int id, struct perf_sample *sample, long err) +static void thread__update_stats(struct thread *thread, struct thread_trace *ttrace, + int id, struct perf_sample *sample, long err, bool errno_summary) { struct int_node *inode; struct syscall_stats *stats; @@ -1981,6 +1984,8 @@ static void thread__update_stats(struct thread_trace *ttrace, return; stats->nr_failures = 0; + stats->max_errno = 0; + stats->errnos = NULL; init_stats(&stats->stats); inode->priv = stats; } @@ -1990,8 +1995,30 @@ static void thread__update_stats(struct thread_trace *ttrace, update_stats(&stats->stats, duration); - if (err < 0) + if (err < 0) { ++stats->nr_failures; + + if (!errno_summary) + return; + + err = -err; + if (err > stats->max_errno) { + u32 *new_errnos = realloc(stats->errnos, err * sizeof(u32)); + + if (new_errnos) { + memset(new_errnos + stats->max_errno, 0, (err - stats->max_errno) * sizeof(u32)); + } else { + pr_debug("Not enough memory for errno stats for thread \"%s\"(%d/%d), results will be incomplete\n", + thread__comm_str(thread), thread->pid_, thread->tid); + return; + } + + stats->errnos = new_errnos; + stats->max_errno = err; + } + + ++stats->errnos[err - 1]; + } } static int trace__printf_interrupted_entry(struct trace *trace) @@ -2239,7 +2266,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel, ret = perf_evsel__sc_tp_uint(evsel, ret, sample); if (trace->summary) - thread__update_stats(ttrace, id, sample, ret); + thread__update_stats(thread, ttrace, id, sample, ret, trace->errno_summary); if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) { trace__set_fd_pathname(thread, ret, ttrace->filename.name); @@ -4073,6 +4100,16 @@ static size_t thread__dump_stats(struct thread_trace *ttrace, printed += fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", n, stats->nr_failures, syscall_stats_entry->msecs, min, avg); printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); + + if (trace->errno_summary && stats->nr_failures) { + const char *arch_name = perf_env__arch(trace->host->env); + int e; + + for (e = 0; e < stats->max_errno; ++e) { + if (stats->errnos[e] != 0) + fprintf(fp, "\t\t\t\t%s: %d\n", arch_syscalls__strerrno(arch_name, e + 1), stats->errnos[e]); + } + } } } @@ -4511,6 +4548,8 @@ int cmd_trace(int argc, const char **argv) "Show only syscall summary with statistics"), OPT_BOOLEAN('S', "with-summary", &trace.summary, "Show all syscalls and summary with statistics"), + OPT_BOOLEAN(0, "errno-summary", &trace.errno_summary, + "Show errno stats per syscall, use with -s or -S"), OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min", "Trace pagefaults", parse_pagefaults, "maj"), OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), @@ -4816,6 +4855,10 @@ init_augmented_syscall_tp: if ((argc >= 1) && (strcmp(argv[0], "record") == 0)) return trace__record(&trace, argc-1, &argv[1]); + /* Using just --errno-summary will trigger --summary */ + if (trace.errno_summary && !trace.summary && !trace.summary_only) + trace.summary_only = true; + /* summary_only implies summary option, but don't overwrite summary if set */ if (trace.summary_only) trace.summary = trace.summary_only; From da949f507a73df5b5ad5031cb23b82a4d81846eb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2019 20:10:50 -0300 Subject: [PATCH 16/57] perf string: Export asprintf__tp_filter_pids() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will be used directly in 'perf trace' for setting up the command line argv array to pass to cmd_record, as this was how 'perf trace record' was implemented, following the model used in 'perf kvm record', 'perf sched record', etc. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-w3cuwjs63lxf5zpryy3145uv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 3 ++- tools/perf/util/string2.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 8793b4e322b0..0f9cd703e725 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -21,6 +21,7 @@ #include "../perf.h" #include "asm/bug.h" #include "bpf-event.h" +#include "util/string2.h" #include #include #include @@ -959,7 +960,7 @@ int perf_evlist__append_tp_filter(struct evlist *evlist, const char *filter) return err; } -static char *asprintf__tp_filter_pids(size_t npids, pid_t *pids) +char *asprintf__tp_filter_pids(size_t npids, pid_t *pids) { char *filter; size_t i; diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h index 708805f5573e..73df616ced43 100644 --- a/tools/perf/util/string2.h +++ b/tools/perf/util/string2.h @@ -4,6 +4,7 @@ #include #include +#include // pid_t #include #include @@ -32,6 +33,8 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int return asprintf_expr_inout_ints(var, false, nints, ints); } +char *asprintf__tp_filter_pids(size_t npids, pid_t *pids); + char *strpbrk_esc(char *str, const char *stopset); char *strdup_esc(const char *str); From 7fbfe22cf4cfe01a88704dd76ca65d108039d297 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 14 Oct 2019 20:13:51 -0300 Subject: [PATCH 17/57] perf trace: Filter own pid to avoid a feedback look in 'perf trace record -a' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing a system wide 'perf trace record' we need, just like in 'perf trace' live mode, to filter out perf trace's own pid, so set up a tracepoint filter for the raw_syscalls tracepoints right after adding them to the argv array that is set up to then call cmd_record(). Reported-by: Andi Kleen Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-uysx5w8f2y5ndoln5cq370tv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 467e18e6f8ec..cdee22dac2b3 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2796,21 +2796,23 @@ static int trace__record(struct trace *trace, int argc, const char **argv) "-m", "1024", "-c", "1", }; - + pid_t pid = getpid(); + char *filter = asprintf__tp_filter_pids(1, &pid); const char * const sc_args[] = { "-e", }; unsigned int sc_args_nr = ARRAY_SIZE(sc_args); const char * const majpf_args[] = { "-e", "major-faults" }; unsigned int majpf_args_nr = ARRAY_SIZE(majpf_args); const char * const minpf_args[] = { "-e", "minor-faults" }; unsigned int minpf_args_nr = ARRAY_SIZE(minpf_args); + int err = -1; - /* +1 is for the event string below */ - rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 1 + + /* +3 is for the event string below and the pid filter */ + rec_argc = ARRAY_SIZE(record_args) + sc_args_nr + 3 + majpf_args_nr + minpf_args_nr + argc; rec_argv = calloc(rec_argc + 1, sizeof(char *)); - if (rec_argv == NULL) - return -ENOMEM; + if (rec_argv == NULL || filter == NULL) + goto out_free; j = 0; for (i = 0; i < ARRAY_SIZE(record_args); i++) @@ -2827,11 +2829,13 @@ static int trace__record(struct trace *trace, int argc, const char **argv) rec_argv[j++] = "syscalls:sys_enter,syscalls:sys_exit"; else { pr_err("Neither raw_syscalls nor syscalls events exist.\n"); - free(rec_argv); - return -1; + goto out_free; } } + rec_argv[j++] = "--filter"; + rec_argv[j++] = filter; + if (trace->trace_pgfaults & TRACE_PFMAJ) for (i = 0; i < majpf_args_nr; i++) rec_argv[j++] = majpf_args[i]; @@ -2843,7 +2847,11 @@ static int trace__record(struct trace *trace, int argc, const char **argv) for (i = 0; i < (unsigned int)argc; i++) rec_argv[j++] = argv[i]; - return cmd_record(j, rec_argv); + err = cmd_record(j, rec_argv); +out_free: + free(filter); + free(rec_argv); + return err; } static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); From c5e006cdbd278a8a5185a3a56acdba161cf159ea Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 08:26:47 -0300 Subject: [PATCH 18/57] perf trace: Support tracepoint dynamic char arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Things like: # grep __data_loc /sys/kernel/debug/tracing/events/sched/sched_process_exec/format field:__data_loc char[] filename; offset:8; size:4; signed:1; # That, at that offset (8) and with that size(8) have an integer that contains the real length and offset for the contents of that array. Now this works: # perf trace --max-events 1 -e sched:*exec -a 0.000 sed/19441 sched:sched_process_exec(filename: "/usr/bin/sync", pid: 19441 (sync), old_pid: 19441 (sync)) # As when using the libtraceevent based beautifier: # perf trace --libtraceevent --max-events 1 -e sched:*exec -a 0.000 sync/19463 sched:sched_process_exec(filename=/usr/bin/sync pid=19463 old_pid=19463) # I.e. that 'filename' is implemented as a dynamic char array. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-950p0m842fe6n7sxsdwqj5i2@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 19 ++++++++++++++----- tools/perf/trace/beauty/beauty.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index cdee22dac2b3..907eaf316f5b 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -563,7 +563,7 @@ static size_t syscall_arg__scnprintf_char_array(char *bf, size_t size, struct sy // XXX Hey, maybe for sched:sched_switch prev/next comm fields we can // fill missing comms using thread__set_comm()... // here or in a special syscall_arg__scnprintf_pid_sched_tp... - return scnprintf(bf, size, "\"%-.*s\"", arg->fmt->nr_entries, arg->val); + return scnprintf(bf, size, "\"%-.*s\"", arg->fmt->nr_entries ?: arg->len, arg->val); } #define SCA_CHAR_ARRAY syscall_arg__scnprintf_char_array @@ -1559,7 +1559,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field arg->scnprintf = SCA_PID; else if (strcmp(field->type, "umode_t") == 0) arg->scnprintf = SCA_MODE_T; - else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstarts(field->type, "char")) { + else if ((field->flags & TEP_FIELD_IS_ARRAY) && strstr(field->type, "char")) { arg->scnprintf = SCA_CHAR_ARRAY; arg->nr_entries = field->arraylen; } else if ((strcmp(field->type, "int") == 0 || @@ -2523,10 +2523,19 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, if (syscall_arg.mask & bit) continue; + syscall_arg.len = 0; syscall_arg.fmt = arg; - if (field->flags & TEP_FIELD_IS_ARRAY) - val = (uintptr_t)(sample->raw_data + field->offset); - else + if (field->flags & TEP_FIELD_IS_ARRAY) { + int offset = field->offset; + + if (field->flags & TEP_FIELD_IS_DYNAMIC) { + offset = format_field__intval(field, sample, evsel->needs_swap); + syscall_arg.len = offset >> 16; + offset &= 0xffff; + } + + val = (uintptr_t)(sample->raw_data + offset); + } else val = format_field__intval(field, sample, evsel->needs_swap); /* * Some syscall args need some mask, most don't and diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 77ad80a399fd..0dee0cf4fda8 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -87,6 +87,7 @@ struct syscall_arg_fmt; /** * @val: value of syscall argument being formatted + * @len: for tracepoint dynamic arrays, if fmt->nr_entries == 0, then its not a fixed array, look at arg->len * @args: All the args, use syscall_args__val(arg, nth) to access one * @augmented_args: Extra data that can be collected, for instance, with eBPF for expanding the pathname for open, etc * @augmented_args_size: augmented_args total payload size @@ -109,6 +110,7 @@ struct syscall_arg { struct thread *thread; struct trace *trace; void *parm; + u16 len; u8 idx; u8 mask; bool show_string_prefix; From 84b0975f4853ba32d2d9b3c19ffa2b947f023fb3 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 4 Sep 2019 23:54:41 +0800 Subject: [PATCH 19/57] perf vendor events arm64: Fix Hisi hip08 DDRC PMU eventname The "EventName" for the DDRC precharge command event is incorrect, so fix it. Fixes: 57cc732479ba ("perf jevents: Add support for Hisi hip08 DDRC PMU aliasing") Signed-off-by: John Garry Reviewed-by: Shaokun Zhang Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linuxarm@huawei.com Link: http://lore.kernel.org/lkml/1567612484-195727-2-git-send-email-john.garry@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json index 0d1556fcdffe..99f4fc425564 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json @@ -15,7 +15,7 @@ }, { "EventCode": "0x04", - "EventName": "uncore_hisi_ddrc.flux_wr", + "EventName": "uncore_hisi_ddrc.pre_cmd", "BriefDescription": "DDRC precharge commands", "PublicDescription": "DDRC precharge commands", "Unit": "hisi_sccl,ddrc", From 1410732a1b642ba82730a95bdf7f4dafbac1891a Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 4 Sep 2019 23:54:42 +0800 Subject: [PATCH 20/57] perf vendor events arm64: Add some missing events for Hisi hip08 DDRC PMU Add some more missing events. Signed-off-by: John Garry Reviewed-by: Shaokun Zhang Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linuxarm@huawei.com Link: http://lore.kernel.org/lkml/1567612484-195727-3-git-send-email-john.garry@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/arm64/hisilicon/hip08/uncore-ddrc.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json index 99f4fc425564..7da86942dae2 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-ddrc.json @@ -1,4 +1,18 @@ [ + { + "EventCode": "0x00", + "EventName": "uncore_hisi_ddrc.flux_wr", + "BriefDescription": "DDRC total write operations", + "PublicDescription": "DDRC total write operations", + "Unit": "hisi_sccl,ddrc", + }, + { + "EventCode": "0x01", + "EventName": "uncore_hisi_ddrc.flux_rd", + "BriefDescription": "DDRC total read operations", + "PublicDescription": "DDRC total read operations", + "Unit": "hisi_sccl,ddrc", + }, { "EventCode": "0x02", "EventName": "uncore_hisi_ddrc.flux_wcmd", From e3ae569541802a6c9e89ab1f0f3ff613a5a1237b Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 4 Sep 2019 23:54:43 +0800 Subject: [PATCH 21/57] perf vendor events arm64: Add some missing events for Hisi hip08 L3C PMU Add some more missing events. Signed-off-by: John Garry Reviewed-by: Shaokun Zhang Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linuxarm@huawei.com Link: http://lore.kernel.org/lkml/1567612484-195727-4-git-send-email-john.garry@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arm64/hisilicon/hip08/uncore-l3c.json | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json index ca48747642e1..f463d0acfaef 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-l3c.json @@ -34,4 +34,60 @@ "PublicDescription": "l3c precharge commands", "Unit": "hisi_sccl,l3c", }, + { + "EventCode": "0x20", + "EventName": "uncore_hisi_l3c.rd_spipe", + "BriefDescription": "Count of the number of read lines that come from this cluster of CPU core in spipe", + "PublicDescription": "Count of the number of read lines that come from this cluster of CPU core in spipe", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x21", + "EventName": "uncore_hisi_l3c.wr_spipe", + "BriefDescription": "Count of the number of write lines that come from this cluster of CPU core in spipe", + "PublicDescription": "Count of the number of write lines that come from this cluster of CPU core in spipe", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x22", + "EventName": "uncore_hisi_l3c.rd_hit_spipe", + "BriefDescription": "Count of the number of read lines that hits in spipe of this L3C", + "PublicDescription": "Count of the number of read lines that hits in spipe of this L3C", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x23", + "EventName": "uncore_hisi_l3c.wr_hit_spipe", + "BriefDescription": "Count of the number of write lines that hits in spipe of this L3C", + "PublicDescription": "Count of the number of write lines that hits in spipe of this L3C", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x29", + "EventName": "uncore_hisi_l3c.back_invalid", + "BriefDescription": "Count of the number of L3C back invalid operations", + "PublicDescription": "Count of the number of L3C back invalid operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x40", + "EventName": "uncore_hisi_l3c.retry_cpu", + "BriefDescription": "Count of the number of retry that L3C suppresses the CPU operations", + "PublicDescription": "Count of the number of retry that L3C suppresses the CPU operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x41", + "EventName": "uncore_hisi_l3c.retry_ring", + "BriefDescription": "Count of the number of retry that L3C suppresses the ring operations", + "PublicDescription": "Count of the number of retry that L3C suppresses the ring operations", + "Unit": "hisi_sccl,l3c", + }, + { + "EventCode": "0x42", + "EventName": "uncore_hisi_l3c.prefetch_drop", + "BriefDescription": "Count of the number of prefetch drops from this L3C", + "PublicDescription": "Count of the number of prefetch drops from this L3C", + "Unit": "hisi_sccl,l3c", + }, ] From 2b7847158120136c4b23684c768a82d571ee59bf Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 4 Sep 2019 23:54:44 +0800 Subject: [PATCH 22/57] perf vendor events arm64: Add some missing events for Hisi hip08 HHA PMU Add some more missing events. A trivial typo is also fixed. Signed-off-by: John Garry Reviewed-by: Shaokun Zhang Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Cc: linuxarm@huawei.com Link: http://lore.kernel.org/lkml/1567612484-195727-5-git-send-email-john.garry@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arm64/hisilicon/hip08/uncore-hha.json | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json index 447d3064de90..3be418a248ea 100644 --- a/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json +++ b/tools/perf/pmu-events/arch/arm64/hisilicon/hip08/uncore-hha.json @@ -20,6 +20,13 @@ "PublicDescription": "The number of all operations received by the HHA from another SCCL in this socket", "Unit": "hisi_sccl,hha", }, + { + "EventCode": "0x03", + "EventName": "uncore_hisi_hha.rx_ccix", + "BriefDescription": "Count of the number of operations that HHA has received from CCIX", + "PublicDescription": "Count of the number of operations that HHA has received from CCIX", + "Unit": "hisi_sccl,hha", + }, { "EventCode": "0x1c", "EventName": "uncore_hisi_hha.rd_ddr_64b", @@ -29,7 +36,7 @@ }, { "EventCode": "0x1d", - "EventName": "uncore_hisi_hha.wr_dr_64b", + "EventName": "uncore_hisi_hha.wr_ddr_64b", "BriefDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes", "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 64 bytes", "Unit": "hisi_sccl,hha", @@ -48,4 +55,18 @@ "PublicDescription": "The number of write operations sent by HHA to DDRC which size is 128 bytes", "Unit": "hisi_sccl,hha", }, + { + "EventCode": "0x20", + "EventName": "uncore_hisi_hha.spill_num", + "BriefDescription": "Count of the number of spill operations that the HHA has sent", + "PublicDescription": "Count of the number of spill operations that the HHA has sent", + "Unit": "hisi_sccl,hha", + }, + { + "EventCode": "0x21", + "EventName": "uncore_hisi_hha.spill_success", + "BriefDescription": "Count of the number of successful spill operations that the HHA has sent", + "PublicDescription": "Count of the number of successful spill operations that the HHA has sent", + "Unit": "hisi_sccl,hha", + }, ] From d2b72b7280370aaddcbe93d00939bc7ec21dda48 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 15:40:23 -0300 Subject: [PATCH 23/57] tools arch x86: Grab a copy of the file containing the IRQ vector defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll use it to generate a table and then convert the irq_vectors:* tracepoint 'vector' arg in things like perf trace, script, etc. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-z7gi058lzhnrm32slevg3xod@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/arch/x86/include/asm/irq_vectors.h | 146 +++++++++++++++++++++++ tools/perf/check-headers.sh | 1 + 2 files changed, 147 insertions(+) create mode 100644 tools/arch/x86/include/asm/irq_vectors.h diff --git a/tools/arch/x86/include/asm/irq_vectors.h b/tools/arch/x86/include/asm/irq_vectors.h new file mode 100644 index 000000000000..889f8b1b5b7f --- /dev/null +++ b/tools/arch/x86/include/asm/irq_vectors.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_IRQ_VECTORS_H +#define _ASM_X86_IRQ_VECTORS_H + +#include +/* + * Linux IRQ vector layout. + * + * There are 256 IDT entries (per CPU - each entry is 8 bytes) which can + * be defined by Linux. They are used as a jump table by the CPU when a + * given vector is triggered - by a CPU-external, CPU-internal or + * software-triggered event. + * + * Linux sets the kernel code address each entry jumps to early during + * bootup, and never changes them. This is the general layout of the + * IDT entries: + * + * Vectors 0 ... 31 : system traps and exceptions - hardcoded events + * Vectors 32 ... 127 : device interrupts + * Vector 128 : legacy int80 syscall interface + * Vectors 129 ... LOCAL_TIMER_VECTOR-1 + * Vectors LOCAL_TIMER_VECTOR ... 255 : special interrupts + * + * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table. + * + * This file enumerates the exact layout of them: + */ + +#define NMI_VECTOR 0x02 +#define MCE_VECTOR 0x12 + +/* + * IDT vectors usable for external interrupt sources start at 0x20. + * (0x80 is the syscall vector, 0x30-0x3f are for ISA) + */ +#define FIRST_EXTERNAL_VECTOR 0x20 + +/* + * Reserve the lowest usable vector (and hence lowest priority) 0x20 for + * triggering cleanup after irq migration. 0x21-0x2f will still be used + * for device interrupts. + */ +#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR + +#define IA32_SYSCALL_VECTOR 0x80 + +/* + * Vectors 0x30-0x3f are used for ISA interrupts. + * round up to the next 16-vector boundary + */ +#define ISA_IRQ_VECTOR(irq) (((FIRST_EXTERNAL_VECTOR + 16) & ~15) + irq) + +/* + * Special IRQ vectors used by the SMP architecture, 0xf0-0xff + * + * some of the following vectors are 'rare', they are merged + * into a single vector (CALL_FUNCTION_VECTOR) to save vector space. + * TLB, reschedule and local APIC vectors are performance-critical. + */ + +#define SPURIOUS_APIC_VECTOR 0xff +/* + * Sanity check + */ +#if ((SPURIOUS_APIC_VECTOR & 0x0F) != 0x0F) +# error SPURIOUS_APIC_VECTOR definition error +#endif + +#define ERROR_APIC_VECTOR 0xfe +#define RESCHEDULE_VECTOR 0xfd +#define CALL_FUNCTION_VECTOR 0xfc +#define CALL_FUNCTION_SINGLE_VECTOR 0xfb +#define THERMAL_APIC_VECTOR 0xfa +#define THRESHOLD_APIC_VECTOR 0xf9 +#define REBOOT_VECTOR 0xf8 + +/* + * Generic system vector for platform specific use + */ +#define X86_PLATFORM_IPI_VECTOR 0xf7 + +/* + * IRQ work vector: + */ +#define IRQ_WORK_VECTOR 0xf6 + +#define UV_BAU_MESSAGE 0xf5 +#define DEFERRED_ERROR_VECTOR 0xf4 + +/* Vector on which hypervisor callbacks will be delivered */ +#define HYPERVISOR_CALLBACK_VECTOR 0xf3 + +/* Vector for KVM to deliver posted interrupt IPI */ +#ifdef CONFIG_HAVE_KVM +#define POSTED_INTR_VECTOR 0xf2 +#define POSTED_INTR_WAKEUP_VECTOR 0xf1 +#define POSTED_INTR_NESTED_VECTOR 0xf0 +#endif + +#define MANAGED_IRQ_SHUTDOWN_VECTOR 0xef + +#if IS_ENABLED(CONFIG_HYPERV) +#define HYPERV_REENLIGHTENMENT_VECTOR 0xee +#define HYPERV_STIMER0_VECTOR 0xed +#endif + +#define LOCAL_TIMER_VECTOR 0xec + +#define NR_VECTORS 256 + +#ifdef CONFIG_X86_LOCAL_APIC +#define FIRST_SYSTEM_VECTOR LOCAL_TIMER_VECTOR +#else +#define FIRST_SYSTEM_VECTOR NR_VECTORS +#endif + +/* + * Size the maximum number of interrupts. + * + * If the irq_desc[] array has a sparse layout, we can size things + * generously - it scales up linearly with the maximum number of CPUs, + * and the maximum number of IO-APICs, whichever is higher. + * + * In other cases we size more conservatively, to not create too large + * static arrays. + */ + +#define NR_IRQS_LEGACY 16 + +#define CPU_VECTOR_LIMIT (64 * NR_CPUS) +#define IO_APIC_VECTOR_LIMIT (32 * MAX_IO_APICS) + +#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_PCI_MSI) +#define NR_IRQS \ + (CPU_VECTOR_LIMIT > IO_APIC_VECTOR_LIMIT ? \ + (NR_VECTORS + CPU_VECTOR_LIMIT) : \ + (NR_VECTORS + IO_APIC_VECTOR_LIMIT)) +#elif defined(CONFIG_X86_IO_APIC) +#define NR_IRQS (NR_VECTORS + IO_APIC_VECTOR_LIMIT) +#elif defined(CONFIG_PCI_MSI) +#define NR_IRQS (NR_VECTORS + CPU_VECTOR_LIMIT) +#else +#define NR_IRQS NR_IRQS_LEGACY +#endif + +#endif /* _ASM_X86_IRQ_VECTORS_H */ diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 93c46d38024e..48290a0c917c 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -28,6 +28,7 @@ arch/x86/include/asm/disabled-features.h arch/x86/include/asm/required-features.h arch/x86/include/asm/cpufeatures.h arch/x86/include/asm/inat_types.h +arch/x86/include/asm/irq_vectors.h arch/x86/include/asm/msr-index.h arch/x86/include/uapi/asm/prctl.h arch/x86/lib/x86-opcode-map.txt From 5fa022aeba8420d4803e6198642d0a0cbbac99f3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 15:33:24 -0300 Subject: [PATCH 24/57] libbeauty: Add a generator for x86's IRQ vectors -> strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll wire this up with the 'vector' arg in irq_vectors:*, etc: Just run it straight away and check what it produces: $ tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh static const char *x86_irq_vectors[] = { [0x02] = "NMI", [0x12] = "MCE", [0x20] = "IRQ_MOVE_CLEANUP", [0x80] = "IA32_SYSCALL", [0xec] = "LOCAL_TIMER", [0xed] = "HYPERV_STIMER0", [0xee] = "HYPERV_REENLIGHTENMENT", [0xef] = "MANAGED_IRQ_SHUTDOWN", [0xf0] = "POSTED_INTR_NESTED", [0xf1] = "POSTED_INTR_WAKEUP", [0xf2] = "POSTED_INTR", [0xf3] = "HYPERVISOR_CALLBACK", [0xf4] = "DEFERRED_ERROR", [0xf6] = "IRQ_WORK", [0xf7] = "X86_PLATFORM_IPI", [0xf8] = "REBOOT", [0xf9] = "THRESHOLD_APIC", [0xfa] = "THERMAL_APIC", [0xfb] = "CALL_FUNCTION_SINGLE", [0xfc] = "CALL_FUNCTION", [0xfd] = "RESCHEDULE", [0xfe] = "ERROR_APIC", [0xff] = "SPURIOUS_APIC", }; $ Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-cpl1pa7kkwn0llufi5qw4li8@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- .../beauty/tracepoints/x86_irq_vectors.sh | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh diff --git a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh new file mode 100755 index 000000000000..f920003723b3 --- /dev/null +++ b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# SPDX-License-Identifier: LGPL-2.1 +# (C) 2019, Arnaldo Carvalho de Melo + +if [ $# -ne 1 ] ; then + arch_x86_header_dir=tools/arch/x86/include/asm/ +else + arch_x86_header_dir=$1 +fi + +x86_irq_vectors=${arch_x86_header_dir}/irq_vectors.h + +# FIRST_EXTERNAL_VECTOR is not that useful, find what is its number +# and then replace whatever is using it and that is useful, which at +# the time of writing of this script was: IRQ_MOVE_CLEANUP_VECTOR. + +first_external_regex='^#define[[:space:]]+FIRST_EXTERNAL_VECTOR[[:space:]]+(0x[[:xdigit:]]+)$' +first_external_vector=$(egrep ${first_external_regex} ${x86_irq_vectors} | sed -r "s/${first_external_regex}/\1/g") + +printf "static const char *x86_irq_vectors[] = {\n" +regex='^#define[[:space:]]+([[:alnum:]_]+)_VECTOR[[:space:]]+(0x[[:xdigit:]]+)$' +sed -r "s/FIRST_EXTERNAL_VECTOR/${first_external_vector}/g" ${x86_irq_vectors} | \ +egrep ${regex} | \ + sed -r "s/${regex}/\2 \1/g" | sort -n | \ + xargs printf "\t[%s] = \"%s\",\n" +printf "};\n\n" + From f19a85c68cb4d5bb90c587d9390e1ce2716d6160 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 15:48:50 -0300 Subject: [PATCH 25/57] libbeauty: Hook up the x86 irq_vectors table generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I.e. after running: $ make -C tools/perf O=/tmp/build/perf We end up with: $ cat /tmp/build/perf/trace/beauty/generated/x86_arch_irq_vectors_array.c static const char *x86_irq_vectors[] = { [0x02] = "NMI", [0x12] = "MCE", [0x20] = "IRQ_MOVE_CLEANUP", [0x80] = "IA32_SYSCALL", [0xec] = "LOCAL_TIMER", [0xed] = "HYPERV_STIMER0", [0xee] = "HYPERV_REENLIGHTENMENT", [0xef] = "MANAGED_IRQ_SHUTDOWN", [0xf0] = "POSTED_INTR_NESTED", [0xf1] = "POSTED_INTR_WAKEUP", [0xf2] = "POSTED_INTR", [0xf3] = "HYPERVISOR_CALLBACK", [0xf4] = "DEFERRED_ERROR", [0xf6] = "IRQ_WORK", [0xf7] = "X86_PLATFORM_IPI", [0xf8] = "REBOOT", [0xf9] = "THRESHOLD_APIC", [0xfa] = "THERMAL_APIC", [0xfb] = "CALL_FUNCTION_SINGLE", [0xfc] = "CALL_FUNCTION", [0xfd] = "RESCHEDULE", [0xfe] = "ERROR_APIC", [0xff] = "SPURIOUS_APIC", }; $ Now its just a matter of using it, associating it to tracepoint arguments named 'vector', all of which can be correctly used with this table, for int args. At some point we should move tools/perf/trace/beauty to tools/beauty/, so that it can be used more generally and even made available externally like libbpf, libperf, libtraceevent, etc. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-0p2df4kq1afrxbck4e4ct34r@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 8f1ba986d3bf..1cd294468a1f 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -546,6 +546,12 @@ x86_arch_prctl_code_tbl := $(srctree)/tools/perf/trace/beauty/x86_arch_prctl.sh $(x86_arch_prctl_code_array): $(x86_arch_asm_uapi_dir)/prctl.h $(x86_arch_prctl_code_tbl) $(Q)$(SHELL) '$(x86_arch_prctl_code_tbl)' $(x86_arch_asm_uapi_dir) > $@ +x86_arch_irq_vectors_array := $(beauty_outdir)/x86_arch_irq_vectors_array.c +x86_arch_irq_vectors_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.sh + +$(x86_arch_irq_vectors_array): $(x86_arch_asm_dir)/irq_vectors.h $(x86_arch_irq_vectors_tbl) + $(Q)$(SHELL) '$(x86_arch_irq_vectors_tbl)' $(x86_arch_asm_dir) > $@ + x86_arch_MSRs_array := $(beauty_outdir)/x86_arch_MSRs_array.c x86_arch_MSRs_tbl := $(srctree)/tools/perf/trace/beauty/tracepoints/x86_msr.sh @@ -686,6 +692,7 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc $(perf_ioctl_array) \ $(prctl_option_array) \ $(usbdevfs_ioctl_array) \ + $(x86_arch_irq_vectors_array) \ $(x86_arch_MSRs_array) \ $(x86_arch_prctl_code_array) \ $(rename_flags_array) \ @@ -991,6 +998,7 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea $(OUTPUT)$(perf_ioctl_array) \ $(OUTPUT)$(prctl_option_array) \ $(OUTPUT)$(usbdevfs_ioctl_array) \ + $(OUTPUT)$(x86_arch_irq_vectors_array) \ $(OUTPUT)$(x86_arch_MSRs_array) \ $(OUTPUT)$(x86_arch_prctl_code_array) \ $(OUTPUT)$(rename_flags_array) \ From 97c2a7806f691f4124914623baa54223416a8cef Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 16:01:42 -0300 Subject: [PATCH 26/57] libbeauty: Add a strarray__scnprintf_suffix() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some cases, like with x86 IRQ vectors, the common part in names is at the end, so a suffix, add a scnprintf function for that. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-agxbj6es2ke3rehwt4gkdw23@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 14 ++++++++++++++ tools/perf/trace/beauty/beauty.h | 1 + 2 files changed, 15 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 907eaf316f5b..58bbe85d4166 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -423,6 +423,20 @@ out_delete: ({ struct syscall_tp *fields = evsel->priv; \ fields->name.pointer(&fields->name, sample); }) +size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val) +{ + int idx = val - sa->offset; + + if (idx < 0 || idx >= sa->nr_entries || sa->entries[idx] == NULL) { + size_t printed = scnprintf(bf, size, intfmt, val); + if (show_suffix) + printed += scnprintf(bf + printed, size - printed, " /* %s??? */", sa->prefix); + return printed; + } + + return scnprintf(bf, size, "%s%s", sa->entries[idx], show_suffix ? sa->prefix : ""); +} + size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val) { int idx = val - sa->offset; diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 0dee0cf4fda8..165f56b456be 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -28,6 +28,7 @@ struct strarray { } size_t strarray__scnprintf(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_prefix, int val); +size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val); size_t strarray__scnprintf_flags(struct strarray *sa, char *bf, size_t size, bool show_prefix, unsigned long flags); bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret); From 573ed8985d290214440e6d9866e5330b5ef064ca Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 16:03:25 -0300 Subject: [PATCH 27/57] perf trace beauty: Add the glue for the autogenerated x86 IRQ vector array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to wrap this autogenerated string array with the strarray__scnprintf() formatter and the strarray__strotul() lookup method, do it. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-bx2cjcyv6aerhyy3gvu3uwcy@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/beauty.h | 6 ++++ tools/perf/trace/beauty/tracepoints/Build | 1 + .../beauty/tracepoints/x86_irq_vectors.c | 29 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 165f56b456be..232b64d70096 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -122,6 +122,12 @@ unsigned long syscall_arg__val(struct syscall_arg *arg, u8 idx); size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg); #define SCA_STRARRAY_FLAGS syscall_arg__scnprintf_strarray_flags +size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg); +#define SCA_X86_IRQ_VECTORS syscall_arg__scnprintf_x86_irq_vectors + +bool syscall_arg__strtoul_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_X86_IRQ_VECTORS syscall_arg__strtoul_x86_irq_vectors + size_t syscall_arg__scnprintf_x86_MSR(char *bf, size_t size, struct syscall_arg *arg); #define SCA_X86_MSR syscall_arg__scnprintf_x86_MSR diff --git a/tools/perf/trace/beauty/tracepoints/Build b/tools/perf/trace/beauty/tracepoints/Build index 625a67663de3..e35087fdd108 100644 --- a/tools/perf/trace/beauty/tracepoints/Build +++ b/tools/perf/trace/beauty/tracepoints/Build @@ -1 +1,2 @@ +perf-y += x86_irq_vectors.o perf-y += x86_msr.o diff --git a/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c new file mode 100644 index 000000000000..8eb9bc8534ac --- /dev/null +++ b/tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * trace/beauty/x86_irq_vectors.c + * + * Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo + */ + +#include "trace/beauty/beauty.h" + +#include "trace/beauty/generated/x86_arch_irq_vectors_array.c" + +static DEFINE_STRARRAY(x86_irq_vectors, "_VECTOR"); + +static size_t x86_irq_vectors__scnprintf(unsigned long vector, char *bf, size_t size, bool show_prefix) +{ + return strarray__scnprintf_suffix(&strarray__x86_irq_vectors, bf, size, "%#x", show_prefix, vector); +} + +size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg) +{ + unsigned long vector = arg->val; + + return x86_irq_vectors__scnprintf(vector, bf, size, arg->show_string_prefix); +} + +bool syscall_arg__strtoul_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg __maybe_unused, u64 *ret) +{ + return strarray__strtoul(&strarray__x86_irq_vectors, bf, size, ret); +} From df604bfda6f550f088e1cffcd098bfec0eee9cdf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 15 Oct 2019 16:50:13 -0300 Subject: [PATCH 28/57] perf trace: Hook the 'vec' tracepoint argument with the x86 IRQ vectors scnprintf/strtoul MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ended up only being useful when filtering multiple irq_vectors tracepoints, as we end up having a tracepoint for each of the entries, i.e.: This will always come with the "RESCHEDULE_VECTOR" in the 'vector' arg: # perf trace --max-events 8 -e irq_vectors:reschedule* 0.000 cc1/29067 irq_vectors:reschedule_entry(vector: RESCHEDULE) 0.004 cc1/29067 irq_vectors:reschedule_exit(vector: RESCHEDULE) 0.553 cc1/29067 irq_vectors:reschedule_entry(vector: RESCHEDULE) 0.556 cc1/29067 irq_vectors:reschedule_exit(vector: RESCHEDULE) 1.182 cc1/29067 irq_vectors:reschedule_entry(vector: RESCHEDULE) 1.185 cc1/29067 irq_vectors:reschedule_exit(vector: RESCHEDULE) 1.203 :29052/29052 irq_vectors:reschedule_entry(vector: RESCHEDULE) 1.206 :29052/29052 irq_vectors:reschedule_exit(vector: RESCHEDULE) # While filtering that value will produce nothing: # perf trace --max-events 8 -e irq_vectors:reschedule* --filter="vector != RESCHEDULE" ^C# Maybe it'll be useful for those other tracepoints: # perf list irq_vectors:vector_* List of pre-defined events (to be used in -e): irq_vectors:vector_activate [Tracepoint event] irq_vectors:vector_alloc [Tracepoint event] irq_vectors:vector_alloc_managed [Tracepoint event] irq_vectors:vector_clear [Tracepoint event] irq_vectors:vector_config [Tracepoint event] irq_vectors:vector_deactivate [Tracepoint event] irq_vectors:vector_free_moved [Tracepoint event] irq_vectors:vector_reserve [Tracepoint event] irq_vectors:vector_reserve_managed [Tracepoint event] irq_vectors:vector_setup [Tracepoint event] irq_vectors:vector_teardown [Tracepoint event] irq_vectors:vector_update [Tracepoint event] # But since we have it done, keep it. This at least served to teach me that all those irq vectors have a entry and an exit tracepoint that I can then use just like with raw_syscalls:sys_{enter,exit}, i.e. pair them, use just a trace__irq_vectors_entry() + trace__irq_vectors_exit() and use the 'vector' arg as I use the 'syscall id' one for syscalls. Then the default for 'perf trace' will include irq_vectors in addition to syscalls. Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-wer4cwbbqub3o7sa8h1j3uzb@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 58bbe85d4166..e71605c99080 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1528,7 +1528,8 @@ static int syscall__alloc_arg_fmts(struct syscall *sc, int nr_args) } static struct syscall_arg_fmt syscall_arg_fmts__by_name[] = { - { .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, } + { .name = "msr", .scnprintf = SCA_X86_MSR, .strtoul = STUL_X86_MSR, }, + { .name = "vector", .scnprintf = SCA_X86_IRQ_VECTORS, .strtoul = STUL_X86_IRQ_VECTORS, }, }; static int syscall_arg_fmt__cmp(const void *name, const void *fmtp) From 3cdc8db91e0e73f94fa6ce3bf966b0152ccf0107 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 16:41:34 -0300 Subject: [PATCH 29/57] perf trace: Show error message when not finding a field used in a filter expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was there, but as pr_debug(), make it pr_err() so that we can see it without -v: # trace -e syscalls:*lseek --filter="whenc==SET" sleep 1 "whenc" not found in "syscalls:sys_enter_lseek", can't set filter "whenc==SET" # Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-ly4rgm1bto8uwc2itpaixjob@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e71605c99080..cafd18466dfa 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3611,8 +3611,8 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel fmt = perf_evsel__syscall_arg_fmt(evsel, arg); if (fmt == NULL) { - pr_debug("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", - arg, evsel->name, evsel->filter); + pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", + arg, evsel->name, evsel->filter); return -1; } From fecd990720306f93151747771f16bca71bb29c33 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 08:15:11 -0300 Subject: [PATCH 30/57] perf trace: Introduce accessors to trace specific evsel->priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're using evsel->priv in syscalls:sys_{enter,exit}_SYSCALL and in raw_syscalls:sys_{enter,exit} to cache the offset of the common fields, the multiplexor id/syscall_id in the sys_enter case and syscall_id + ret for sys_exit. And for the rest of the tracepoints we use it to have a syscall_arg_fmt array to have scnprintf/strtoul for tracepoint args. So we better clearly mark them with accessors so that we can move to having a 'struct evsel_trace' struct for all 'perf trace' specific evsel->priv usage. Cc: Adrian Hunter Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-dcoyxfslg7atz821tz9aupjh@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 43 ++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index cafd18466dfa..e0be1df555a2 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -285,6 +285,27 @@ struct syscall_tp { }; }; +/* + * Used with raw_syscalls:sys_{enter,exit} and with the + * syscalls:sys_{enter,exit}_SYSCALL tracepoints + */ +static inline struct syscall_tp *__evsel__syscall_tp(struct evsel *evsel) +{ + struct syscall_tp *sc = evsel->priv; + + return sc; +} + +/* + * Used with all the other tracepoints. + */ +static inline struct syscall_arg_fmt *__evsel__syscall_arg_fmt(struct evsel *evsel) +{ + struct syscall_arg_fmt *fmt = evsel->priv; + + return fmt; +} + static int perf_evsel__init_tp_uint_field(struct evsel *evsel, struct tp_field *field, const char *name) @@ -298,7 +319,7 @@ static int perf_evsel__init_tp_uint_field(struct evsel *evsel, } #define perf_evsel__init_sc_tp_uint_field(evsel, name) \ - ({ struct syscall_tp *sc = evsel->priv;\ + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ perf_evsel__init_tp_uint_field(evsel, &sc->name, #name); }) static int perf_evsel__init_tp_ptr_field(struct evsel *evsel, @@ -314,7 +335,7 @@ static int perf_evsel__init_tp_ptr_field(struct evsel *evsel, } #define perf_evsel__init_sc_tp_ptr_field(evsel, name) \ - ({ struct syscall_tp *sc = evsel->priv;\ + ({ struct syscall_tp *sc = __evsel__syscall_tp(evsel);\ perf_evsel__init_tp_ptr_field(evsel, &sc->name, #name); }) static void evsel__delete_priv(struct evsel *evsel) @@ -364,14 +385,14 @@ out_delete: static int perf_evsel__init_augmented_syscall_tp_args(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); return __tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64)); } static int perf_evsel__init_augmented_syscall_tp_ret(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); return __tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap); } @@ -416,11 +437,11 @@ out_delete: } #define perf_evsel__sc_tp_uint(evsel, name, sample) \ - ({ struct syscall_tp *fields = evsel->priv; \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ fields->name.integer(&fields->name, sample); }) #define perf_evsel__sc_tp_ptr(evsel, name, sample) \ - ({ struct syscall_tp *fields = evsel->priv; \ + ({ struct syscall_tp *fields = __evsel__syscall_tp(evsel); \ fields->name.pointer(&fields->name, sample); }) size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, const char *intfmt, bool show_suffix, int val) @@ -2518,7 +2539,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, char bf[2048]; size_t size = sizeof(bf); struct tep_format_field *field = evsel->tp_format->format.fields; - struct syscall_arg_fmt *arg = evsel->priv; + struct syscall_arg_fmt *arg = __evsel__syscall_arg_fmt(evsel); size_t printed = 0; unsigned long val; u8 bit = 1; @@ -3557,7 +3578,7 @@ static int ordered_events__deliver_event(struct ordered_events *oe, static struct syscall_arg_fmt *perf_evsel__syscall_arg_fmt(struct evsel *evsel, char *arg) { struct tep_format_field *field; - struct syscall_arg_fmt *fmt = evsel->priv; + struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel); if (evsel->tp_format == NULL || fmt == NULL) return NULL; @@ -4315,12 +4336,12 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist) return -1; if (!strncmp(evsel->tp_format->name, "sys_enter_", 10)) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); if (__tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64))) return -1; } else if (!strncmp(evsel->tp_format->name, "sys_exit_", 9)) { - struct syscall_tp *sc = evsel->priv; + struct syscall_tp *sc = __evsel__syscall_tp(evsel); if (__tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap)) return -1; @@ -4856,7 +4877,7 @@ int cmd_trace(int argc, const char **argv) init_augmented_syscall_tp: if (perf_evsel__init_augmented_syscall_tp(evsel, evsel)) goto out; - sc = evsel->priv; + sc = __evsel__syscall_tp(evsel); /* * For now with BPF raw_augmented we hook into * raw_syscalls:sys_enter and there we get all From 8b913df50f56a26b9e336becdd0af9d5ce3831cd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 10:39:46 -0300 Subject: [PATCH 31/57] perf trace: Hide evsel->access further, simplify code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next step will be to have a 'struct evsel_trace' to allow for handling the syscalls tracepoints via the strace-like code while reusing parts of that code with the other tracepoints, where we don't have things like the 'syscall_nr' or 'ret' ((raw_)?syscalls:sys_{enter,exit}(_SYSCALL)?) args that we want to cache offsets and have been using evsel->priv for that, while for the other tracepoints we'll have just an array of 'struct syscall_arg_fmt' (i.e. ->scnprint() for number->string and ->strtoul() string->number conversions and other state those functions need). Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-fre21jbyoqxmmquxcho7oa0x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 57 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e0be1df555a2..1d2ed2823202 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -296,6 +296,15 @@ static inline struct syscall_tp *__evsel__syscall_tp(struct evsel *evsel) return sc; } +static struct syscall_tp *evsel__syscall_tp(struct evsel *evsel) +{ + if (evsel->priv == NULL) { + evsel->priv = zalloc(sizeof(struct syscall_tp)); + } + + return __evsel__syscall_tp(evsel); +} + /* * Used with all the other tracepoints. */ @@ -306,6 +315,15 @@ static inline struct syscall_arg_fmt *__evsel__syscall_arg_fmt(struct evsel *evs return fmt; } +static struct syscall_arg_fmt *evsel__syscall_arg_fmt(struct evsel *evsel) +{ + if (evsel->priv == NULL) { + evsel->priv = calloc(evsel->tp_format->format.nr_fields, sizeof(struct syscall_arg_fmt)); + } + + return __evsel__syscall_arg_fmt(evsel); +} + static int perf_evsel__init_tp_uint_field(struct evsel *evsel, struct tp_field *field, const char *name) @@ -346,41 +364,34 @@ static void evsel__delete_priv(struct evsel *evsel) static int perf_evsel__init_syscall_tp(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); + struct syscall_tp *sc = evsel__syscall_tp(evsel); - if (evsel->priv != NULL) { + if (sc != NULL) { if (perf_evsel__init_tp_uint_field(evsel, &sc->id, "__syscall_nr") && perf_evsel__init_tp_uint_field(evsel, &sc->id, "nr")) - goto out_delete; + return -ENOENT; return 0; } return -ENOMEM; -out_delete: - zfree(&evsel->priv); - return -ENOENT; } static int perf_evsel__init_augmented_syscall_tp(struct evsel *evsel, struct evsel *tp) { - struct syscall_tp *sc = evsel->priv = malloc(sizeof(struct syscall_tp)); + struct syscall_tp *sc = evsel__syscall_tp(evsel); - if (evsel->priv != NULL) { + if (sc != NULL) { struct tep_format_field *syscall_id = perf_evsel__field(tp, "id"); if (syscall_id == NULL) syscall_id = perf_evsel__field(tp, "__syscall_nr"); - if (syscall_id == NULL) - goto out_delete; - if (__tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) - goto out_delete; + if (syscall_id == NULL || + __tp_field__init_uint(&sc->id, syscall_id->size, syscall_id->offset, evsel->needs_swap)) + return -EINVAL; return 0; } return -ENOMEM; -out_delete: - zfree(&evsel->priv); - return -EINVAL; } static int perf_evsel__init_augmented_syscall_tp_args(struct evsel *evsel) @@ -399,20 +410,15 @@ static int perf_evsel__init_augmented_syscall_tp_ret(struct evsel *evsel) static int perf_evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler) { - evsel->priv = malloc(sizeof(struct syscall_tp)); - if (evsel->priv != NULL) { + if (evsel__syscall_tp(evsel) != NULL) { if (perf_evsel__init_sc_tp_uint_field(evsel, id)) - goto out_delete; + return -ENOENT; evsel->handler = handler; return 0; } return -ENOMEM; - -out_delete: - zfree(&evsel->priv); - return -ENOENT; } static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler) @@ -1690,11 +1696,10 @@ static int trace__read_syscall_info(struct trace *trace, int id) static int perf_evsel__init_tp_arg_scnprintf(struct evsel *evsel) { - int nr_args = evsel->tp_format->format.nr_fields; + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); - evsel->priv = calloc(nr_args, sizeof(struct syscall_arg_fmt)); - if (evsel->priv != NULL) { - syscall_arg_fmt__init_array(evsel->priv, evsel->tp_format->format.fields); + if (fmt != NULL) { + syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields); return 0; } From 2b00bb627f62ed1c6180f49f7883789bc5e1b33f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 16:37:18 -0300 Subject: [PATCH 32/57] perf trace: Introduce 'struct evsel__trace' for evsel->priv needs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For syscalls we need to cache the 'syscall_id' and 'ret' field offsets but as well have a pointer to the syscall_fmt_arg array for the fields, so that we can expand strings in filter expressions, so introduce a 'struct evsel_trace' to have in evsel->priv that allows for that. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-hx8ukasuws5sz6rsar73cocv@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 54 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1d2ed2823202..5792278065f6 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -285,21 +285,47 @@ struct syscall_tp { }; }; +/* + * The evsel->priv as used by 'perf trace' + * sc: for raw_syscalls:sys_{enter,exit} and syscalls:sys_{enter,exit}_SYSCALLNAME + * fmt: for all the other tracepoints + */ +struct evsel_trace { + struct syscall_tp sc; + struct syscall_arg_fmt *fmt; +}; + +static struct evsel_trace *evsel_trace__new(void) +{ + return zalloc(sizeof(struct evsel_trace)); +} + +static void evsel_trace__delete(struct evsel_trace *et) +{ + if (et == NULL) + return; + + zfree(&et->fmt); + free(et); +} + /* * Used with raw_syscalls:sys_{enter,exit} and with the * syscalls:sys_{enter,exit}_SYSCALL tracepoints */ static inline struct syscall_tp *__evsel__syscall_tp(struct evsel *evsel) { - struct syscall_tp *sc = evsel->priv; + struct evsel_trace *et = evsel->priv; - return sc; + return &et->sc; } static struct syscall_tp *evsel__syscall_tp(struct evsel *evsel) { if (evsel->priv == NULL) { - evsel->priv = zalloc(sizeof(struct syscall_tp)); + evsel->priv = evsel_trace__new(); + if (evsel->priv == NULL) + return NULL; } return __evsel__syscall_tp(evsel); @@ -310,18 +336,34 @@ static struct syscall_tp *evsel__syscall_tp(struct evsel *evsel) */ static inline struct syscall_arg_fmt *__evsel__syscall_arg_fmt(struct evsel *evsel) { - struct syscall_arg_fmt *fmt = evsel->priv; + struct evsel_trace *et = evsel->priv; - return fmt; + return et->fmt; } static struct syscall_arg_fmt *evsel__syscall_arg_fmt(struct evsel *evsel) { + struct evsel_trace *et = evsel->priv; + if (evsel->priv == NULL) { - evsel->priv = calloc(evsel->tp_format->format.nr_fields, sizeof(struct syscall_arg_fmt)); + et = evsel->priv = evsel_trace__new(); + + if (et == NULL) + return NULL; + } + + if (et->fmt == NULL) { + et->fmt = calloc(evsel->tp_format->format.nr_fields, sizeof(struct syscall_arg_fmt)); + if (et->fmt == NULL) + goto out_delete; } return __evsel__syscall_arg_fmt(evsel); + +out_delete: + evsel_trace__delete(evsel->priv); + evsel->priv = NULL; + return NULL; } static int perf_evsel__init_tp_uint_field(struct evsel *evsel, From 362222f877f1369c0a8017c58b075abf30b16ab7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 17:33:08 -0300 Subject: [PATCH 33/57] perf trace: Initialize evsel_trace->fmt for syscalls:sys_enter_* tracepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the syscall_fmts->arg entries for formatting strace-like syscalls. This is when resolving the string "whence" on a filter expression for the syscalls:sys_enter_lseek: Breakpoint 3, perf_evsel__syscall_arg_fmt (evsel=0xc91ed0, arg=0x7fffffff7cd0 "whence") at builtin-trace.c:3626 3626 { (gdb) n 3628 struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel); (gdb) n 3630 if (evsel->tp_format == NULL || fmt == NULL) (gdb) n 3633 for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) (gdb) n 3634 if (strcmp(field->name, arg) == 0) (gdb) p field->name $3 = 0xc945e0 "__syscall_nr" (gdb) n 3633 for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) (gdb) p *fmt $4 = {scnprintf = 0x0, strtoul = 0x0, mask_val = 0x0, parm = 0x0, name = 0x0, nr_entries = 0, show_zero = false} (gdb) n 3634 if (strcmp(field->name, arg) == 0) (gdb) p field->name $5 = 0xc94690 "fd" (gdb) n 3633 for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) (gdb) n 3634 if (strcmp(field->name, arg) == 0) (gdb) n 3633 for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) (gdb) n 3634 if (strcmp(field->name, arg) == 0) (gdb) p *fmt $9 = {scnprintf = 0x489be2 , strtoul = 0x0, mask_val = 0x0, parm = 0xa2da80 , name = 0x0, nr_entries = 0, show_zero = false} (gdb) p field->name $10 = 0xc947b0 "whence" (gdb) p fmt->parm $11 = (void *) 0xa2da80 (gdb) p *(struct strarray *)fmt->parm $12 = {offset = 0, nr_entries = 5, prefix = 0x724d37 "SEEK_", entries = 0xa2da40 } (gdb) p (struct strarray *)fmt->parm)->entries Junk after end of expression. (gdb) p ((struct strarray *)fmt->parm)->entries $13 = (const char **) 0xa2da40 (gdb) p ((struct strarray *)fmt->parm)->entries[0] $14 = 0x724d21 "SET" (gdb) p ((struct strarray *)fmt->parm)->entries[1] $15 = 0x724d25 "CUR" (gdb) p ((struct strarray *)fmt->parm)->entries[2] $16 = 0x724d29 "END" (gdb) p ((struct strarray *)fmt->parm)->entries[2] $17 = 0x724d29 "END" (gdb) p ((struct strarray *)fmt->parm)->entries[3] $18 = 0x724d2d "DATA" (gdb) p ((struct strarray *)fmt->parm)->entries[4] $19 = 0x724d32 "HOLE" (gdb) Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-lc8h9jgvbnboe0g7ic8tra1y@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5792278065f6..3502417dc7f2 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -4366,6 +4366,25 @@ static void evlist__set_default_evsel_handler(struct evlist *evlist, void *handl } } +static void evsel__set_syscall_arg_fmt(struct evsel *evsel, const char *name) +{ + struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel); + + if (fmt) { + struct syscall_fmt *scfmt = syscall_fmt__find(name); + + if (scfmt) { + int skip = 0; + + if (strcmp(evsel->tp_format->format.fields->name, "__syscall_nr") == 0 || + strcmp(evsel->tp_format->format.fields->name, "nr") == 0) + ++skip; + + memcpy(fmt + skip, scfmt->arg, (evsel->tp_format->format.nr_fields - skip) * sizeof(*fmt)); + } + } +} + static int evlist__set_syscall_tp_fields(struct evlist *evlist) { struct evsel *evsel; @@ -4387,11 +4406,15 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist) if (__tp_field__init_ptr(&sc->args, sc->id.offset + sizeof(u64))) return -1; + + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_enter_") - 1); } else if (!strncmp(evsel->tp_format->name, "sys_exit_", 9)) { struct syscall_tp *sc = __evsel__syscall_tp(evsel); if (__tp_field__init_uint(&sc->ret, sizeof(u64), sc->id.offset + sizeof(u64), evsel->needs_swap)) return -1; + + evsel__set_syscall_arg_fmt(evsel, evsel->tp_format->name + sizeof("sys_exit_") - 1); } } From a5e05abc6b8d81148b35cd8632a4a6252383d968 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 17 Oct 2019 17:05:22 -0400 Subject: [PATCH 34/57] perf scripting engines: Iterate on tep event arrays directly Instead of calling a useless (and broken) helper function to get the next event of a tep event array, just get the array directly and iterate over it. Note, the broken part was from trace_find_next_event() which after this will no longer be used, and can be removed. Committer notes: This fixes a segfault when generating python scripts from perf.data files with multiple tracepoint events, i.e. the following use case is fixed by this patch: # perf record -e sched:* sleep 1 [ perf record: Woken up 31 times to write data ] [ perf record: Captured and wrote 0.031 MB perf.data (9 samples) ] # perf script -g python Segmentation fault (core dumped) # Reported-by: Daniel Bristot de Oliveira Signed-off-by: Steven Rostedt (VMware) Tested-by: Arnaldo Carvalho de Melo Cc: Andrew Morton Cc: Jiri Olsa Cc: Namhyung Kim Cc: Tzvetomir Stoyanov Cc: linux-trace-devel@vger.kernel.org Link: http://lkml.kernel.org/r/20191017153733.630cd5eb@gandalf.local.home Link: http://lore.kernel.org/lkml/20191017210636.061448713@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-perl.c | 8 ++++++-- tools/perf/util/scripting-engines/trace-event-python.c | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 15961854ba67..741f040648b5 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -539,10 +539,11 @@ static int perl_stop_script(void) static int perl_generate_script(struct tep_handle *pevent, const char *outfile) { + int i, not_first, count, nr_events; + struct tep_event **all_events; struct tep_event *event = NULL; struct tep_format_field *f; char fname[PATH_MAX]; - int not_first, count; FILE *ofp; sprintf(fname, "%s.pl", outfile); @@ -603,8 +604,11 @@ sub print_backtrace\n\ }\n\n\ "); + nr_events = tep_get_events_count(pevent); + all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID); - while ((event = trace_find_next_event(pevent, event))) { + for (i = 0; all_events && i < nr_events; i++) { + event = all_events[i]; fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); fprintf(ofp, "\tmy ("); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 5d341efc3237..93c03b39cd9c 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -1687,10 +1687,11 @@ static int python_stop_script(void) static int python_generate_script(struct tep_handle *pevent, const char *outfile) { + int i, not_first, count, nr_events; + struct tep_event **all_events; struct tep_event *event = NULL; struct tep_format_field *f; char fname[PATH_MAX]; - int not_first, count; FILE *ofp; sprintf(fname, "%s.py", outfile); @@ -1735,7 +1736,11 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile fprintf(ofp, "def trace_end():\n"); fprintf(ofp, "\tprint(\"in trace_end\")\n\n"); - while ((event = trace_find_next_event(pevent, event))) { + nr_events = tep_get_events_count(pevent); + all_events = tep_list_events(pevent, TEP_EVENT_SORT_ID); + + for (i = 0; all_events && i < nr_events; i++) { + event = all_events[i]; fprintf(ofp, "def %s__%s(", event->system, event->name); fprintf(ofp, "event_name, "); fprintf(ofp, "context, "); From 9bdff5b6436655d42dd30253c521e86ce07b9961 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 17 Oct 2019 17:05:23 -0400 Subject: [PATCH 35/57] perf tools: Remove unused trace_find_next_event() trace_find_next_event() was buggy and pretty much a useless helper. As there are no more users, just remove it. Signed-off-by: Steven Rostedt (VMware) Cc: Andrew Morton Cc: Jiri Olsa Cc: Namhyung Kim Cc: Tzvetomir Stoyanov Cc: linux-trace-devel@vger.kernel.org Link: http://lore.kernel.org/lkml/20191017210636.224045576@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event-parse.c | 31 ----------------------------- tools/perf/util/trace-event.h | 2 -- 2 files changed, 33 deletions(-) diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 5d6bfc70b210..9634f0ae57be 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -173,37 +173,6 @@ int parse_event_file(struct tep_handle *pevent, return tep_parse_event(pevent, buf, size, sys); } -struct tep_event *trace_find_next_event(struct tep_handle *pevent, - struct tep_event *event) -{ - static int idx; - int events_count; - struct tep_event *all_events; - - all_events = tep_get_first_event(pevent); - events_count = tep_get_events_count(pevent); - if (!pevent || !all_events || events_count < 1) - return NULL; - - if (!event) { - idx = 0; - return all_events; - } - - if (idx < events_count && event == (all_events + idx)) { - idx++; - if (idx == events_count) - return NULL; - return (all_events + idx); - } - - for (idx = 1; idx < events_count; idx++) { - if (event == (all_events + (idx - 1))) - return (all_events + idx); - } - return NULL; -} - struct flag { const char *name; unsigned long long value; diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 2e158387b3d7..72fdf2a3577c 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -47,8 +47,6 @@ void parse_saved_cmdline(struct tep_handle *pevent, char *file, unsigned int siz ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); -struct tep_event *trace_find_next_event(struct tep_handle *pevent, - struct tep_event *event); unsigned long long read_size(struct tep_event *event, void *ptr, int size); unsigned long long eval_flag(const char *flag); From d066da978f89ef035c823367a97650f0c4cfa464 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 17 Oct 2019 18:33:00 -0300 Subject: [PATCH 36/57] libbeauty: Introduce syscall_arg__strtoul_strarray() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To go from strarrays strings to its indexes. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-wta0qvo207z27huib2c4ijxq@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 ++++++ tools/perf/trace/beauty/beauty.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 3502417dc7f2..0294b17ed510 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -535,6 +535,11 @@ static size_t syscall_arg__scnprintf_strarray(char *bf, size_t size, #define SCA_STRARRAY syscall_arg__scnprintf_strarray +bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarray__strtoul(arg->parm, bf, size, ret); +} + size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg) { return strarray__scnprintf_flags(arg->parm, bf, size, arg->show_string_prefix, arg->val); @@ -824,6 +829,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ + .strtoul = STUL_STRARRAY, \ .parm = &strarray__##array, } #define STRARRAY_FLAGS(name, array) \ diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 232b64d70096..1b8a30e5dcf9 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -122,6 +122,9 @@ unsigned long syscall_arg__val(struct syscall_arg *arg, u8 idx); size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg); #define SCA_STRARRAY_FLAGS syscall_arg__scnprintf_strarray_flags +bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAY syscall_arg__strtoul_strarray + size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg); #define SCA_X86_IRQ_VECTORS syscall_arg__scnprintf_x86_irq_vectors From db25bf98a3861225bc0b2138cf665097141c72ee Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 18 Oct 2019 11:48:57 -0300 Subject: [PATCH 37/57] perf trace: Honour --max-events in processing syscalls:sys_enter_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were doing this only at the sys_exit syscall tracepoint, as for strace-like we count the pair of sys_enter and sys_exit as one event, but when asking specifically for a the syscalls:sys_enter_NAME tracepoint we need to count each of those as an event. I.e. things like: # perf trace --max-events=4 -e syscalls:sys_enter_lseek 0.000 pool/2242 syscalls:sys_enter_lseek(fd: 14, offset: 0, whence: CUR) 0.034 pool/2242 syscalls:sys_enter_lseek(fd: 15, offset: 0, whence: CUR) 0.051 pool/2242 syscalls:sys_enter_lseek(fd: 16, offset: 0, whence: CUR) 2307.900 sshd/30800 syscalls:sys_enter_lseek(fd: 3, offset: 9032, whence: SET) # Were going on forever, since we only had sys_enter events. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-0ob1dky1a9ijlfrfhxyl40wr@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 0294b17ed510..1aaf7b28eec4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2727,12 +2727,6 @@ static int trace__event_handler(struct trace *trace, struct evsel *evsel, } else { trace__fprintf_tp_fields(trace, evsel, sample, thread, NULL, 0); } - ++trace->nr_events_printed; - - if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { - evsel__disable(evsel); - evsel__close(evsel); - } } } @@ -2743,6 +2737,13 @@ newline: trace__fprintf_callchain(trace, sample); else if (callchain_ret < 0) pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel)); + + ++trace->nr_events_printed; + + if (evsel->max_events != ULONG_MAX && ++evsel->nr_events_printed == evsel->max_events) { + evsel__disable(evsel); + evsel__close(evsel); + } out: thread__put(thread); return 0; From 9afec87ec1f832b8a521da42a72b73a44a59949d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 18 Oct 2019 11:15:55 -0300 Subject: [PATCH 38/57] perf trace: Pass a syscall_arg to syscall_arg_fmt->strtoul() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With just what we need for the STUL_STRARRAY, i.e. the 'struct strarray' pointer to be used, just like with syscall_arg_fmt->scnprintf() for the other direction (number -> string). With this all the strarrays that are associated with syscalls can be used with '-e syscalls:sys_enter_SYSCALLNAME --filter', and soon will be possible as well to use with the strace-like shorter form, with just the syscall names, i.e. something like: -e lseek/whence==END/ For now we have to use the longer form: # perf trace -e syscalls:sys_enter_lseek 0.000 pool/2242 syscalls:sys_enter_lseek(fd: 14, offset: 0, whence: CUR) 0.031 pool/2242 syscalls:sys_enter_lseek(fd: 15, offset: 0, whence: CUR) 0.046 pool/2242 syscalls:sys_enter_lseek(fd: 16, offset: 0, whence: CUR) 5003.528 pool/2242 syscalls:sys_enter_lseek(fd: 14, offset: 0, whence: CUR) 5003.575 pool/2242 syscalls:sys_enter_lseek(fd: 15, offset: 0, whence: CUR) 5003.593 pool/2242 syscalls:sys_enter_lseek(fd: 16, offset: 0, whence: CUR) 10002.017 pool/2242 syscalls:sys_enter_lseek(fd: 14, offset: 0, whence: CUR) 10002.051 pool/2242 syscalls:sys_enter_lseek(fd: 15, offset: 0, whence: CUR) 10002.068 pool/2242 syscalls:sys_enter_lseek(fd: 16, offset: 0, whence: CUR) ^C# perf trace -e syscalls:sys_enter_lseek --filter="whence!=CUR" 0.000 sshd/24476 syscalls:sys_enter_lseek(fd: 3, offset: 9032, whence: SET) 0.060 sshd/24476 syscalls:sys_enter_lseek(fd: 3, offset: 9032, whence: SET) 0.187 sshd/24476 syscalls:sys_enter_lseek(fd: 3, offset: 118632, whence: SET) 0.203 sshd/24476 syscalls:sys_enter_lseek(fd: 3, offset: 118632, whence: SET) 0.349 sshd/24476 syscalls:sys_enter_lseek(fd: 3, offset: 61936, whence: SET) ^C# And for those curious about what are those lseek(DSO, offset, SET), well, its the loader: # perf trace -e syscalls:sys_enter_lseek/max-stack=16/ --filter="whence!=CUR" 0.000 sshd/24495 syscalls:sys_enter_lseek(fd: 3, offset: 9032, whence: SET) __libc_lseek64 (/usr/lib64/ld-2.29.so) _dl_map_object (/usr/lib64/ld-2.29.so) 0.067 sshd/24495 syscalls:sys_enter_lseek(fd: 3, offset: 9032, whence: SET) __libc_lseek64 (/usr/lib64/ld-2.29.so) _dl_map_object_from_fd (/usr/lib64/ld-2.29.so) _dl_map_object (/usr/lib64/ld-2.29.so) 0.198 sshd/24495 syscalls:sys_enter_lseek(fd: 3, offset: 118632, whence: SET) __libc_lseek64 (/usr/lib64/ld-2.29.so) _dl_map_object (/usr/lib64/ld-2.29.so) 0.219 sshd/24495 syscalls:sys_enter_lseek(fd: 3, offset: 118632, whence: SET) __libc_lseek64 (/usr/lib64/ld-2.29.so) _dl_map_object_from_fd (/usr/lib64/ld-2.29.so) _dl_map_object (/usr/lib64/ld-2.29.so) ^C# :-) With this we can use strings in strarrays in filters, which allows us to reuse all these that are in place for syscalls: $ find tools/perf/trace/beauty/ -name "*.c" | xargs grep -w DEFINE_STRARRAY tools/perf/trace/beauty/fcntl.c: static DEFINE_STRARRAY(fcntl_setlease, "F_"); tools/perf/trace/beauty/mmap.c: static DEFINE_STRARRAY(mmap_flags, "MAP_"); tools/perf/trace/beauty/mmap.c: static DEFINE_STRARRAY(madvise_advices, "MADV_"); tools/perf/trace/beauty/sync_file_range.c: static DEFINE_STRARRAY(sync_file_range_flags, "SYNC_FILE_RANGE_"); tools/perf/trace/beauty/socket.c: static DEFINE_STRARRAY(socket_ipproto, "IPPROTO_"); tools/perf/trace/beauty/mount_flags.c: static DEFINE_STRARRAY(mount_flags, "MS_"); tools/perf/trace/beauty/pkey_alloc.c: static DEFINE_STRARRAY(pkey_alloc_access_rights, "PKEY_"); tools/perf/trace/beauty/sockaddr.c:DEFINE_STRARRAY(socket_families, "PF_"); tools/perf/trace/beauty/tracepoints/x86_irq_vectors.c:static DEFINE_STRARRAY(x86_irq_vectors, "_VECTOR"); tools/perf/trace/beauty/tracepoints/x86_msr.c:static DEFINE_STRARRAY(x86_MSRs, "MSR_"); tools/perf/trace/beauty/prctl.c: static DEFINE_STRARRAY(prctl_options, "PR_"); tools/perf/trace/beauty/prctl.c: static DEFINE_STRARRAY(prctl_set_mm_options, "PR_SET_MM_"); tools/perf/trace/beauty/fspick.c: static DEFINE_STRARRAY(fspick_flags, "FSPICK_"); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(ioctl_tty_cmd, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(drm_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(sndrv_pcm_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(sndrv_ctl_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(kvm_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(vhost_virtio_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(vhost_virtio_ioctl_read_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(perf_ioctl_cmds, ""); tools/perf/trace/beauty/ioctl.c: static DEFINE_STRARRAY(usbdevfs_ioctl_cmds, ""); tools/perf/trace/beauty/fsmount.c: static DEFINE_STRARRAY(fsmount_attr_flags, "MOUNT_ATTR_"); tools/perf/trace/beauty/renameat.c: static DEFINE_STRARRAY(rename_flags, "RENAME_"); tools/perf/trace/beauty/kcmp.c: static DEFINE_STRARRAY(kcmp_types, "KCMP_"); tools/perf/trace/beauty/move_mount.c: static DEFINE_STRARRAY(move_mount_flags, "MOVE_MOUNT_"); $ Well, some, as the mmap flags are like: $ tools/perf/trace/beauty/mmap_flags.sh static const char *mmap_flags[] = { [ilog2(0x40) + 1] = "32BIT", [ilog2(0x01) + 1] = "SHARED", [ilog2(0x02) + 1] = "PRIVATE", [ilog2(0x10) + 1] = "FIXED", [ilog2(0x20) + 1] = "ANONYMOUS", [ilog2(0x008000) + 1] = "POPULATE", [ilog2(0x010000) + 1] = "NONBLOCK", [ilog2(0x020000) + 1] = "STACK", [ilog2(0x040000) + 1] = "HUGETLB", [ilog2(0x080000) + 1] = "SYNC", [ilog2(0x100000) + 1] = "FIXED_NOREPLACE", [ilog2(0x0100) + 1] = "GROWSDOWN", [ilog2(0x0800) + 1] = "DENYWRITE", [ilog2(0x1000) + 1] = "EXECUTABLE", [ilog2(0x2000) + 1] = "LOCKED", [ilog2(0x4000) + 1] = "NORESERVE", }; $ So we'll need a strarray__strtoul_flags() that will break donw the flags into tokens separated by '|' before doing the lookup and then go on reconstructing the value from, say: # perf trace -e syscalls:sys_enter_mmap --filter="flags==PRIVATE|FIXED|DENYWRITE" into: # perf trace -e syscalls:sys_enter_mmap --filter="flags==0x2|0x10|0x0800" and finally into: # perf trace -e syscalls:sys_enter_mmap --filter="flags==0x812" That is what we see if we don't use the augmented view obtained from: # perf trace -e mmap 211792.885 procmail/15393 mmap(addr: 0x7fcd11645000, len: 8192, prot: READ, flags: PRIVATE|FIXED|DENYWRITE, fd: 8, off: 0xa000) = 0x7fcd11645000 But plain use tracefs: procmail-15559 [000] .... 54557.178262: sys_mmap(addr: 7f5c9bf7a000, len: 9b000, prot: 1, flags: 812, fd: 3, off: a9000) Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-c6mgkjt8ujnc263eld5tb7q3@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1aaf7b28eec4..0e7fc7cc42d9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -3696,7 +3696,11 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel if (fmt->strtoul) { u64 val; - if (fmt->strtoul(right, right_size, NULL, &val)) { + struct syscall_arg syscall_arg = { + .parm = fmt->parm, + }; + + if (fmt->strtoul(right, right_size, &syscall_arg, &val)) { char *n, expansion[19]; int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); int expansion_offset = right - new_filter; From a7f6c8c81afdd6d24eb12558f2fb66901207d349 Mon Sep 17 00:00:00 2001 From: Jin Yao Date: Tue, 15 Oct 2019 10:53:57 +0800 Subject: [PATCH 39/57] perf list: Hide deprecated events by default There are some deprecated events listed by perf list. But we can't remove them from perf list with ease because some old scripts may use them. Deprecated events are old names of renamed events. When an event gets renamed the old name is kept around for some time and marked with Deprecated. The newer Intel event lists in the tree already have these headers. So we need to keep them in the event list, but provide a new option to show them. The new option is "--deprecated". With this patch, the deprecated events are hidden by default but they can be displayed when option "--deprecated" is enabled. Signed-off-by: Jin Yao Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191015025357.8708-1-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-list.txt | 3 +++ tools/perf/builtin-list.c | 14 ++++++++++---- tools/perf/pmu-events/jevents.c | 26 ++++++++++++++++++++------ tools/perf/pmu-events/jevents.h | 3 ++- tools/perf/pmu-events/pmu-events.h | 1 + tools/perf/util/parse-events.c | 4 ++-- tools/perf/util/parse-events.h | 2 +- tools/perf/util/pmu.c | 17 +++++++++++++---- tools/perf/util/pmu.h | 4 +++- 9 files changed, 55 insertions(+), 19 deletions(-) diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 18ed1b0fceb3..6345db33c533 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -36,6 +36,9 @@ Enable debugging output. Print how named events are resolved internally into perf events, and also any extra expressions computed by perf stat. +--deprecated:: +Print deprecated events. By default the deprecated events are hidden. + [[EVENT_MODIFIERS]] EVENT MODIFIERS --------------- diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 08e62ae9d37e..965ef017496f 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -26,6 +26,7 @@ int cmd_list(int argc, const char **argv) int i; bool raw_dump = false; bool long_desc_flag = false; + bool deprecated = false; struct option list_options[] = { OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), OPT_BOOLEAN('d', "desc", &desc_flag, @@ -34,6 +35,8 @@ int cmd_list(int argc, const char **argv) "Print longer event descriptions."), OPT_BOOLEAN(0, "details", &details_flag, "Print information on the perf event names and expressions used internally by events."), + OPT_BOOLEAN(0, "deprecated", &deprecated, + "Print deprecated events."), OPT_INCR(0, "debug", &verbose, "Enable debugging output"), OPT_END() @@ -55,7 +58,7 @@ int cmd_list(int argc, const char **argv) if (argc == 0) { print_events(NULL, raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, deprecated); return 0; } @@ -78,7 +81,8 @@ int cmd_list(int argc, const char **argv) print_hwcache_events(NULL, raw_dump); else if (strcmp(argv[i], "pmu") == 0) print_pmu_events(NULL, raw_dump, !desc_flag, - long_desc_flag, details_flag); + long_desc_flag, details_flag, + deprecated); else if (strcmp(argv[i], "sdt") == 0) print_sdt_events(NULL, NULL, raw_dump); else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) @@ -91,7 +95,8 @@ int cmd_list(int argc, const char **argv) if (sep == NULL) { print_events(argv[i], raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, + deprecated); continue; } sep_idx = sep - argv[i]; @@ -117,7 +122,8 @@ int cmd_list(int argc, const char **argv) print_hwcache_events(s, raw_dump); print_pmu_events(s, raw_dump, !desc_flag, long_desc_flag, - details_flag); + details_flag, + deprecated); print_tracepoint_events(NULL, s, raw_dump); print_sdt_events(NULL, s, raw_dump); metricgroup__print(true, true, s, raw_dump, details_flag); diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c index e2837260ca4d..7d69727f44bd 100644 --- a/tools/perf/pmu-events/jevents.c +++ b/tools/perf/pmu-events/jevents.c @@ -322,7 +322,8 @@ static int print_events_table_entry(void *data, char *name, char *event, char *desc, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group) + char *metric_name, char *metric_group, + char *deprecated) { struct perf_entry_data *pd = data; FILE *outfp = pd->outfp; @@ -354,6 +355,8 @@ static int print_events_table_entry(void *data, char *name, char *event, fprintf(outfp, "\t.metric_name = \"%s\",\n", metric_name); if (metric_group) fprintf(outfp, "\t.metric_group = \"%s\",\n", metric_group); + if (deprecated) + fprintf(outfp, "\t.deprecated = \"%s\",\n", deprecated); fprintf(outfp, "},\n"); return 0; @@ -371,6 +374,7 @@ struct event_struct { char *metric_expr; char *metric_name; char *metric_group; + char *deprecated; }; #define ADD_EVENT_FIELD(field) do { if (field) { \ @@ -398,6 +402,7 @@ struct event_struct { op(metric_expr); \ op(metric_name); \ op(metric_group); \ + op(deprecated); \ } while (0) static LIST_HEAD(arch_std_events); @@ -416,7 +421,8 @@ static void free_arch_std_events(void) static int save_arch_std_events(void *data, char *name, char *event, char *desc, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group) + char *metric_name, char *metric_group, + char *deprecated) { struct event_struct *es; @@ -479,7 +485,8 @@ static int try_fixup(const char *fn, char *arch_std, char **event, char **desc, char **name, char **long_desc, char **pmu, char **filter, char **perpkg, char **unit, char **metric_expr, char **metric_name, - char **metric_group, unsigned long long eventcode) + char **metric_group, unsigned long long eventcode, + char **deprecated) { /* try to find matching event from arch standard values */ struct event_struct *es; @@ -507,7 +514,8 @@ int json_events(const char *fn, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group), + char *metric_name, char *metric_group, + char *deprecated), void *data) { int err; @@ -536,6 +544,7 @@ int json_events(const char *fn, char *metric_expr = NULL; char *metric_name = NULL; char *metric_group = NULL; + char *deprecated = NULL; char *arch_std = NULL; unsigned long long eventcode = 0; struct msrmap *msr = NULL; @@ -614,6 +623,8 @@ int json_events(const char *fn, addfield(map, &unit, "", "", val); } else if (json_streq(map, field, "PerPkg")) { addfield(map, &perpkg, "", "", val); + } else if (json_streq(map, field, "Deprecated")) { + addfield(map, &deprecated, "", "", val); } else if (json_streq(map, field, "MetricName")) { addfield(map, &metric_name, "", "", val); } else if (json_streq(map, field, "MetricGroup")) { @@ -658,12 +669,14 @@ int json_events(const char *fn, err = try_fixup(fn, arch_std, &event, &desc, &name, &long_desc, &pmu, &filter, &perpkg, &unit, &metric_expr, &metric_name, - &metric_group, eventcode); + &metric_group, eventcode, + &deprecated); if (err) goto free_strings; } err = func(data, name, real_event(name, event), desc, long_desc, - pmu, unit, perpkg, metric_expr, metric_name, metric_group); + pmu, unit, perpkg, metric_expr, metric_name, + metric_group, deprecated); free_strings: free(event); free(desc); @@ -673,6 +686,7 @@ free_strings: free(pmu); free(filter); free(perpkg); + free(deprecated); free(unit); free(metric_expr); free(metric_name); diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h index 4684c673c445..5cda49a42143 100644 --- a/tools/perf/pmu-events/jevents.h +++ b/tools/perf/pmu-events/jevents.h @@ -7,7 +7,8 @@ int json_events(const char *fn, char *long_desc, char *pmu, char *unit, char *perpkg, char *metric_expr, - char *metric_name, char *metric_group), + char *metric_name, char *metric_group, + char *deprecated), void *data); char *get_cpu_str(void); diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index 92a4d15ee0b9..caeb577d36c9 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -17,6 +17,7 @@ struct pmu_event { const char *metric_expr; const char *metric_name; const char *metric_group; + const char *deprecated; }; /* diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b5e2adef49de..db882f630f7e 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2600,7 +2600,7 @@ out_enomem: * Print the help text for the event symbols: */ void print_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc, bool details_flag) + bool long_desc, bool details_flag, bool deprecated) { print_symbol_events(event_glob, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX, name_only); @@ -2612,7 +2612,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, print_hwcache_events(event_glob, name_only); print_pmu_events(event_glob, name_only, quiet_flag, long_desc, - details_flag); + details_flag, deprecated); if (event_glob != NULL) return; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 616ca1eda0eb..769e07cddaa2 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -195,7 +195,7 @@ void parse_events_evlist_error(struct parse_events_state *parse_state, int idx, const char *str); void print_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc, bool details_flag); + bool long_desc, bool details_flag, bool deprecated); struct event_symbol { const char *symbol; diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 5608da82ad23..adbe97e941dd 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -308,7 +308,8 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, char *long_desc, char *topic, char *unit, char *perpkg, char *metric_expr, - char *metric_name) + char *metric_name, + char *deprecated) { struct parse_events_term *term; struct perf_pmu_alias *alias; @@ -325,6 +326,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->unit[0] = '\0'; alias->per_pkg = false; alias->snapshot = false; + alias->deprecated = false; ret = parse_events_terms(&alias->terms, val); if (ret) { @@ -379,6 +381,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; alias->str = strdup(newval); + if (deprecated) + alias->deprecated = true; + if (!perf_pmu_merge_alias(alias, list)) list_add_tail(&alias->list, list); @@ -400,7 +405,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI strim(buf); return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); } static inline bool pmu_alias_info_file(char *name) @@ -787,7 +792,8 @@ new_alias: (char *)pe->long_desc, (char *)pe->topic, (char *)pe->unit, (char *)pe->perpkg, (char *)pe->metric_expr, - (char *)pe->metric_name); + (char *)pe->metric_name, + (char *)pe->deprecated); } } @@ -1383,7 +1389,7 @@ static void wordwrap(char *s, int start, int max, int corr) } void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc, bool details_flag) + bool long_desc, bool details_flag, bool deprecated) { struct perf_pmu *pmu; struct perf_pmu_alias *alias; @@ -1414,6 +1420,9 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, format_alias(buf, sizeof(buf), pmu, alias); bool is_cpu = !strcmp(pmu->name, "cpu"); + if (alias->deprecated && !deprecated) + continue; + if (event_glob != NULL && !(strglobmatch_nocase(name, event_glob) || (!is_cpu && strglobmatch_nocase(alias->name, diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index f36ade6df76d..3e8cd31a89cc 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -57,6 +57,7 @@ struct perf_pmu_alias { double scale; bool per_pkg; bool snapshot; + bool deprecated; char *metric_expr; char *metric_name; }; @@ -85,7 +86,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); void print_pmu_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc, bool details_flag); + bool long_desc, bool details_flag, + bool deprecated); bool pmu_have_event(const char *pname, const char *name); int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4); From 12d795637ba169650ea10ad6babcc5425255944a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 18 Oct 2019 16:55:29 +0800 Subject: [PATCH 40/57] perf tests: Remove needless headers for bp_account A few headers are not needed and were introduced by copying from other test file. This patch removes the needless headers for the breakpoint accounting testing. Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Brajeswar Ghosh Cc: Florian Fainelli Cc: Jiri Olsa Cc: Mark Rutland Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Souptick Joarder Cc: Will Deacon Link: http://lore.kernel.org/lkml/20191018085531.6348-1-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bp_account.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 016bba2c142d..52ff7a462670 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -10,11 +10,7 @@ #include #include #include -#include #include -#include -#include -#include #include #include "tests.h" From e533eadf6596451880f518949cbb964dbd6189ae Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 18 Oct 2019 16:55:30 +0800 Subject: [PATCH 41/57] perf tests bp_account: Add dedicated checking helper is_supported() The arm architecture supports breakpoint accounting but it doesn't support breakpoint overflow signal handling. The current code uses the same checking helper, thus it disables both testings (bp_account and bp_signal) for arm platform. For handling two testings separately, this patch adds a dedicated checking helper is_supported() for breakpoint accounting testing, thus it allows supporting breakpoint accounting testing on arm platform; the old helper test__bp_signal_is_supported() is only used to checking for breakpoint overflow signal testing. Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Brajeswar Ghosh Cc: Florian Fainelli Cc: Jiri Olsa Cc: Mark Rutland Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Souptick Joarder Cc: Will Deacon Link: http://lore.kernel.org/lkml/20191018085531.6348-2-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bp_account.c | 16 ++++++++++++++++ tools/perf/tests/builtin-test.c | 2 +- tools/perf/tests/tests.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 52ff7a462670..d0b935356274 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -188,3 +188,19 @@ int test__bp_accounting(struct test *test __maybe_unused, int subtest __maybe_un return bp_accounting(wp_cnt, share); } + +bool test__bp_account_is_supported(void) +{ + /* + * PowerPC and S390 do not support creation of instruction + * breakpoints using the perf_event interface. + * + * Just disable the test for these architectures until these + * issues are resolved. + */ +#if defined(__powerpc__) || defined(__s390x__) + return false; +#else + return true; +#endif +} diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 55774baffc2a..8b286e9b7549 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -121,7 +121,7 @@ static struct test generic_tests[] = { { .desc = "Breakpoint accounting", .func = test__bp_accounting, - .is_supported = test__bp_signal_is_supported, + .is_supported = test__bp_account_is_supported, }, { .desc = "Watchpoint", diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 72912eb473cb..9837b6e93023 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -111,6 +111,7 @@ int test__map_groups__merge_in(struct test *t, int subtest); int test__time_utils(struct test *t, int subtest); bool test__bp_signal_is_supported(void); +bool test__bp_account_is_supported(void); bool test__wp_is_supported(void); #if defined(__arm__) || defined(__aarch64__) From 6a5f3d94cb69a185b921cb92c39888dc31009acb Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Fri, 18 Oct 2019 16:55:31 +0800 Subject: [PATCH 42/57] perf tests: Disable bp_signal testing for arm64 As there are several discussions for enabling perf breakpoint signal testing on arm64 platform: arm64 needs to rely on single-step to execute the breakpointed instruction and then reinstall the breakpoint exception handler. But if we hook the breakpoint with a signal, the signal handler will do the stepping rather than the breakpointed instruction, this causes infinite loops as below: Kernel space | Userspace ---------------------------------|-------------------------------- | __test_function() -> hit | breakpoint breakpoint_handler() | `-> user_enable_single_step() | do_signal() | | sig_handler() -> Step one | instruction and | trap to kernel single_step_handler() | `-> reinstall_suspended_bps() | | __test_function() -> hit | breakpoint again and | repeat up flow infinitely As Will Deacon mentioned [1]: "that we require the overflow handler to do the stepping on arm/arm64, which is relied upon by GDB/ptrace. The hw_breakpoint code is a complete disaster so my preference would be to rip out the perf part and just implement something directly in ptrace, but it's a pretty horrible job". Though Will commented this on arm architecture, but the comment also can apply on arm64 architecture. For complete information, I searched online and found a few years back, Wang Nan sent one patch 'arm64: Store breakpoint single step state into pstate' [2]; the patch tried to resolve this issue by avoiding single stepping in signal handler and defer to enable the signal stepping when return to __test_function(). The fixing was not merged due to the concern for missing to handle different usage cases. Based on the info, the most feasible way is to skip Perf breakpoint signal testing for arm64 and this could avoid the duplicate investigation efforts when people see the failure. This patch skips this case on arm64 platform, which is same with arm architecture. [1] https://lkml.org/lkml/2018/11/15/205 [2] https://lkml.org/lkml/2015/12/23/477 Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Brajeswar Ghosh Cc: Florian Fainelli Cc: Jiri Olsa Cc: Mark Rutland Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Cc: Souptick Joarder Cc: Will Deacon Link: http://lore.kernel.org/lkml/20191018085531.6348-3-leo.yan@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bp_signal.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index c1c2c13de254..166f411568a5 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -49,14 +49,6 @@ asm ( "__test_function:\n" "incq (%rdi)\n" "ret\n"); -#elif defined (__aarch64__) -extern void __test_function(volatile long *ptr); -asm ( - ".globl __test_function\n" - "__test_function:\n" - "str x30, [x0]\n" - "ret\n"); - #else static void __test_function(volatile long *ptr) { @@ -302,10 +294,15 @@ bool test__bp_signal_is_supported(void) * stepping into the SIGIO handler and getting stuck on the * breakpointed instruction. * + * Since arm64 has the same issue with arm for the single-step + * handling, this case also gets suck on the breakpointed + * instruction. + * * Just disable the test for these architectures until these * issues are resolved. */ -#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) +#if defined(__powerpc__) || defined(__s390x__) || defined(__arm__) || \ + defined(__aarch64__) return false; #else return true; From 6eb65f7a5cc553f5dffb5cea3a874f1087524d99 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:09 +0200 Subject: [PATCH 43/57] libperf: Introduce perf_evlist__for_each_mmap() Add the perf_evlist__for_each_mmap() function and export it in the perf/evlist.h header, so that the user can iterate through 'struct perf_mmap' objects. Add a internal perf_mmap__link() function to do the actual linking. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-2-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/evlist.c | 26 +++++++++++++++++++++++- tools/perf/lib/include/internal/evlist.h | 2 ++ tools/perf/lib/include/internal/mmap.h | 5 +++-- tools/perf/lib/include/perf/evlist.h | 9 ++++++++ tools/perf/lib/libperf.map | 1 + tools/perf/lib/mmap.c | 6 ++++-- tools/perf/util/evlist.c | 4 +++- 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index 65045614c938..854efff1519d 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -347,6 +347,8 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo return NULL; for (i = 0; i < evlist->nr_mmaps; i++) { + struct perf_mmap *prev = i ? &map[i - 1] : NULL; + /* * When the perf_mmap() call is made we grab one refcount, plus * one extra to let perf_mmap__consume() get the last @@ -356,7 +358,7 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and * thus does perf_mmap__get() on it. */ - perf_mmap__init(&map[i], overwrite, NULL); + perf_mmap__init(&map[i], prev, overwrite, NULL); } return map; @@ -405,6 +407,15 @@ perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp, return perf_mmap__mmap(map, mp, output, cpu); } +static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (overwrite) + evlist->mmap_ovw_first = map; + else + evlist->mmap_first = map; +} + static int mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, int idx, struct perf_mmap_param *mp, int cpu_idx, @@ -460,6 +471,9 @@ mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, if (ops->mmap(map, mp, *output, evlist_cpu) < 0) return -1; + + if (!idx) + perf_evlist__set_mmap_first(evlist, map, overwrite); } else { if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) return -1; @@ -605,3 +619,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist) zfree(&evlist->mmap); zfree(&evlist->mmap_ovw); } + +struct perf_mmap* +perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (map) + return map->next; + + return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first; +} diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h index be0b25a70730..20d90e29fc0e 100644 --- a/tools/perf/lib/include/internal/evlist.h +++ b/tools/perf/lib/include/internal/evlist.h @@ -25,6 +25,8 @@ struct perf_evlist { struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; struct perf_mmap *mmap; struct perf_mmap *mmap_ovw; + struct perf_mmap *mmap_first; + struct perf_mmap *mmap_ovw_first; }; typedef void diff --git a/tools/perf/lib/include/internal/mmap.h b/tools/perf/lib/include/internal/mmap.h index ee536c4441bb..be7556e0a2b2 100644 --- a/tools/perf/lib/include/internal/mmap.h +++ b/tools/perf/lib/include/internal/mmap.h @@ -32,6 +32,7 @@ struct perf_mmap { u64 flush; libperf_unmap_cb_t unmap_cb; char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); + struct perf_mmap *next; }; struct perf_mmap_param { @@ -41,8 +42,8 @@ struct perf_mmap_param { size_t perf_mmap__mmap_len(struct perf_mmap *map); -void perf_mmap__init(struct perf_mmap *map, bool overwrite, - libperf_unmap_cb_t unmap_cb); +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb); int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, int fd, int cpu); void perf_mmap__munmap(struct perf_mmap *map); diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h index 16f526e74d13..8c4b3c28535e 100644 --- a/tools/perf/lib/include/perf/evlist.h +++ b/tools/perf/lib/include/perf/evlist.h @@ -3,6 +3,7 @@ #define __LIBPERF_EVLIST_H #include +#include struct perf_evlist; struct perf_evsel; @@ -38,4 +39,12 @@ LIBPERF_API int perf_evlist__filter_pollfd(struct perf_evlist *evlist, LIBPERF_API int perf_evlist__mmap(struct perf_evlist *evlist, int pages); LIBPERF_API void perf_evlist__munmap(struct perf_evlist *evlist); +LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, + struct perf_mmap *map, + bool overwrite); +#define perf_evlist__for_each_mmap(evlist, pos, overwrite) \ + for ((pos) = perf_evlist__next_mmap((evlist), NULL, overwrite); \ + (pos) != NULL; \ + (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite)) + #endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map index 2184aba36c3f..8be02afc324b 100644 --- a/tools/perf/lib/libperf.map +++ b/tools/perf/lib/libperf.map @@ -43,6 +43,7 @@ LIBPERF_0.0.1 { perf_evlist__mmap; perf_evlist__munmap; perf_evlist__filter_pollfd; + perf_evlist__next_mmap; perf_mmap__consume; perf_mmap__read_init; perf_mmap__read_done; diff --git a/tools/perf/lib/mmap.c b/tools/perf/lib/mmap.c index 0752c193b0fb..79d5ed6c38cc 100644 --- a/tools/perf/lib/mmap.c +++ b/tools/perf/lib/mmap.c @@ -13,13 +13,15 @@ #include #include "internal.h" -void perf_mmap__init(struct perf_mmap *map, bool overwrite, - libperf_unmap_cb_t unmap_cb) +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb) { map->fd = -1; map->overwrite = overwrite; map->unmap_cb = unmap_cb; refcount_set(&map->refcnt, 0); + if (prev) + prev->next = map; } size_t perf_mmap__mmap_len(struct perf_mmap *map) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0f9cd703e725..6cda5a311ba5 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -607,6 +607,8 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, return NULL; for (i = 0; i < evlist->core.nr_mmaps; i++) { + struct perf_mmap *prev = i ? &map[i - 1].core : NULL; + /* * When the perf_mmap() call is made we grab one refcount, plus * one extra to let perf_mmap__consume() get the last @@ -616,7 +618,7 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and * thus does perf_mmap__get() on it. */ - perf_mmap__init(&map[i].core, overwrite, perf_mmap__unmap_cb); + perf_mmap__init(&map[i].core, prev, overwrite, perf_mmap__unmap_cb); } return map; From 3805e4f303314c2b53fb217dd8549a5b9eb06b11 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:10 +0200 Subject: [PATCH 44/57] libperf: Move mmap allocation to perf_evlist__mmap_ops::get Move allocation of the mmap array into perf_evlist__mmap_ops::get, to centralize the mmap allocation. Also move nr_mmap setup to perf_evlist__mmap_ops so it's centralized and shared by both perf and libperf mmap code. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-3-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/evlist.c | 42 ++++++++++++++++++++++++---------------- tools/perf/util/evlist.c | 24 +++++++++-------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index 854efff1519d..73aac6bb2ac5 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -338,10 +338,6 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo int i; struct perf_mmap *map; - evlist->nr_mmaps = perf_cpu_map__nr(evlist->cpus); - if (perf_cpu_map__empty(evlist->cpus)) - evlist->nr_mmaps = perf_thread_map__nr(evlist->threads); - map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); if (!map) return NULL; @@ -384,18 +380,22 @@ static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, static struct perf_mmap* perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx) { - struct perf_mmap *map = &evlist->mmap[idx]; + struct perf_mmap *maps; - if (overwrite) { - if (!evlist->mmap_ovw) { - evlist->mmap_ovw = perf_evlist__alloc_mmap(evlist, true); - if (!evlist->mmap_ovw) - return NULL; - } - map = &evlist->mmap_ovw[idx]; + maps = overwrite ? evlist->mmap_ovw : evlist->mmap; + + if (!maps) { + maps = perf_evlist__alloc_mmap(evlist, overwrite); + if (!maps) + return NULL; + + if (overwrite) + evlist->mmap_ovw = maps; + else + evlist->mmap = maps; } - return map; + return &maps[idx]; } #define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) @@ -556,6 +556,17 @@ out_unmap: return -1; } +static int perf_evlist__nr_mmaps(struct perf_evlist *evlist) +{ + int nr_mmaps; + + nr_mmaps = perf_cpu_map__nr(evlist->cpus); + if (perf_cpu_map__empty(evlist->cpus)) + nr_mmaps = perf_thread_map__nr(evlist->threads); + + return nr_mmaps; +} + int perf_evlist__mmap_ops(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, struct perf_mmap_param *mp) @@ -567,10 +578,7 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist, if (!ops || !ops->get || !ops->mmap) return -EINVAL; - if (!evlist->mmap) - evlist->mmap = perf_evlist__alloc_mmap(evlist, false); - if (!evlist->mmap) - return -ENOMEM; + evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); perf_evlist__for_each_entry(evlist, evsel) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 6cda5a311ba5..5cded4ec5806 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -599,9 +599,6 @@ static struct mmap *evlist__alloc_mmap(struct evlist *evlist, int i; struct mmap *map; - evlist->core.nr_mmaps = perf_cpu_map__nr(evlist->core.cpus); - if (perf_cpu_map__empty(evlist->core.cpus)) - evlist->core.nr_mmaps = perf_thread_map__nr(evlist->core.threads); map = zalloc(evlist->core.nr_mmaps * sizeof(struct mmap)); if (!map) return NULL; @@ -639,19 +636,21 @@ static struct perf_mmap* perf_evlist__mmap_cb_get(struct perf_evlist *_evlist, bool overwrite, int idx) { struct evlist *evlist = container_of(_evlist, struct evlist, core); - struct mmap *maps = evlist->mmap; + struct mmap *maps; - if (overwrite) { - maps = evlist->overwrite_mmap; + maps = overwrite ? evlist->overwrite_mmap : evlist->mmap; - if (!maps) { - maps = evlist__alloc_mmap(evlist, true); - if (!maps) - return NULL; + if (!maps) { + maps = evlist__alloc_mmap(evlist, overwrite); + if (!maps) + return NULL; + if (overwrite) { evlist->overwrite_mmap = maps; if (evlist->bkw_mmap_state == BKW_MMAP_NOTREADY) perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_RUNNING); + } else { + evlist->mmap = maps; } } @@ -812,11 +811,6 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned int pages, .mmap = perf_evlist__mmap_cb_mmap, }; - if (!evlist->mmap) - evlist->mmap = evlist__alloc_mmap(evlist, false); - if (!evlist->mmap) - return -ENOMEM; - evlist->core.mmap_len = evlist__mmap_size(pages); pr_debug("mmap size %zuB\n", evlist->core.mmap_len); mp.core.mask = evlist->core.mmap_len - page_size - 1; From b6cd35e4e09c12f9478ed98cb015d4352fa98056 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:11 +0200 Subject: [PATCH 45/57] libperf: Move mask setup to perf_evlist__mmap_ops() Move the mask setup to perf_evlist__mmap_ops(), because it's the same on both perf and libperf path. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-4-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/evlist.c | 3 ++- tools/perf/util/evlist.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c index 73aac6bb2ac5..205ddbb80bc1 100644 --- a/tools/perf/lib/evlist.c +++ b/tools/perf/lib/evlist.c @@ -578,6 +578,8 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist, if (!ops || !ops->get || !ops->mmap) return -EINVAL; + mp->mask = evlist->mmap_len - page_size - 1; + evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); perf_evlist__for_each_entry(evlist, evsel) { @@ -605,7 +607,6 @@ int perf_evlist__mmap(struct perf_evlist *evlist, int pages) }; evlist->mmap_len = (pages + 1) * page_size; - mp.mask = evlist->mmap_len - page_size - 1; return perf_evlist__mmap_ops(evlist, &ops, &mp); } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 5cded4ec5806..fdce590d2278 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -813,7 +813,6 @@ int evlist__mmap_ex(struct evlist *evlist, unsigned int pages, evlist->core.mmap_len = evlist__mmap_size(pages); pr_debug("mmap size %zuB\n", evlist->core.mmap_len); - mp.core.mask = evlist->core.mmap_len - page_size - 1; auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->core.mmap_len, auxtrace_pages, auxtrace_overwrite); From 395e62cde10df64122d708c68baee64b1d1622fc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:12 +0200 Subject: [PATCH 46/57] libperf: Link static tests with libapi.a Both static and dynamic tests needs to link with libapi.a, because it's using its functions. Also include path for libapi includes. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-5-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/Makefile | 1 + tools/perf/lib/tests/Makefile | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/lib/Makefile b/tools/perf/lib/Makefile index 0889c9c3ec19..0f233638ef1f 100644 --- a/tools/perf/lib/Makefile +++ b/tools/perf/lib/Makefile @@ -107,6 +107,7 @@ else endif LIBAPI = $(API_PATH)libapi.a +export LIBAPI $(LIBAPI): FORCE $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a diff --git a/tools/perf/lib/tests/Makefile b/tools/perf/lib/tests/Makefile index 1ee4e9ba848b..a43cd08c5c03 100644 --- a/tools/perf/lib/tests/Makefile +++ b/tools/perf/lib/tests/Makefile @@ -16,13 +16,13 @@ all: include $(srctree)/tools/scripts/Makefile.include -INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include +INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/lib $(TESTS_A): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) $(TESTS_SO): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) -lperf + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) $(LIBAPI) -lperf all: $(TESTS_A) $(TESTS_SO) From bd6b7736c1ed109f4d86f725e96a48fb81ce71f6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:14 +0200 Subject: [PATCH 47/57] libperf: Add tests_mmap_thread test Add mmaping tests that generates 100 prctl calls in monitored child process and validates it gets 100 events in ring buffer. Committer tests: # make -C tools/perf/lib tests make: Entering directory '/home/acme/git/perf/tools/perf/lib' LINK test-cpumap-a LINK test-threadmap-a LINK test-evlist-a LINK test-evsel-a LINK test-cpumap-so LINK test-threadmap-so LINK test-evlist-so LINK test-evsel-so running static: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...OK - running test-evsel.c...OK running dynamic: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...OK - running test-evsel.c...OK make: Leaving directory '/home/acme/git/perf/tools/perf/lib' # Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-7-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/tests/test-evlist.c | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c index e6b2ab2e2bde..90a1869ba4b1 100644 --- a/tools/perf/lib/tests/test-evlist.c +++ b/tools/perf/lib/tests/test-evlist.c @@ -1,12 +1,21 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include +#include #include +#include +#include +#include +#include #include #include #include #include +#include +#include #include +#include static int libperf_print(enum libperf_print_level level, const char *fmt, va_list ap) @@ -181,6 +190,115 @@ static int test_stat_thread_enable(void) return 0; } +static int test_mmap_thread(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + char path[PATH_MAX]; + int id, err, pid, go_pipe[2]; + union perf_event *event; + char bf; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + err = pipe(go_pipe); + __T("failed to create pipe", err == 0); + + fflush(NULL); + + pid = fork(); + if (!pid) { + int i; + + read(go_pipe[0], &bf, 1); + + /* Generate 100 prctl calls. */ + for (i = 0; i < 100; i++) + prctl(0, 0, 0, 0, 0); + + exit(0); + } + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + cpus = perf_cpu_map__dummy_new(); + __T("failed to create cpus", cpus); + + perf_thread_map__set_pid(threads, 0, pid); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, threads); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + /* kick the child and wait for it to finish */ + write(go_pipe[1], &bf, 1); + waitpid(pid, NULL, 0); + + /* + * There's no need to call perf_evlist__disable, + * monitored process is dead now. + */ + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + perf_thread_map__put(threads); + perf_cpu_map__put(cpus); + + /* + * The generated prctl calls should match the + * number of events in the buffer. + */ + __T("failed count", count == 100); + + return 0; +} + int main(int argc, char **argv) { __T_START; @@ -190,6 +308,7 @@ int main(int argc, char **argv) test_stat_cpu(); test_stat_thread(); test_stat_thread_enable(); + test_mmap_thread(); __T_OK; return 0; From 37ac1bbdc31a2007f398b7caf0cbe522f1af9c6c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:15 +0200 Subject: [PATCH 48/57] libperf: Add tests_mmap_cpus test Add mmaping tests that generates prctl call on every cpu validates it gets all the related events in ring buffer. Committer testing: # make -C tools/perf/lib tests make: Entering directory '/home/acme/git/perf/tools/perf/lib' LINK test-cpumap-a LINK test-threadmap-a LINK test-evlist-a LINK test-evsel-a LINK test-cpumap-so LINK test-threadmap-so LINK test-evlist-so LINK test-evsel-so running static: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...OK - running test-evsel.c...OK running dynamic: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...OK - running test-evsel.c...OK make: Leaving directory '/home/acme/git/perf/tools/perf/lib' # Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-8-jolsa@kernel.org [ Added _GNU_SOURCE define for sched.h to get sched_[gs]et_affinity Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/tests/test-evlist.c | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c index 90a1869ba4b1..741bc1bb4524 100644 --- a/tools/perf/lib/tests/test-evlist.c +++ b/tools/perf/lib/tests/test-evlist.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET) +#include #include #include #include @@ -299,6 +301,101 @@ static int test_mmap_thread(void) return 0; } +static int test_mmap_cpus(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + cpu_set_t saved_mask; + char path[PATH_MAX]; + int id, err, cpu, tmp; + union perf_event *event; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + cpus = perf_cpu_map__new(NULL); + __T("failed to create cpus", cpus); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, NULL); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_getaffinity failed", err == 0); + + perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + + err = sched_setaffinity(0, sizeof(mask), &mask); + __T("sched_setaffinity failed", err == 0); + + prctl(0, 0, 0, 0, 0); + } + + err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_setaffinity failed", err == 0); + + perf_evlist__disable(evlist); + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + /* + * The generated prctl events should match the + * number of cpus or be bigger (we are system-wide). + */ + __T("failed count", count >= perf_cpu_map__nr(cpus)); + + perf_cpu_map__put(cpus); + + return 0; +} + int main(int argc, char **argv) { __T_START; @@ -309,6 +406,7 @@ int main(int argc, char **argv) test_stat_thread(); test_stat_thread_enable(); test_mmap_thread(); + test_mmap_cpus(); __T_OK; return 0; From 301a89f8cf628316eea6c768787a836b63a83439 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:16 +0200 Subject: [PATCH 49/57] libperf: Keep count of failed tests Keep the count of failed tests, so we get better output with failures, like: # make tests ... running static: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...FAILED test-evlist.c:53 failed to create evsel2 FAILED test-evlist.c:163 failed to create evsel2 FAILED test-evlist.c:287 failed count FAILED (3) - running test-evsel.c...OK running dynamic: - running test-cpumap.c...OK - running test-threadmap.c...OK - running test-evlist.c...FAILED test-evlist.c:53 failed to create evsel2 FAILED test-evlist.c:163 failed to create evsel2 FAILED test-evlist.c:287 failed count FAILED (3) - running test-evsel.c...OK ... Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-9-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/include/internal/tests.h | 20 +++++++++++++++++--- tools/perf/lib/tests/test-cpumap.c | 2 +- tools/perf/lib/tests/test-evlist.c | 2 +- tools/perf/lib/tests/test-evsel.c | 2 +- tools/perf/lib/tests/test-threadmap.c | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/tools/perf/lib/include/internal/tests.h b/tools/perf/lib/include/internal/tests.h index b7a20cd24ee1..2093e8868a67 100644 --- a/tools/perf/lib/include/internal/tests.h +++ b/tools/perf/lib/include/internal/tests.h @@ -4,14 +4,28 @@ #include -#define __T_START fprintf(stdout, "- running %s...", __FILE__) -#define __T_OK fprintf(stdout, "OK\n") -#define __T_FAIL fprintf(stdout, "FAIL\n") +int tests_failed; + +#define __T_START \ +do { \ + fprintf(stdout, "- running %s...", __FILE__); \ + fflush(NULL); \ + tests_failed = 0; \ +} while (0) + +#define __T_END \ +do { \ + if (tests_failed) \ + fprintf(stdout, " FAILED (%d)\n", tests_failed); \ + else \ + fprintf(stdout, "OK\n"); \ +} while (0) #define __T(text, cond) \ do { \ if (!(cond)) { \ fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + tests_failed++; \ return -1; \ } \ } while (0) diff --git a/tools/perf/lib/tests/test-cpumap.c b/tools/perf/lib/tests/test-cpumap.c index aa34c20df07e..c8d45091e7c2 100644 --- a/tools/perf/lib/tests/test-cpumap.c +++ b/tools/perf/lib/tests/test-cpumap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv) perf_cpu_map__put(cpus); perf_cpu_map__put(cpus); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c index 741bc1bb4524..6d8ebe0c2504 100644 --- a/tools/perf/lib/tests/test-evlist.c +++ b/tools/perf/lib/tests/test-evlist.c @@ -408,6 +408,6 @@ int main(int argc, char **argv) test_mmap_thread(); test_mmap_cpus(); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-evsel.c b/tools/perf/lib/tests/test-evsel.c index 1b6c4285ac2b..135722ac965b 100644 --- a/tools/perf/lib/tests/test-evsel.c +++ b/tools/perf/lib/tests/test-evsel.c @@ -130,6 +130,6 @@ int main(int argc, char **argv) test_stat_thread(); test_stat_thread_enable(); - __T_OK; + __T_END; return 0; } diff --git a/tools/perf/lib/tests/test-threadmap.c b/tools/perf/lib/tests/test-threadmap.c index 8c5f47247d9e..7dc4d6fbedde 100644 --- a/tools/perf/lib/tests/test-threadmap.c +++ b/tools/perf/lib/tests/test-threadmap.c @@ -26,6 +26,6 @@ int main(int argc, char **argv) perf_thread_map__put(threads); perf_thread_map__put(threads); - __T_OK; + __T_END; return 0; } From c27feefea10aed40e82cd5c6b06a154e141dc038 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:17 +0200 Subject: [PATCH 50/57] libperf: Do not export perf_evsel__init()/perf_evlist__init() There's no point in exporting perf_evsel__init()/perf_evlist__init(), it's called from perf_evsel__new()/perf_evlist__new() respectively. It's used only from perf where perf_evsel()/perf_evlist() is embedded perf's evsel/evlist. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-10-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/include/internal/evlist.h | 1 + tools/perf/lib/include/internal/evsel.h | 1 + tools/perf/lib/include/perf/evlist.h | 1 - tools/perf/lib/include/perf/evsel.h | 2 -- tools/perf/lib/libperf.map | 2 -- 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h index 20d90e29fc0e..a2fbccf1922f 100644 --- a/tools/perf/lib/include/internal/evlist.h +++ b/tools/perf/lib/include/internal/evlist.h @@ -50,6 +50,7 @@ int perf_evlist__mmap_ops(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, struct perf_mmap_param *mp); +void perf_evlist__init(struct perf_evlist *evlist); void perf_evlist__exit(struct perf_evlist *evlist); /** diff --git a/tools/perf/lib/include/internal/evsel.h b/tools/perf/lib/include/internal/evsel.h index a69b8299c36f..1ffd083b235e 100644 --- a/tools/perf/lib/include/internal/evsel.h +++ b/tools/perf/lib/include/internal/evsel.h @@ -50,6 +50,7 @@ struct perf_evsel { bool system_wide; }; +void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); void perf_evsel__close_fd(struct perf_evsel *evsel); void perf_evsel__free_fd(struct perf_evsel *evsel); diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h index 8c4b3c28535e..0a7479dc13bf 100644 --- a/tools/perf/lib/include/perf/evlist.h +++ b/tools/perf/lib/include/perf/evlist.h @@ -10,7 +10,6 @@ struct perf_evsel; struct perf_cpu_map; struct perf_thread_map; -LIBPERF_API void perf_evlist__init(struct perf_evlist *evlist); LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *evsel); LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist, diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h index 4388667f265c..557f5815a9c9 100644 --- a/tools/perf/lib/include/perf/evsel.h +++ b/tools/perf/lib/include/perf/evsel.h @@ -21,8 +21,6 @@ struct perf_counts_values { }; }; -LIBPERF_API void perf_evsel__init(struct perf_evsel *evsel, - struct perf_event_attr *attr); LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel); LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map index 8be02afc324b..7be1af8a546c 100644 --- a/tools/perf/lib/libperf.map +++ b/tools/perf/lib/libperf.map @@ -21,7 +21,6 @@ LIBPERF_0.0.1 { perf_evsel__delete; perf_evsel__enable; perf_evsel__disable; - perf_evsel__init; perf_evsel__open; perf_evsel__close; perf_evsel__read; @@ -34,7 +33,6 @@ LIBPERF_0.0.1 { perf_evlist__close; perf_evlist__enable; perf_evlist__disable; - perf_evlist__init; perf_evlist__add; perf_evlist__remove; perf_evlist__next; From dcc6854215f115efcbd79368bc07099c41de0b6c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Oct 2019 12:59:18 +0200 Subject: [PATCH 51/57] libperf: Add pr_err() macro And missing include for "perf/core.h" header, which provides LIBPERF_* debug levels and add missing pr_err() support. Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Alexey Budankov Cc: Andi Kleen Cc: Jin Yao Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lore.kernel.org/lkml/20191017105918.20873-11-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/lib/include/perf/core.h | 1 + tools/perf/lib/internal.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/tools/perf/lib/include/perf/core.h b/tools/perf/lib/include/perf/core.h index 2a80e4b6f819..a3f6d68edad7 100644 --- a/tools/perf/lib/include/perf/core.h +++ b/tools/perf/lib/include/perf/core.h @@ -9,6 +9,7 @@ #endif enum libperf_print_level { + LIBPERF_ERR, LIBPERF_WARN, LIBPERF_INFO, LIBPERF_DEBUG, diff --git a/tools/perf/lib/internal.h b/tools/perf/lib/internal.h index 37db745e1502..2c27e158de6b 100644 --- a/tools/perf/lib/internal.h +++ b/tools/perf/lib/internal.h @@ -2,6 +2,8 @@ #ifndef __LIBPERF_INTERNAL_H #define __LIBPERF_INTERNAL_H +#include + void libperf_print(enum libperf_print_level level, const char *format, ...) __attribute__((format(printf, 2, 3))); @@ -11,6 +13,7 @@ do { \ libperf_print(level, "libperf: " fmt, ##__VA_ARGS__); \ } while (0) +#define pr_err(fmt, ...) __pr(LIBPERF_ERR, fmt, ##__VA_ARGS__) #define pr_warning(fmt, ...) __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__) #define pr_info(fmt, ...) __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__) #define pr_debug(fmt, ...) __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__) From 1a8a90b823f5e7279f84b39bbcd6c59e266e7dc2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 18 Oct 2019 15:41:07 -0300 Subject: [PATCH 52/57] libbeauty: Introduce syscall_arg__strtoul_strarrays() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow going from string to integer for 'struct strarrays'. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-b1ia3xzcy72hv0u4m168fcd0@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 5 +++++ tools/perf/trace/beauty/beauty.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 0e7fc7cc42d9..265ea876f00b 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -540,6 +540,11 @@ bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *ar return strarray__strtoul(arg->parm, bf, size, ret); } +bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarrays__strtoul(arg->parm, bf, size, ret); +} + size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct syscall_arg *arg) { return strarray__scnprintf_flags(arg->parm, bf, size, arg->show_string_prefix, arg->val); diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 1b8a30e5dcf9..10801660a71f 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -125,6 +125,9 @@ size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct sysca bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); #define STUL_STRARRAY syscall_arg__strtoul_strarray +bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAYS syscall_arg__strtoul_strarrays + size_t syscall_arg__scnprintf_x86_irq_vectors(char *bf, size_t size, struct syscall_arg *arg); #define SCA_X86_IRQ_VECTORS syscall_arg__scnprintf_x86_irq_vectors From 82c38338e0850a01057960efc94c6130f1a0fdde Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 18 Oct 2019 15:44:42 -0300 Subject: [PATCH 53/57] perf trace: Use strtoul for the fcntl 'cmd' argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since its values are in two ranges of values we ended up codifying it using a 'struct strarrays', so now hook it up with STUL_STRARRAYS so that we can do: # perf trace -e syscalls:*enter_fcntl --filter=cmd==SETLK||cmd==SETLKW 0.000 sssd_kcm/19021 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLK, arg: 0x7ffcf0a4dee0) 1.523 sssd_kcm/19021 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLK, arg: 0x7ffcf0a4de90) 1.629 sssd_kcm/19021 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLK, arg: 0x7ffcf0a4de90) 2.711 sssd_kcm/19021 syscalls:sys_enter_fcntl(fd: 13, cmd: SETLK, arg: 0x7ffcf0a4de70) ^C# Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-mob96wyzri4r3rvyigqfjv0a@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 265ea876f00b..72ef3b395504 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -894,7 +894,8 @@ static struct syscall_fmt syscall_fmts[] = { { .name = "fchownat", .arg = { [0] = { .scnprintf = SCA_FDAT, /* fd */ }, }, }, { .name = "fcntl", - .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ + .arg = { [1] = { .scnprintf = SCA_FCNTL_CMD, /* cmd */ + .strtoul = STUL_STRARRAYS, .parm = &strarrays__fcntl_cmds_arrays, .show_zero = true, }, [2] = { .scnprintf = SCA_FCNTL_ARG, /* arg */ }, }, }, From f77526be82fcc31cb0d54796dd8f8476472b05d0 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 19 Oct 2019 15:13:21 -0300 Subject: [PATCH 54/57] libbeauty: Make the mmap_flags strarray visible outside of its beautifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can later use it with the strarray__strtoul_flags() routine that will be soon introduced. Cc: Adrian Hunter Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-vldj3ch8su6i20to5eq31e8x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/mmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/trace/beauty/mmap.c b/tools/perf/trace/beauty/mmap.c index 859a8a9db2c6..9fa771a90d79 100644 --- a/tools/perf/trace/beauty/mmap.c +++ b/tools/perf/trace/beauty/mmap.c @@ -33,11 +33,11 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot -static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix) -{ #include "trace/beauty/generated/mmap_flags_array.c" static DEFINE_STRARRAY(mmap_flags, "MAP_"); +static size_t mmap__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix) +{ return strarray__scnprintf_flags(&strarray__mmap_flags, bf, size, show_prefix, flags); } From 154c978d484c610468727c361576b7cfe9c3fec7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 19 Oct 2019 15:17:30 -0300 Subject: [PATCH 55/57] libbeauty: Introduce strarray__strtoul_flags() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Counterpart of strarray__scnprintf_flags(), i.e. from a expression like: # perf trace -e syscalls:sys_enter_mmap --filter="flags==PRIVATE|FIXED|DENYWRITE" I.e. that "flags==PRIVATE|FIXED|DENYWRITE", turn that into # perf trace -e syscalls:sys_enter_mmap --filter=0x812 Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-8xst3zrqqogax7fmfzwymvbl@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 45 +++++++++++++++++++++++++++++++- tools/perf/trace/beauty/beauty.h | 1 + 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 72ef3b395504..73c5c14b52eb 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -586,6 +586,49 @@ bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret) return false; } +bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret) +{ + u64 val = 0; + char *tok = bf, *sep, *end; + + *ret = 0; + + while (size != 0) { + int toklen = size; + + sep = memchr(tok, '|', size); + if (sep != NULL) { + size -= sep - tok + 1; + + end = sep - 1; + while (end > tok && isspace(*end)) + --end; + + toklen = end - tok + 1; + } + + while (isspace(*tok)) + ++tok; + + if (isalpha(*tok) || *tok == '_') { + if (!strarray__strtoul(sa, tok, toklen, &val)) + return false; + } else { + bool is_hexa = tok[0] == 0 && (tok[1] = 'x' || tok[1] == 'X'); + + val = strtoul(tok, NULL, is_hexa ? 16 : 0); + } + + *ret |= (1 << (val - 1)); + + if (sep == NULL) + break; + tok = sep + 1; + } + + return true; +} + bool strarrays__strtoul(struct strarrays *sas, char *bf, size_t size, u64 *ret) { int i; @@ -3676,7 +3719,7 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel } right_end = right + 1; - while (isalnum(*right_end) || *right_end == '_') + while (isalnum(*right_end) || *right_end == '_' || *right_end == '|') ++right_end; if (isalpha(*right)) { diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 10801660a71f..e12b2228b892 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -32,6 +32,7 @@ size_t strarray__scnprintf_suffix(struct strarray *sa, char *bf, size_t size, co size_t strarray__scnprintf_flags(struct strarray *sa, char *bf, size_t size, bool show_prefix, unsigned long flags); bool strarray__strtoul(struct strarray *sa, char *bf, size_t size, u64 *ret); +bool strarray__strtoul_flags(struct strarray *sa, char *bf, size_t size, u64 *ret); struct trace; struct thread; From e0712baa00322881ee74e28031f12a1cc032f0d4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 19 Oct 2019 15:26:50 -0300 Subject: [PATCH 56/57] perf trace: Wire up strarray__strtoul_flags() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now anything that uses STRARRAY_FLAGS, like the 'fsmount' syscall will support mapping or-ed strings back to a value that can be used in a filter. In some cases, where STRARRAY_FLAGS isn't used but instead the scnprintf is a special one because of specific needs, like for mmap, then one has to set the ->pars to the strarray. See the next cset. Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-r2lpqo7dfsrhi4ll0npsb3u7@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 ++++++ tools/perf/trace/beauty/beauty.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 73c5c14b52eb..7bb84c4a8f29 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -540,6 +540,11 @@ bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *ar return strarray__strtoul(arg->parm, bf, size, ret); } +bool syscall_arg__strtoul_strarray_flags(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) +{ + return strarray__strtoul_flags(arg->parm, bf, size, ret); +} + bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret) { return strarrays__strtoul(arg->parm, bf, size, ret); @@ -882,6 +887,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, #define STRARRAY_FLAGS(name, array) \ { .scnprintf = SCA_STRARRAY_FLAGS, \ + .strtoul = STUL_STRARRAY_FLAGS, \ .parm = &strarray__##array, } #include "trace/beauty/arch_errno_names.c" diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index e12b2228b892..5a61043c2ff7 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -126,6 +126,9 @@ size_t syscall_arg__scnprintf_strarray_flags(char *bf, size_t size, struct sysca bool syscall_arg__strtoul_strarray(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); #define STUL_STRARRAY syscall_arg__strtoul_strarray +bool syscall_arg__strtoul_strarray_flags(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); +#define STUL_STRARRAY_FLAGS syscall_arg__strtoul_strarray_flags + bool syscall_arg__strtoul_strarrays(char *bf, size_t size, struct syscall_arg *arg, u64 *ret); #define STUL_STRARRAYS syscall_arg__strtoul_strarrays From 27198a893ba074407e7a87e346252b3e6fab454f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 19 Oct 2019 15:30:15 -0300 Subject: [PATCH 57/57] perf trace: Use STUL_STRARRAY_FLAGS with mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'mmap' syscall has special needs so it doesn't use SCA_STRARRAY_FLAGS, see its implementation in syscall_arg__scnprintf_mmap_flags(), related to special handling of MAP_ANONYMOUS, so set ->parm to the strarray__mmap_flags and hook up with strarray__strtoul_flags manually, now we can filter by those or-ed string expressions: # perf trace -e syscalls:sys_enter_mmap sleep 1 0.000 syscalls:sys_enter_mmap(addr: NULL, len: 134346, prot: READ, flags: PRIVATE, fd: 3, off: 0) 0.026 syscalls:sys_enter_mmap(addr: NULL, len: 8192, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS) 0.036 syscalls:sys_enter_mmap(addr: NULL, len: 1857472, prot: READ, flags: PRIVATE|DENYWRITE, fd: 3, off: 0) 0.046 syscalls:sys_enter_mmap(addr: 0x7fae003d9000, len: 1363968, prot: READ|EXEC, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x22000) 0.052 syscalls:sys_enter_mmap(addr: 0x7fae00526000, len: 311296, prot: READ, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x16f000) 0.055 syscalls:sys_enter_mmap(addr: 0x7fae00573000, len: 24576, prot: READ|WRITE, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x1bb000) 0.062 syscalls:sys_enter_mmap(addr: 0x7fae00579000, len: 14272, prot: READ|WRITE, flags: PRIVATE|FIXED|ANONYMOUS) 0.253 syscalls:sys_enter_mmap(addr: NULL, len: 217750512, prot: READ, flags: PRIVATE, fd: 3, off: 0) # # perf trace -e syscalls:sys_enter_mmap --filter="flags==PRIVATE|FIXED|DENYWRITE" sleep 1 0.000 syscalls:sys_enter_mmap(addr: 0x7f6ab3dcb000, len: 1363968, prot: READ|EXEC, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x22000) 0.010 syscalls:sys_enter_mmap(addr: 0x7f6ab3f18000, len: 311296, prot: READ, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x16f000) 0.014 syscalls:sys_enter_mmap(addr: 0x7f6ab3f65000, len: 24576, prot: READ|WRITE, flags: PRIVATE|FIXED|DENYWRITE, fd: 3, off: 0x1bb000) # perf trace -e syscalls:sys_enter_mmap --filter="flags==PRIVATE|ANONYMOUS" sleep 1 0.000 syscalls:sys_enter_mmap(addr: NULL, len: 8192, prot: READ|WRITE, flags: PRIVATE|ANONYMOUS) # # perf trace -v -e syscalls:sys_enter_mmap --filter="flags==PRIVATE|ANONYMOUS" sleep 1 |& grep "New filter" New filter for syscalls:sys_enter_mmap: flags==0x22 # Cc: Adrian Hunter Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Jiri Olsa Cc: Luis Cláudio Gonçalves Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-czw754b7m9rp9ibq2f6be2o1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7bb84c4a8f29..43c05eae1768 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1015,7 +1015,9 @@ static struct syscall_fmt syscall_fmts[] = { .alias = "old_mmap", #endif .arg = { [2] = { .scnprintf = SCA_MMAP_PROT, /* prot */ }, - [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ }, + [3] = { .scnprintf = SCA_MMAP_FLAGS, /* flags */ + .strtoul = STUL_STRARRAY_FLAGS, + .parm = &strarray__mmap_flags, }, [5] = { .scnprintf = SCA_HEX, /* offset */ }, }, }, { .name = "mount", .arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ },