From a9e57009dacd58052755cf58463ce41a14a01db5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:33 +0300 Subject: [PATCH 01/27] perf record: Fix documentation 'event_sources' -> 'event_source' Change '/sys/bus/event_sources' to the correct path which is '/sys/bus/event_source'. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 1a24f4d64328..babbb63e6d9d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -35,15 +35,15 @@ OPTIONS - a symbolically formed PMU event like 'pmu/param1=0x3,param2/' where 'param1', 'param2', etc are defined as formats for the PMU in - /sys/bus/event_sources/devices//format/*. + /sys/bus/event_source/devices//format/*. - a symbolically formed event like 'pmu/config=M,config1=N,config3=K/' where M, N, K are numbers (in decimal, hex, octal format). Acceptable values for each of 'config', 'config1' and 'config2' are defined by - corresponding entries in /sys/bus/event_sources/devices//format/* + corresponding entries in /sys/bus/event_source/devices//format/* param1 and param2 are defined as formats for the PMU in: - /sys/bus/event_sources/devices//format/* + /sys/bus/event_source/devices//format/* There are also some params which are not defined in ...//format/*. These params can be used to overload default config values per event. From 973186ca7fe94d770a9847d7d530864de6ab638b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:34 +0300 Subject: [PATCH 02/27] perf tools: Fix MMAP event synthesis broken by MAP_HUGETLB change Patch "perf record: Mark MAP_HUGETLB when synthesizing mmap events") breaks MMAP event synthesis. The executable name comparison will match any name if the length is zero, resulting in all the user space maps becoming anonymous. This is particularly noticeable with system-wide traces. Example: perf record -a sleep 1 perf script --show-mmap-events Committer note: That is not the case when, say, one has a qemu instance and libvirt actually mounts hugetlbfs. To test this I had to first umount it: [root@jouet ~]# mount | grep hugetlbfs hugetlbfs on /dev/hugepages type hugetlbfs (rw,relatime,seclabel) [root@jouet ~]# After unmount it the error fixed by this patch manifests itself: # perf record -a sleep 1 # perf script --show-mmap-events | grep PERF_RECORD_MMAP2 | head -5 systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x557d47ed8000(0x167000) @ 0 fd:00 3146896 7362875424355726126]: r-xp //anon systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c488d000(0x4000) @ 0 fd:00 3153214 7362875424355726126]: r-xp //anon systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4a92000(0x3d000) @ 0 fd:00 3159276 7362875424355726126]: r-xp //anon systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4cd5000(0x15000) @ 0 fd:00 3153725 7362875424355726126]: r-xp //anon systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4eeb000(0x25000) @ 0 fd:00 3153260 7362875424355726126]: r-xp //anon # Fixed version: # perf record -a sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 1.419 MB perf.data (182 samples) ] # perf script --show-mmap-events | grep PERF_RECORD_MMAP2 | head -5 systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x557d47ed8000(0x167000) @ 0 fd:00 3146896 7362875424355726126]: r-xp /usr/lib/systemd/systemd systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c488d000(0x4000) @ 0 fd:00 3153214 7362875424355726126]: r-xp /usr/lib64/libuuid.so.1.3.0 systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4a92000(0x3d000) @ 0 fd:00 3159276 7362875424355726126]: r-xp /usr/lib64/libblkid.so.1.1.0 systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4cd5000(0x15000) @ 0 fd:00 3153725 7362875424355726126]: r-xp /usr/lib64/libz.so.1.2.8 systemd 0 [000] 0.000000: PERF_RECORD_MMAP2 1/1: [0x7f96c4eeb000(0x25000) @ 0 fd:00 3153260 7362875424355726126]: r-xp /usr/lib64/liblzma.so.5.2.2 [root@jouet ~]# Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2880e2226fdb..8ab0d7da956b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -346,7 +346,8 @@ out: if (!strcmp(execname, "")) strcpy(execname, anonstr); - if (!strncmp(execname, hugetlbfs_mnt, hugetlbfs_mnt_len)) { + if (hugetlbfs_mnt_len && + !strncmp(execname, hugetlbfs_mnt, hugetlbfs_mnt_len)) { strcpy(execname, anonstr); event->mmap2.flags |= MAP_HUGETLB; } From f2c8852e6e990fcab0d9e68de9d86e5fbea0b5dc Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 28 Sep 2016 03:58:46 +0000 Subject: [PATCH 03/27] perf data: Fix building in 32 bit platform with libbabeltrace On ARM32 building it report following error when we build with libbabeltrace: util/data-convert-bt.c: In function 'add_bpf_output_values': util/data-convert-bt.c:440:3: error: format '%lu' expects argument of type 'long unsigned int', but argument 5 has type 'unsigned int' [-Werror=format] cc1: all warnings being treated as errors Fix it by changing %lu to %zu. Signed-off-by: Wang Nan Cc: Jiri Olsa Cc: Zefan Li Cc: pi3orama@163.com Fixes: 6122d57e9f7c ("perf data: Support converting data from bpf_perf_event_output()") Link: http://lkml.kernel.org/r/1475035126-146587-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-bt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 4f979bb27b6c..7123f4de32cc 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -437,7 +437,7 @@ add_bpf_output_values(struct bt_ctf_event_class *event_class, int ret; if (nr_elements * sizeof(u32) != raw_size) - pr_warning("Incorrect raw_size (%u) in bpf output event, skip %lu bytes\n", + pr_warning("Incorrect raw_size (%u) in bpf output event, skip %zu bytes\n", raw_size, nr_elements * sizeof(u32) - raw_size); len_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_len"); From f0bbd602268e69f4f428075c645391d64bdd6c9f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 28 Sep 2016 13:45:38 -0300 Subject: [PATCH 04/27] perf trace: Beautify sched_[gs]et_attr return value Both return errno, show the string associated then. More work needed to capture the sched_attr arg to beautify it in turn, probably using BPF. Before: 0.210 ( 0.001 ms): sched_setattr(uattr: 0x7ffc684f02b0) = -22 After the patch, for this sched_attr, all other parms are zero, so not shown: struct sched_attr attr = { .size = sizeof(attr), .sched_policy = SCHED_DEADLINE, .sched_runtime = 10 * USECS_PER_SEC, .sched_period = 30 * USECS_PER_SEC, .sched_deadline = attr.sched_period, }; 0.321 ( 0.002 ms): sched_setattr(uattr: 0x7ffc44116da0) = -1 EINVAL Invalid argument [root@jouet c]# perf trace -e sched_setattr ./sched_deadline Couldn't negotiate deadline: Invalid argument 0.229 ( 0.003 ms): sched_setattr(uattr: 0x7ffd8dcd8df0) = -1 EINVAL Invalid argument [root@jouet c]# Now to figure out the reason for this EINVAL. Cc: Adrian Hunter Cc: Clark Williams Cc: Daniel Bristot de Oliveira Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-tyot2n7e48zm8pdw8tbcm3sl@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index b4fc1ab3d2a7..1a54ce9d101c 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -742,6 +742,8 @@ static struct syscall_fmt { .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, { .name = "rt_tgsigqueueinfo", .errmsg = true, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, + { .name = "sched_getattr", .errmsg = true, }, + { .name = "sched_setattr", .errmsg = true, }, { .name = "sched_setscheduler", .errmsg = true, .arg_scnprintf = { [1] = SCA_SCHED_POLICY, /* policy */ }, }, { .name = "seccomp", .errmsg = true, From 2acad19500c28ce0c4dc3f9bf1dcfc82040b6531 Mon Sep 17 00:00:00 2001 From: Simon Que Date: Wed, 28 Sep 2016 11:37:53 -0700 Subject: [PATCH 05/27] perf tools: Update documentation info about quipper The existing link is outdated. The most recent quipper code can be found at the new URL. Committer notes: Quipper is a C++ parser that can be used to convert from a perf.data file to and from a protobuf, a Chromium OS facility. Signed-off-by: Simon Que Acked-by: Andi Kleen Cc: Adrian Hunter Cc: Chong Jiang Link: http://lkml.kernel.org/n/tip-4q1nm7jl3vovp66p5bki20pq@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf.data-file-format.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index fdc99fe6bbc3..b664b18d3991 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt @@ -437,6 +437,10 @@ in pmu-tools parser. This allows to read perf.data from python and dump it. quipper The quipper C++ parser is available at -https://chromium.googlesource.com/chromiumos/platform/chromiumos-wide-profiling/ +https://chromium.googlesource.com/chromiumos/platform2 + +It is under the chromiumos-wide-profiling/ subdirectory. This library can +convert a perf data file to a protobuf and vice versa. + Unfortunately this parser tends to be many versions behind and may not be able to parse data files generated by recent perf. From b15d0a4c828eafc82ea68fcf88db6fa93eeb23d7 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Fri, 16 Sep 2016 08:44:03 -0600 Subject: [PATCH 06/27] perf tools: Make perf_evsel__append_filter() generic By making function perf_evsel__append_filter() take a format rather than an operator it is possible to reuse the code for other purposes (ex. Intel PT and CoreSight) than tracepoints. Signed-off-by: Mathieu Poirier Acked-by: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Peter Zijlstra Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1474037045-31730-2-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 9 +++++++-- tools/perf/util/evsel.c | 4 ++-- tools/perf/util/evsel.h | 2 +- tools/perf/util/parse-events.c | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1a54ce9d101c..e04ba9d852d4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2143,6 +2143,7 @@ out_delete_sys_enter: static int trace__set_ev_qualifier_filter(struct trace *trace) { int err = -1; + struct perf_evsel *sys_exit; char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier, trace->ev_qualifier_ids.nr, trace->ev_qualifier_ids.entries); @@ -2150,8 +2151,12 @@ static int trace__set_ev_qualifier_filter(struct trace *trace) if (filter == NULL) goto out_enomem; - if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, "&&", filter)) - err = perf_evsel__append_filter(trace->syscalls.events.sys_exit, "&&", filter); + if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, + "(%s) && (%s)", filter)) { + sys_exit = trace->syscalls.events.sys_exit; + err = perf_evsel__append_filter(sys_exit, + "(%s) && (%s)", filter); + } free(filter); out: diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f3225a2e6eee..4f327b522e5c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1046,14 +1046,14 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter) } int perf_evsel__append_filter(struct perf_evsel *evsel, - const char *op, const char *filter) + const char *fmt, const char *filter) { char *new_filter; if (evsel->filter == NULL) return perf_evsel__set_filter(evsel, filter); - if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) { + if (asprintf(&new_filter, fmt, evsel->filter, filter) > 0) { free(evsel->filter); evsel->filter = new_filter; return 0; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 323806082c58..7ef960298b3d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -236,7 +236,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel, int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter); int perf_evsel__append_filter(struct perf_evsel *evsel, - const char *op, const char *filter); + const char *fmt, const char *filter); int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 2eb8b1ed4cc8..b14784c765eb 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1767,7 +1767,7 @@ static int set_filter(struct perf_evsel *evsel, const void *arg) return -1; } - if (perf_evsel__append_filter(evsel, "&&", str) < 0) { + if (perf_evsel__append_filter(evsel, "(%s) && (%s)", str) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); return -1; @@ -1798,7 +1798,7 @@ static int add_exclude_perf_filter(struct perf_evsel *evsel, snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid()); - if (perf_evsel__append_filter(evsel, "&&", new_filter) < 0) { + if (perf_evsel__append_filter(evsel, "(%s) && (%s)", new_filter) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); return -1; From 3541c034d9b953cbd4b961db74630fb6d72e7f37 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Fri, 16 Sep 2016 08:44:04 -0600 Subject: [PATCH 07/27] perf evsel: New tracepoint specific function Making function perf_evsel__append_filter() static and introducing a new tracepoint specific function to append filters. That way we eliminate redundant code and avoid formatting mistake. Signed-off-by: Mathieu Poirier Acked-by: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Peter Zijlstra Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1474037045-31730-3-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 7 +++---- tools/perf/util/evsel.c | 9 +++++++-- tools/perf/util/evsel.h | 3 +-- tools/perf/util/parse-events.c | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e04ba9d852d4..c298bd3e1d90 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2151,11 +2151,10 @@ static int trace__set_ev_qualifier_filter(struct trace *trace) if (filter == NULL) goto out_enomem; - if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, - "(%s) && (%s)", filter)) { + if (!perf_evsel__append_tp_filter(trace->syscalls.events.sys_enter, + filter)) { sys_exit = trace->syscalls.events.sys_exit; - err = perf_evsel__append_filter(sys_exit, - "(%s) && (%s)", filter); + err = perf_evsel__append_tp_filter(sys_exit, filter); } free(filter); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 4f327b522e5c..3b4e7c452e43 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1045,8 +1045,8 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter) return -1; } -int perf_evsel__append_filter(struct perf_evsel *evsel, - const char *fmt, const char *filter) +static int perf_evsel__append_filter(struct perf_evsel *evsel, + const char *fmt, const char *filter) { char *new_filter; @@ -1062,6 +1062,11 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, return -1; } +int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter) +{ + return perf_evsel__append_filter(evsel, "(%s) && (%s)", filter); +} + int perf_evsel__enable(struct perf_evsel *evsel) { int nthreads = thread_map__nr(evsel->threads); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 7ef960298b3d..1f8c48f87f7d 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -235,8 +235,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel, bool use_sample_identifier); int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter); -int perf_evsel__append_filter(struct perf_evsel *evsel, - const char *fmt, const char *filter); +int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter); int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b14784c765eb..16bf09cc3e8d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1767,7 +1767,7 @@ static int set_filter(struct perf_evsel *evsel, const void *arg) return -1; } - if (perf_evsel__append_filter(evsel, "(%s) && (%s)", str) < 0) { + if (perf_evsel__append_tp_filter(evsel, str) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); return -1; @@ -1798,7 +1798,7 @@ static int add_exclude_perf_filter(struct perf_evsel *evsel, snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid()); - if (perf_evsel__append_filter(evsel, "(%s) && (%s)", new_filter) < 0) { + if (perf_evsel__append_tp_filter(evsel, new_filter) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); return -1; From 1e85748437ba5fd05abe5396d67062e0b4b502f2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Fri, 16 Sep 2016 08:44:05 -0600 Subject: [PATCH 08/27] perf evsel: Add support for address filters This patch makes it possible to use the current filter framework with address filters. That way address filters for HW tracers such as CoreSight and Intel PT can be communicated to the kernel drivers. Signed-off-by: Mathieu Poirier Acked-by: Adrian Hunter Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Peter Zijlstra Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1474037045-31730-4-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 5 +++++ tools/perf/util/evsel.h | 2 ++ tools/perf/util/parse-events.c | 39 +++++++++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3b4e7c452e43..380e84c3af3d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1067,6 +1067,11 @@ int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter) return perf_evsel__append_filter(evsel, "(%s) && (%s)", filter); } +int perf_evsel__append_addr_filter(struct perf_evsel *evsel, const char *filter) +{ + return perf_evsel__append_filter(evsel, "%s,%s", filter); +} + int perf_evsel__enable(struct perf_evsel *evsel) { int nthreads = thread_map__nr(evsel->threads); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 1f8c48f87f7d..b1503b0ecdff 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -236,6 +236,8 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel, int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter); int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter); +int perf_evsel__append_addr_filter(struct perf_evsel *evsel, + const char *filter); int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 16bf09cc3e8d..33546c3ac1fe 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1760,20 +1760,49 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist, static int set_filter(struct perf_evsel *evsel, const void *arg) { const char *str = arg; + bool found = false; + int nr_addr_filters = 0; + struct perf_pmu *pmu = NULL; - if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) { - fprintf(stderr, - "--filter option should follow a -e tracepoint option\n"); - return -1; + if (evsel == NULL) + goto err; + + if (evsel->attr.type == PERF_TYPE_TRACEPOINT) { + if (perf_evsel__append_tp_filter(evsel, str) < 0) { + fprintf(stderr, + "not enough memory to hold filter string\n"); + return -1; + } + + return 0; } - if (perf_evsel__append_tp_filter(evsel, str) < 0) { + while ((pmu = perf_pmu__scan(pmu)) != NULL) + if (pmu->type == evsel->attr.type) { + found = true; + break; + } + + if (found) + perf_pmu__scan_file(pmu, "nr_addr_filters", + "%d", &nr_addr_filters); + + if (!nr_addr_filters) + goto err; + + if (perf_evsel__append_addr_filter(evsel, str) < 0) { fprintf(stderr, "not enough memory to hold filter string\n"); return -1; } return 0; + +err: + fprintf(stderr, + "--filter option should follow a -e tracepoint or HW tracer option\n"); + + return -1; } int parse_filter(const struct option *opt, const char *str, From e7a06a5353574cac3a34211bd4e514bb8d00d766 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:35 +0300 Subject: [PATCH 09/27] perf script: Fix vanished idle symbols Commit 608c34de0b3d ("perf symbols: Mark if a symbol is idle in the library") causes idle symbols to vanish from perf script output. That is because print functions suppress symbols marked as 'idle'. However, suppression of 'idle' functions is only used by 'perf top' and 'perf top' does not use the print functions. Consequently that functionality can simply be removed from the print functions. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Fixes: 608c34de0b3d ("perf symbols: Mark if a symbol is idle in the library") Link: http://lkml.kernel.org/r/1474641528-18776-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel_fprintf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 9111e0666950..662a0a6182e7 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -122,9 +122,6 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, if (!node) break; - if (node->sym && node->sym->idle) - goto next; - printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); if (print_ip) @@ -158,7 +155,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, if (!print_oneline) printed += fprintf(fp, "\n"); -next: + callchain_cursor_advance(cursor); } } @@ -181,7 +178,7 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, if (cursor != NULL) { printed += sample__fprintf_callchain(sample, left_alignment, print_opts, cursor, fp); - } else if (!(al->sym && al->sym->idle)) { + } else { printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); if (print_ip) From 394c01ed8aafdf0f14fb18c6d320d8a783da243c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:36 +0300 Subject: [PATCH 10/27] perf record: Rename label 'out_symbol_exit' In preparation for fixing the error paths, rename label 'out_symbol_exit' to be 'out' because that error path can be used irrespective of whether symbols (or anything else) has been initialized. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2d0d69be3bf8..b32a880ec473 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1643,7 +1643,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (rec->evlist->nr_entries == 0 && perf_evlist__add_default(rec->evlist) < 0) { pr_err("Not enough memory for event selector list\n"); - goto out_symbol_exit; + goto out; } if (rec->opts.target.tid && !rec->opts.no_inherit_set) @@ -1663,7 +1663,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) ui__error("%s", errbuf); err = -saved_errno; - goto out_symbol_exit; + goto out; } err = -ENOMEM; @@ -1672,7 +1672,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) err = auxtrace_record__options(rec->itr, rec->evlist, &rec->opts); if (err) - goto out_symbol_exit; + goto out; /* * We take all buildids when the file contains @@ -1684,11 +1684,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (record_opts__config(&rec->opts)) { err = -EINVAL; - goto out_symbol_exit; + goto out; } err = __cmd_record(&record, argc, argv); -out_symbol_exit: +out: perf_evlist__delete(rec->evlist); symbol__exit(); auxtrace_record__free(rec->itr); From 5c01ad60b8a23f8ff59b9a5a756f07ed08f0b6d1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:37 +0300 Subject: [PATCH 11/27] perf record: Fix error paths Some error paths do not tidy-up. Fix that. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-6-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b32a880ec473..962adcfc43a5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1573,23 +1573,23 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (!rec->itr) { rec->itr = auxtrace_record__init(rec->evlist, &err); if (err) - return err; + goto out; } err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, rec->opts.auxtrace_snapshot_opts); if (err) - return err; + goto out; if (dry_run) - return 0; + goto out; err = bpf__setup_stdout(rec->evlist); if (err) { bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); pr_err("ERROR: Setup BPF stdout failed: %s\n", errbuf); - return err; + goto out; } err = -ENOMEM; From cd67f99fe90dcf515f1c70c474b84d56b6236cbb Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:38 +0300 Subject: [PATCH 12/27] perf symbols: Add dso__last_symbol() Add a function to find the last symbol in a DSO. This will be used when parsing address filters to calculate a region that includes the entire DSO. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-7-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 15 +++++++++++++++ tools/perf/util/symbol.h | 1 + 2 files changed, 16 insertions(+) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 19c9c558454f..aecff69a510d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -345,6 +345,16 @@ static struct symbol *symbols__first(struct rb_root *symbols) return NULL; } +static struct symbol *symbols__last(struct rb_root *symbols) +{ + struct rb_node *n = rb_last(symbols); + + if (n) + return rb_entry(n, struct symbol, rb_node); + + return NULL; +} + static struct symbol *symbols__next(struct symbol *sym) { struct rb_node *n = rb_next(&sym->rb_node); @@ -466,6 +476,11 @@ struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) return symbols__first(&dso->symbols[type]); } +struct symbol *dso__last_symbol(struct dso *dso, enum map_type type) +{ + return symbols__last(&dso->symbols[type]); +} + struct symbol *dso__next_symbol(struct symbol *sym) { return symbols__next(sym); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0dacfb7d5b67..d964844eb314 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -259,6 +259,7 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, struct symbol *symbol__next_by_name(struct symbol *sym); struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); +struct symbol *dso__last_symbol(struct dso *dso, enum map_type type); struct symbol *dso__next_symbol(struct symbol *sym); enum dso_type dso__type_fd(int fd); From 1b36c03e356936d62abbe2accaae1573d3b66f14 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:39 +0300 Subject: [PATCH 13/27] perf record: Add support for using symbols in address filters Symbols come from either the DSO or /proc/kallsyms for the kernel. Details of the functionality can be found in Documentation/perf-record.txt. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 55 +- tools/perf/builtin-record.c | 14 +- tools/perf/util/auxtrace.c | 737 +++++++++++++++++++++++ tools/perf/util/auxtrace.h | 54 ++ 4 files changed, 857 insertions(+), 3 deletions(-) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index babbb63e6d9d..92335193dc33 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -89,9 +89,62 @@ OPTIONS --filter=:: Event filter. This option should follow a event selector (-e) which - selects tracepoint event(s). Multiple '--filter' options are combined + selects either tracepoint event(s) or a hardware trace PMU + (e.g. Intel PT or CoreSight). + + - tracepoint filters + + In the case of tracepoints, multiple '--filter' options are combined using '&&'. + - address filters + + A hardware trace PMU advertises its ability to accept a number of + address filters by specifying a non-zero value in + /sys/bus/event_source/devices//nr_addr_filters. + + Address filters have the format: + + filter|start|stop|tracestop [/ ] [@] + + Where: + - 'filter': defines a region that will be traced. + - 'start': defines an address at which tracing will begin. + - 'stop': defines an address at which tracing will stop. + - 'tracestop': defines a region in which tracing will stop. + + is the name of the object file, is the offset to the + code to trace in that file, and is the size of the region to + trace. 'start' and 'stop' filters need not specify a . + + If no object file is specified then the kernel is assumed, in which case + the start address must be a current kernel memory address. + + can also be specified by providing the name of a symbol. If the + symbol name is not unique, it can be disambiguated by inserting #n where + 'n' selects the n'th symbol in address order. Alternately #0, #g or #G + select only a global symbol. can also be specified by providing + the name of a symbol, in which case the size is calculated to the end + of that symbol. For 'filter' and 'tracestop' filters, if is + omitted and is a symbol, then the size is calculated to the end + of that symbol. + + If is omitted and is '*', then the start and size will + be calculated from the first and last symbols, i.e. to trace the whole + file. + + If symbol names (or '*') are provided, they must be surrounded by white + space. + + The filter passed to the kernel is not necessarily the same as entered. + To see the filter that is passed, use the -v option. + + The kernel may not be able to configure a trace region if it is not + within a single mapping. MMAP events (or /proc//maps) can be + examined to determine if that is a possibility. + + Multiple filters can be separated with space or comma. + --exclude-perf:: Don't record events issued by perf itself. This option should follow a event selector (-e) which selects tracepoint event(s). It adds a diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 962adcfc43a5..67d2a9003294 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1581,6 +1581,18 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) if (err) goto out; + /* + * Allow aliases to facilitate the lookup of symbols for address + * filters. Refer to auxtrace_parse_filters(). + */ + symbol_conf.allow_aliases = true; + + symbol__init(NULL); + + err = auxtrace_parse_filters(rec->evlist); + if (err) + goto out; + if (dry_run) goto out; @@ -1594,8 +1606,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) err = -ENOMEM; - symbol__init(NULL); - if (symbol_conf.kptr_restrict) pr_warning( "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index c0aba8e839aa..c5a6e0b12452 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -16,6 +16,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -35,9 +39,14 @@ #include "../perf.h" #include "util.h" #include "evlist.h" +#include "dso.h" +#include "map.h" +#include "pmu.h" +#include "evsel.h" #include "cpumap.h" #include "thread_map.h" #include "asm/bug.h" +#include "symbol/kallsyms.h" #include "auxtrace.h" #include @@ -1399,3 +1408,731 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key) return NULL; } + +static void addr_filter__free_str(struct addr_filter *filt) +{ + free(filt->str); + filt->action = NULL; + filt->sym_from = NULL; + filt->sym_to = NULL; + filt->filename = NULL; + filt->str = NULL; +} + +static struct addr_filter *addr_filter__new(void) +{ + struct addr_filter *filt = zalloc(sizeof(*filt)); + + if (filt) + INIT_LIST_HEAD(&filt->list); + + return filt; +} + +static void addr_filter__free(struct addr_filter *filt) +{ + if (filt) + addr_filter__free_str(filt); + free(filt); +} + +static void addr_filters__add(struct addr_filters *filts, + struct addr_filter *filt) +{ + list_add_tail(&filt->list, &filts->head); + filts->cnt += 1; +} + +static void addr_filters__del(struct addr_filters *filts, + struct addr_filter *filt) +{ + list_del_init(&filt->list); + filts->cnt -= 1; +} + +void addr_filters__init(struct addr_filters *filts) +{ + INIT_LIST_HEAD(&filts->head); + filts->cnt = 0; +} + +void addr_filters__exit(struct addr_filters *filts) +{ + struct addr_filter *filt, *n; + + list_for_each_entry_safe(filt, n, &filts->head, list) { + addr_filters__del(filts, filt); + addr_filter__free(filt); + } +} + +static int parse_num_or_str(char **inp, u64 *num, const char **str, + const char *str_delim) +{ + *inp += strspn(*inp, " "); + + if (isdigit(**inp)) { + char *endptr; + + if (!num) + return -EINVAL; + errno = 0; + *num = strtoull(*inp, &endptr, 0); + if (errno) + return -errno; + if (endptr == *inp) + return -EINVAL; + *inp = endptr; + } else { + size_t n; + + if (!str) + return -EINVAL; + *inp += strspn(*inp, " "); + *str = *inp; + n = strcspn(*inp, str_delim); + if (!n) + return -EINVAL; + *inp += n; + if (**inp) { + **inp = '\0'; + *inp += 1; + } + } + return 0; +} + +static int parse_action(struct addr_filter *filt) +{ + if (!strcmp(filt->action, "filter")) { + filt->start = true; + filt->range = true; + } else if (!strcmp(filt->action, "start")) { + filt->start = true; + } else if (!strcmp(filt->action, "stop")) { + filt->start = false; + } else if (!strcmp(filt->action, "tracestop")) { + filt->start = false; + filt->range = true; + filt->action += 5; /* Change 'tracestop' to 'stop' */ + } else { + return -EINVAL; + } + return 0; +} + +static int parse_sym_idx(char **inp, int *idx) +{ + *idx = -1; + + *inp += strspn(*inp, " "); + + if (**inp != '#') + return 0; + + *inp += 1; + + if (**inp == 'g' || **inp == 'G') { + *inp += 1; + *idx = 0; + } else { + unsigned long num; + char *endptr; + + errno = 0; + num = strtoul(*inp, &endptr, 0); + if (errno) + return -errno; + if (endptr == *inp || num > INT_MAX) + return -EINVAL; + *inp = endptr; + *idx = num; + } + + return 0; +} + +static int parse_addr_size(char **inp, u64 *num, const char **str, int *idx) +{ + int err = parse_num_or_str(inp, num, str, " "); + + if (!err && *str) + err = parse_sym_idx(inp, idx); + + return err; +} + +static int parse_one_filter(struct addr_filter *filt, const char **filter_inp) +{ + char *fstr; + int err; + + filt->str = fstr = strdup(*filter_inp); + if (!fstr) + return -ENOMEM; + + err = parse_num_or_str(&fstr, NULL, &filt->action, " "); + if (err) + goto out_err; + + err = parse_action(filt); + if (err) + goto out_err; + + err = parse_addr_size(&fstr, &filt->addr, &filt->sym_from, + &filt->sym_from_idx); + if (err) + goto out_err; + + fstr += strspn(fstr, " "); + + if (*fstr == '/') { + fstr += 1; + err = parse_addr_size(&fstr, &filt->size, &filt->sym_to, + &filt->sym_to_idx); + if (err) + goto out_err; + filt->range = true; + } + + fstr += strspn(fstr, " "); + + if (*fstr == '@') { + fstr += 1; + err = parse_num_or_str(&fstr, NULL, &filt->filename, " ,"); + if (err) + goto out_err; + } + + fstr += strspn(fstr, " ,"); + + *filter_inp += fstr - filt->str; + + return 0; + +out_err: + addr_filter__free_str(filt); + + return err; +} + +int addr_filters__parse_bare_filter(struct addr_filters *filts, + const char *filter) +{ + struct addr_filter *filt; + const char *fstr = filter; + int err; + + while (*fstr) { + filt = addr_filter__new(); + err = parse_one_filter(filt, &fstr); + if (err) { + addr_filter__free(filt); + addr_filters__exit(filts); + return err; + } + addr_filters__add(filts, filt); + } + + return 0; +} + +struct sym_args { + const char *name; + u64 start; + u64 size; + int idx; + int cnt; + bool started; + bool global; + bool selected; + bool duplicate; + bool near; +}; + +static bool kern_sym_match(struct sym_args *args, const char *name, char type) +{ + /* A function with the same name, and global or the n'th found or any */ + return symbol_type__is_a(type, MAP__FUNCTION) && + !strcmp(name, args->name) && + ((args->global && isupper(type)) || + (args->selected && ++(args->cnt) == args->idx) || + (!args->global && !args->selected)); +} + +static int find_kern_sym_cb(void *arg, const char *name, char type, u64 start) +{ + struct sym_args *args = arg; + + if (args->started) { + if (!args->size) + args->size = start - args->start; + if (args->selected) { + if (args->size) + return 1; + } else if (kern_sym_match(args, name, type)) { + args->duplicate = true; + return 1; + } + } else if (kern_sym_match(args, name, type)) { + args->started = true; + args->start = start; + } + + return 0; +} + +static int print_kern_sym_cb(void *arg, const char *name, char type, u64 start) +{ + struct sym_args *args = arg; + + if (kern_sym_match(args, name, type)) { + pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", + ++args->cnt, start, type, name); + args->near = true; + } else if (args->near) { + args->near = false; + pr_err("\t\twhich is near\t\t%s\n", name); + } + + return 0; +} + +static int sym_not_found_error(const char *sym_name, int idx) +{ + if (idx > 0) { + pr_err("N'th occurrence (N=%d) of symbol '%s' not found.\n", + idx, sym_name); + } else if (!idx) { + pr_err("Global symbol '%s' not found.\n", sym_name); + } else { + pr_err("Symbol '%s' not found.\n", sym_name); + } + pr_err("Note that symbols must be functions.\n"); + + return -EINVAL; +} + +static int find_kern_sym(const char *sym_name, u64 *start, u64 *size, int idx) +{ + struct sym_args args = { + .name = sym_name, + .idx = idx, + .global = !idx, + .selected = idx > 0, + }; + int err; + + *start = 0; + *size = 0; + + err = kallsyms__parse("/proc/kallsyms", &args, find_kern_sym_cb); + if (err < 0) { + pr_err("Failed to parse /proc/kallsyms\n"); + return err; + } + + if (args.duplicate) { + pr_err("Multiple kernel symbols with name '%s'\n", sym_name); + args.cnt = 0; + kallsyms__parse("/proc/kallsyms", &args, print_kern_sym_cb); + pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", + sym_name); + pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); + return -EINVAL; + } + + if (!args.started) { + pr_err("Kernel symbol lookup: "); + return sym_not_found_error(sym_name, idx); + } + + *start = args.start; + *size = args.size; + + return 0; +} + +static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, + char type, u64 start) +{ + struct sym_args *args = arg; + + if (!symbol_type__is_a(type, MAP__FUNCTION)) + return 0; + + if (!args->started) { + args->started = true; + args->start = start; + } + /* Don't know exactly where the kernel ends, so we add a page */ + args->size = round_up(start, page_size) + page_size - args->start; + + return 0; +} + +static int addr_filter__entire_kernel(struct addr_filter *filt) +{ + struct sym_args args = { .started = false }; + int err; + + err = kallsyms__parse("/proc/kallsyms", &args, find_entire_kern_cb); + if (err < 0 || !args.started) { + pr_err("Failed to parse /proc/kallsyms\n"); + return err; + } + + filt->addr = args.start; + filt->size = args.size; + + return 0; +} + +static int check_end_after_start(struct addr_filter *filt, u64 start, u64 size) +{ + if (start + size >= filt->addr) + return 0; + + if (filt->sym_from) { + pr_err("Symbol '%s' (0x%"PRIx64") comes before '%s' (0x%"PRIx64")\n", + filt->sym_to, start, filt->sym_from, filt->addr); + } else { + pr_err("Symbol '%s' (0x%"PRIx64") comes before address 0x%"PRIx64")\n", + filt->sym_to, start, filt->addr); + } + + return -EINVAL; +} + +static int addr_filter__resolve_kernel_syms(struct addr_filter *filt) +{ + bool no_size = false; + u64 start, size; + int err; + + if (symbol_conf.kptr_restrict) { + pr_err("Kernel addresses are restricted. Unable to resolve kernel symbols.\n"); + return -EINVAL; + } + + if (filt->sym_from && !strcmp(filt->sym_from, "*")) + return addr_filter__entire_kernel(filt); + + if (filt->sym_from) { + err = find_kern_sym(filt->sym_from, &start, &size, + filt->sym_from_idx); + if (err) + return err; + filt->addr = start; + if (filt->range && !filt->size && !filt->sym_to) { + filt->size = size; + no_size = !!size; + } + } + + if (filt->sym_to) { + err = find_kern_sym(filt->sym_to, &start, &size, + filt->sym_to_idx); + if (err) + return err; + + err = check_end_after_start(filt, start, size); + if (err) + return err; + filt->size = start + size - filt->addr; + no_size = !!size; + } + + /* The very last symbol in kallsyms does not imply a particular size */ + if (no_size) { + pr_err("Cannot determine size of symbol '%s'\n", + filt->sym_to ? filt->sym_to : filt->sym_from); + return -EINVAL; + } + + return 0; +} + +static struct dso *load_dso(const char *name) +{ + struct map *map; + struct dso *dso; + + map = dso__new_map(name); + if (!map) + return NULL; + + map__load(map); + + dso = dso__get(map->dso); + + map__put(map); + + return dso; +} + +static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt, + int idx) +{ + /* Same name, and global or the n'th found or any */ + return !arch__compare_symbol_names(name, sym->name) && + ((!idx && sym->binding == STB_GLOBAL) || + (idx > 0 && ++*cnt == idx) || + idx < 0); +} + +static void print_duplicate_syms(struct dso *dso, const char *sym_name) +{ + struct symbol *sym; + bool near = false; + int cnt = 0; + + pr_err("Multiple symbols with name '%s'\n", sym_name); + + sym = dso__first_symbol(dso, MAP__FUNCTION); + while (sym) { + if (dso_sym_match(sym, sym_name, &cnt, -1)) { + pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", + ++cnt, sym->start, + sym->binding == STB_GLOBAL ? 'g' : + sym->binding == STB_LOCAL ? 'l' : 'w', + sym->name); + near = true; + } else if (near) { + near = false; + pr_err("\t\twhich is near\t\t%s\n", sym->name); + } + sym = dso__next_symbol(sym); + } + + pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", + sym_name); + pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); +} + +static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, + u64 *size, int idx) +{ + struct symbol *sym; + int cnt = 0; + + *start = 0; + *size = 0; + + sym = dso__first_symbol(dso, MAP__FUNCTION); + while (sym) { + if (*start) { + if (!*size) + *size = sym->start - *start; + if (idx > 0) { + if (*size) + return 1; + } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { + print_duplicate_syms(dso, sym_name); + return -EINVAL; + } + } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { + *start = sym->start; + *size = sym->end - sym->start; + } + sym = dso__next_symbol(sym); + } + + if (!*start) + return sym_not_found_error(sym_name, idx); + + return 0; +} + +static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso) +{ + struct symbol *first_sym = dso__first_symbol(dso, MAP__FUNCTION); + struct symbol *last_sym = dso__last_symbol(dso, MAP__FUNCTION); + + if (!first_sym || !last_sym) { + pr_err("Failed to determine filter for %s\nNo symbols found.\n", + filt->filename); + return -EINVAL; + } + + filt->addr = first_sym->start; + filt->size = last_sym->end - first_sym->start; + + return 0; +} + +static int addr_filter__resolve_syms(struct addr_filter *filt) +{ + u64 start, size; + struct dso *dso; + int err = 0; + + if (!filt->sym_from && !filt->sym_to) + return 0; + + if (!filt->filename) + return addr_filter__resolve_kernel_syms(filt); + + dso = load_dso(filt->filename); + if (!dso) { + pr_err("Failed to load symbols from: %s\n", filt->filename); + return -EINVAL; + } + + if (filt->sym_from && !strcmp(filt->sym_from, "*")) { + err = addr_filter__entire_dso(filt, dso); + goto put_dso; + } + + if (filt->sym_from) { + err = find_dso_sym(dso, filt->sym_from, &start, &size, + filt->sym_from_idx); + if (err) + goto put_dso; + filt->addr = start; + if (filt->range && !filt->size && !filt->sym_to) + filt->size = size; + } + + if (filt->sym_to) { + err = find_dso_sym(dso, filt->sym_to, &start, &size, + filt->sym_to_idx); + if (err) + goto put_dso; + + err = check_end_after_start(filt, start, size); + if (err) + return err; + + filt->size = start + size - filt->addr; + } + +put_dso: + dso__put(dso); + + return err; +} + +static char *addr_filter__to_str(struct addr_filter *filt) +{ + char filename_buf[PATH_MAX]; + const char *at = ""; + const char *fn = ""; + char *filter; + int err; + + if (filt->filename) { + at = "@"; + fn = realpath(filt->filename, filename_buf); + if (!fn) + return NULL; + } + + if (filt->range) { + err = asprintf(&filter, "%s 0x%"PRIx64"/0x%"PRIx64"%s%s", + filt->action, filt->addr, filt->size, at, fn); + } else { + err = asprintf(&filter, "%s 0x%"PRIx64"%s%s", + filt->action, filt->addr, at, fn); + } + + return err < 0 ? NULL : filter; +} + +static int parse_addr_filter(struct perf_evsel *evsel, const char *filter, + int max_nr) +{ + struct addr_filters filts; + struct addr_filter *filt; + int err; + + addr_filters__init(&filts); + + err = addr_filters__parse_bare_filter(&filts, filter); + if (err) + goto out_exit; + + if (filts.cnt > max_nr) { + pr_err("Error: number of address filters (%d) exceeds maximum (%d)\n", + filts.cnt, max_nr); + err = -EINVAL; + goto out_exit; + } + + list_for_each_entry(filt, &filts.head, list) { + char *new_filter; + + err = addr_filter__resolve_syms(filt); + if (err) + goto out_exit; + + new_filter = addr_filter__to_str(filt); + if (!new_filter) { + err = -ENOMEM; + goto out_exit; + } + + if (perf_evsel__append_addr_filter(evsel, new_filter)) { + err = -ENOMEM; + goto out_exit; + } + } + +out_exit: + addr_filters__exit(&filts); + + if (err) { + pr_err("Failed to parse address filter: '%s'\n", filter); + pr_err("Filter format is: filter|start|stop|tracestop [/ ] [@]\n"); + pr_err("Where multiple filters are separated by space or comma.\n"); + } + + return err; +} + +static struct perf_pmu *perf_evsel__find_pmu(struct perf_evsel *evsel) +{ + struct perf_pmu *pmu = NULL; + + while ((pmu = perf_pmu__scan(pmu)) != NULL) { + if (pmu->type == evsel->attr.type) + break; + } + + return pmu; +} + +static int perf_evsel__nr_addr_filter(struct perf_evsel *evsel) +{ + struct perf_pmu *pmu = perf_evsel__find_pmu(evsel); + int nr_addr_filters = 0; + + if (!pmu) + return 0; + + perf_pmu__scan_file(pmu, "nr_addr_filters", "%d", &nr_addr_filters); + + return nr_addr_filters; +} + +int auxtrace_parse_filters(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + char *filter; + int err, max_nr; + + evlist__for_each_entry(evlist, evsel) { + filter = evsel->filter; + max_nr = perf_evsel__nr_addr_filter(evsel); + if (!filter || !max_nr) + continue; + evsel->filter = NULL; + err = parse_addr_filter(evsel, filter, max_nr); + free(filter); + if (err) + return err; + pr_debug("Address filter: %s\n", evsel->filter); + } + + return 0; +} diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 09286f193532..26fb1ee5746a 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -318,6 +318,48 @@ struct auxtrace_record { unsigned int alignment; }; +/** + * struct addr_filter - address filter. + * @list: list node + * @range: true if it is a range filter + * @start: true if action is 'filter' or 'start' + * @action: 'filter', 'start' or 'stop' ('tracestop' is accepted but converted + * to 'stop') + * @sym_from: symbol name for the filter address + * @sym_to: symbol name that determines the filter size + * @sym_from_idx: selects n'th from symbols with the same name (0 means global + * and less than 0 means symbol must be unique) + * @sym_to_idx: same as @sym_from_idx but for @sym_to + * @addr: filter address + * @size: filter region size (for range filters) + * @filename: DSO file name or NULL for the kernel + * @str: allocated string that contains the other string members + */ +struct addr_filter { + struct list_head list; + bool range; + bool start; + const char *action; + const char *sym_from; + const char *sym_to; + int sym_from_idx; + int sym_to_idx; + u64 addr; + u64 size; + const char *filename; + char *str; +}; + +/** + * struct addr_filters - list of address filters. + * @head: list of address filters + * @cnt: number of address filters + */ +struct addr_filters { + struct list_head head; + int cnt; +}; + #ifdef HAVE_AUXTRACE_SUPPORT /* @@ -482,6 +524,12 @@ void perf_session__auxtrace_error_inc(struct perf_session *session, union perf_event *event); void events_stats__auxtrace_error_warn(const struct events_stats *stats); +void addr_filters__init(struct addr_filters *filts); +void addr_filters__exit(struct addr_filters *filts); +int addr_filters__parse_bare_filter(struct addr_filters *filts, + const char *filter); +int auxtrace_parse_filters(struct perf_evlist *evlist); + static inline int auxtrace__process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, @@ -640,6 +688,12 @@ void auxtrace_index__free(struct list_head *head __maybe_unused) { } +static inline +int auxtrace_parse_filters(struct perf_evlist *evlist __maybe_unused) +{ + return 0; +} + int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, struct auxtrace_mmap_params *mp, void *userpg, int fd); From f9655200ecd2d6cc13900a727150177b94ca229e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:40 +0300 Subject: [PATCH 14/27] perf probe: Increase debug level of SDT debug messages Two SDT debug messages can occur for every DSO which is too noisy. Consequently, increase debug level of SDT messages. Signed-off-by: Adrian Hunter Acked-by: Masami Hiramatsu Cc: Jiri Olsa Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-9-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 4 ++-- tools/perf/util/probe-file.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5651f3c12f93..e528c40739cc 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -620,7 +620,7 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id, ret = probe_cache__scan_sdt(cache, realname); if (ret >= 0) { - pr_debug("Found %d SDTs in %s\n", ret, realname); + pr_debug4("Found %d SDTs in %s\n", ret, realname); if (probe_cache__commit(cache) < 0) ret = -1; } @@ -691,7 +691,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, /* Update SDT cache : error is just warned */ if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0) - pr_debug("Failed to update/scan SDT cache for %s\n", realname); + pr_debug4("Failed to update/scan SDT cache for %s\n", realname); out_free: if (!is_kallsyms) diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 6f931e442f14..436b64731f65 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -699,7 +699,7 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) INIT_LIST_HEAD(&sdtlist); ret = get_sdt_note_list(&sdtlist, pathname); if (ret < 0) { - pr_debug("Failed to get sdt note: %d\n", ret); + pr_debug4("Failed to get sdt note: %d\n", ret); return ret; } list_for_each_entry(note, &sdtlist, note_list) { From 810c398bc09b2f2dfde52a7d2483a710612c5fb8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:41 +0300 Subject: [PATCH 15/27] perf intel-pt: Fix snapshot overlap detection decoder errors Fix occasional decoder errors decoding trace data collected in snapshot mode. Snapshot mode can take successive snapshots of trace which might overlap. The decoder checks whether there is an overlap but only looks at the current and previous buffer. However buffers that do not contain synchronization (i.e. PSB) packets cannot be decoded or used for overlap checking. That means the decoder actually needs to check overlaps between the current buffer and the previous buffer that contained usable data. Make that change. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Cc: stable@vger.kernel.org # v4.3+ Link: http://lkml.kernel.org/r/1474641528-18776-10-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index b9cc353cace2..b744ea812a2e 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -241,7 +241,7 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) } queue = &ptq->pt->queues.queue_array[ptq->queue_nr]; - +next: buffer = auxtrace_buffer__next(queue, buffer); if (!buffer) { if (old_buffer) @@ -264,9 +264,6 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer)) return -ENOMEM; - if (old_buffer) - auxtrace_buffer__drop_data(old_buffer); - if (buffer->use_data) { b->len = buffer->use_size; b->buf = buffer->use_data; @@ -276,6 +273,16 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) } b->ref_timestamp = buffer->reference; + /* + * If in snapshot mode and the buffer has no usable data, get next + * buffer and again check overlap against old_buffer. + */ + if (ptq->pt->snapshot_mode && !b->len) + goto next; + + if (old_buffer) + auxtrace_buffer__drop_data(old_buffer); + if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode && !buffer->consecutive)) { b->consecutive = false; From fa8025c37454501a2df4a90ae84ff01f4aff8ba8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:42 +0300 Subject: [PATCH 16/27] perf intel-pt: Add support for recording the max non-turbo ratio Previously the maximum non-turbo ratio was calculated from TSC assuming a 100 MHz multiplier which is correct for current hardware supporting Intel PT. However more recent kernels also now export the value, so use that in preference to the calculated value. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/intel-pt.c | 6 ++++++ tools/perf/util/intel-pt.c | 14 +++++++++++++- tools/perf/util/intel-pt.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index a2412e9d883b..18b21514c153 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -302,6 +302,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, bool cap_user_time_zero = false, per_cpu_mmaps; u64 tsc_bit, mtc_bit, mtc_freq_bits, cyc_bit, noretcomp_bit; u32 tsc_ctc_ratio_n, tsc_ctc_ratio_d; + unsigned long max_non_turbo_ratio; int err; if (priv_size != INTEL_PT_AUXTRACE_PRIV_SIZE) @@ -317,6 +318,10 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); + if (perf_pmu__scan_file(intel_pt_pmu, "max_nonturbo_ratio", + "%lu", &max_non_turbo_ratio) != 1) + max_non_turbo_ratio = 0; + if (!session->evlist->nr_mmaps) return -EINVAL; @@ -351,6 +356,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, auxtrace_info->priv[INTEL_PT_TSC_CTC_N] = tsc_ctc_ratio_n; auxtrace_info->priv[INTEL_PT_TSC_CTC_D] = tsc_ctc_ratio_d; auxtrace_info->priv[INTEL_PT_CYC_BIT] = cyc_bit; + auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO] = max_non_turbo_ratio; return 0; } diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index b744ea812a2e..77fbf02c8e41 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -2023,6 +2023,7 @@ static const char * const intel_pt_info_fmts[] = { [INTEL_PT_TSC_CTC_N] = " TSC:CTC numerator %"PRIu64"\n", [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", + [INTEL_PT_MAX_NONTURBO_RATIO] = " Max non-turbo ratio %"PRIu64"\n", }; static void intel_pt_print_info(u64 *arr, int start, int finish) @@ -2087,6 +2088,15 @@ int intel_pt_process_auxtrace_info(union perf_event *event, INTEL_PT_CYC_BIT); } + if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) + + (sizeof(u64) * INTEL_PT_MAX_NONTURBO_RATIO)) { + pt->max_non_turbo_ratio = + auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO]; + intel_pt_print_info(&auxtrace_info->priv[0], + INTEL_PT_MAX_NONTURBO_RATIO, + INTEL_PT_MAX_NONTURBO_RATIO); + } + pt->timeless_decoding = intel_pt_timeless_decoding(pt); pt->have_tsc = intel_pt_have_tsc(pt); pt->sampling_mode = false; @@ -2156,7 +2166,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event, if (pt->tc.time_mult) { u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000); - pt->max_non_turbo_ratio = (tsc_freq + 50000000) / 100000000; + if (!pt->max_non_turbo_ratio) + pt->max_non_turbo_ratio = + (tsc_freq + 50000000) / 100000000; intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq); intel_pt_log("Maximum non-turbo ratio %u\n", pt->max_non_turbo_ratio); diff --git a/tools/perf/util/intel-pt.h b/tools/perf/util/intel-pt.h index 0065949df693..8b8356233e6a 100644 --- a/tools/perf/util/intel-pt.h +++ b/tools/perf/util/intel-pt.h @@ -34,6 +34,7 @@ enum { INTEL_PT_TSC_CTC_N, INTEL_PT_TSC_CTC_D, INTEL_PT_CYC_BIT, + INTEL_PT_MAX_NONTURBO_RATIO, INTEL_PT_AUXTRACE_PRIV_MAX, }; From 4d34e10a9f9a38c611cac0deda8f91b064282747 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:43 +0300 Subject: [PATCH 17/27] perf intel-pt: Fix missing error codes processing auxtrace_info Fix 2 places where the err variable was not being set. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-12-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 77fbf02c8e41..96519e801e53 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -2138,11 +2138,13 @@ int intel_pt_process_auxtrace_info(union perf_event *event, pt->switch_evsel = intel_pt_find_sched_switch(session->evlist); if (!pt->switch_evsel) { pr_err("%s: missing sched_switch event\n", __func__); + err = -EINVAL; goto err_delete_thread; } } else if (pt->have_sched_switch == 2 && !intel_pt_find_switch(session->evlist)) { pr_err("%s: missing context_switch attribute flag\n", __func__); + err = -EINVAL; goto err_delete_thread; } From 40b746a06332799786ba557fe84184428bef62fb Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:44 +0300 Subject: [PATCH 18/27] perf intel-pt: Add a helper function for processing AUXTRACE_INFO Add a helper function 'intel_pt_has()' to make it easier to determine which members the AUXTRACE_INFO event contains. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-13-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 96519e801e53..f16b00f55a19 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -2037,6 +2037,12 @@ static void intel_pt_print_info(u64 *arr, int start, int finish) fprintf(stdout, intel_pt_info_fmts[i], arr[i]); } +static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos) +{ + return auxtrace_info->header.size >= + sizeof(struct auxtrace_info_event) + (sizeof(u64) * (pos + 1)); +} + int intel_pt_process_auxtrace_info(union perf_event *event, struct perf_session *session) { @@ -2077,8 +2083,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event, intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE, INTEL_PT_PER_CPU_MMAPS); - if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) + - (sizeof(u64) * INTEL_PT_CYC_BIT)) { + if (intel_pt_has(auxtrace_info, INTEL_PT_CYC_BIT)) { pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT]; pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS]; pt->tsc_ctc_ratio_n = auxtrace_info->priv[INTEL_PT_TSC_CTC_N]; @@ -2088,8 +2093,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event, INTEL_PT_CYC_BIT); } - if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) + - (sizeof(u64) * INTEL_PT_MAX_NONTURBO_RATIO)) { + if (intel_pt_has(auxtrace_info, INTEL_PT_MAX_NONTURBO_RATIO)) { pt->max_non_turbo_ratio = auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO]; intel_pt_print_info(&auxtrace_info->priv[0], From c093f308cea3eb4be3ed6e7e9ad54bf67a26abe4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:45 +0300 Subject: [PATCH 19/27] perf intel-pt: Record address filter in AUXTRACE_INFO event The address filter is needed to help decode the trace, so store it in the AUXTRACE_INFO event. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-14-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/intel-pt.c | 53 ++++++++++++++++++++++++++--- tools/perf/util/intel-pt.h | 3 +- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 18b21514c153..90fa2286edcf 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -62,6 +62,7 @@ struct intel_pt_recording { size_t snapshot_ref_buf_size; int snapshot_ref_cnt; struct intel_pt_snapshot_ref *snapshot_refs; + size_t priv_size; }; static int intel_pt_parse_terms_with_default(struct list_head *formats, @@ -273,11 +274,37 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) return attr; } -static size_t -intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused, - struct perf_evlist *evlist __maybe_unused) +static const char *intel_pt_find_filter(struct perf_evlist *evlist, + struct perf_pmu *intel_pt_pmu) { - return INTEL_PT_AUXTRACE_PRIV_SIZE; + struct perf_evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + if (evsel->attr.type == intel_pt_pmu->type) + return evsel->filter; + } + + return NULL; +} + +static size_t intel_pt_filter_bytes(const char *filter) +{ + size_t len = filter ? strlen(filter) : 0; + + return len ? roundup(len + 1, 8) : 0; +} + +static size_t +intel_pt_info_priv_size(struct auxtrace_record *itr, struct perf_evlist *evlist) +{ + struct intel_pt_recording *ptr = + container_of(itr, struct intel_pt_recording, itr); + const char *filter = intel_pt_find_filter(evlist, ptr->intel_pt_pmu); + + ptr->priv_size = (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) + + intel_pt_filter_bytes(filter); + + return ptr->priv_size; } static void intel_pt_tsc_ctc_ratio(u32 *n, u32 *d) @@ -303,9 +330,12 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, u64 tsc_bit, mtc_bit, mtc_freq_bits, cyc_bit, noretcomp_bit; u32 tsc_ctc_ratio_n, tsc_ctc_ratio_d; unsigned long max_non_turbo_ratio; + size_t filter_str_len; + const char *filter; + u64 *info; int err; - if (priv_size != INTEL_PT_AUXTRACE_PRIV_SIZE) + if (priv_size != ptr->priv_size) return -EINVAL; intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit); @@ -322,6 +352,9 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, "%lu", &max_non_turbo_ratio) != 1) max_non_turbo_ratio = 0; + filter = intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); + filter_str_len = filter ? strlen(filter) : 0; + if (!session->evlist->nr_mmaps) return -EINVAL; @@ -357,6 +390,16 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, auxtrace_info->priv[INTEL_PT_TSC_CTC_D] = tsc_ctc_ratio_d; auxtrace_info->priv[INTEL_PT_CYC_BIT] = cyc_bit; auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO] = max_non_turbo_ratio; + auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] = filter_str_len; + + info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; + + if (filter_str_len) { + size_t len = intel_pt_filter_bytes(filter); + + strncpy((char *)info, filter, len); + info += len >> 3; + } return 0; } diff --git a/tools/perf/util/intel-pt.h b/tools/perf/util/intel-pt.h index 8b8356233e6a..e13b14e5a37b 100644 --- a/tools/perf/util/intel-pt.h +++ b/tools/perf/util/intel-pt.h @@ -35,11 +35,10 @@ enum { INTEL_PT_TSC_CTC_D, INTEL_PT_CYC_BIT, INTEL_PT_MAX_NONTURBO_RATIO, + INTEL_PT_FILTER_STR_LEN, INTEL_PT_AUXTRACE_PRIV_MAX, }; -#define INTEL_PT_AUXTRACE_PRIV_SIZE (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) - struct auxtrace_record; struct perf_tool; union perf_event; From 2b9e32c47fd3edb0373067de7a151775b0e005c2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:46 +0300 Subject: [PATCH 20/27] perf intel-pt: Read address filter from AUXTRACE_INFO event Read the address filter from the AUXTRACE_INFO event in preparation for using it to assist in decoding. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-15-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index f16b00f55a19..c9fec19a7914 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -103,6 +103,8 @@ struct intel_pt { unsigned max_non_turbo_ratio; unsigned long num_events; + + char *filter; }; enum switch_state { @@ -1774,6 +1776,7 @@ static void intel_pt_free(struct perf_session *session) intel_pt_free_events(session); session->auxtrace = NULL; thread__put(pt->unknown_thread); + zfree(&pt->filter); free(pt); } @@ -2024,6 +2027,7 @@ static const char * const intel_pt_info_fmts[] = { [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", [INTEL_PT_MAX_NONTURBO_RATIO] = " Max non-turbo ratio %"PRIu64"\n", + [INTEL_PT_FILTER_STR_LEN] = " Filter string len. %"PRIu64"\n", }; static void intel_pt_print_info(u64 *arr, int start, int finish) @@ -2037,6 +2041,14 @@ static void intel_pt_print_info(u64 *arr, int start, int finish) fprintf(stdout, intel_pt_info_fmts[i], arr[i]); } +static void intel_pt_print_info_str(const char *name, const char *str) +{ + if (!dump_trace) + return; + + fprintf(stdout, " %-20s%s\n", name, str ? str : ""); +} + static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos) { return auxtrace_info->header.size >= @@ -2049,6 +2061,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS; struct intel_pt *pt; + void *info_end; + u64 *info; int err; if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + @@ -2101,6 +2115,42 @@ int intel_pt_process_auxtrace_info(union perf_event *event, INTEL_PT_MAX_NONTURBO_RATIO); } + info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; + info_end = (void *)info + auxtrace_info->header.size; + + if (intel_pt_has(auxtrace_info, INTEL_PT_FILTER_STR_LEN)) { + size_t len; + + len = auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN]; + intel_pt_print_info(&auxtrace_info->priv[0], + INTEL_PT_FILTER_STR_LEN, + INTEL_PT_FILTER_STR_LEN); + if (len) { + const char *filter = (const char *)info; + + len = roundup(len + 1, 8); + info += len >> 3; + if ((void *)info > info_end) { + pr_err("%s: bad filter string length\n", __func__); + err = -EINVAL; + goto err_free_queues; + } + pt->filter = memdup(filter, len); + if (!pt->filter) { + err = -ENOMEM; + goto err_free_queues; + } + if (session->header.needs_swap) + mem_bswap_64(pt->filter, len); + if (pt->filter[len - 1]) { + pr_err("%s: filter string not null terminated\n", __func__); + err = -EINVAL; + goto err_free_queues; + } + } + intel_pt_print_info_str("Filter string", pt->filter); + } + pt->timeless_decoding = intel_pt_timeless_decoding(pt); pt->have_tsc = intel_pt_have_tsc(pt); pt->sampling_mode = false; @@ -2218,6 +2268,7 @@ err_free_queues: auxtrace_queues__free(&pt->queues); session->auxtrace = NULL; err_free: + zfree(&pt->filter); free(pt); return err; } From 9f1d122b528ef3ffcef1bdcf6a3dddf9450a864e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:47 +0300 Subject: [PATCH 21/27] perf intel-pt: Enable decoder to handle TIP.PGD with missing IP When address filters are used, the decoder must detect the end of a filter region (or a branch into a tracestop region) by matching Packet Generation Disabled (TIP.PGD) packets against the object code using the IP given in the packet. However, due to errata SKL014 "Intel PT TIP.PGD May Not Have Target IP Payload", that IP may not be present. Enable the decoder to handle that by adding a new callback function 'pgd_ip()' which indicates whether the IP is not traced, in which case that is the point where the trace was disabled. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-16-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../util/intel-pt-decoder/intel-pt-decoder.c | 30 +++++++++++++++++++ .../util/intel-pt-decoder/intel-pt-decoder.h | 1 + 2 files changed, 31 insertions(+) diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 8ff6c6a61291..7591a0c37473 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c @@ -80,6 +80,7 @@ struct intel_pt_decoder { int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, uint64_t max_insn_cnt, void *data); + bool (*pgd_ip)(uint64_t ip, void *data); void *data; struct intel_pt_state state; const unsigned char *buf; @@ -186,6 +187,7 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params) decoder->get_trace = params->get_trace; decoder->walk_insn = params->walk_insn; + decoder->pgd_ip = params->pgd_ip; decoder->data = params->data; decoder->return_compression = params->return_compression; @@ -1008,6 +1010,19 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) int err; err = intel_pt_walk_insn(decoder, &intel_pt_insn, 0); + if (err == INTEL_PT_RETURN && + decoder->pgd_ip && + decoder->pkt_state == INTEL_PT_STATE_TIP_PGD && + (decoder->state.type & INTEL_PT_BRANCH) && + decoder->pgd_ip(decoder->state.to_ip, decoder->data)) { + /* Unconditional branch leaving filter region */ + decoder->no_progress = 0; + decoder->pge = false; + decoder->continuous_period = false; + decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; + decoder->state.to_ip = 0; + return 0; + } if (err == INTEL_PT_RETURN) return 0; if (err) @@ -1036,6 +1051,21 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) } if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) { + uint64_t to_ip = decoder->ip + intel_pt_insn.length + + intel_pt_insn.rel; + + if (decoder->pgd_ip && + decoder->pkt_state == INTEL_PT_STATE_TIP_PGD && + decoder->pgd_ip(to_ip, decoder->data)) { + /* Conditional branch leaving filter region */ + decoder->pge = false; + decoder->continuous_period = false; + decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; + decoder->ip = to_ip; + decoder->state.from_ip = decoder->ip; + decoder->state.to_ip = 0; + return 0; + } intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch", decoder->ip); decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC; diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h index 02c38fec1c37..89399985fa4d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h @@ -83,6 +83,7 @@ struct intel_pt_params { int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, uint64_t max_insn_cnt, void *data); + bool (*pgd_ip)(uint64_t ip, void *data); void *data; bool return_compression; uint64_t period; From 2acee108f58045d07475516852d4282ab73904dc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Sep 2016 17:38:48 +0300 Subject: [PATCH 22/27] perf intel-pt: Fix decoding when there are address filters Due to errata SKL014 "Intel PT TIP.PGD May Not Have Target IP Payload", the Intel PT decoder needs to match address filters against TIP.PGD packets. Parse the address filters and implement the decoder's 'pgd_ip()' callback to match the IP against the filter regions. Signed-off-by: Adrian Hunter Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Mathieu Poirier Link: http://lkml.kernel.org/r/1474641528-18776-17-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index c9fec19a7914..dc041d4368c8 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -105,6 +105,7 @@ struct intel_pt { unsigned long num_events; char *filter; + struct addr_filters filts; }; enum switch_state { @@ -550,6 +551,76 @@ out_no_cache: return 0; } +static bool intel_pt_match_pgd_ip(struct intel_pt *pt, uint64_t ip, + uint64_t offset, const char *filename) +{ + struct addr_filter *filt; + bool have_filter = false; + bool hit_tracestop = false; + bool hit_filter = false; + + list_for_each_entry(filt, &pt->filts.head, list) { + if (filt->start) + have_filter = true; + + if ((filename && !filt->filename) || + (!filename && filt->filename) || + (filename && strcmp(filename, filt->filename))) + continue; + + if (!(offset >= filt->addr && offset < filt->addr + filt->size)) + continue; + + intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s hit filter: %s offset %#"PRIx64" size %#"PRIx64"\n", + ip, offset, filename ? filename : "[kernel]", + filt->start ? "filter" : "stop", + filt->addr, filt->size); + + if (filt->start) + hit_filter = true; + else + hit_tracestop = true; + } + + if (!hit_tracestop && !hit_filter) + intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s is not in a filter region\n", + ip, offset, filename ? filename : "[kernel]"); + + return hit_tracestop || (have_filter && !hit_filter); +} + +static int __intel_pt_pgd_ip(uint64_t ip, void *data) +{ + struct intel_pt_queue *ptq = data; + struct thread *thread; + struct addr_location al; + u8 cpumode; + u64 offset; + + if (ip >= ptq->pt->kernel_start) + return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL); + + cpumode = PERF_RECORD_MISC_USER; + + thread = ptq->thread; + if (!thread) + return -EINVAL; + + thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al); + if (!al.map || !al.map->dso) + return -EINVAL; + + offset = al.map->map_ip(al.map, ip); + + return intel_pt_match_pgd_ip(ptq->pt, ip, offset, + al.map->dso->long_name); +} + +static bool intel_pt_pgd_ip(uint64_t ip, void *data) +{ + return __intel_pt_pgd_ip(ip, data) > 0; +} + static bool intel_pt_get_config(struct intel_pt *pt, struct perf_event_attr *attr, u64 *config) { @@ -726,6 +797,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n; params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d; + if (pt->filts.cnt > 0) + params.pgd_ip = intel_pt_pgd_ip; + if (pt->synth_opts.instructions) { if (pt->synth_opts.period) { switch (pt->synth_opts.period_type) { @@ -1776,6 +1850,7 @@ static void intel_pt_free(struct perf_session *session) intel_pt_free_events(session); session->auxtrace = NULL; thread__put(pt->unknown_thread); + addr_filters__exit(&pt->filts); zfree(&pt->filter); free(pt); } @@ -2073,6 +2148,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, if (!pt) return -ENOMEM; + addr_filters__init(&pt->filts); + perf_config(intel_pt_perf_config, pt); err = auxtrace_queues__init(&pt->queues); @@ -2147,6 +2224,10 @@ int intel_pt_process_auxtrace_info(union perf_event *event, err = -EINVAL; goto err_free_queues; } + err = addr_filters__parse_bare_filter(&pt->filts, + filter); + if (err) + goto err_free_queues; } intel_pt_print_info_str("Filter string", pt->filter); } @@ -2268,6 +2349,7 @@ err_free_queues: auxtrace_queues__free(&pt->queues); session->auxtrace = NULL; err_free: + addr_filters__exit(&pt->filts); zfree(&pt->filter); free(pt); return err; From f8da4b5155ed9a639ee4250746b5f7ffa6302bf6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sat, 24 Sep 2016 00:34:57 +0900 Subject: [PATCH 23/27] perf probe: Ignore the error of finding inline instance Ignore the error when the perf probe failed to find inline function instances. This can happen when we search a method in C++ debuginfo. If there is completely no instance in target, perf probe can return an error. E.g. without this fix: ---- $ perf probe -x /usr/lib64/libstdc++.so.6 -vD showmanyc probe-definition(0): showmanyc symbol:showmanyc file:(null) line:0 offset:0 return:0 lazy:(null) 0 arguments symbol:catch file:(null) line:0 offset:0 return:0 lazy:(null) symbol:throw file:(null) line:0 offset:0 return:0 lazy:(null) symbol:rethrow file:(null) line:0 offset:0 return:0 lazy:(null) Open Debuginfo file: /usr/lib/debug/usr/lib64/libstdc++.so.6.0.22.debug Try to find probe point from debuginfo. Matched function: showmanyc An error occurred in debuginfo analysis (-2). Trying to use symbols. Failed to find symbol showmanyc in /usr/lib64/libstdc++.so.6.0.22 Error: Failed to add events. Reason: No such file or directory (Code: -2) ---- This is because one of showmanyc is defined as inline but no instance found. With this fix, it is succeeded to show as below. ---- $ perf probe -x /usr/lib64/libstdc++.so.6 -D showmanyc p:probe_libstdc++/showmanyc /usr/lib64/libstdc++.so.6.0.22:0xb0e50 p:probe_libstdc++/showmanyc_1 /usr/lib64/libstdc++.so.6.0.22:0xc7c40 p:probe_libstdc++/showmanyc_2 /usr/lib64/libstdc++.so.6.0.22:0xecfa0 p:probe_libstdc++/showmanyc_3 /usr/lib64/libstdc++.so.6.0.22:0x115fc0 p:probe_libstdc++/showmanyc_4 /usr/lib64/libstdc++.so.6.0.22:0x121a90 ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Tested-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/147464489775.29804.3190419491209875936.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 8daca4fc1f8d..5fe8325e81f2 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -988,7 +988,8 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) return DWARF_CB_OK; - pr_debug("Matched function: %s\n", dwarf_diename(sp_die)); + pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), + (unsigned long)dwarf_dieoffset(sp_die)); pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); @@ -1011,7 +1012,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) param->retval = die_walk_instances(sp_die, probe_point_inline_cb, (void *)pf); /* This could be a non-existed inline definition */ - if (param->retval == -ENOENT && strisglob(pp->function)) + if (param->retval == -ENOENT) param->retval = 0; } From 0ad45b33c58dca60dec7e1fb44766753bc4a7a38 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sat, 24 Sep 2016 00:35:07 +0900 Subject: [PATCH 24/27] perf probe: Skip if the function address is 0 Skip probes if the entry address of the target function is 0. This can happen when we're handling C++ debuginfo files. E.g. without this fix, below case still fail. ---- $ ./perf probe -x /usr/lib64/libstdc++.so.6 -vD is_open probe-definition(0): is_open symbol:is_open file:(null) line:0 offset:0 return:0 lazy:(null) 0 arguments symbol:catch file:(null) line:0 offset:0 return:0 lazy:(null) symbol:throw file:(null) line:0 offset:0 return:0 lazy:(null) symbol:rethrow file:(null) line:0 offset:0 return:0 lazy:(null) Open Debuginfo file: /usr/lib/debug/usr/lib64/libstdc++.so.6.0.22.debug Try to find probe point from debuginfo. Matched function: is_open [295df] found inline addr: 0x8ca80 Probe point found: is_open+0 found inline addr: 0x8ca70 Probe point found: is_open+0 found inline addr: 0x8ca60 Probe point found: is_open+0 Matched function: is_open [6527f] Matched function: is_open [9fe8a] Probe point found: is_open+0 Matched function: is_open [19710b] found inline addr: 0xecca9 Probe point found: stdio_filebuf+57 found inline addr: 0x0 Probe point found: swap+0 Matched function: is_open [19fc9d] Probe point found: is_open+0 Found 7 probe_trace_events. p:probe_libstdc++/is_open /usr/lib64/libstdc++.so.6.0.22:0x8ca80 p:probe_libstdc++/is_open_1 /usr/lib64/libstdc++.so.6.0.22:0x8ca70 p:probe_libstdc++/is_open_2 /usr/lib64/libstdc++.so.6.0.22:0x8ca60 p:probe_libstdc++/is_open_3 /usr/lib64/libstdc++.so.6.0.22:0xb0ad0 p:probe_libstdc++/is_open_4 /usr/lib64/libstdc++.so.6.0.22:0xecca9 Failed to synthesize probe trace event. Error: Failed to add events. Reason: Invalid argument (Code: -22) ---- This is because some instances have entry_pc == 0 (see 19710b and 19fc9d). With this fix, those are skipped. ---- $ ./perf probe -x /usr/lib64/libstdc++.so.6 -D is_open p:probe_libstdc++/is_open /usr/lib64/libstdc++.so.6.0.22:0x8ca80 p:probe_libstdc++/is_open_1 /usr/lib64/libstdc++.so.6.0.22:0x8ca70 p:probe_libstdc++/is_open_2 /usr/lib64/libstdc++.so.6.0.22:0x8ca60 p:probe_libstdc++/is_open_3 /usr/lib64/libstdc++.so.6.0.22:0xb0ad0 p:probe_libstdc++/is_open_4 /usr/lib64/libstdc++.so.6.0.22:0xecca9 ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Tested-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/147464490707.29804.14277897643725143867.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5fe8325e81f2..df4debe564da 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -955,6 +955,11 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) dwarf_diename(in_die)); return -ENOENT; } + if (addr == 0) { + pr_debug("%s has no valid entry address. skipped.\n", + dwarf_diename(in_die)); + return -ENOENT; + } pf->addr = addr; pf->addr += pp->offset; pr_debug("found inline addr: 0x%jx\n", @@ -998,8 +1003,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) } else if (die_is_func_instance(sp_die)) { /* Instances always have the entry address */ dwarf_entrypc(sp_die, &pf->addr); + /* But in some case the entry address is 0 */ + if (pf->addr == 0) { + pr_debug("%s has no entry PC. Skipped\n", + dwarf_diename(sp_die)); + param->retval = 0; /* Real function */ - if (pp->lazy_line) + } else if (pp->lazy_line) param->retval = find_probe_point_lazy(sp_die, pf); else { skip_prologue(sp_die, pf); From 35726d3a4ca9ce10811032baf6fa0b3529aac087 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sat, 24 Sep 2016 00:35:16 +0900 Subject: [PATCH 25/27] perf probe: Fix to cut off incompatible chars from group name Cut off the characters which can not use for group name of uprobes when making it based on executable filename. For example, if the exec name is libstdc++.so, without this fix perf probe generates "probe_libstdc++" as the group name, but it is failed to set because '+' can not be used for group name. With this fix perf accepts only alphabet, number or '_' for group name, thus perf generates "probe_libstdc" as the group name. E.g. with this fix, you can see the event name has no "+". ---- $ ./perf probe -x /usr/lib64/libstdc++.so.6 -D is_open p:probe_libstdc/is_open /usr/lib64/libstdc++.so.6.0.22:0x8ca80 p:probe_libstdc/is_open_1 /usr/lib64/libstdc++.so.6.0.22:0x8ca70 p:probe_libstdc/is_open_2 /usr/lib64/libstdc++.so.6.0.22:0x8ca60 p:probe_libstdc/is_open_3 /usr/lib64/libstdc++.so.6.0.22:0xb0ad0 p:probe_libstdc/is_open_4 /usr/lib64/libstdc++.so.6.0.22:0xecca9 ---- Committer note: Before this fix: # perf probe -x /usr/lib64/libstdc++.so.6 is_open Failed to write event: Invalid argument Error: Failed to add events. # After the fix: # perf probe -x /usr/lib64/libstdc++.so.6 is_open Added new events: probe_libstdc:is_open (on is_open in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_1 (on is_open in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_2 (on is_open in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_3 (on is_open in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_4 (on is_open in /usr/lib64/libstdc++.so.6.0.22) You can now use it in all perf tools, such as: perf record -e probe_libstdc:is_open_4 -aR sleep 1 # perf probe -l probe_libstdc:* probe_libstdc:is_open (on is_open@libstdc++-v3/include/fstream in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_1 (on is_open@libstdc++-v3/include/fstream in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_2 (on is_open@libstdc++-v3/include/fstream in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_3 (on is_open@src/c++98/basic_file.cc in /usr/lib64/libstdc++.so.6.0.22) probe_libstdc:is_open_4 (on stdio_filebuf:5@include/ext/stdio_filebuf.h in /usr/lib64/libstdc++.so.6.0.22) # Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Tested-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/147464491667.29804.9553638175441827970.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bc60ce49720b..fcfbef07b92d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -213,9 +213,13 @@ static int convert_exec_to_group(const char *exec, char **result) goto out; } - ptr2 = strpbrk(ptr1, "-._"); - if (ptr2) - *ptr2 = '\0'; + for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) { + if (!isalnum(*ptr2) && *ptr2 != '_') { + *ptr2 = '\0'; + break; + } + } + ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); if (ret < 0) goto out; From d5a00296a63fdd049273f86d0a0cdef6b230f8e6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sat, 24 Sep 2016 00:35:31 +0900 Subject: [PATCH 26/27] perf probe: Match linkage name with mangled name Match linkage name with mangled name if exists. The linkage_name is used for storing mangled name of the object. Thus, this allows 'perf probe' to find appropriate probe point from mangled symbol as below. E.g. without this fix: ---- $ perf probe -x /usr/lib64/libstdc++.so.6 \ -D _ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv Probe point '_ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv' not found. Error: Failed to add events. ---- With this fix, perf probe can find the correct one. ---- $ perf probe -x /usr/lib64/libstdc++.so.6 \ -D _ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv p:probe_libstdc/_ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv /usr/lib64/libstdc++.so.6.0.22:0x8ca60 ---- Committer notes: After the fix, setting it for real (no -D/--definition, that amounts to a --dry-run): # perf probe -x /usr/lib64/libstdc++.so.6 _ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv Added new event: probe_libstdc:_ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv (on _ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv in /usr/lib64/libstdc++.so.6.0.22) You can now use it in all perf tools, such as: perf record -e probe_libstdc:_ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv -aR sleep 1 # perf probe -l probe_libstdc:* probe_libstdc:_ZNKSt15basic_fstreamXXIwSt11char_traitsIwEE7is_openEv (on is_open@libstdc++-v3/include/fstream in /usr/lib64/libstdc++.so.6.0.22) # Reported-by: Thomas Gleixner Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Tested-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/147464493162.29804.16715053505069382443.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 28 ++++++++++++++++++++++++++-- tools/perf/util/dwarf-aux.h | 3 +++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index faec899435f2..41e068e94349 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -129,6 +129,22 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, } +/** + * die_get_linkage_name - Get the linkage name of the object + * @dw_die: A DIE of the object + * + * Get the linkage name attiribute of given @dw_die. + * For C++ binary, the linkage name will be the mangled symbol. + */ +const char *die_get_linkage_name(Dwarf_Die *dw_die) +{ + Dwarf_Attribute attr; + + if (dwarf_attr_integrate(dw_die, DW_AT_linkage_name, &attr) == NULL) + return NULL; + return dwarf_formstring(&attr); +} + /** * die_compare_name - Compare diename and tname * @dw_die: a DIE @@ -145,18 +161,26 @@ bool die_compare_name(Dwarf_Die *dw_die, const char *tname) } /** - * die_match_name - Match diename and glob + * die_match_name - Match diename/linkage name and glob * @dw_die: a DIE * @glob: a string of target glob pattern * * Glob matching the name of @dw_die and @glob. Return false if matching fail. + * This also match linkage name. */ bool die_match_name(Dwarf_Die *dw_die, const char *glob) { const char *name; name = dwarf_diename(dw_die); - return name ? strglobmatch(name, glob) : false; + if (name && strglobmatch(name, glob)) + return true; + /* fall back to check linkage name */ + name = die_get_linkage_name(dw_die); + if (name && strglobmatch(name, glob)) + return true; + + return false; } /** diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 8b6d2f83af02..8ac53bf1ec4e 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -38,6 +38,9 @@ int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, int (*callback)(Dwarf_Die *, void *), void *data); +/* Get DW_AT_linkage_name (should be NULL for C binary) */ +const char *die_get_linkage_name(Dwarf_Die *dw_die); + /* Ensure that this DIE is a subprogram and definition (not declaration) */ bool die_is_func_def(Dwarf_Die *dw_die); From d18019a53a07e009899ff6b8dc5ec30f249360d9 Mon Sep 17 00:00:00 2001 From: Ravi Bangoria Date: Mon, 19 Sep 2016 02:38:20 -0400 Subject: [PATCH 27/27] perf tests: Add dwarf unwind test for powerpc The user stack dump feature was recently added for powerpc. But there was no test case available to test it. This test works same as on other architectures by preparing a stack frame on the perf test thread and comparing each frame by unwinding it. $ ./perf test 50 50: Test dwarf unwind : Ok User stack dump for powerpc: https://lkml.org/lkml/2016/4/28/482 Signed-off-by: Ravi Bangoria Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Anju T Sudhakar Cc: Josh Poimboeuf Cc: Masami Hiramatsu Cc: Matt Fleming Cc: Michael Ellerman Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Cc: linuxppc-dev@lists.ozlabs.org Link: http://lkml.kernel.org/r/1474267100-31079-1-git-send-email-ravi.bangoria@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/Build | 1 + tools/perf/arch/powerpc/include/arch-tests.h | 13 +++ tools/perf/arch/powerpc/include/perf_regs.h | 2 + tools/perf/arch/powerpc/tests/Build | 4 + tools/perf/arch/powerpc/tests/arch-tests.c | 15 ++++ tools/perf/arch/powerpc/tests/dwarf-unwind.c | 62 +++++++++++++ tools/perf/arch/powerpc/tests/regs_load.S | 94 ++++++++++++++++++++ tools/perf/tests/Build | 2 +- tools/perf/tests/dwarf-unwind.c | 2 +- 9 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 tools/perf/arch/powerpc/include/arch-tests.h create mode 100644 tools/perf/arch/powerpc/tests/Build create mode 100644 tools/perf/arch/powerpc/tests/arch-tests.c create mode 100644 tools/perf/arch/powerpc/tests/dwarf-unwind.c create mode 100644 tools/perf/arch/powerpc/tests/regs_load.S diff --git a/tools/perf/arch/powerpc/Build b/tools/perf/arch/powerpc/Build index 54afe4a467e7..db52fa22d3a1 100644 --- a/tools/perf/arch/powerpc/Build +++ b/tools/perf/arch/powerpc/Build @@ -1 +1,2 @@ libperf-y += util/ +libperf-y += tests/ diff --git a/tools/perf/arch/powerpc/include/arch-tests.h b/tools/perf/arch/powerpc/include/arch-tests.h new file mode 100644 index 000000000000..84d8dedef2ed --- /dev/null +++ b/tools/perf/arch/powerpc/include/arch-tests.h @@ -0,0 +1,13 @@ +#ifndef ARCH_TESTS_H +#define ARCH_TESTS_H + +#ifdef HAVE_DWARF_UNWIND_SUPPORT +struct thread; +struct perf_sample; +int test__arch_unwind_sample(struct perf_sample *sample, + struct thread *thread); +#endif + +extern struct test arch_tests[]; + +#endif diff --git a/tools/perf/arch/powerpc/include/perf_regs.h b/tools/perf/arch/powerpc/include/perf_regs.h index 75de0e92e71e..c12f4e804f66 100644 --- a/tools/perf/arch/powerpc/include/perf_regs.h +++ b/tools/perf/arch/powerpc/include/perf_regs.h @@ -5,6 +5,8 @@ #include #include +void perf_regs_load(u64 *regs); + #define PERF_REGS_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1) #define PERF_REGS_MAX PERF_REG_POWERPC_MAX #ifdef __powerpc64__ diff --git a/tools/perf/arch/powerpc/tests/Build b/tools/perf/arch/powerpc/tests/Build new file mode 100644 index 000000000000..d827ef384b33 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/Build @@ -0,0 +1,4 @@ +libperf-$(CONFIG_DWARF_UNWIND) += regs_load.o +libperf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o + +libperf-y += arch-tests.o diff --git a/tools/perf/arch/powerpc/tests/arch-tests.c b/tools/perf/arch/powerpc/tests/arch-tests.c new file mode 100644 index 000000000000..e24f46241f40 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/arch-tests.c @@ -0,0 +1,15 @@ +#include +#include "tests/tests.h" +#include "arch-tests.h" + +struct test arch_tests[] = { +#ifdef HAVE_DWARF_UNWIND_SUPPORT + { + .desc = "Test dwarf unwind", + .func = test__dwarf_unwind, + }, +#endif + { + .func = NULL, + }, +}; diff --git a/tools/perf/arch/powerpc/tests/dwarf-unwind.c b/tools/perf/arch/powerpc/tests/dwarf-unwind.c new file mode 100644 index 000000000000..0bac3137ccbd --- /dev/null +++ b/tools/perf/arch/powerpc/tests/dwarf-unwind.c @@ -0,0 +1,62 @@ +#include +#include "perf_regs.h" +#include "thread.h" +#include "map.h" +#include "event.h" +#include "debug.h" +#include "tests/tests.h" +#include "arch-tests.h" + +#define STACK_SIZE 8192 + +static int sample_ustack(struct perf_sample *sample, + struct thread *thread, u64 *regs) +{ + struct stack_dump *stack = &sample->user_stack; + struct map *map; + unsigned long sp; + u64 stack_size, *buf; + + buf = malloc(STACK_SIZE); + if (!buf) { + pr_debug("failed to allocate sample uregs data\n"); + return -1; + } + + sp = (unsigned long) regs[PERF_REG_POWERPC_R1]; + + map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp); + if (!map) { + pr_debug("failed to get stack map\n"); + free(buf); + return -1; + } + + stack_size = map->end - sp; + stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size; + + memcpy(buf, (void *) sp, stack_size); + stack->data = (char *) buf; + stack->size = stack_size; + return 0; +} + +int test__arch_unwind_sample(struct perf_sample *sample, + struct thread *thread) +{ + struct regs_dump *regs = &sample->user_regs; + u64 *buf; + + buf = calloc(1, sizeof(u64) * PERF_REGS_MAX); + if (!buf) { + pr_debug("failed to allocate sample uregs data\n"); + return -1; + } + + perf_regs_load(buf); + regs->abi = PERF_SAMPLE_REGS_ABI; + regs->regs = buf; + regs->mask = PERF_REGS_MASK; + + return sample_ustack(sample, thread, buf); +} diff --git a/tools/perf/arch/powerpc/tests/regs_load.S b/tools/perf/arch/powerpc/tests/regs_load.S new file mode 100644 index 000000000000..d76c9a32f327 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/regs_load.S @@ -0,0 +1,94 @@ +#include + +/* Offset is based on macros from arch/powerpc/include/uapi/asm/ptrace.h. */ +#define R0 0 +#define R1 1 * 8 +#define R2 2 * 8 +#define R3 3 * 8 +#define R4 4 * 8 +#define R5 5 * 8 +#define R6 6 * 8 +#define R7 7 * 8 +#define R8 8 * 8 +#define R9 9 * 8 +#define R10 10 * 8 +#define R11 11 * 8 +#define R12 12 * 8 +#define R13 13 * 8 +#define R14 14 * 8 +#define R15 15 * 8 +#define R16 16 * 8 +#define R17 17 * 8 +#define R18 18 * 8 +#define R19 19 * 8 +#define R20 20 * 8 +#define R21 21 * 8 +#define R22 22 * 8 +#define R23 23 * 8 +#define R24 24 * 8 +#define R25 25 * 8 +#define R26 26 * 8 +#define R27 27 * 8 +#define R28 28 * 8 +#define R29 29 * 8 +#define R30 30 * 8 +#define R31 31 * 8 +#define NIP 32 * 8 +#define CTR 35 * 8 +#define LINK 36 * 8 +#define XER 37 * 8 + +.globl perf_regs_load +perf_regs_load: + std 0, R0(3) + std 1, R1(3) + std 2, R2(3) + std 3, R3(3) + std 4, R4(3) + std 5, R5(3) + std 6, R6(3) + std 7, R7(3) + std 8, R8(3) + std 9, R9(3) + std 10, R10(3) + std 11, R11(3) + std 12, R12(3) + std 13, R13(3) + std 14, R14(3) + std 15, R15(3) + std 16, R16(3) + std 17, R17(3) + std 18, R18(3) + std 19, R19(3) + std 20, R20(3) + std 21, R21(3) + std 22, R22(3) + std 23, R23(3) + std 24, R24(3) + std 25, R25(3) + std 26, R26(3) + std 27, R27(3) + std 28, R28(3) + std 29, R29(3) + std 30, R30(3) + std 31, R31(3) + + /* store NIP */ + mflr 4 + std 4, NIP(3) + + /* Store LR */ + std 4, LINK(3) + + /* Store XER */ + mfxer 4 + std 4, XER(3) + + /* Store CTR */ + mfctr 4 + std 4, CTR(3) + + /* Restore original value of r4 */ + ld 4, R4(3) + + blr diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index dc51bc570e51..8a4ce492f7b2 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -71,7 +71,7 @@ $(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/B $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ $(Q)echo ';' >> $@ -ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) +ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64 powerpc)) perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 8f6eb853aaf7..1046491de4b2 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -11,7 +11,7 @@ #include "thread.h" #include "callchain.h" -#if defined (__x86_64__) || defined (__i386__) +#if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__) #include "arch-tests.h" #endif