diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-03-31 02:33:43 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-03-31 02:33:43 -0400 |
commit | 643cb15ba07260faadd9fcfabac4f5d9d0ddc053 (patch) | |
tree | e57bb3a38ed43eb91daac81c0818ba614e6d6bac | |
parent | c932cf07ddc02f79e093596924e41cf6d7fda509 (diff) | |
parent | d1706b39f0af6901ab2a5e2ebb210b53c1a5bdc7 (diff) |
Merge tag 'perf-core-for-mingo-20160330' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes:
User visible changes:
- Add support for skipping itrace instructions, useful to fast forward
processor trace (Intel PT, BTS) to right after initialization code at the start
of a workload (Andi Kleen)
- Add support for backtraces in perl 'perf script's (Dima Kogan)
- Add -U/-K (--all-user/--all-kernel) options to 'perf mem' (Jiri Olsa)
- Make -f/--force option documentation consistent across tools (Jiri Olsa)
Infrastructure changes:
- Add 'perf test' to check for event times (Jiri Olsa)
- 'perf config' cleanups (Taeung Song)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/perf/Documentation/intel-pt.txt | 7 | ||||
-rw-r--r-- | tools/perf/Documentation/itrace.txt | 8 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-annotate.txt | 2 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-diff.txt | 2 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-mem.txt | 8 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-report.txt | 2 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-script.txt | 4 | ||||
-rw-r--r-- | tools/perf/builtin-mem.c | 11 | ||||
-rw-r--r-- | tools/perf/perf.c | 3 | ||||
-rw-r--r-- | tools/perf/tests/Build | 1 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
-rw-r--r-- | tools/perf/tests/event-times.c | 236 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
-rw-r--r-- | tools/perf/util/auxtrace.c | 7 | ||||
-rw-r--r-- | tools/perf/util/auxtrace.h | 2 | ||||
-rw-r--r-- | tools/perf/util/config.c | 57 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 5 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 2 | ||||
-rw-r--r-- | tools/perf/util/intel-bts.c | 5 | ||||
-rw-r--r-- | tools/perf/util/intel-pt.c | 22 | ||||
-rw-r--r-- | tools/perf/util/scripting-engines/trace-event-perl.c | 114 |
21 files changed, 448 insertions, 55 deletions
diff --git a/tools/perf/Documentation/intel-pt.txt b/tools/perf/Documentation/intel-pt.txt index be764f9ec769..c6c8318e38a2 100644 --- a/tools/perf/Documentation/intel-pt.txt +++ b/tools/perf/Documentation/intel-pt.txt | |||
@@ -672,6 +672,7 @@ The letters are: | |||
672 | d create a debug log | 672 | d create a debug log |
673 | g synthesize a call chain (use with i or x) | 673 | g synthesize a call chain (use with i or x) |
674 | l synthesize last branch entries (use with i or x) | 674 | l synthesize last branch entries (use with i or x) |
675 | s skip initial number of events | ||
675 | 676 | ||
676 | "Instructions" events look like they were recorded by "perf record -e | 677 | "Instructions" events look like they were recorded by "perf record -e |
677 | instructions". | 678 | instructions". |
@@ -730,6 +731,12 @@ from one sample to the next. | |||
730 | 731 | ||
731 | To disable trace decoding entirely, use the option --no-itrace. | 732 | To disable trace decoding entirely, use the option --no-itrace. |
732 | 733 | ||
734 | It is also possible to skip events generated (instructions, branches, transactions) | ||
735 | at the beginning. This is useful to ignore initialization code. | ||
736 | |||
737 | --itrace=i0nss1000000 | ||
738 | |||
739 | skips the first million instructions. | ||
733 | 740 | ||
734 | dump option | 741 | dump option |
735 | ----------- | 742 | ----------- |
diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt index 65453f4c7006..e2a4c5e0dbe5 100644 --- a/tools/perf/Documentation/itrace.txt +++ b/tools/perf/Documentation/itrace.txt | |||
@@ -7,6 +7,7 @@ | |||
7 | d create a debug log | 7 | d create a debug log |
8 | g synthesize a call chain (use with i or x) | 8 | g synthesize a call chain (use with i or x) |
9 | l synthesize last branch entries (use with i or x) | 9 | l synthesize last branch entries (use with i or x) |
10 | s skip initial number of events | ||
10 | 11 | ||
11 | The default is all events i.e. the same as --itrace=ibxe | 12 | The default is all events i.e. the same as --itrace=ibxe |
12 | 13 | ||
@@ -24,3 +25,10 @@ | |||
24 | 25 | ||
25 | Also the number of last branch entries (default 64, max. 1024) for | 26 | Also the number of last branch entries (default 64, max. 1024) for |
26 | instructions or transactions events can be specified. | 27 | instructions or transactions events can be specified. |
28 | |||
29 | It is also possible to skip events generated (instructions, branches, transactions) | ||
30 | at the beginning. This is useful to ignore initialization code. | ||
31 | |||
32 | --itrace=i0nss1000000 | ||
33 | |||
34 | skips the first million instructions. | ||
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index e9cd39a92dc2..778f54d4d0bd 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -33,7 +33,7 @@ OPTIONS | |||
33 | 33 | ||
34 | -f:: | 34 | -f:: |
35 | --force:: | 35 | --force:: |
36 | Don't complain, do it. | 36 | Don't do ownership validation. |
37 | 37 | ||
38 | -v:: | 38 | -v:: |
39 | --verbose:: | 39 | --verbose:: |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index d1deb573877f..3e9490b9c533 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -75,7 +75,7 @@ OPTIONS | |||
75 | 75 | ||
76 | -f:: | 76 | -f:: |
77 | --force:: | 77 | --force:: |
78 | Don't complain, do it. | 78 | Don't do ownership validation. |
79 | 79 | ||
80 | --symfs=<directory>:: | 80 | --symfs=<directory>:: |
81 | Look for files with symbols relative to this directory. | 81 | Look for files with symbols relative to this directory. |
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 43310d8661fe..1d6092c460dd 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt | |||
@@ -48,6 +48,14 @@ OPTIONS | |||
48 | option can be passed in record mode. It will be interpreted the same way as perf | 48 | option can be passed in record mode. It will be interpreted the same way as perf |
49 | record. | 49 | record. |
50 | 50 | ||
51 | -K:: | ||
52 | --all-kernel:: | ||
53 | Configure all used events to run in kernel space. | ||
54 | |||
55 | -U:: | ||
56 | --all-user:: | ||
57 | Configure all used events to run in user space. | ||
58 | |||
51 | SEE ALSO | 59 | SEE ALSO |
52 | -------- | 60 | -------- |
53 | linkperf:perf-record[1], linkperf:perf-report[1] | 61 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 12113992ac9d..496d42cdf02b 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -285,7 +285,7 @@ OPTIONS | |||
285 | 285 | ||
286 | -f:: | 286 | -f:: |
287 | --force:: | 287 | --force:: |
288 | Don't complain, do it. | 288 | Don't do ownership validation. |
289 | 289 | ||
290 | --symfs=<directory>:: | 290 | --symfs=<directory>:: |
291 | Look for files with symbols relative to this directory. | 291 | Look for files with symbols relative to this directory. |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 382ddfb45d1d..22ef3933342a 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -262,6 +262,10 @@ include::itrace.txt[] | |||
262 | --ns:: | 262 | --ns:: |
263 | Use 9 decimal places when displaying time (i.e. show the nanoseconds) | 263 | Use 9 decimal places when displaying time (i.e. show the nanoseconds) |
264 | 264 | ||
265 | -f:: | ||
266 | --force:: | ||
267 | Don't do ownership validation. | ||
268 | |||
265 | SEE ALSO | 269 | SEE ALSO |
266 | -------- | 270 | -------- |
267 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 271 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 85db3be4b3cb..1dc140c5481d 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -62,19 +62,22 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) | |||
62 | int rec_argc, i = 0, j; | 62 | int rec_argc, i = 0, j; |
63 | const char **rec_argv; | 63 | const char **rec_argv; |
64 | int ret; | 64 | int ret; |
65 | bool all_user = false, all_kernel = false; | ||
65 | struct option options[] = { | 66 | struct option options[] = { |
66 | OPT_CALLBACK('e', "event", &mem, "event", | 67 | OPT_CALLBACK('e', "event", &mem, "event", |
67 | "event selector. use 'perf mem record -e list' to list available events", | 68 | "event selector. use 'perf mem record -e list' to list available events", |
68 | parse_record_events), | 69 | parse_record_events), |
69 | OPT_INCR('v', "verbose", &verbose, | 70 | OPT_INCR('v', "verbose", &verbose, |
70 | "be more verbose (show counter open errors, etc)"), | 71 | "be more verbose (show counter open errors, etc)"), |
72 | OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"), | ||
73 | OPT_BOOLEAN('K', "--all-kernel", &all_kernel, "collect only kernel level data"), | ||
71 | OPT_END() | 74 | OPT_END() |
72 | }; | 75 | }; |
73 | 76 | ||
74 | argc = parse_options(argc, argv, options, record_mem_usage, | 77 | argc = parse_options(argc, argv, options, record_mem_usage, |
75 | PARSE_OPT_STOP_AT_NON_OPTION); | 78 | PARSE_OPT_STOP_AT_NON_OPTION); |
76 | 79 | ||
77 | rec_argc = argc + 7; /* max number of arguments */ | 80 | rec_argc = argc + 9; /* max number of arguments */ |
78 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 81 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
79 | if (!rec_argv) | 82 | if (!rec_argv) |
80 | return -1; | 83 | return -1; |
@@ -103,6 +106,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) | |||
103 | rec_argv[i++] = perf_mem_events__name(j); | 106 | rec_argv[i++] = perf_mem_events__name(j); |
104 | }; | 107 | }; |
105 | 108 | ||
109 | if (all_user) | ||
110 | rec_argv[i++] = "--all-user"; | ||
111 | |||
112 | if (all_kernel) | ||
113 | rec_argv[i++] = "--all-kernel"; | ||
114 | |||
106 | for (j = 0; j < argc; j++, i++) | 115 | for (j = 0; j < argc; j++, i++) |
107 | rec_argv[i] = argv[j]; | 116 | rec_argv[i] = argv[j]; |
108 | 117 | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index aaee0a782747..7b2df2b46525 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -549,6 +549,7 @@ int main(int argc, const char **argv) | |||
549 | srandom(time(NULL)); | 549 | srandom(time(NULL)); |
550 | 550 | ||
551 | perf_config(perf_default_config, NULL); | 551 | perf_config(perf_default_config, NULL); |
552 | set_buildid_dir(NULL); | ||
552 | 553 | ||
553 | /* get debugfs/tracefs mount point from /proc/mounts */ | 554 | /* get debugfs/tracefs mount point from /proc/mounts */ |
554 | tracing_path_mount(); | 555 | tracing_path_mount(); |
@@ -572,7 +573,6 @@ int main(int argc, const char **argv) | |||
572 | } | 573 | } |
573 | if (!prefixcmp(cmd, "trace")) { | 574 | if (!prefixcmp(cmd, "trace")) { |
574 | #ifdef HAVE_LIBAUDIT_SUPPORT | 575 | #ifdef HAVE_LIBAUDIT_SUPPORT |
575 | set_buildid_dir(NULL); | ||
576 | setup_path(); | 576 | setup_path(); |
577 | argv[0] = "trace"; | 577 | argv[0] = "trace"; |
578 | return cmd_trace(argc, argv, NULL); | 578 | return cmd_trace(argc, argv, NULL); |
@@ -587,7 +587,6 @@ int main(int argc, const char **argv) | |||
587 | argc--; | 587 | argc--; |
588 | handle_options(&argv, &argc, NULL); | 588 | handle_options(&argv, &argc, NULL); |
589 | commit_pager_choice(); | 589 | commit_pager_choice(); |
590 | set_buildid_dir(NULL); | ||
591 | 590 | ||
592 | if (argc > 0) { | 591 | if (argc > 0) { |
593 | if (!prefixcmp(argv[0], "--")) | 592 | if (!prefixcmp(argv[0], "--")) |
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 1ba628ed049a..449fe97a555f 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -37,6 +37,7 @@ perf-y += topology.o | |||
37 | perf-y += cpumap.o | 37 | perf-y += cpumap.o |
38 | perf-y += stat.o | 38 | perf-y += stat.o |
39 | perf-y += event_update.o | 39 | perf-y += event_update.o |
40 | perf-y += event-times.o | ||
40 | 41 | ||
41 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build | 42 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build |
42 | $(call rule_mkdir) | 43 | $(call rule_mkdir) |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index f2b1dcac45d3..93c467015e71 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -204,6 +204,10 @@ static struct test generic_tests[] = { | |||
204 | .func = test__event_update, | 204 | .func = test__event_update, |
205 | }, | 205 | }, |
206 | { | 206 | { |
207 | .desc = "Test events times", | ||
208 | .func = test__event_times, | ||
209 | }, | ||
210 | { | ||
207 | .func = NULL, | 211 | .func = NULL, |
208 | }, | 212 | }, |
209 | }; | 213 | }; |
diff --git a/tools/perf/tests/event-times.c b/tools/perf/tests/event-times.c new file mode 100644 index 000000000000..95fb744f6628 --- /dev/null +++ b/tools/perf/tests/event-times.c | |||
@@ -0,0 +1,236 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <string.h> | ||
3 | #include "tests.h" | ||
4 | #include "evlist.h" | ||
5 | #include "evsel.h" | ||
6 | #include "util.h" | ||
7 | #include "debug.h" | ||
8 | #include "thread_map.h" | ||
9 | #include "target.h" | ||
10 | |||
11 | static int attach__enable_on_exec(struct perf_evlist *evlist) | ||
12 | { | ||
13 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
14 | struct target target = { | ||
15 | .uid = UINT_MAX, | ||
16 | }; | ||
17 | const char *argv[] = { "true", NULL, }; | ||
18 | char sbuf[STRERR_BUFSIZE]; | ||
19 | int err; | ||
20 | |||
21 | pr_debug("attaching to spawned child, enable on exec\n"); | ||
22 | |||
23 | err = perf_evlist__create_maps(evlist, &target); | ||
24 | if (err < 0) { | ||
25 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
26 | return err; | ||
27 | } | ||
28 | |||
29 | err = perf_evlist__prepare_workload(evlist, &target, argv, false, NULL); | ||
30 | if (err < 0) { | ||
31 | pr_debug("Couldn't run the workload!\n"); | ||
32 | return err; | ||
33 | } | ||
34 | |||
35 | evsel->attr.enable_on_exec = 1; | ||
36 | |||
37 | err = perf_evlist__open(evlist); | ||
38 | if (err < 0) { | ||
39 | pr_debug("perf_evlist__open: %s\n", | ||
40 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
41 | return err; | ||
42 | } | ||
43 | |||
44 | return perf_evlist__start_workload(evlist) == 1 ? TEST_OK : TEST_FAIL; | ||
45 | } | ||
46 | |||
47 | static int detach__enable_on_exec(struct perf_evlist *evlist) | ||
48 | { | ||
49 | waitpid(evlist->workload.pid, NULL, 0); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int attach__current_disabled(struct perf_evlist *evlist) | ||
54 | { | ||
55 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
56 | struct thread_map *threads; | ||
57 | int err; | ||
58 | |||
59 | pr_debug("attaching to current thread as disabled\n"); | ||
60 | |||
61 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
62 | if (threads == NULL) { | ||
63 | pr_debug("thread_map__new\n"); | ||
64 | return -1; | ||
65 | } | ||
66 | |||
67 | evsel->attr.disabled = 1; | ||
68 | |||
69 | err = perf_evsel__open_per_thread(evsel, threads); | ||
70 | if (err) { | ||
71 | pr_debug("Failed to open event cpu-clock:u\n"); | ||
72 | return err; | ||
73 | } | ||
74 | |||
75 | thread_map__put(threads); | ||
76 | return perf_evsel__enable(evsel) == 0 ? TEST_OK : TEST_FAIL; | ||
77 | } | ||
78 | |||
79 | static int attach__current_enabled(struct perf_evlist *evlist) | ||
80 | { | ||
81 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
82 | struct thread_map *threads; | ||
83 | int err; | ||
84 | |||
85 | pr_debug("attaching to current thread as enabled\n"); | ||
86 | |||
87 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
88 | if (threads == NULL) { | ||
89 | pr_debug("failed to call thread_map__new\n"); | ||
90 | return -1; | ||
91 | } | ||
92 | |||
93 | err = perf_evsel__open_per_thread(evsel, threads); | ||
94 | |||
95 | thread_map__put(threads); | ||
96 | return err == 0 ? TEST_OK : TEST_FAIL; | ||
97 | } | ||
98 | |||
99 | static int detach__disable(struct perf_evlist *evlist) | ||
100 | { | ||
101 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
102 | |||
103 | return perf_evsel__enable(evsel); | ||
104 | } | ||
105 | |||
106 | static int attach__cpu_disabled(struct perf_evlist *evlist) | ||
107 | { | ||
108 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
109 | struct cpu_map *cpus; | ||
110 | int err; | ||
111 | |||
112 | pr_debug("attaching to CPU 0 as enabled\n"); | ||
113 | |||
114 | cpus = cpu_map__new("0"); | ||
115 | if (cpus == NULL) { | ||
116 | pr_debug("failed to call cpu_map__new\n"); | ||
117 | return -1; | ||
118 | } | ||
119 | |||
120 | evsel->attr.disabled = 1; | ||
121 | |||
122 | err = perf_evsel__open_per_cpu(evsel, cpus); | ||
123 | if (err) { | ||
124 | if (err == -EACCES) | ||
125 | return TEST_SKIP; | ||
126 | |||
127 | pr_debug("Failed to open event cpu-clock:u\n"); | ||
128 | return err; | ||
129 | } | ||
130 | |||
131 | cpu_map__put(cpus); | ||
132 | return perf_evsel__enable(evsel); | ||
133 | } | ||
134 | |||
135 | static int attach__cpu_enabled(struct perf_evlist *evlist) | ||
136 | { | ||
137 | struct perf_evsel *evsel = perf_evlist__last(evlist); | ||
138 | struct cpu_map *cpus; | ||
139 | int err; | ||
140 | |||
141 | pr_debug("attaching to CPU 0 as enabled\n"); | ||
142 | |||
143 | cpus = cpu_map__new("0"); | ||
144 | if (cpus == NULL) { | ||
145 | pr_debug("failed to call cpu_map__new\n"); | ||
146 | return -1; | ||
147 | } | ||
148 | |||
149 | err = perf_evsel__open_per_cpu(evsel, cpus); | ||
150 | if (err == -EACCES) | ||
151 | return TEST_SKIP; | ||
152 | |||
153 | cpu_map__put(cpus); | ||
154 | return err ? TEST_FAIL : TEST_OK; | ||
155 | } | ||
156 | |||
157 | static int test_times(int (attach)(struct perf_evlist *), | ||
158 | int (detach)(struct perf_evlist *)) | ||
159 | { | ||
160 | struct perf_counts_values count; | ||
161 | struct perf_evlist *evlist = NULL; | ||
162 | struct perf_evsel *evsel; | ||
163 | int err = -1, i; | ||
164 | |||
165 | evlist = perf_evlist__new(); | ||
166 | if (!evlist) { | ||
167 | pr_debug("failed to create event list\n"); | ||
168 | goto out_err; | ||
169 | } | ||
170 | |||
171 | err = parse_events(evlist, "cpu-clock:u", NULL); | ||
172 | if (err) { | ||
173 | pr_debug("failed to parse event cpu-clock:u\n"); | ||
174 | goto out_err; | ||
175 | } | ||
176 | |||
177 | evsel = perf_evlist__last(evlist); | ||
178 | evsel->attr.read_format |= | ||
179 | PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
180 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
181 | |||
182 | err = attach(evlist); | ||
183 | if (err == TEST_SKIP) { | ||
184 | pr_debug(" SKIP : not enough rights\n"); | ||
185 | return err; | ||
186 | } | ||
187 | |||
188 | TEST_ASSERT_VAL("failed to attach", !err); | ||
189 | |||
190 | for (i = 0; i < 100000000; i++) { } | ||
191 | |||
192 | TEST_ASSERT_VAL("failed to detach", !detach(evlist)); | ||
193 | |||
194 | perf_evsel__read(evsel, 0, 0, &count); | ||
195 | |||
196 | err = !(count.ena == count.run); | ||
197 | |||
198 | pr_debug(" %s: ena %" PRIu64", run %" PRIu64"\n", | ||
199 | !err ? "OK " : "FAILED", | ||
200 | count.ena, count.run); | ||
201 | |||
202 | out_err: | ||
203 | if (evlist) | ||
204 | perf_evlist__delete(evlist); | ||
205 | return !err ? TEST_OK : TEST_FAIL; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * This test creates software event 'cpu-clock' | ||
210 | * attaches it in several ways (explained below) | ||
211 | * and checks that enabled and running times | ||
212 | * match. | ||
213 | */ | ||
214 | int test__event_times(int subtest __maybe_unused) | ||
215 | { | ||
216 | int err, ret = 0; | ||
217 | |||
218 | #define _T(attach, detach) \ | ||
219 | err = test_times(attach, detach); \ | ||
220 | if (err && (ret == TEST_OK || ret == TEST_SKIP)) \ | ||
221 | ret = err; | ||
222 | |||
223 | /* attach on newly spawned process after exec */ | ||
224 | _T(attach__enable_on_exec, detach__enable_on_exec) | ||
225 | /* attach on current process as enabled */ | ||
226 | _T(attach__current_enabled, detach__disable) | ||
227 | /* attach on current process as disabled */ | ||
228 | _T(attach__current_disabled, detach__disable) | ||
229 | /* attach on cpu as disabled */ | ||
230 | _T(attach__cpu_disabled, detach__disable) | ||
231 | /* attach on cpu as enabled */ | ||
232 | _T(attach__cpu_enabled, detach__disable) | ||
233 | |||
234 | #undef _T | ||
235 | return ret; | ||
236 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 82b2b5e6ba7c..0fc946989cf0 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -85,6 +85,7 @@ int test__synthesize_stat_config(int subtest); | |||
85 | int test__synthesize_stat(int subtest); | 85 | int test__synthesize_stat(int subtest); |
86 | int test__synthesize_stat_round(int subtest); | 86 | int test__synthesize_stat_round(int subtest); |
87 | int test__event_update(int subtest); | 87 | int test__event_update(int subtest); |
88 | int test__event_times(int subtest); | ||
88 | 89 | ||
89 | #if defined(__arm__) || defined(__aarch64__) | 90 | #if defined(__arm__) || defined(__aarch64__) |
90 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 91 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index ec164fe70718..c9169011e55e 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -940,6 +940,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts) | |||
940 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; | 940 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; |
941 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; | 941 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; |
942 | synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ; | 942 | synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ; |
943 | synth_opts->initial_skip = 0; | ||
943 | } | 944 | } |
944 | 945 | ||
945 | /* | 946 | /* |
@@ -1064,6 +1065,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str, | |||
1064 | synth_opts->last_branch_sz = val; | 1065 | synth_opts->last_branch_sz = val; |
1065 | } | 1066 | } |
1066 | break; | 1067 | break; |
1068 | case 's': | ||
1069 | synth_opts->initial_skip = strtoul(p, &endptr, 10); | ||
1070 | if (p == endptr) | ||
1071 | goto out_err; | ||
1072 | p = endptr; | ||
1073 | break; | ||
1067 | case ' ': | 1074 | case ' ': |
1068 | case ',': | 1075 | case ',': |
1069 | break; | 1076 | break; |
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 57ff31ecb8e4..767989e0e312 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -68,6 +68,7 @@ enum itrace_period_type { | |||
68 | * @last_branch_sz: branch context size | 68 | * @last_branch_sz: branch context size |
69 | * @period: 'instructions' events period | 69 | * @period: 'instructions' events period |
70 | * @period_type: 'instructions' events period type | 70 | * @period_type: 'instructions' events period type |
71 | * @initial_skip: skip N events at the beginning. | ||
71 | */ | 72 | */ |
72 | struct itrace_synth_opts { | 73 | struct itrace_synth_opts { |
73 | bool set; | 74 | bool set; |
@@ -86,6 +87,7 @@ struct itrace_synth_opts { | |||
86 | unsigned int last_branch_sz; | 87 | unsigned int last_branch_sz; |
87 | unsigned long long period; | 88 | unsigned long long period; |
88 | enum itrace_period_type period_type; | 89 | enum itrace_period_type period_type; |
90 | unsigned long initial_skip; | ||
89 | }; | 91 | }; |
90 | 92 | ||
91 | /** | 93 | /** |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 4e727635476e..5c20d783423b 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -377,6 +377,21 @@ const char *perf_config_dirname(const char *name, const char *value) | |||
377 | return value; | 377 | return value; |
378 | } | 378 | } |
379 | 379 | ||
380 | static int perf_buildid_config(const char *var, const char *value) | ||
381 | { | ||
382 | /* same dir for all commands */ | ||
383 | if (!strcmp(var, "buildid.dir")) { | ||
384 | const char *dirname = perf_config_dirname(var, value); | ||
385 | |||
386 | if (!dirname) | ||
387 | return -1; | ||
388 | strncpy(buildid_dir, dirname, MAXPATHLEN-1); | ||
389 | buildid_dir[MAXPATHLEN-1] = '\0'; | ||
390 | } | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
380 | static int perf_default_core_config(const char *var __maybe_unused, | 395 | static int perf_default_core_config(const char *var __maybe_unused, |
381 | const char *value __maybe_unused) | 396 | const char *value __maybe_unused) |
382 | { | 397 | { |
@@ -412,6 +427,9 @@ int perf_default_config(const char *var, const char *value, | |||
412 | if (!prefixcmp(var, "llvm.")) | 427 | if (!prefixcmp(var, "llvm.")) |
413 | return perf_llvm_config(var, value); | 428 | return perf_llvm_config(var, value); |
414 | 429 | ||
430 | if (!prefixcmp(var, "buildid.")) | ||
431 | return perf_buildid_config(var, value); | ||
432 | |||
415 | /* Add other config variables here. */ | 433 | /* Add other config variables here. */ |
416 | return 0; | 434 | return 0; |
417 | } | 435 | } |
@@ -515,49 +533,18 @@ int config_error_nonbool(const char *var) | |||
515 | return error("Missing value for '%s'", var); | 533 | return error("Missing value for '%s'", var); |
516 | } | 534 | } |
517 | 535 | ||
518 | struct buildid_dir_config { | ||
519 | char *dir; | ||
520 | }; | ||
521 | |||
522 | static int buildid_dir_command_config(const char *var, const char *value, | ||
523 | void *data) | ||
524 | { | ||
525 | struct buildid_dir_config *c = data; | ||
526 | const char *v; | ||
527 | |||
528 | /* same dir for all commands */ | ||
529 | if (!strcmp(var, "buildid.dir")) { | ||
530 | v = perf_config_dirname(var, value); | ||
531 | if (!v) | ||
532 | return -1; | ||
533 | strncpy(c->dir, v, MAXPATHLEN-1); | ||
534 | c->dir[MAXPATHLEN-1] = '\0'; | ||
535 | } | ||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | static void check_buildid_dir_config(void) | ||
540 | { | ||
541 | struct buildid_dir_config c; | ||
542 | c.dir = buildid_dir; | ||
543 | perf_config(buildid_dir_command_config, &c); | ||
544 | } | ||
545 | |||
546 | void set_buildid_dir(const char *dir) | 536 | void set_buildid_dir(const char *dir) |
547 | { | 537 | { |
548 | if (dir) | 538 | if (dir) |
549 | scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); | 539 | scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); |
550 | 540 | ||
551 | /* try config file */ | ||
552 | if (buildid_dir[0] == '\0') | ||
553 | check_buildid_dir_config(); | ||
554 | |||
555 | /* default to $HOME/.debug */ | 541 | /* default to $HOME/.debug */ |
556 | if (buildid_dir[0] == '\0') { | 542 | if (buildid_dir[0] == '\0') { |
557 | char *v = getenv("HOME"); | 543 | char *home = getenv("HOME"); |
558 | if (v) { | 544 | |
545 | if (home) { | ||
559 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", | 546 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", |
560 | v, DEBUG_CACHE_DIR); | 547 | home, DEBUG_CACHE_DIR); |
561 | } else { | 548 | } else { |
562 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); | 549 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); |
563 | } | 550 | } |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 31c4641fe5ff..3d34c57dfbe2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1295,8 +1295,9 @@ static int hists__hierarchy_insert_entry(struct hists *hists, | |||
1295 | return ret; | 1295 | return ret; |
1296 | } | 1296 | } |
1297 | 1297 | ||
1298 | int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root, | 1298 | static int hists__collapse_insert_entry(struct hists *hists, |
1299 | struct hist_entry *he) | 1299 | struct rb_root *root, |
1300 | struct hist_entry *he) | ||
1300 | { | 1301 | { |
1301 | struct rb_node **p = &root->rb_node; | 1302 | struct rb_node **p = &root->rb_node; |
1302 | struct rb_node *parent = NULL; | 1303 | struct rb_node *parent = NULL; |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index bec0cd660fbd..588596561cb3 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -199,8 +199,6 @@ int hists__init(void); | |||
199 | int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list); | 199 | int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list); |
200 | 200 | ||
201 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists); | 201 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists); |
202 | int hists__collapse_insert_entry(struct hists *hists, | ||
203 | struct rb_root *root, struct hist_entry *he); | ||
204 | 202 | ||
205 | struct perf_hpp { | 203 | struct perf_hpp { |
206 | char *buf; | 204 | char *buf; |
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index abf1366e2a24..9df996085563 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c | |||
@@ -66,6 +66,7 @@ struct intel_bts { | |||
66 | u64 branches_id; | 66 | u64 branches_id; |
67 | size_t branches_event_size; | 67 | size_t branches_event_size; |
68 | bool synth_needs_swap; | 68 | bool synth_needs_swap; |
69 | unsigned long num_events; | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | struct intel_bts_queue { | 72 | struct intel_bts_queue { |
@@ -275,6 +276,10 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | |||
275 | union perf_event event; | 276 | union perf_event event; |
276 | struct perf_sample sample = { .ip = 0, }; | 277 | struct perf_sample sample = { .ip = 0, }; |
277 | 278 | ||
279 | if (bts->synth_opts.initial_skip && | ||
280 | bts->num_events++ <= bts->synth_opts.initial_skip) | ||
281 | return 0; | ||
282 | |||
278 | event.sample.header.type = PERF_RECORD_SAMPLE; | 283 | event.sample.header.type = PERF_RECORD_SAMPLE; |
279 | event.sample.header.misc = PERF_RECORD_MISC_USER; | 284 | event.sample.header.misc = PERF_RECORD_MISC_USER; |
280 | event.sample.header.size = sizeof(struct perf_event_header); | 285 | event.sample.header.size = sizeof(struct perf_event_header); |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 407f11b97c8d..ddec87f6e616 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -100,6 +100,8 @@ struct intel_pt { | |||
100 | u64 cyc_bit; | 100 | u64 cyc_bit; |
101 | u64 noretcomp_bit; | 101 | u64 noretcomp_bit; |
102 | unsigned max_non_turbo_ratio; | 102 | unsigned max_non_turbo_ratio; |
103 | |||
104 | unsigned long num_events; | ||
103 | }; | 105 | }; |
104 | 106 | ||
105 | enum switch_state { | 107 | enum switch_state { |
@@ -972,6 +974,10 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) | |||
972 | if (pt->branches_filter && !(pt->branches_filter & ptq->flags)) | 974 | if (pt->branches_filter && !(pt->branches_filter & ptq->flags)) |
973 | return 0; | 975 | return 0; |
974 | 976 | ||
977 | if (pt->synth_opts.initial_skip && | ||
978 | pt->num_events++ < pt->synth_opts.initial_skip) | ||
979 | return 0; | ||
980 | |||
975 | event->sample.header.type = PERF_RECORD_SAMPLE; | 981 | event->sample.header.type = PERF_RECORD_SAMPLE; |
976 | event->sample.header.misc = PERF_RECORD_MISC_USER; | 982 | event->sample.header.misc = PERF_RECORD_MISC_USER; |
977 | event->sample.header.size = sizeof(struct perf_event_header); | 983 | event->sample.header.size = sizeof(struct perf_event_header); |
@@ -1029,6 +1035,10 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) | |||
1029 | union perf_event *event = ptq->event_buf; | 1035 | union perf_event *event = ptq->event_buf; |
1030 | struct perf_sample sample = { .ip = 0, }; | 1036 | struct perf_sample sample = { .ip = 0, }; |
1031 | 1037 | ||
1038 | if (pt->synth_opts.initial_skip && | ||
1039 | pt->num_events++ < pt->synth_opts.initial_skip) | ||
1040 | return 0; | ||
1041 | |||
1032 | event->sample.header.type = PERF_RECORD_SAMPLE; | 1042 | event->sample.header.type = PERF_RECORD_SAMPLE; |
1033 | event->sample.header.misc = PERF_RECORD_MISC_USER; | 1043 | event->sample.header.misc = PERF_RECORD_MISC_USER; |
1034 | event->sample.header.size = sizeof(struct perf_event_header); | 1044 | event->sample.header.size = sizeof(struct perf_event_header); |
@@ -1087,6 +1097,10 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) | |||
1087 | union perf_event *event = ptq->event_buf; | 1097 | union perf_event *event = ptq->event_buf; |
1088 | struct perf_sample sample = { .ip = 0, }; | 1098 | struct perf_sample sample = { .ip = 0, }; |
1089 | 1099 | ||
1100 | if (pt->synth_opts.initial_skip && | ||
1101 | pt->num_events++ < pt->synth_opts.initial_skip) | ||
1102 | return 0; | ||
1103 | |||
1090 | event->sample.header.type = PERF_RECORD_SAMPLE; | 1104 | event->sample.header.type = PERF_RECORD_SAMPLE; |
1091 | event->sample.header.misc = PERF_RECORD_MISC_USER; | 1105 | event->sample.header.misc = PERF_RECORD_MISC_USER; |
1092 | event->sample.header.size = sizeof(struct perf_event_header); | 1106 | event->sample.header.size = sizeof(struct perf_event_header); |
@@ -1199,14 +1213,18 @@ static int intel_pt_sample(struct intel_pt_queue *ptq) | |||
1199 | ptq->have_sample = false; | 1213 | ptq->have_sample = false; |
1200 | 1214 | ||
1201 | if (pt->sample_instructions && | 1215 | if (pt->sample_instructions && |
1202 | (state->type & INTEL_PT_INSTRUCTION)) { | 1216 | (state->type & INTEL_PT_INSTRUCTION) && |
1217 | (!pt->synth_opts.initial_skip || | ||
1218 | pt->num_events++ >= pt->synth_opts.initial_skip)) { | ||
1203 | err = intel_pt_synth_instruction_sample(ptq); | 1219 | err = intel_pt_synth_instruction_sample(ptq); |
1204 | if (err) | 1220 | if (err) |
1205 | return err; | 1221 | return err; |
1206 | } | 1222 | } |
1207 | 1223 | ||
1208 | if (pt->sample_transactions && | 1224 | if (pt->sample_transactions && |
1209 | (state->type & INTEL_PT_TRANSACTION)) { | 1225 | (state->type & INTEL_PT_TRANSACTION) && |
1226 | (!pt->synth_opts.initial_skip || | ||
1227 | pt->num_events++ >= pt->synth_opts.initial_skip)) { | ||
1210 | err = intel_pt_synth_transaction_sample(ptq); | 1228 | err = intel_pt_synth_transaction_sample(ptq); |
1211 | if (err) | 1229 | if (err) |
1212 | return err; | 1230 | return err; |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b3aabc0d4eb0..1d160855cda9 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -31,6 +31,8 @@ | |||
31 | #include <perl.h> | 31 | #include <perl.h> |
32 | 32 | ||
33 | #include "../../perf.h" | 33 | #include "../../perf.h" |
34 | #include "../callchain.h" | ||
35 | #include "../machine.h" | ||
34 | #include "../thread.h" | 36 | #include "../thread.h" |
35 | #include "../event.h" | 37 | #include "../event.h" |
36 | #include "../trace-event.h" | 38 | #include "../trace-event.h" |
@@ -248,10 +250,78 @@ static void define_event_symbols(struct event_format *event, | |||
248 | define_event_symbols(event, ev_name, args->next); | 250 | define_event_symbols(event, ev_name, args->next); |
249 | } | 251 | } |
250 | 252 | ||
253 | static SV *perl_process_callchain(struct perf_sample *sample, | ||
254 | struct perf_evsel *evsel, | ||
255 | struct addr_location *al) | ||
256 | { | ||
257 | AV *list; | ||
258 | |||
259 | list = newAV(); | ||
260 | if (!list) | ||
261 | goto exit; | ||
262 | |||
263 | if (!symbol_conf.use_callchain || !sample->callchain) | ||
264 | goto exit; | ||
265 | |||
266 | if (thread__resolve_callchain(al->thread, evsel, | ||
267 | sample, NULL, NULL, | ||
268 | PERF_MAX_STACK_DEPTH) != 0) { | ||
269 | pr_err("Failed to resolve callchain. Skipping\n"); | ||
270 | goto exit; | ||
271 | } | ||
272 | callchain_cursor_commit(&callchain_cursor); | ||
273 | |||
274 | |||
275 | while (1) { | ||
276 | HV *elem; | ||
277 | struct callchain_cursor_node *node; | ||
278 | node = callchain_cursor_current(&callchain_cursor); | ||
279 | if (!node) | ||
280 | break; | ||
281 | |||
282 | elem = newHV(); | ||
283 | if (!elem) | ||
284 | goto exit; | ||
285 | |||
286 | hv_stores(elem, "ip", newSVuv(node->ip)); | ||
287 | |||
288 | if (node->sym) { | ||
289 | HV *sym = newHV(); | ||
290 | if (!sym) | ||
291 | goto exit; | ||
292 | hv_stores(sym, "start", newSVuv(node->sym->start)); | ||
293 | hv_stores(sym, "end", newSVuv(node->sym->end)); | ||
294 | hv_stores(sym, "binding", newSVuv(node->sym->binding)); | ||
295 | hv_stores(sym, "name", newSVpvn(node->sym->name, | ||
296 | node->sym->namelen)); | ||
297 | hv_stores(elem, "sym", newRV_noinc((SV*)sym)); | ||
298 | } | ||
299 | |||
300 | if (node->map) { | ||
301 | struct map *map = node->map; | ||
302 | const char *dsoname = "[unknown]"; | ||
303 | if (map && map->dso && (map->dso->name || map->dso->long_name)) { | ||
304 | if (symbol_conf.show_kernel_path && map->dso->long_name) | ||
305 | dsoname = map->dso->long_name; | ||
306 | else if (map->dso->name) | ||
307 | dsoname = map->dso->name; | ||
308 | } | ||
309 | hv_stores(elem, "dso", newSVpv(dsoname,0)); | ||
310 | } | ||
311 | |||
312 | callchain_cursor_advance(&callchain_cursor); | ||
313 | av_push(list, newRV_noinc((SV*)elem)); | ||
314 | } | ||
315 | |||
316 | exit: | ||
317 | return newRV_noinc((SV*)list); | ||
318 | } | ||
319 | |||
251 | static void perl_process_tracepoint(struct perf_sample *sample, | 320 | static void perl_process_tracepoint(struct perf_sample *sample, |
252 | struct perf_evsel *evsel, | 321 | struct perf_evsel *evsel, |
253 | struct thread *thread) | 322 | struct addr_location *al) |
254 | { | 323 | { |
324 | struct thread *thread = al->thread; | ||
255 | struct event_format *event = evsel->tp_format; | 325 | struct event_format *event = evsel->tp_format; |
256 | struct format_field *field; | 326 | struct format_field *field; |
257 | static char handler[256]; | 327 | static char handler[256]; |
@@ -295,6 +365,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
295 | XPUSHs(sv_2mortal(newSVuv(ns))); | 365 | XPUSHs(sv_2mortal(newSVuv(ns))); |
296 | XPUSHs(sv_2mortal(newSViv(pid))); | 366 | XPUSHs(sv_2mortal(newSViv(pid))); |
297 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | 367 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); |
368 | XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); | ||
298 | 369 | ||
299 | /* common fields other than pid can be accessed via xsub fns */ | 370 | /* common fields other than pid can be accessed via xsub fns */ |
300 | 371 | ||
@@ -329,6 +400,7 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
329 | XPUSHs(sv_2mortal(newSVuv(nsecs))); | 400 | XPUSHs(sv_2mortal(newSVuv(nsecs))); |
330 | XPUSHs(sv_2mortal(newSViv(pid))); | 401 | XPUSHs(sv_2mortal(newSViv(pid))); |
331 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | 402 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); |
403 | XPUSHs(sv_2mortal(perl_process_callchain(sample, evsel, al))); | ||
332 | call_pv("main::trace_unhandled", G_SCALAR); | 404 | call_pv("main::trace_unhandled", G_SCALAR); |
333 | } | 405 | } |
334 | SPAGAIN; | 406 | SPAGAIN; |
@@ -366,7 +438,7 @@ static void perl_process_event(union perf_event *event, | |||
366 | struct perf_evsel *evsel, | 438 | struct perf_evsel *evsel, |
367 | struct addr_location *al) | 439 | struct addr_location *al) |
368 | { | 440 | { |
369 | perl_process_tracepoint(sample, evsel, al->thread); | 441 | perl_process_tracepoint(sample, evsel, al); |
370 | perl_process_event_generic(event, sample, evsel); | 442 | perl_process_event_generic(event, sample, evsel); |
371 | } | 443 | } |
372 | 444 | ||
@@ -490,7 +562,27 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
490 | fprintf(ofp, "use Perf::Trace::Util;\n\n"); | 562 | fprintf(ofp, "use Perf::Trace::Util;\n\n"); |
491 | 563 | ||
492 | fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); | 564 | fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); |
493 | fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); | 565 | fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n"); |
566 | |||
567 | |||
568 | fprintf(ofp, "\n\ | ||
569 | sub print_backtrace\n\ | ||
570 | {\n\ | ||
571 | my $callchain = shift;\n\ | ||
572 | for my $node (@$callchain)\n\ | ||
573 | {\n\ | ||
574 | if(exists $node->{sym})\n\ | ||
575 | {\n\ | ||
576 | printf( \"\\t[\\%%x] \\%%s\\n\", $node->{ip}, $node->{sym}{name});\n\ | ||
577 | }\n\ | ||
578 | else\n\ | ||
579 | {\n\ | ||
580 | printf( \"\\t[\\%%x]\\n\", $node{ip});\n\ | ||
581 | }\n\ | ||
582 | }\n\ | ||
583 | }\n\n\ | ||
584 | "); | ||
585 | |||
494 | 586 | ||
495 | while ((event = trace_find_next_event(pevent, event))) { | 587 | while ((event = trace_find_next_event(pevent, event))) { |
496 | fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); | 588 | fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); |
@@ -502,7 +594,8 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
502 | fprintf(ofp, "$common_secs, "); | 594 | fprintf(ofp, "$common_secs, "); |
503 | fprintf(ofp, "$common_nsecs,\n"); | 595 | fprintf(ofp, "$common_nsecs,\n"); |
504 | fprintf(ofp, "\t $common_pid, "); | 596 | fprintf(ofp, "\t $common_pid, "); |
505 | fprintf(ofp, "$common_comm,\n\t "); | 597 | fprintf(ofp, "$common_comm, "); |
598 | fprintf(ofp, "$common_callchain,\n\t "); | ||
506 | 599 | ||
507 | not_first = 0; | 600 | not_first = 0; |
508 | count = 0; | 601 | count = 0; |
@@ -519,7 +612,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
519 | 612 | ||
520 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | 613 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " |
521 | "$common_secs, $common_nsecs,\n\t " | 614 | "$common_secs, $common_nsecs,\n\t " |
522 | "$common_pid, $common_comm);\n\n"); | 615 | "$common_pid, $common_comm, $common_callchain);\n\n"); |
523 | 616 | ||
524 | fprintf(ofp, "\tprintf(\""); | 617 | fprintf(ofp, "\tprintf(\""); |
525 | 618 | ||
@@ -581,17 +674,22 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
581 | fprintf(ofp, "$%s", f->name); | 674 | fprintf(ofp, "$%s", f->name); |
582 | } | 675 | } |
583 | 676 | ||
584 | fprintf(ofp, ");\n"); | 677 | fprintf(ofp, ");\n\n"); |
678 | |||
679 | fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); | ||
680 | |||
585 | fprintf(ofp, "}\n\n"); | 681 | fprintf(ofp, "}\n\n"); |
586 | } | 682 | } |
587 | 683 | ||
588 | fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " | 684 | fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " |
589 | "$common_cpu, $common_secs, $common_nsecs,\n\t " | 685 | "$common_cpu, $common_secs, $common_nsecs,\n\t " |
590 | "$common_pid, $common_comm) = @_;\n\n"); | 686 | "$common_pid, $common_comm, $common_callchain) = @_;\n\n"); |
591 | 687 | ||
592 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | 688 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " |
593 | "$common_secs, $common_nsecs,\n\t $common_pid, " | 689 | "$common_secs, $common_nsecs,\n\t $common_pid, " |
594 | "$common_comm);\n}\n\n"); | 690 | "$common_comm, $common_callchain);\n"); |
691 | fprintf(ofp, "\tprint_backtrace($common_callchain);\n"); | ||
692 | fprintf(ofp, "}\n\n"); | ||
595 | 693 | ||
596 | fprintf(ofp, "sub print_header\n{\n" | 694 | fprintf(ofp, "sub print_header\n{\n" |
597 | "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" | 695 | "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" |