diff options
author | Ingo Molnar <mingo@kernel.org> | 2015-05-05 22:42:12 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-05-05 22:42:12 -0400 |
commit | 1836ac856e4fb446e48afa4f8cae897d4856b06c (patch) | |
tree | 1b5da329b15a09e13189801c82266fb6805a90af | |
parent | 5ebe6afaf0057ac3eaeb98defd5456894b446d22 (diff) | |
parent | 3698dab1c849c7e1cd440df4fca24baa1973d53b (diff) |
Merge tag 'perf-core-for-mingo-3' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Improve --filter support for 'perf probe', allowing using its arguments
on other commands, as --add, --del, etc (Masami Hiramatsu)
- Show warning when running 'perf kmem stat' on a unsuitable perf.data file,
i.e. one with events that are not the ones required for the stat variant
used (Namhyung Kim).
Infrastructure changes:
- Auxtrace support patches, paving the way to support Intel PT and BTS (Adrian Hunter)
- hists browser (top, report) refactorings (Namhyung Kim)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
82 files changed, 5843 insertions, 812 deletions
@@ -215,7 +215,6 @@ VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) | |||
215 | 215 | ||
216 | export srctree objtree VPATH | 216 | export srctree objtree VPATH |
217 | 217 | ||
218 | |||
219 | # SUBARCH tells the usermode build what the underlying arch is. That is set | 218 | # SUBARCH tells the usermode build what the underlying arch is. That is set |
220 | # first, and if a usermode build is happening, the "ARCH=um" on the command | 219 | # first, and if a usermode build is happening, the "ARCH=um" on the command |
221 | # line overrides the setting of ARCH below. If a native build is happening, | 220 | # line overrides the setting of ARCH below. If a native build is happening, |
@@ -1497,11 +1496,11 @@ image_name: | |||
1497 | # Clear a bunch of variables before executing the submake | 1496 | # Clear a bunch of variables before executing the submake |
1498 | tools/: FORCE | 1497 | tools/: FORCE |
1499 | $(Q)mkdir -p $(objtree)/tools | 1498 | $(Q)mkdir -p $(objtree)/tools |
1500 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ | 1499 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/ |
1501 | 1500 | ||
1502 | tools/%: FORCE | 1501 | tools/%: FORCE |
1503 | $(Q)mkdir -p $(objtree)/tools | 1502 | $(Q)mkdir -p $(objtree)/tools |
1504 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ $* | 1503 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/ $* |
1505 | 1504 | ||
1506 | # Single targets | 1505 | # Single targets |
1507 | # --------------------------------------------------------------------------- | 1506 | # --------------------------------------------------------------------------- |
diff --git a/tools/Makefile b/tools/Makefile index 9a617adc6675..b35102721cbb 100644 --- a/tools/Makefile +++ b/tools/Makefile | |||
@@ -1,3 +1,8 @@ | |||
1 | # Some of the tools (perf) use same make variables | ||
2 | # as in kernel build. | ||
3 | export srctree= | ||
4 | export objtree= | ||
5 | |||
1 | include scripts/Makefile.include | 6 | include scripts/Makefile.include |
2 | 7 | ||
3 | help: | 8 | help: |
@@ -47,11 +52,16 @@ cgroup firewire hv guest usb virtio vm net: FORCE | |||
47 | liblockdep: FORCE | 52 | liblockdep: FORCE |
48 | $(call descend,lib/lockdep) | 53 | $(call descend,lib/lockdep) |
49 | 54 | ||
50 | libapikfs: FORCE | 55 | libapi: FORCE |
51 | $(call descend,lib/api) | 56 | $(call descend,lib/api) |
52 | 57 | ||
53 | perf: libapikfs FORCE | 58 | # The perf build does not follow the descend function setup, |
54 | $(call descend,$@) | 59 | # invoking it via it's own make rule. |
60 | PERF_O = $(if $(O),$(O)/tools/perf,) | ||
61 | |||
62 | perf: FORCE | ||
63 | $(Q)mkdir -p $(PERF_O) . | ||
64 | $(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= | ||
55 | 65 | ||
56 | selftests: FORCE | 66 | selftests: FORCE |
57 | $(call descend,testing/$@) | 67 | $(call descend,testing/$@) |
@@ -97,10 +107,10 @@ cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clea | |||
97 | liblockdep_clean: | 107 | liblockdep_clean: |
98 | $(call descend,lib/lockdep,clean) | 108 | $(call descend,lib/lockdep,clean) |
99 | 109 | ||
100 | libapikfs_clean: | 110 | libapi_clean: |
101 | $(call descend,lib/api,clean) | 111 | $(call descend,lib/api,clean) |
102 | 112 | ||
103 | perf_clean: libapikfs_clean | 113 | perf_clean: |
104 | $(call descend,$(@:_clean=),clean) | 114 | $(call descend,$(@:_clean=),clean) |
105 | 115 | ||
106 | selftests_clean: | 116 | selftests_clean: |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index e0917c0f5d9f..e29e4f81a40d 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -1387,7 +1387,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1387 | do_warning_event(event, "%s: no type found", __func__); | 1387 | do_warning_event(event, "%s: no type found", __func__); |
1388 | goto fail; | 1388 | goto fail; |
1389 | } | 1389 | } |
1390 | field->name = last_token; | 1390 | field->name = field->alias = last_token; |
1391 | 1391 | ||
1392 | if (test_type(type, EVENT_OP)) | 1392 | if (test_type(type, EVENT_OP)) |
1393 | goto fail; | 1393 | goto fail; |
@@ -1469,7 +1469,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1469 | size_dynamic = type_size(field->name); | 1469 | size_dynamic = type_size(field->name); |
1470 | free_token(field->name); | 1470 | free_token(field->name); |
1471 | strcat(field->type, brackets); | 1471 | strcat(field->type, brackets); |
1472 | field->name = token; | 1472 | field->name = field->alias = token; |
1473 | type = read_token(&token); | 1473 | type = read_token(&token); |
1474 | } else { | 1474 | } else { |
1475 | char *new_type; | 1475 | char *new_type; |
@@ -6444,6 +6444,8 @@ void pevent_ref(struct pevent *pevent) | |||
6444 | void pevent_free_format_field(struct format_field *field) | 6444 | void pevent_free_format_field(struct format_field *field) |
6445 | { | 6445 | { |
6446 | free(field->type); | 6446 | free(field->type); |
6447 | if (field->alias != field->name) | ||
6448 | free(field->alias); | ||
6447 | free(field->name); | 6449 | free(field->name); |
6448 | free(field); | 6450 | free(field); |
6449 | } | 6451 | } |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 86a5839fb048..063b1971eb35 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
@@ -191,6 +191,7 @@ struct format_field { | |||
191 | struct event_format *event; | 191 | struct event_format *event; |
192 | char *type; | 192 | char *type; |
193 | char *name; | 193 | char *name; |
194 | char *alias; | ||
194 | int offset; | 195 | int offset; |
195 | int size; | 196 | int size; |
196 | unsigned int arraylen; | 197 | unsigned int arraylen; |
diff --git a/tools/perf/Documentation/callchain-overhead-calculation.txt b/tools/perf/Documentation/callchain-overhead-calculation.txt new file mode 100644 index 000000000000..1a757927195e --- /dev/null +++ b/tools/perf/Documentation/callchain-overhead-calculation.txt | |||
@@ -0,0 +1,108 @@ | |||
1 | Overhead calculation | ||
2 | -------------------- | ||
3 | The overhead can be shown in two columns as 'Children' and 'Self' when | ||
4 | perf collects callchains. The 'self' overhead is simply calculated by | ||
5 | adding all period values of the entry - usually a function (symbol). | ||
6 | This is the value that perf shows traditionally and sum of all the | ||
7 | 'self' overhead values should be 100%. | ||
8 | |||
9 | The 'children' overhead is calculated by adding all period values of | ||
10 | the child functions so that it can show the total overhead of the | ||
11 | higher level functions even if they don't directly execute much. | ||
12 | 'Children' here means functions that are called from another (parent) | ||
13 | function. | ||
14 | |||
15 | It might be confusing that the sum of all the 'children' overhead | ||
16 | values exceeds 100% since each of them is already an accumulation of | ||
17 | 'self' overhead of its child functions. But with this enabled, users | ||
18 | can find which function has the most overhead even if samples are | ||
19 | spread over the children. | ||
20 | |||
21 | Consider the following example; there are three functions like below. | ||
22 | |||
23 | ----------------------- | ||
24 | void foo(void) { | ||
25 | /* do something */ | ||
26 | } | ||
27 | |||
28 | void bar(void) { | ||
29 | /* do something */ | ||
30 | foo(); | ||
31 | } | ||
32 | |||
33 | int main(void) { | ||
34 | bar() | ||
35 | return 0; | ||
36 | } | ||
37 | ----------------------- | ||
38 | |||
39 | In this case 'foo' is a child of 'bar', and 'bar' is an immediate | ||
40 | child of 'main' so 'foo' also is a child of 'main'. In other words, | ||
41 | 'main' is a parent of 'foo' and 'bar', and 'bar' is a parent of 'foo'. | ||
42 | |||
43 | Suppose all samples are recorded in 'foo' and 'bar' only. When it's | ||
44 | recorded with callchains the output will show something like below | ||
45 | in the usual (self-overhead-only) output of perf report: | ||
46 | |||
47 | ---------------------------------- | ||
48 | Overhead Symbol | ||
49 | ........ ..................... | ||
50 | 60.00% foo | ||
51 | | | ||
52 | --- foo | ||
53 | bar | ||
54 | main | ||
55 | __libc_start_main | ||
56 | |||
57 | 40.00% bar | ||
58 | | | ||
59 | --- bar | ||
60 | main | ||
61 | __libc_start_main | ||
62 | ---------------------------------- | ||
63 | |||
64 | When the --children option is enabled, the 'self' overhead values of | ||
65 | child functions (i.e. 'foo' and 'bar') are added to the parents to | ||
66 | calculate the 'children' overhead. In this case the report could be | ||
67 | displayed as: | ||
68 | |||
69 | ------------------------------------------- | ||
70 | Children Self Symbol | ||
71 | ........ ........ .................... | ||
72 | 100.00% 0.00% __libc_start_main | ||
73 | | | ||
74 | --- __libc_start_main | ||
75 | |||
76 | 100.00% 0.00% main | ||
77 | | | ||
78 | --- main | ||
79 | __libc_start_main | ||
80 | |||
81 | 100.00% 40.00% bar | ||
82 | | | ||
83 | --- bar | ||
84 | main | ||
85 | __libc_start_main | ||
86 | |||
87 | 60.00% 60.00% foo | ||
88 | | | ||
89 | --- foo | ||
90 | bar | ||
91 | main | ||
92 | __libc_start_main | ||
93 | ------------------------------------------- | ||
94 | |||
95 | In the above output, the 'self' overhead of 'foo' (60%) was add to the | ||
96 | 'children' overhead of 'bar', 'main' and '\_\_libc_start_main'. | ||
97 | Likewise, the 'self' overhead of 'bar' (40%) was added to the | ||
98 | 'children' overhead of 'main' and '\_\_libc_start_main'. | ||
99 | |||
100 | So '\_\_libc_start_main' and 'main' are shown first since they have | ||
101 | same (100%) 'children' overhead (even though they have zero 'self' | ||
102 | overhead) and they are the parents of 'foo' and 'bar'. | ||
103 | |||
104 | Since v3.16 the 'children' overhead is shown by default and the output | ||
105 | is sorted by its values. The 'children' overhead is disabled by | ||
106 | specifying --no-children option on the command line or by adding | ||
107 | 'report.children = false' or 'top.children = false' in the perf config | ||
108 | file. | ||
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index dc7442cf3d7f..b876ae312699 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt | |||
@@ -44,6 +44,33 @@ OPTIONS | |||
44 | --kallsyms=<file>:: | 44 | --kallsyms=<file>:: |
45 | kallsyms pathname | 45 | kallsyms pathname |
46 | 46 | ||
47 | --itrace:: | ||
48 | Decode Instruction Tracing data, replacing it with synthesized events. | ||
49 | Options are: | ||
50 | |||
51 | i synthesize instructions events | ||
52 | b synthesize branches events | ||
53 | c synthesize branches events (calls only) | ||
54 | r synthesize branches events (returns only) | ||
55 | x synthesize transactions events | ||
56 | e synthesize error events | ||
57 | d create a debug log | ||
58 | g synthesize a call chain (use with i or x) | ||
59 | |||
60 | The default is all events i.e. the same as --itrace=ibxe | ||
61 | |||
62 | In addition, the period (default 100000) for instructions events | ||
63 | can be specified in units of: | ||
64 | |||
65 | i instructions | ||
66 | t ticks | ||
67 | ms milliseconds | ||
68 | us microseconds | ||
69 | ns nanoseconds (default) | ||
70 | |||
71 | Also the call chain size (default 16, max. 1024) for instructions or | ||
72 | transactions events can be specified. | ||
73 | |||
47 | SEE ALSO | 74 | SEE ALSO |
48 | -------- | 75 | -------- |
49 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] | 76 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] |
diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt index 23219c65c16f..ff0f433b3fce 100644 --- a/tools/perf/Documentation/perf-kmem.txt +++ b/tools/perf/Documentation/perf-kmem.txt | |||
@@ -37,7 +37,11 @@ OPTIONS | |||
37 | 37 | ||
38 | -s <key[,key2...]>:: | 38 | -s <key[,key2...]>:: |
39 | --sort=<key[,key2...]>:: | 39 | --sort=<key[,key2...]>:: |
40 | Sort the output (default: frag,hit,bytes) | 40 | Sort the output (default: 'frag,hit,bytes' for slab and 'bytes,hit' |
41 | for page). Available sort keys are 'ptr, callsite, bytes, hit, | ||
42 | pingpong, frag' for slab and 'page, callsite, bytes, hit, order, | ||
43 | migtype, gfp' for page. This option should be preceded by one of the | ||
44 | mode selection options - i.e. --slab, --page, --alloc and/or --caller. | ||
41 | 45 | ||
42 | -l <num>:: | 46 | -l <num>:: |
43 | --line=<num>:: | 47 | --line=<num>:: |
@@ -52,6 +56,11 @@ OPTIONS | |||
52 | --page:: | 56 | --page:: |
53 | Analyze page allocator events | 57 | Analyze page allocator events |
54 | 58 | ||
59 | --live:: | ||
60 | Show live page stat. The perf kmem shows total allocation stat by | ||
61 | default, but this option shows live (currently allocated) pages | ||
62 | instead. (This option works with --page option only) | ||
63 | |||
55 | SEE ALSO | 64 | SEE ALSO |
56 | -------- | 65 | -------- |
57 | linkperf:perf-record[1] | 66 | linkperf:perf-record[1] |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 239609c09f83..a272f2e9a1cf 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -14,11 +14,13 @@ or | |||
14 | or | 14 | or |
15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] | 15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] |
16 | or | 16 | or |
17 | 'perf probe' --list | 17 | 'perf probe' --list[=[GROUP:]EVENT] |
18 | or | 18 | or |
19 | 'perf probe' [options] --line='LINE' | 19 | 'perf probe' [options] --line='LINE' |
20 | or | 20 | or |
21 | 'perf probe' [options] --vars='PROBEPOINT' | 21 | 'perf probe' [options] --vars='PROBEPOINT' |
22 | or | ||
23 | 'perf probe' [options] --funcs | ||
22 | 24 | ||
23 | DESCRIPTION | 25 | DESCRIPTION |
24 | ----------- | 26 | ----------- |
@@ -64,8 +66,8 @@ OPTIONS | |||
64 | classes(e.g. [a-z], [!A-Z]). | 66 | classes(e.g. [a-z], [!A-Z]). |
65 | 67 | ||
66 | -l:: | 68 | -l:: |
67 | --list:: | 69 | --list[=[GROUP:]EVENT]:: |
68 | List up current probe events. | 70 | List up current probe events. This can also accept filtering patterns of event names. |
69 | 71 | ||
70 | -L:: | 72 | -L:: |
71 | --line=:: | 73 | --line=:: |
@@ -82,9 +84,10 @@ OPTIONS | |||
82 | variables. | 84 | variables. |
83 | 85 | ||
84 | -F:: | 86 | -F:: |
85 | --funcs:: | 87 | --funcs[=FILTER]:: |
86 | Show available functions in given module or kernel. With -x/--exec, | 88 | Show available functions in given module or kernel. With -x/--exec, |
87 | can also list functions in a user space executable / shared library. | 89 | can also list functions in a user space executable / shared library. |
90 | This also can accept a FILTER rule argument. | ||
88 | 91 | ||
89 | --filter=FILTER:: | 92 | --filter=FILTER:: |
90 | (Only for --vars and --funcs) Set filter. FILTER is a combination of glob | 93 | (Only for --vars and --funcs) Set filter. FILTER is a combination of glob |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 4847a793de65..57dd57bcef95 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -108,6 +108,8 @@ OPTIONS | |||
108 | Number of mmap data pages (must be a power of two) or size | 108 | Number of mmap data pages (must be a power of two) or size |
109 | specification with appended unit character - B/K/M/G. The | 109 | specification with appended unit character - B/K/M/G. The |
110 | size is rounded up to have nearest pages power of two value. | 110 | size is rounded up to have nearest pages power of two value. |
111 | Also, by adding a comma, the number of mmap pages for AUX | ||
112 | area tracing can be specified. | ||
111 | 113 | ||
112 | --group:: | 114 | --group:: |
113 | Put all events in a single event group. This precedes the --event | 115 | Put all events in a single event group. This precedes the --event |
@@ -257,6 +259,13 @@ records. See clock_gettime(). In particular CLOCK_MONOTONIC and | |||
257 | CLOCK_MONOTONIC_RAW are supported, some events might also allow | 259 | CLOCK_MONOTONIC_RAW are supported, some events might also allow |
258 | CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI. | 260 | CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI. |
259 | 261 | ||
262 | -S:: | ||
263 | --snapshot:: | ||
264 | Select AUX area tracing Snapshot Mode. This option is valid only with an | ||
265 | AUX area tracing event. Optionally the number of bytes to capture per | ||
266 | snapshot can be specified. In Snapshot Mode, trace data is captured only when | ||
267 | signal SIGUSR2 is received. | ||
268 | |||
260 | SEE ALSO | 269 | SEE ALSO |
261 | -------- | 270 | -------- |
262 | linkperf:perf-stat[1], linkperf:perf-list[1] | 271 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 4879cf638824..27190ed06f9c 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -193,6 +193,7 @@ OPTIONS | |||
193 | Accumulate callchain of children to parent entry so that then can | 193 | Accumulate callchain of children to parent entry so that then can |
194 | show up in the output. The output will have a new "Children" column | 194 | show up in the output. The output will have a new "Children" column |
195 | and will be sorted on the data. It requires callchains are recorded. | 195 | and will be sorted on the data. It requires callchains are recorded. |
196 | See the `overhead calculation' section for more details. | ||
196 | 197 | ||
197 | --max-stack:: | 198 | --max-stack:: |
198 | Set the stack depth limit when parsing the callchain, anything | 199 | Set the stack depth limit when parsing the callchain, anything |
@@ -323,6 +324,37 @@ OPTIONS | |||
323 | --header-only:: | 324 | --header-only:: |
324 | Show only perf.data header (forces --stdio). | 325 | Show only perf.data header (forces --stdio). |
325 | 326 | ||
327 | --itrace:: | ||
328 | Options for decoding instruction tracing data. The options are: | ||
329 | |||
330 | i synthesize instructions events | ||
331 | b synthesize branches events | ||
332 | c synthesize branches events (calls only) | ||
333 | r synthesize branches events (returns only) | ||
334 | x synthesize transactions events | ||
335 | e synthesize error events | ||
336 | d create a debug log | ||
337 | g synthesize a call chain (use with i or x) | ||
338 | |||
339 | The default is all events i.e. the same as --itrace=ibxe | ||
340 | |||
341 | In addition, the period (default 100000) for instructions events | ||
342 | can be specified in units of: | ||
343 | |||
344 | i instructions | ||
345 | t ticks | ||
346 | ms milliseconds | ||
347 | us microseconds | ||
348 | ns nanoseconds (default) | ||
349 | |||
350 | Also the call chain size (default 16, max. 1024) for instructions or | ||
351 | transactions events can be specified. | ||
352 | |||
353 | To disable decoding entirely, use --no-itrace. | ||
354 | |||
355 | |||
356 | include::callchain-overhead-calculation.txt[] | ||
357 | |||
326 | SEE ALSO | 358 | SEE ALSO |
327 | -------- | 359 | -------- |
328 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 360 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 79445750fcb3..c82df572fac2 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -115,7 +115,8 @@ OPTIONS | |||
115 | -f:: | 115 | -f:: |
116 | --fields:: | 116 | --fields:: |
117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period. | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, |
119 | srcline, period, flags. | ||
119 | Field list can be prepended with the type, trace, sw or hw, | 120 | Field list can be prepended with the type, trace, sw or hw, |
120 | to indicate to which event type the field list applies. | 121 | to indicate to which event type the field list applies. |
121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace | 122 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
@@ -165,6 +166,12 @@ OPTIONS | |||
165 | 166 | ||
166 | At this point usage is displayed, and perf-script exits. | 167 | At this point usage is displayed, and perf-script exits. |
167 | 168 | ||
169 | The flags field is synthesized and may have a value when Instruction | ||
170 | Trace decoding. The flags are "bcrosyiABEx" which stand for branch, | ||
171 | call, return, conditional, system, asynchronous, interrupt, | ||
172 | transaction abort, trace begin, trace end, and in transaction, | ||
173 | respectively. | ||
174 | |||
168 | Finally, a user may not set fields to none for all event types. | 175 | Finally, a user may not set fields to none for all event types. |
169 | i.e., -f "" is not allowed. | 176 | i.e., -f "" is not allowed. |
170 | 177 | ||
@@ -221,6 +228,34 @@ OPTIONS | |||
221 | --header-only | 228 | --header-only |
222 | Show only perf.data header. | 229 | Show only perf.data header. |
223 | 230 | ||
231 | --itrace:: | ||
232 | Options for decoding instruction tracing data. The options are: | ||
233 | |||
234 | i synthesize instructions events | ||
235 | b synthesize branches events | ||
236 | c synthesize branches events (calls only) | ||
237 | r synthesize branches events (returns only) | ||
238 | x synthesize transactions events | ||
239 | e synthesize error events | ||
240 | d create a debug log | ||
241 | g synthesize a call chain (use with i or x) | ||
242 | |||
243 | The default is all events i.e. the same as --itrace=ibxe | ||
244 | |||
245 | In addition, the period (default 100000) for instructions events | ||
246 | can be specified in units of: | ||
247 | |||
248 | i instructions | ||
249 | t ticks | ||
250 | ms milliseconds | ||
251 | us microseconds | ||
252 | ns nanoseconds (default) | ||
253 | |||
254 | Also the call chain size (default 16, max. 1024) for instructions or | ||
255 | transactions events can be specified. | ||
256 | |||
257 | To disable decoding entirely, use --no-itrace. | ||
258 | |||
224 | SEE ALSO | 259 | SEE ALSO |
225 | -------- | 260 | -------- |
226 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 261 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 3265b1070518..9e5b07eb7d35 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -168,7 +168,7 @@ Default is to monitor all CPUS. | |||
168 | Accumulate callchain of children to parent entry so that then can | 168 | Accumulate callchain of children to parent entry so that then can |
169 | show up in the output. The output will have a new "Children" column | 169 | show up in the output. The output will have a new "Children" column |
170 | and will be sorted on the data. It requires -g/--call-graph option | 170 | and will be sorted on the data. It requires -g/--call-graph option |
171 | enabled. | 171 | enabled. See the `overhead calculation' section for more details. |
172 | 172 | ||
173 | --max-stack:: | 173 | --max-stack:: |
174 | Set the stack depth limit when parsing the callchain, anything | 174 | Set the stack depth limit when parsing the callchain, anything |
@@ -234,6 +234,7 @@ INTERACTIVE PROMPTING KEYS | |||
234 | 234 | ||
235 | Pressing any unmapped key displays a menu, and prompts for input. | 235 | Pressing any unmapped key displays a menu, and prompts for input. |
236 | 236 | ||
237 | include::callchain-overhead-calculation.txt[] | ||
237 | 238 | ||
238 | SEE ALSO | 239 | SEE ALSO |
239 | -------- | 240 | -------- |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index ba03fd5d1a54..1db9c8b79880 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -35,7 +35,7 @@ OPTIONS | |||
35 | 35 | ||
36 | -e:: | 36 | -e:: |
37 | --expr:: | 37 | --expr:: |
38 | List of events to show, currently only syscall names. | 38 | List of syscalls to show, currently only syscall names. |
39 | Prefixing with ! shows all syscalls but the ones specified. You may | 39 | Prefixing with ! shows all syscalls but the ones specified. You may |
40 | need to escape it. | 40 | need to escape it. |
41 | 41 | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index c699dc35eef9..d31a7bbd7cee 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -24,7 +24,7 @@ unexport MAKEFLAGS | |||
24 | # (To override it, run 'make JOBS=1' and similar.) | 24 | # (To override it, run 'make JOBS=1' and similar.) |
25 | # | 25 | # |
26 | ifeq ($(JOBS),) | 26 | ifeq ($(JOBS),) |
27 | JOBS := $(shell egrep -c '^processor|^CPU' /proc/cpuinfo 2>/dev/null) | 27 | JOBS := $(shell (getconf _NPROCESSORS_ONLN || egrep -c '^processor|^CPU[0-9]' /proc/cpuinfo) 2>/dev/null) |
28 | ifeq ($(JOBS),0) | 28 | ifeq ($(JOBS),0) |
29 | JOBS := 1 | 29 | JOBS := 1 |
30 | endif | 30 | endif |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c43a20517591..03409cc02117 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -73,6 +73,8 @@ include config/utilities.mak | |||
73 | # for CTF data format. | 73 | # for CTF data format. |
74 | # | 74 | # |
75 | # Define NO_LZMA if you do not want to support compressed (xz) kernel modules | 75 | # Define NO_LZMA if you do not want to support compressed (xz) kernel modules |
76 | # | ||
77 | # Define NO_AUXTRACE if you do not want AUX area tracing support | ||
76 | 78 | ||
77 | ifeq ($(srctree),) | 79 | ifeq ($(srctree),) |
78 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | 80 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) |
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index 0af6e9b3f728..7b8b0d1a1b62 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build | |||
@@ -1,4 +1,5 @@ | |||
1 | libperf-y += header.o | 1 | libperf-y += header.o |
2 | libperf-y += sym-handling.o | ||
2 | 3 | ||
3 | libperf-$(CONFIG_DWARF) += dwarf-regs.o | 4 | libperf-$(CONFIG_DWARF) += dwarf-regs.o |
4 | libperf-$(CONFIG_DWARF) += skip-callchain-idx.o | 5 | libperf-$(CONFIG_DWARF) += skip-callchain-idx.o |
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c new file mode 100644 index 000000000000..bbc1a50768dd --- /dev/null +++ b/tools/perf/arch/powerpc/util/sym-handling.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License, version 2, as | ||
4 | * published by the Free Software Foundation. | ||
5 | * | ||
6 | * Copyright (C) 2015 Naveen N. Rao, IBM Corporation | ||
7 | */ | ||
8 | |||
9 | #include "debug.h" | ||
10 | #include "symbol.h" | ||
11 | #include "map.h" | ||
12 | #include "probe-event.h" | ||
13 | |||
14 | #ifdef HAVE_LIBELF_SUPPORT | ||
15 | bool elf__needs_adjust_symbols(GElf_Ehdr ehdr) | ||
16 | { | ||
17 | return ehdr.e_type == ET_EXEC || | ||
18 | ehdr.e_type == ET_REL || | ||
19 | ehdr.e_type == ET_DYN; | ||
20 | } | ||
21 | |||
22 | #if defined(_CALL_ELF) && _CALL_ELF == 2 | ||
23 | void arch__elf_sym_adjust(GElf_Sym *sym) | ||
24 | { | ||
25 | sym->st_value += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); | ||
26 | } | ||
27 | #endif | ||
28 | #endif | ||
29 | |||
30 | #if !defined(_CALL_ELF) || _CALL_ELF != 2 | ||
31 | int arch__choose_best_symbol(struct symbol *syma, | ||
32 | struct symbol *symb __maybe_unused) | ||
33 | { | ||
34 | char *sym = syma->name; | ||
35 | |||
36 | /* Skip over any initial dot */ | ||
37 | if (*sym == '.') | ||
38 | sym++; | ||
39 | |||
40 | /* Avoid "SyS" kernel syscall aliases */ | ||
41 | if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3)) | ||
42 | return SYMBOL_B; | ||
43 | if (strlen(sym) >= 10 && !strncmp(sym, "compat_SyS", 10)) | ||
44 | return SYMBOL_B; | ||
45 | |||
46 | return SYMBOL_A; | ||
47 | } | ||
48 | |||
49 | /* Allow matching against dot variants */ | ||
50 | int arch__compare_symbol_names(const char *namea, const char *nameb) | ||
51 | { | ||
52 | /* Skip over initial dot */ | ||
53 | if (*namea == '.') | ||
54 | namea++; | ||
55 | if (*nameb == '.') | ||
56 | nameb++; | ||
57 | |||
58 | return strcmp(namea, nameb); | ||
59 | } | ||
60 | #endif | ||
61 | |||
62 | #if defined(_CALL_ELF) && _CALL_ELF == 2 | ||
63 | bool arch__prefers_symtab(void) | ||
64 | { | ||
65 | return true; | ||
66 | } | ||
67 | |||
68 | #define PPC64LE_LEP_OFFSET 8 | ||
69 | |||
70 | void arch__fix_tev_from_maps(struct perf_probe_event *pev, | ||
71 | struct probe_trace_event *tev, struct map *map) | ||
72 | { | ||
73 | /* | ||
74 | * ppc64 ABIv2 local entry point is currently always 2 instructions | ||
75 | * (8 bytes) after the global entry point. | ||
76 | */ | ||
77 | if (!pev->uprobes && map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { | ||
78 | tev->point.address += PPC64LE_LEP_OFFSET; | ||
79 | tev->point.offset += PPC64LE_LEP_OFFSET; | ||
80 | } | ||
81 | } | ||
82 | #endif | ||
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index ebfa163b80b5..0b704c5f6d90 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <pthread.h> | 23 | #include <pthread.h> |
24 | #include <sys/mman.h> | 24 | #include <sys/mman.h> |
25 | #include <sys/time.h> | 25 | #include <sys/time.h> |
26 | #include <sys/resource.h> | ||
26 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
27 | #include <sys/prctl.h> | 28 | #include <sys/prctl.h> |
28 | #include <sys/types.h> | 29 | #include <sys/types.h> |
@@ -51,6 +52,9 @@ struct thread_data { | |||
51 | unsigned int loops_done; | 52 | unsigned int loops_done; |
52 | u64 val; | 53 | u64 val; |
53 | u64 runtime_ns; | 54 | u64 runtime_ns; |
55 | u64 system_time_ns; | ||
56 | u64 user_time_ns; | ||
57 | double speed_gbs; | ||
54 | pthread_mutex_t *process_lock; | 58 | pthread_mutex_t *process_lock; |
55 | }; | 59 | }; |
56 | 60 | ||
@@ -1034,6 +1038,7 @@ static void *worker_thread(void *__tdata) | |||
1034 | u64 bytes_done; | 1038 | u64 bytes_done; |
1035 | long work_done; | 1039 | long work_done; |
1036 | u32 l; | 1040 | u32 l; |
1041 | struct rusage rusage; | ||
1037 | 1042 | ||
1038 | bind_to_cpumask(td->bind_cpumask); | 1043 | bind_to_cpumask(td->bind_cpumask); |
1039 | bind_to_memnode(td->bind_node); | 1044 | bind_to_memnode(td->bind_node); |
@@ -1186,6 +1191,13 @@ static void *worker_thread(void *__tdata) | |||
1186 | timersub(&stop, &start0, &diff); | 1191 | timersub(&stop, &start0, &diff); |
1187 | td->runtime_ns = diff.tv_sec * 1000000000ULL; | 1192 | td->runtime_ns = diff.tv_sec * 1000000000ULL; |
1188 | td->runtime_ns += diff.tv_usec * 1000ULL; | 1193 | td->runtime_ns += diff.tv_usec * 1000ULL; |
1194 | td->speed_gbs = bytes_done / (td->runtime_ns / 1e9) / 1e9; | ||
1195 | |||
1196 | getrusage(RUSAGE_THREAD, &rusage); | ||
1197 | td->system_time_ns = rusage.ru_stime.tv_sec * 1000000000ULL; | ||
1198 | td->system_time_ns += rusage.ru_stime.tv_usec * 1000ULL; | ||
1199 | td->user_time_ns = rusage.ru_utime.tv_sec * 1000000000ULL; | ||
1200 | td->user_time_ns += rusage.ru_utime.tv_usec * 1000ULL; | ||
1189 | 1201 | ||
1190 | free_data(thread_data, g->p.bytes_thread); | 1202 | free_data(thread_data, g->p.bytes_thread); |
1191 | 1203 | ||
@@ -1412,7 +1424,7 @@ static int __bench_numa(const char *name) | |||
1412 | double runtime_sec_min; | 1424 | double runtime_sec_min; |
1413 | int wait_stat; | 1425 | int wait_stat; |
1414 | double bytes; | 1426 | double bytes; |
1415 | int i, t; | 1427 | int i, t, p; |
1416 | 1428 | ||
1417 | if (init()) | 1429 | if (init()) |
1418 | return -1; | 1430 | return -1; |
@@ -1548,6 +1560,24 @@ static int __bench_numa(const char *name) | |||
1548 | print_res(name, bytes / runtime_sec_max / 1e9, | 1560 | print_res(name, bytes / runtime_sec_max / 1e9, |
1549 | "GB/sec,", "total-speed", "GB/sec total speed"); | 1561 | "GB/sec,", "total-speed", "GB/sec total speed"); |
1550 | 1562 | ||
1563 | if (g->p.show_details >= 2) { | ||
1564 | char tname[32]; | ||
1565 | struct thread_data *td; | ||
1566 | for (p = 0; p < g->p.nr_proc; p++) { | ||
1567 | for (t = 0; t < g->p.nr_threads; t++) { | ||
1568 | memset(tname, 0, 32); | ||
1569 | td = g->threads + p*g->p.nr_threads + t; | ||
1570 | snprintf(tname, 32, "process%d:thread%d", p, t); | ||
1571 | print_res(tname, td->speed_gbs, | ||
1572 | "GB/sec", "thread-speed", "GB/sec/thread speed"); | ||
1573 | print_res(tname, td->system_time_ns / 1e9, | ||
1574 | "secs", "thread-system-time", "system CPU time/thread"); | ||
1575 | print_res(tname, td->user_time_ns / 1e9, | ||
1576 | "secs", "thread-user-time", "user CPU time/thread"); | ||
1577 | } | ||
1578 | } | ||
1579 | } | ||
1580 | |||
1551 | free(pids); | 1581 | free(pids); |
1552 | 1582 | ||
1553 | deinit(); | 1583 | deinit(); |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index feb420f74c2d..9fe93c8d4fcf 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -69,6 +69,15 @@ static int perf_session__list_build_ids(bool force, bool with_hits) | |||
69 | session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops); | 69 | session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops); |
70 | if (session == NULL) | 70 | if (session == NULL) |
71 | return -1; | 71 | return -1; |
72 | |||
73 | /* | ||
74 | * We take all buildids when the file contains AUX area tracing data | ||
75 | * because we do not decode the trace because it would take too long. | ||
76 | */ | ||
77 | if (!perf_data_file__is_pipe(&file) && | ||
78 | perf_header__has_feat(&session->header, HEADER_AUXTRACE)) | ||
79 | with_hits = false; | ||
80 | |||
72 | /* | 81 | /* |
73 | * in pipe-mode, the only way to get the buildids is to parse | 82 | * in pipe-mode, the only way to get the buildids is to parse |
74 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID | 83 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 40a33d7334cc..d6a47e854b2b 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include "util/debug.h" | 16 | #include "util/debug.h" |
17 | #include "util/build-id.h" | 17 | #include "util/build-id.h" |
18 | #include "util/data.h" | 18 | #include "util/data.h" |
19 | #include "util/auxtrace.h" | ||
19 | 20 | ||
20 | #include "util/parse-options.h" | 21 | #include "util/parse-options.h" |
21 | 22 | ||
@@ -26,10 +27,12 @@ struct perf_inject { | |||
26 | struct perf_session *session; | 27 | struct perf_session *session; |
27 | bool build_ids; | 28 | bool build_ids; |
28 | bool sched_stat; | 29 | bool sched_stat; |
30 | bool have_auxtrace; | ||
29 | const char *input_name; | 31 | const char *input_name; |
30 | struct perf_data_file output; | 32 | struct perf_data_file output; |
31 | u64 bytes_written; | 33 | u64 bytes_written; |
32 | struct list_head samples; | 34 | struct list_head samples; |
35 | struct itrace_synth_opts itrace_synth_opts; | ||
33 | }; | 36 | }; |
34 | 37 | ||
35 | struct event_entry { | 38 | struct event_entry { |
@@ -38,14 +41,11 @@ struct event_entry { | |||
38 | union perf_event event[0]; | 41 | union perf_event event[0]; |
39 | }; | 42 | }; |
40 | 43 | ||
41 | static int perf_event__repipe_synth(struct perf_tool *tool, | 44 | static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) |
42 | union perf_event *event) | ||
43 | { | 45 | { |
44 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
45 | ssize_t size; | 46 | ssize_t size; |
46 | 47 | ||
47 | size = perf_data_file__write(&inject->output, event, | 48 | size = perf_data_file__write(&inject->output, buf, sz); |
48 | event->header.size); | ||
49 | if (size < 0) | 49 | if (size < 0) |
50 | return -errno; | 50 | return -errno; |
51 | 51 | ||
@@ -53,6 +53,15 @@ static int perf_event__repipe_synth(struct perf_tool *tool, | |||
53 | return 0; | 53 | return 0; |
54 | } | 54 | } |
55 | 55 | ||
56 | static int perf_event__repipe_synth(struct perf_tool *tool, | ||
57 | union perf_event *event) | ||
58 | { | ||
59 | struct perf_inject *inject = container_of(tool, struct perf_inject, | ||
60 | tool); | ||
61 | |||
62 | return output_bytes(inject, event, event->header.size); | ||
63 | } | ||
64 | |||
56 | static int perf_event__repipe_oe_synth(struct perf_tool *tool, | 65 | static int perf_event__repipe_oe_synth(struct perf_tool *tool, |
57 | union perf_event *event, | 66 | union perf_event *event, |
58 | struct ordered_events *oe __maybe_unused) | 67 | struct ordered_events *oe __maybe_unused) |
@@ -86,6 +95,79 @@ static int perf_event__repipe_attr(struct perf_tool *tool, | |||
86 | return perf_event__repipe_synth(tool, event); | 95 | return perf_event__repipe_synth(tool, event); |
87 | } | 96 | } |
88 | 97 | ||
98 | #ifdef HAVE_AUXTRACE_SUPPORT | ||
99 | |||
100 | static int copy_bytes(struct perf_inject *inject, int fd, off_t size) | ||
101 | { | ||
102 | char buf[4096]; | ||
103 | ssize_t ssz; | ||
104 | int ret; | ||
105 | |||
106 | while (size > 0) { | ||
107 | ssz = read(fd, buf, min(size, (off_t)sizeof(buf))); | ||
108 | if (ssz < 0) | ||
109 | return -errno; | ||
110 | ret = output_bytes(inject, buf, ssz); | ||
111 | if (ret) | ||
112 | return ret; | ||
113 | size -= ssz; | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, | ||
120 | union perf_event *event, | ||
121 | struct perf_session *session | ||
122 | __maybe_unused) | ||
123 | { | ||
124 | struct perf_inject *inject = container_of(tool, struct perf_inject, | ||
125 | tool); | ||
126 | int ret; | ||
127 | |||
128 | inject->have_auxtrace = true; | ||
129 | |||
130 | if (!inject->output.is_pipe) { | ||
131 | off_t offset; | ||
132 | |||
133 | offset = lseek(inject->output.fd, 0, SEEK_CUR); | ||
134 | if (offset == -1) | ||
135 | return -errno; | ||
136 | ret = auxtrace_index__auxtrace_event(&session->auxtrace_index, | ||
137 | event, offset); | ||
138 | if (ret < 0) | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | if (perf_data_file__is_pipe(session->file) || !session->one_mmap) { | ||
143 | ret = output_bytes(inject, event, event->header.size); | ||
144 | if (ret < 0) | ||
145 | return ret; | ||
146 | ret = copy_bytes(inject, perf_data_file__fd(session->file), | ||
147 | event->auxtrace.size); | ||
148 | } else { | ||
149 | ret = output_bytes(inject, event, | ||
150 | event->header.size + event->auxtrace.size); | ||
151 | } | ||
152 | if (ret < 0) | ||
153 | return ret; | ||
154 | |||
155 | return event->auxtrace.size; | ||
156 | } | ||
157 | |||
158 | #else | ||
159 | |||
160 | static s64 | ||
161 | perf_event__repipe_auxtrace(struct perf_tool *tool __maybe_unused, | ||
162 | union perf_event *event __maybe_unused, | ||
163 | struct perf_session *session __maybe_unused) | ||
164 | { | ||
165 | pr_err("AUX area tracing not supported\n"); | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | |||
169 | #endif | ||
170 | |||
89 | static int perf_event__repipe(struct perf_tool *tool, | 171 | static int perf_event__repipe(struct perf_tool *tool, |
90 | union perf_event *event, | 172 | union perf_event *event, |
91 | struct perf_sample *sample __maybe_unused, | 173 | struct perf_sample *sample __maybe_unused, |
@@ -155,6 +237,32 @@ static int perf_event__repipe_fork(struct perf_tool *tool, | |||
155 | return err; | 237 | return err; |
156 | } | 238 | } |
157 | 239 | ||
240 | static int perf_event__repipe_comm(struct perf_tool *tool, | ||
241 | union perf_event *event, | ||
242 | struct perf_sample *sample, | ||
243 | struct machine *machine) | ||
244 | { | ||
245 | int err; | ||
246 | |||
247 | err = perf_event__process_comm(tool, event, sample, machine); | ||
248 | perf_event__repipe(tool, event, sample, machine); | ||
249 | |||
250 | return err; | ||
251 | } | ||
252 | |||
253 | static int perf_event__repipe_exit(struct perf_tool *tool, | ||
254 | union perf_event *event, | ||
255 | struct perf_sample *sample, | ||
256 | struct machine *machine) | ||
257 | { | ||
258 | int err; | ||
259 | |||
260 | err = perf_event__process_exit(tool, event, sample, machine); | ||
261 | perf_event__repipe(tool, event, sample, machine); | ||
262 | |||
263 | return err; | ||
264 | } | ||
265 | |||
158 | static int perf_event__repipe_tracing_data(struct perf_tool *tool, | 266 | static int perf_event__repipe_tracing_data(struct perf_tool *tool, |
159 | union perf_event *event, | 267 | union perf_event *event, |
160 | struct perf_session *session) | 268 | struct perf_session *session) |
@@ -167,6 +275,18 @@ static int perf_event__repipe_tracing_data(struct perf_tool *tool, | |||
167 | return err; | 275 | return err; |
168 | } | 276 | } |
169 | 277 | ||
278 | static int perf_event__repipe_id_index(struct perf_tool *tool, | ||
279 | union perf_event *event, | ||
280 | struct perf_session *session) | ||
281 | { | ||
282 | int err; | ||
283 | |||
284 | perf_event__repipe_synth(tool, event); | ||
285 | err = perf_event__process_id_index(tool, event, session); | ||
286 | |||
287 | return err; | ||
288 | } | ||
289 | |||
170 | static int dso__read_build_id(struct dso *dso) | 290 | static int dso__read_build_id(struct dso *dso) |
171 | { | 291 | { |
172 | if (dso->has_build_id) | 292 | if (dso->has_build_id) |
@@ -351,16 +471,20 @@ static int __cmd_inject(struct perf_inject *inject) | |||
351 | struct perf_session *session = inject->session; | 471 | struct perf_session *session = inject->session; |
352 | struct perf_data_file *file_out = &inject->output; | 472 | struct perf_data_file *file_out = &inject->output; |
353 | int fd = perf_data_file__fd(file_out); | 473 | int fd = perf_data_file__fd(file_out); |
474 | u64 output_data_offset; | ||
354 | 475 | ||
355 | signal(SIGINT, sig_handler); | 476 | signal(SIGINT, sig_handler); |
356 | 477 | ||
357 | if (inject->build_ids || inject->sched_stat) { | 478 | if (inject->build_ids || inject->sched_stat || |
479 | inject->itrace_synth_opts.set) { | ||
358 | inject->tool.mmap = perf_event__repipe_mmap; | 480 | inject->tool.mmap = perf_event__repipe_mmap; |
359 | inject->tool.mmap2 = perf_event__repipe_mmap2; | 481 | inject->tool.mmap2 = perf_event__repipe_mmap2; |
360 | inject->tool.fork = perf_event__repipe_fork; | 482 | inject->tool.fork = perf_event__repipe_fork; |
361 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 483 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
362 | } | 484 | } |
363 | 485 | ||
486 | output_data_offset = session->header.data_offset; | ||
487 | |||
364 | if (inject->build_ids) { | 488 | if (inject->build_ids) { |
365 | inject->tool.sample = perf_event__inject_buildid; | 489 | inject->tool.sample = perf_event__inject_buildid; |
366 | } else if (inject->sched_stat) { | 490 | } else if (inject->sched_stat) { |
@@ -379,17 +503,43 @@ static int __cmd_inject(struct perf_inject *inject) | |||
379 | else if (!strncmp(name, "sched:sched_stat_", 17)) | 503 | else if (!strncmp(name, "sched:sched_stat_", 17)) |
380 | evsel->handler = perf_inject__sched_stat; | 504 | evsel->handler = perf_inject__sched_stat; |
381 | } | 505 | } |
506 | } else if (inject->itrace_synth_opts.set) { | ||
507 | session->itrace_synth_opts = &inject->itrace_synth_opts; | ||
508 | inject->itrace_synth_opts.inject = true; | ||
509 | inject->tool.comm = perf_event__repipe_comm; | ||
510 | inject->tool.exit = perf_event__repipe_exit; | ||
511 | inject->tool.id_index = perf_event__repipe_id_index; | ||
512 | inject->tool.auxtrace_info = perf_event__process_auxtrace_info; | ||
513 | inject->tool.auxtrace = perf_event__process_auxtrace; | ||
514 | inject->tool.ordered_events = true; | ||
515 | inject->tool.ordering_requires_timestamps = true; | ||
516 | /* Allow space in the header for new attributes */ | ||
517 | output_data_offset = 4096; | ||
382 | } | 518 | } |
383 | 519 | ||
520 | if (!inject->itrace_synth_opts.set) | ||
521 | auxtrace_index__free(&session->auxtrace_index); | ||
522 | |||
384 | if (!file_out->is_pipe) | 523 | if (!file_out->is_pipe) |
385 | lseek(fd, session->header.data_offset, SEEK_SET); | 524 | lseek(fd, output_data_offset, SEEK_SET); |
386 | 525 | ||
387 | ret = perf_session__process_events(session); | 526 | ret = perf_session__process_events(session); |
388 | 527 | ||
389 | if (!file_out->is_pipe) { | 528 | if (!file_out->is_pipe) { |
390 | if (inject->build_ids) | 529 | if (inject->build_ids) { |
391 | perf_header__set_feat(&session->header, | 530 | perf_header__set_feat(&session->header, |
392 | HEADER_BUILD_ID); | 531 | HEADER_BUILD_ID); |
532 | if (inject->have_auxtrace) | ||
533 | dsos__hit_all(session); | ||
534 | } | ||
535 | /* | ||
536 | * The AUX areas have been removed and replaced with | ||
537 | * synthesized hardware events, so clear the feature flag. | ||
538 | */ | ||
539 | if (inject->itrace_synth_opts.set) | ||
540 | perf_header__clear_feat(&session->header, | ||
541 | HEADER_AUXTRACE); | ||
542 | session->header.data_offset = output_data_offset; | ||
393 | session->header.data_size = inject->bytes_written; | 543 | session->header.data_size = inject->bytes_written; |
394 | perf_session__write_header(session, session->evlist, fd, true); | 544 | perf_session__write_header(session, session->evlist, fd, true); |
395 | } | 545 | } |
@@ -408,11 +558,16 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
408 | .fork = perf_event__repipe, | 558 | .fork = perf_event__repipe, |
409 | .exit = perf_event__repipe, | 559 | .exit = perf_event__repipe, |
410 | .lost = perf_event__repipe, | 560 | .lost = perf_event__repipe, |
561 | .aux = perf_event__repipe, | ||
562 | .itrace_start = perf_event__repipe, | ||
411 | .read = perf_event__repipe_sample, | 563 | .read = perf_event__repipe_sample, |
412 | .throttle = perf_event__repipe, | 564 | .throttle = perf_event__repipe, |
413 | .unthrottle = perf_event__repipe, | 565 | .unthrottle = perf_event__repipe, |
414 | .attr = perf_event__repipe_attr, | 566 | .attr = perf_event__repipe_attr, |
415 | .tracing_data = perf_event__repipe_op2_synth, | 567 | .tracing_data = perf_event__repipe_op2_synth, |
568 | .auxtrace_info = perf_event__repipe_op2_synth, | ||
569 | .auxtrace = perf_event__repipe_auxtrace, | ||
570 | .auxtrace_error = perf_event__repipe_op2_synth, | ||
416 | .finished_round = perf_event__repipe_oe_synth, | 571 | .finished_round = perf_event__repipe_oe_synth, |
417 | .build_id = perf_event__repipe_op2_synth, | 572 | .build_id = perf_event__repipe_op2_synth, |
418 | .id_index = perf_event__repipe_op2_synth, | 573 | .id_index = perf_event__repipe_op2_synth, |
@@ -444,6 +599,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
444 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", | 599 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", |
445 | "kallsyms pathname"), | 600 | "kallsyms pathname"), |
446 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), | 601 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
602 | OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts, | ||
603 | NULL, "opts", "Instruction Tracing options", | ||
604 | itrace_parse_synth_opts), | ||
447 | OPT_END() | 605 | OPT_END() |
448 | }; | 606 | }; |
449 | const char * const inject_usage[] = { | 607 | const char * const inject_usage[] = { |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 63ea01349b6e..e628bf1a0c24 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "util/header.h" | 10 | #include "util/header.h" |
11 | #include "util/session.h" | 11 | #include "util/session.h" |
12 | #include "util/tool.h" | 12 | #include "util/tool.h" |
13 | #include "util/callchain.h" | ||
13 | 14 | ||
14 | #include "util/parse-options.h" | 15 | #include "util/parse-options.h" |
15 | #include "util/trace-event.h" | 16 | #include "util/trace-event.h" |
@@ -21,14 +22,19 @@ | |||
21 | #include <linux/rbtree.h> | 22 | #include <linux/rbtree.h> |
22 | #include <linux/string.h> | 23 | #include <linux/string.h> |
23 | #include <locale.h> | 24 | #include <locale.h> |
25 | #include <regex.h> | ||
24 | 26 | ||
25 | static int kmem_slab; | 27 | static int kmem_slab; |
26 | static int kmem_page; | 28 | static int kmem_page; |
27 | 29 | ||
28 | static long kmem_page_size; | 30 | static long kmem_page_size; |
31 | static enum { | ||
32 | KMEM_SLAB, | ||
33 | KMEM_PAGE, | ||
34 | } kmem_default = KMEM_SLAB; /* for backward compatibility */ | ||
29 | 35 | ||
30 | struct alloc_stat; | 36 | struct alloc_stat; |
31 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | 37 | typedef int (*sort_fn_t)(void *, void *); |
32 | 38 | ||
33 | static int alloc_flag; | 39 | static int alloc_flag; |
34 | static int caller_flag; | 40 | static int caller_flag; |
@@ -179,8 +185,8 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel, | |||
179 | return ret; | 185 | return ret; |
180 | } | 186 | } |
181 | 187 | ||
182 | static int ptr_cmp(struct alloc_stat *, struct alloc_stat *); | 188 | static int ptr_cmp(void *, void *); |
183 | static int callsite_cmp(struct alloc_stat *, struct alloc_stat *); | 189 | static int slab_callsite_cmp(void *, void *); |
184 | 190 | ||
185 | static struct alloc_stat *search_alloc_stat(unsigned long ptr, | 191 | static struct alloc_stat *search_alloc_stat(unsigned long ptr, |
186 | unsigned long call_site, | 192 | unsigned long call_site, |
@@ -221,7 +227,8 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel, | |||
221 | s_alloc->pingpong++; | 227 | s_alloc->pingpong++; |
222 | 228 | ||
223 | s_caller = search_alloc_stat(0, s_alloc->call_site, | 229 | s_caller = search_alloc_stat(0, s_alloc->call_site, |
224 | &root_caller_stat, callsite_cmp); | 230 | &root_caller_stat, |
231 | slab_callsite_cmp); | ||
225 | if (!s_caller) | 232 | if (!s_caller) |
226 | return -1; | 233 | return -1; |
227 | s_caller->pingpong++; | 234 | s_caller->pingpong++; |
@@ -241,6 +248,8 @@ static unsigned long nr_page_fails; | |||
241 | static unsigned long nr_page_nomatch; | 248 | static unsigned long nr_page_nomatch; |
242 | 249 | ||
243 | static bool use_pfn; | 250 | static bool use_pfn; |
251 | static bool live_page; | ||
252 | static struct perf_session *kmem_session; | ||
244 | 253 | ||
245 | #define MAX_MIGRATE_TYPES 6 | 254 | #define MAX_MIGRATE_TYPES 6 |
246 | #define MAX_PAGE_ORDER 11 | 255 | #define MAX_PAGE_ORDER 11 |
@@ -250,6 +259,7 @@ static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES]; | |||
250 | struct page_stat { | 259 | struct page_stat { |
251 | struct rb_node node; | 260 | struct rb_node node; |
252 | u64 page; | 261 | u64 page; |
262 | u64 callsite; | ||
253 | int order; | 263 | int order; |
254 | unsigned gfp_flags; | 264 | unsigned gfp_flags; |
255 | unsigned migrate_type; | 265 | unsigned migrate_type; |
@@ -259,13 +269,158 @@ struct page_stat { | |||
259 | int nr_free; | 269 | int nr_free; |
260 | }; | 270 | }; |
261 | 271 | ||
262 | static struct rb_root page_tree; | 272 | static struct rb_root page_live_tree; |
263 | static struct rb_root page_alloc_tree; | 273 | static struct rb_root page_alloc_tree; |
264 | static struct rb_root page_alloc_sorted; | 274 | static struct rb_root page_alloc_sorted; |
275 | static struct rb_root page_caller_tree; | ||
276 | static struct rb_root page_caller_sorted; | ||
265 | 277 | ||
266 | static struct page_stat *search_page(unsigned long page, bool create) | 278 | struct alloc_func { |
279 | u64 start; | ||
280 | u64 end; | ||
281 | char *name; | ||
282 | }; | ||
283 | |||
284 | static int nr_alloc_funcs; | ||
285 | static struct alloc_func *alloc_func_list; | ||
286 | |||
287 | static int funcmp(const void *a, const void *b) | ||
288 | { | ||
289 | const struct alloc_func *fa = a; | ||
290 | const struct alloc_func *fb = b; | ||
291 | |||
292 | if (fa->start > fb->start) | ||
293 | return 1; | ||
294 | else | ||
295 | return -1; | ||
296 | } | ||
297 | |||
298 | static int callcmp(const void *a, const void *b) | ||
299 | { | ||
300 | const struct alloc_func *fa = a; | ||
301 | const struct alloc_func *fb = b; | ||
302 | |||
303 | if (fb->start <= fa->start && fa->end < fb->end) | ||
304 | return 0; | ||
305 | |||
306 | if (fa->start > fb->start) | ||
307 | return 1; | ||
308 | else | ||
309 | return -1; | ||
310 | } | ||
311 | |||
312 | static int build_alloc_func_list(void) | ||
313 | { | ||
314 | int ret; | ||
315 | struct map *kernel_map; | ||
316 | struct symbol *sym; | ||
317 | struct rb_node *node; | ||
318 | struct alloc_func *func; | ||
319 | struct machine *machine = &kmem_session->machines.host; | ||
320 | regex_t alloc_func_regex; | ||
321 | const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?"; | ||
322 | |||
323 | ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED); | ||
324 | if (ret) { | ||
325 | char err[BUFSIZ]; | ||
326 | |||
327 | regerror(ret, &alloc_func_regex, err, sizeof(err)); | ||
328 | pr_err("Invalid regex: %s\n%s", pattern, err); | ||
329 | return -EINVAL; | ||
330 | } | ||
331 | |||
332 | kernel_map = machine->vmlinux_maps[MAP__FUNCTION]; | ||
333 | if (map__load(kernel_map, NULL) < 0) { | ||
334 | pr_err("cannot load kernel map\n"); | ||
335 | return -ENOENT; | ||
336 | } | ||
337 | |||
338 | map__for_each_symbol(kernel_map, sym, node) { | ||
339 | if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0)) | ||
340 | continue; | ||
341 | |||
342 | func = realloc(alloc_func_list, | ||
343 | (nr_alloc_funcs + 1) * sizeof(*func)); | ||
344 | if (func == NULL) | ||
345 | return -ENOMEM; | ||
346 | |||
347 | pr_debug("alloc func: %s\n", sym->name); | ||
348 | func[nr_alloc_funcs].start = sym->start; | ||
349 | func[nr_alloc_funcs].end = sym->end; | ||
350 | func[nr_alloc_funcs].name = sym->name; | ||
351 | |||
352 | alloc_func_list = func; | ||
353 | nr_alloc_funcs++; | ||
354 | } | ||
355 | |||
356 | qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp); | ||
357 | |||
358 | regfree(&alloc_func_regex); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * Find first non-memory allocation function from callchain. | ||
364 | * The allocation functions are in the 'alloc_func_list'. | ||
365 | */ | ||
366 | static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample) | ||
367 | { | ||
368 | struct addr_location al; | ||
369 | struct machine *machine = &kmem_session->machines.host; | ||
370 | struct callchain_cursor_node *node; | ||
371 | |||
372 | if (alloc_func_list == NULL) { | ||
373 | if (build_alloc_func_list() < 0) | ||
374 | goto out; | ||
375 | } | ||
376 | |||
377 | al.thread = machine__findnew_thread(machine, sample->pid, sample->tid); | ||
378 | sample__resolve_callchain(sample, NULL, evsel, &al, 16); | ||
379 | |||
380 | callchain_cursor_commit(&callchain_cursor); | ||
381 | while (true) { | ||
382 | struct alloc_func key, *caller; | ||
383 | u64 addr; | ||
384 | |||
385 | node = callchain_cursor_current(&callchain_cursor); | ||
386 | if (node == NULL) | ||
387 | break; | ||
388 | |||
389 | key.start = key.end = node->ip; | ||
390 | caller = bsearch(&key, alloc_func_list, nr_alloc_funcs, | ||
391 | sizeof(key), callcmp); | ||
392 | if (!caller) { | ||
393 | /* found */ | ||
394 | if (node->map) | ||
395 | addr = map__unmap_ip(node->map, node->ip); | ||
396 | else | ||
397 | addr = node->ip; | ||
398 | |||
399 | return addr; | ||
400 | } else | ||
401 | pr_debug3("skipping alloc function: %s\n", caller->name); | ||
402 | |||
403 | callchain_cursor_advance(&callchain_cursor); | ||
404 | } | ||
405 | |||
406 | out: | ||
407 | pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip); | ||
408 | return sample->ip; | ||
409 | } | ||
410 | |||
411 | struct sort_dimension { | ||
412 | const char name[20]; | ||
413 | sort_fn_t cmp; | ||
414 | struct list_head list; | ||
415 | }; | ||
416 | |||
417 | static LIST_HEAD(page_alloc_sort_input); | ||
418 | static LIST_HEAD(page_caller_sort_input); | ||
419 | |||
420 | static struct page_stat * | ||
421 | __page_stat__findnew_page(struct page_stat *pstat, bool create) | ||
267 | { | 422 | { |
268 | struct rb_node **node = &page_tree.rb_node; | 423 | struct rb_node **node = &page_live_tree.rb_node; |
269 | struct rb_node *parent = NULL; | 424 | struct rb_node *parent = NULL; |
270 | struct page_stat *data; | 425 | struct page_stat *data; |
271 | 426 | ||
@@ -275,7 +430,7 @@ static struct page_stat *search_page(unsigned long page, bool create) | |||
275 | parent = *node; | 430 | parent = *node; |
276 | data = rb_entry(*node, struct page_stat, node); | 431 | data = rb_entry(*node, struct page_stat, node); |
277 | 432 | ||
278 | cmp = data->page - page; | 433 | cmp = data->page - pstat->page; |
279 | if (cmp < 0) | 434 | if (cmp < 0) |
280 | node = &parent->rb_left; | 435 | node = &parent->rb_left; |
281 | else if (cmp > 0) | 436 | else if (cmp > 0) |
@@ -289,49 +444,48 @@ static struct page_stat *search_page(unsigned long page, bool create) | |||
289 | 444 | ||
290 | data = zalloc(sizeof(*data)); | 445 | data = zalloc(sizeof(*data)); |
291 | if (data != NULL) { | 446 | if (data != NULL) { |
292 | data->page = page; | 447 | data->page = pstat->page; |
448 | data->order = pstat->order; | ||
449 | data->gfp_flags = pstat->gfp_flags; | ||
450 | data->migrate_type = pstat->migrate_type; | ||
293 | 451 | ||
294 | rb_link_node(&data->node, parent, node); | 452 | rb_link_node(&data->node, parent, node); |
295 | rb_insert_color(&data->node, &page_tree); | 453 | rb_insert_color(&data->node, &page_live_tree); |
296 | } | 454 | } |
297 | 455 | ||
298 | return data; | 456 | return data; |
299 | } | 457 | } |
300 | 458 | ||
301 | static int page_stat_cmp(struct page_stat *a, struct page_stat *b) | 459 | static struct page_stat *page_stat__find_page(struct page_stat *pstat) |
302 | { | 460 | { |
303 | if (a->page > b->page) | 461 | return __page_stat__findnew_page(pstat, false); |
304 | return -1; | 462 | } |
305 | if (a->page < b->page) | 463 | |
306 | return 1; | 464 | static struct page_stat *page_stat__findnew_page(struct page_stat *pstat) |
307 | if (a->order > b->order) | 465 | { |
308 | return -1; | 466 | return __page_stat__findnew_page(pstat, true); |
309 | if (a->order < b->order) | ||
310 | return 1; | ||
311 | if (a->migrate_type > b->migrate_type) | ||
312 | return -1; | ||
313 | if (a->migrate_type < b->migrate_type) | ||
314 | return 1; | ||
315 | if (a->gfp_flags > b->gfp_flags) | ||
316 | return -1; | ||
317 | if (a->gfp_flags < b->gfp_flags) | ||
318 | return 1; | ||
319 | return 0; | ||
320 | } | 467 | } |
321 | 468 | ||
322 | static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool create) | 469 | static struct page_stat * |
470 | __page_stat__findnew_alloc(struct page_stat *pstat, bool create) | ||
323 | { | 471 | { |
324 | struct rb_node **node = &page_alloc_tree.rb_node; | 472 | struct rb_node **node = &page_alloc_tree.rb_node; |
325 | struct rb_node *parent = NULL; | 473 | struct rb_node *parent = NULL; |
326 | struct page_stat *data; | 474 | struct page_stat *data; |
475 | struct sort_dimension *sort; | ||
327 | 476 | ||
328 | while (*node) { | 477 | while (*node) { |
329 | s64 cmp; | 478 | int cmp = 0; |
330 | 479 | ||
331 | parent = *node; | 480 | parent = *node; |
332 | data = rb_entry(*node, struct page_stat, node); | 481 | data = rb_entry(*node, struct page_stat, node); |
333 | 482 | ||
334 | cmp = page_stat_cmp(data, stat); | 483 | list_for_each_entry(sort, &page_alloc_sort_input, list) { |
484 | cmp = sort->cmp(pstat, data); | ||
485 | if (cmp) | ||
486 | break; | ||
487 | } | ||
488 | |||
335 | if (cmp < 0) | 489 | if (cmp < 0) |
336 | node = &parent->rb_left; | 490 | node = &parent->rb_left; |
337 | else if (cmp > 0) | 491 | else if (cmp > 0) |
@@ -345,10 +499,10 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool cre | |||
345 | 499 | ||
346 | data = zalloc(sizeof(*data)); | 500 | data = zalloc(sizeof(*data)); |
347 | if (data != NULL) { | 501 | if (data != NULL) { |
348 | data->page = stat->page; | 502 | data->page = pstat->page; |
349 | data->order = stat->order; | 503 | data->order = pstat->order; |
350 | data->gfp_flags = stat->gfp_flags; | 504 | data->gfp_flags = pstat->gfp_flags; |
351 | data->migrate_type = stat->migrate_type; | 505 | data->migrate_type = pstat->migrate_type; |
352 | 506 | ||
353 | rb_link_node(&data->node, parent, node); | 507 | rb_link_node(&data->node, parent, node); |
354 | rb_insert_color(&data->node, &page_alloc_tree); | 508 | rb_insert_color(&data->node, &page_alloc_tree); |
@@ -357,6 +511,71 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *stat, bool cre | |||
357 | return data; | 511 | return data; |
358 | } | 512 | } |
359 | 513 | ||
514 | static struct page_stat *page_stat__find_alloc(struct page_stat *pstat) | ||
515 | { | ||
516 | return __page_stat__findnew_alloc(pstat, false); | ||
517 | } | ||
518 | |||
519 | static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat) | ||
520 | { | ||
521 | return __page_stat__findnew_alloc(pstat, true); | ||
522 | } | ||
523 | |||
524 | static struct page_stat * | ||
525 | __page_stat__findnew_caller(struct page_stat *pstat, bool create) | ||
526 | { | ||
527 | struct rb_node **node = &page_caller_tree.rb_node; | ||
528 | struct rb_node *parent = NULL; | ||
529 | struct page_stat *data; | ||
530 | struct sort_dimension *sort; | ||
531 | |||
532 | while (*node) { | ||
533 | int cmp = 0; | ||
534 | |||
535 | parent = *node; | ||
536 | data = rb_entry(*node, struct page_stat, node); | ||
537 | |||
538 | list_for_each_entry(sort, &page_caller_sort_input, list) { | ||
539 | cmp = sort->cmp(pstat, data); | ||
540 | if (cmp) | ||
541 | break; | ||
542 | } | ||
543 | |||
544 | if (cmp < 0) | ||
545 | node = &parent->rb_left; | ||
546 | else if (cmp > 0) | ||
547 | node = &parent->rb_right; | ||
548 | else | ||
549 | return data; | ||
550 | } | ||
551 | |||
552 | if (!create) | ||
553 | return NULL; | ||
554 | |||
555 | data = zalloc(sizeof(*data)); | ||
556 | if (data != NULL) { | ||
557 | data->callsite = pstat->callsite; | ||
558 | data->order = pstat->order; | ||
559 | data->gfp_flags = pstat->gfp_flags; | ||
560 | data->migrate_type = pstat->migrate_type; | ||
561 | |||
562 | rb_link_node(&data->node, parent, node); | ||
563 | rb_insert_color(&data->node, &page_caller_tree); | ||
564 | } | ||
565 | |||
566 | return data; | ||
567 | } | ||
568 | |||
569 | static struct page_stat *page_stat__find_caller(struct page_stat *pstat) | ||
570 | { | ||
571 | return __page_stat__findnew_caller(pstat, false); | ||
572 | } | ||
573 | |||
574 | static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat) | ||
575 | { | ||
576 | return __page_stat__findnew_caller(pstat, true); | ||
577 | } | ||
578 | |||
360 | static bool valid_page(u64 pfn_or_page) | 579 | static bool valid_page(u64 pfn_or_page) |
361 | { | 580 | { |
362 | if (use_pfn && pfn_or_page == -1UL) | 581 | if (use_pfn && pfn_or_page == -1UL) |
@@ -366,6 +585,176 @@ static bool valid_page(u64 pfn_or_page) | |||
366 | return true; | 585 | return true; |
367 | } | 586 | } |
368 | 587 | ||
588 | struct gfp_flag { | ||
589 | unsigned int flags; | ||
590 | char *compact_str; | ||
591 | char *human_readable; | ||
592 | }; | ||
593 | |||
594 | static struct gfp_flag *gfps; | ||
595 | static int nr_gfps; | ||
596 | |||
597 | static int gfpcmp(const void *a, const void *b) | ||
598 | { | ||
599 | const struct gfp_flag *fa = a; | ||
600 | const struct gfp_flag *fb = b; | ||
601 | |||
602 | return fa->flags - fb->flags; | ||
603 | } | ||
604 | |||
605 | /* see include/trace/events/gfpflags.h */ | ||
606 | static const struct { | ||
607 | const char *original; | ||
608 | const char *compact; | ||
609 | } gfp_compact_table[] = { | ||
610 | { "GFP_TRANSHUGE", "THP" }, | ||
611 | { "GFP_HIGHUSER_MOVABLE", "HUM" }, | ||
612 | { "GFP_HIGHUSER", "HU" }, | ||
613 | { "GFP_USER", "U" }, | ||
614 | { "GFP_TEMPORARY", "TMP" }, | ||
615 | { "GFP_KERNEL", "K" }, | ||
616 | { "GFP_NOFS", "NF" }, | ||
617 | { "GFP_ATOMIC", "A" }, | ||
618 | { "GFP_NOIO", "NI" }, | ||
619 | { "GFP_HIGH", "H" }, | ||
620 | { "GFP_WAIT", "W" }, | ||
621 | { "GFP_IO", "I" }, | ||
622 | { "GFP_COLD", "CO" }, | ||
623 | { "GFP_NOWARN", "NWR" }, | ||
624 | { "GFP_REPEAT", "R" }, | ||
625 | { "GFP_NOFAIL", "NF" }, | ||
626 | { "GFP_NORETRY", "NR" }, | ||
627 | { "GFP_COMP", "C" }, | ||
628 | { "GFP_ZERO", "Z" }, | ||
629 | { "GFP_NOMEMALLOC", "NMA" }, | ||
630 | { "GFP_MEMALLOC", "MA" }, | ||
631 | { "GFP_HARDWALL", "HW" }, | ||
632 | { "GFP_THISNODE", "TN" }, | ||
633 | { "GFP_RECLAIMABLE", "RC" }, | ||
634 | { "GFP_MOVABLE", "M" }, | ||
635 | { "GFP_NOTRACK", "NT" }, | ||
636 | { "GFP_NO_KSWAPD", "NK" }, | ||
637 | { "GFP_OTHER_NODE", "ON" }, | ||
638 | { "GFP_NOWAIT", "NW" }, | ||
639 | }; | ||
640 | |||
641 | static size_t max_gfp_len; | ||
642 | |||
643 | static char *compact_gfp_flags(char *gfp_flags) | ||
644 | { | ||
645 | char *orig_flags = strdup(gfp_flags); | ||
646 | char *new_flags = NULL; | ||
647 | char *str, *pos; | ||
648 | size_t len = 0; | ||
649 | |||
650 | if (orig_flags == NULL) | ||
651 | return NULL; | ||
652 | |||
653 | str = strtok_r(orig_flags, "|", &pos); | ||
654 | while (str) { | ||
655 | size_t i; | ||
656 | char *new; | ||
657 | const char *cpt; | ||
658 | |||
659 | for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) { | ||
660 | if (strcmp(gfp_compact_table[i].original, str)) | ||
661 | continue; | ||
662 | |||
663 | cpt = gfp_compact_table[i].compact; | ||
664 | new = realloc(new_flags, len + strlen(cpt) + 2); | ||
665 | if (new == NULL) { | ||
666 | free(new_flags); | ||
667 | return NULL; | ||
668 | } | ||
669 | |||
670 | new_flags = new; | ||
671 | |||
672 | if (!len) { | ||
673 | strcpy(new_flags, cpt); | ||
674 | } else { | ||
675 | strcat(new_flags, "|"); | ||
676 | strcat(new_flags, cpt); | ||
677 | len++; | ||
678 | } | ||
679 | |||
680 | len += strlen(cpt); | ||
681 | } | ||
682 | |||
683 | str = strtok_r(NULL, "|", &pos); | ||
684 | } | ||
685 | |||
686 | if (max_gfp_len < len) | ||
687 | max_gfp_len = len; | ||
688 | |||
689 | free(orig_flags); | ||
690 | return new_flags; | ||
691 | } | ||
692 | |||
693 | static char *compact_gfp_string(unsigned long gfp_flags) | ||
694 | { | ||
695 | struct gfp_flag key = { | ||
696 | .flags = gfp_flags, | ||
697 | }; | ||
698 | struct gfp_flag *gfp; | ||
699 | |||
700 | gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp); | ||
701 | if (gfp) | ||
702 | return gfp->compact_str; | ||
703 | |||
704 | return NULL; | ||
705 | } | ||
706 | |||
707 | static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample, | ||
708 | unsigned int gfp_flags) | ||
709 | { | ||
710 | struct pevent_record record = { | ||
711 | .cpu = sample->cpu, | ||
712 | .data = sample->raw_data, | ||
713 | .size = sample->raw_size, | ||
714 | }; | ||
715 | struct trace_seq seq; | ||
716 | char *str, *pos; | ||
717 | |||
718 | if (nr_gfps) { | ||
719 | struct gfp_flag key = { | ||
720 | .flags = gfp_flags, | ||
721 | }; | ||
722 | |||
723 | if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp)) | ||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | trace_seq_init(&seq); | ||
728 | pevent_event_info(&seq, evsel->tp_format, &record); | ||
729 | |||
730 | str = strtok_r(seq.buffer, " ", &pos); | ||
731 | while (str) { | ||
732 | if (!strncmp(str, "gfp_flags=", 10)) { | ||
733 | struct gfp_flag *new; | ||
734 | |||
735 | new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps)); | ||
736 | if (new == NULL) | ||
737 | return -ENOMEM; | ||
738 | |||
739 | gfps = new; | ||
740 | new += nr_gfps++; | ||
741 | |||
742 | new->flags = gfp_flags; | ||
743 | new->human_readable = strdup(str + 10); | ||
744 | new->compact_str = compact_gfp_flags(str + 10); | ||
745 | if (!new->human_readable || !new->compact_str) | ||
746 | return -ENOMEM; | ||
747 | |||
748 | qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp); | ||
749 | } | ||
750 | |||
751 | str = strtok_r(NULL, " ", &pos); | ||
752 | } | ||
753 | |||
754 | trace_seq_destroy(&seq); | ||
755 | return 0; | ||
756 | } | ||
757 | |||
369 | static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, | 758 | static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, |
370 | struct perf_sample *sample) | 759 | struct perf_sample *sample) |
371 | { | 760 | { |
@@ -375,7 +764,8 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, | |||
375 | unsigned int migrate_type = perf_evsel__intval(evsel, sample, | 764 | unsigned int migrate_type = perf_evsel__intval(evsel, sample, |
376 | "migratetype"); | 765 | "migratetype"); |
377 | u64 bytes = kmem_page_size << order; | 766 | u64 bytes = kmem_page_size << order; |
378 | struct page_stat *stat; | 767 | u64 callsite; |
768 | struct page_stat *pstat; | ||
379 | struct page_stat this = { | 769 | struct page_stat this = { |
380 | .order = order, | 770 | .order = order, |
381 | .gfp_flags = gfp_flags, | 771 | .gfp_flags = gfp_flags, |
@@ -397,25 +787,41 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel, | |||
397 | return 0; | 787 | return 0; |
398 | } | 788 | } |
399 | 789 | ||
790 | if (parse_gfp_flags(evsel, sample, gfp_flags) < 0) | ||
791 | return -1; | ||
792 | |||
793 | callsite = find_callsite(evsel, sample); | ||
794 | |||
400 | /* | 795 | /* |
401 | * This is to find the current page (with correct gfp flags and | 796 | * This is to find the current page (with correct gfp flags and |
402 | * migrate type) at free event. | 797 | * migrate type) at free event. |
403 | */ | 798 | */ |
404 | stat = search_page(page, true); | 799 | this.page = page; |
405 | if (stat == NULL) | 800 | pstat = page_stat__findnew_page(&this); |
801 | if (pstat == NULL) | ||
406 | return -ENOMEM; | 802 | return -ENOMEM; |
407 | 803 | ||
408 | stat->order = order; | 804 | pstat->nr_alloc++; |
409 | stat->gfp_flags = gfp_flags; | 805 | pstat->alloc_bytes += bytes; |
410 | stat->migrate_type = migrate_type; | 806 | pstat->callsite = callsite; |
411 | 807 | ||
412 | this.page = page; | 808 | if (!live_page) { |
413 | stat = search_page_alloc_stat(&this, true); | 809 | pstat = page_stat__findnew_alloc(&this); |
414 | if (stat == NULL) | 810 | if (pstat == NULL) |
811 | return -ENOMEM; | ||
812 | |||
813 | pstat->nr_alloc++; | ||
814 | pstat->alloc_bytes += bytes; | ||
815 | pstat->callsite = callsite; | ||
816 | } | ||
817 | |||
818 | this.callsite = callsite; | ||
819 | pstat = page_stat__findnew_caller(&this); | ||
820 | if (pstat == NULL) | ||
415 | return -ENOMEM; | 821 | return -ENOMEM; |
416 | 822 | ||
417 | stat->nr_alloc++; | 823 | pstat->nr_alloc++; |
418 | stat->alloc_bytes += bytes; | 824 | pstat->alloc_bytes += bytes; |
419 | 825 | ||
420 | order_stats[order][migrate_type]++; | 826 | order_stats[order][migrate_type]++; |
421 | 827 | ||
@@ -428,7 +834,7 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
428 | u64 page; | 834 | u64 page; |
429 | unsigned int order = perf_evsel__intval(evsel, sample, "order"); | 835 | unsigned int order = perf_evsel__intval(evsel, sample, "order"); |
430 | u64 bytes = kmem_page_size << order; | 836 | u64 bytes = kmem_page_size << order; |
431 | struct page_stat *stat; | 837 | struct page_stat *pstat; |
432 | struct page_stat this = { | 838 | struct page_stat this = { |
433 | .order = order, | 839 | .order = order, |
434 | }; | 840 | }; |
@@ -441,8 +847,9 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
441 | nr_page_frees++; | 847 | nr_page_frees++; |
442 | total_page_free_bytes += bytes; | 848 | total_page_free_bytes += bytes; |
443 | 849 | ||
444 | stat = search_page(page, false); | 850 | this.page = page; |
445 | if (stat == NULL) { | 851 | pstat = page_stat__find_page(&this); |
852 | if (pstat == NULL) { | ||
446 | pr_debug2("missing free at page %"PRIx64" (order: %d)\n", | 853 | pr_debug2("missing free at page %"PRIx64" (order: %d)\n", |
447 | page, order); | 854 | page, order); |
448 | 855 | ||
@@ -452,19 +859,40 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel, | |||
452 | return 0; | 859 | return 0; |
453 | } | 860 | } |
454 | 861 | ||
455 | this.page = page; | 862 | this.gfp_flags = pstat->gfp_flags; |
456 | this.gfp_flags = stat->gfp_flags; | 863 | this.migrate_type = pstat->migrate_type; |
457 | this.migrate_type = stat->migrate_type; | 864 | this.callsite = pstat->callsite; |
458 | 865 | ||
459 | rb_erase(&stat->node, &page_tree); | 866 | rb_erase(&pstat->node, &page_live_tree); |
460 | free(stat); | 867 | free(pstat); |
461 | 868 | ||
462 | stat = search_page_alloc_stat(&this, false); | 869 | if (live_page) { |
463 | if (stat == NULL) | 870 | order_stats[this.order][this.migrate_type]--; |
871 | } else { | ||
872 | pstat = page_stat__find_alloc(&this); | ||
873 | if (pstat == NULL) | ||
874 | return -ENOMEM; | ||
875 | |||
876 | pstat->nr_free++; | ||
877 | pstat->free_bytes += bytes; | ||
878 | } | ||
879 | |||
880 | pstat = page_stat__find_caller(&this); | ||
881 | if (pstat == NULL) | ||
464 | return -ENOENT; | 882 | return -ENOENT; |
465 | 883 | ||
466 | stat->nr_free++; | 884 | pstat->nr_free++; |
467 | stat->free_bytes += bytes; | 885 | pstat->free_bytes += bytes; |
886 | |||
887 | if (live_page) { | ||
888 | pstat->nr_alloc--; | ||
889 | pstat->alloc_bytes -= bytes; | ||
890 | |||
891 | if (pstat->nr_alloc == 0) { | ||
892 | rb_erase(&pstat->node, &page_caller_tree); | ||
893 | free(pstat); | ||
894 | } | ||
895 | } | ||
468 | 896 | ||
469 | return 0; | 897 | return 0; |
470 | } | 898 | } |
@@ -576,41 +1004,111 @@ static const char * const migrate_type_str[] = { | |||
576 | "UNKNOWN", | 1004 | "UNKNOWN", |
577 | }; | 1005 | }; |
578 | 1006 | ||
579 | static void __print_page_result(struct rb_root *root, | 1007 | static void __print_page_alloc_result(struct perf_session *session, int n_lines) |
580 | struct perf_session *session __maybe_unused, | ||
581 | int n_lines) | ||
582 | { | 1008 | { |
583 | struct rb_node *next = rb_first(root); | 1009 | struct rb_node *next = rb_first(&page_alloc_sorted); |
1010 | struct machine *machine = &session->machines.host; | ||
584 | const char *format; | 1011 | const char *format; |
1012 | int gfp_len = max(strlen("GFP flags"), max_gfp_len); | ||
585 | 1013 | ||
586 | printf("\n%.80s\n", graph_dotted_line); | 1014 | printf("\n%.105s\n", graph_dotted_line); |
587 | printf(" %-16s | Total alloc (KB) | Hits | Order | Mig.type | GFP flags\n", | 1015 | printf(" %-16s | %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n", |
588 | use_pfn ? "PFN" : "Page"); | 1016 | use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total", |
589 | printf("%.80s\n", graph_dotted_line); | 1017 | gfp_len, "GFP flags"); |
1018 | printf("%.105s\n", graph_dotted_line); | ||
590 | 1019 | ||
591 | if (use_pfn) | 1020 | if (use_pfn) |
592 | format = " %16llu | %'16llu | %'9d | %5d | %8s | %08lx\n"; | 1021 | format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n"; |
593 | else | 1022 | else |
594 | format = " %016llx | %'16llu | %'9d | %5d | %8s | %08lx\n"; | 1023 | format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n"; |
595 | 1024 | ||
596 | while (next && n_lines--) { | 1025 | while (next && n_lines--) { |
597 | struct page_stat *data; | 1026 | struct page_stat *data; |
1027 | struct symbol *sym; | ||
1028 | struct map *map; | ||
1029 | char buf[32]; | ||
1030 | char *caller = buf; | ||
598 | 1031 | ||
599 | data = rb_entry(next, struct page_stat, node); | 1032 | data = rb_entry(next, struct page_stat, node); |
1033 | sym = machine__find_kernel_function(machine, data->callsite, | ||
1034 | &map, NULL); | ||
1035 | if (sym && sym->name) | ||
1036 | caller = sym->name; | ||
1037 | else | ||
1038 | scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite); | ||
600 | 1039 | ||
601 | printf(format, (unsigned long long)data->page, | 1040 | printf(format, (unsigned long long)data->page, |
602 | (unsigned long long)data->alloc_bytes / 1024, | 1041 | (unsigned long long)data->alloc_bytes / 1024, |
603 | data->nr_alloc, data->order, | 1042 | data->nr_alloc, data->order, |
604 | migrate_type_str[data->migrate_type], | 1043 | migrate_type_str[data->migrate_type], |
605 | (unsigned long)data->gfp_flags); | 1044 | gfp_len, compact_gfp_string(data->gfp_flags), caller); |
606 | 1045 | ||
607 | next = rb_next(next); | 1046 | next = rb_next(next); |
608 | } | 1047 | } |
609 | 1048 | ||
610 | if (n_lines == -1) | 1049 | if (n_lines == -1) { |
611 | printf(" ... | ... | ... | ... | ... | ... \n"); | 1050 | printf(" ... | ... | ... | ... | ... | %-*s | ...\n", |
1051 | gfp_len, "..."); | ||
1052 | } | ||
612 | 1053 | ||
613 | printf("%.80s\n", graph_dotted_line); | 1054 | printf("%.105s\n", graph_dotted_line); |
1055 | } | ||
1056 | |||
1057 | static void __print_page_caller_result(struct perf_session *session, int n_lines) | ||
1058 | { | ||
1059 | struct rb_node *next = rb_first(&page_caller_sorted); | ||
1060 | struct machine *machine = &session->machines.host; | ||
1061 | int gfp_len = max(strlen("GFP flags"), max_gfp_len); | ||
1062 | |||
1063 | printf("\n%.105s\n", graph_dotted_line); | ||
1064 | printf(" %5s alloc (KB) | Hits | Order | Mig.type | %-*s | Callsite\n", | ||
1065 | live_page ? "Live" : "Total", gfp_len, "GFP flags"); | ||
1066 | printf("%.105s\n", graph_dotted_line); | ||
1067 | |||
1068 | while (next && n_lines--) { | ||
1069 | struct page_stat *data; | ||
1070 | struct symbol *sym; | ||
1071 | struct map *map; | ||
1072 | char buf[32]; | ||
1073 | char *caller = buf; | ||
1074 | |||
1075 | data = rb_entry(next, struct page_stat, node); | ||
1076 | sym = machine__find_kernel_function(machine, data->callsite, | ||
1077 | &map, NULL); | ||
1078 | if (sym && sym->name) | ||
1079 | caller = sym->name; | ||
1080 | else | ||
1081 | scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite); | ||
1082 | |||
1083 | printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n", | ||
1084 | (unsigned long long)data->alloc_bytes / 1024, | ||
1085 | data->nr_alloc, data->order, | ||
1086 | migrate_type_str[data->migrate_type], | ||
1087 | gfp_len, compact_gfp_string(data->gfp_flags), caller); | ||
1088 | |||
1089 | next = rb_next(next); | ||
1090 | } | ||
1091 | |||
1092 | if (n_lines == -1) { | ||
1093 | printf(" ... | ... | ... | ... | %-*s | ...\n", | ||
1094 | gfp_len, "..."); | ||
1095 | } | ||
1096 | |||
1097 | printf("%.105s\n", graph_dotted_line); | ||
1098 | } | ||
1099 | |||
1100 | static void print_gfp_flags(void) | ||
1101 | { | ||
1102 | int i; | ||
1103 | |||
1104 | printf("#\n"); | ||
1105 | printf("# GFP flags\n"); | ||
1106 | printf("# ---------\n"); | ||
1107 | for (i = 0; i < nr_gfps; i++) { | ||
1108 | printf("# %08x: %*s: %s\n", gfps[i].flags, | ||
1109 | (int) max_gfp_len, gfps[i].compact_str, | ||
1110 | gfps[i].human_readable); | ||
1111 | } | ||
614 | } | 1112 | } |
615 | 1113 | ||
616 | static void print_slab_summary(void) | 1114 | static void print_slab_summary(void) |
@@ -682,8 +1180,12 @@ static void print_slab_result(struct perf_session *session) | |||
682 | 1180 | ||
683 | static void print_page_result(struct perf_session *session) | 1181 | static void print_page_result(struct perf_session *session) |
684 | { | 1182 | { |
1183 | if (caller_flag || alloc_flag) | ||
1184 | print_gfp_flags(); | ||
1185 | if (caller_flag) | ||
1186 | __print_page_caller_result(session, caller_lines); | ||
685 | if (alloc_flag) | 1187 | if (alloc_flag) |
686 | __print_page_result(&page_alloc_sorted, session, alloc_lines); | 1188 | __print_page_alloc_result(session, alloc_lines); |
687 | print_page_summary(); | 1189 | print_page_summary(); |
688 | } | 1190 | } |
689 | 1191 | ||
@@ -695,14 +1197,10 @@ static void print_result(struct perf_session *session) | |||
695 | print_page_result(session); | 1197 | print_page_result(session); |
696 | } | 1198 | } |
697 | 1199 | ||
698 | struct sort_dimension { | 1200 | static LIST_HEAD(slab_caller_sort); |
699 | const char name[20]; | 1201 | static LIST_HEAD(slab_alloc_sort); |
700 | sort_fn_t cmp; | 1202 | static LIST_HEAD(page_caller_sort); |
701 | struct list_head list; | 1203 | static LIST_HEAD(page_alloc_sort); |
702 | }; | ||
703 | |||
704 | static LIST_HEAD(caller_sort); | ||
705 | static LIST_HEAD(alloc_sort); | ||
706 | 1204 | ||
707 | static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data, | 1205 | static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data, |
708 | struct list_head *sort_list) | 1206 | struct list_head *sort_list) |
@@ -751,10 +1249,12 @@ static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted | |||
751 | } | 1249 | } |
752 | } | 1250 | } |
753 | 1251 | ||
754 | static void sort_page_insert(struct rb_root *root, struct page_stat *data) | 1252 | static void sort_page_insert(struct rb_root *root, struct page_stat *data, |
1253 | struct list_head *sort_list) | ||
755 | { | 1254 | { |
756 | struct rb_node **new = &root->rb_node; | 1255 | struct rb_node **new = &root->rb_node; |
757 | struct rb_node *parent = NULL; | 1256 | struct rb_node *parent = NULL; |
1257 | struct sort_dimension *sort; | ||
758 | 1258 | ||
759 | while (*new) { | 1259 | while (*new) { |
760 | struct page_stat *this; | 1260 | struct page_stat *this; |
@@ -763,8 +1263,11 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data) | |||
763 | this = rb_entry(*new, struct page_stat, node); | 1263 | this = rb_entry(*new, struct page_stat, node); |
764 | parent = *new; | 1264 | parent = *new; |
765 | 1265 | ||
766 | /* TODO: support more sort key */ | 1266 | list_for_each_entry(sort, sort_list, list) { |
767 | cmp = data->alloc_bytes - this->alloc_bytes; | 1267 | cmp = sort->cmp(data, this); |
1268 | if (cmp) | ||
1269 | break; | ||
1270 | } | ||
768 | 1271 | ||
769 | if (cmp > 0) | 1272 | if (cmp > 0) |
770 | new = &parent->rb_left; | 1273 | new = &parent->rb_left; |
@@ -776,7 +1279,8 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data) | |||
776 | rb_insert_color(&data->node, root); | 1279 | rb_insert_color(&data->node, root); |
777 | } | 1280 | } |
778 | 1281 | ||
779 | static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted) | 1282 | static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted, |
1283 | struct list_head *sort_list) | ||
780 | { | 1284 | { |
781 | struct rb_node *node; | 1285 | struct rb_node *node; |
782 | struct page_stat *data; | 1286 | struct page_stat *data; |
@@ -788,7 +1292,7 @@ static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted | |||
788 | 1292 | ||
789 | rb_erase(node, root); | 1293 | rb_erase(node, root); |
790 | data = rb_entry(node, struct page_stat, node); | 1294 | data = rb_entry(node, struct page_stat, node); |
791 | sort_page_insert(root_sorted, data); | 1295 | sort_page_insert(root_sorted, data, sort_list); |
792 | } | 1296 | } |
793 | } | 1297 | } |
794 | 1298 | ||
@@ -796,12 +1300,20 @@ static void sort_result(void) | |||
796 | { | 1300 | { |
797 | if (kmem_slab) { | 1301 | if (kmem_slab) { |
798 | __sort_slab_result(&root_alloc_stat, &root_alloc_sorted, | 1302 | __sort_slab_result(&root_alloc_stat, &root_alloc_sorted, |
799 | &alloc_sort); | 1303 | &slab_alloc_sort); |
800 | __sort_slab_result(&root_caller_stat, &root_caller_sorted, | 1304 | __sort_slab_result(&root_caller_stat, &root_caller_sorted, |
801 | &caller_sort); | 1305 | &slab_caller_sort); |
802 | } | 1306 | } |
803 | if (kmem_page) { | 1307 | if (kmem_page) { |
804 | __sort_page_result(&page_alloc_tree, &page_alloc_sorted); | 1308 | if (live_page) |
1309 | __sort_page_result(&page_live_tree, &page_alloc_sorted, | ||
1310 | &page_alloc_sort); | ||
1311 | else | ||
1312 | __sort_page_result(&page_alloc_tree, &page_alloc_sorted, | ||
1313 | &page_alloc_sort); | ||
1314 | |||
1315 | __sort_page_result(&page_caller_tree, &page_caller_sorted, | ||
1316 | &page_caller_sort); | ||
805 | } | 1317 | } |
806 | } | 1318 | } |
807 | 1319 | ||
@@ -850,8 +1362,12 @@ out: | |||
850 | return err; | 1362 | return err; |
851 | } | 1363 | } |
852 | 1364 | ||
853 | static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1365 | /* slab sort keys */ |
1366 | static int ptr_cmp(void *a, void *b) | ||
854 | { | 1367 | { |
1368 | struct alloc_stat *l = a; | ||
1369 | struct alloc_stat *r = b; | ||
1370 | |||
855 | if (l->ptr < r->ptr) | 1371 | if (l->ptr < r->ptr) |
856 | return -1; | 1372 | return -1; |
857 | else if (l->ptr > r->ptr) | 1373 | else if (l->ptr > r->ptr) |
@@ -864,8 +1380,11 @@ static struct sort_dimension ptr_sort_dimension = { | |||
864 | .cmp = ptr_cmp, | 1380 | .cmp = ptr_cmp, |
865 | }; | 1381 | }; |
866 | 1382 | ||
867 | static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1383 | static int slab_callsite_cmp(void *a, void *b) |
868 | { | 1384 | { |
1385 | struct alloc_stat *l = a; | ||
1386 | struct alloc_stat *r = b; | ||
1387 | |||
869 | if (l->call_site < r->call_site) | 1388 | if (l->call_site < r->call_site) |
870 | return -1; | 1389 | return -1; |
871 | else if (l->call_site > r->call_site) | 1390 | else if (l->call_site > r->call_site) |
@@ -875,11 +1394,14 @@ static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) | |||
875 | 1394 | ||
876 | static struct sort_dimension callsite_sort_dimension = { | 1395 | static struct sort_dimension callsite_sort_dimension = { |
877 | .name = "callsite", | 1396 | .name = "callsite", |
878 | .cmp = callsite_cmp, | 1397 | .cmp = slab_callsite_cmp, |
879 | }; | 1398 | }; |
880 | 1399 | ||
881 | static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1400 | static int hit_cmp(void *a, void *b) |
882 | { | 1401 | { |
1402 | struct alloc_stat *l = a; | ||
1403 | struct alloc_stat *r = b; | ||
1404 | |||
883 | if (l->hit < r->hit) | 1405 | if (l->hit < r->hit) |
884 | return -1; | 1406 | return -1; |
885 | else if (l->hit > r->hit) | 1407 | else if (l->hit > r->hit) |
@@ -892,8 +1414,11 @@ static struct sort_dimension hit_sort_dimension = { | |||
892 | .cmp = hit_cmp, | 1414 | .cmp = hit_cmp, |
893 | }; | 1415 | }; |
894 | 1416 | ||
895 | static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1417 | static int bytes_cmp(void *a, void *b) |
896 | { | 1418 | { |
1419 | struct alloc_stat *l = a; | ||
1420 | struct alloc_stat *r = b; | ||
1421 | |||
897 | if (l->bytes_alloc < r->bytes_alloc) | 1422 | if (l->bytes_alloc < r->bytes_alloc) |
898 | return -1; | 1423 | return -1; |
899 | else if (l->bytes_alloc > r->bytes_alloc) | 1424 | else if (l->bytes_alloc > r->bytes_alloc) |
@@ -906,9 +1431,11 @@ static struct sort_dimension bytes_sort_dimension = { | |||
906 | .cmp = bytes_cmp, | 1431 | .cmp = bytes_cmp, |
907 | }; | 1432 | }; |
908 | 1433 | ||
909 | static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1434 | static int frag_cmp(void *a, void *b) |
910 | { | 1435 | { |
911 | double x, y; | 1436 | double x, y; |
1437 | struct alloc_stat *l = a; | ||
1438 | struct alloc_stat *r = b; | ||
912 | 1439 | ||
913 | x = fragmentation(l->bytes_req, l->bytes_alloc); | 1440 | x = fragmentation(l->bytes_req, l->bytes_alloc); |
914 | y = fragmentation(r->bytes_req, r->bytes_alloc); | 1441 | y = fragmentation(r->bytes_req, r->bytes_alloc); |
@@ -925,8 +1452,11 @@ static struct sort_dimension frag_sort_dimension = { | |||
925 | .cmp = frag_cmp, | 1452 | .cmp = frag_cmp, |
926 | }; | 1453 | }; |
927 | 1454 | ||
928 | static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r) | 1455 | static int pingpong_cmp(void *a, void *b) |
929 | { | 1456 | { |
1457 | struct alloc_stat *l = a; | ||
1458 | struct alloc_stat *r = b; | ||
1459 | |||
930 | if (l->pingpong < r->pingpong) | 1460 | if (l->pingpong < r->pingpong) |
931 | return -1; | 1461 | return -1; |
932 | else if (l->pingpong > r->pingpong) | 1462 | else if (l->pingpong > r->pingpong) |
@@ -939,7 +1469,135 @@ static struct sort_dimension pingpong_sort_dimension = { | |||
939 | .cmp = pingpong_cmp, | 1469 | .cmp = pingpong_cmp, |
940 | }; | 1470 | }; |
941 | 1471 | ||
942 | static struct sort_dimension *avail_sorts[] = { | 1472 | /* page sort keys */ |
1473 | static int page_cmp(void *a, void *b) | ||
1474 | { | ||
1475 | struct page_stat *l = a; | ||
1476 | struct page_stat *r = b; | ||
1477 | |||
1478 | if (l->page < r->page) | ||
1479 | return -1; | ||
1480 | else if (l->page > r->page) | ||
1481 | return 1; | ||
1482 | return 0; | ||
1483 | } | ||
1484 | |||
1485 | static struct sort_dimension page_sort_dimension = { | ||
1486 | .name = "page", | ||
1487 | .cmp = page_cmp, | ||
1488 | }; | ||
1489 | |||
1490 | static int page_callsite_cmp(void *a, void *b) | ||
1491 | { | ||
1492 | struct page_stat *l = a; | ||
1493 | struct page_stat *r = b; | ||
1494 | |||
1495 | if (l->callsite < r->callsite) | ||
1496 | return -1; | ||
1497 | else if (l->callsite > r->callsite) | ||
1498 | return 1; | ||
1499 | return 0; | ||
1500 | } | ||
1501 | |||
1502 | static struct sort_dimension page_callsite_sort_dimension = { | ||
1503 | .name = "callsite", | ||
1504 | .cmp = page_callsite_cmp, | ||
1505 | }; | ||
1506 | |||
1507 | static int page_hit_cmp(void *a, void *b) | ||
1508 | { | ||
1509 | struct page_stat *l = a; | ||
1510 | struct page_stat *r = b; | ||
1511 | |||
1512 | if (l->nr_alloc < r->nr_alloc) | ||
1513 | return -1; | ||
1514 | else if (l->nr_alloc > r->nr_alloc) | ||
1515 | return 1; | ||
1516 | return 0; | ||
1517 | } | ||
1518 | |||
1519 | static struct sort_dimension page_hit_sort_dimension = { | ||
1520 | .name = "hit", | ||
1521 | .cmp = page_hit_cmp, | ||
1522 | }; | ||
1523 | |||
1524 | static int page_bytes_cmp(void *a, void *b) | ||
1525 | { | ||
1526 | struct page_stat *l = a; | ||
1527 | struct page_stat *r = b; | ||
1528 | |||
1529 | if (l->alloc_bytes < r->alloc_bytes) | ||
1530 | return -1; | ||
1531 | else if (l->alloc_bytes > r->alloc_bytes) | ||
1532 | return 1; | ||
1533 | return 0; | ||
1534 | } | ||
1535 | |||
1536 | static struct sort_dimension page_bytes_sort_dimension = { | ||
1537 | .name = "bytes", | ||
1538 | .cmp = page_bytes_cmp, | ||
1539 | }; | ||
1540 | |||
1541 | static int page_order_cmp(void *a, void *b) | ||
1542 | { | ||
1543 | struct page_stat *l = a; | ||
1544 | struct page_stat *r = b; | ||
1545 | |||
1546 | if (l->order < r->order) | ||
1547 | return -1; | ||
1548 | else if (l->order > r->order) | ||
1549 | return 1; | ||
1550 | return 0; | ||
1551 | } | ||
1552 | |||
1553 | static struct sort_dimension page_order_sort_dimension = { | ||
1554 | .name = "order", | ||
1555 | .cmp = page_order_cmp, | ||
1556 | }; | ||
1557 | |||
1558 | static int migrate_type_cmp(void *a, void *b) | ||
1559 | { | ||
1560 | struct page_stat *l = a; | ||
1561 | struct page_stat *r = b; | ||
1562 | |||
1563 | /* for internal use to find free'd page */ | ||
1564 | if (l->migrate_type == -1U) | ||
1565 | return 0; | ||
1566 | |||
1567 | if (l->migrate_type < r->migrate_type) | ||
1568 | return -1; | ||
1569 | else if (l->migrate_type > r->migrate_type) | ||
1570 | return 1; | ||
1571 | return 0; | ||
1572 | } | ||
1573 | |||
1574 | static struct sort_dimension migrate_type_sort_dimension = { | ||
1575 | .name = "migtype", | ||
1576 | .cmp = migrate_type_cmp, | ||
1577 | }; | ||
1578 | |||
1579 | static int gfp_flags_cmp(void *a, void *b) | ||
1580 | { | ||
1581 | struct page_stat *l = a; | ||
1582 | struct page_stat *r = b; | ||
1583 | |||
1584 | /* for internal use to find free'd page */ | ||
1585 | if (l->gfp_flags == -1U) | ||
1586 | return 0; | ||
1587 | |||
1588 | if (l->gfp_flags < r->gfp_flags) | ||
1589 | return -1; | ||
1590 | else if (l->gfp_flags > r->gfp_flags) | ||
1591 | return 1; | ||
1592 | return 0; | ||
1593 | } | ||
1594 | |||
1595 | static struct sort_dimension gfp_flags_sort_dimension = { | ||
1596 | .name = "gfp", | ||
1597 | .cmp = gfp_flags_cmp, | ||
1598 | }; | ||
1599 | |||
1600 | static struct sort_dimension *slab_sorts[] = { | ||
943 | &ptr_sort_dimension, | 1601 | &ptr_sort_dimension, |
944 | &callsite_sort_dimension, | 1602 | &callsite_sort_dimension, |
945 | &hit_sort_dimension, | 1603 | &hit_sort_dimension, |
@@ -948,16 +1606,24 @@ static struct sort_dimension *avail_sorts[] = { | |||
948 | &pingpong_sort_dimension, | 1606 | &pingpong_sort_dimension, |
949 | }; | 1607 | }; |
950 | 1608 | ||
951 | #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts)) | 1609 | static struct sort_dimension *page_sorts[] = { |
1610 | &page_sort_dimension, | ||
1611 | &page_callsite_sort_dimension, | ||
1612 | &page_hit_sort_dimension, | ||
1613 | &page_bytes_sort_dimension, | ||
1614 | &page_order_sort_dimension, | ||
1615 | &migrate_type_sort_dimension, | ||
1616 | &gfp_flags_sort_dimension, | ||
1617 | }; | ||
952 | 1618 | ||
953 | static int sort_dimension__add(const char *tok, struct list_head *list) | 1619 | static int slab_sort_dimension__add(const char *tok, struct list_head *list) |
954 | { | 1620 | { |
955 | struct sort_dimension *sort; | 1621 | struct sort_dimension *sort; |
956 | int i; | 1622 | int i; |
957 | 1623 | ||
958 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { | 1624 | for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) { |
959 | if (!strcmp(avail_sorts[i]->name, tok)) { | 1625 | if (!strcmp(slab_sorts[i]->name, tok)) { |
960 | sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i])); | 1626 | sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i])); |
961 | if (!sort) { | 1627 | if (!sort) { |
962 | pr_err("%s: memdup failed\n", __func__); | 1628 | pr_err("%s: memdup failed\n", __func__); |
963 | return -1; | 1629 | return -1; |
@@ -970,7 +1636,53 @@ static int sort_dimension__add(const char *tok, struct list_head *list) | |||
970 | return -1; | 1636 | return -1; |
971 | } | 1637 | } |
972 | 1638 | ||
973 | static int setup_sorting(struct list_head *sort_list, const char *arg) | 1639 | static int page_sort_dimension__add(const char *tok, struct list_head *list) |
1640 | { | ||
1641 | struct sort_dimension *sort; | ||
1642 | int i; | ||
1643 | |||
1644 | for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) { | ||
1645 | if (!strcmp(page_sorts[i]->name, tok)) { | ||
1646 | sort = memdup(page_sorts[i], sizeof(*page_sorts[i])); | ||
1647 | if (!sort) { | ||
1648 | pr_err("%s: memdup failed\n", __func__); | ||
1649 | return -1; | ||
1650 | } | ||
1651 | list_add_tail(&sort->list, list); | ||
1652 | return 0; | ||
1653 | } | ||
1654 | } | ||
1655 | |||
1656 | return -1; | ||
1657 | } | ||
1658 | |||
1659 | static int setup_slab_sorting(struct list_head *sort_list, const char *arg) | ||
1660 | { | ||
1661 | char *tok; | ||
1662 | char *str = strdup(arg); | ||
1663 | char *pos = str; | ||
1664 | |||
1665 | if (!str) { | ||
1666 | pr_err("%s: strdup failed\n", __func__); | ||
1667 | return -1; | ||
1668 | } | ||
1669 | |||
1670 | while (true) { | ||
1671 | tok = strsep(&pos, ","); | ||
1672 | if (!tok) | ||
1673 | break; | ||
1674 | if (slab_sort_dimension__add(tok, sort_list) < 0) { | ||
1675 | error("Unknown slab --sort key: '%s'", tok); | ||
1676 | free(str); | ||
1677 | return -1; | ||
1678 | } | ||
1679 | } | ||
1680 | |||
1681 | free(str); | ||
1682 | return 0; | ||
1683 | } | ||
1684 | |||
1685 | static int setup_page_sorting(struct list_head *sort_list, const char *arg) | ||
974 | { | 1686 | { |
975 | char *tok; | 1687 | char *tok; |
976 | char *str = strdup(arg); | 1688 | char *str = strdup(arg); |
@@ -985,8 +1697,8 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) | |||
985 | tok = strsep(&pos, ","); | 1697 | tok = strsep(&pos, ","); |
986 | if (!tok) | 1698 | if (!tok) |
987 | break; | 1699 | break; |
988 | if (sort_dimension__add(tok, sort_list) < 0) { | 1700 | if (page_sort_dimension__add(tok, sort_list) < 0) { |
989 | error("Unknown --sort key: '%s'", tok); | 1701 | error("Unknown page --sort key: '%s'", tok); |
990 | free(str); | 1702 | free(str); |
991 | return -1; | 1703 | return -1; |
992 | } | 1704 | } |
@@ -1002,10 +1714,18 @@ static int parse_sort_opt(const struct option *opt __maybe_unused, | |||
1002 | if (!arg) | 1714 | if (!arg) |
1003 | return -1; | 1715 | return -1; |
1004 | 1716 | ||
1005 | if (caller_flag > alloc_flag) | 1717 | if (kmem_page > kmem_slab || |
1006 | return setup_sorting(&caller_sort, arg); | 1718 | (kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) { |
1007 | else | 1719 | if (caller_flag > alloc_flag) |
1008 | return setup_sorting(&alloc_sort, arg); | 1720 | return setup_page_sorting(&page_caller_sort, arg); |
1721 | else | ||
1722 | return setup_page_sorting(&page_alloc_sort, arg); | ||
1723 | } else { | ||
1724 | if (caller_flag > alloc_flag) | ||
1725 | return setup_slab_sorting(&slab_caller_sort, arg); | ||
1726 | else | ||
1727 | return setup_slab_sorting(&slab_alloc_sort, arg); | ||
1728 | } | ||
1009 | 1729 | ||
1010 | return 0; | 1730 | return 0; |
1011 | } | 1731 | } |
@@ -1084,7 +1804,7 @@ static int __cmd_record(int argc, const char **argv) | |||
1084 | if (kmem_slab) | 1804 | if (kmem_slab) |
1085 | rec_argc += ARRAY_SIZE(slab_events); | 1805 | rec_argc += ARRAY_SIZE(slab_events); |
1086 | if (kmem_page) | 1806 | if (kmem_page) |
1087 | rec_argc += ARRAY_SIZE(page_events); | 1807 | rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */ |
1088 | 1808 | ||
1089 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1809 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
1090 | 1810 | ||
@@ -1099,6 +1819,8 @@ static int __cmd_record(int argc, const char **argv) | |||
1099 | rec_argv[i] = strdup(slab_events[j]); | 1819 | rec_argv[i] = strdup(slab_events[j]); |
1100 | } | 1820 | } |
1101 | if (kmem_page) { | 1821 | if (kmem_page) { |
1822 | rec_argv[i++] = strdup("-g"); | ||
1823 | |||
1102 | for (j = 0; j < ARRAY_SIZE(page_events); j++, i++) | 1824 | for (j = 0; j < ARRAY_SIZE(page_events); j++, i++) |
1103 | rec_argv[i] = strdup(page_events[j]); | 1825 | rec_argv[i] = strdup(page_events[j]); |
1104 | } | 1826 | } |
@@ -1109,9 +1831,26 @@ static int __cmd_record(int argc, const char **argv) | |||
1109 | return cmd_record(i, rec_argv, NULL); | 1831 | return cmd_record(i, rec_argv, NULL); |
1110 | } | 1832 | } |
1111 | 1833 | ||
1834 | static int kmem_config(const char *var, const char *value, void *cb) | ||
1835 | { | ||
1836 | if (!strcmp(var, "kmem.default")) { | ||
1837 | if (!strcmp(value, "slab")) | ||
1838 | kmem_default = KMEM_SLAB; | ||
1839 | else if (!strcmp(value, "page")) | ||
1840 | kmem_default = KMEM_PAGE; | ||
1841 | else | ||
1842 | pr_err("invalid default value ('slab' or 'page' required): %s\n", | ||
1843 | value); | ||
1844 | return 0; | ||
1845 | } | ||
1846 | |||
1847 | return perf_default_config(var, value, cb); | ||
1848 | } | ||
1849 | |||
1112 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | 1850 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) |
1113 | { | 1851 | { |
1114 | const char * const default_sort_order = "frag,hit,bytes"; | 1852 | const char * const default_slab_sort = "frag,hit,bytes"; |
1853 | const char * const default_page_sort = "bytes,hit"; | ||
1115 | struct perf_data_file file = { | 1854 | struct perf_data_file file = { |
1116 | .mode = PERF_DATA_MODE_READ, | 1855 | .mode = PERF_DATA_MODE_READ, |
1117 | }; | 1856 | }; |
@@ -1124,8 +1863,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1124 | OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, | 1863 | OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, |
1125 | "show per-allocation statistics", parse_alloc_opt), | 1864 | "show per-allocation statistics", parse_alloc_opt), |
1126 | OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", | 1865 | OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", |
1127 | "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", | 1866 | "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, " |
1128 | parse_sort_opt), | 1867 | "page, order, migtype, gfp", parse_sort_opt), |
1129 | OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), | 1868 | OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), |
1130 | OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), | 1869 | OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), |
1131 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), | 1870 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
@@ -1133,6 +1872,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1133 | parse_slab_opt), | 1872 | parse_slab_opt), |
1134 | OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", | 1873 | OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator", |
1135 | parse_page_opt), | 1874 | parse_page_opt), |
1875 | OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"), | ||
1136 | OPT_END() | 1876 | OPT_END() |
1137 | }; | 1877 | }; |
1138 | const char *const kmem_subcommands[] = { "record", "stat", NULL }; | 1878 | const char *const kmem_subcommands[] = { "record", "stat", NULL }; |
@@ -1142,15 +1882,21 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1142 | }; | 1882 | }; |
1143 | struct perf_session *session; | 1883 | struct perf_session *session; |
1144 | int ret = -1; | 1884 | int ret = -1; |
1885 | const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n"; | ||
1145 | 1886 | ||
1887 | perf_config(kmem_config, NULL); | ||
1146 | argc = parse_options_subcommand(argc, argv, kmem_options, | 1888 | argc = parse_options_subcommand(argc, argv, kmem_options, |
1147 | kmem_subcommands, kmem_usage, 0); | 1889 | kmem_subcommands, kmem_usage, 0); |
1148 | 1890 | ||
1149 | if (!argc) | 1891 | if (!argc) |
1150 | usage_with_options(kmem_usage, kmem_options); | 1892 | usage_with_options(kmem_usage, kmem_options); |
1151 | 1893 | ||
1152 | if (kmem_slab == 0 && kmem_page == 0) | 1894 | if (kmem_slab == 0 && kmem_page == 0) { |
1153 | kmem_slab = 1; /* for backward compatibility */ | 1895 | if (kmem_default == KMEM_SLAB) |
1896 | kmem_slab = 1; | ||
1897 | else | ||
1898 | kmem_page = 1; | ||
1899 | } | ||
1154 | 1900 | ||
1155 | if (!strncmp(argv[0], "rec", 3)) { | 1901 | if (!strncmp(argv[0], "rec", 3)) { |
1156 | symbol__init(NULL); | 1902 | symbol__init(NULL); |
@@ -1159,19 +1905,30 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1159 | 1905 | ||
1160 | file.path = input_name; | 1906 | file.path = input_name; |
1161 | 1907 | ||
1162 | session = perf_session__new(&file, false, &perf_kmem); | 1908 | kmem_session = session = perf_session__new(&file, false, &perf_kmem); |
1163 | if (session == NULL) | 1909 | if (session == NULL) |
1164 | return -1; | 1910 | return -1; |
1165 | 1911 | ||
1912 | if (kmem_slab) { | ||
1913 | if (!perf_evlist__find_tracepoint_by_name(session->evlist, | ||
1914 | "kmem:kmalloc")) { | ||
1915 | pr_err(errmsg, "slab", "slab"); | ||
1916 | return -1; | ||
1917 | } | ||
1918 | } | ||
1919 | |||
1166 | if (kmem_page) { | 1920 | if (kmem_page) { |
1167 | struct perf_evsel *evsel = perf_evlist__first(session->evlist); | 1921 | struct perf_evsel *evsel; |
1168 | 1922 | ||
1169 | if (evsel == NULL || evsel->tp_format == NULL) { | 1923 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, |
1170 | pr_err("invalid event found.. aborting\n"); | 1924 | "kmem:mm_page_alloc"); |
1925 | if (evsel == NULL) { | ||
1926 | pr_err(errmsg, "page", "page"); | ||
1171 | return -1; | 1927 | return -1; |
1172 | } | 1928 | } |
1173 | 1929 | ||
1174 | kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent); | 1930 | kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent); |
1931 | symbol_conf.use_callchain = true; | ||
1175 | } | 1932 | } |
1176 | 1933 | ||
1177 | symbol__init(&session->header.env); | 1934 | symbol__init(&session->header.env); |
@@ -1182,11 +1939,21 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1182 | if (cpu__setup_cpunode_map()) | 1939 | if (cpu__setup_cpunode_map()) |
1183 | goto out_delete; | 1940 | goto out_delete; |
1184 | 1941 | ||
1185 | if (list_empty(&caller_sort)) | 1942 | if (list_empty(&slab_caller_sort)) |
1186 | setup_sorting(&caller_sort, default_sort_order); | 1943 | setup_slab_sorting(&slab_caller_sort, default_slab_sort); |
1187 | if (list_empty(&alloc_sort)) | 1944 | if (list_empty(&slab_alloc_sort)) |
1188 | setup_sorting(&alloc_sort, default_sort_order); | 1945 | setup_slab_sorting(&slab_alloc_sort, default_slab_sort); |
1189 | 1946 | if (list_empty(&page_caller_sort)) | |
1947 | setup_page_sorting(&page_caller_sort, default_page_sort); | ||
1948 | if (list_empty(&page_alloc_sort)) | ||
1949 | setup_page_sorting(&page_alloc_sort, default_page_sort); | ||
1950 | |||
1951 | if (kmem_page) { | ||
1952 | setup_page_sorting(&page_alloc_sort_input, | ||
1953 | "page,order,migtype,gfp"); | ||
1954 | setup_page_sorting(&page_caller_sort_input, | ||
1955 | "callsite,order,migtype,gfp"); | ||
1956 | } | ||
1190 | ret = __cmd_kmem(session); | 1957 | ret = __cmd_kmem(session); |
1191 | } else | 1958 | } else |
1192 | usage_with_options(kmem_usage, kmem_options); | 1959 | usage_with_options(kmem_usage, kmem_options); |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index f7b1af67e9f6..53d475b1422e 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -44,22 +44,19 @@ | |||
44 | 44 | ||
45 | #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" | 45 | #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" |
46 | #define DEFAULT_FUNC_FILTER "!_*" | 46 | #define DEFAULT_FUNC_FILTER "!_*" |
47 | #define DEFAULT_LIST_FILTER "*:*" | ||
47 | 48 | ||
48 | /* Session management structure */ | 49 | /* Session management structure */ |
49 | static struct { | 50 | static struct { |
51 | int command; /* Command short_name */ | ||
50 | bool list_events; | 52 | bool list_events; |
51 | bool force_add; | 53 | bool force_add; |
52 | bool show_lines; | ||
53 | bool show_vars; | ||
54 | bool show_ext_vars; | 54 | bool show_ext_vars; |
55 | bool show_funcs; | ||
56 | bool mod_events; | ||
57 | bool uprobes; | 55 | bool uprobes; |
58 | bool quiet; | 56 | bool quiet; |
59 | bool target_used; | 57 | bool target_used; |
60 | int nevents; | 58 | int nevents; |
61 | struct perf_probe_event events[MAX_PROBES]; | 59 | struct perf_probe_event events[MAX_PROBES]; |
62 | struct strlist *dellist; | ||
63 | struct line_range line_range; | 60 | struct line_range line_range; |
64 | char *target; | 61 | char *target; |
65 | int max_probe_points; | 62 | int max_probe_points; |
@@ -93,6 +90,28 @@ static int parse_probe_event(const char *str) | |||
93 | return ret; | 90 | return ret; |
94 | } | 91 | } |
95 | 92 | ||
93 | static int params_add_filter(const char *str) | ||
94 | { | ||
95 | const char *err = NULL; | ||
96 | int ret = 0; | ||
97 | |||
98 | pr_debug2("Add filter: %s\n", str); | ||
99 | if (!params.filter) { | ||
100 | params.filter = strfilter__new(str, &err); | ||
101 | if (!params.filter) | ||
102 | ret = err ? -EINVAL : -ENOMEM; | ||
103 | } else | ||
104 | ret = strfilter__or(params.filter, str, &err); | ||
105 | |||
106 | if (ret == -EINVAL) { | ||
107 | pr_err("Filter parse error at %td.\n", err - str + 1); | ||
108 | pr_err("Source: \"%s\"\n", str); | ||
109 | pr_err(" %*c\n", (int)(err - str + 1), '^'); | ||
110 | } | ||
111 | |||
112 | return ret; | ||
113 | } | ||
114 | |||
96 | static int set_target(const char *ptr) | 115 | static int set_target(const char *ptr) |
97 | { | 116 | { |
98 | int found = 0; | 117 | int found = 0; |
@@ -152,34 +171,11 @@ static int parse_probe_event_argv(int argc, const char **argv) | |||
152 | 171 | ||
153 | len += sprintf(&buf[len], "%s ", argv[i]); | 172 | len += sprintf(&buf[len], "%s ", argv[i]); |
154 | } | 173 | } |
155 | params.mod_events = true; | ||
156 | ret = parse_probe_event(buf); | 174 | ret = parse_probe_event(buf); |
157 | free(buf); | 175 | free(buf); |
158 | return ret; | 176 | return ret; |
159 | } | 177 | } |
160 | 178 | ||
161 | static int opt_add_probe_event(const struct option *opt __maybe_unused, | ||
162 | const char *str, int unset __maybe_unused) | ||
163 | { | ||
164 | if (str) { | ||
165 | params.mod_events = true; | ||
166 | return parse_probe_event(str); | ||
167 | } else | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int opt_del_probe_event(const struct option *opt __maybe_unused, | ||
172 | const char *str, int unset __maybe_unused) | ||
173 | { | ||
174 | if (str) { | ||
175 | params.mod_events = true; | ||
176 | if (!params.dellist) | ||
177 | params.dellist = strlist__new(true, NULL); | ||
178 | strlist__add(params.dellist, str); | ||
179 | } | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int opt_set_target(const struct option *opt, const char *str, | 179 | static int opt_set_target(const struct option *opt, const char *str, |
184 | int unset __maybe_unused) | 180 | int unset __maybe_unused) |
185 | { | 181 | { |
@@ -217,8 +213,10 @@ static int opt_set_target(const struct option *opt, const char *str, | |||
217 | return ret; | 213 | return ret; |
218 | } | 214 | } |
219 | 215 | ||
216 | /* Command option callbacks */ | ||
217 | |||
220 | #ifdef HAVE_DWARF_SUPPORT | 218 | #ifdef HAVE_DWARF_SUPPORT |
221 | static int opt_show_lines(const struct option *opt __maybe_unused, | 219 | static int opt_show_lines(const struct option *opt, |
222 | const char *str, int unset __maybe_unused) | 220 | const char *str, int unset __maybe_unused) |
223 | { | 221 | { |
224 | int ret = 0; | 222 | int ret = 0; |
@@ -226,19 +224,19 @@ static int opt_show_lines(const struct option *opt __maybe_unused, | |||
226 | if (!str) | 224 | if (!str) |
227 | return 0; | 225 | return 0; |
228 | 226 | ||
229 | if (params.show_lines) { | 227 | if (params.command == 'L') { |
230 | pr_warning("Warning: more than one --line options are" | 228 | pr_warning("Warning: more than one --line options are" |
231 | " detected. Only the first one is valid.\n"); | 229 | " detected. Only the first one is valid.\n"); |
232 | return 0; | 230 | return 0; |
233 | } | 231 | } |
234 | 232 | ||
235 | params.show_lines = true; | 233 | params.command = opt->short_name; |
236 | ret = parse_line_range_desc(str, ¶ms.line_range); | 234 | ret = parse_line_range_desc(str, ¶ms.line_range); |
237 | 235 | ||
238 | return ret; | 236 | return ret; |
239 | } | 237 | } |
240 | 238 | ||
241 | static int opt_show_vars(const struct option *opt __maybe_unused, | 239 | static int opt_show_vars(const struct option *opt, |
242 | const char *str, int unset __maybe_unused) | 240 | const char *str, int unset __maybe_unused) |
243 | { | 241 | { |
244 | struct perf_probe_event *pev = ¶ms.events[params.nevents]; | 242 | struct perf_probe_event *pev = ¶ms.events[params.nevents]; |
@@ -252,29 +250,39 @@ static int opt_show_vars(const struct option *opt __maybe_unused, | |||
252 | pr_err(" Error: '--vars' doesn't accept arguments.\n"); | 250 | pr_err(" Error: '--vars' doesn't accept arguments.\n"); |
253 | return -EINVAL; | 251 | return -EINVAL; |
254 | } | 252 | } |
255 | params.show_vars = true; | 253 | params.command = opt->short_name; |
256 | 254 | ||
257 | return ret; | 255 | return ret; |
258 | } | 256 | } |
259 | #endif | 257 | #endif |
258 | static int opt_add_probe_event(const struct option *opt, | ||
259 | const char *str, int unset __maybe_unused) | ||
260 | { | ||
261 | if (str) { | ||
262 | params.command = opt->short_name; | ||
263 | return parse_probe_event(str); | ||
264 | } | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static int opt_set_filter_with_command(const struct option *opt, | ||
270 | const char *str, int unset) | ||
271 | { | ||
272 | if (!unset) | ||
273 | params.command = opt->short_name; | ||
274 | |||
275 | if (str) | ||
276 | return params_add_filter(str); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
260 | 280 | ||
261 | static int opt_set_filter(const struct option *opt __maybe_unused, | 281 | static int opt_set_filter(const struct option *opt __maybe_unused, |
262 | const char *str, int unset __maybe_unused) | 282 | const char *str, int unset __maybe_unused) |
263 | { | 283 | { |
264 | const char *err; | 284 | if (str) |
265 | 285 | return params_add_filter(str); | |
266 | if (str) { | ||
267 | pr_debug2("Set filter: %s\n", str); | ||
268 | if (params.filter) | ||
269 | strfilter__delete(params.filter); | ||
270 | params.filter = strfilter__new(str, &err); | ||
271 | if (!params.filter) { | ||
272 | pr_err("Filter parse error at %td.\n", err - str + 1); | ||
273 | pr_err("Source: \"%s\"\n", str); | ||
274 | pr_err(" %*c\n", (int)(err - str + 1), '^'); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | } | ||
278 | 286 | ||
279 | return 0; | 287 | return 0; |
280 | } | 288 | } |
@@ -290,8 +298,6 @@ static void cleanup_params(void) | |||
290 | 298 | ||
291 | for (i = 0; i < params.nevents; i++) | 299 | for (i = 0; i < params.nevents; i++) |
292 | clear_perf_probe_event(params.events + i); | 300 | clear_perf_probe_event(params.events + i); |
293 | if (params.dellist) | ||
294 | strlist__delete(params.dellist); | ||
295 | line_range__clear(¶ms.line_range); | 301 | line_range__clear(¶ms.line_range); |
296 | free(params.target); | 302 | free(params.target); |
297 | if (params.filter) | 303 | if (params.filter) |
@@ -316,22 +322,24 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
316 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", | 322 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", |
317 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | 323 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", |
318 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", | 324 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", |
319 | "perf probe --list", | 325 | "perf probe --list [GROUP:]EVENT ...", |
320 | #ifdef HAVE_DWARF_SUPPORT | 326 | #ifdef HAVE_DWARF_SUPPORT |
321 | "perf probe [<options>] --line 'LINEDESC'", | 327 | "perf probe [<options>] --line 'LINEDESC'", |
322 | "perf probe [<options>] --vars 'PROBEPOINT'", | 328 | "perf probe [<options>] --vars 'PROBEPOINT'", |
323 | #endif | 329 | #endif |
330 | "perf probe [<options>] --funcs", | ||
324 | NULL | 331 | NULL |
325 | }; | 332 | }; |
326 | struct option options[] = { | 333 | struct option options[] = { |
327 | OPT_INCR('v', "verbose", &verbose, | 334 | OPT_INCR('v', "verbose", &verbose, |
328 | "be more verbose (show parsed arguments, etc)"), | 335 | "be more verbose (show parsed arguments, etc)"), |
329 | OPT_BOOLEAN('q', "quiet", ¶ms.quiet, | 336 | OPT_BOOLEAN('q', "quiet", ¶ms.quiet, |
330 | "be quiet (do not show any mesages)"), | 337 | "be quiet (do not show any mesages)"), |
331 | OPT_BOOLEAN('l', "list", ¶ms.list_events, | 338 | OPT_CALLBACK_DEFAULT('l', "list", NULL, "[GROUP:]EVENT", |
332 | "list up current probe events"), | 339 | "list up probe events", |
340 | opt_set_filter_with_command, DEFAULT_LIST_FILTER), | ||
333 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", | 341 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", |
334 | opt_del_probe_event), | 342 | opt_set_filter_with_command), |
335 | OPT_CALLBACK('a', "add", NULL, | 343 | OPT_CALLBACK('a', "add", NULL, |
336 | #ifdef HAVE_DWARF_SUPPORT | 344 | #ifdef HAVE_DWARF_SUPPORT |
337 | "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" | 345 | "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" |
@@ -378,8 +386,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
378 | OPT__DRY_RUN(&probe_event_dry_run), | 386 | OPT__DRY_RUN(&probe_event_dry_run), |
379 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 387 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
380 | "Set how many probe points can be found for a probe."), | 388 | "Set how many probe points can be found for a probe."), |
381 | OPT_BOOLEAN('F', "funcs", ¶ms.show_funcs, | 389 | OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]", |
382 | "Show potential probe-able functions."), | 390 | "Show potential probe-able functions.", |
391 | opt_set_filter_with_command, DEFAULT_FUNC_FILTER), | ||
383 | OPT_CALLBACK('\0', "filter", NULL, | 392 | OPT_CALLBACK('\0', "filter", NULL, |
384 | "[!]FILTER", "Set a filter (with --vars/funcs only)\n" | 393 | "[!]FILTER", "Set a filter (with --vars/funcs only)\n" |
385 | "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" | 394 | "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" |
@@ -402,6 +411,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
402 | set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); | 411 | set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); |
403 | set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); | 412 | set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); |
404 | #endif | 413 | #endif |
414 | set_option_flag(options, 'F', "funcs", PARSE_OPT_EXCLUSIVE); | ||
405 | 415 | ||
406 | argc = parse_options(argc, argv, options, probe_usage, | 416 | argc = parse_options(argc, argv, options, probe_usage, |
407 | PARSE_OPT_STOP_AT_NON_OPTION); | 417 | PARSE_OPT_STOP_AT_NON_OPTION); |
@@ -410,11 +420,16 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
410 | pr_warning(" Error: '-' is not supported.\n"); | 420 | pr_warning(" Error: '-' is not supported.\n"); |
411 | usage_with_options(probe_usage, options); | 421 | usage_with_options(probe_usage, options); |
412 | } | 422 | } |
423 | if (params.command && params.command != 'a') { | ||
424 | pr_warning(" Error: another command except --add is set.\n"); | ||
425 | usage_with_options(probe_usage, options); | ||
426 | } | ||
413 | ret = parse_probe_event_argv(argc, argv); | 427 | ret = parse_probe_event_argv(argc, argv); |
414 | if (ret < 0) { | 428 | if (ret < 0) { |
415 | pr_err_with_code(" Error: Command Parse Error.", ret); | 429 | pr_err_with_code(" Error: Command Parse Error.", ret); |
416 | return ret; | 430 | return ret; |
417 | } | 431 | } |
432 | params.command = 'a'; | ||
418 | } | 433 | } |
419 | 434 | ||
420 | if (params.quiet) { | 435 | if (params.quiet) { |
@@ -428,47 +443,35 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
428 | if (params.max_probe_points == 0) | 443 | if (params.max_probe_points == 0) |
429 | params.max_probe_points = MAX_PROBES; | 444 | params.max_probe_points = MAX_PROBES; |
430 | 445 | ||
431 | if ((!params.nevents && !params.dellist && !params.list_events && | ||
432 | !params.show_lines && !params.show_funcs)) | ||
433 | usage_with_options(probe_usage, options); | ||
434 | |||
435 | /* | 446 | /* |
436 | * Only consider the user's kernel image path if given. | 447 | * Only consider the user's kernel image path if given. |
437 | */ | 448 | */ |
438 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 449 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
439 | 450 | ||
440 | if (params.list_events) { | 451 | switch (params.command) { |
452 | case 'l': | ||
441 | if (params.uprobes) { | 453 | if (params.uprobes) { |
442 | pr_warning(" Error: Don't use --list with --exec.\n"); | 454 | pr_warning(" Error: Don't use --list with --exec.\n"); |
443 | usage_with_options(probe_usage, options); | 455 | usage_with_options(probe_usage, options); |
444 | } | 456 | } |
445 | ret = show_perf_probe_events(); | 457 | ret = show_perf_probe_events(params.filter); |
446 | if (ret < 0) | 458 | if (ret < 0) |
447 | pr_err_with_code(" Error: Failed to show event list.", ret); | 459 | pr_err_with_code(" Error: Failed to show event list.", ret); |
448 | return ret; | 460 | return ret; |
449 | } | 461 | case 'F': |
450 | if (params.show_funcs) { | ||
451 | if (!params.filter) | ||
452 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, | ||
453 | NULL); | ||
454 | ret = show_available_funcs(params.target, params.filter, | 462 | ret = show_available_funcs(params.target, params.filter, |
455 | params.uprobes); | 463 | params.uprobes); |
456 | strfilter__delete(params.filter); | ||
457 | params.filter = NULL; | ||
458 | if (ret < 0) | 464 | if (ret < 0) |
459 | pr_err_with_code(" Error: Failed to show functions.", ret); | 465 | pr_err_with_code(" Error: Failed to show functions.", ret); |
460 | return ret; | 466 | return ret; |
461 | } | ||
462 | |||
463 | #ifdef HAVE_DWARF_SUPPORT | 467 | #ifdef HAVE_DWARF_SUPPORT |
464 | if (params.show_lines) { | 468 | case 'L': |
465 | ret = show_line_range(¶ms.line_range, params.target, | 469 | ret = show_line_range(¶ms.line_range, params.target, |
466 | params.uprobes); | 470 | params.uprobes); |
467 | if (ret < 0) | 471 | if (ret < 0) |
468 | pr_err_with_code(" Error: Failed to show lines.", ret); | 472 | pr_err_with_code(" Error: Failed to show lines.", ret); |
469 | return ret; | 473 | return ret; |
470 | } | 474 | case 'V': |
471 | if (params.show_vars) { | ||
472 | if (!params.filter) | 475 | if (!params.filter) |
473 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, | 476 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, |
474 | NULL); | 477 | NULL); |
@@ -478,23 +481,18 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
478 | params.target, | 481 | params.target, |
479 | params.filter, | 482 | params.filter, |
480 | params.show_ext_vars); | 483 | params.show_ext_vars); |
481 | strfilter__delete(params.filter); | ||
482 | params.filter = NULL; | ||
483 | if (ret < 0) | 484 | if (ret < 0) |
484 | pr_err_with_code(" Error: Failed to show vars.", ret); | 485 | pr_err_with_code(" Error: Failed to show vars.", ret); |
485 | return ret; | 486 | return ret; |
486 | } | ||
487 | #endif | 487 | #endif |
488 | 488 | case 'd': | |
489 | if (params.dellist) { | 489 | ret = del_perf_probe_events(params.filter); |
490 | ret = del_perf_probe_events(params.dellist); | ||
491 | if (ret < 0) { | 490 | if (ret < 0) { |
492 | pr_err_with_code(" Error: Failed to delete events.", ret); | 491 | pr_err_with_code(" Error: Failed to delete events.", ret); |
493 | return ret; | 492 | return ret; |
494 | } | 493 | } |
495 | } | 494 | break; |
496 | 495 | case 'a': | |
497 | if (params.nevents) { | ||
498 | /* Ensure the last given target is used */ | 496 | /* Ensure the last given target is used */ |
499 | if (params.target && !params.target_used) { | 497 | if (params.target && !params.target_used) { |
500 | pr_warning(" Error: -x/-m must follow the probe definitions.\n"); | 498 | pr_warning(" Error: -x/-m must follow the probe definitions.\n"); |
@@ -508,6 +506,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
508 | pr_err_with_code(" Error: Failed to add events.", ret); | 506 | pr_err_with_code(" Error: Failed to add events.", ret); |
509 | return ret; | 507 | return ret; |
510 | } | 508 | } |
509 | break; | ||
510 | default: | ||
511 | usage_with_options(probe_usage, options); | ||
511 | } | 512 | } |
512 | return 0; | 513 | return 0; |
513 | } | 514 | } |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index c3efdfb630b5..5dfe91395617 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "util/cpumap.h" | 27 | #include "util/cpumap.h" |
28 | #include "util/thread_map.h" | 28 | #include "util/thread_map.h" |
29 | #include "util/data.h" | 29 | #include "util/data.h" |
30 | #include "util/auxtrace.h" | ||
30 | 31 | ||
31 | #include <unistd.h> | 32 | #include <unistd.h> |
32 | #include <sched.h> | 33 | #include <sched.h> |
@@ -38,6 +39,7 @@ struct record { | |||
38 | struct record_opts opts; | 39 | struct record_opts opts; |
39 | u64 bytes_written; | 40 | u64 bytes_written; |
40 | struct perf_data_file file; | 41 | struct perf_data_file file; |
42 | struct auxtrace_record *itr; | ||
41 | struct perf_evlist *evlist; | 43 | struct perf_evlist *evlist; |
42 | struct perf_session *session; | 44 | struct perf_session *session; |
43 | const char *progname; | 45 | const char *progname; |
@@ -110,9 +112,12 @@ out: | |||
110 | return rc; | 112 | return rc; |
111 | } | 113 | } |
112 | 114 | ||
113 | static volatile int done = 0; | 115 | static volatile int done; |
114 | static volatile int signr = -1; | 116 | static volatile int signr = -1; |
115 | static volatile int child_finished = 0; | 117 | static volatile int child_finished; |
118 | static volatile int auxtrace_snapshot_enabled; | ||
119 | static volatile int auxtrace_snapshot_err; | ||
120 | static volatile int auxtrace_record__snapshot_started; | ||
116 | 121 | ||
117 | static void sig_handler(int sig) | 122 | static void sig_handler(int sig) |
118 | { | 123 | { |
@@ -133,6 +138,133 @@ static void record__sig_exit(void) | |||
133 | raise(signr); | 138 | raise(signr); |
134 | } | 139 | } |
135 | 140 | ||
141 | #ifdef HAVE_AUXTRACE_SUPPORT | ||
142 | |||
143 | static int record__process_auxtrace(struct perf_tool *tool, | ||
144 | union perf_event *event, void *data1, | ||
145 | size_t len1, void *data2, size_t len2) | ||
146 | { | ||
147 | struct record *rec = container_of(tool, struct record, tool); | ||
148 | struct perf_data_file *file = &rec->file; | ||
149 | size_t padding; | ||
150 | u8 pad[8] = {0}; | ||
151 | |||
152 | if (!perf_data_file__is_pipe(file)) { | ||
153 | off_t file_offset; | ||
154 | int fd = perf_data_file__fd(file); | ||
155 | int err; | ||
156 | |||
157 | file_offset = lseek(fd, 0, SEEK_CUR); | ||
158 | if (file_offset == -1) | ||
159 | return -1; | ||
160 | err = auxtrace_index__auxtrace_event(&rec->session->auxtrace_index, | ||
161 | event, file_offset); | ||
162 | if (err) | ||
163 | return err; | ||
164 | } | ||
165 | |||
166 | /* event.auxtrace.size includes padding, see __auxtrace_mmap__read() */ | ||
167 | padding = (len1 + len2) & 7; | ||
168 | if (padding) | ||
169 | padding = 8 - padding; | ||
170 | |||
171 | record__write(rec, event, event->header.size); | ||
172 | record__write(rec, data1, len1); | ||
173 | if (len2) | ||
174 | record__write(rec, data2, len2); | ||
175 | record__write(rec, &pad, padding); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int record__auxtrace_mmap_read(struct record *rec, | ||
181 | struct auxtrace_mmap *mm) | ||
182 | { | ||
183 | int ret; | ||
184 | |||
185 | ret = auxtrace_mmap__read(mm, rec->itr, &rec->tool, | ||
186 | record__process_auxtrace); | ||
187 | if (ret < 0) | ||
188 | return ret; | ||
189 | |||
190 | if (ret) | ||
191 | rec->samples++; | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int record__auxtrace_mmap_read_snapshot(struct record *rec, | ||
197 | struct auxtrace_mmap *mm) | ||
198 | { | ||
199 | int ret; | ||
200 | |||
201 | ret = auxtrace_mmap__read_snapshot(mm, rec->itr, &rec->tool, | ||
202 | record__process_auxtrace, | ||
203 | rec->opts.auxtrace_snapshot_size); | ||
204 | if (ret < 0) | ||
205 | return ret; | ||
206 | |||
207 | if (ret) | ||
208 | rec->samples++; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int record__auxtrace_read_snapshot_all(struct record *rec) | ||
214 | { | ||
215 | int i; | ||
216 | int rc = 0; | ||
217 | |||
218 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | ||
219 | struct auxtrace_mmap *mm = | ||
220 | &rec->evlist->mmap[i].auxtrace_mmap; | ||
221 | |||
222 | if (!mm->base) | ||
223 | continue; | ||
224 | |||
225 | if (record__auxtrace_mmap_read_snapshot(rec, mm) != 0) { | ||
226 | rc = -1; | ||
227 | goto out; | ||
228 | } | ||
229 | } | ||
230 | out: | ||
231 | return rc; | ||
232 | } | ||
233 | |||
234 | static void record__read_auxtrace_snapshot(struct record *rec) | ||
235 | { | ||
236 | pr_debug("Recording AUX area tracing snapshot\n"); | ||
237 | if (record__auxtrace_read_snapshot_all(rec) < 0) { | ||
238 | auxtrace_snapshot_err = -1; | ||
239 | } else { | ||
240 | auxtrace_snapshot_err = auxtrace_record__snapshot_finish(rec->itr); | ||
241 | if (!auxtrace_snapshot_err) | ||
242 | auxtrace_snapshot_enabled = 1; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | #else | ||
247 | |||
248 | static inline | ||
249 | int record__auxtrace_mmap_read(struct record *rec __maybe_unused, | ||
250 | struct auxtrace_mmap *mm __maybe_unused) | ||
251 | { | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static inline | ||
256 | void record__read_auxtrace_snapshot(struct record *rec __maybe_unused) | ||
257 | { | ||
258 | } | ||
259 | |||
260 | static inline | ||
261 | int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused) | ||
262 | { | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | #endif | ||
267 | |||
136 | static int record__open(struct record *rec) | 268 | static int record__open(struct record *rec) |
137 | { | 269 | { |
138 | char msg[512]; | 270 | char msg[512]; |
@@ -169,13 +301,16 @@ try_again: | |||
169 | goto out; | 301 | goto out; |
170 | } | 302 | } |
171 | 303 | ||
172 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 304 | if (perf_evlist__mmap_ex(evlist, opts->mmap_pages, false, |
305 | opts->auxtrace_mmap_pages, | ||
306 | opts->auxtrace_snapshot_mode) < 0) { | ||
173 | if (errno == EPERM) { | 307 | if (errno == EPERM) { |
174 | pr_err("Permission error mapping pages.\n" | 308 | pr_err("Permission error mapping pages.\n" |
175 | "Consider increasing " | 309 | "Consider increasing " |
176 | "/proc/sys/kernel/perf_event_mlock_kb,\n" | 310 | "/proc/sys/kernel/perf_event_mlock_kb,\n" |
177 | "or try again with a smaller value of -m/--mmap_pages.\n" | 311 | "or try again with a smaller value of -m/--mmap_pages.\n" |
178 | "(current value: %u)\n", opts->mmap_pages); | 312 | "(current value: %u,%u)\n", |
313 | opts->mmap_pages, opts->auxtrace_mmap_pages); | ||
179 | rc = -errno; | 314 | rc = -errno; |
180 | } else { | 315 | } else { |
181 | pr_err("failed to mmap with %d (%s)\n", errno, | 316 | pr_err("failed to mmap with %d (%s)\n", errno, |
@@ -270,12 +405,20 @@ static int record__mmap_read_all(struct record *rec) | |||
270 | int rc = 0; | 405 | int rc = 0; |
271 | 406 | ||
272 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 407 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
408 | struct auxtrace_mmap *mm = &rec->evlist->mmap[i].auxtrace_mmap; | ||
409 | |||
273 | if (rec->evlist->mmap[i].base) { | 410 | if (rec->evlist->mmap[i].base) { |
274 | if (record__mmap_read(rec, i) != 0) { | 411 | if (record__mmap_read(rec, i) != 0) { |
275 | rc = -1; | 412 | rc = -1; |
276 | goto out; | 413 | goto out; |
277 | } | 414 | } |
278 | } | 415 | } |
416 | |||
417 | if (mm->base && !rec->opts.auxtrace_snapshot_mode && | ||
418 | record__auxtrace_mmap_read(rec, mm) != 0) { | ||
419 | rc = -1; | ||
420 | goto out; | ||
421 | } | ||
279 | } | 422 | } |
280 | 423 | ||
281 | /* | 424 | /* |
@@ -305,6 +448,9 @@ static void record__init_features(struct record *rec) | |||
305 | 448 | ||
306 | if (!rec->opts.branch_stack) | 449 | if (!rec->opts.branch_stack) |
307 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); | 450 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); |
451 | |||
452 | if (!rec->opts.full_auxtrace) | ||
453 | perf_header__clear_feat(&session->header, HEADER_AUXTRACE); | ||
308 | } | 454 | } |
309 | 455 | ||
310 | static volatile int workload_exec_errno; | 456 | static volatile int workload_exec_errno; |
@@ -323,6 +469,8 @@ static void workload_exec_failed_signal(int signo __maybe_unused, | |||
323 | child_finished = 1; | 469 | child_finished = 1; |
324 | } | 470 | } |
325 | 471 | ||
472 | static void snapshot_sig_handler(int sig); | ||
473 | |||
326 | static int __cmd_record(struct record *rec, int argc, const char **argv) | 474 | static int __cmd_record(struct record *rec, int argc, const char **argv) |
327 | { | 475 | { |
328 | int err; | 476 | int err; |
@@ -343,6 +491,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
343 | signal(SIGCHLD, sig_handler); | 491 | signal(SIGCHLD, sig_handler); |
344 | signal(SIGINT, sig_handler); | 492 | signal(SIGINT, sig_handler); |
345 | signal(SIGTERM, sig_handler); | 493 | signal(SIGTERM, sig_handler); |
494 | if (rec->opts.auxtrace_snapshot_mode) | ||
495 | signal(SIGUSR2, snapshot_sig_handler); | ||
496 | else | ||
497 | signal(SIGUSR2, SIG_IGN); | ||
346 | 498 | ||
347 | session = perf_session__new(file, false, tool); | 499 | session = perf_session__new(file, false, tool); |
348 | if (session == NULL) { | 500 | if (session == NULL) { |
@@ -421,6 +573,13 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
421 | } | 573 | } |
422 | } | 574 | } |
423 | 575 | ||
576 | if (rec->opts.full_auxtrace) { | ||
577 | err = perf_event__synthesize_auxtrace_info(rec->itr, tool, | ||
578 | session, process_synthesized_event); | ||
579 | if (err) | ||
580 | goto out_delete_session; | ||
581 | } | ||
582 | |||
424 | err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, | 583 | err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, |
425 | machine); | 584 | machine); |
426 | if (err < 0) | 585 | if (err < 0) |
@@ -475,14 +634,27 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
475 | perf_evlist__enable(rec->evlist); | 634 | perf_evlist__enable(rec->evlist); |
476 | } | 635 | } |
477 | 636 | ||
637 | auxtrace_snapshot_enabled = 1; | ||
478 | for (;;) { | 638 | for (;;) { |
479 | int hits = rec->samples; | 639 | int hits = rec->samples; |
480 | 640 | ||
481 | if (record__mmap_read_all(rec) < 0) { | 641 | if (record__mmap_read_all(rec) < 0) { |
642 | auxtrace_snapshot_enabled = 0; | ||
482 | err = -1; | 643 | err = -1; |
483 | goto out_child; | 644 | goto out_child; |
484 | } | 645 | } |
485 | 646 | ||
647 | if (auxtrace_record__snapshot_started) { | ||
648 | auxtrace_record__snapshot_started = 0; | ||
649 | if (!auxtrace_snapshot_err) | ||
650 | record__read_auxtrace_snapshot(rec); | ||
651 | if (auxtrace_snapshot_err) { | ||
652 | pr_err("AUX area tracing snapshot failed\n"); | ||
653 | err = -1; | ||
654 | goto out_child; | ||
655 | } | ||
656 | } | ||
657 | |||
486 | if (hits == rec->samples) { | 658 | if (hits == rec->samples) { |
487 | if (done || draining) | 659 | if (done || draining) |
488 | break; | 660 | break; |
@@ -505,10 +677,12 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
505 | * disable events in this case. | 677 | * disable events in this case. |
506 | */ | 678 | */ |
507 | if (done && !disabled && !target__none(&opts->target)) { | 679 | if (done && !disabled && !target__none(&opts->target)) { |
680 | auxtrace_snapshot_enabled = 0; | ||
508 | perf_evlist__disable(rec->evlist); | 681 | perf_evlist__disable(rec->evlist); |
509 | disabled = true; | 682 | disabled = true; |
510 | } | 683 | } |
511 | } | 684 | } |
685 | auxtrace_snapshot_enabled = 0; | ||
512 | 686 | ||
513 | if (forks && workload_exec_errno) { | 687 | if (forks && workload_exec_errno) { |
514 | char msg[STRERR_BUFSIZE]; | 688 | char msg[STRERR_BUFSIZE]; |
@@ -545,15 +719,23 @@ out_child: | |||
545 | if (!err && !file->is_pipe) { | 719 | if (!err && !file->is_pipe) { |
546 | rec->session->header.data_size += rec->bytes_written; | 720 | rec->session->header.data_size += rec->bytes_written; |
547 | 721 | ||
548 | if (!rec->no_buildid) | 722 | if (!rec->no_buildid) { |
549 | process_buildids(rec); | 723 | process_buildids(rec); |
724 | /* | ||
725 | * We take all buildids when the file contains | ||
726 | * AUX area tracing data because we do not decode the | ||
727 | * trace because it would take too long. | ||
728 | */ | ||
729 | if (rec->opts.full_auxtrace) | ||
730 | dsos__hit_all(rec->session); | ||
731 | } | ||
550 | perf_session__write_header(rec->session, rec->evlist, fd, true); | 732 | perf_session__write_header(rec->session, rec->evlist, fd, true); |
551 | } | 733 | } |
552 | 734 | ||
553 | if (!err && !quiet) { | 735 | if (!err && !quiet) { |
554 | char samples[128]; | 736 | char samples[128]; |
555 | 737 | ||
556 | if (rec->samples) | 738 | if (rec->samples && !rec->opts.full_auxtrace) |
557 | scnprintf(samples, sizeof(samples), | 739 | scnprintf(samples, sizeof(samples), |
558 | " (%" PRIu64 " samples)", rec->samples); | 740 | " (%" PRIu64 " samples)", rec->samples); |
559 | else | 741 | else |
@@ -795,6 +977,49 @@ static int parse_clockid(const struct option *opt, const char *str, int unset) | |||
795 | return -1; | 977 | return -1; |
796 | } | 978 | } |
797 | 979 | ||
980 | static int record__parse_mmap_pages(const struct option *opt, | ||
981 | const char *str, | ||
982 | int unset __maybe_unused) | ||
983 | { | ||
984 | struct record_opts *opts = opt->value; | ||
985 | char *s, *p; | ||
986 | unsigned int mmap_pages; | ||
987 | int ret; | ||
988 | |||
989 | if (!str) | ||
990 | return -EINVAL; | ||
991 | |||
992 | s = strdup(str); | ||
993 | if (!s) | ||
994 | return -ENOMEM; | ||
995 | |||
996 | p = strchr(s, ','); | ||
997 | if (p) | ||
998 | *p = '\0'; | ||
999 | |||
1000 | if (*s) { | ||
1001 | ret = __perf_evlist__parse_mmap_pages(&mmap_pages, s); | ||
1002 | if (ret) | ||
1003 | goto out_free; | ||
1004 | opts->mmap_pages = mmap_pages; | ||
1005 | } | ||
1006 | |||
1007 | if (!p) { | ||
1008 | ret = 0; | ||
1009 | goto out_free; | ||
1010 | } | ||
1011 | |||
1012 | ret = __perf_evlist__parse_mmap_pages(&mmap_pages, p + 1); | ||
1013 | if (ret) | ||
1014 | goto out_free; | ||
1015 | |||
1016 | opts->auxtrace_mmap_pages = mmap_pages; | ||
1017 | |||
1018 | out_free: | ||
1019 | free(s); | ||
1020 | return ret; | ||
1021 | } | ||
1022 | |||
798 | static const char * const __record_usage[] = { | 1023 | static const char * const __record_usage[] = { |
799 | "perf record [<options>] [<command>]", | 1024 | "perf record [<options>] [<command>]", |
800 | "perf record [<options>] -- <command> [<options>]", | 1025 | "perf record [<options>] -- <command> [<options>]", |
@@ -875,9 +1100,9 @@ struct option __record_options[] = { | |||
875 | &record.opts.no_inherit_set, | 1100 | &record.opts.no_inherit_set, |
876 | "child tasks do not inherit counters"), | 1101 | "child tasks do not inherit counters"), |
877 | OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), | 1102 | OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), |
878 | OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages", | 1103 | OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]", |
879 | "number of mmap data pages", | 1104 | "number of mmap data pages and AUX area tracing mmap pages", |
880 | perf_evlist__parse_mmap_pages), | 1105 | record__parse_mmap_pages), |
881 | OPT_BOOLEAN(0, "group", &record.opts.group, | 1106 | OPT_BOOLEAN(0, "group", &record.opts.group, |
882 | "put the counters into a counter group"), | 1107 | "put the counters into a counter group"), |
883 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, | 1108 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, |
@@ -929,6 +1154,8 @@ struct option __record_options[] = { | |||
929 | OPT_CALLBACK('k', "clockid", &record.opts, | 1154 | OPT_CALLBACK('k', "clockid", &record.opts, |
930 | "clockid", "clockid to use for events, see clock_gettime()", | 1155 | "clockid", "clockid to use for events, see clock_gettime()", |
931 | parse_clockid), | 1156 | parse_clockid), |
1157 | OPT_STRING_OPTARG('S', "snapshot", &record.opts.auxtrace_snapshot_opts, | ||
1158 | "opts", "AUX area tracing Snapshot Mode", ""), | ||
932 | OPT_END() | 1159 | OPT_END() |
933 | }; | 1160 | }; |
934 | 1161 | ||
@@ -936,7 +1163,7 @@ struct option *record_options = __record_options; | |||
936 | 1163 | ||
937 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | 1164 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) |
938 | { | 1165 | { |
939 | int err = -ENOMEM; | 1166 | int err; |
940 | struct record *rec = &record; | 1167 | struct record *rec = &record; |
941 | char errbuf[BUFSIZ]; | 1168 | char errbuf[BUFSIZ]; |
942 | 1169 | ||
@@ -957,6 +1184,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
957 | usage_with_options(record_usage, record_options); | 1184 | usage_with_options(record_usage, record_options); |
958 | } | 1185 | } |
959 | 1186 | ||
1187 | if (!rec->itr) { | ||
1188 | rec->itr = auxtrace_record__init(rec->evlist, &err); | ||
1189 | if (err) | ||
1190 | return err; | ||
1191 | } | ||
1192 | |||
1193 | err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, | ||
1194 | rec->opts.auxtrace_snapshot_opts); | ||
1195 | if (err) | ||
1196 | return err; | ||
1197 | |||
1198 | err = -ENOMEM; | ||
1199 | |||
960 | symbol__init(NULL); | 1200 | symbol__init(NULL); |
961 | 1201 | ||
962 | if (symbol_conf.kptr_restrict) | 1202 | if (symbol_conf.kptr_restrict) |
@@ -1002,6 +1242,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1002 | if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0) | 1242 | if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0) |
1003 | usage_with_options(record_usage, record_options); | 1243 | usage_with_options(record_usage, record_options); |
1004 | 1244 | ||
1245 | err = auxtrace_record__options(rec->itr, rec->evlist, &rec->opts); | ||
1246 | if (err) | ||
1247 | goto out_symbol_exit; | ||
1248 | |||
1005 | if (record_opts__config(&rec->opts)) { | 1249 | if (record_opts__config(&rec->opts)) { |
1006 | err = -EINVAL; | 1250 | err = -EINVAL; |
1007 | goto out_symbol_exit; | 1251 | goto out_symbol_exit; |
@@ -1011,5 +1255,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1011 | out_symbol_exit: | 1255 | out_symbol_exit: |
1012 | perf_evlist__delete(rec->evlist); | 1256 | perf_evlist__delete(rec->evlist); |
1013 | symbol__exit(); | 1257 | symbol__exit(); |
1258 | auxtrace_record__free(rec->itr); | ||
1014 | return err; | 1259 | return err; |
1015 | } | 1260 | } |
1261 | |||
1262 | static void snapshot_sig_handler(int sig __maybe_unused) | ||
1263 | { | ||
1264 | if (!auxtrace_snapshot_enabled) | ||
1265 | return; | ||
1266 | auxtrace_snapshot_enabled = 0; | ||
1267 | auxtrace_snapshot_err = auxtrace_record__snapshot_start(record.itr); | ||
1268 | auxtrace_record__snapshot_started = 1; | ||
1269 | } | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 476cdf7afcca..18cb0ff39b4e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -36,6 +36,8 @@ | |||
36 | #include "util/data.h" | 36 | #include "util/data.h" |
37 | #include "arch/common.h" | 37 | #include "arch/common.h" |
38 | 38 | ||
39 | #include "util/auxtrace.h" | ||
40 | |||
39 | #include <dlfcn.h> | 41 | #include <dlfcn.h> |
40 | #include <linux/bitmap.h> | 42 | #include <linux/bitmap.h> |
41 | 43 | ||
@@ -585,6 +587,7 @@ parse_percent_limit(const struct option *opt, const char *str, | |||
585 | int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | 587 | int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) |
586 | { | 588 | { |
587 | struct perf_session *session; | 589 | struct perf_session *session; |
590 | struct itrace_synth_opts itrace_synth_opts = { .set = 0, }; | ||
588 | struct stat st; | 591 | struct stat st; |
589 | bool has_br_stack = false; | 592 | bool has_br_stack = false; |
590 | int branch_mode = -1; | 593 | int branch_mode = -1; |
@@ -607,6 +610,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
607 | .attr = perf_event__process_attr, | 610 | .attr = perf_event__process_attr, |
608 | .tracing_data = perf_event__process_tracing_data, | 611 | .tracing_data = perf_event__process_tracing_data, |
609 | .build_id = perf_event__process_build_id, | 612 | .build_id = perf_event__process_build_id, |
613 | .id_index = perf_event__process_id_index, | ||
614 | .auxtrace_info = perf_event__process_auxtrace_info, | ||
615 | .auxtrace = perf_event__process_auxtrace, | ||
610 | .ordered_events = true, | 616 | .ordered_events = true, |
611 | .ordering_requires_timestamps = true, | 617 | .ordering_requires_timestamps = true, |
612 | }, | 618 | }, |
@@ -717,6 +723,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
717 | "Don't show entries under that percent", parse_percent_limit), | 723 | "Don't show entries under that percent", parse_percent_limit), |
718 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 724 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
719 | "how to display percentage of filtered entries", parse_filter_percentage), | 725 | "how to display percentage of filtered entries", parse_filter_percentage), |
726 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", | ||
727 | "Instruction Tracing options", | ||
728 | itrace_parse_synth_opts), | ||
720 | OPT_END() | 729 | OPT_END() |
721 | }; | 730 | }; |
722 | struct perf_data_file file = { | 731 | struct perf_data_file file = { |
@@ -761,6 +770,8 @@ repeat: | |||
761 | report.queue_size); | 770 | report.queue_size); |
762 | } | 771 | } |
763 | 772 | ||
773 | session->itrace_synth_opts = &itrace_synth_opts; | ||
774 | |||
764 | report.session = session; | 775 | report.session = session; |
765 | 776 | ||
766 | has_br_stack = perf_header__has_feat(&session->header, | 777 | has_br_stack = perf_header__has_feat(&session->header, |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 58f10b8e6ff2..6805098e3751 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include "util/evsel.h" | 16 | #include "util/evsel.h" |
17 | #include "util/sort.h" | 17 | #include "util/sort.h" |
18 | #include "util/data.h" | 18 | #include "util/data.h" |
19 | #include "util/auxtrace.h" | ||
19 | #include <linux/bitmap.h> | 20 | #include <linux/bitmap.h> |
20 | 21 | ||
21 | static char const *script_name; | 22 | static char const *script_name; |
@@ -26,6 +27,7 @@ static u64 nr_unordered; | |||
26 | static bool no_callchain; | 27 | static bool no_callchain; |
27 | static bool latency_format; | 28 | static bool latency_format; |
28 | static bool system_wide; | 29 | static bool system_wide; |
30 | static bool print_flags; | ||
29 | static const char *cpu_list; | 31 | static const char *cpu_list; |
30 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 32 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
31 | 33 | ||
@@ -146,9 +148,10 @@ static const char *output_field2str(enum perf_output_field field) | |||
146 | 148 | ||
147 | #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) | 149 | #define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) |
148 | 150 | ||
149 | static int perf_evsel__check_stype(struct perf_evsel *evsel, | 151 | static int perf_evsel__do_check_stype(struct perf_evsel *evsel, |
150 | u64 sample_type, const char *sample_msg, | 152 | u64 sample_type, const char *sample_msg, |
151 | enum perf_output_field field) | 153 | enum perf_output_field field, |
154 | bool allow_user_set) | ||
152 | { | 155 | { |
153 | struct perf_event_attr *attr = &evsel->attr; | 156 | struct perf_event_attr *attr = &evsel->attr; |
154 | int type = attr->type; | 157 | int type = attr->type; |
@@ -158,6 +161,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel, | |||
158 | return 0; | 161 | return 0; |
159 | 162 | ||
160 | if (output[type].user_set) { | 163 | if (output[type].user_set) { |
164 | if (allow_user_set) | ||
165 | return 0; | ||
161 | evname = perf_evsel__name(evsel); | 166 | evname = perf_evsel__name(evsel); |
162 | pr_err("Samples for '%s' event do not have %s attribute set. " | 167 | pr_err("Samples for '%s' event do not have %s attribute set. " |
163 | "Cannot print '%s' field.\n", | 168 | "Cannot print '%s' field.\n", |
@@ -175,10 +180,22 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel, | |||
175 | return 0; | 180 | return 0; |
176 | } | 181 | } |
177 | 182 | ||
183 | static int perf_evsel__check_stype(struct perf_evsel *evsel, | ||
184 | u64 sample_type, const char *sample_msg, | ||
185 | enum perf_output_field field) | ||
186 | { | ||
187 | return perf_evsel__do_check_stype(evsel, sample_type, sample_msg, field, | ||
188 | false); | ||
189 | } | ||
190 | |||
178 | static int perf_evsel__check_attr(struct perf_evsel *evsel, | 191 | static int perf_evsel__check_attr(struct perf_evsel *evsel, |
179 | struct perf_session *session) | 192 | struct perf_session *session) |
180 | { | 193 | { |
181 | struct perf_event_attr *attr = &evsel->attr; | 194 | struct perf_event_attr *attr = &evsel->attr; |
195 | bool allow_user_set; | ||
196 | |||
197 | allow_user_set = perf_header__has_feat(&session->header, | ||
198 | HEADER_AUXTRACE); | ||
182 | 199 | ||
183 | if (PRINT_FIELD(TRACE) && | 200 | if (PRINT_FIELD(TRACE) && |
184 | !perf_session__has_traces(session, "record -R")) | 201 | !perf_session__has_traces(session, "record -R")) |
@@ -191,8 +208,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
191 | } | 208 | } |
192 | 209 | ||
193 | if (PRINT_FIELD(ADDR) && | 210 | if (PRINT_FIELD(ADDR) && |
194 | perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", | 211 | perf_evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", |
195 | PERF_OUTPUT_ADDR)) | 212 | PERF_OUTPUT_ADDR, allow_user_set)) |
196 | return -EINVAL; | 213 | return -EINVAL; |
197 | 214 | ||
198 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | 215 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { |
@@ -229,8 +246,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
229 | return -EINVAL; | 246 | return -EINVAL; |
230 | 247 | ||
231 | if (PRINT_FIELD(CPU) && | 248 | if (PRINT_FIELD(CPU) && |
232 | perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU", | 249 | perf_evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU", |
233 | PERF_OUTPUT_CPU)) | 250 | PERF_OUTPUT_CPU, allow_user_set)) |
234 | return -EINVAL; | 251 | return -EINVAL; |
235 | 252 | ||
236 | if (PRINT_FIELD(PERIOD) && | 253 | if (PRINT_FIELD(PERIOD) && |
@@ -445,6 +462,25 @@ static void print_sample_bts(union perf_event *event, | |||
445 | printf("\n"); | 462 | printf("\n"); |
446 | } | 463 | } |
447 | 464 | ||
465 | static void print_sample_flags(u32 flags) | ||
466 | { | ||
467 | const char *chars = PERF_IP_FLAG_CHARS; | ||
468 | const int n = strlen(PERF_IP_FLAG_CHARS); | ||
469 | char str[33]; | ||
470 | int i, pos = 0; | ||
471 | |||
472 | for (i = 0; i < n; i++, flags >>= 1) { | ||
473 | if (flags & 1) | ||
474 | str[pos++] = chars[i]; | ||
475 | } | ||
476 | for (; i < 32; i++, flags >>= 1) { | ||
477 | if (flags & 1) | ||
478 | str[pos++] = '?'; | ||
479 | } | ||
480 | str[pos] = 0; | ||
481 | printf(" %-4s ", str); | ||
482 | } | ||
483 | |||
448 | static void process_event(union perf_event *event, struct perf_sample *sample, | 484 | static void process_event(union perf_event *event, struct perf_sample *sample, |
449 | struct perf_evsel *evsel, struct addr_location *al) | 485 | struct perf_evsel *evsel, struct addr_location *al) |
450 | { | 486 | { |
@@ -464,6 +500,9 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
464 | printf("%s: ", evname ? evname : "[unknown]"); | 500 | printf("%s: ", evname ? evname : "[unknown]"); |
465 | } | 501 | } |
466 | 502 | ||
503 | if (print_flags) | ||
504 | print_sample_flags(sample->flags); | ||
505 | |||
467 | if (is_bts_event(attr)) { | 506 | if (is_bts_event(attr)) { |
468 | print_sample_bts(event, sample, evsel, thread, al); | 507 | print_sample_bts(event, sample, evsel, thread, al); |
469 | return; | 508 | return; |
@@ -999,12 +1038,15 @@ static int parse_output_fields(const struct option *opt __maybe_unused, | |||
999 | } | 1038 | } |
1000 | } | 1039 | } |
1001 | 1040 | ||
1002 | tok = strtok(tok, ","); | 1041 | for (tok = strtok(tok, ","); tok; tok = strtok(NULL, ",")) { |
1003 | while (tok) { | ||
1004 | for (i = 0; i < imax; ++i) { | 1042 | for (i = 0; i < imax; ++i) { |
1005 | if (strcmp(tok, all_output_options[i].str) == 0) | 1043 | if (strcmp(tok, all_output_options[i].str) == 0) |
1006 | break; | 1044 | break; |
1007 | } | 1045 | } |
1046 | if (i == imax && strcmp(tok, "flags") == 0) { | ||
1047 | print_flags = true; | ||
1048 | continue; | ||
1049 | } | ||
1008 | if (i == imax) { | 1050 | if (i == imax) { |
1009 | fprintf(stderr, "Invalid field requested.\n"); | 1051 | fprintf(stderr, "Invalid field requested.\n"); |
1010 | rc = -EINVAL; | 1052 | rc = -EINVAL; |
@@ -1032,8 +1074,6 @@ static int parse_output_fields(const struct option *opt __maybe_unused, | |||
1032 | } | 1074 | } |
1033 | output[type].fields |= all_output_options[i].field; | 1075 | output[type].fields |= all_output_options[i].field; |
1034 | } | 1076 | } |
1035 | |||
1036 | tok = strtok(NULL, ","); | ||
1037 | } | 1077 | } |
1038 | 1078 | ||
1039 | if (type >= 0) { | 1079 | if (type >= 0) { |
@@ -1497,6 +1537,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1497 | char *rec_script_path = NULL; | 1537 | char *rec_script_path = NULL; |
1498 | char *rep_script_path = NULL; | 1538 | char *rep_script_path = NULL; |
1499 | struct perf_session *session; | 1539 | struct perf_session *session; |
1540 | struct itrace_synth_opts itrace_synth_opts = { .set = false, }; | ||
1500 | char *script_path = NULL; | 1541 | char *script_path = NULL; |
1501 | const char **__argv; | 1542 | const char **__argv; |
1502 | int i, j, err = 0; | 1543 | int i, j, err = 0; |
@@ -1511,6 +1552,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1511 | .attr = process_attr, | 1552 | .attr = process_attr, |
1512 | .tracing_data = perf_event__process_tracing_data, | 1553 | .tracing_data = perf_event__process_tracing_data, |
1513 | .build_id = perf_event__process_build_id, | 1554 | .build_id = perf_event__process_build_id, |
1555 | .id_index = perf_event__process_id_index, | ||
1556 | .auxtrace_info = perf_event__process_auxtrace_info, | ||
1557 | .auxtrace = perf_event__process_auxtrace, | ||
1558 | .auxtrace_error = perf_event__process_auxtrace_error, | ||
1514 | .ordered_events = true, | 1559 | .ordered_events = true, |
1515 | .ordering_requires_timestamps = true, | 1560 | .ordering_requires_timestamps = true, |
1516 | }, | 1561 | }, |
@@ -1549,7 +1594,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1549 | "comma separated output fields prepend with 'type:'. " | 1594 | "comma separated output fields prepend with 'type:'. " |
1550 | "Valid types: hw,sw,trace,raw. " | 1595 | "Valid types: hw,sw,trace,raw. " |
1551 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," | 1596 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," |
1552 | "addr,symoff,period", parse_output_fields), | 1597 | "addr,symoff,period,flags", parse_output_fields), |
1553 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1598 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1554 | "system-wide collection from all CPUs"), | 1599 | "system-wide collection from all CPUs"), |
1555 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 1600 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
@@ -1570,6 +1615,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1570 | OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, | 1615 | OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events, |
1571 | "Show the mmap events"), | 1616 | "Show the mmap events"), |
1572 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), | 1617 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
1618 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", | ||
1619 | "Instruction Tracing options", | ||
1620 | itrace_parse_synth_opts), | ||
1573 | OPT_END() | 1621 | OPT_END() |
1574 | }; | 1622 | }; |
1575 | const char * const script_subcommands[] = { "record", "report", NULL }; | 1623 | const char * const script_subcommands[] = { "record", "report", NULL }; |
@@ -1765,6 +1813,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1765 | 1813 | ||
1766 | script.session = session; | 1814 | script.session = session; |
1767 | 1815 | ||
1816 | session->itrace_synth_opts = &itrace_synth_opts; | ||
1817 | |||
1768 | if (cpu_list) { | 1818 | if (cpu_list) { |
1769 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | 1819 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); |
1770 | if (err < 0) | 1820 | if (err < 0) |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f7b8218785f6..fd577f725d23 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -247,21 +247,50 @@ out_free: | |||
247 | return -1; | 247 | return -1; |
248 | } | 248 | } |
249 | 249 | ||
250 | enum { | ||
251 | CTX_BIT_USER = 1 << 0, | ||
252 | CTX_BIT_KERNEL = 1 << 1, | ||
253 | CTX_BIT_HV = 1 << 2, | ||
254 | CTX_BIT_HOST = 1 << 3, | ||
255 | CTX_BIT_IDLE = 1 << 4, | ||
256 | CTX_BIT_MAX = 1 << 5, | ||
257 | }; | ||
258 | |||
259 | #define NUM_CTX CTX_BIT_MAX | ||
260 | |||
250 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 261 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
251 | static struct stats runtime_cycles_stats[MAX_NR_CPUS]; | 262 | static struct stats runtime_cycles_stats[NUM_CTX][MAX_NR_CPUS]; |
252 | static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; | 263 | static struct stats runtime_stalled_cycles_front_stats[NUM_CTX][MAX_NR_CPUS]; |
253 | static struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS]; | 264 | static struct stats runtime_stalled_cycles_back_stats[NUM_CTX][MAX_NR_CPUS]; |
254 | static struct stats runtime_branches_stats[MAX_NR_CPUS]; | 265 | static struct stats runtime_branches_stats[NUM_CTX][MAX_NR_CPUS]; |
255 | static struct stats runtime_cacherefs_stats[MAX_NR_CPUS]; | 266 | static struct stats runtime_cacherefs_stats[NUM_CTX][MAX_NR_CPUS]; |
256 | static struct stats runtime_l1_dcache_stats[MAX_NR_CPUS]; | 267 | static struct stats runtime_l1_dcache_stats[NUM_CTX][MAX_NR_CPUS]; |
257 | static struct stats runtime_l1_icache_stats[MAX_NR_CPUS]; | 268 | static struct stats runtime_l1_icache_stats[NUM_CTX][MAX_NR_CPUS]; |
258 | static struct stats runtime_ll_cache_stats[MAX_NR_CPUS]; | 269 | static struct stats runtime_ll_cache_stats[NUM_CTX][MAX_NR_CPUS]; |
259 | static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | 270 | static struct stats runtime_itlb_cache_stats[NUM_CTX][MAX_NR_CPUS]; |
260 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 271 | static struct stats runtime_dtlb_cache_stats[NUM_CTX][MAX_NR_CPUS]; |
261 | static struct stats runtime_cycles_in_tx_stats[MAX_NR_CPUS]; | 272 | static struct stats runtime_cycles_in_tx_stats[NUM_CTX][MAX_NR_CPUS]; |
262 | static struct stats walltime_nsecs_stats; | 273 | static struct stats walltime_nsecs_stats; |
263 | static struct stats runtime_transaction_stats[MAX_NR_CPUS]; | 274 | static struct stats runtime_transaction_stats[NUM_CTX][MAX_NR_CPUS]; |
264 | static struct stats runtime_elision_stats[MAX_NR_CPUS]; | 275 | static struct stats runtime_elision_stats[NUM_CTX][MAX_NR_CPUS]; |
276 | |||
277 | static int evsel_context(struct perf_evsel *evsel) | ||
278 | { | ||
279 | int ctx = 0; | ||
280 | |||
281 | if (evsel->attr.exclude_kernel) | ||
282 | ctx |= CTX_BIT_KERNEL; | ||
283 | if (evsel->attr.exclude_user) | ||
284 | ctx |= CTX_BIT_USER; | ||
285 | if (evsel->attr.exclude_hv) | ||
286 | ctx |= CTX_BIT_HV; | ||
287 | if (evsel->attr.exclude_host) | ||
288 | ctx |= CTX_BIT_HOST; | ||
289 | if (evsel->attr.exclude_idle) | ||
290 | ctx |= CTX_BIT_IDLE; | ||
291 | |||
292 | return ctx; | ||
293 | } | ||
265 | 294 | ||
266 | static void perf_stat__reset_stats(struct perf_evlist *evlist) | 295 | static void perf_stat__reset_stats(struct perf_evlist *evlist) |
267 | { | 296 | { |
@@ -356,37 +385,39 @@ static struct perf_evsel *nth_evsel(int n) | |||
356 | static void update_shadow_stats(struct perf_evsel *counter, u64 *count, | 385 | static void update_shadow_stats(struct perf_evsel *counter, u64 *count, |
357 | int cpu) | 386 | int cpu) |
358 | { | 387 | { |
388 | int ctx = evsel_context(counter); | ||
389 | |||
359 | if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK)) | 390 | if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK)) |
360 | update_stats(&runtime_nsecs_stats[cpu], count[0]); | 391 | update_stats(&runtime_nsecs_stats[cpu], count[0]); |
361 | else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) | 392 | else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) |
362 | update_stats(&runtime_cycles_stats[cpu], count[0]); | 393 | update_stats(&runtime_cycles_stats[ctx][cpu], count[0]); |
363 | else if (transaction_run && | 394 | else if (transaction_run && |
364 | perf_evsel__cmp(counter, nth_evsel(T_CYCLES_IN_TX))) | 395 | perf_evsel__cmp(counter, nth_evsel(T_CYCLES_IN_TX))) |
365 | update_stats(&runtime_cycles_in_tx_stats[cpu], count[0]); | 396 | update_stats(&runtime_transaction_stats[ctx][cpu], count[0]); |
366 | else if (transaction_run && | 397 | else if (transaction_run && |
367 | perf_evsel__cmp(counter, nth_evsel(T_TRANSACTION_START))) | 398 | perf_evsel__cmp(counter, nth_evsel(T_TRANSACTION_START))) |
368 | update_stats(&runtime_transaction_stats[cpu], count[0]); | 399 | update_stats(&runtime_transaction_stats[ctx][cpu], count[0]); |
369 | else if (transaction_run && | 400 | else if (transaction_run && |
370 | perf_evsel__cmp(counter, nth_evsel(T_ELISION_START))) | 401 | perf_evsel__cmp(counter, nth_evsel(T_ELISION_START))) |
371 | update_stats(&runtime_elision_stats[cpu], count[0]); | 402 | update_stats(&runtime_elision_stats[ctx][cpu], count[0]); |
372 | else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) | 403 | else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) |
373 | update_stats(&runtime_stalled_cycles_front_stats[cpu], count[0]); | 404 | update_stats(&runtime_stalled_cycles_front_stats[ctx][cpu], count[0]); |
374 | else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND)) | 405 | else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND)) |
375 | update_stats(&runtime_stalled_cycles_back_stats[cpu], count[0]); | 406 | update_stats(&runtime_stalled_cycles_back_stats[ctx][cpu], count[0]); |
376 | else if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS)) | 407 | else if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS)) |
377 | update_stats(&runtime_branches_stats[cpu], count[0]); | 408 | update_stats(&runtime_branches_stats[ctx][cpu], count[0]); |
378 | else if (perf_evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES)) | 409 | else if (perf_evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES)) |
379 | update_stats(&runtime_cacherefs_stats[cpu], count[0]); | 410 | update_stats(&runtime_cacherefs_stats[ctx][cpu], count[0]); |
380 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1D)) | 411 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1D)) |
381 | update_stats(&runtime_l1_dcache_stats[cpu], count[0]); | 412 | update_stats(&runtime_l1_dcache_stats[ctx][cpu], count[0]); |
382 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1I)) | 413 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1I)) |
383 | update_stats(&runtime_l1_icache_stats[cpu], count[0]); | 414 | update_stats(&runtime_ll_cache_stats[ctx][cpu], count[0]); |
384 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_LL)) | 415 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_LL)) |
385 | update_stats(&runtime_ll_cache_stats[cpu], count[0]); | 416 | update_stats(&runtime_ll_cache_stats[ctx][cpu], count[0]); |
386 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_DTLB)) | 417 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_DTLB)) |
387 | update_stats(&runtime_dtlb_cache_stats[cpu], count[0]); | 418 | update_stats(&runtime_dtlb_cache_stats[ctx][cpu], count[0]); |
388 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB)) | 419 | else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB)) |
389 | update_stats(&runtime_itlb_cache_stats[cpu], count[0]); | 420 | update_stats(&runtime_itlb_cache_stats[ctx][cpu], count[0]); |
390 | } | 421 | } |
391 | 422 | ||
392 | static void zero_per_pkg(struct perf_evsel *counter) | 423 | static void zero_per_pkg(struct perf_evsel *counter) |
@@ -908,8 +939,9 @@ static void print_stalled_cycles_frontend(int cpu, | |||
908 | { | 939 | { |
909 | double total, ratio = 0.0; | 940 | double total, ratio = 0.0; |
910 | const char *color; | 941 | const char *color; |
942 | int ctx = evsel_context(evsel); | ||
911 | 943 | ||
912 | total = avg_stats(&runtime_cycles_stats[cpu]); | 944 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
913 | 945 | ||
914 | if (total) | 946 | if (total) |
915 | ratio = avg / total * 100.0; | 947 | ratio = avg / total * 100.0; |
@@ -927,8 +959,9 @@ static void print_stalled_cycles_backend(int cpu, | |||
927 | { | 959 | { |
928 | double total, ratio = 0.0; | 960 | double total, ratio = 0.0; |
929 | const char *color; | 961 | const char *color; |
962 | int ctx = evsel_context(evsel); | ||
930 | 963 | ||
931 | total = avg_stats(&runtime_cycles_stats[cpu]); | 964 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
932 | 965 | ||
933 | if (total) | 966 | if (total) |
934 | ratio = avg / total * 100.0; | 967 | ratio = avg / total * 100.0; |
@@ -946,8 +979,9 @@ static void print_branch_misses(int cpu, | |||
946 | { | 979 | { |
947 | double total, ratio = 0.0; | 980 | double total, ratio = 0.0; |
948 | const char *color; | 981 | const char *color; |
982 | int ctx = evsel_context(evsel); | ||
949 | 983 | ||
950 | total = avg_stats(&runtime_branches_stats[cpu]); | 984 | total = avg_stats(&runtime_branches_stats[ctx][cpu]); |
951 | 985 | ||
952 | if (total) | 986 | if (total) |
953 | ratio = avg / total * 100.0; | 987 | ratio = avg / total * 100.0; |
@@ -965,8 +999,9 @@ static void print_l1_dcache_misses(int cpu, | |||
965 | { | 999 | { |
966 | double total, ratio = 0.0; | 1000 | double total, ratio = 0.0; |
967 | const char *color; | 1001 | const char *color; |
1002 | int ctx = evsel_context(evsel); | ||
968 | 1003 | ||
969 | total = avg_stats(&runtime_l1_dcache_stats[cpu]); | 1004 | total = avg_stats(&runtime_l1_dcache_stats[ctx][cpu]); |
970 | 1005 | ||
971 | if (total) | 1006 | if (total) |
972 | ratio = avg / total * 100.0; | 1007 | ratio = avg / total * 100.0; |
@@ -984,8 +1019,9 @@ static void print_l1_icache_misses(int cpu, | |||
984 | { | 1019 | { |
985 | double total, ratio = 0.0; | 1020 | double total, ratio = 0.0; |
986 | const char *color; | 1021 | const char *color; |
1022 | int ctx = evsel_context(evsel); | ||
987 | 1023 | ||
988 | total = avg_stats(&runtime_l1_icache_stats[cpu]); | 1024 | total = avg_stats(&runtime_l1_icache_stats[ctx][cpu]); |
989 | 1025 | ||
990 | if (total) | 1026 | if (total) |
991 | ratio = avg / total * 100.0; | 1027 | ratio = avg / total * 100.0; |
@@ -1003,8 +1039,9 @@ static void print_dtlb_cache_misses(int cpu, | |||
1003 | { | 1039 | { |
1004 | double total, ratio = 0.0; | 1040 | double total, ratio = 0.0; |
1005 | const char *color; | 1041 | const char *color; |
1042 | int ctx = evsel_context(evsel); | ||
1006 | 1043 | ||
1007 | total = avg_stats(&runtime_dtlb_cache_stats[cpu]); | 1044 | total = avg_stats(&runtime_dtlb_cache_stats[ctx][cpu]); |
1008 | 1045 | ||
1009 | if (total) | 1046 | if (total) |
1010 | ratio = avg / total * 100.0; | 1047 | ratio = avg / total * 100.0; |
@@ -1022,8 +1059,9 @@ static void print_itlb_cache_misses(int cpu, | |||
1022 | { | 1059 | { |
1023 | double total, ratio = 0.0; | 1060 | double total, ratio = 0.0; |
1024 | const char *color; | 1061 | const char *color; |
1062 | int ctx = evsel_context(evsel); | ||
1025 | 1063 | ||
1026 | total = avg_stats(&runtime_itlb_cache_stats[cpu]); | 1064 | total = avg_stats(&runtime_itlb_cache_stats[ctx][cpu]); |
1027 | 1065 | ||
1028 | if (total) | 1066 | if (total) |
1029 | ratio = avg / total * 100.0; | 1067 | ratio = avg / total * 100.0; |
@@ -1041,8 +1079,9 @@ static void print_ll_cache_misses(int cpu, | |||
1041 | { | 1079 | { |
1042 | double total, ratio = 0.0; | 1080 | double total, ratio = 0.0; |
1043 | const char *color; | 1081 | const char *color; |
1082 | int ctx = evsel_context(evsel); | ||
1044 | 1083 | ||
1045 | total = avg_stats(&runtime_ll_cache_stats[cpu]); | 1084 | total = avg_stats(&runtime_ll_cache_stats[ctx][cpu]); |
1046 | 1085 | ||
1047 | if (total) | 1086 | if (total) |
1048 | ratio = avg / total * 100.0; | 1087 | ratio = avg / total * 100.0; |
@@ -1060,6 +1099,7 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1060 | double sc = evsel->scale; | 1099 | double sc = evsel->scale; |
1061 | const char *fmt; | 1100 | const char *fmt; |
1062 | int cpu = cpu_map__id_to_cpu(id); | 1101 | int cpu = cpu_map__id_to_cpu(id); |
1102 | int ctx = evsel_context(evsel); | ||
1063 | 1103 | ||
1064 | if (csv_output) { | 1104 | if (csv_output) { |
1065 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; | 1105 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; |
@@ -1091,15 +1131,15 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1091 | return; | 1131 | return; |
1092 | 1132 | ||
1093 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | 1133 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { |
1094 | total = avg_stats(&runtime_cycles_stats[cpu]); | 1134 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
1095 | if (total) { | 1135 | if (total) { |
1096 | ratio = avg / total; | 1136 | ratio = avg / total; |
1097 | fprintf(output, " # %5.2f insns per cycle ", ratio); | 1137 | fprintf(output, " # %5.2f insns per cycle ", ratio); |
1098 | } else { | 1138 | } else { |
1099 | fprintf(output, " "); | 1139 | fprintf(output, " "); |
1100 | } | 1140 | } |
1101 | total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]); | 1141 | total = avg_stats(&runtime_stalled_cycles_front_stats[ctx][cpu]); |
1102 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu])); | 1142 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[ctx][cpu])); |
1103 | 1143 | ||
1104 | if (total && avg) { | 1144 | if (total && avg) { |
1105 | ratio = total / avg; | 1145 | ratio = total / avg; |
@@ -1110,46 +1150,46 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1110 | } | 1150 | } |
1111 | 1151 | ||
1112 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && | 1152 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && |
1113 | runtime_branches_stats[cpu].n != 0) { | 1153 | runtime_branches_stats[ctx][cpu].n != 0) { |
1114 | print_branch_misses(cpu, evsel, avg); | 1154 | print_branch_misses(cpu, evsel, avg); |
1115 | } else if ( | 1155 | } else if ( |
1116 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 1156 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
1117 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | | 1157 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | |
1118 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 1158 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
1119 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 1159 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && |
1120 | runtime_l1_dcache_stats[cpu].n != 0) { | 1160 | runtime_l1_dcache_stats[ctx][cpu].n != 0) { |
1121 | print_l1_dcache_misses(cpu, evsel, avg); | 1161 | print_l1_dcache_misses(cpu, evsel, avg); |
1122 | } else if ( | 1162 | } else if ( |
1123 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 1163 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
1124 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | | 1164 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | |
1125 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 1165 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
1126 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 1166 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && |
1127 | runtime_l1_icache_stats[cpu].n != 0) { | 1167 | runtime_l1_icache_stats[ctx][cpu].n != 0) { |
1128 | print_l1_icache_misses(cpu, evsel, avg); | 1168 | print_l1_icache_misses(cpu, evsel, avg); |
1129 | } else if ( | 1169 | } else if ( |
1130 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 1170 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
1131 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | | 1171 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | |
1132 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 1172 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
1133 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 1173 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && |
1134 | runtime_dtlb_cache_stats[cpu].n != 0) { | 1174 | runtime_dtlb_cache_stats[ctx][cpu].n != 0) { |
1135 | print_dtlb_cache_misses(cpu, evsel, avg); | 1175 | print_dtlb_cache_misses(cpu, evsel, avg); |
1136 | } else if ( | 1176 | } else if ( |
1137 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 1177 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
1138 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | | 1178 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | |
1139 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 1179 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
1140 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 1180 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && |
1141 | runtime_itlb_cache_stats[cpu].n != 0) { | 1181 | runtime_itlb_cache_stats[ctx][cpu].n != 0) { |
1142 | print_itlb_cache_misses(cpu, evsel, avg); | 1182 | print_itlb_cache_misses(cpu, evsel, avg); |
1143 | } else if ( | 1183 | } else if ( |
1144 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 1184 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
1145 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | | 1185 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | |
1146 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 1186 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
1147 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 1187 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && |
1148 | runtime_ll_cache_stats[cpu].n != 0) { | 1188 | runtime_ll_cache_stats[ctx][cpu].n != 0) { |
1149 | print_ll_cache_misses(cpu, evsel, avg); | 1189 | print_ll_cache_misses(cpu, evsel, avg); |
1150 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES) && | 1190 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES) && |
1151 | runtime_cacherefs_stats[cpu].n != 0) { | 1191 | runtime_cacherefs_stats[ctx][cpu].n != 0) { |
1152 | total = avg_stats(&runtime_cacherefs_stats[cpu]); | 1192 | total = avg_stats(&runtime_cacherefs_stats[ctx][cpu]); |
1153 | 1193 | ||
1154 | if (total) | 1194 | if (total) |
1155 | ratio = avg * 100 / total; | 1195 | ratio = avg * 100 / total; |
@@ -1171,15 +1211,15 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1171 | } | 1211 | } |
1172 | } else if (transaction_run && | 1212 | } else if (transaction_run && |
1173 | perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) { | 1213 | perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) { |
1174 | total = avg_stats(&runtime_cycles_stats[cpu]); | 1214 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
1175 | if (total) | 1215 | if (total) |
1176 | fprintf(output, | 1216 | fprintf(output, |
1177 | " # %5.2f%% transactional cycles ", | 1217 | " # %5.2f%% transactional cycles ", |
1178 | 100.0 * (avg / total)); | 1218 | 100.0 * (avg / total)); |
1179 | } else if (transaction_run && | 1219 | } else if (transaction_run && |
1180 | perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX_CP))) { | 1220 | perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX_CP))) { |
1181 | total = avg_stats(&runtime_cycles_stats[cpu]); | 1221 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
1182 | total2 = avg_stats(&runtime_cycles_in_tx_stats[cpu]); | 1222 | total2 = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
1183 | if (total2 < avg) | 1223 | if (total2 < avg) |
1184 | total2 = avg; | 1224 | total2 = avg; |
1185 | if (total) | 1225 | if (total) |
@@ -1189,8 +1229,8 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1189 | } else if (transaction_run && | 1229 | } else if (transaction_run && |
1190 | perf_evsel__cmp(evsel, nth_evsel(T_TRANSACTION_START)) && | 1230 | perf_evsel__cmp(evsel, nth_evsel(T_TRANSACTION_START)) && |
1191 | avg > 0 && | 1231 | avg > 0 && |
1192 | runtime_cycles_in_tx_stats[cpu].n != 0) { | 1232 | runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { |
1193 | total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); | 1233 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
1194 | 1234 | ||
1195 | if (total) | 1235 | if (total) |
1196 | ratio = total / avg; | 1236 | ratio = total / avg; |
@@ -1199,8 +1239,8 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
1199 | } else if (transaction_run && | 1239 | } else if (transaction_run && |
1200 | perf_evsel__cmp(evsel, nth_evsel(T_ELISION_START)) && | 1240 | perf_evsel__cmp(evsel, nth_evsel(T_ELISION_START)) && |
1201 | avg > 0 && | 1241 | avg > 0 && |
1202 | runtime_cycles_in_tx_stats[cpu].n != 0) { | 1242 | runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { |
1203 | total = avg_stats(&runtime_cycles_in_tx_stats[cpu]); | 1243 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
1204 | 1244 | ||
1205 | if (total) | 1245 | if (total) |
1206 | ratio = total / avg; | 1246 | ratio = total / avg; |
@@ -1541,7 +1581,7 @@ static int setup_events(const char * const *attrs, unsigned len) | |||
1541 | unsigned i; | 1581 | unsigned i; |
1542 | 1582 | ||
1543 | for (i = 0; i < len; i++) { | 1583 | for (i = 0; i < len; i++) { |
1544 | if (parse_events(evsel_list, attrs[i])) | 1584 | if (parse_events(evsel_list, attrs[i], NULL)) |
1545 | return -1; | 1585 | return -1; |
1546 | } | 1586 | } |
1547 | return 0; | 1587 | return 0; |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e124741be187..d1139b6bd534 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -2660,16 +2660,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2660 | OPT_BOOLEAN(0, "comm", &trace.show_comm, | 2660 | OPT_BOOLEAN(0, "comm", &trace.show_comm, |
2661 | "show the thread COMM next to its id"), | 2661 | "show the thread COMM next to its id"), |
2662 | OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), | 2662 | OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), |
2663 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 2663 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"), |
2664 | "list of events to trace"), | ||
2665 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | 2664 | OPT_STRING('o', "output", &output_name, "file", "output file name"), |
2666 | OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), | 2665 | OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), |
2667 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 2666 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
2668 | "trace events on existing process id"), | 2667 | "trace events on existing process id"), |
2669 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", | 2668 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
2670 | "trace events on existing thread id"), | 2669 | "trace events on existing thread id"), |
2671 | OPT_CALLBACK(0, "filter-pids", &trace, "float", | 2670 | OPT_CALLBACK(0, "filter-pids", &trace, "CSV list of pids", |
2672 | "show only events with duration > N.M ms", trace__set_filter_pids), | 2671 | "pids to filter (by the kernel)", trace__set_filter_pids), |
2673 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, | 2672 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, |
2674 | "system-wide collection from all CPUs"), | 2673 | "system-wide collection from all CPUs"), |
2675 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", | 2674 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 59a98c643240..435b6ca85b1f 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -610,6 +610,11 @@ ifdef LIBBABELTRACE | |||
610 | endif | 610 | endif |
611 | endif | 611 | endif |
612 | 612 | ||
613 | ifndef NO_AUXTRACE | ||
614 | $(call detected,CONFIG_AUXTRACE) | ||
615 | CFLAGS += -DHAVE_AUXTRACE_SUPPORT | ||
616 | endif | ||
617 | |||
613 | # Among the variables below, these: | 618 | # Among the variables below, these: |
614 | # perfexecdir | 619 | # perfexecdir |
615 | # template_dir | 620 | # template_dir |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index e14bb637255c..aa79fb8a16d4 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -54,12 +54,17 @@ struct record_opts { | |||
54 | bool period; | 54 | bool period; |
55 | bool sample_intr_regs; | 55 | bool sample_intr_regs; |
56 | bool running_time; | 56 | bool running_time; |
57 | bool full_auxtrace; | ||
58 | bool auxtrace_snapshot_mode; | ||
57 | unsigned int freq; | 59 | unsigned int freq; |
58 | unsigned int mmap_pages; | 60 | unsigned int mmap_pages; |
61 | unsigned int auxtrace_mmap_pages; | ||
59 | unsigned int user_freq; | 62 | unsigned int user_freq; |
60 | u64 branch_stack; | 63 | u64 branch_stack; |
61 | u64 default_interval; | 64 | u64 default_interval; |
62 | u64 user_interval; | 65 | u64 user_interval; |
66 | size_t auxtrace_snapshot_size; | ||
67 | const char *auxtrace_snapshot_opts; | ||
63 | bool sample_transaction; | 68 | bool sample_transaction; |
64 | unsigned initial_delay; | 69 | unsigned initial_delay; |
65 | bool use_clockid; | 70 | bool use_clockid; |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index f671ec37a7c4..ca0e480e741b 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -482,7 +482,7 @@ static int do_test_code_reading(bool try_kcore) | |||
482 | else | 482 | else |
483 | str = "cycles"; | 483 | str = "cycles"; |
484 | pr_debug("Parsing event '%s'\n", str); | 484 | pr_debug("Parsing event '%s'\n", str); |
485 | ret = parse_events(evlist, str); | 485 | ret = parse_events(evlist, str, NULL); |
486 | if (ret < 0) { | 486 | if (ret < 0) { |
487 | pr_debug("parse_events failed\n"); | 487 | pr_debug("parse_events failed\n"); |
488 | goto out_err; | 488 | goto out_err; |
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index b8d8341b383e..3fa715987a5e 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c | |||
@@ -23,7 +23,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) | |||
23 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 23 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
24 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | 24 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, |
25 | name, sizeof(name)); | 25 | name, sizeof(name)); |
26 | err = parse_events(evlist, name); | 26 | err = parse_events(evlist, name, NULL); |
27 | if (err) | 27 | if (err) |
28 | ret = err; | 28 | ret = err; |
29 | } | 29 | } |
@@ -71,7 +71,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) | |||
71 | return -ENOMEM; | 71 | return -ENOMEM; |
72 | 72 | ||
73 | for (i = 0; i < nr_names; ++i) { | 73 | for (i = 0; i < nr_names; ++i) { |
74 | err = parse_events(evlist, names[i]); | 74 | err = parse_events(evlist, names[i], NULL); |
75 | if (err) { | 75 | if (err) { |
76 | pr_debug("failed to parse event '%s', err %d\n", | 76 | pr_debug("failed to parse event '%s', err %d\n", |
77 | names[i], err); | 77 | names[i], err); |
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 18619966454c..b08a95a5ca1a 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
@@ -695,7 +695,7 @@ int test__hists_cumulate(void) | |||
695 | 695 | ||
696 | TEST_ASSERT_VAL("No memory", evlist); | 696 | TEST_ASSERT_VAL("No memory", evlist); |
697 | 697 | ||
698 | err = parse_events(evlist, "cpu-clock"); | 698 | err = parse_events(evlist, "cpu-clock", NULL); |
699 | if (err) | 699 | if (err) |
700 | goto out; | 700 | goto out; |
701 | 701 | ||
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 59e53db7914c..108488cd71fa 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
@@ -108,10 +108,10 @@ int test__hists_filter(void) | |||
108 | 108 | ||
109 | TEST_ASSERT_VAL("No memory", evlist); | 109 | TEST_ASSERT_VAL("No memory", evlist); |
110 | 110 | ||
111 | err = parse_events(evlist, "cpu-clock"); | 111 | err = parse_events(evlist, "cpu-clock", NULL); |
112 | if (err) | 112 | if (err) |
113 | goto out; | 113 | goto out; |
114 | err = parse_events(evlist, "task-clock"); | 114 | err = parse_events(evlist, "task-clock", NULL); |
115 | if (err) | 115 | if (err) |
116 | goto out; | 116 | goto out; |
117 | 117 | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 278ba8344c23..34c61e4d3352 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -282,10 +282,10 @@ int test__hists_link(void) | |||
282 | if (evlist == NULL) | 282 | if (evlist == NULL) |
283 | return -ENOMEM; | 283 | return -ENOMEM; |
284 | 284 | ||
285 | err = parse_events(evlist, "cpu-clock"); | 285 | err = parse_events(evlist, "cpu-clock", NULL); |
286 | if (err) | 286 | if (err) |
287 | goto out; | 287 | goto out; |
288 | err = parse_events(evlist, "task-clock"); | 288 | err = parse_events(evlist, "task-clock", NULL); |
289 | if (err) | 289 | if (err) |
290 | goto out; | 290 | goto out; |
291 | 291 | ||
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index b52c9faea224..d8a23db80094 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
@@ -590,7 +590,7 @@ int test__hists_output(void) | |||
590 | 590 | ||
591 | TEST_ASSERT_VAL("No memory", evlist); | 591 | TEST_ASSERT_VAL("No memory", evlist); |
592 | 592 | ||
593 | err = parse_events(evlist, "cpu-clock"); | 593 | err = parse_events(evlist, "cpu-clock", NULL); |
594 | if (err) | 594 | if (err) |
595 | goto out; | 595 | goto out; |
596 | 596 | ||
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 7a5ab7b0b8f6..5b171d1e338b 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c | |||
@@ -78,8 +78,8 @@ int test__keep_tracking(void) | |||
78 | 78 | ||
79 | perf_evlist__set_maps(evlist, cpus, threads); | 79 | perf_evlist__set_maps(evlist, cpus, threads); |
80 | 80 | ||
81 | CHECK__(parse_events(evlist, "dummy:u")); | 81 | CHECK__(parse_events(evlist, "dummy:u", NULL)); |
82 | CHECK__(parse_events(evlist, "cycles:u")); | 82 | CHECK__(parse_events(evlist, "cycles:u", NULL)); |
83 | 83 | ||
84 | perf_evlist__config(evlist, &opts); | 84 | perf_evlist__config(evlist, &opts); |
85 | 85 | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index bff85324f799..65280d28662e 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
@@ -32,6 +32,7 @@ make_no_backtrace := NO_BACKTRACE=1 | |||
32 | make_no_libnuma := NO_LIBNUMA=1 | 32 | make_no_libnuma := NO_LIBNUMA=1 |
33 | make_no_libaudit := NO_LIBAUDIT=1 | 33 | make_no_libaudit := NO_LIBAUDIT=1 |
34 | make_no_libbionic := NO_LIBBIONIC=1 | 34 | make_no_libbionic := NO_LIBBIONIC=1 |
35 | make_no_auxtrace := NO_AUXTRACE=1 | ||
35 | make_tags := tags | 36 | make_tags := tags |
36 | make_cscope := cscope | 37 | make_cscope := cscope |
37 | make_help := help | 38 | make_help := help |
@@ -52,7 +53,7 @@ make_static := LDFLAGS=-static | |||
52 | make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 | 53 | make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 |
53 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 | 54 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 |
54 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 | 55 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 |
55 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 | 56 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 |
56 | 57 | ||
57 | # $(run) contains all available tests | 58 | # $(run) contains all available tests |
58 | run := make_pure | 59 | run := make_pure |
@@ -74,6 +75,7 @@ run += make_no_backtrace | |||
74 | run += make_no_libnuma | 75 | run += make_no_libnuma |
75 | run += make_no_libaudit | 76 | run += make_no_libaudit |
76 | run += make_no_libbionic | 77 | run += make_no_libbionic |
78 | run += make_no_auxtrace | ||
77 | run += make_help | 79 | run += make_help |
78 | run += make_doc | 80 | run += make_doc |
79 | run += make_perf_o | 81 | run += make_perf_o |
@@ -223,7 +225,19 @@ tarpkg: | |||
223 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 225 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ |
224 | ( eval $$cmd ) >> $@ 2>&1 | 226 | ( eval $$cmd ) >> $@ 2>&1 |
225 | 227 | ||
226 | all: $(run) $(run_O) tarpkg | 228 | make_kernelsrc: |
229 | @echo " - make -C <kernelsrc> tools/perf" | ||
230 | $(call clean); \ | ||
231 | (make -C ../.. tools/perf) > $@ 2>&1 && \ | ||
232 | test -x perf && rm -f $@ || (cat $@ ; false) | ||
233 | |||
234 | make_kernelsrc_tools: | ||
235 | @echo " - make -C <kernelsrc>/tools perf" | ||
236 | $(call clean); \ | ||
237 | (make -C ../../tools perf) > $@ 2>&1 && \ | ||
238 | test -x perf && rm -f $@ || (cat $@ ; false) | ||
239 | |||
240 | all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools | ||
227 | @echo OK | 241 | @echo OK |
228 | 242 | ||
229 | out: $(run_O) | 243 | out: $(run_O) |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 3de744961739..82d2a1636f7f 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -1571,7 +1571,7 @@ static int test_event(struct evlist_test *e) | |||
1571 | if (evlist == NULL) | 1571 | if (evlist == NULL) |
1572 | return -ENOMEM; | 1572 | return -ENOMEM; |
1573 | 1573 | ||
1574 | ret = parse_events(evlist, e->name); | 1574 | ret = parse_events(evlist, e->name, NULL); |
1575 | if (ret) { | 1575 | if (ret) { |
1576 | pr_debug("failed to parse event '%s', err %d\n", | 1576 | pr_debug("failed to parse event '%s', err %d\n", |
1577 | e->name, ret); | 1577 | e->name, ret); |
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index f238442b238a..5f49484f1abc 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c | |||
@@ -68,7 +68,7 @@ int test__perf_time_to_tsc(void) | |||
68 | 68 | ||
69 | perf_evlist__set_maps(evlist, cpus, threads); | 69 | perf_evlist__set_maps(evlist, cpus, threads); |
70 | 70 | ||
71 | CHECK__(parse_events(evlist, "cycles:u")); | 71 | CHECK__(parse_events(evlist, "cycles:u", NULL)); |
72 | 72 | ||
73 | perf_evlist__config(evlist, &opts); | 73 | perf_evlist__config(evlist, &opts); |
74 | 74 | ||
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index eeb68bb1972d..faa04e9d5d5f 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c | |||
@@ -152,7 +152,8 @@ int test__pmu(void) | |||
152 | if (ret) | 152 | if (ret) |
153 | break; | 153 | break; |
154 | 154 | ||
155 | ret = perf_pmu__config_terms(&formats, &attr, terms, false); | 155 | ret = perf_pmu__config_terms(&formats, &attr, terms, |
156 | false, NULL); | ||
156 | if (ret) | 157 | if (ret) |
157 | break; | 158 | break; |
158 | 159 | ||
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index cc68648c7c55..0d31403ea593 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c | |||
@@ -347,7 +347,7 @@ int test__switch_tracking(void) | |||
347 | perf_evlist__set_maps(evlist, cpus, threads); | 347 | perf_evlist__set_maps(evlist, cpus, threads); |
348 | 348 | ||
349 | /* First event */ | 349 | /* First event */ |
350 | err = parse_events(evlist, "cpu-clock:u"); | 350 | err = parse_events(evlist, "cpu-clock:u", NULL); |
351 | if (err) { | 351 | if (err) { |
352 | pr_debug("Failed to parse event dummy:u\n"); | 352 | pr_debug("Failed to parse event dummy:u\n"); |
353 | goto out_err; | 353 | goto out_err; |
@@ -356,7 +356,7 @@ int test__switch_tracking(void) | |||
356 | cpu_clocks_evsel = perf_evlist__last(evlist); | 356 | cpu_clocks_evsel = perf_evlist__last(evlist); |
357 | 357 | ||
358 | /* Second event */ | 358 | /* Second event */ |
359 | err = parse_events(evlist, "cycles:u"); | 359 | err = parse_events(evlist, "cycles:u", NULL); |
360 | if (err) { | 360 | if (err) { |
361 | pr_debug("Failed to parse event cycles:u\n"); | 361 | pr_debug("Failed to parse event cycles:u\n"); |
362 | goto out_err; | 362 | goto out_err; |
@@ -371,7 +371,7 @@ int test__switch_tracking(void) | |||
371 | goto out; | 371 | goto out; |
372 | } | 372 | } |
373 | 373 | ||
374 | err = parse_events(evlist, sched_switch); | 374 | err = parse_events(evlist, sched_switch, NULL); |
375 | if (err) { | 375 | if (err) { |
376 | pr_debug("Failed to parse event %s\n", sched_switch); | 376 | pr_debug("Failed to parse event %s\n", sched_switch); |
377 | goto out_err; | 377 | goto out_err; |
@@ -401,7 +401,7 @@ int test__switch_tracking(void) | |||
401 | perf_evsel__set_sample_bit(cycles_evsel, TIME); | 401 | perf_evsel__set_sample_bit(cycles_evsel, TIME); |
402 | 402 | ||
403 | /* Fourth event */ | 403 | /* Fourth event */ |
404 | err = parse_events(evlist, "dummy:u"); | 404 | err = parse_events(evlist, "dummy:u", NULL); |
405 | if (err) { | 405 | if (err) { |
406 | pr_debug("Failed to parse event dummy:u\n"); | 406 | pr_debug("Failed to parse event dummy:u\n"); |
407 | goto out_err; | 407 | goto out_err; |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 995b7a8596b1..f981cb8f0158 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -25,6 +25,9 @@ struct hist_browser { | |||
25 | struct hists *hists; | 25 | struct hists *hists; |
26 | struct hist_entry *he_selection; | 26 | struct hist_entry *he_selection; |
27 | struct map_symbol *selection; | 27 | struct map_symbol *selection; |
28 | struct hist_browser_timer *hbt; | ||
29 | struct pstack *pstack; | ||
30 | struct perf_session_env *env; | ||
28 | int print_seq; | 31 | int print_seq; |
29 | bool show_dso; | 32 | bool show_dso; |
30 | bool show_headers; | 33 | bool show_headers; |
@@ -60,7 +63,7 @@ static int hist_browser__get_folding(struct hist_browser *browser) | |||
60 | struct hist_entry *he = | 63 | struct hist_entry *he = |
61 | rb_entry(nd, struct hist_entry, rb_node); | 64 | rb_entry(nd, struct hist_entry, rb_node); |
62 | 65 | ||
63 | if (he->ms.unfolded) | 66 | if (he->unfolded) |
64 | unfolded_rows += he->nr_rows; | 67 | unfolded_rows += he->nr_rows; |
65 | } | 68 | } |
66 | return unfolded_rows; | 69 | return unfolded_rows; |
@@ -136,24 +139,19 @@ static char tree__folded_sign(bool unfolded) | |||
136 | return unfolded ? '-' : '+'; | 139 | return unfolded ? '-' : '+'; |
137 | } | 140 | } |
138 | 141 | ||
139 | static char map_symbol__folded(const struct map_symbol *ms) | ||
140 | { | ||
141 | return ms->has_children ? tree__folded_sign(ms->unfolded) : ' '; | ||
142 | } | ||
143 | |||
144 | static char hist_entry__folded(const struct hist_entry *he) | 142 | static char hist_entry__folded(const struct hist_entry *he) |
145 | { | 143 | { |
146 | return map_symbol__folded(&he->ms); | 144 | return he->has_children ? tree__folded_sign(he->unfolded) : ' '; |
147 | } | 145 | } |
148 | 146 | ||
149 | static char callchain_list__folded(const struct callchain_list *cl) | 147 | static char callchain_list__folded(const struct callchain_list *cl) |
150 | { | 148 | { |
151 | return map_symbol__folded(&cl->ms); | 149 | return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; |
152 | } | 150 | } |
153 | 151 | ||
154 | static void map_symbol__set_folding(struct map_symbol *ms, bool unfold) | 152 | static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) |
155 | { | 153 | { |
156 | ms->unfolded = unfold ? ms->has_children : false; | 154 | cl->unfolded = unfold ? cl->has_children : false; |
157 | } | 155 | } |
158 | 156 | ||
159 | static int callchain_node__count_rows_rb_tree(struct callchain_node *node) | 157 | static int callchain_node__count_rows_rb_tree(struct callchain_node *node) |
@@ -189,7 +187,7 @@ static int callchain_node__count_rows(struct callchain_node *node) | |||
189 | 187 | ||
190 | list_for_each_entry(chain, &node->val, list) { | 188 | list_for_each_entry(chain, &node->val, list) { |
191 | ++n; | 189 | ++n; |
192 | unfolded = chain->ms.unfolded; | 190 | unfolded = chain->unfolded; |
193 | } | 191 | } |
194 | 192 | ||
195 | if (unfolded) | 193 | if (unfolded) |
@@ -211,15 +209,27 @@ static int callchain__count_rows(struct rb_root *chain) | |||
211 | return n; | 209 | return n; |
212 | } | 210 | } |
213 | 211 | ||
214 | static bool map_symbol__toggle_fold(struct map_symbol *ms) | 212 | static bool hist_entry__toggle_fold(struct hist_entry *he) |
215 | { | 213 | { |
216 | if (!ms) | 214 | if (!he) |
217 | return false; | 215 | return false; |
218 | 216 | ||
219 | if (!ms->has_children) | 217 | if (!he->has_children) |
220 | return false; | 218 | return false; |
221 | 219 | ||
222 | ms->unfolded = !ms->unfolded; | 220 | he->unfolded = !he->unfolded; |
221 | return true; | ||
222 | } | ||
223 | |||
224 | static bool callchain_list__toggle_fold(struct callchain_list *cl) | ||
225 | { | ||
226 | if (!cl) | ||
227 | return false; | ||
228 | |||
229 | if (!cl->has_children) | ||
230 | return false; | ||
231 | |||
232 | cl->unfolded = !cl->unfolded; | ||
223 | return true; | 233 | return true; |
224 | } | 234 | } |
225 | 235 | ||
@@ -235,10 +245,10 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no | |||
235 | list_for_each_entry(chain, &child->val, list) { | 245 | list_for_each_entry(chain, &child->val, list) { |
236 | if (first) { | 246 | if (first) { |
237 | first = false; | 247 | first = false; |
238 | chain->ms.has_children = chain->list.next != &child->val || | 248 | chain->has_children = chain->list.next != &child->val || |
239 | !RB_EMPTY_ROOT(&child->rb_root); | 249 | !RB_EMPTY_ROOT(&child->rb_root); |
240 | } else | 250 | } else |
241 | chain->ms.has_children = chain->list.next == &child->val && | 251 | chain->has_children = chain->list.next == &child->val && |
242 | !RB_EMPTY_ROOT(&child->rb_root); | 252 | !RB_EMPTY_ROOT(&child->rb_root); |
243 | } | 253 | } |
244 | 254 | ||
@@ -252,11 +262,11 @@ static void callchain_node__init_have_children(struct callchain_node *node, | |||
252 | struct callchain_list *chain; | 262 | struct callchain_list *chain; |
253 | 263 | ||
254 | chain = list_entry(node->val.next, struct callchain_list, list); | 264 | chain = list_entry(node->val.next, struct callchain_list, list); |
255 | chain->ms.has_children = has_sibling; | 265 | chain->has_children = has_sibling; |
256 | 266 | ||
257 | if (!list_empty(&node->val)) { | 267 | if (!list_empty(&node->val)) { |
258 | chain = list_entry(node->val.prev, struct callchain_list, list); | 268 | chain = list_entry(node->val.prev, struct callchain_list, list); |
259 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); | 269 | chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); |
260 | } | 270 | } |
261 | 271 | ||
262 | callchain_node__init_have_children_rb_tree(node); | 272 | callchain_node__init_have_children_rb_tree(node); |
@@ -276,7 +286,7 @@ static void callchain__init_have_children(struct rb_root *root) | |||
276 | static void hist_entry__init_have_children(struct hist_entry *he) | 286 | static void hist_entry__init_have_children(struct hist_entry *he) |
277 | { | 287 | { |
278 | if (!he->init_have_children) { | 288 | if (!he->init_have_children) { |
279 | he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain); | 289 | he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); |
280 | callchain__init_have_children(&he->sorted_chain); | 290 | callchain__init_have_children(&he->sorted_chain); |
281 | he->init_have_children = true; | 291 | he->init_have_children = true; |
282 | } | 292 | } |
@@ -284,14 +294,22 @@ static void hist_entry__init_have_children(struct hist_entry *he) | |||
284 | 294 | ||
285 | static bool hist_browser__toggle_fold(struct hist_browser *browser) | 295 | static bool hist_browser__toggle_fold(struct hist_browser *browser) |
286 | { | 296 | { |
287 | if (map_symbol__toggle_fold(browser->selection)) { | 297 | struct hist_entry *he = browser->he_selection; |
288 | struct hist_entry *he = browser->he_selection; | 298 | struct map_symbol *ms = browser->selection; |
299 | struct callchain_list *cl = container_of(ms, struct callchain_list, ms); | ||
300 | bool has_children; | ||
289 | 301 | ||
302 | if (ms == &he->ms) | ||
303 | has_children = hist_entry__toggle_fold(he); | ||
304 | else | ||
305 | has_children = callchain_list__toggle_fold(cl); | ||
306 | |||
307 | if (has_children) { | ||
290 | hist_entry__init_have_children(he); | 308 | hist_entry__init_have_children(he); |
291 | browser->b.nr_entries -= he->nr_rows; | 309 | browser->b.nr_entries -= he->nr_rows; |
292 | browser->nr_callchain_rows -= he->nr_rows; | 310 | browser->nr_callchain_rows -= he->nr_rows; |
293 | 311 | ||
294 | if (he->ms.unfolded) | 312 | if (he->unfolded) |
295 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | 313 | he->nr_rows = callchain__count_rows(&he->sorted_chain); |
296 | else | 314 | else |
297 | he->nr_rows = 0; | 315 | he->nr_rows = 0; |
@@ -318,8 +336,8 @@ static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool | |||
318 | 336 | ||
319 | list_for_each_entry(chain, &child->val, list) { | 337 | list_for_each_entry(chain, &child->val, list) { |
320 | ++n; | 338 | ++n; |
321 | map_symbol__set_folding(&chain->ms, unfold); | 339 | callchain_list__set_folding(chain, unfold); |
322 | has_children = chain->ms.has_children; | 340 | has_children = chain->has_children; |
323 | } | 341 | } |
324 | 342 | ||
325 | if (has_children) | 343 | if (has_children) |
@@ -337,8 +355,8 @@ static int callchain_node__set_folding(struct callchain_node *node, bool unfold) | |||
337 | 355 | ||
338 | list_for_each_entry(chain, &node->val, list) { | 356 | list_for_each_entry(chain, &node->val, list) { |
339 | ++n; | 357 | ++n; |
340 | map_symbol__set_folding(&chain->ms, unfold); | 358 | callchain_list__set_folding(chain, unfold); |
341 | has_children = chain->ms.has_children; | 359 | has_children = chain->has_children; |
342 | } | 360 | } |
343 | 361 | ||
344 | if (has_children) | 362 | if (has_children) |
@@ -363,9 +381,9 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold) | |||
363 | static void hist_entry__set_folding(struct hist_entry *he, bool unfold) | 381 | static void hist_entry__set_folding(struct hist_entry *he, bool unfold) |
364 | { | 382 | { |
365 | hist_entry__init_have_children(he); | 383 | hist_entry__init_have_children(he); |
366 | map_symbol__set_folding(&he->ms, unfold); | 384 | he->unfolded = unfold ? he->has_children : false; |
367 | 385 | ||
368 | if (he->ms.has_children) { | 386 | if (he->has_children) { |
369 | int n = callchain__set_folding(&he->sorted_chain, unfold); | 387 | int n = callchain__set_folding(&he->sorted_chain, unfold); |
370 | he->nr_rows = unfold ? n : 0; | 388 | he->nr_rows = unfold ? n : 0; |
371 | } else | 389 | } else |
@@ -406,11 +424,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) | |||
406 | "Or reduce the sampling frequency."); | 424 | "Or reduce the sampling frequency."); |
407 | } | 425 | } |
408 | 426 | ||
409 | static int hist_browser__run(struct hist_browser *browser, | 427 | static int hist_browser__run(struct hist_browser *browser) |
410 | struct hist_browser_timer *hbt) | ||
411 | { | 428 | { |
412 | int key; | 429 | int key; |
413 | char title[160]; | 430 | char title[160]; |
431 | struct hist_browser_timer *hbt = browser->hbt; | ||
414 | int delay_secs = hbt ? hbt->refresh : 0; | 432 | int delay_secs = hbt ? hbt->refresh : 0; |
415 | 433 | ||
416 | browser->b.entries = &browser->hists->entries; | 434 | browser->b.entries = &browser->hists->entries; |
@@ -1016,7 +1034,7 @@ do_offset: | |||
1016 | if (offset > 0) { | 1034 | if (offset > 0) { |
1017 | do { | 1035 | do { |
1018 | h = rb_entry(nd, struct hist_entry, rb_node); | 1036 | h = rb_entry(nd, struct hist_entry, rb_node); |
1019 | if (h->ms.unfolded) { | 1037 | if (h->unfolded) { |
1020 | u16 remaining = h->nr_rows - h->row_offset; | 1038 | u16 remaining = h->nr_rows - h->row_offset; |
1021 | if (offset > remaining) { | 1039 | if (offset > remaining) { |
1022 | offset -= remaining; | 1040 | offset -= remaining; |
@@ -1037,7 +1055,7 @@ do_offset: | |||
1037 | } else if (offset < 0) { | 1055 | } else if (offset < 0) { |
1038 | while (1) { | 1056 | while (1) { |
1039 | h = rb_entry(nd, struct hist_entry, rb_node); | 1057 | h = rb_entry(nd, struct hist_entry, rb_node); |
1040 | if (h->ms.unfolded) { | 1058 | if (h->unfolded) { |
1041 | if (first) { | 1059 | if (first) { |
1042 | if (-offset > h->row_offset) { | 1060 | if (-offset > h->row_offset) { |
1043 | offset += h->row_offset; | 1061 | offset += h->row_offset; |
@@ -1074,7 +1092,7 @@ do_offset: | |||
1074 | * row_offset at its last entry. | 1092 | * row_offset at its last entry. |
1075 | */ | 1093 | */ |
1076 | h = rb_entry(nd, struct hist_entry, rb_node); | 1094 | h = rb_entry(nd, struct hist_entry, rb_node); |
1077 | if (h->ms.unfolded) | 1095 | if (h->unfolded) |
1078 | h->row_offset = h->nr_rows; | 1096 | h->row_offset = h->nr_rows; |
1079 | break; | 1097 | break; |
1080 | } | 1098 | } |
@@ -1195,7 +1213,9 @@ static int hist_browser__dump(struct hist_browser *browser) | |||
1195 | return 0; | 1213 | return 0; |
1196 | } | 1214 | } |
1197 | 1215 | ||
1198 | static struct hist_browser *hist_browser__new(struct hists *hists) | 1216 | static struct hist_browser *hist_browser__new(struct hists *hists, |
1217 | struct hist_browser_timer *hbt, | ||
1218 | struct perf_session_env *env) | ||
1199 | { | 1219 | { |
1200 | struct hist_browser *browser = zalloc(sizeof(*browser)); | 1220 | struct hist_browser *browser = zalloc(sizeof(*browser)); |
1201 | 1221 | ||
@@ -1206,6 +1226,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
1206 | browser->b.seek = ui_browser__hists_seek; | 1226 | browser->b.seek = ui_browser__hists_seek; |
1207 | browser->b.use_navkeypressed = true; | 1227 | browser->b.use_navkeypressed = true; |
1208 | browser->show_headers = symbol_conf.show_hist_headers; | 1228 | browser->show_headers = symbol_conf.show_hist_headers; |
1229 | browser->hbt = hbt; | ||
1230 | browser->env = env; | ||
1209 | } | 1231 | } |
1210 | 1232 | ||
1211 | return browser; | 1233 | return browser; |
@@ -1395,6 +1417,257 @@ close_file_and_continue: | |||
1395 | return ret; | 1417 | return ret; |
1396 | } | 1418 | } |
1397 | 1419 | ||
1420 | struct popup_action { | ||
1421 | struct thread *thread; | ||
1422 | struct dso *dso; | ||
1423 | struct map_symbol ms; | ||
1424 | |||
1425 | int (*fn)(struct hist_browser *browser, struct popup_action *act); | ||
1426 | }; | ||
1427 | |||
1428 | static int | ||
1429 | do_annotate(struct hist_browser *browser, struct popup_action *act) | ||
1430 | { | ||
1431 | struct perf_evsel *evsel; | ||
1432 | struct annotation *notes; | ||
1433 | struct hist_entry *he; | ||
1434 | int err; | ||
1435 | |||
1436 | if (!objdump_path && perf_session_env__lookup_objdump(browser->env)) | ||
1437 | return 0; | ||
1438 | |||
1439 | notes = symbol__annotation(act->ms.sym); | ||
1440 | if (!notes->src) | ||
1441 | return 0; | ||
1442 | |||
1443 | evsel = hists_to_evsel(browser->hists); | ||
1444 | err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt); | ||
1445 | he = hist_browser__selected_entry(browser); | ||
1446 | /* | ||
1447 | * offer option to annotate the other branch source or target | ||
1448 | * (if they exists) when returning from annotate | ||
1449 | */ | ||
1450 | if ((err == 'q' || err == CTRL('c')) && he->branch_info) | ||
1451 | return 1; | ||
1452 | |||
1453 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
1454 | if (err) | ||
1455 | ui_browser__handle_resize(&browser->b); | ||
1456 | return 0; | ||
1457 | } | ||
1458 | |||
1459 | static int | ||
1460 | add_annotate_opt(struct hist_browser *browser __maybe_unused, | ||
1461 | struct popup_action *act, char **optstr, | ||
1462 | struct map *map, struct symbol *sym) | ||
1463 | { | ||
1464 | if (sym == NULL || map->dso->annotate_warned) | ||
1465 | return 0; | ||
1466 | |||
1467 | if (asprintf(optstr, "Annotate %s", sym->name) < 0) | ||
1468 | return 0; | ||
1469 | |||
1470 | act->ms.map = map; | ||
1471 | act->ms.sym = sym; | ||
1472 | act->fn = do_annotate; | ||
1473 | return 1; | ||
1474 | } | ||
1475 | |||
1476 | static int | ||
1477 | do_zoom_thread(struct hist_browser *browser, struct popup_action *act) | ||
1478 | { | ||
1479 | struct thread *thread = act->thread; | ||
1480 | |||
1481 | if (browser->hists->thread_filter) { | ||
1482 | pstack__remove(browser->pstack, &browser->hists->thread_filter); | ||
1483 | perf_hpp__set_elide(HISTC_THREAD, false); | ||
1484 | thread__zput(browser->hists->thread_filter); | ||
1485 | ui_helpline__pop(); | ||
1486 | } else { | ||
1487 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | ||
1488 | thread->comm_set ? thread__comm_str(thread) : "", | ||
1489 | thread->tid); | ||
1490 | browser->hists->thread_filter = thread__get(thread); | ||
1491 | perf_hpp__set_elide(HISTC_THREAD, false); | ||
1492 | pstack__push(browser->pstack, &browser->hists->thread_filter); | ||
1493 | } | ||
1494 | |||
1495 | hists__filter_by_thread(browser->hists); | ||
1496 | hist_browser__reset(browser); | ||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | static int | ||
1501 | add_thread_opt(struct hist_browser *browser, struct popup_action *act, | ||
1502 | char **optstr, struct thread *thread) | ||
1503 | { | ||
1504 | if (thread == NULL) | ||
1505 | return 0; | ||
1506 | |||
1507 | if (asprintf(optstr, "Zoom %s %s(%d) thread", | ||
1508 | browser->hists->thread_filter ? "out of" : "into", | ||
1509 | thread->comm_set ? thread__comm_str(thread) : "", | ||
1510 | thread->tid) < 0) | ||
1511 | return 0; | ||
1512 | |||
1513 | act->thread = thread; | ||
1514 | act->fn = do_zoom_thread; | ||
1515 | return 1; | ||
1516 | } | ||
1517 | |||
1518 | static int | ||
1519 | do_zoom_dso(struct hist_browser *browser, struct popup_action *act) | ||
1520 | { | ||
1521 | struct dso *dso = act->dso; | ||
1522 | |||
1523 | if (browser->hists->dso_filter) { | ||
1524 | pstack__remove(browser->pstack, &browser->hists->dso_filter); | ||
1525 | perf_hpp__set_elide(HISTC_DSO, false); | ||
1526 | browser->hists->dso_filter = NULL; | ||
1527 | ui_helpline__pop(); | ||
1528 | } else { | ||
1529 | if (dso == NULL) | ||
1530 | return 0; | ||
1531 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | ||
1532 | dso->kernel ? "the Kernel" : dso->short_name); | ||
1533 | browser->hists->dso_filter = dso; | ||
1534 | perf_hpp__set_elide(HISTC_DSO, true); | ||
1535 | pstack__push(browser->pstack, &browser->hists->dso_filter); | ||
1536 | } | ||
1537 | |||
1538 | hists__filter_by_dso(browser->hists); | ||
1539 | hist_browser__reset(browser); | ||
1540 | return 0; | ||
1541 | } | ||
1542 | |||
1543 | static int | ||
1544 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, | ||
1545 | char **optstr, struct dso *dso) | ||
1546 | { | ||
1547 | if (dso == NULL) | ||
1548 | return 0; | ||
1549 | |||
1550 | if (asprintf(optstr, "Zoom %s %s DSO", | ||
1551 | browser->hists->dso_filter ? "out of" : "into", | ||
1552 | dso->kernel ? "the Kernel" : dso->short_name) < 0) | ||
1553 | return 0; | ||
1554 | |||
1555 | act->dso = dso; | ||
1556 | act->fn = do_zoom_dso; | ||
1557 | return 1; | ||
1558 | } | ||
1559 | |||
1560 | static int | ||
1561 | do_browse_map(struct hist_browser *browser __maybe_unused, | ||
1562 | struct popup_action *act) | ||
1563 | { | ||
1564 | map__browse(act->ms.map); | ||
1565 | return 0; | ||
1566 | } | ||
1567 | |||
1568 | static int | ||
1569 | add_map_opt(struct hist_browser *browser __maybe_unused, | ||
1570 | struct popup_action *act, char **optstr, struct map *map) | ||
1571 | { | ||
1572 | if (map == NULL) | ||
1573 | return 0; | ||
1574 | |||
1575 | if (asprintf(optstr, "Browse map details") < 0) | ||
1576 | return 0; | ||
1577 | |||
1578 | act->ms.map = map; | ||
1579 | act->fn = do_browse_map; | ||
1580 | return 1; | ||
1581 | } | ||
1582 | |||
1583 | static int | ||
1584 | do_run_script(struct hist_browser *browser __maybe_unused, | ||
1585 | struct popup_action *act) | ||
1586 | { | ||
1587 | char script_opt[64]; | ||
1588 | memset(script_opt, 0, sizeof(script_opt)); | ||
1589 | |||
1590 | if (act->thread) { | ||
1591 | scnprintf(script_opt, sizeof(script_opt), " -c %s ", | ||
1592 | thread__comm_str(act->thread)); | ||
1593 | } else if (act->ms.sym) { | ||
1594 | scnprintf(script_opt, sizeof(script_opt), " -S %s ", | ||
1595 | act->ms.sym->name); | ||
1596 | } | ||
1597 | |||
1598 | script_browse(script_opt); | ||
1599 | return 0; | ||
1600 | } | ||
1601 | |||
1602 | static int | ||
1603 | add_script_opt(struct hist_browser *browser __maybe_unused, | ||
1604 | struct popup_action *act, char **optstr, | ||
1605 | struct thread *thread, struct symbol *sym) | ||
1606 | { | ||
1607 | if (thread) { | ||
1608 | if (asprintf(optstr, "Run scripts for samples of thread [%s]", | ||
1609 | thread__comm_str(thread)) < 0) | ||
1610 | return 0; | ||
1611 | } else if (sym) { | ||
1612 | if (asprintf(optstr, "Run scripts for samples of symbol [%s]", | ||
1613 | sym->name) < 0) | ||
1614 | return 0; | ||
1615 | } else { | ||
1616 | if (asprintf(optstr, "Run scripts for all samples") < 0) | ||
1617 | return 0; | ||
1618 | } | ||
1619 | |||
1620 | act->thread = thread; | ||
1621 | act->ms.sym = sym; | ||
1622 | act->fn = do_run_script; | ||
1623 | return 1; | ||
1624 | } | ||
1625 | |||
1626 | static int | ||
1627 | do_switch_data(struct hist_browser *browser __maybe_unused, | ||
1628 | struct popup_action *act __maybe_unused) | ||
1629 | { | ||
1630 | if (switch_data_file()) { | ||
1631 | ui__warning("Won't switch the data files due to\n" | ||
1632 | "no valid data file get selected!\n"); | ||
1633 | return 0; | ||
1634 | } | ||
1635 | |||
1636 | return K_SWITCH_INPUT_DATA; | ||
1637 | } | ||
1638 | |||
1639 | static int | ||
1640 | add_switch_opt(struct hist_browser *browser, | ||
1641 | struct popup_action *act, char **optstr) | ||
1642 | { | ||
1643 | if (!is_report_browser(browser->hbt)) | ||
1644 | return 0; | ||
1645 | |||
1646 | if (asprintf(optstr, "Switch to another data file in PWD") < 0) | ||
1647 | return 0; | ||
1648 | |||
1649 | act->fn = do_switch_data; | ||
1650 | return 1; | ||
1651 | } | ||
1652 | |||
1653 | static int | ||
1654 | do_exit_browser(struct hist_browser *browser __maybe_unused, | ||
1655 | struct popup_action *act __maybe_unused) | ||
1656 | { | ||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | static int | ||
1661 | add_exit_opt(struct hist_browser *browser __maybe_unused, | ||
1662 | struct popup_action *act, char **optstr) | ||
1663 | { | ||
1664 | if (asprintf(optstr, "Exit") < 0) | ||
1665 | return 0; | ||
1666 | |||
1667 | act->fn = do_exit_browser; | ||
1668 | return 1; | ||
1669 | } | ||
1670 | |||
1398 | static void hist_browser__update_nr_entries(struct hist_browser *hb) | 1671 | static void hist_browser__update_nr_entries(struct hist_browser *hb) |
1399 | { | 1672 | { |
1400 | u64 nr_entries = 0; | 1673 | u64 nr_entries = 0; |
@@ -1421,14 +1694,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1421 | struct perf_session_env *env) | 1694 | struct perf_session_env *env) |
1422 | { | 1695 | { |
1423 | struct hists *hists = evsel__hists(evsel); | 1696 | struct hists *hists = evsel__hists(evsel); |
1424 | struct hist_browser *browser = hist_browser__new(hists); | 1697 | struct hist_browser *browser = hist_browser__new(hists, hbt, env); |
1425 | struct branch_info *bi; | 1698 | struct branch_info *bi; |
1426 | struct pstack *fstack; | 1699 | #define MAX_OPTIONS 16 |
1427 | char *options[16]; | 1700 | char *options[MAX_OPTIONS]; |
1701 | struct popup_action actions[MAX_OPTIONS]; | ||
1428 | int nr_options = 0; | 1702 | int nr_options = 0; |
1429 | int key = -1; | 1703 | int key = -1; |
1430 | char buf[64]; | 1704 | char buf[64]; |
1431 | char script_opt[64]; | ||
1432 | int delay_secs = hbt ? hbt->refresh : 0; | 1705 | int delay_secs = hbt ? hbt->refresh : 0; |
1433 | struct perf_hpp_fmt *fmt; | 1706 | struct perf_hpp_fmt *fmt; |
1434 | 1707 | ||
@@ -1473,13 +1746,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1473 | hist_browser__update_nr_entries(browser); | 1746 | hist_browser__update_nr_entries(browser); |
1474 | } | 1747 | } |
1475 | 1748 | ||
1476 | fstack = pstack__new(2); | 1749 | browser->pstack = pstack__new(2); |
1477 | if (fstack == NULL) | 1750 | if (browser->pstack == NULL) |
1478 | goto out; | 1751 | goto out; |
1479 | 1752 | ||
1480 | ui_helpline__push(helpline); | 1753 | ui_helpline__push(helpline); |
1481 | 1754 | ||
1482 | memset(options, 0, sizeof(options)); | 1755 | memset(options, 0, sizeof(options)); |
1756 | memset(actions, 0, sizeof(actions)); | ||
1483 | 1757 | ||
1484 | perf_hpp__for_each_format(fmt) | 1758 | perf_hpp__for_each_format(fmt) |
1485 | perf_hpp__reset_width(fmt, hists); | 1759 | perf_hpp__reset_width(fmt, hists); |
@@ -1489,16 +1763,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1489 | 1763 | ||
1490 | while (1) { | 1764 | while (1) { |
1491 | struct thread *thread = NULL; | 1765 | struct thread *thread = NULL; |
1492 | const struct dso *dso = NULL; | 1766 | struct dso *dso = NULL; |
1493 | int choice = 0, | 1767 | int choice = 0; |
1494 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | ||
1495 | annotate_f = -2, annotate_t = -2, browse_map = -2; | ||
1496 | int scripts_comm = -2, scripts_symbol = -2, | ||
1497 | scripts_all = -2, switch_data = -2; | ||
1498 | 1768 | ||
1499 | nr_options = 0; | 1769 | nr_options = 0; |
1500 | 1770 | ||
1501 | key = hist_browser__run(browser, hbt); | 1771 | key = hist_browser__run(browser); |
1502 | 1772 | ||
1503 | if (browser->he_selection != NULL) { | 1773 | if (browser->he_selection != NULL) { |
1504 | thread = hist_browser__selected_thread(browser); | 1774 | thread = hist_browser__selected_thread(browser); |
@@ -1526,17 +1796,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1526 | browser->selection->sym == NULL || | 1796 | browser->selection->sym == NULL || |
1527 | browser->selection->map->dso->annotate_warned) | 1797 | browser->selection->map->dso->annotate_warned) |
1528 | continue; | 1798 | continue; |
1529 | goto do_annotate; | 1799 | |
1800 | actions->ms.map = browser->selection->map; | ||
1801 | actions->ms.sym = browser->selection->sym; | ||
1802 | do_annotate(browser, actions); | ||
1803 | continue; | ||
1530 | case 'P': | 1804 | case 'P': |
1531 | hist_browser__dump(browser); | 1805 | hist_browser__dump(browser); |
1532 | continue; | 1806 | continue; |
1533 | case 'd': | 1807 | case 'd': |
1534 | goto zoom_dso; | 1808 | actions->dso = dso; |
1809 | do_zoom_dso(browser, actions); | ||
1810 | continue; | ||
1535 | case 'V': | 1811 | case 'V': |
1536 | browser->show_dso = !browser->show_dso; | 1812 | browser->show_dso = !browser->show_dso; |
1537 | continue; | 1813 | continue; |
1538 | case 't': | 1814 | case 't': |
1539 | goto zoom_thread; | 1815 | actions->thread = thread; |
1816 | do_zoom_thread(browser, actions); | ||
1817 | continue; | ||
1540 | case '/': | 1818 | case '/': |
1541 | if (ui_browser__input_window("Symbol to show", | 1819 | if (ui_browser__input_window("Symbol to show", |
1542 | "Please enter the name of symbol you want to see", | 1820 | "Please enter the name of symbol you want to see", |
@@ -1548,12 +1826,18 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1548 | } | 1826 | } |
1549 | continue; | 1827 | continue; |
1550 | case 'r': | 1828 | case 'r': |
1551 | if (is_report_browser(hbt)) | 1829 | if (is_report_browser(hbt)) { |
1552 | goto do_scripts; | 1830 | actions->thread = NULL; |
1831 | actions->ms.sym = NULL; | ||
1832 | do_run_script(browser, actions); | ||
1833 | } | ||
1553 | continue; | 1834 | continue; |
1554 | case 's': | 1835 | case 's': |
1555 | if (is_report_browser(hbt)) | 1836 | if (is_report_browser(hbt)) { |
1556 | goto do_data_switch; | 1837 | key = do_switch_data(browser, actions); |
1838 | if (key == K_SWITCH_INPUT_DATA) | ||
1839 | goto out_free_stack; | ||
1840 | } | ||
1557 | continue; | 1841 | continue; |
1558 | case 'i': | 1842 | case 'i': |
1559 | /* env->arch is NULL for live-mode (i.e. perf top) */ | 1843 | /* env->arch is NULL for live-mode (i.e. perf top) */ |
@@ -1583,7 +1867,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1583 | case K_LEFT: { | 1867 | case K_LEFT: { |
1584 | const void *top; | 1868 | const void *top; |
1585 | 1869 | ||
1586 | if (pstack__empty(fstack)) { | 1870 | if (pstack__empty(browser->pstack)) { |
1587 | /* | 1871 | /* |
1588 | * Go back to the perf_evsel_menu__run or other user | 1872 | * Go back to the perf_evsel_menu__run or other user |
1589 | */ | 1873 | */ |
@@ -1591,11 +1875,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1591 | goto out_free_stack; | 1875 | goto out_free_stack; |
1592 | continue; | 1876 | continue; |
1593 | } | 1877 | } |
1594 | top = pstack__pop(fstack); | 1878 | top = pstack__peek(browser->pstack); |
1595 | if (top == &browser->hists->dso_filter) | 1879 | if (top == &browser->hists->dso_filter) { |
1596 | goto zoom_out_dso; | 1880 | /* |
1881 | * No need to set actions->dso here since | ||
1882 | * it's just to remove the current filter. | ||
1883 | * Ditto for thread below. | ||
1884 | */ | ||
1885 | do_zoom_dso(browser, actions); | ||
1886 | } | ||
1597 | if (top == &browser->hists->thread_filter) | 1887 | if (top == &browser->hists->thread_filter) |
1598 | goto zoom_out_thread; | 1888 | do_zoom_thread(browser, actions); |
1599 | continue; | 1889 | continue; |
1600 | } | 1890 | } |
1601 | case K_ESC: | 1891 | case K_ESC: |
@@ -1623,196 +1913,71 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1623 | if (bi == NULL) | 1913 | if (bi == NULL) |
1624 | goto skip_annotation; | 1914 | goto skip_annotation; |
1625 | 1915 | ||
1626 | if (bi->from.sym != NULL && | 1916 | nr_options += add_annotate_opt(browser, |
1627 | !bi->from.map->dso->annotate_warned && | 1917 | &actions[nr_options], |
1628 | asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) { | 1918 | &options[nr_options], |
1629 | annotate_f = nr_options++; | 1919 | bi->from.map, |
1630 | } | 1920 | bi->from.sym); |
1631 | 1921 | if (bi->to.sym != bi->from.sym) | |
1632 | if (bi->to.sym != NULL && | 1922 | nr_options += add_annotate_opt(browser, |
1633 | !bi->to.map->dso->annotate_warned && | 1923 | &actions[nr_options], |
1634 | (bi->to.sym != bi->from.sym || | 1924 | &options[nr_options], |
1635 | bi->to.map->dso != bi->from.map->dso) && | 1925 | bi->to.map, |
1636 | asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) { | 1926 | bi->to.sym); |
1637 | annotate_t = nr_options++; | ||
1638 | } | ||
1639 | } else { | 1927 | } else { |
1640 | if (browser->selection->sym != NULL && | 1928 | nr_options += add_annotate_opt(browser, |
1641 | !browser->selection->map->dso->annotate_warned) { | 1929 | &actions[nr_options], |
1642 | struct annotation *notes; | 1930 | &options[nr_options], |
1643 | 1931 | browser->selection->map, | |
1644 | notes = symbol__annotation(browser->selection->sym); | 1932 | browser->selection->sym); |
1645 | |||
1646 | if (notes->src && | ||
1647 | asprintf(&options[nr_options], "Annotate %s", | ||
1648 | browser->selection->sym->name) > 0) { | ||
1649 | annotate = nr_options++; | ||
1650 | } | ||
1651 | } | ||
1652 | } | 1933 | } |
1653 | skip_annotation: | 1934 | skip_annotation: |
1654 | if (thread != NULL && | 1935 | nr_options += add_thread_opt(browser, &actions[nr_options], |
1655 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 1936 | &options[nr_options], thread); |
1656 | (browser->hists->thread_filter ? "out of" : "into"), | 1937 | nr_options += add_dso_opt(browser, &actions[nr_options], |
1657 | (thread->comm_set ? thread__comm_str(thread) : ""), | 1938 | &options[nr_options], dso); |
1658 | thread->tid) > 0) | 1939 | nr_options += add_map_opt(browser, &actions[nr_options], |
1659 | zoom_thread = nr_options++; | 1940 | &options[nr_options], |
1660 | 1941 | browser->selection->map); | |
1661 | if (dso != NULL && | ||
1662 | asprintf(&options[nr_options], "Zoom %s %s DSO", | ||
1663 | (browser->hists->dso_filter ? "out of" : "into"), | ||
1664 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | ||
1665 | zoom_dso = nr_options++; | ||
1666 | |||
1667 | if (browser->selection != NULL && | ||
1668 | browser->selection->map != NULL && | ||
1669 | asprintf(&options[nr_options], "Browse map details") > 0) | ||
1670 | browse_map = nr_options++; | ||
1671 | 1942 | ||
1672 | /* perf script support */ | 1943 | /* perf script support */ |
1673 | if (browser->he_selection) { | 1944 | if (browser->he_selection) { |
1674 | struct symbol *sym; | 1945 | nr_options += add_script_opt(browser, |
1675 | 1946 | &actions[nr_options], | |
1676 | if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", | 1947 | &options[nr_options], |
1677 | thread__comm_str(browser->he_selection->thread)) > 0) | 1948 | thread, NULL); |
1678 | scripts_comm = nr_options++; | 1949 | nr_options += add_script_opt(browser, |
1679 | 1950 | &actions[nr_options], | |
1680 | sym = browser->he_selection->ms.sym; | 1951 | &options[nr_options], |
1681 | if (sym && sym->namelen && | 1952 | NULL, browser->selection->sym); |
1682 | asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", | ||
1683 | sym->name) > 0) | ||
1684 | scripts_symbol = nr_options++; | ||
1685 | } | 1953 | } |
1686 | 1954 | nr_options += add_script_opt(browser, &actions[nr_options], | |
1687 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) | 1955 | &options[nr_options], NULL, NULL); |
1688 | scripts_all = nr_options++; | 1956 | nr_options += add_switch_opt(browser, &actions[nr_options], |
1689 | 1957 | &options[nr_options]); | |
1690 | if (is_report_browser(hbt) && asprintf(&options[nr_options], | ||
1691 | "Switch to another data file in PWD") > 0) | ||
1692 | switch_data = nr_options++; | ||
1693 | add_exit_option: | 1958 | add_exit_option: |
1694 | options[nr_options++] = (char *)"Exit"; | 1959 | nr_options += add_exit_opt(browser, &actions[nr_options], |
1695 | retry_popup_menu: | 1960 | &options[nr_options]); |
1696 | choice = ui__popup_menu(nr_options, options); | ||
1697 | |||
1698 | if (choice == nr_options - 1) | ||
1699 | break; | ||
1700 | 1961 | ||
1701 | if (choice == -1) { | 1962 | do { |
1702 | free_popup_options(options, nr_options - 1); | 1963 | struct popup_action *act; |
1703 | continue; | ||
1704 | } | ||
1705 | |||
1706 | if (choice == annotate || choice == annotate_t || choice == annotate_f) { | ||
1707 | struct hist_entry *he; | ||
1708 | struct annotation *notes; | ||
1709 | struct map_symbol ms; | ||
1710 | int err; | ||
1711 | do_annotate: | ||
1712 | if (!objdump_path && perf_session_env__lookup_objdump(env)) | ||
1713 | continue; | ||
1714 | |||
1715 | he = hist_browser__selected_entry(browser); | ||
1716 | if (he == NULL) | ||
1717 | continue; | ||
1718 | |||
1719 | if (choice == annotate_f) { | ||
1720 | ms.map = he->branch_info->from.map; | ||
1721 | ms.sym = he->branch_info->from.sym; | ||
1722 | } else if (choice == annotate_t) { | ||
1723 | ms.map = he->branch_info->to.map; | ||
1724 | ms.sym = he->branch_info->to.sym; | ||
1725 | } else { | ||
1726 | ms = *browser->selection; | ||
1727 | } | ||
1728 | |||
1729 | notes = symbol__annotation(ms.sym); | ||
1730 | if (!notes->src) | ||
1731 | continue; | ||
1732 | |||
1733 | err = map_symbol__tui_annotate(&ms, evsel, hbt); | ||
1734 | /* | ||
1735 | * offer option to annotate the other branch source or target | ||
1736 | * (if they exists) when returning from annotate | ||
1737 | */ | ||
1738 | if ((err == 'q' || err == CTRL('c')) | ||
1739 | && annotate_t != -2 && annotate_f != -2) | ||
1740 | goto retry_popup_menu; | ||
1741 | |||
1742 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
1743 | if (err) | ||
1744 | ui_browser__handle_resize(&browser->b); | ||
1745 | |||
1746 | } else if (choice == browse_map) | ||
1747 | map__browse(browser->selection->map); | ||
1748 | else if (choice == zoom_dso) { | ||
1749 | zoom_dso: | ||
1750 | if (browser->hists->dso_filter) { | ||
1751 | pstack__remove(fstack, &browser->hists->dso_filter); | ||
1752 | zoom_out_dso: | ||
1753 | ui_helpline__pop(); | ||
1754 | browser->hists->dso_filter = NULL; | ||
1755 | perf_hpp__set_elide(HISTC_DSO, false); | ||
1756 | } else { | ||
1757 | if (dso == NULL) | ||
1758 | continue; | ||
1759 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | ||
1760 | dso->kernel ? "the Kernel" : dso->short_name); | ||
1761 | browser->hists->dso_filter = dso; | ||
1762 | perf_hpp__set_elide(HISTC_DSO, true); | ||
1763 | pstack__push(fstack, &browser->hists->dso_filter); | ||
1764 | } | ||
1765 | hists__filter_by_dso(hists); | ||
1766 | hist_browser__reset(browser); | ||
1767 | } else if (choice == zoom_thread) { | ||
1768 | zoom_thread: | ||
1769 | if (browser->hists->thread_filter) { | ||
1770 | pstack__remove(fstack, &browser->hists->thread_filter); | ||
1771 | zoom_out_thread: | ||
1772 | ui_helpline__pop(); | ||
1773 | thread__zput(browser->hists->thread_filter); | ||
1774 | perf_hpp__set_elide(HISTC_THREAD, false); | ||
1775 | } else { | ||
1776 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | ||
1777 | thread->comm_set ? thread__comm_str(thread) : "", | ||
1778 | thread->tid); | ||
1779 | browser->hists->thread_filter = thread__get(thread); | ||
1780 | perf_hpp__set_elide(HISTC_THREAD, false); | ||
1781 | pstack__push(fstack, &browser->hists->thread_filter); | ||
1782 | } | ||
1783 | hists__filter_by_thread(hists); | ||
1784 | hist_browser__reset(browser); | ||
1785 | } | ||
1786 | /* perf scripts support */ | ||
1787 | else if (choice == scripts_all || choice == scripts_comm || | ||
1788 | choice == scripts_symbol) { | ||
1789 | do_scripts: | ||
1790 | memset(script_opt, 0, 64); | ||
1791 | 1964 | ||
1792 | if (choice == scripts_comm) | 1965 | choice = ui__popup_menu(nr_options, options); |
1793 | sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread)); | 1966 | if (choice == -1 || choice >= nr_options) |
1967 | break; | ||
1794 | 1968 | ||
1795 | if (choice == scripts_symbol) | 1969 | act = &actions[choice]; |
1796 | sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); | 1970 | key = act->fn(browser, act); |
1971 | } while (key == 1); | ||
1797 | 1972 | ||
1798 | script_browse(script_opt); | 1973 | if (key == K_SWITCH_INPUT_DATA) |
1799 | } | 1974 | break; |
1800 | /* Switch to another data file */ | ||
1801 | else if (choice == switch_data) { | ||
1802 | do_data_switch: | ||
1803 | if (!switch_data_file()) { | ||
1804 | key = K_SWITCH_INPUT_DATA; | ||
1805 | break; | ||
1806 | } else | ||
1807 | ui__warning("Won't switch the data files due to\n" | ||
1808 | "no valid data file get selected!\n"); | ||
1809 | } | ||
1810 | } | 1975 | } |
1811 | out_free_stack: | 1976 | out_free_stack: |
1812 | pstack__delete(fstack); | 1977 | pstack__delete(browser->pstack); |
1813 | out: | 1978 | out: |
1814 | hist_browser__delete(browser); | 1979 | hist_browser__delete(browser); |
1815 | free_popup_options(options, nr_options - 1); | 1980 | free_popup_options(options, MAX_OPTIONS); |
1816 | return key; | 1981 | return key; |
1817 | } | 1982 | } |
1818 | 1983 | ||
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 797490a40075..d552203aead0 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -74,6 +74,7 @@ libperf-y += data.o | |||
74 | libperf-$(CONFIG_X86) += tsc.o | 74 | libperf-$(CONFIG_X86) += tsc.o |
75 | libperf-y += cloexec.o | 75 | libperf-y += cloexec.o |
76 | libperf-y += thread-stack.o | 76 | libperf-y += thread-stack.o |
77 | libperf-$(CONFIG_AUXTRACE) += auxtrace.o | ||
77 | 78 | ||
78 | libperf-$(CONFIG_LIBELF) += symbol-elf.o | 79 | libperf-$(CONFIG_LIBELF) += symbol-elf.o |
79 | libperf-$(CONFIG_LIBELF) += probe-event.o | 80 | libperf-$(CONFIG_LIBELF) += probe-event.o |
@@ -117,7 +118,7 @@ $(OUTPUT)util/pmu-bison.c: util/pmu.y | |||
117 | 118 | ||
118 | CFLAGS_parse-events-flex.o += -w | 119 | CFLAGS_parse-events-flex.o += -w |
119 | CFLAGS_pmu-flex.o += -w | 120 | CFLAGS_pmu-flex.o += -w |
120 | CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w | 121 | CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w |
121 | CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w | 122 | CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w |
122 | 123 | ||
123 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c | 124 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c |
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c new file mode 100644 index 000000000000..df66966cfde7 --- /dev/null +++ b/tools/perf/util/auxtrace.c | |||
@@ -0,0 +1,1352 @@ | |||
1 | /* | ||
2 | * auxtrace.c: AUX area trace support | ||
3 | * Copyright (c) 2013-2015, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <sys/types.h> | ||
17 | #include <sys/mman.h> | ||
18 | #include <stdbool.h> | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/perf_event.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/bitops.h> | ||
24 | #include <linux/log2.h> | ||
25 | #include <linux/string.h> | ||
26 | |||
27 | #include <sys/param.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <stdio.h> | ||
30 | #include <string.h> | ||
31 | #include <limits.h> | ||
32 | #include <errno.h> | ||
33 | #include <linux/list.h> | ||
34 | |||
35 | #include "../perf.h" | ||
36 | #include "util.h" | ||
37 | #include "evlist.h" | ||
38 | #include "cpumap.h" | ||
39 | #include "thread_map.h" | ||
40 | #include "asm/bug.h" | ||
41 | #include "auxtrace.h" | ||
42 | |||
43 | #include <linux/hash.h> | ||
44 | |||
45 | #include "event.h" | ||
46 | #include "session.h" | ||
47 | #include "debug.h" | ||
48 | #include "parse-options.h" | ||
49 | |||
50 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, | ||
51 | struct auxtrace_mmap_params *mp, | ||
52 | void *userpg, int fd) | ||
53 | { | ||
54 | struct perf_event_mmap_page *pc = userpg; | ||
55 | |||
56 | #if BITS_PER_LONG != 64 && !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) | ||
57 | pr_err("Cannot use AUX area tracing mmaps\n"); | ||
58 | return -1; | ||
59 | #endif | ||
60 | |||
61 | WARN_ONCE(mm->base, "Uninitialized auxtrace_mmap\n"); | ||
62 | |||
63 | mm->userpg = userpg; | ||
64 | mm->mask = mp->mask; | ||
65 | mm->len = mp->len; | ||
66 | mm->prev = 0; | ||
67 | mm->idx = mp->idx; | ||
68 | mm->tid = mp->tid; | ||
69 | mm->cpu = mp->cpu; | ||
70 | |||
71 | if (!mp->len) { | ||
72 | mm->base = NULL; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | pc->aux_offset = mp->offset; | ||
77 | pc->aux_size = mp->len; | ||
78 | |||
79 | mm->base = mmap(NULL, mp->len, mp->prot, MAP_SHARED, fd, mp->offset); | ||
80 | if (mm->base == MAP_FAILED) { | ||
81 | pr_debug2("failed to mmap AUX area\n"); | ||
82 | mm->base = NULL; | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | void auxtrace_mmap__munmap(struct auxtrace_mmap *mm) | ||
90 | { | ||
91 | if (mm->base) { | ||
92 | munmap(mm->base, mm->len); | ||
93 | mm->base = NULL; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, | ||
98 | off_t auxtrace_offset, | ||
99 | unsigned int auxtrace_pages, | ||
100 | bool auxtrace_overwrite) | ||
101 | { | ||
102 | if (auxtrace_pages) { | ||
103 | mp->offset = auxtrace_offset; | ||
104 | mp->len = auxtrace_pages * (size_t)page_size; | ||
105 | mp->mask = is_power_of_2(mp->len) ? mp->len - 1 : 0; | ||
106 | mp->prot = PROT_READ | (auxtrace_overwrite ? 0 : PROT_WRITE); | ||
107 | pr_debug2("AUX area mmap length %zu\n", mp->len); | ||
108 | } else { | ||
109 | mp->len = 0; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | ||
114 | struct perf_evlist *evlist, int idx, | ||
115 | bool per_cpu) | ||
116 | { | ||
117 | mp->idx = idx; | ||
118 | |||
119 | if (per_cpu) { | ||
120 | mp->cpu = evlist->cpus->map[idx]; | ||
121 | if (evlist->threads) | ||
122 | mp->tid = evlist->threads->map[0]; | ||
123 | else | ||
124 | mp->tid = -1; | ||
125 | } else { | ||
126 | mp->cpu = -1; | ||
127 | mp->tid = evlist->threads->map[idx]; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | #define AUXTRACE_INIT_NR_QUEUES 32 | ||
132 | |||
133 | static struct auxtrace_queue *auxtrace_alloc_queue_array(unsigned int nr_queues) | ||
134 | { | ||
135 | struct auxtrace_queue *queue_array; | ||
136 | unsigned int max_nr_queues, i; | ||
137 | |||
138 | max_nr_queues = UINT_MAX / sizeof(struct auxtrace_queue); | ||
139 | if (nr_queues > max_nr_queues) | ||
140 | return NULL; | ||
141 | |||
142 | queue_array = calloc(nr_queues, sizeof(struct auxtrace_queue)); | ||
143 | if (!queue_array) | ||
144 | return NULL; | ||
145 | |||
146 | for (i = 0; i < nr_queues; i++) { | ||
147 | INIT_LIST_HEAD(&queue_array[i].head); | ||
148 | queue_array[i].priv = NULL; | ||
149 | } | ||
150 | |||
151 | return queue_array; | ||
152 | } | ||
153 | |||
154 | int auxtrace_queues__init(struct auxtrace_queues *queues) | ||
155 | { | ||
156 | queues->nr_queues = AUXTRACE_INIT_NR_QUEUES; | ||
157 | queues->queue_array = auxtrace_alloc_queue_array(queues->nr_queues); | ||
158 | if (!queues->queue_array) | ||
159 | return -ENOMEM; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int auxtrace_queues__grow(struct auxtrace_queues *queues, | ||
164 | unsigned int new_nr_queues) | ||
165 | { | ||
166 | unsigned int nr_queues = queues->nr_queues; | ||
167 | struct auxtrace_queue *queue_array; | ||
168 | unsigned int i; | ||
169 | |||
170 | if (!nr_queues) | ||
171 | nr_queues = AUXTRACE_INIT_NR_QUEUES; | ||
172 | |||
173 | while (nr_queues && nr_queues < new_nr_queues) | ||
174 | nr_queues <<= 1; | ||
175 | |||
176 | if (nr_queues < queues->nr_queues || nr_queues < new_nr_queues) | ||
177 | return -EINVAL; | ||
178 | |||
179 | queue_array = auxtrace_alloc_queue_array(nr_queues); | ||
180 | if (!queue_array) | ||
181 | return -ENOMEM; | ||
182 | |||
183 | for (i = 0; i < queues->nr_queues; i++) { | ||
184 | list_splice_tail(&queues->queue_array[i].head, | ||
185 | &queue_array[i].head); | ||
186 | queue_array[i].priv = queues->queue_array[i].priv; | ||
187 | } | ||
188 | |||
189 | queues->nr_queues = nr_queues; | ||
190 | queues->queue_array = queue_array; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static void *auxtrace_copy_data(u64 size, struct perf_session *session) | ||
196 | { | ||
197 | int fd = perf_data_file__fd(session->file); | ||
198 | void *p; | ||
199 | ssize_t ret; | ||
200 | |||
201 | if (size > SSIZE_MAX) | ||
202 | return NULL; | ||
203 | |||
204 | p = malloc(size); | ||
205 | if (!p) | ||
206 | return NULL; | ||
207 | |||
208 | ret = readn(fd, p, size); | ||
209 | if (ret != (ssize_t)size) { | ||
210 | free(p); | ||
211 | return NULL; | ||
212 | } | ||
213 | |||
214 | return p; | ||
215 | } | ||
216 | |||
217 | static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues, | ||
218 | unsigned int idx, | ||
219 | struct auxtrace_buffer *buffer) | ||
220 | { | ||
221 | struct auxtrace_queue *queue; | ||
222 | int err; | ||
223 | |||
224 | if (idx >= queues->nr_queues) { | ||
225 | err = auxtrace_queues__grow(queues, idx + 1); | ||
226 | if (err) | ||
227 | return err; | ||
228 | } | ||
229 | |||
230 | queue = &queues->queue_array[idx]; | ||
231 | |||
232 | if (!queue->set) { | ||
233 | queue->set = true; | ||
234 | queue->tid = buffer->tid; | ||
235 | queue->cpu = buffer->cpu; | ||
236 | } else if (buffer->cpu != queue->cpu || buffer->tid != queue->tid) { | ||
237 | pr_err("auxtrace queue conflict: cpu %d, tid %d vs cpu %d, tid %d\n", | ||
238 | queue->cpu, queue->tid, buffer->cpu, buffer->tid); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | |||
242 | buffer->buffer_nr = queues->next_buffer_nr++; | ||
243 | |||
244 | list_add_tail(&buffer->list, &queue->head); | ||
245 | |||
246 | queues->new_data = true; | ||
247 | queues->populated = true; | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | /* Limit buffers to 32MiB on 32-bit */ | ||
253 | #define BUFFER_LIMIT_FOR_32_BIT (32 * 1024 * 1024) | ||
254 | |||
255 | static int auxtrace_queues__split_buffer(struct auxtrace_queues *queues, | ||
256 | unsigned int idx, | ||
257 | struct auxtrace_buffer *buffer) | ||
258 | { | ||
259 | u64 sz = buffer->size; | ||
260 | bool consecutive = false; | ||
261 | struct auxtrace_buffer *b; | ||
262 | int err; | ||
263 | |||
264 | while (sz > BUFFER_LIMIT_FOR_32_BIT) { | ||
265 | b = memdup(buffer, sizeof(struct auxtrace_buffer)); | ||
266 | if (!b) | ||
267 | return -ENOMEM; | ||
268 | b->size = BUFFER_LIMIT_FOR_32_BIT; | ||
269 | b->consecutive = consecutive; | ||
270 | err = auxtrace_queues__add_buffer(queues, idx, b); | ||
271 | if (err) { | ||
272 | auxtrace_buffer__free(b); | ||
273 | return err; | ||
274 | } | ||
275 | buffer->data_offset += BUFFER_LIMIT_FOR_32_BIT; | ||
276 | sz -= BUFFER_LIMIT_FOR_32_BIT; | ||
277 | consecutive = true; | ||
278 | } | ||
279 | |||
280 | buffer->size = sz; | ||
281 | buffer->consecutive = consecutive; | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int auxtrace_queues__add_event_buffer(struct auxtrace_queues *queues, | ||
287 | struct perf_session *session, | ||
288 | unsigned int idx, | ||
289 | struct auxtrace_buffer *buffer) | ||
290 | { | ||
291 | if (session->one_mmap) { | ||
292 | buffer->data = buffer->data_offset - session->one_mmap_offset + | ||
293 | session->one_mmap_addr; | ||
294 | } else if (perf_data_file__is_pipe(session->file)) { | ||
295 | buffer->data = auxtrace_copy_data(buffer->size, session); | ||
296 | if (!buffer->data) | ||
297 | return -ENOMEM; | ||
298 | buffer->data_needs_freeing = true; | ||
299 | } else if (BITS_PER_LONG == 32 && | ||
300 | buffer->size > BUFFER_LIMIT_FOR_32_BIT) { | ||
301 | int err; | ||
302 | |||
303 | err = auxtrace_queues__split_buffer(queues, idx, buffer); | ||
304 | if (err) | ||
305 | return err; | ||
306 | } | ||
307 | |||
308 | return auxtrace_queues__add_buffer(queues, idx, buffer); | ||
309 | } | ||
310 | |||
311 | int auxtrace_queues__add_event(struct auxtrace_queues *queues, | ||
312 | struct perf_session *session, | ||
313 | union perf_event *event, off_t data_offset, | ||
314 | struct auxtrace_buffer **buffer_ptr) | ||
315 | { | ||
316 | struct auxtrace_buffer *buffer; | ||
317 | unsigned int idx; | ||
318 | int err; | ||
319 | |||
320 | buffer = zalloc(sizeof(struct auxtrace_buffer)); | ||
321 | if (!buffer) | ||
322 | return -ENOMEM; | ||
323 | |||
324 | buffer->pid = -1; | ||
325 | buffer->tid = event->auxtrace.tid; | ||
326 | buffer->cpu = event->auxtrace.cpu; | ||
327 | buffer->data_offset = data_offset; | ||
328 | buffer->offset = event->auxtrace.offset; | ||
329 | buffer->reference = event->auxtrace.reference; | ||
330 | buffer->size = event->auxtrace.size; | ||
331 | idx = event->auxtrace.idx; | ||
332 | |||
333 | err = auxtrace_queues__add_event_buffer(queues, session, idx, buffer); | ||
334 | if (err) | ||
335 | goto out_err; | ||
336 | |||
337 | if (buffer_ptr) | ||
338 | *buffer_ptr = buffer; | ||
339 | |||
340 | return 0; | ||
341 | |||
342 | out_err: | ||
343 | auxtrace_buffer__free(buffer); | ||
344 | return err; | ||
345 | } | ||
346 | |||
347 | static int auxtrace_queues__add_indexed_event(struct auxtrace_queues *queues, | ||
348 | struct perf_session *session, | ||
349 | off_t file_offset, size_t sz) | ||
350 | { | ||
351 | union perf_event *event; | ||
352 | int err; | ||
353 | char buf[PERF_SAMPLE_MAX_SIZE]; | ||
354 | |||
355 | err = perf_session__peek_event(session, file_offset, buf, | ||
356 | PERF_SAMPLE_MAX_SIZE, &event, NULL); | ||
357 | if (err) | ||
358 | return err; | ||
359 | |||
360 | if (event->header.type == PERF_RECORD_AUXTRACE) { | ||
361 | if (event->header.size < sizeof(struct auxtrace_event) || | ||
362 | event->header.size != sz) { | ||
363 | err = -EINVAL; | ||
364 | goto out; | ||
365 | } | ||
366 | file_offset += event->header.size; | ||
367 | err = auxtrace_queues__add_event(queues, session, event, | ||
368 | file_offset, NULL); | ||
369 | } | ||
370 | out: | ||
371 | return err; | ||
372 | } | ||
373 | |||
374 | void auxtrace_queues__free(struct auxtrace_queues *queues) | ||
375 | { | ||
376 | unsigned int i; | ||
377 | |||
378 | for (i = 0; i < queues->nr_queues; i++) { | ||
379 | while (!list_empty(&queues->queue_array[i].head)) { | ||
380 | struct auxtrace_buffer *buffer; | ||
381 | |||
382 | buffer = list_entry(queues->queue_array[i].head.next, | ||
383 | struct auxtrace_buffer, list); | ||
384 | list_del(&buffer->list); | ||
385 | auxtrace_buffer__free(buffer); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | zfree(&queues->queue_array); | ||
390 | queues->nr_queues = 0; | ||
391 | } | ||
392 | |||
393 | static void auxtrace_heapify(struct auxtrace_heap_item *heap_array, | ||
394 | unsigned int pos, unsigned int queue_nr, | ||
395 | u64 ordinal) | ||
396 | { | ||
397 | unsigned int parent; | ||
398 | |||
399 | while (pos) { | ||
400 | parent = (pos - 1) >> 1; | ||
401 | if (heap_array[parent].ordinal <= ordinal) | ||
402 | break; | ||
403 | heap_array[pos] = heap_array[parent]; | ||
404 | pos = parent; | ||
405 | } | ||
406 | heap_array[pos].queue_nr = queue_nr; | ||
407 | heap_array[pos].ordinal = ordinal; | ||
408 | } | ||
409 | |||
410 | int auxtrace_heap__add(struct auxtrace_heap *heap, unsigned int queue_nr, | ||
411 | u64 ordinal) | ||
412 | { | ||
413 | struct auxtrace_heap_item *heap_array; | ||
414 | |||
415 | if (queue_nr >= heap->heap_sz) { | ||
416 | unsigned int heap_sz = AUXTRACE_INIT_NR_QUEUES; | ||
417 | |||
418 | while (heap_sz <= queue_nr) | ||
419 | heap_sz <<= 1; | ||
420 | heap_array = realloc(heap->heap_array, | ||
421 | heap_sz * sizeof(struct auxtrace_heap_item)); | ||
422 | if (!heap_array) | ||
423 | return -ENOMEM; | ||
424 | heap->heap_array = heap_array; | ||
425 | heap->heap_sz = heap_sz; | ||
426 | } | ||
427 | |||
428 | auxtrace_heapify(heap->heap_array, heap->heap_cnt++, queue_nr, ordinal); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | void auxtrace_heap__free(struct auxtrace_heap *heap) | ||
434 | { | ||
435 | zfree(&heap->heap_array); | ||
436 | heap->heap_cnt = 0; | ||
437 | heap->heap_sz = 0; | ||
438 | } | ||
439 | |||
440 | void auxtrace_heap__pop(struct auxtrace_heap *heap) | ||
441 | { | ||
442 | unsigned int pos, last, heap_cnt = heap->heap_cnt; | ||
443 | struct auxtrace_heap_item *heap_array; | ||
444 | |||
445 | if (!heap_cnt) | ||
446 | return; | ||
447 | |||
448 | heap->heap_cnt -= 1; | ||
449 | |||
450 | heap_array = heap->heap_array; | ||
451 | |||
452 | pos = 0; | ||
453 | while (1) { | ||
454 | unsigned int left, right; | ||
455 | |||
456 | left = (pos << 1) + 1; | ||
457 | if (left >= heap_cnt) | ||
458 | break; | ||
459 | right = left + 1; | ||
460 | if (right >= heap_cnt) { | ||
461 | heap_array[pos] = heap_array[left]; | ||
462 | return; | ||
463 | } | ||
464 | if (heap_array[left].ordinal < heap_array[right].ordinal) { | ||
465 | heap_array[pos] = heap_array[left]; | ||
466 | pos = left; | ||
467 | } else { | ||
468 | heap_array[pos] = heap_array[right]; | ||
469 | pos = right; | ||
470 | } | ||
471 | } | ||
472 | |||
473 | last = heap_cnt - 1; | ||
474 | auxtrace_heapify(heap_array, pos, heap_array[last].queue_nr, | ||
475 | heap_array[last].ordinal); | ||
476 | } | ||
477 | |||
478 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) | ||
479 | { | ||
480 | if (itr) | ||
481 | return itr->info_priv_size(itr); | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int auxtrace_not_supported(void) | ||
486 | { | ||
487 | pr_err("AUX area tracing is not supported on this architecture\n"); | ||
488 | return -EINVAL; | ||
489 | } | ||
490 | |||
491 | int auxtrace_record__info_fill(struct auxtrace_record *itr, | ||
492 | struct perf_session *session, | ||
493 | struct auxtrace_info_event *auxtrace_info, | ||
494 | size_t priv_size) | ||
495 | { | ||
496 | if (itr) | ||
497 | return itr->info_fill(itr, session, auxtrace_info, priv_size); | ||
498 | return auxtrace_not_supported(); | ||
499 | } | ||
500 | |||
501 | void auxtrace_record__free(struct auxtrace_record *itr) | ||
502 | { | ||
503 | if (itr) | ||
504 | itr->free(itr); | ||
505 | } | ||
506 | |||
507 | int auxtrace_record__snapshot_start(struct auxtrace_record *itr) | ||
508 | { | ||
509 | if (itr && itr->snapshot_start) | ||
510 | return itr->snapshot_start(itr); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | int auxtrace_record__snapshot_finish(struct auxtrace_record *itr) | ||
515 | { | ||
516 | if (itr && itr->snapshot_finish) | ||
517 | return itr->snapshot_finish(itr); | ||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx, | ||
522 | struct auxtrace_mmap *mm, | ||
523 | unsigned char *data, u64 *head, u64 *old) | ||
524 | { | ||
525 | if (itr && itr->find_snapshot) | ||
526 | return itr->find_snapshot(itr, idx, mm, data, head, old); | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | int auxtrace_record__options(struct auxtrace_record *itr, | ||
531 | struct perf_evlist *evlist, | ||
532 | struct record_opts *opts) | ||
533 | { | ||
534 | if (itr) | ||
535 | return itr->recording_options(itr, evlist, opts); | ||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | u64 auxtrace_record__reference(struct auxtrace_record *itr) | ||
540 | { | ||
541 | if (itr) | ||
542 | return itr->reference(itr); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, | ||
547 | struct record_opts *opts, const char *str) | ||
548 | { | ||
549 | if (!str) | ||
550 | return 0; | ||
551 | |||
552 | if (itr) | ||
553 | return itr->parse_snapshot_options(itr, opts, str); | ||
554 | |||
555 | pr_err("No AUX area tracing to snapshot\n"); | ||
556 | return -EINVAL; | ||
557 | } | ||
558 | |||
559 | struct auxtrace_record *__weak | ||
560 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err) | ||
561 | { | ||
562 | *err = 0; | ||
563 | return NULL; | ||
564 | } | ||
565 | |||
566 | static int auxtrace_index__alloc(struct list_head *head) | ||
567 | { | ||
568 | struct auxtrace_index *auxtrace_index; | ||
569 | |||
570 | auxtrace_index = malloc(sizeof(struct auxtrace_index)); | ||
571 | if (!auxtrace_index) | ||
572 | return -ENOMEM; | ||
573 | |||
574 | auxtrace_index->nr = 0; | ||
575 | INIT_LIST_HEAD(&auxtrace_index->list); | ||
576 | |||
577 | list_add_tail(&auxtrace_index->list, head); | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | void auxtrace_index__free(struct list_head *head) | ||
583 | { | ||
584 | struct auxtrace_index *auxtrace_index, *n; | ||
585 | |||
586 | list_for_each_entry_safe(auxtrace_index, n, head, list) { | ||
587 | list_del(&auxtrace_index->list); | ||
588 | free(auxtrace_index); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | static struct auxtrace_index *auxtrace_index__last(struct list_head *head) | ||
593 | { | ||
594 | struct auxtrace_index *auxtrace_index; | ||
595 | int err; | ||
596 | |||
597 | if (list_empty(head)) { | ||
598 | err = auxtrace_index__alloc(head); | ||
599 | if (err) | ||
600 | return NULL; | ||
601 | } | ||
602 | |||
603 | auxtrace_index = list_entry(head->prev, struct auxtrace_index, list); | ||
604 | |||
605 | if (auxtrace_index->nr >= PERF_AUXTRACE_INDEX_ENTRY_COUNT) { | ||
606 | err = auxtrace_index__alloc(head); | ||
607 | if (err) | ||
608 | return NULL; | ||
609 | auxtrace_index = list_entry(head->prev, struct auxtrace_index, | ||
610 | list); | ||
611 | } | ||
612 | |||
613 | return auxtrace_index; | ||
614 | } | ||
615 | |||
616 | int auxtrace_index__auxtrace_event(struct list_head *head, | ||
617 | union perf_event *event, off_t file_offset) | ||
618 | { | ||
619 | struct auxtrace_index *auxtrace_index; | ||
620 | size_t nr; | ||
621 | |||
622 | auxtrace_index = auxtrace_index__last(head); | ||
623 | if (!auxtrace_index) | ||
624 | return -ENOMEM; | ||
625 | |||
626 | nr = auxtrace_index->nr; | ||
627 | auxtrace_index->entries[nr].file_offset = file_offset; | ||
628 | auxtrace_index->entries[nr].sz = event->header.size; | ||
629 | auxtrace_index->nr += 1; | ||
630 | |||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int auxtrace_index__do_write(int fd, | ||
635 | struct auxtrace_index *auxtrace_index) | ||
636 | { | ||
637 | struct auxtrace_index_entry ent; | ||
638 | size_t i; | ||
639 | |||
640 | for (i = 0; i < auxtrace_index->nr; i++) { | ||
641 | ent.file_offset = auxtrace_index->entries[i].file_offset; | ||
642 | ent.sz = auxtrace_index->entries[i].sz; | ||
643 | if (writen(fd, &ent, sizeof(ent)) != sizeof(ent)) | ||
644 | return -errno; | ||
645 | } | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | int auxtrace_index__write(int fd, struct list_head *head) | ||
650 | { | ||
651 | struct auxtrace_index *auxtrace_index; | ||
652 | u64 total = 0; | ||
653 | int err; | ||
654 | |||
655 | list_for_each_entry(auxtrace_index, head, list) | ||
656 | total += auxtrace_index->nr; | ||
657 | |||
658 | if (writen(fd, &total, sizeof(total)) != sizeof(total)) | ||
659 | return -errno; | ||
660 | |||
661 | list_for_each_entry(auxtrace_index, head, list) { | ||
662 | err = auxtrace_index__do_write(fd, auxtrace_index); | ||
663 | if (err) | ||
664 | return err; | ||
665 | } | ||
666 | |||
667 | return 0; | ||
668 | } | ||
669 | |||
670 | static int auxtrace_index__process_entry(int fd, struct list_head *head, | ||
671 | bool needs_swap) | ||
672 | { | ||
673 | struct auxtrace_index *auxtrace_index; | ||
674 | struct auxtrace_index_entry ent; | ||
675 | size_t nr; | ||
676 | |||
677 | if (readn(fd, &ent, sizeof(ent)) != sizeof(ent)) | ||
678 | return -1; | ||
679 | |||
680 | auxtrace_index = auxtrace_index__last(head); | ||
681 | if (!auxtrace_index) | ||
682 | return -1; | ||
683 | |||
684 | nr = auxtrace_index->nr; | ||
685 | if (needs_swap) { | ||
686 | auxtrace_index->entries[nr].file_offset = | ||
687 | bswap_64(ent.file_offset); | ||
688 | auxtrace_index->entries[nr].sz = bswap_64(ent.sz); | ||
689 | } else { | ||
690 | auxtrace_index->entries[nr].file_offset = ent.file_offset; | ||
691 | auxtrace_index->entries[nr].sz = ent.sz; | ||
692 | } | ||
693 | |||
694 | auxtrace_index->nr = nr + 1; | ||
695 | |||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | int auxtrace_index__process(int fd, u64 size, struct perf_session *session, | ||
700 | bool needs_swap) | ||
701 | { | ||
702 | struct list_head *head = &session->auxtrace_index; | ||
703 | u64 nr; | ||
704 | |||
705 | if (readn(fd, &nr, sizeof(u64)) != sizeof(u64)) | ||
706 | return -1; | ||
707 | |||
708 | if (needs_swap) | ||
709 | nr = bswap_64(nr); | ||
710 | |||
711 | if (sizeof(u64) + nr * sizeof(struct auxtrace_index_entry) > size) | ||
712 | return -1; | ||
713 | |||
714 | while (nr--) { | ||
715 | int err; | ||
716 | |||
717 | err = auxtrace_index__process_entry(fd, head, needs_swap); | ||
718 | if (err) | ||
719 | return -1; | ||
720 | } | ||
721 | |||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static int auxtrace_queues__process_index_entry(struct auxtrace_queues *queues, | ||
726 | struct perf_session *session, | ||
727 | struct auxtrace_index_entry *ent) | ||
728 | { | ||
729 | return auxtrace_queues__add_indexed_event(queues, session, | ||
730 | ent->file_offset, ent->sz); | ||
731 | } | ||
732 | |||
733 | int auxtrace_queues__process_index(struct auxtrace_queues *queues, | ||
734 | struct perf_session *session) | ||
735 | { | ||
736 | struct auxtrace_index *auxtrace_index; | ||
737 | struct auxtrace_index_entry *ent; | ||
738 | size_t i; | ||
739 | int err; | ||
740 | |||
741 | list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) { | ||
742 | for (i = 0; i < auxtrace_index->nr; i++) { | ||
743 | ent = &auxtrace_index->entries[i]; | ||
744 | err = auxtrace_queues__process_index_entry(queues, | ||
745 | session, | ||
746 | ent); | ||
747 | if (err) | ||
748 | return err; | ||
749 | } | ||
750 | } | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue, | ||
755 | struct auxtrace_buffer *buffer) | ||
756 | { | ||
757 | if (buffer) { | ||
758 | if (list_is_last(&buffer->list, &queue->head)) | ||
759 | return NULL; | ||
760 | return list_entry(buffer->list.next, struct auxtrace_buffer, | ||
761 | list); | ||
762 | } else { | ||
763 | if (list_empty(&queue->head)) | ||
764 | return NULL; | ||
765 | return list_entry(queue->head.next, struct auxtrace_buffer, | ||
766 | list); | ||
767 | } | ||
768 | } | ||
769 | |||
770 | void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd) | ||
771 | { | ||
772 | size_t adj = buffer->data_offset & (page_size - 1); | ||
773 | size_t size = buffer->size + adj; | ||
774 | off_t file_offset = buffer->data_offset - adj; | ||
775 | void *addr; | ||
776 | |||
777 | if (buffer->data) | ||
778 | return buffer->data; | ||
779 | |||
780 | addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, file_offset); | ||
781 | if (addr == MAP_FAILED) | ||
782 | return NULL; | ||
783 | |||
784 | buffer->mmap_addr = addr; | ||
785 | buffer->mmap_size = size; | ||
786 | |||
787 | buffer->data = addr + adj; | ||
788 | |||
789 | return buffer->data; | ||
790 | } | ||
791 | |||
792 | void auxtrace_buffer__put_data(struct auxtrace_buffer *buffer) | ||
793 | { | ||
794 | if (!buffer->data || !buffer->mmap_addr) | ||
795 | return; | ||
796 | munmap(buffer->mmap_addr, buffer->mmap_size); | ||
797 | buffer->mmap_addr = NULL; | ||
798 | buffer->mmap_size = 0; | ||
799 | buffer->data = NULL; | ||
800 | buffer->use_data = NULL; | ||
801 | } | ||
802 | |||
803 | void auxtrace_buffer__drop_data(struct auxtrace_buffer *buffer) | ||
804 | { | ||
805 | auxtrace_buffer__put_data(buffer); | ||
806 | if (buffer->data_needs_freeing) { | ||
807 | buffer->data_needs_freeing = false; | ||
808 | zfree(&buffer->data); | ||
809 | buffer->use_data = NULL; | ||
810 | buffer->size = 0; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | void auxtrace_buffer__free(struct auxtrace_buffer *buffer) | ||
815 | { | ||
816 | auxtrace_buffer__drop_data(buffer); | ||
817 | free(buffer); | ||
818 | } | ||
819 | |||
820 | void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type, | ||
821 | int code, int cpu, pid_t pid, pid_t tid, u64 ip, | ||
822 | const char *msg) | ||
823 | { | ||
824 | size_t size; | ||
825 | |||
826 | memset(auxtrace_error, 0, sizeof(struct auxtrace_error_event)); | ||
827 | |||
828 | auxtrace_error->header.type = PERF_RECORD_AUXTRACE_ERROR; | ||
829 | auxtrace_error->type = type; | ||
830 | auxtrace_error->code = code; | ||
831 | auxtrace_error->cpu = cpu; | ||
832 | auxtrace_error->pid = pid; | ||
833 | auxtrace_error->tid = tid; | ||
834 | auxtrace_error->ip = ip; | ||
835 | strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG); | ||
836 | |||
837 | size = (void *)auxtrace_error->msg - (void *)auxtrace_error + | ||
838 | strlen(auxtrace_error->msg) + 1; | ||
839 | auxtrace_error->header.size = PERF_ALIGN(size, sizeof(u64)); | ||
840 | } | ||
841 | |||
842 | int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, | ||
843 | struct perf_tool *tool, | ||
844 | struct perf_session *session, | ||
845 | perf_event__handler_t process) | ||
846 | { | ||
847 | union perf_event *ev; | ||
848 | size_t priv_size; | ||
849 | int err; | ||
850 | |||
851 | pr_debug2("Synthesizing auxtrace information\n"); | ||
852 | priv_size = auxtrace_record__info_priv_size(itr); | ||
853 | ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); | ||
854 | if (!ev) | ||
855 | return -ENOMEM; | ||
856 | |||
857 | ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO; | ||
858 | ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) + | ||
859 | priv_size; | ||
860 | err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info, | ||
861 | priv_size); | ||
862 | if (err) | ||
863 | goto out_free; | ||
864 | |||
865 | err = process(tool, ev, NULL, NULL); | ||
866 | out_free: | ||
867 | free(ev); | ||
868 | return err; | ||
869 | } | ||
870 | |||
871 | static bool auxtrace__dont_decode(struct perf_session *session) | ||
872 | { | ||
873 | return !session->itrace_synth_opts || | ||
874 | session->itrace_synth_opts->dont_decode; | ||
875 | } | ||
876 | |||
877 | int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused, | ||
878 | union perf_event *event, | ||
879 | struct perf_session *session __maybe_unused) | ||
880 | { | ||
881 | enum auxtrace_type type = event->auxtrace_info.type; | ||
882 | |||
883 | if (dump_trace) | ||
884 | fprintf(stdout, " type: %u\n", type); | ||
885 | |||
886 | switch (type) { | ||
887 | case PERF_AUXTRACE_UNKNOWN: | ||
888 | default: | ||
889 | return -EINVAL; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | s64 perf_event__process_auxtrace(struct perf_tool *tool, | ||
894 | union perf_event *event, | ||
895 | struct perf_session *session) | ||
896 | { | ||
897 | s64 err; | ||
898 | |||
899 | if (dump_trace) | ||
900 | fprintf(stdout, " size: %#"PRIx64" offset: %#"PRIx64" ref: %#"PRIx64" idx: %u tid: %d cpu: %d\n", | ||
901 | event->auxtrace.size, event->auxtrace.offset, | ||
902 | event->auxtrace.reference, event->auxtrace.idx, | ||
903 | event->auxtrace.tid, event->auxtrace.cpu); | ||
904 | |||
905 | if (auxtrace__dont_decode(session)) | ||
906 | return event->auxtrace.size; | ||
907 | |||
908 | if (!session->auxtrace || event->header.type != PERF_RECORD_AUXTRACE) | ||
909 | return -EINVAL; | ||
910 | |||
911 | err = session->auxtrace->process_auxtrace_event(session, event, tool); | ||
912 | if (err < 0) | ||
913 | return err; | ||
914 | |||
915 | return event->auxtrace.size; | ||
916 | } | ||
917 | |||
918 | #define PERF_ITRACE_DEFAULT_PERIOD_TYPE PERF_ITRACE_PERIOD_NANOSECS | ||
919 | #define PERF_ITRACE_DEFAULT_PERIOD 100000 | ||
920 | #define PERF_ITRACE_DEFAULT_CALLCHAIN_SZ 16 | ||
921 | #define PERF_ITRACE_MAX_CALLCHAIN_SZ 1024 | ||
922 | |||
923 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts) | ||
924 | { | ||
925 | synth_opts->instructions = true; | ||
926 | synth_opts->branches = true; | ||
927 | synth_opts->transactions = true; | ||
928 | synth_opts->errors = true; | ||
929 | synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; | ||
930 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; | ||
931 | synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; | ||
932 | } | ||
933 | |||
934 | /* | ||
935 | * Please check tools/perf/Documentation/perf-script.txt for information | ||
936 | * about the options parsed here, which is introduced after this cset, | ||
937 | * when support in 'perf script' for these options is introduced. | ||
938 | */ | ||
939 | int itrace_parse_synth_opts(const struct option *opt, const char *str, | ||
940 | int unset) | ||
941 | { | ||
942 | struct itrace_synth_opts *synth_opts = opt->value; | ||
943 | const char *p; | ||
944 | char *endptr; | ||
945 | |||
946 | synth_opts->set = true; | ||
947 | |||
948 | if (unset) { | ||
949 | synth_opts->dont_decode = true; | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | if (!str) { | ||
954 | itrace_synth_opts__set_default(synth_opts); | ||
955 | return 0; | ||
956 | } | ||
957 | |||
958 | for (p = str; *p;) { | ||
959 | switch (*p++) { | ||
960 | case 'i': | ||
961 | synth_opts->instructions = true; | ||
962 | while (*p == ' ' || *p == ',') | ||
963 | p += 1; | ||
964 | if (isdigit(*p)) { | ||
965 | synth_opts->period = strtoull(p, &endptr, 10); | ||
966 | p = endptr; | ||
967 | while (*p == ' ' || *p == ',') | ||
968 | p += 1; | ||
969 | switch (*p++) { | ||
970 | case 'i': | ||
971 | synth_opts->period_type = | ||
972 | PERF_ITRACE_PERIOD_INSTRUCTIONS; | ||
973 | break; | ||
974 | case 't': | ||
975 | synth_opts->period_type = | ||
976 | PERF_ITRACE_PERIOD_TICKS; | ||
977 | break; | ||
978 | case 'm': | ||
979 | synth_opts->period *= 1000; | ||
980 | /* Fall through */ | ||
981 | case 'u': | ||
982 | synth_opts->period *= 1000; | ||
983 | /* Fall through */ | ||
984 | case 'n': | ||
985 | if (*p++ != 's') | ||
986 | goto out_err; | ||
987 | synth_opts->period_type = | ||
988 | PERF_ITRACE_PERIOD_NANOSECS; | ||
989 | break; | ||
990 | case '\0': | ||
991 | goto out; | ||
992 | default: | ||
993 | goto out_err; | ||
994 | } | ||
995 | } | ||
996 | break; | ||
997 | case 'b': | ||
998 | synth_opts->branches = true; | ||
999 | break; | ||
1000 | case 'x': | ||
1001 | synth_opts->transactions = true; | ||
1002 | break; | ||
1003 | case 'e': | ||
1004 | synth_opts->errors = true; | ||
1005 | break; | ||
1006 | case 'd': | ||
1007 | synth_opts->log = true; | ||
1008 | break; | ||
1009 | case 'c': | ||
1010 | synth_opts->branches = true; | ||
1011 | synth_opts->calls = true; | ||
1012 | break; | ||
1013 | case 'r': | ||
1014 | synth_opts->branches = true; | ||
1015 | synth_opts->returns = true; | ||
1016 | break; | ||
1017 | case 'g': | ||
1018 | synth_opts->callchain = true; | ||
1019 | synth_opts->callchain_sz = | ||
1020 | PERF_ITRACE_DEFAULT_CALLCHAIN_SZ; | ||
1021 | while (*p == ' ' || *p == ',') | ||
1022 | p += 1; | ||
1023 | if (isdigit(*p)) { | ||
1024 | unsigned int val; | ||
1025 | |||
1026 | val = strtoul(p, &endptr, 10); | ||
1027 | p = endptr; | ||
1028 | if (!val || val > PERF_ITRACE_MAX_CALLCHAIN_SZ) | ||
1029 | goto out_err; | ||
1030 | synth_opts->callchain_sz = val; | ||
1031 | } | ||
1032 | break; | ||
1033 | case ' ': | ||
1034 | case ',': | ||
1035 | break; | ||
1036 | default: | ||
1037 | goto out_err; | ||
1038 | } | ||
1039 | } | ||
1040 | out: | ||
1041 | if (synth_opts->instructions) { | ||
1042 | if (!synth_opts->period_type) | ||
1043 | synth_opts->period_type = | ||
1044 | PERF_ITRACE_DEFAULT_PERIOD_TYPE; | ||
1045 | if (!synth_opts->period) | ||
1046 | synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; | ||
1047 | } | ||
1048 | |||
1049 | return 0; | ||
1050 | |||
1051 | out_err: | ||
1052 | pr_err("Bad Instruction Tracing options '%s'\n", str); | ||
1053 | return -EINVAL; | ||
1054 | } | ||
1055 | |||
1056 | static const char * const auxtrace_error_type_name[] = { | ||
1057 | [PERF_AUXTRACE_ERROR_ITRACE] = "instruction trace", | ||
1058 | }; | ||
1059 | |||
1060 | static const char *auxtrace_error_name(int type) | ||
1061 | { | ||
1062 | const char *error_type_name = NULL; | ||
1063 | |||
1064 | if (type < PERF_AUXTRACE_ERROR_MAX) | ||
1065 | error_type_name = auxtrace_error_type_name[type]; | ||
1066 | if (!error_type_name) | ||
1067 | error_type_name = "unknown AUX"; | ||
1068 | return error_type_name; | ||
1069 | } | ||
1070 | |||
1071 | size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp) | ||
1072 | { | ||
1073 | struct auxtrace_error_event *e = &event->auxtrace_error; | ||
1074 | int ret; | ||
1075 | |||
1076 | ret = fprintf(fp, " %s error type %u", | ||
1077 | auxtrace_error_name(e->type), e->type); | ||
1078 | ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRIx64" code %u: %s\n", | ||
1079 | e->cpu, e->pid, e->tid, e->ip, e->code, e->msg); | ||
1080 | return ret; | ||
1081 | } | ||
1082 | |||
1083 | void perf_session__auxtrace_error_inc(struct perf_session *session, | ||
1084 | union perf_event *event) | ||
1085 | { | ||
1086 | struct auxtrace_error_event *e = &event->auxtrace_error; | ||
1087 | |||
1088 | if (e->type < PERF_AUXTRACE_ERROR_MAX) | ||
1089 | session->evlist->stats.nr_auxtrace_errors[e->type] += 1; | ||
1090 | } | ||
1091 | |||
1092 | void events_stats__auxtrace_error_warn(const struct events_stats *stats) | ||
1093 | { | ||
1094 | int i; | ||
1095 | |||
1096 | for (i = 0; i < PERF_AUXTRACE_ERROR_MAX; i++) { | ||
1097 | if (!stats->nr_auxtrace_errors[i]) | ||
1098 | continue; | ||
1099 | ui__warning("%u %s errors\n", | ||
1100 | stats->nr_auxtrace_errors[i], | ||
1101 | auxtrace_error_name(i)); | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | int perf_event__process_auxtrace_error(struct perf_tool *tool __maybe_unused, | ||
1106 | union perf_event *event, | ||
1107 | struct perf_session *session) | ||
1108 | { | ||
1109 | if (auxtrace__dont_decode(session)) | ||
1110 | return 0; | ||
1111 | |||
1112 | perf_event__fprintf_auxtrace_error(event, stdout); | ||
1113 | return 0; | ||
1114 | } | ||
1115 | |||
1116 | static int __auxtrace_mmap__read(struct auxtrace_mmap *mm, | ||
1117 | struct auxtrace_record *itr, | ||
1118 | struct perf_tool *tool, process_auxtrace_t fn, | ||
1119 | bool snapshot, size_t snapshot_size) | ||
1120 | { | ||
1121 | u64 head, old = mm->prev, offset, ref; | ||
1122 | unsigned char *data = mm->base; | ||
1123 | size_t size, head_off, old_off, len1, len2, padding; | ||
1124 | union perf_event ev; | ||
1125 | void *data1, *data2; | ||
1126 | |||
1127 | if (snapshot) { | ||
1128 | head = auxtrace_mmap__read_snapshot_head(mm); | ||
1129 | if (auxtrace_record__find_snapshot(itr, mm->idx, mm, data, | ||
1130 | &head, &old)) | ||
1131 | return -1; | ||
1132 | } else { | ||
1133 | head = auxtrace_mmap__read_head(mm); | ||
1134 | } | ||
1135 | |||
1136 | if (old == head) | ||
1137 | return 0; | ||
1138 | |||
1139 | pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n", | ||
1140 | mm->idx, old, head, head - old); | ||
1141 | |||
1142 | if (mm->mask) { | ||
1143 | head_off = head & mm->mask; | ||
1144 | old_off = old & mm->mask; | ||
1145 | } else { | ||
1146 | head_off = head % mm->len; | ||
1147 | old_off = old % mm->len; | ||
1148 | } | ||
1149 | |||
1150 | if (head_off > old_off) | ||
1151 | size = head_off - old_off; | ||
1152 | else | ||
1153 | size = mm->len - (old_off - head_off); | ||
1154 | |||
1155 | if (snapshot && size > snapshot_size) | ||
1156 | size = snapshot_size; | ||
1157 | |||
1158 | ref = auxtrace_record__reference(itr); | ||
1159 | |||
1160 | if (head > old || size <= head || mm->mask) { | ||
1161 | offset = head - size; | ||
1162 | } else { | ||
1163 | /* | ||
1164 | * When the buffer size is not a power of 2, 'head' wraps at the | ||
1165 | * highest multiple of the buffer size, so we have to subtract | ||
1166 | * the remainder here. | ||
1167 | */ | ||
1168 | u64 rem = (0ULL - mm->len) % mm->len; | ||
1169 | |||
1170 | offset = head - size - rem; | ||
1171 | } | ||
1172 | |||
1173 | if (size > head_off) { | ||
1174 | len1 = size - head_off; | ||
1175 | data1 = &data[mm->len - len1]; | ||
1176 | len2 = head_off; | ||
1177 | data2 = &data[0]; | ||
1178 | } else { | ||
1179 | len1 = size; | ||
1180 | data1 = &data[head_off - len1]; | ||
1181 | len2 = 0; | ||
1182 | data2 = NULL; | ||
1183 | } | ||
1184 | |||
1185 | /* padding must be written by fn() e.g. record__process_auxtrace() */ | ||
1186 | padding = size & 7; | ||
1187 | if (padding) | ||
1188 | padding = 8 - padding; | ||
1189 | |||
1190 | memset(&ev, 0, sizeof(ev)); | ||
1191 | ev.auxtrace.header.type = PERF_RECORD_AUXTRACE; | ||
1192 | ev.auxtrace.header.size = sizeof(ev.auxtrace); | ||
1193 | ev.auxtrace.size = size + padding; | ||
1194 | ev.auxtrace.offset = offset; | ||
1195 | ev.auxtrace.reference = ref; | ||
1196 | ev.auxtrace.idx = mm->idx; | ||
1197 | ev.auxtrace.tid = mm->tid; | ||
1198 | ev.auxtrace.cpu = mm->cpu; | ||
1199 | |||
1200 | if (fn(tool, &ev, data1, len1, data2, len2)) | ||
1201 | return -1; | ||
1202 | |||
1203 | mm->prev = head; | ||
1204 | |||
1205 | if (!snapshot) { | ||
1206 | auxtrace_mmap__write_tail(mm, head); | ||
1207 | if (itr->read_finish) { | ||
1208 | int err; | ||
1209 | |||
1210 | err = itr->read_finish(itr, mm->idx); | ||
1211 | if (err < 0) | ||
1212 | return err; | ||
1213 | } | ||
1214 | } | ||
1215 | |||
1216 | return 1; | ||
1217 | } | ||
1218 | |||
1219 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, | ||
1220 | struct perf_tool *tool, process_auxtrace_t fn) | ||
1221 | { | ||
1222 | return __auxtrace_mmap__read(mm, itr, tool, fn, false, 0); | ||
1223 | } | ||
1224 | |||
1225 | int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm, | ||
1226 | struct auxtrace_record *itr, | ||
1227 | struct perf_tool *tool, process_auxtrace_t fn, | ||
1228 | size_t snapshot_size) | ||
1229 | { | ||
1230 | return __auxtrace_mmap__read(mm, itr, tool, fn, true, snapshot_size); | ||
1231 | } | ||
1232 | |||
1233 | /** | ||
1234 | * struct auxtrace_cache - hash table to implement a cache | ||
1235 | * @hashtable: the hashtable | ||
1236 | * @sz: hashtable size (number of hlists) | ||
1237 | * @entry_size: size of an entry | ||
1238 | * @limit: limit the number of entries to this maximum, when reached the cache | ||
1239 | * is dropped and caching begins again with an empty cache | ||
1240 | * @cnt: current number of entries | ||
1241 | * @bits: hashtable size (@sz = 2^@bits) | ||
1242 | */ | ||
1243 | struct auxtrace_cache { | ||
1244 | struct hlist_head *hashtable; | ||
1245 | size_t sz; | ||
1246 | size_t entry_size; | ||
1247 | size_t limit; | ||
1248 | size_t cnt; | ||
1249 | unsigned int bits; | ||
1250 | }; | ||
1251 | |||
1252 | struct auxtrace_cache *auxtrace_cache__new(unsigned int bits, size_t entry_size, | ||
1253 | unsigned int limit_percent) | ||
1254 | { | ||
1255 | struct auxtrace_cache *c; | ||
1256 | struct hlist_head *ht; | ||
1257 | size_t sz, i; | ||
1258 | |||
1259 | c = zalloc(sizeof(struct auxtrace_cache)); | ||
1260 | if (!c) | ||
1261 | return NULL; | ||
1262 | |||
1263 | sz = 1UL << bits; | ||
1264 | |||
1265 | ht = calloc(sz, sizeof(struct hlist_head)); | ||
1266 | if (!ht) | ||
1267 | goto out_free; | ||
1268 | |||
1269 | for (i = 0; i < sz; i++) | ||
1270 | INIT_HLIST_HEAD(&ht[i]); | ||
1271 | |||
1272 | c->hashtable = ht; | ||
1273 | c->sz = sz; | ||
1274 | c->entry_size = entry_size; | ||
1275 | c->limit = (c->sz * limit_percent) / 100; | ||
1276 | c->bits = bits; | ||
1277 | |||
1278 | return c; | ||
1279 | |||
1280 | out_free: | ||
1281 | free(c); | ||
1282 | return NULL; | ||
1283 | } | ||
1284 | |||
1285 | static void auxtrace_cache__drop(struct auxtrace_cache *c) | ||
1286 | { | ||
1287 | struct auxtrace_cache_entry *entry; | ||
1288 | struct hlist_node *tmp; | ||
1289 | size_t i; | ||
1290 | |||
1291 | if (!c) | ||
1292 | return; | ||
1293 | |||
1294 | for (i = 0; i < c->sz; i++) { | ||
1295 | hlist_for_each_entry_safe(entry, tmp, &c->hashtable[i], hash) { | ||
1296 | hlist_del(&entry->hash); | ||
1297 | auxtrace_cache__free_entry(c, entry); | ||
1298 | } | ||
1299 | } | ||
1300 | |||
1301 | c->cnt = 0; | ||
1302 | } | ||
1303 | |||
1304 | void auxtrace_cache__free(struct auxtrace_cache *c) | ||
1305 | { | ||
1306 | if (!c) | ||
1307 | return; | ||
1308 | |||
1309 | auxtrace_cache__drop(c); | ||
1310 | free(c->hashtable); | ||
1311 | free(c); | ||
1312 | } | ||
1313 | |||
1314 | void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c) | ||
1315 | { | ||
1316 | return malloc(c->entry_size); | ||
1317 | } | ||
1318 | |||
1319 | void auxtrace_cache__free_entry(struct auxtrace_cache *c __maybe_unused, | ||
1320 | void *entry) | ||
1321 | { | ||
1322 | free(entry); | ||
1323 | } | ||
1324 | |||
1325 | int auxtrace_cache__add(struct auxtrace_cache *c, u32 key, | ||
1326 | struct auxtrace_cache_entry *entry) | ||
1327 | { | ||
1328 | if (c->limit && ++c->cnt > c->limit) | ||
1329 | auxtrace_cache__drop(c); | ||
1330 | |||
1331 | entry->key = key; | ||
1332 | hlist_add_head(&entry->hash, &c->hashtable[hash_32(key, c->bits)]); | ||
1333 | |||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key) | ||
1338 | { | ||
1339 | struct auxtrace_cache_entry *entry; | ||
1340 | struct hlist_head *hlist; | ||
1341 | |||
1342 | if (!c) | ||
1343 | return NULL; | ||
1344 | |||
1345 | hlist = &c->hashtable[hash_32(key, c->bits)]; | ||
1346 | hlist_for_each_entry(entry, hlist, hash) { | ||
1347 | if (entry->key == key) | ||
1348 | return entry; | ||
1349 | } | ||
1350 | |||
1351 | return NULL; | ||
1352 | } | ||
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h new file mode 100644 index 000000000000..a171abbe7301 --- /dev/null +++ b/tools/perf/util/auxtrace.h | |||
@@ -0,0 +1,643 @@ | |||
1 | /* | ||
2 | * auxtrace.h: AUX area trace support | ||
3 | * Copyright (c) 2013-2015, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __PERF_AUXTRACE_H | ||
17 | #define __PERF_AUXTRACE_H | ||
18 | |||
19 | #include <sys/types.h> | ||
20 | #include <stdbool.h> | ||
21 | #include <stddef.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/perf_event.h> | ||
24 | #include <linux/types.h> | ||
25 | |||
26 | #include "../perf.h" | ||
27 | #include "event.h" | ||
28 | #include "session.h" | ||
29 | #include "debug.h" | ||
30 | |||
31 | union perf_event; | ||
32 | struct perf_session; | ||
33 | struct perf_evlist; | ||
34 | struct perf_tool; | ||
35 | struct option; | ||
36 | struct record_opts; | ||
37 | struct auxtrace_info_event; | ||
38 | struct events_stats; | ||
39 | |||
40 | enum auxtrace_type { | ||
41 | PERF_AUXTRACE_UNKNOWN, | ||
42 | }; | ||
43 | |||
44 | enum itrace_period_type { | ||
45 | PERF_ITRACE_PERIOD_INSTRUCTIONS, | ||
46 | PERF_ITRACE_PERIOD_TICKS, | ||
47 | PERF_ITRACE_PERIOD_NANOSECS, | ||
48 | }; | ||
49 | |||
50 | /** | ||
51 | * struct itrace_synth_opts - AUX area tracing synthesis options. | ||
52 | * @set: indicates whether or not options have been set | ||
53 | * @inject: indicates the event (not just the sample) must be fully synthesized | ||
54 | * because 'perf inject' will write it out | ||
55 | * @instructions: whether to synthesize 'instructions' events | ||
56 | * @branches: whether to synthesize 'branches' events | ||
57 | * @transactions: whether to synthesize events for transactions | ||
58 | * @errors: whether to synthesize decoder error events | ||
59 | * @dont_decode: whether to skip decoding entirely | ||
60 | * @log: write a decoding log | ||
61 | * @calls: limit branch samples to calls (can be combined with @returns) | ||
62 | * @returns: limit branch samples to returns (can be combined with @calls) | ||
63 | * @callchain: add callchain to 'instructions' events | ||
64 | * @callchain_sz: maximum callchain size | ||
65 | * @period: 'instructions' events period | ||
66 | * @period_type: 'instructions' events period type | ||
67 | */ | ||
68 | struct itrace_synth_opts { | ||
69 | bool set; | ||
70 | bool inject; | ||
71 | bool instructions; | ||
72 | bool branches; | ||
73 | bool transactions; | ||
74 | bool errors; | ||
75 | bool dont_decode; | ||
76 | bool log; | ||
77 | bool calls; | ||
78 | bool returns; | ||
79 | bool callchain; | ||
80 | unsigned int callchain_sz; | ||
81 | unsigned long long period; | ||
82 | enum itrace_period_type period_type; | ||
83 | }; | ||
84 | |||
85 | /** | ||
86 | * struct auxtrace_index_entry - indexes a AUX area tracing event within a | ||
87 | * perf.data file. | ||
88 | * @file_offset: offset within the perf.data file | ||
89 | * @sz: size of the event | ||
90 | */ | ||
91 | struct auxtrace_index_entry { | ||
92 | u64 file_offset; | ||
93 | u64 sz; | ||
94 | }; | ||
95 | |||
96 | #define PERF_AUXTRACE_INDEX_ENTRY_COUNT 256 | ||
97 | |||
98 | /** | ||
99 | * struct auxtrace_index - index of AUX area tracing events within a perf.data | ||
100 | * file. | ||
101 | * @list: linking a number of arrays of entries | ||
102 | * @nr: number of entries | ||
103 | * @entries: array of entries | ||
104 | */ | ||
105 | struct auxtrace_index { | ||
106 | struct list_head list; | ||
107 | size_t nr; | ||
108 | struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT]; | ||
109 | }; | ||
110 | |||
111 | /** | ||
112 | * struct auxtrace - session callbacks to allow AUX area data decoding. | ||
113 | * @process_event: lets the decoder see all session events | ||
114 | * @flush_events: process any remaining data | ||
115 | * @free_events: free resources associated with event processing | ||
116 | * @free: free resources associated with the session | ||
117 | */ | ||
118 | struct auxtrace { | ||
119 | int (*process_event)(struct perf_session *session, | ||
120 | union perf_event *event, | ||
121 | struct perf_sample *sample, | ||
122 | struct perf_tool *tool); | ||
123 | int (*process_auxtrace_event)(struct perf_session *session, | ||
124 | union perf_event *event, | ||
125 | struct perf_tool *tool); | ||
126 | int (*flush_events)(struct perf_session *session, | ||
127 | struct perf_tool *tool); | ||
128 | void (*free_events)(struct perf_session *session); | ||
129 | void (*free)(struct perf_session *session); | ||
130 | }; | ||
131 | |||
132 | /** | ||
133 | * struct auxtrace_buffer - a buffer containing AUX area tracing data. | ||
134 | * @list: buffers are queued in a list held by struct auxtrace_queue | ||
135 | * @size: size of the buffer in bytes | ||
136 | * @pid: in per-thread mode, the pid this buffer is associated with | ||
137 | * @tid: in per-thread mode, the tid this buffer is associated with | ||
138 | * @cpu: in per-cpu mode, the cpu this buffer is associated with | ||
139 | * @data: actual buffer data (can be null if the data has not been loaded) | ||
140 | * @data_offset: file offset at which the buffer can be read | ||
141 | * @mmap_addr: mmap address at which the buffer can be read | ||
142 | * @mmap_size: size of the mmap at @mmap_addr | ||
143 | * @data_needs_freeing: @data was malloc'd so free it when it is no longer | ||
144 | * needed | ||
145 | * @consecutive: the original data was split up and this buffer is consecutive | ||
146 | * to the previous buffer | ||
147 | * @offset: offset as determined by aux_head / aux_tail members of struct | ||
148 | * perf_event_mmap_page | ||
149 | * @reference: an implementation-specific reference determined when the data is | ||
150 | * recorded | ||
151 | * @buffer_nr: used to number each buffer | ||
152 | * @use_size: implementation actually only uses this number of bytes | ||
153 | * @use_data: implementation actually only uses data starting at this address | ||
154 | */ | ||
155 | struct auxtrace_buffer { | ||
156 | struct list_head list; | ||
157 | size_t size; | ||
158 | pid_t pid; | ||
159 | pid_t tid; | ||
160 | int cpu; | ||
161 | void *data; | ||
162 | off_t data_offset; | ||
163 | void *mmap_addr; | ||
164 | size_t mmap_size; | ||
165 | bool data_needs_freeing; | ||
166 | bool consecutive; | ||
167 | u64 offset; | ||
168 | u64 reference; | ||
169 | u64 buffer_nr; | ||
170 | size_t use_size; | ||
171 | void *use_data; | ||
172 | }; | ||
173 | |||
174 | /** | ||
175 | * struct auxtrace_queue - a queue of AUX area tracing data buffers. | ||
176 | * @head: head of buffer list | ||
177 | * @tid: in per-thread mode, the tid this queue is associated with | ||
178 | * @cpu: in per-cpu mode, the cpu this queue is associated with | ||
179 | * @set: %true once this queue has been dedicated to a specific thread or cpu | ||
180 | * @priv: implementation-specific data | ||
181 | */ | ||
182 | struct auxtrace_queue { | ||
183 | struct list_head head; | ||
184 | pid_t tid; | ||
185 | int cpu; | ||
186 | bool set; | ||
187 | void *priv; | ||
188 | }; | ||
189 | |||
190 | /** | ||
191 | * struct auxtrace_queues - an array of AUX area tracing queues. | ||
192 | * @queue_array: array of queues | ||
193 | * @nr_queues: number of queues | ||
194 | * @new_data: set whenever new data is queued | ||
195 | * @populated: queues have been fully populated using the auxtrace_index | ||
196 | * @next_buffer_nr: used to number each buffer | ||
197 | */ | ||
198 | struct auxtrace_queues { | ||
199 | struct auxtrace_queue *queue_array; | ||
200 | unsigned int nr_queues; | ||
201 | bool new_data; | ||
202 | bool populated; | ||
203 | u64 next_buffer_nr; | ||
204 | }; | ||
205 | |||
206 | /** | ||
207 | * struct auxtrace_heap_item - element of struct auxtrace_heap. | ||
208 | * @queue_nr: queue number | ||
209 | * @ordinal: value used for sorting (lowest ordinal is top of the heap) expected | ||
210 | * to be a timestamp | ||
211 | */ | ||
212 | struct auxtrace_heap_item { | ||
213 | unsigned int queue_nr; | ||
214 | u64 ordinal; | ||
215 | }; | ||
216 | |||
217 | /** | ||
218 | * struct auxtrace_heap - a heap suitable for sorting AUX area tracing queues. | ||
219 | * @heap_array: the heap | ||
220 | * @heap_cnt: the number of elements in the heap | ||
221 | * @heap_sz: maximum number of elements (grows as needed) | ||
222 | */ | ||
223 | struct auxtrace_heap { | ||
224 | struct auxtrace_heap_item *heap_array; | ||
225 | unsigned int heap_cnt; | ||
226 | unsigned int heap_sz; | ||
227 | }; | ||
228 | |||
229 | /** | ||
230 | * struct auxtrace_mmap - records an mmap of the auxtrace buffer. | ||
231 | * @base: address of mapped area | ||
232 | * @userpg: pointer to buffer's perf_event_mmap_page | ||
233 | * @mask: %0 if @len is not a power of two, otherwise (@len - %1) | ||
234 | * @len: size of mapped area | ||
235 | * @prev: previous aux_head | ||
236 | * @idx: index of this mmap | ||
237 | * @tid: tid for a per-thread mmap (also set if there is only 1 tid on a per-cpu | ||
238 | * mmap) otherwise %0 | ||
239 | * @cpu: cpu number for a per-cpu mmap otherwise %-1 | ||
240 | */ | ||
241 | struct auxtrace_mmap { | ||
242 | void *base; | ||
243 | void *userpg; | ||
244 | size_t mask; | ||
245 | size_t len; | ||
246 | u64 prev; | ||
247 | int idx; | ||
248 | pid_t tid; | ||
249 | int cpu; | ||
250 | }; | ||
251 | |||
252 | /** | ||
253 | * struct auxtrace_mmap_params - parameters to set up struct auxtrace_mmap. | ||
254 | * @mask: %0 if @len is not a power of two, otherwise (@len - %1) | ||
255 | * @offset: file offset of mapped area | ||
256 | * @len: size of mapped area | ||
257 | * @prot: mmap memory protection | ||
258 | * @idx: index of this mmap | ||
259 | * @tid: tid for a per-thread mmap (also set if there is only 1 tid on a per-cpu | ||
260 | * mmap) otherwise %0 | ||
261 | * @cpu: cpu number for a per-cpu mmap otherwise %-1 | ||
262 | */ | ||
263 | struct auxtrace_mmap_params { | ||
264 | size_t mask; | ||
265 | off_t offset; | ||
266 | size_t len; | ||
267 | int prot; | ||
268 | int idx; | ||
269 | pid_t tid; | ||
270 | int cpu; | ||
271 | }; | ||
272 | |||
273 | /** | ||
274 | * struct auxtrace_record - callbacks for recording AUX area data. | ||
275 | * @recording_options: validate and process recording options | ||
276 | * @info_priv_size: return the size of the private data in auxtrace_info_event | ||
277 | * @info_fill: fill-in the private data in auxtrace_info_event | ||
278 | * @free: free this auxtrace record structure | ||
279 | * @snapshot_start: starting a snapshot | ||
280 | * @snapshot_finish: finishing a snapshot | ||
281 | * @find_snapshot: find data to snapshot within auxtrace mmap | ||
282 | * @parse_snapshot_options: parse snapshot options | ||
283 | * @reference: provide a 64-bit reference number for auxtrace_event | ||
284 | * @read_finish: called after reading from an auxtrace mmap | ||
285 | */ | ||
286 | struct auxtrace_record { | ||
287 | int (*recording_options)(struct auxtrace_record *itr, | ||
288 | struct perf_evlist *evlist, | ||
289 | struct record_opts *opts); | ||
290 | size_t (*info_priv_size)(struct auxtrace_record *itr); | ||
291 | int (*info_fill)(struct auxtrace_record *itr, | ||
292 | struct perf_session *session, | ||
293 | struct auxtrace_info_event *auxtrace_info, | ||
294 | size_t priv_size); | ||
295 | void (*free)(struct auxtrace_record *itr); | ||
296 | int (*snapshot_start)(struct auxtrace_record *itr); | ||
297 | int (*snapshot_finish)(struct auxtrace_record *itr); | ||
298 | int (*find_snapshot)(struct auxtrace_record *itr, int idx, | ||
299 | struct auxtrace_mmap *mm, unsigned char *data, | ||
300 | u64 *head, u64 *old); | ||
301 | int (*parse_snapshot_options)(struct auxtrace_record *itr, | ||
302 | struct record_opts *opts, | ||
303 | const char *str); | ||
304 | u64 (*reference)(struct auxtrace_record *itr); | ||
305 | int (*read_finish)(struct auxtrace_record *itr, int idx); | ||
306 | }; | ||
307 | |||
308 | #ifdef HAVE_AUXTRACE_SUPPORT | ||
309 | |||
310 | /* | ||
311 | * In snapshot mode the mmapped page is read-only which makes using | ||
312 | * __sync_val_compare_and_swap() problematic. However, snapshot mode expects | ||
313 | * the buffer is not updated while the snapshot is made (e.g. Intel PT disables | ||
314 | * the event) so there is not a race anyway. | ||
315 | */ | ||
316 | static inline u64 auxtrace_mmap__read_snapshot_head(struct auxtrace_mmap *mm) | ||
317 | { | ||
318 | struct perf_event_mmap_page *pc = mm->userpg; | ||
319 | u64 head = ACCESS_ONCE(pc->aux_head); | ||
320 | |||
321 | /* Ensure all reads are done after we read the head */ | ||
322 | rmb(); | ||
323 | return head; | ||
324 | } | ||
325 | |||
326 | static inline u64 auxtrace_mmap__read_head(struct auxtrace_mmap *mm) | ||
327 | { | ||
328 | struct perf_event_mmap_page *pc = mm->userpg; | ||
329 | #if BITS_PER_LONG == 64 || !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) | ||
330 | u64 head = ACCESS_ONCE(pc->aux_head); | ||
331 | #else | ||
332 | u64 head = __sync_val_compare_and_swap(&pc->aux_head, 0, 0); | ||
333 | #endif | ||
334 | |||
335 | /* Ensure all reads are done after we read the head */ | ||
336 | rmb(); | ||
337 | return head; | ||
338 | } | ||
339 | |||
340 | static inline void auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) | ||
341 | { | ||
342 | struct perf_event_mmap_page *pc = mm->userpg; | ||
343 | #if BITS_PER_LONG != 64 && defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) | ||
344 | u64 old_tail; | ||
345 | #endif | ||
346 | |||
347 | /* Ensure all reads are done before we write the tail out */ | ||
348 | mb(); | ||
349 | #if BITS_PER_LONG == 64 || !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) | ||
350 | pc->aux_tail = tail; | ||
351 | #else | ||
352 | do { | ||
353 | old_tail = __sync_val_compare_and_swap(&pc->aux_tail, 0, 0); | ||
354 | } while (!__sync_bool_compare_and_swap(&pc->aux_tail, old_tail, tail)); | ||
355 | #endif | ||
356 | } | ||
357 | |||
358 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, | ||
359 | struct auxtrace_mmap_params *mp, | ||
360 | void *userpg, int fd); | ||
361 | void auxtrace_mmap__munmap(struct auxtrace_mmap *mm); | ||
362 | void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, | ||
363 | off_t auxtrace_offset, | ||
364 | unsigned int auxtrace_pages, | ||
365 | bool auxtrace_overwrite); | ||
366 | void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | ||
367 | struct perf_evlist *evlist, int idx, | ||
368 | bool per_cpu); | ||
369 | |||
370 | typedef int (*process_auxtrace_t)(struct perf_tool *tool, | ||
371 | union perf_event *event, void *data1, | ||
372 | size_t len1, void *data2, size_t len2); | ||
373 | |||
374 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, | ||
375 | struct perf_tool *tool, process_auxtrace_t fn); | ||
376 | |||
377 | int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm, | ||
378 | struct auxtrace_record *itr, | ||
379 | struct perf_tool *tool, process_auxtrace_t fn, | ||
380 | size_t snapshot_size); | ||
381 | |||
382 | int auxtrace_queues__init(struct auxtrace_queues *queues); | ||
383 | int auxtrace_queues__add_event(struct auxtrace_queues *queues, | ||
384 | struct perf_session *session, | ||
385 | union perf_event *event, off_t data_offset, | ||
386 | struct auxtrace_buffer **buffer_ptr); | ||
387 | void auxtrace_queues__free(struct auxtrace_queues *queues); | ||
388 | int auxtrace_queues__process_index(struct auxtrace_queues *queues, | ||
389 | struct perf_session *session); | ||
390 | struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue, | ||
391 | struct auxtrace_buffer *buffer); | ||
392 | void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd); | ||
393 | void auxtrace_buffer__put_data(struct auxtrace_buffer *buffer); | ||
394 | void auxtrace_buffer__drop_data(struct auxtrace_buffer *buffer); | ||
395 | void auxtrace_buffer__free(struct auxtrace_buffer *buffer); | ||
396 | |||
397 | int auxtrace_heap__add(struct auxtrace_heap *heap, unsigned int queue_nr, | ||
398 | u64 ordinal); | ||
399 | void auxtrace_heap__pop(struct auxtrace_heap *heap); | ||
400 | void auxtrace_heap__free(struct auxtrace_heap *heap); | ||
401 | |||
402 | struct auxtrace_cache_entry { | ||
403 | struct hlist_node hash; | ||
404 | u32 key; | ||
405 | }; | ||
406 | |||
407 | struct auxtrace_cache *auxtrace_cache__new(unsigned int bits, size_t entry_size, | ||
408 | unsigned int limit_percent); | ||
409 | void auxtrace_cache__free(struct auxtrace_cache *auxtrace_cache); | ||
410 | void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c); | ||
411 | void auxtrace_cache__free_entry(struct auxtrace_cache *c, void *entry); | ||
412 | int auxtrace_cache__add(struct auxtrace_cache *c, u32 key, | ||
413 | struct auxtrace_cache_entry *entry); | ||
414 | void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key); | ||
415 | |||
416 | struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist, | ||
417 | int *err); | ||
418 | |||
419 | int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, | ||
420 | struct record_opts *opts, | ||
421 | const char *str); | ||
422 | int auxtrace_record__options(struct auxtrace_record *itr, | ||
423 | struct perf_evlist *evlist, | ||
424 | struct record_opts *opts); | ||
425 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr); | ||
426 | int auxtrace_record__info_fill(struct auxtrace_record *itr, | ||
427 | struct perf_session *session, | ||
428 | struct auxtrace_info_event *auxtrace_info, | ||
429 | size_t priv_size); | ||
430 | void auxtrace_record__free(struct auxtrace_record *itr); | ||
431 | int auxtrace_record__snapshot_start(struct auxtrace_record *itr); | ||
432 | int auxtrace_record__snapshot_finish(struct auxtrace_record *itr); | ||
433 | int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx, | ||
434 | struct auxtrace_mmap *mm, | ||
435 | unsigned char *data, u64 *head, u64 *old); | ||
436 | u64 auxtrace_record__reference(struct auxtrace_record *itr); | ||
437 | |||
438 | int auxtrace_index__auxtrace_event(struct list_head *head, union perf_event *event, | ||
439 | off_t file_offset); | ||
440 | int auxtrace_index__write(int fd, struct list_head *head); | ||
441 | int auxtrace_index__process(int fd, u64 size, struct perf_session *session, | ||
442 | bool needs_swap); | ||
443 | void auxtrace_index__free(struct list_head *head); | ||
444 | |||
445 | void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type, | ||
446 | int code, int cpu, pid_t pid, pid_t tid, u64 ip, | ||
447 | const char *msg); | ||
448 | |||
449 | int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, | ||
450 | struct perf_tool *tool, | ||
451 | struct perf_session *session, | ||
452 | perf_event__handler_t process); | ||
453 | int perf_event__process_auxtrace_info(struct perf_tool *tool, | ||
454 | union perf_event *event, | ||
455 | struct perf_session *session); | ||
456 | s64 perf_event__process_auxtrace(struct perf_tool *tool, | ||
457 | union perf_event *event, | ||
458 | struct perf_session *session); | ||
459 | int perf_event__process_auxtrace_error(struct perf_tool *tool, | ||
460 | union perf_event *event, | ||
461 | struct perf_session *session); | ||
462 | int itrace_parse_synth_opts(const struct option *opt, const char *str, | ||
463 | int unset); | ||
464 | void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts); | ||
465 | |||
466 | size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp); | ||
467 | void perf_session__auxtrace_error_inc(struct perf_session *session, | ||
468 | union perf_event *event); | ||
469 | void events_stats__auxtrace_error_warn(const struct events_stats *stats); | ||
470 | |||
471 | static inline int auxtrace__process_event(struct perf_session *session, | ||
472 | union perf_event *event, | ||
473 | struct perf_sample *sample, | ||
474 | struct perf_tool *tool) | ||
475 | { | ||
476 | if (!session->auxtrace) | ||
477 | return 0; | ||
478 | |||
479 | return session->auxtrace->process_event(session, event, sample, tool); | ||
480 | } | ||
481 | |||
482 | static inline int auxtrace__flush_events(struct perf_session *session, | ||
483 | struct perf_tool *tool) | ||
484 | { | ||
485 | if (!session->auxtrace) | ||
486 | return 0; | ||
487 | |||
488 | return session->auxtrace->flush_events(session, tool); | ||
489 | } | ||
490 | |||
491 | static inline void auxtrace__free_events(struct perf_session *session) | ||
492 | { | ||
493 | if (!session->auxtrace) | ||
494 | return; | ||
495 | |||
496 | return session->auxtrace->free_events(session); | ||
497 | } | ||
498 | |||
499 | static inline void auxtrace__free(struct perf_session *session) | ||
500 | { | ||
501 | if (!session->auxtrace) | ||
502 | return; | ||
503 | |||
504 | return session->auxtrace->free(session); | ||
505 | } | ||
506 | |||
507 | #else | ||
508 | |||
509 | static inline struct auxtrace_record * | ||
510 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, | ||
511 | int *err __maybe_unused) | ||
512 | { | ||
513 | *err = 0; | ||
514 | return NULL; | ||
515 | } | ||
516 | |||
517 | static inline | ||
518 | void auxtrace_record__free(struct auxtrace_record *itr __maybe_unused) | ||
519 | { | ||
520 | } | ||
521 | |||
522 | static inline int | ||
523 | perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr __maybe_unused, | ||
524 | struct perf_tool *tool __maybe_unused, | ||
525 | struct perf_session *session __maybe_unused, | ||
526 | perf_event__handler_t process __maybe_unused) | ||
527 | { | ||
528 | return -EINVAL; | ||
529 | } | ||
530 | |||
531 | static inline | ||
532 | int auxtrace_record__options(struct auxtrace_record *itr __maybe_unused, | ||
533 | struct perf_evlist *evlist __maybe_unused, | ||
534 | struct record_opts *opts __maybe_unused) | ||
535 | { | ||
536 | return 0; | ||
537 | } | ||
538 | |||
539 | #define perf_event__process_auxtrace_info 0 | ||
540 | #define perf_event__process_auxtrace 0 | ||
541 | #define perf_event__process_auxtrace_error 0 | ||
542 | |||
543 | static inline | ||
544 | void perf_session__auxtrace_error_inc(struct perf_session *session | ||
545 | __maybe_unused, | ||
546 | union perf_event *event | ||
547 | __maybe_unused) | ||
548 | { | ||
549 | } | ||
550 | |||
551 | static inline | ||
552 | void events_stats__auxtrace_error_warn(const struct events_stats *stats | ||
553 | __maybe_unused) | ||
554 | { | ||
555 | } | ||
556 | |||
557 | static inline | ||
558 | int itrace_parse_synth_opts(const struct option *opt __maybe_unused, | ||
559 | const char *str __maybe_unused, | ||
560 | int unset __maybe_unused) | ||
561 | { | ||
562 | pr_err("AUX area tracing not supported\n"); | ||
563 | return -EINVAL; | ||
564 | } | ||
565 | |||
566 | static inline | ||
567 | int auxtrace_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused, | ||
568 | struct record_opts *opts __maybe_unused, | ||
569 | const char *str) | ||
570 | { | ||
571 | if (!str) | ||
572 | return 0; | ||
573 | pr_err("AUX area tracing not supported\n"); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | static inline | ||
578 | int auxtrace__process_event(struct perf_session *session __maybe_unused, | ||
579 | union perf_event *event __maybe_unused, | ||
580 | struct perf_sample *sample __maybe_unused, | ||
581 | struct perf_tool *tool __maybe_unused) | ||
582 | { | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static inline | ||
587 | int auxtrace__flush_events(struct perf_session *session __maybe_unused, | ||
588 | struct perf_tool *tool __maybe_unused) | ||
589 | { | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static inline | ||
594 | void auxtrace__free_events(struct perf_session *session __maybe_unused) | ||
595 | { | ||
596 | } | ||
597 | |||
598 | static inline | ||
599 | void auxtrace_cache__free(struct auxtrace_cache *auxtrace_cache __maybe_unused) | ||
600 | { | ||
601 | } | ||
602 | |||
603 | static inline | ||
604 | void auxtrace__free(struct perf_session *session __maybe_unused) | ||
605 | { | ||
606 | } | ||
607 | |||
608 | static inline | ||
609 | int auxtrace_index__write(int fd __maybe_unused, | ||
610 | struct list_head *head __maybe_unused) | ||
611 | { | ||
612 | return -EINVAL; | ||
613 | } | ||
614 | |||
615 | static inline | ||
616 | int auxtrace_index__process(int fd __maybe_unused, | ||
617 | u64 size __maybe_unused, | ||
618 | struct perf_session *session __maybe_unused, | ||
619 | bool needs_swap __maybe_unused) | ||
620 | { | ||
621 | return -EINVAL; | ||
622 | } | ||
623 | |||
624 | static inline | ||
625 | void auxtrace_index__free(struct list_head *head __maybe_unused) | ||
626 | { | ||
627 | } | ||
628 | |||
629 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, | ||
630 | struct auxtrace_mmap_params *mp, | ||
631 | void *userpg, int fd); | ||
632 | void auxtrace_mmap__munmap(struct auxtrace_mmap *mm); | ||
633 | void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, | ||
634 | off_t auxtrace_offset, | ||
635 | unsigned int auxtrace_pages, | ||
636 | bool auxtrace_overwrite); | ||
637 | void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | ||
638 | struct perf_evlist *evlist, int idx, | ||
639 | bool per_cpu); | ||
640 | |||
641 | #endif | ||
642 | |||
643 | #endif | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 6033a0a212ca..679c2c6d8ade 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -72,6 +72,10 @@ extern struct callchain_param callchain_param; | |||
72 | struct callchain_list { | 72 | struct callchain_list { |
73 | u64 ip; | 73 | u64 ip; |
74 | struct map_symbol ms; | 74 | struct map_symbol ms; |
75 | struct /* for TUI */ { | ||
76 | bool unfolded; | ||
77 | bool has_children; | ||
78 | }; | ||
75 | char *srcline; | 79 | char *srcline; |
76 | struct list_head list; | 80 | struct list_head list; |
77 | }; | 81 | }; |
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index dd17c9a32fbc..5bfc1198ab46 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <babeltrace/ctf-writer/event.h> | 14 | #include <babeltrace/ctf-writer/event.h> |
15 | #include <babeltrace/ctf-writer/event-types.h> | 15 | #include <babeltrace/ctf-writer/event-types.h> |
16 | #include <babeltrace/ctf-writer/event-fields.h> | 16 | #include <babeltrace/ctf-writer/event-fields.h> |
17 | #include <babeltrace/ctf-ir/utils.h> | ||
17 | #include <babeltrace/ctf/events.h> | 18 | #include <babeltrace/ctf/events.h> |
18 | #include <traceevent/event-parse.h> | 19 | #include <traceevent/event-parse.h> |
19 | #include "asm/bug.h" | 20 | #include "asm/bug.h" |
@@ -38,12 +39,21 @@ struct evsel_priv { | |||
38 | struct bt_ctf_event_class *event_class; | 39 | struct bt_ctf_event_class *event_class; |
39 | }; | 40 | }; |
40 | 41 | ||
42 | #define MAX_CPUS 4096 | ||
43 | |||
44 | struct ctf_stream { | ||
45 | struct bt_ctf_stream *stream; | ||
46 | int cpu; | ||
47 | u32 count; | ||
48 | }; | ||
49 | |||
41 | struct ctf_writer { | 50 | struct ctf_writer { |
42 | /* writer primitives */ | 51 | /* writer primitives */ |
43 | struct bt_ctf_writer *writer; | 52 | struct bt_ctf_writer *writer; |
44 | struct bt_ctf_stream *stream; | 53 | struct ctf_stream **stream; |
45 | struct bt_ctf_stream_class *stream_class; | 54 | int stream_cnt; |
46 | struct bt_ctf_clock *clock; | 55 | struct bt_ctf_stream_class *stream_class; |
56 | struct bt_ctf_clock *clock; | ||
47 | 57 | ||
48 | /* data types */ | 58 | /* data types */ |
49 | union { | 59 | union { |
@@ -65,6 +75,9 @@ struct convert { | |||
65 | 75 | ||
66 | u64 events_size; | 76 | u64 events_size; |
67 | u64 events_count; | 77 | u64 events_count; |
78 | |||
79 | /* Ordered events configured queue size. */ | ||
80 | u64 queue_size; | ||
68 | }; | 81 | }; |
69 | 82 | ||
70 | static int value_set(struct bt_ctf_field_type *type, | 83 | static int value_set(struct bt_ctf_field_type *type, |
@@ -153,6 +166,43 @@ get_tracepoint_field_type(struct ctf_writer *cw, struct format_field *field) | |||
153 | return cw->data.u32; | 166 | return cw->data.u32; |
154 | } | 167 | } |
155 | 168 | ||
169 | static unsigned long long adjust_signedness(unsigned long long value_int, int size) | ||
170 | { | ||
171 | unsigned long long value_mask; | ||
172 | |||
173 | /* | ||
174 | * value_mask = (1 << (size * 8 - 1)) - 1. | ||
175 | * Directly set value_mask for code readers. | ||
176 | */ | ||
177 | switch (size) { | ||
178 | case 1: | ||
179 | value_mask = 0x7fULL; | ||
180 | break; | ||
181 | case 2: | ||
182 | value_mask = 0x7fffULL; | ||
183 | break; | ||
184 | case 4: | ||
185 | value_mask = 0x7fffffffULL; | ||
186 | break; | ||
187 | case 8: | ||
188 | /* | ||
189 | * For 64 bit value, return it self. There is no need | ||
190 | * to fill high bit. | ||
191 | */ | ||
192 | /* Fall through */ | ||
193 | default: | ||
194 | /* BUG! */ | ||
195 | return value_int; | ||
196 | } | ||
197 | |||
198 | /* If it is a positive value, don't adjust. */ | ||
199 | if ((value_int & (~0ULL - value_mask)) == 0) | ||
200 | return value_int; | ||
201 | |||
202 | /* Fill upper part of value_int with 1 to make it a negative long long. */ | ||
203 | return (value_int & value_mask) | ~value_mask; | ||
204 | } | ||
205 | |||
156 | static int add_tracepoint_field_value(struct ctf_writer *cw, | 206 | static int add_tracepoint_field_value(struct ctf_writer *cw, |
157 | struct bt_ctf_event_class *event_class, | 207 | struct bt_ctf_event_class *event_class, |
158 | struct bt_ctf_event *event, | 208 | struct bt_ctf_event *event, |
@@ -164,7 +214,6 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
164 | struct bt_ctf_field *field; | 214 | struct bt_ctf_field *field; |
165 | const char *name = fmtf->name; | 215 | const char *name = fmtf->name; |
166 | void *data = sample->raw_data; | 216 | void *data = sample->raw_data; |
167 | unsigned long long value_int; | ||
168 | unsigned long flags = fmtf->flags; | 217 | unsigned long flags = fmtf->flags; |
169 | unsigned int n_items; | 218 | unsigned int n_items; |
170 | unsigned int i; | 219 | unsigned int i; |
@@ -172,6 +221,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
172 | unsigned int len; | 221 | unsigned int len; |
173 | int ret; | 222 | int ret; |
174 | 223 | ||
224 | name = fmtf->alias; | ||
175 | offset = fmtf->offset; | 225 | offset = fmtf->offset; |
176 | len = fmtf->size; | 226 | len = fmtf->size; |
177 | if (flags & FIELD_IS_STRING) | 227 | if (flags & FIELD_IS_STRING) |
@@ -208,11 +258,6 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
208 | type = get_tracepoint_field_type(cw, fmtf); | 258 | type = get_tracepoint_field_type(cw, fmtf); |
209 | 259 | ||
210 | for (i = 0; i < n_items; i++) { | 260 | for (i = 0; i < n_items; i++) { |
211 | if (!(flags & FIELD_IS_STRING)) | ||
212 | value_int = pevent_read_number( | ||
213 | fmtf->event->pevent, | ||
214 | data + offset + i * len, len); | ||
215 | |||
216 | if (flags & FIELD_IS_ARRAY) | 261 | if (flags & FIELD_IS_ARRAY) |
217 | field = bt_ctf_field_array_get_field(array_field, i); | 262 | field = bt_ctf_field_array_get_field(array_field, i); |
218 | else | 263 | else |
@@ -226,12 +271,21 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, | |||
226 | if (flags & FIELD_IS_STRING) | 271 | if (flags & FIELD_IS_STRING) |
227 | ret = bt_ctf_field_string_set_value(field, | 272 | ret = bt_ctf_field_string_set_value(field, |
228 | data + offset + i * len); | 273 | data + offset + i * len); |
229 | else if (!(flags & FIELD_IS_SIGNED)) | 274 | else { |
230 | ret = bt_ctf_field_unsigned_integer_set_value( | 275 | unsigned long long value_int; |
231 | field, value_int); | 276 | |
232 | else | 277 | value_int = pevent_read_number( |
233 | ret = bt_ctf_field_signed_integer_set_value( | 278 | fmtf->event->pevent, |
234 | field, value_int); | 279 | data + offset + i * len, len); |
280 | |||
281 | if (!(flags & FIELD_IS_SIGNED)) | ||
282 | ret = bt_ctf_field_unsigned_integer_set_value( | ||
283 | field, value_int); | ||
284 | else | ||
285 | ret = bt_ctf_field_signed_integer_set_value( | ||
286 | field, adjust_signedness(value_int, len)); | ||
287 | } | ||
288 | |||
235 | if (ret) { | 289 | if (ret) { |
236 | pr_err("failed to set file value %s\n", name); | 290 | pr_err("failed to set file value %s\n", name); |
237 | goto err_put_field; | 291 | goto err_put_field; |
@@ -346,12 +400,6 @@ static int add_generic_values(struct ctf_writer *cw, | |||
346 | return -1; | 400 | return -1; |
347 | } | 401 | } |
348 | 402 | ||
349 | if (type & PERF_SAMPLE_CPU) { | ||
350 | ret = value_set_u32(cw, event, "perf_cpu", sample->cpu); | ||
351 | if (ret) | ||
352 | return -1; | ||
353 | } | ||
354 | |||
355 | if (type & PERF_SAMPLE_PERIOD) { | 403 | if (type & PERF_SAMPLE_PERIOD) { |
356 | ret = value_set_u64(cw, event, "perf_period", sample->period); | 404 | ret = value_set_u64(cw, event, "perf_period", sample->period); |
357 | if (ret) | 405 | if (ret) |
@@ -381,6 +429,129 @@ static int add_generic_values(struct ctf_writer *cw, | |||
381 | return 0; | 429 | return 0; |
382 | } | 430 | } |
383 | 431 | ||
432 | static int ctf_stream__flush(struct ctf_stream *cs) | ||
433 | { | ||
434 | int err = 0; | ||
435 | |||
436 | if (cs) { | ||
437 | err = bt_ctf_stream_flush(cs->stream); | ||
438 | if (err) | ||
439 | pr_err("CTF stream %d flush failed\n", cs->cpu); | ||
440 | |||
441 | pr("Flush stream for cpu %d (%u samples)\n", | ||
442 | cs->cpu, cs->count); | ||
443 | |||
444 | cs->count = 0; | ||
445 | } | ||
446 | |||
447 | return err; | ||
448 | } | ||
449 | |||
450 | static struct ctf_stream *ctf_stream__create(struct ctf_writer *cw, int cpu) | ||
451 | { | ||
452 | struct ctf_stream *cs; | ||
453 | struct bt_ctf_field *pkt_ctx = NULL; | ||
454 | struct bt_ctf_field *cpu_field = NULL; | ||
455 | struct bt_ctf_stream *stream = NULL; | ||
456 | int ret; | ||
457 | |||
458 | cs = zalloc(sizeof(*cs)); | ||
459 | if (!cs) { | ||
460 | pr_err("Failed to allocate ctf stream\n"); | ||
461 | return NULL; | ||
462 | } | ||
463 | |||
464 | stream = bt_ctf_writer_create_stream(cw->writer, cw->stream_class); | ||
465 | if (!stream) { | ||
466 | pr_err("Failed to create CTF stream\n"); | ||
467 | goto out; | ||
468 | } | ||
469 | |||
470 | pkt_ctx = bt_ctf_stream_get_packet_context(stream); | ||
471 | if (!pkt_ctx) { | ||
472 | pr_err("Failed to obtain packet context\n"); | ||
473 | goto out; | ||
474 | } | ||
475 | |||
476 | cpu_field = bt_ctf_field_structure_get_field(pkt_ctx, "cpu_id"); | ||
477 | bt_ctf_field_put(pkt_ctx); | ||
478 | if (!cpu_field) { | ||
479 | pr_err("Failed to obtain cpu field\n"); | ||
480 | goto out; | ||
481 | } | ||
482 | |||
483 | ret = bt_ctf_field_unsigned_integer_set_value(cpu_field, (u32) cpu); | ||
484 | if (ret) { | ||
485 | pr_err("Failed to update CPU number\n"); | ||
486 | goto out; | ||
487 | } | ||
488 | |||
489 | bt_ctf_field_put(cpu_field); | ||
490 | |||
491 | cs->cpu = cpu; | ||
492 | cs->stream = stream; | ||
493 | return cs; | ||
494 | |||
495 | out: | ||
496 | if (cpu_field) | ||
497 | bt_ctf_field_put(cpu_field); | ||
498 | if (stream) | ||
499 | bt_ctf_stream_put(stream); | ||
500 | |||
501 | free(cs); | ||
502 | return NULL; | ||
503 | } | ||
504 | |||
505 | static void ctf_stream__delete(struct ctf_stream *cs) | ||
506 | { | ||
507 | if (cs) { | ||
508 | bt_ctf_stream_put(cs->stream); | ||
509 | free(cs); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | static struct ctf_stream *ctf_stream(struct ctf_writer *cw, int cpu) | ||
514 | { | ||
515 | struct ctf_stream *cs = cw->stream[cpu]; | ||
516 | |||
517 | if (!cs) { | ||
518 | cs = ctf_stream__create(cw, cpu); | ||
519 | cw->stream[cpu] = cs; | ||
520 | } | ||
521 | |||
522 | return cs; | ||
523 | } | ||
524 | |||
525 | static int get_sample_cpu(struct ctf_writer *cw, struct perf_sample *sample, | ||
526 | struct perf_evsel *evsel) | ||
527 | { | ||
528 | int cpu = 0; | ||
529 | |||
530 | if (evsel->attr.sample_type & PERF_SAMPLE_CPU) | ||
531 | cpu = sample->cpu; | ||
532 | |||
533 | if (cpu > cw->stream_cnt) { | ||
534 | pr_err("Event was recorded for CPU %d, limit is at %d.\n", | ||
535 | cpu, cw->stream_cnt); | ||
536 | cpu = 0; | ||
537 | } | ||
538 | |||
539 | return cpu; | ||
540 | } | ||
541 | |||
542 | #define STREAM_FLUSH_COUNT 100000 | ||
543 | |||
544 | /* | ||
545 | * Currently we have no other way to determine the | ||
546 | * time for the stream flush other than keep track | ||
547 | * of the number of events and check it against | ||
548 | * threshold. | ||
549 | */ | ||
550 | static bool is_flush_needed(struct ctf_stream *cs) | ||
551 | { | ||
552 | return cs->count >= STREAM_FLUSH_COUNT; | ||
553 | } | ||
554 | |||
384 | static int process_sample_event(struct perf_tool *tool, | 555 | static int process_sample_event(struct perf_tool *tool, |
385 | union perf_event *_event __maybe_unused, | 556 | union perf_event *_event __maybe_unused, |
386 | struct perf_sample *sample, | 557 | struct perf_sample *sample, |
@@ -390,6 +561,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
390 | struct convert *c = container_of(tool, struct convert, tool); | 561 | struct convert *c = container_of(tool, struct convert, tool); |
391 | struct evsel_priv *priv = evsel->priv; | 562 | struct evsel_priv *priv = evsel->priv; |
392 | struct ctf_writer *cw = &c->writer; | 563 | struct ctf_writer *cw = &c->writer; |
564 | struct ctf_stream *cs; | ||
393 | struct bt_ctf_event_class *event_class; | 565 | struct bt_ctf_event_class *event_class; |
394 | struct bt_ctf_event *event; | 566 | struct bt_ctf_event *event; |
395 | int ret; | 567 | int ret; |
@@ -424,9 +596,93 @@ static int process_sample_event(struct perf_tool *tool, | |||
424 | return -1; | 596 | return -1; |
425 | } | 597 | } |
426 | 598 | ||
427 | bt_ctf_stream_append_event(cw->stream, event); | 599 | cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel)); |
600 | if (cs) { | ||
601 | if (is_flush_needed(cs)) | ||
602 | ctf_stream__flush(cs); | ||
603 | |||
604 | cs->count++; | ||
605 | bt_ctf_stream_append_event(cs->stream, event); | ||
606 | } | ||
607 | |||
428 | bt_ctf_event_put(event); | 608 | bt_ctf_event_put(event); |
429 | return 0; | 609 | return cs ? 0 : -1; |
610 | } | ||
611 | |||
612 | /* If dup < 0, add a prefix. Else, add _dupl_X suffix. */ | ||
613 | static char *change_name(char *name, char *orig_name, int dup) | ||
614 | { | ||
615 | char *new_name = NULL; | ||
616 | size_t len; | ||
617 | |||
618 | if (!name) | ||
619 | name = orig_name; | ||
620 | |||
621 | if (dup >= 10) | ||
622 | goto out; | ||
623 | /* | ||
624 | * Add '_' prefix to potential keywork. According to | ||
625 | * Mathieu Desnoyers (https://lkml.org/lkml/2015/1/23/652), | ||
626 | * futher CTF spec updating may require us to use '$'. | ||
627 | */ | ||
628 | if (dup < 0) | ||
629 | len = strlen(name) + sizeof("_"); | ||
630 | else | ||
631 | len = strlen(orig_name) + sizeof("_dupl_X"); | ||
632 | |||
633 | new_name = malloc(len); | ||
634 | if (!new_name) | ||
635 | goto out; | ||
636 | |||
637 | if (dup < 0) | ||
638 | snprintf(new_name, len, "_%s", name); | ||
639 | else | ||
640 | snprintf(new_name, len, "%s_dupl_%d", orig_name, dup); | ||
641 | |||
642 | out: | ||
643 | if (name != orig_name) | ||
644 | free(name); | ||
645 | return new_name; | ||
646 | } | ||
647 | |||
648 | static int event_class_add_field(struct bt_ctf_event_class *event_class, | ||
649 | struct bt_ctf_field_type *type, | ||
650 | struct format_field *field) | ||
651 | { | ||
652 | struct bt_ctf_field_type *t = NULL; | ||
653 | char *name; | ||
654 | int dup = 1; | ||
655 | int ret; | ||
656 | |||
657 | /* alias was already assigned */ | ||
658 | if (field->alias != field->name) | ||
659 | return bt_ctf_event_class_add_field(event_class, type, | ||
660 | (char *)field->alias); | ||
661 | |||
662 | name = field->name; | ||
663 | |||
664 | /* If 'name' is a keywork, add prefix. */ | ||
665 | if (bt_ctf_validate_identifier(name)) | ||
666 | name = change_name(name, field->name, -1); | ||
667 | |||
668 | if (!name) { | ||
669 | pr_err("Failed to fix invalid identifier."); | ||
670 | return -1; | ||
671 | } | ||
672 | while ((t = bt_ctf_event_class_get_field_by_name(event_class, name))) { | ||
673 | bt_ctf_field_type_put(t); | ||
674 | name = change_name(name, field->name, dup++); | ||
675 | if (!name) { | ||
676 | pr_err("Failed to create dup name for '%s'\n", field->name); | ||
677 | return -1; | ||
678 | } | ||
679 | } | ||
680 | |||
681 | ret = bt_ctf_event_class_add_field(event_class, type, name); | ||
682 | if (!ret) | ||
683 | field->alias = name; | ||
684 | |||
685 | return ret; | ||
430 | } | 686 | } |
431 | 687 | ||
432 | static int add_tracepoint_fields_types(struct ctf_writer *cw, | 688 | static int add_tracepoint_fields_types(struct ctf_writer *cw, |
@@ -457,14 +713,14 @@ static int add_tracepoint_fields_types(struct ctf_writer *cw, | |||
457 | if (flags & FIELD_IS_ARRAY) | 713 | if (flags & FIELD_IS_ARRAY) |
458 | type = bt_ctf_field_type_array_create(type, field->arraylen); | 714 | type = bt_ctf_field_type_array_create(type, field->arraylen); |
459 | 715 | ||
460 | ret = bt_ctf_event_class_add_field(event_class, type, | 716 | ret = event_class_add_field(event_class, type, field); |
461 | field->name); | ||
462 | 717 | ||
463 | if (flags & FIELD_IS_ARRAY) | 718 | if (flags & FIELD_IS_ARRAY) |
464 | bt_ctf_field_type_put(type); | 719 | bt_ctf_field_type_put(type); |
465 | 720 | ||
466 | if (ret) { | 721 | if (ret) { |
467 | pr_err("Failed to add field '%s\n", field->name); | 722 | pr_err("Failed to add field '%s': %d\n", |
723 | field->name, ret); | ||
468 | return -1; | 724 | return -1; |
469 | } | 725 | } |
470 | } | 726 | } |
@@ -508,7 +764,7 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, | |||
508 | do { \ | 764 | do { \ |
509 | pr2(" field '%s'\n", n); \ | 765 | pr2(" field '%s'\n", n); \ |
510 | if (bt_ctf_event_class_add_field(cl, t, n)) { \ | 766 | if (bt_ctf_event_class_add_field(cl, t, n)) { \ |
511 | pr_err("Failed to add field '%s;\n", n); \ | 767 | pr_err("Failed to add field '%s';\n", n); \ |
512 | return -1; \ | 768 | return -1; \ |
513 | } \ | 769 | } \ |
514 | } while (0) | 770 | } while (0) |
@@ -528,9 +784,6 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, | |||
528 | if (type & PERF_SAMPLE_STREAM_ID) | 784 | if (type & PERF_SAMPLE_STREAM_ID) |
529 | ADD_FIELD(event_class, cw->data.u64, "perf_stream_id"); | 785 | ADD_FIELD(event_class, cw->data.u64, "perf_stream_id"); |
530 | 786 | ||
531 | if (type & PERF_SAMPLE_CPU) | ||
532 | ADD_FIELD(event_class, cw->data.u32, "perf_cpu"); | ||
533 | |||
534 | if (type & PERF_SAMPLE_PERIOD) | 787 | if (type & PERF_SAMPLE_PERIOD) |
535 | ADD_FIELD(event_class, cw->data.u64, "perf_period"); | 788 | ADD_FIELD(event_class, cw->data.u64, "perf_period"); |
536 | 789 | ||
@@ -604,6 +857,39 @@ static int setup_events(struct ctf_writer *cw, struct perf_session *session) | |||
604 | return 0; | 857 | return 0; |
605 | } | 858 | } |
606 | 859 | ||
860 | static int setup_streams(struct ctf_writer *cw, struct perf_session *session) | ||
861 | { | ||
862 | struct ctf_stream **stream; | ||
863 | struct perf_header *ph = &session->header; | ||
864 | int ncpus; | ||
865 | |||
866 | /* | ||
867 | * Try to get the number of cpus used in the data file, | ||
868 | * if not present fallback to the MAX_CPUS. | ||
869 | */ | ||
870 | ncpus = ph->env.nr_cpus_avail ?: MAX_CPUS; | ||
871 | |||
872 | stream = zalloc(sizeof(*stream) * ncpus); | ||
873 | if (!stream) { | ||
874 | pr_err("Failed to allocate streams.\n"); | ||
875 | return -ENOMEM; | ||
876 | } | ||
877 | |||
878 | cw->stream = stream; | ||
879 | cw->stream_cnt = ncpus; | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | static void free_streams(struct ctf_writer *cw) | ||
884 | { | ||
885 | int cpu; | ||
886 | |||
887 | for (cpu = 0; cpu < cw->stream_cnt; cpu++) | ||
888 | ctf_stream__delete(cw->stream[cpu]); | ||
889 | |||
890 | free(cw->stream); | ||
891 | } | ||
892 | |||
607 | static int ctf_writer__setup_env(struct ctf_writer *cw, | 893 | static int ctf_writer__setup_env(struct ctf_writer *cw, |
608 | struct perf_session *session) | 894 | struct perf_session *session) |
609 | { | 895 | { |
@@ -713,7 +999,7 @@ static void ctf_writer__cleanup(struct ctf_writer *cw) | |||
713 | ctf_writer__cleanup_data(cw); | 999 | ctf_writer__cleanup_data(cw); |
714 | 1000 | ||
715 | bt_ctf_clock_put(cw->clock); | 1001 | bt_ctf_clock_put(cw->clock); |
716 | bt_ctf_stream_put(cw->stream); | 1002 | free_streams(cw); |
717 | bt_ctf_stream_class_put(cw->stream_class); | 1003 | bt_ctf_stream_class_put(cw->stream_class); |
718 | bt_ctf_writer_put(cw->writer); | 1004 | bt_ctf_writer_put(cw->writer); |
719 | 1005 | ||
@@ -725,8 +1011,9 @@ static int ctf_writer__init(struct ctf_writer *cw, const char *path) | |||
725 | { | 1011 | { |
726 | struct bt_ctf_writer *writer; | 1012 | struct bt_ctf_writer *writer; |
727 | struct bt_ctf_stream_class *stream_class; | 1013 | struct bt_ctf_stream_class *stream_class; |
728 | struct bt_ctf_stream *stream; | ||
729 | struct bt_ctf_clock *clock; | 1014 | struct bt_ctf_clock *clock; |
1015 | struct bt_ctf_field_type *pkt_ctx_type; | ||
1016 | int ret; | ||
730 | 1017 | ||
731 | /* CTF writer */ | 1018 | /* CTF writer */ |
732 | writer = bt_ctf_writer_create(path); | 1019 | writer = bt_ctf_writer_create(path); |
@@ -767,14 +1054,15 @@ static int ctf_writer__init(struct ctf_writer *cw, const char *path) | |||
767 | if (ctf_writer__init_data(cw)) | 1054 | if (ctf_writer__init_data(cw)) |
768 | goto err_cleanup; | 1055 | goto err_cleanup; |
769 | 1056 | ||
770 | /* CTF stream instance */ | 1057 | /* Add cpu_id for packet context */ |
771 | stream = bt_ctf_writer_create_stream(writer, stream_class); | 1058 | pkt_ctx_type = bt_ctf_stream_class_get_packet_context_type(stream_class); |
772 | if (!stream) { | 1059 | if (!pkt_ctx_type) |
773 | pr("Failed to create CTF stream.\n"); | ||
774 | goto err_cleanup; | 1060 | goto err_cleanup; |
775 | } | ||
776 | 1061 | ||
777 | cw->stream = stream; | 1062 | ret = bt_ctf_field_type_structure_add_field(pkt_ctx_type, cw->data.u32, "cpu_id"); |
1063 | bt_ctf_field_type_put(pkt_ctx_type); | ||
1064 | if (ret) | ||
1065 | goto err_cleanup; | ||
778 | 1066 | ||
779 | /* CTF clock writer setup */ | 1067 | /* CTF clock writer setup */ |
780 | if (bt_ctf_writer_add_clock(writer, clock)) { | 1068 | if (bt_ctf_writer_add_clock(writer, clock)) { |
@@ -791,6 +1079,28 @@ err: | |||
791 | return -1; | 1079 | return -1; |
792 | } | 1080 | } |
793 | 1081 | ||
1082 | static int ctf_writer__flush_streams(struct ctf_writer *cw) | ||
1083 | { | ||
1084 | int cpu, ret = 0; | ||
1085 | |||
1086 | for (cpu = 0; cpu < cw->stream_cnt && !ret; cpu++) | ||
1087 | ret = ctf_stream__flush(cw->stream[cpu]); | ||
1088 | |||
1089 | return ret; | ||
1090 | } | ||
1091 | |||
1092 | static int convert__config(const char *var, const char *value, void *cb) | ||
1093 | { | ||
1094 | struct convert *c = cb; | ||
1095 | |||
1096 | if (!strcmp(var, "convert.queue-size")) { | ||
1097 | c->queue_size = perf_config_u64(var, value); | ||
1098 | return 0; | ||
1099 | } | ||
1100 | |||
1101 | return perf_default_config(var, value, cb); | ||
1102 | } | ||
1103 | |||
794 | int bt_convert__perf2ctf(const char *input, const char *path, bool force) | 1104 | int bt_convert__perf2ctf(const char *input, const char *path, bool force) |
795 | { | 1105 | { |
796 | struct perf_session *session; | 1106 | struct perf_session *session; |
@@ -817,6 +1127,8 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) | |||
817 | struct ctf_writer *cw = &c.writer; | 1127 | struct ctf_writer *cw = &c.writer; |
818 | int err = -1; | 1128 | int err = -1; |
819 | 1129 | ||
1130 | perf_config(convert__config, &c); | ||
1131 | |||
820 | /* CTF writer */ | 1132 | /* CTF writer */ |
821 | if (ctf_writer__init(cw, path)) | 1133 | if (ctf_writer__init(cw, path)) |
822 | return -1; | 1134 | return -1; |
@@ -826,6 +1138,11 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) | |||
826 | if (!session) | 1138 | if (!session) |
827 | goto free_writer; | 1139 | goto free_writer; |
828 | 1140 | ||
1141 | if (c.queue_size) { | ||
1142 | ordered_events__set_alloc_size(&session->ordered_events, | ||
1143 | c.queue_size); | ||
1144 | } | ||
1145 | |||
829 | /* CTF writer env/clock setup */ | 1146 | /* CTF writer env/clock setup */ |
830 | if (ctf_writer__setup_env(cw, session)) | 1147 | if (ctf_writer__setup_env(cw, session)) |
831 | goto free_session; | 1148 | goto free_session; |
@@ -834,9 +1151,14 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) | |||
834 | if (setup_events(cw, session)) | 1151 | if (setup_events(cw, session)) |
835 | goto free_session; | 1152 | goto free_session; |
836 | 1153 | ||
1154 | if (setup_streams(cw, session)) | ||
1155 | goto free_session; | ||
1156 | |||
837 | err = perf_session__process_events(session); | 1157 | err = perf_session__process_events(session); |
838 | if (!err) | 1158 | if (!err) |
839 | err = bt_ctf_stream_flush(cw->stream); | 1159 | err = ctf_writer__flush_streams(cw); |
1160 | else | ||
1161 | pr_err("Error during conversion.\n"); | ||
840 | 1162 | ||
841 | fprintf(stderr, | 1163 | fprintf(stderr, |
842 | "[ perf data convert: Converted '%s' into CTF data '%s' ]\n", | 1164 | "[ perf data convert: Converted '%s' into CTF data '%s' ]\n", |
@@ -847,11 +1169,15 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) | |||
847 | (double) c.events_size / 1024.0 / 1024.0, | 1169 | (double) c.events_size / 1024.0 / 1024.0, |
848 | c.events_count); | 1170 | c.events_count); |
849 | 1171 | ||
850 | /* its all good */ | ||
851 | free_session: | ||
852 | perf_session__delete(session); | 1172 | perf_session__delete(session); |
1173 | ctf_writer__cleanup(cw); | ||
1174 | |||
1175 | return err; | ||
853 | 1176 | ||
1177 | free_session: | ||
1178 | perf_session__delete(session); | ||
854 | free_writer: | 1179 | free_writer: |
855 | ctf_writer__cleanup(cw); | 1180 | ctf_writer__cleanup(cw); |
1181 | pr_err("Error during conversion setup.\n"); | ||
856 | return err; | 1182 | return err; |
857 | } | 1183 | } |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index fc0ddd5792a9..13d9ae0bd15c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "symbol.h" | 4 | #include "symbol.h" |
5 | #include "dso.h" | 5 | #include "dso.h" |
6 | #include "machine.h" | 6 | #include "machine.h" |
7 | #include "auxtrace.h" | ||
7 | #include "util.h" | 8 | #include "util.h" |
8 | #include "debug.h" | 9 | #include "debug.h" |
9 | 10 | ||
@@ -961,6 +962,7 @@ void dso__delete(struct dso *dso) | |||
961 | } | 962 | } |
962 | 963 | ||
963 | dso__data_close(dso); | 964 | dso__data_close(dso); |
965 | auxtrace_cache__free(dso->auxtrace_cache); | ||
964 | dso_cache__free(&dso->data.cache); | 966 | dso_cache__free(&dso->data.cache); |
965 | dso__free_a2l(dso); | 967 | dso__free_a2l(dso); |
966 | zfree(&dso->symsrc_filename); | 968 | zfree(&dso->symsrc_filename); |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index e0901b4ed8de..3d79c749934c 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -126,6 +126,8 @@ struct dsos { | |||
126 | struct rb_root root; /* rbtree root sorted by long name */ | 126 | struct rb_root root; /* rbtree root sorted by long name */ |
127 | }; | 127 | }; |
128 | 128 | ||
129 | struct auxtrace_cache; | ||
130 | |||
129 | struct dso { | 131 | struct dso { |
130 | struct list_head node; | 132 | struct list_head node; |
131 | struct rb_node rb_node; /* rbtree node sorted by long name */ | 133 | struct rb_node rb_node; /* rbtree node sorted by long name */ |
@@ -156,6 +158,7 @@ struct dso { | |||
156 | u16 long_name_len; | 158 | u16 long_name_len; |
157 | u16 short_name_len; | 159 | u16 short_name_len; |
158 | void *dwfl; /* DWARF debug info */ | 160 | void *dwfl; /* DWARF debug info */ |
161 | struct auxtrace_cache *auxtrace_cache; | ||
159 | 162 | ||
160 | /* dso data file */ | 163 | /* dso data file */ |
161 | struct { | 164 | struct { |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ff866c4d2e2f..db526091f580 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -23,12 +23,17 @@ static const char *perf_event__names[] = { | |||
23 | [PERF_RECORD_FORK] = "FORK", | 23 | [PERF_RECORD_FORK] = "FORK", |
24 | [PERF_RECORD_READ] = "READ", | 24 | [PERF_RECORD_READ] = "READ", |
25 | [PERF_RECORD_SAMPLE] = "SAMPLE", | 25 | [PERF_RECORD_SAMPLE] = "SAMPLE", |
26 | [PERF_RECORD_AUX] = "AUX", | ||
27 | [PERF_RECORD_ITRACE_START] = "ITRACE_START", | ||
26 | [PERF_RECORD_HEADER_ATTR] = "ATTR", | 28 | [PERF_RECORD_HEADER_ATTR] = "ATTR", |
27 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | 29 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", |
28 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 30 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
29 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | 31 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", |
30 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 32 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
31 | [PERF_RECORD_ID_INDEX] = "ID_INDEX", | 33 | [PERF_RECORD_ID_INDEX] = "ID_INDEX", |
34 | [PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO", | ||
35 | [PERF_RECORD_AUXTRACE] = "AUXTRACE", | ||
36 | [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", | ||
32 | }; | 37 | }; |
33 | 38 | ||
34 | const char *perf_event__name(unsigned int id) | 39 | const char *perf_event__name(unsigned int id) |
@@ -692,6 +697,22 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | |||
692 | return machine__process_lost_event(machine, event, sample); | 697 | return machine__process_lost_event(machine, event, sample); |
693 | } | 698 | } |
694 | 699 | ||
700 | int perf_event__process_aux(struct perf_tool *tool __maybe_unused, | ||
701 | union perf_event *event, | ||
702 | struct perf_sample *sample __maybe_unused, | ||
703 | struct machine *machine) | ||
704 | { | ||
705 | return machine__process_aux_event(machine, event); | ||
706 | } | ||
707 | |||
708 | int perf_event__process_itrace_start(struct perf_tool *tool __maybe_unused, | ||
709 | union perf_event *event, | ||
710 | struct perf_sample *sample __maybe_unused, | ||
711 | struct machine *machine) | ||
712 | { | ||
713 | return machine__process_itrace_start_event(machine, event); | ||
714 | } | ||
715 | |||
695 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 716 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
696 | { | 717 | { |
697 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", | 718 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n", |
@@ -755,6 +776,21 @@ int perf_event__process_exit(struct perf_tool *tool __maybe_unused, | |||
755 | return machine__process_exit_event(machine, event, sample); | 776 | return machine__process_exit_event(machine, event, sample); |
756 | } | 777 | } |
757 | 778 | ||
779 | size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp) | ||
780 | { | ||
781 | return fprintf(fp, " offset: %#"PRIx64" size: %#"PRIx64" flags: %#"PRIx64" [%s%s]\n", | ||
782 | event->aux.aux_offset, event->aux.aux_size, | ||
783 | event->aux.flags, | ||
784 | event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "", | ||
785 | event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : ""); | ||
786 | } | ||
787 | |||
788 | size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp) | ||
789 | { | ||
790 | return fprintf(fp, " pid: %u tid: %u\n", | ||
791 | event->itrace_start.pid, event->itrace_start.tid); | ||
792 | } | ||
793 | |||
758 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) | 794 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) |
759 | { | 795 | { |
760 | size_t ret = fprintf(fp, "PERF_RECORD_%s", | 796 | size_t ret = fprintf(fp, "PERF_RECORD_%s", |
@@ -774,6 +810,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
774 | case PERF_RECORD_MMAP2: | 810 | case PERF_RECORD_MMAP2: |
775 | ret += perf_event__fprintf_mmap2(event, fp); | 811 | ret += perf_event__fprintf_mmap2(event, fp); |
776 | break; | 812 | break; |
813 | case PERF_RECORD_AUX: | ||
814 | ret += perf_event__fprintf_aux(event, fp); | ||
815 | break; | ||
816 | case PERF_RECORD_ITRACE_START: | ||
817 | ret += perf_event__fprintf_itrace_start(event, fp); | ||
818 | break; | ||
777 | default: | 819 | default: |
778 | ret += fprintf(fp, "\n"); | 820 | ret += fprintf(fp, "\n"); |
779 | } | 821 | } |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 09b9e8d3fcf7..7eecd5e23d77 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -157,6 +157,8 @@ enum { | |||
157 | PERF_IP_FLAG_IN_TX = 1ULL << 10, | 157 | PERF_IP_FLAG_IN_TX = 1ULL << 10, |
158 | }; | 158 | }; |
159 | 159 | ||
160 | #define PERF_IP_FLAG_CHARS "bcrosyiABEx" | ||
161 | |||
160 | #define PERF_BRANCH_MASK (\ | 162 | #define PERF_BRANCH_MASK (\ |
161 | PERF_IP_FLAG_BRANCH |\ | 163 | PERF_IP_FLAG_BRANCH |\ |
162 | PERF_IP_FLAG_CALL |\ | 164 | PERF_IP_FLAG_CALL |\ |
@@ -215,9 +217,17 @@ enum perf_user_event_type { /* above any possible kernel type */ | |||
215 | PERF_RECORD_HEADER_BUILD_ID = 67, | 217 | PERF_RECORD_HEADER_BUILD_ID = 67, |
216 | PERF_RECORD_FINISHED_ROUND = 68, | 218 | PERF_RECORD_FINISHED_ROUND = 68, |
217 | PERF_RECORD_ID_INDEX = 69, | 219 | PERF_RECORD_ID_INDEX = 69, |
220 | PERF_RECORD_AUXTRACE_INFO = 70, | ||
221 | PERF_RECORD_AUXTRACE = 71, | ||
222 | PERF_RECORD_AUXTRACE_ERROR = 72, | ||
218 | PERF_RECORD_HEADER_MAX | 223 | PERF_RECORD_HEADER_MAX |
219 | }; | 224 | }; |
220 | 225 | ||
226 | enum auxtrace_error_type { | ||
227 | PERF_AUXTRACE_ERROR_ITRACE = 1, | ||
228 | PERF_AUXTRACE_ERROR_MAX | ||
229 | }; | ||
230 | |||
221 | /* | 231 | /* |
222 | * The kernel collects the number of events it couldn't send in a stretch and | 232 | * The kernel collects the number of events it couldn't send in a stretch and |
223 | * when possible sends this number in a PERF_RECORD_LOST event. The number of | 233 | * when possible sends this number in a PERF_RECORD_LOST event. The number of |
@@ -242,6 +252,7 @@ struct events_stats { | |||
242 | u32 nr_invalid_chains; | 252 | u32 nr_invalid_chains; |
243 | u32 nr_unknown_id; | 253 | u32 nr_unknown_id; |
244 | u32 nr_unprocessable_samples; | 254 | u32 nr_unprocessable_samples; |
255 | u32 nr_auxtrace_errors[PERF_AUXTRACE_ERROR_MAX]; | ||
245 | }; | 256 | }; |
246 | 257 | ||
247 | struct attr_event { | 258 | struct attr_event { |
@@ -280,6 +291,50 @@ struct id_index_event { | |||
280 | struct id_index_entry entries[0]; | 291 | struct id_index_entry entries[0]; |
281 | }; | 292 | }; |
282 | 293 | ||
294 | struct auxtrace_info_event { | ||
295 | struct perf_event_header header; | ||
296 | u32 type; | ||
297 | u32 reserved__; /* For alignment */ | ||
298 | u64 priv[]; | ||
299 | }; | ||
300 | |||
301 | struct auxtrace_event { | ||
302 | struct perf_event_header header; | ||
303 | u64 size; | ||
304 | u64 offset; | ||
305 | u64 reference; | ||
306 | u32 idx; | ||
307 | u32 tid; | ||
308 | u32 cpu; | ||
309 | u32 reserved__; /* For alignment */ | ||
310 | }; | ||
311 | |||
312 | #define MAX_AUXTRACE_ERROR_MSG 64 | ||
313 | |||
314 | struct auxtrace_error_event { | ||
315 | struct perf_event_header header; | ||
316 | u32 type; | ||
317 | u32 code; | ||
318 | u32 cpu; | ||
319 | u32 pid; | ||
320 | u32 tid; | ||
321 | u32 reserved__; /* For alignment */ | ||
322 | u64 ip; | ||
323 | char msg[MAX_AUXTRACE_ERROR_MSG]; | ||
324 | }; | ||
325 | |||
326 | struct aux_event { | ||
327 | struct perf_event_header header; | ||
328 | u64 aux_offset; | ||
329 | u64 aux_size; | ||
330 | u64 flags; | ||
331 | }; | ||
332 | |||
333 | struct itrace_start_event { | ||
334 | struct perf_event_header header; | ||
335 | u32 pid, tid; | ||
336 | }; | ||
337 | |||
283 | union perf_event { | 338 | union perf_event { |
284 | struct perf_event_header header; | 339 | struct perf_event_header header; |
285 | struct mmap_event mmap; | 340 | struct mmap_event mmap; |
@@ -295,6 +350,11 @@ union perf_event { | |||
295 | struct tracing_data_event tracing_data; | 350 | struct tracing_data_event tracing_data; |
296 | struct build_id_event build_id; | 351 | struct build_id_event build_id; |
297 | struct id_index_event id_index; | 352 | struct id_index_event id_index; |
353 | struct auxtrace_info_event auxtrace_info; | ||
354 | struct auxtrace_event auxtrace; | ||
355 | struct auxtrace_error_event auxtrace_error; | ||
356 | struct aux_event aux; | ||
357 | struct itrace_start_event itrace_start; | ||
298 | }; | 358 | }; |
299 | 359 | ||
300 | void perf_event__print_totals(void); | 360 | void perf_event__print_totals(void); |
@@ -330,6 +390,14 @@ int perf_event__process_lost(struct perf_tool *tool, | |||
330 | union perf_event *event, | 390 | union perf_event *event, |
331 | struct perf_sample *sample, | 391 | struct perf_sample *sample, |
332 | struct machine *machine); | 392 | struct machine *machine); |
393 | int perf_event__process_aux(struct perf_tool *tool, | ||
394 | union perf_event *event, | ||
395 | struct perf_sample *sample, | ||
396 | struct machine *machine); | ||
397 | int perf_event__process_itrace_start(struct perf_tool *tool, | ||
398 | union perf_event *event, | ||
399 | struct perf_sample *sample, | ||
400 | struct machine *machine); | ||
333 | int perf_event__process_mmap(struct perf_tool *tool, | 401 | int perf_event__process_mmap(struct perf_tool *tool, |
334 | union perf_event *event, | 402 | union perf_event *event, |
335 | struct perf_sample *sample, | 403 | struct perf_sample *sample, |
@@ -387,6 +455,8 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); | |||
387 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); | 455 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); |
388 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); | 456 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); |
389 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); | 457 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); |
458 | size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); | ||
459 | size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); | ||
390 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); | 460 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); |
391 | 461 | ||
392 | u64 kallsyms__get_function_start(const char *kallsyms_filename, | 462 | u64 kallsyms__get_function_start(const char *kallsyms_filename, |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 080be93eea96..7ec1bf93ab28 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -695,7 +695,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
695 | 695 | ||
696 | static bool perf_mmap__empty(struct perf_mmap *md) | 696 | static bool perf_mmap__empty(struct perf_mmap *md) |
697 | { | 697 | { |
698 | return perf_mmap__read_head(md) == md->prev; | 698 | return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base; |
699 | } | 699 | } |
700 | 700 | ||
701 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) | 701 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) |
@@ -725,6 +725,34 @@ void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | |||
725 | perf_evlist__mmap_put(evlist, idx); | 725 | perf_evlist__mmap_put(evlist, idx); |
726 | } | 726 | } |
727 | 727 | ||
728 | int __weak auxtrace_mmap__mmap(struct auxtrace_mmap *mm __maybe_unused, | ||
729 | struct auxtrace_mmap_params *mp __maybe_unused, | ||
730 | void *userpg __maybe_unused, | ||
731 | int fd __maybe_unused) | ||
732 | { | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | void __weak auxtrace_mmap__munmap(struct auxtrace_mmap *mm __maybe_unused) | ||
737 | { | ||
738 | } | ||
739 | |||
740 | void __weak auxtrace_mmap_params__init( | ||
741 | struct auxtrace_mmap_params *mp __maybe_unused, | ||
742 | off_t auxtrace_offset __maybe_unused, | ||
743 | unsigned int auxtrace_pages __maybe_unused, | ||
744 | bool auxtrace_overwrite __maybe_unused) | ||
745 | { | ||
746 | } | ||
747 | |||
748 | void __weak auxtrace_mmap_params__set_idx( | ||
749 | struct auxtrace_mmap_params *mp __maybe_unused, | ||
750 | struct perf_evlist *evlist __maybe_unused, | ||
751 | int idx __maybe_unused, | ||
752 | bool per_cpu __maybe_unused) | ||
753 | { | ||
754 | } | ||
755 | |||
728 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 756 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
729 | { | 757 | { |
730 | if (evlist->mmap[idx].base != NULL) { | 758 | if (evlist->mmap[idx].base != NULL) { |
@@ -732,6 +760,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | |||
732 | evlist->mmap[idx].base = NULL; | 760 | evlist->mmap[idx].base = NULL; |
733 | evlist->mmap[idx].refcnt = 0; | 761 | evlist->mmap[idx].refcnt = 0; |
734 | } | 762 | } |
763 | auxtrace_mmap__munmap(&evlist->mmap[idx].auxtrace_mmap); | ||
735 | } | 764 | } |
736 | 765 | ||
737 | void perf_evlist__munmap(struct perf_evlist *evlist) | 766 | void perf_evlist__munmap(struct perf_evlist *evlist) |
@@ -759,6 +788,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | |||
759 | struct mmap_params { | 788 | struct mmap_params { |
760 | int prot; | 789 | int prot; |
761 | int mask; | 790 | int mask; |
791 | struct auxtrace_mmap_params auxtrace_mp; | ||
762 | }; | 792 | }; |
763 | 793 | ||
764 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | 794 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, |
@@ -789,6 +819,10 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | |||
789 | return -1; | 819 | return -1; |
790 | } | 820 | } |
791 | 821 | ||
822 | if (auxtrace_mmap__mmap(&evlist->mmap[idx].auxtrace_mmap, | ||
823 | &mp->auxtrace_mp, evlist->mmap[idx].base, fd)) | ||
824 | return -1; | ||
825 | |||
792 | return 0; | 826 | return 0; |
793 | } | 827 | } |
794 | 828 | ||
@@ -853,6 +887,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, | |||
853 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 887 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
854 | int output = -1; | 888 | int output = -1; |
855 | 889 | ||
890 | auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, cpu, | ||
891 | true); | ||
892 | |||
856 | for (thread = 0; thread < nr_threads; thread++) { | 893 | for (thread = 0; thread < nr_threads; thread++) { |
857 | if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, | 894 | if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, |
858 | thread, &output)) | 895 | thread, &output)) |
@@ -878,6 +915,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, | |||
878 | for (thread = 0; thread < nr_threads; thread++) { | 915 | for (thread = 0; thread < nr_threads; thread++) { |
879 | int output = -1; | 916 | int output = -1; |
880 | 917 | ||
918 | auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, thread, | ||
919 | false); | ||
920 | |||
881 | if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread, | 921 | if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread, |
882 | &output)) | 922 | &output)) |
883 | goto out_unmap; | 923 | goto out_unmap; |
@@ -960,10 +1000,8 @@ static long parse_pages_arg(const char *str, unsigned long min, | |||
960 | return pages; | 1000 | return pages; |
961 | } | 1001 | } |
962 | 1002 | ||
963 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | 1003 | int __perf_evlist__parse_mmap_pages(unsigned int *mmap_pages, const char *str) |
964 | int unset __maybe_unused) | ||
965 | { | 1004 | { |
966 | unsigned int *mmap_pages = opt->value; | ||
967 | unsigned long max = UINT_MAX; | 1005 | unsigned long max = UINT_MAX; |
968 | long pages; | 1006 | long pages; |
969 | 1007 | ||
@@ -980,20 +1018,32 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | |||
980 | return 0; | 1018 | return 0; |
981 | } | 1019 | } |
982 | 1020 | ||
1021 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | ||
1022 | int unset __maybe_unused) | ||
1023 | { | ||
1024 | return __perf_evlist__parse_mmap_pages(opt->value, str); | ||
1025 | } | ||
1026 | |||
983 | /** | 1027 | /** |
984 | * perf_evlist__mmap - Create mmaps to receive events. | 1028 | * perf_evlist__mmap_ex - Create mmaps to receive events. |
985 | * @evlist: list of events | 1029 | * @evlist: list of events |
986 | * @pages: map length in pages | 1030 | * @pages: map length in pages |
987 | * @overwrite: overwrite older events? | 1031 | * @overwrite: overwrite older events? |
1032 | * @auxtrace_pages - auxtrace map length in pages | ||
1033 | * @auxtrace_overwrite - overwrite older auxtrace data? | ||
988 | * | 1034 | * |
989 | * If @overwrite is %false the user needs to signal event consumption using | 1035 | * If @overwrite is %false the user needs to signal event consumption using |
990 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this | 1036 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this |
991 | * automatically. | 1037 | * automatically. |
992 | * | 1038 | * |
1039 | * Similarly, if @auxtrace_overwrite is %false the user needs to signal data | ||
1040 | * consumption using auxtrace_mmap__write_tail(). | ||
1041 | * | ||
993 | * Return: %0 on success, negative error code otherwise. | 1042 | * Return: %0 on success, negative error code otherwise. |
994 | */ | 1043 | */ |
995 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 1044 | int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages, |
996 | bool overwrite) | 1045 | bool overwrite, unsigned int auxtrace_pages, |
1046 | bool auxtrace_overwrite) | ||
997 | { | 1047 | { |
998 | struct perf_evsel *evsel; | 1048 | struct perf_evsel *evsel; |
999 | const struct cpu_map *cpus = evlist->cpus; | 1049 | const struct cpu_map *cpus = evlist->cpus; |
@@ -1013,6 +1063,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
1013 | pr_debug("mmap size %zuB\n", evlist->mmap_len); | 1063 | pr_debug("mmap size %zuB\n", evlist->mmap_len); |
1014 | mp.mask = evlist->mmap_len - page_size - 1; | 1064 | mp.mask = evlist->mmap_len - page_size - 1; |
1015 | 1065 | ||
1066 | auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->mmap_len, | ||
1067 | auxtrace_pages, auxtrace_overwrite); | ||
1068 | |||
1016 | evlist__for_each(evlist, evsel) { | 1069 | evlist__for_each(evlist, evsel) { |
1017 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 1070 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
1018 | evsel->sample_id == NULL && | 1071 | evsel->sample_id == NULL && |
@@ -1026,6 +1079,12 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
1026 | return perf_evlist__mmap_per_cpu(evlist, &mp); | 1079 | return perf_evlist__mmap_per_cpu(evlist, &mp); |
1027 | } | 1080 | } |
1028 | 1081 | ||
1082 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | ||
1083 | bool overwrite) | ||
1084 | { | ||
1085 | return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false); | ||
1086 | } | ||
1087 | |||
1029 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) | 1088 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) |
1030 | { | 1089 | { |
1031 | evlist->threads = thread_map__new_str(target->pid, target->tid, | 1090 | evlist->threads = thread_map__new_str(target->pid, target->tid, |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index b5cce95d644e..c07b1a94a724 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -8,6 +8,7 @@ | |||
8 | #include "event.h" | 8 | #include "event.h" |
9 | #include "evsel.h" | 9 | #include "evsel.h" |
10 | #include "util.h" | 10 | #include "util.h" |
11 | #include "auxtrace.h" | ||
11 | #include <unistd.h> | 12 | #include <unistd.h> |
12 | 13 | ||
13 | struct pollfd; | 14 | struct pollfd; |
@@ -28,6 +29,7 @@ struct perf_mmap { | |||
28 | int mask; | 29 | int mask; |
29 | int refcnt; | 30 | int refcnt; |
30 | u64 prev; | 31 | u64 prev; |
32 | struct auxtrace_mmap auxtrace_mmap; | ||
31 | char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8))); | 33 | char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8))); |
32 | }; | 34 | }; |
33 | 35 | ||
@@ -122,10 +124,14 @@ int perf_evlist__start_workload(struct perf_evlist *evlist); | |||
122 | 124 | ||
123 | struct option; | 125 | struct option; |
124 | 126 | ||
127 | int __perf_evlist__parse_mmap_pages(unsigned int *mmap_pages, const char *str); | ||
125 | int perf_evlist__parse_mmap_pages(const struct option *opt, | 128 | int perf_evlist__parse_mmap_pages(const struct option *opt, |
126 | const char *str, | 129 | const char *str, |
127 | int unset); | 130 | int unset); |
128 | 131 | ||
132 | int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages, | ||
133 | bool overwrite, unsigned int auxtrace_pages, | ||
134 | bool auxtrace_overwrite); | ||
129 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 135 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
130 | bool overwrite); | 136 | bool overwrite); |
131 | void perf_evlist__munmap(struct perf_evlist *evlist); | 137 | void perf_evlist__munmap(struct perf_evlist *evlist); |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 33e3fd8c2e68..c886b9f7a48d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1121,6 +1121,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr, | |||
1121 | PRINT_ATTRf(sample_stack_user, p_unsigned); | 1121 | PRINT_ATTRf(sample_stack_user, p_unsigned); |
1122 | PRINT_ATTRf(clockid, p_signed); | 1122 | PRINT_ATTRf(clockid, p_signed); |
1123 | PRINT_ATTRf(sample_regs_intr, p_hex); | 1123 | PRINT_ATTRf(sample_regs_intr, p_hex); |
1124 | PRINT_ATTRf(aux_watermark, p_unsigned); | ||
1124 | 1125 | ||
1125 | return ret; | 1126 | return ret; |
1126 | } | 1127 | } |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 918fd8ae2d80..3f0d809d853a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -869,6 +869,20 @@ static int write_branch_stack(int fd __maybe_unused, | |||
869 | return 0; | 869 | return 0; |
870 | } | 870 | } |
871 | 871 | ||
872 | static int write_auxtrace(int fd, struct perf_header *h, | ||
873 | struct perf_evlist *evlist __maybe_unused) | ||
874 | { | ||
875 | struct perf_session *session; | ||
876 | int err; | ||
877 | |||
878 | session = container_of(h, struct perf_session, header); | ||
879 | |||
880 | err = auxtrace_index__write(fd, &session->auxtrace_index); | ||
881 | if (err < 0) | ||
882 | pr_err("Failed to write auxtrace index\n"); | ||
883 | return err; | ||
884 | } | ||
885 | |||
872 | static void print_hostname(struct perf_header *ph, int fd __maybe_unused, | 886 | static void print_hostname(struct perf_header *ph, int fd __maybe_unused, |
873 | FILE *fp) | 887 | FILE *fp) |
874 | { | 888 | { |
@@ -1151,6 +1165,12 @@ static void print_branch_stack(struct perf_header *ph __maybe_unused, | |||
1151 | fprintf(fp, "# contains samples with branch stack\n"); | 1165 | fprintf(fp, "# contains samples with branch stack\n"); |
1152 | } | 1166 | } |
1153 | 1167 | ||
1168 | static void print_auxtrace(struct perf_header *ph __maybe_unused, | ||
1169 | int fd __maybe_unused, FILE *fp) | ||
1170 | { | ||
1171 | fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n"); | ||
1172 | } | ||
1173 | |||
1154 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, | 1174 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, |
1155 | FILE *fp) | 1175 | FILE *fp) |
1156 | { | 1176 | { |
@@ -1821,6 +1841,22 @@ out_free: | |||
1821 | return ret; | 1841 | return ret; |
1822 | } | 1842 | } |
1823 | 1843 | ||
1844 | static int process_auxtrace(struct perf_file_section *section, | ||
1845 | struct perf_header *ph, int fd, | ||
1846 | void *data __maybe_unused) | ||
1847 | { | ||
1848 | struct perf_session *session; | ||
1849 | int err; | ||
1850 | |||
1851 | session = container_of(ph, struct perf_session, header); | ||
1852 | |||
1853 | err = auxtrace_index__process(fd, section->size, session, | ||
1854 | ph->needs_swap); | ||
1855 | if (err < 0) | ||
1856 | pr_err("Failed to process auxtrace index\n"); | ||
1857 | return err; | ||
1858 | } | ||
1859 | |||
1824 | struct feature_ops { | 1860 | struct feature_ops { |
1825 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 1861 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1826 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 1862 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1861,6 +1897,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1861 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 1897 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
1862 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), | 1898 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), |
1863 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | 1899 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), |
1900 | FEAT_OPP(HEADER_AUXTRACE, auxtrace), | ||
1864 | }; | 1901 | }; |
1865 | 1902 | ||
1866 | struct header_print_data { | 1903 | struct header_print_data { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 3bb90ac172a1..d4d57962c591 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -30,6 +30,7 @@ enum { | |||
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | 31 | HEADER_PMU_MAPPINGS, |
32 | HEADER_GROUP_DESC, | 32 | HEADER_GROUP_DESC, |
33 | HEADER_AUXTRACE, | ||
33 | HEADER_LAST_FEATURE, | 34 | HEADER_LAST_FEATURE, |
34 | HEADER_FEAT_BITS = 256, | 35 | HEADER_FEAT_BITS = 256, |
35 | }; | 36 | }; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index cc22b9158b93..338770679863 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1163,7 +1163,7 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h | |||
1163 | return; | 1163 | return; |
1164 | 1164 | ||
1165 | /* force fold unfiltered entry for simplicity */ | 1165 | /* force fold unfiltered entry for simplicity */ |
1166 | h->ms.unfolded = false; | 1166 | h->unfolded = false; |
1167 | h->row_offset = 0; | 1167 | h->row_offset = 0; |
1168 | h->nr_rows = 0; | 1168 | h->nr_rows = 0; |
1169 | 1169 | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 527e032e24f6..2f471105efb1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -486,6 +486,22 @@ machine__module_dso(struct machine *machine, struct kmod_path *m, | |||
486 | return dso; | 486 | return dso; |
487 | } | 487 | } |
488 | 488 | ||
489 | int machine__process_aux_event(struct machine *machine __maybe_unused, | ||
490 | union perf_event *event) | ||
491 | { | ||
492 | if (dump_trace) | ||
493 | perf_event__fprintf_aux(event, stdout); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | int machine__process_itrace_start_event(struct machine *machine __maybe_unused, | ||
498 | union perf_event *event) | ||
499 | { | ||
500 | if (dump_trace) | ||
501 | perf_event__fprintf_itrace_start(event, stdout); | ||
502 | return 0; | ||
503 | } | ||
504 | |||
489 | struct map *machine__new_module(struct machine *machine, u64 start, | 505 | struct map *machine__new_module(struct machine *machine, u64 start, |
490 | const char *filename) | 506 | const char *filename) |
491 | { | 507 | { |
@@ -1331,6 +1347,11 @@ int machine__process_event(struct machine *machine, union perf_event *event, | |||
1331 | ret = machine__process_exit_event(machine, event, sample); break; | 1347 | ret = machine__process_exit_event(machine, event, sample); break; |
1332 | case PERF_RECORD_LOST: | 1348 | case PERF_RECORD_LOST: |
1333 | ret = machine__process_lost_event(machine, event, sample); break; | 1349 | ret = machine__process_lost_event(machine, event, sample); break; |
1350 | case PERF_RECORD_AUX: | ||
1351 | ret = machine__process_aux_event(machine, event); break; | ||
1352 | case PERF_RECORD_ITRACE_START: | ||
1353 | ret = machine__process_itrace_start_event(machine, event); | ||
1354 | break; | ||
1334 | default: | 1355 | default: |
1335 | ret = -1; | 1356 | ret = -1; |
1336 | break; | 1357 | break; |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 6d64cedb9d1e..1d992961d5d1 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -81,6 +81,10 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
81 | struct perf_sample *sample); | 81 | struct perf_sample *sample); |
82 | int machine__process_lost_event(struct machine *machine, union perf_event *event, | 82 | int machine__process_lost_event(struct machine *machine, union perf_event *event, |
83 | struct perf_sample *sample); | 83 | struct perf_sample *sample); |
84 | int machine__process_aux_event(struct machine *machine, | ||
85 | union perf_event *event); | ||
86 | int machine__process_itrace_start_event(struct machine *machine, | ||
87 | union perf_event *event); | ||
84 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, | 88 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, |
85 | struct perf_sample *sample); | 89 | struct perf_sample *sample); |
86 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event, | 90 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event, |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a14f08f41686..cd0e335008b4 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -292,6 +292,11 @@ int map__load(struct map *map, symbol_filter_t filter) | |||
292 | return 0; | 292 | return 0; |
293 | } | 293 | } |
294 | 294 | ||
295 | int __weak arch__compare_symbol_names(const char *namea, const char *nameb) | ||
296 | { | ||
297 | return strcmp(namea, nameb); | ||
298 | } | ||
299 | |||
295 | struct symbol *map__find_symbol(struct map *map, u64 addr, | 300 | struct symbol *map__find_symbol(struct map *map, u64 addr, |
296 | symbol_filter_t filter) | 301 | symbol_filter_t filter) |
297 | { | 302 | { |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index ec19c59ca38e..4e0c729841ab 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -124,7 +124,7 @@ struct thread; | |||
124 | */ | 124 | */ |
125 | #define __map__for_each_symbol_by_name(map, sym_name, pos, filter) \ | 125 | #define __map__for_each_symbol_by_name(map, sym_name, pos, filter) \ |
126 | for (pos = map__find_symbol_by_name(map, sym_name, filter); \ | 126 | for (pos = map__find_symbol_by_name(map, sym_name, filter); \ |
127 | pos && strcmp(pos->name, sym_name) == 0; \ | 127 | pos && arch__compare_symbol_names(pos->name, sym_name) == 0; \ |
128 | pos = symbol__next_by_name(pos)) | 128 | pos = symbol__next_by_name(pos)) |
129 | 129 | ||
130 | #define map__for_each_symbol_by_name(map, sym_name, pos) \ | 130 | #define map__for_each_symbol_by_name(map, sym_name, pos) \ |
@@ -132,6 +132,7 @@ struct thread; | |||
132 | 132 | ||
133 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 133 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
134 | 134 | ||
135 | int arch__compare_symbol_names(const char *namea, const char *nameb); | ||
135 | void map__init(struct map *map, enum map_type type, | 136 | void map__init(struct map *map, enum map_type type, |
136 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 137 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
137 | struct map *map__new(struct machine *machine, u64 start, u64 len, | 138 | struct map *map__new(struct machine *machine, u64 start, u64 len, |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index be0655388b38..80a50fdb6d8a 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "parse-events-flex.h" | 17 | #include "parse-events-flex.h" |
18 | #include "pmu.h" | 18 | #include "pmu.h" |
19 | #include "thread_map.h" | 19 | #include "thread_map.h" |
20 | #include "asm/bug.h" | ||
20 | 21 | ||
21 | #define MAX_NAME_LEN 100 | 22 | #define MAX_NAME_LEN 100 |
22 | 23 | ||
@@ -24,6 +25,12 @@ | |||
24 | extern int parse_events_debug; | 25 | extern int parse_events_debug; |
25 | #endif | 26 | #endif |
26 | int parse_events_parse(void *data, void *scanner); | 27 | int parse_events_parse(void *data, void *scanner); |
28 | int parse_events_term__num(struct parse_events_term **term, | ||
29 | int type_term, char *config, u64 num, | ||
30 | YYLTYPE *loc_term, YYLTYPE *loc_val); | ||
31 | int parse_events_term__str(struct parse_events_term **term, | ||
32 | int type_term, char *config, char *str, | ||
33 | YYLTYPE *loc_term, YYLTYPE *loc_val); | ||
27 | 34 | ||
28 | static struct perf_pmu_event_symbol *perf_pmu_events_list; | 35 | static struct perf_pmu_event_symbol *perf_pmu_events_list; |
29 | /* | 36 | /* |
@@ -538,16 +545,40 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, | |||
538 | return add_event(list, idx, &attr, NULL); | 545 | return add_event(list, idx, &attr, NULL); |
539 | } | 546 | } |
540 | 547 | ||
548 | static int check_type_val(struct parse_events_term *term, | ||
549 | struct parse_events_error *err, | ||
550 | int type) | ||
551 | { | ||
552 | if (type == term->type_val) | ||
553 | return 0; | ||
554 | |||
555 | if (err) { | ||
556 | err->idx = term->err_val; | ||
557 | if (type == PARSE_EVENTS__TERM_TYPE_NUM) | ||
558 | err->str = strdup("expected numeric value"); | ||
559 | else | ||
560 | err->str = strdup("expected string value"); | ||
561 | } | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
541 | static int config_term(struct perf_event_attr *attr, | 565 | static int config_term(struct perf_event_attr *attr, |
542 | struct parse_events_term *term) | 566 | struct parse_events_term *term, |
567 | struct parse_events_error *err) | ||
543 | { | 568 | { |
544 | #define CHECK_TYPE_VAL(type) \ | 569 | #define CHECK_TYPE_VAL(type) \ |
545 | do { \ | 570 | do { \ |
546 | if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \ | 571 | if (check_type_val(term, err, PARSE_EVENTS__TERM_TYPE_ ## type)) \ |
547 | return -EINVAL; \ | 572 | return -EINVAL; \ |
548 | } while (0) | 573 | } while (0) |
549 | 574 | ||
550 | switch (term->type_term) { | 575 | switch (term->type_term) { |
576 | case PARSE_EVENTS__TERM_TYPE_USER: | ||
577 | /* | ||
578 | * Always succeed for sysfs terms, as we dont know | ||
579 | * at this point what type they need to have. | ||
580 | */ | ||
581 | return 0; | ||
551 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | 582 | case PARSE_EVENTS__TERM_TYPE_CONFIG: |
552 | CHECK_TYPE_VAL(NUM); | 583 | CHECK_TYPE_VAL(NUM); |
553 | attr->config = term->val.num; | 584 | attr->config = term->val.num; |
@@ -582,18 +613,20 @@ do { \ | |||
582 | } | 613 | } |
583 | 614 | ||
584 | static int config_attr(struct perf_event_attr *attr, | 615 | static int config_attr(struct perf_event_attr *attr, |
585 | struct list_head *head, int fail) | 616 | struct list_head *head, |
617 | struct parse_events_error *err) | ||
586 | { | 618 | { |
587 | struct parse_events_term *term; | 619 | struct parse_events_term *term; |
588 | 620 | ||
589 | list_for_each_entry(term, head, list) | 621 | list_for_each_entry(term, head, list) |
590 | if (config_term(attr, term) && fail) | 622 | if (config_term(attr, term, err)) |
591 | return -EINVAL; | 623 | return -EINVAL; |
592 | 624 | ||
593 | return 0; | 625 | return 0; |
594 | } | 626 | } |
595 | 627 | ||
596 | int parse_events_add_numeric(struct list_head *list, int *idx, | 628 | int parse_events_add_numeric(struct parse_events_evlist *data, |
629 | struct list_head *list, | ||
597 | u32 type, u64 config, | 630 | u32 type, u64 config, |
598 | struct list_head *head_config) | 631 | struct list_head *head_config) |
599 | { | 632 | { |
@@ -604,10 +637,10 @@ int parse_events_add_numeric(struct list_head *list, int *idx, | |||
604 | attr.config = config; | 637 | attr.config = config; |
605 | 638 | ||
606 | if (head_config && | 639 | if (head_config && |
607 | config_attr(&attr, head_config, 1)) | 640 | config_attr(&attr, head_config, data->error)) |
608 | return -EINVAL; | 641 | return -EINVAL; |
609 | 642 | ||
610 | return add_event(list, idx, &attr, NULL); | 643 | return add_event(list, &data->idx, &attr, NULL); |
611 | } | 644 | } |
612 | 645 | ||
613 | static int parse_events__is_name_term(struct parse_events_term *term) | 646 | static int parse_events__is_name_term(struct parse_events_term *term) |
@@ -626,8 +659,9 @@ static char *pmu_event_name(struct list_head *head_terms) | |||
626 | return NULL; | 659 | return NULL; |
627 | } | 660 | } |
628 | 661 | ||
629 | int parse_events_add_pmu(struct list_head *list, int *idx, | 662 | int parse_events_add_pmu(struct parse_events_evlist *data, |
630 | char *name, struct list_head *head_config) | 663 | struct list_head *list, char *name, |
664 | struct list_head *head_config) | ||
631 | { | 665 | { |
632 | struct perf_event_attr attr; | 666 | struct perf_event_attr attr; |
633 | struct perf_pmu_info info; | 667 | struct perf_pmu_info info; |
@@ -647,7 +681,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
647 | 681 | ||
648 | if (!head_config) { | 682 | if (!head_config) { |
649 | attr.type = pmu->type; | 683 | attr.type = pmu->type; |
650 | evsel = __add_event(list, idx, &attr, NULL, pmu->cpus); | 684 | evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus); |
651 | return evsel ? 0 : -ENOMEM; | 685 | return evsel ? 0 : -ENOMEM; |
652 | } | 686 | } |
653 | 687 | ||
@@ -658,13 +692,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
658 | * Configure hardcoded terms first, no need to check | 692 | * Configure hardcoded terms first, no need to check |
659 | * return value when called with fail == 0 ;) | 693 | * return value when called with fail == 0 ;) |
660 | */ | 694 | */ |
661 | config_attr(&attr, head_config, 0); | 695 | if (config_attr(&attr, head_config, data->error)) |
696 | return -EINVAL; | ||
662 | 697 | ||
663 | if (perf_pmu__config(pmu, &attr, head_config)) | 698 | if (perf_pmu__config(pmu, &attr, head_config, data->error)) |
664 | return -EINVAL; | 699 | return -EINVAL; |
665 | 700 | ||
666 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), | 701 | evsel = __add_event(list, &data->idx, &attr, |
667 | pmu->cpus); | 702 | pmu_event_name(head_config), pmu->cpus); |
668 | if (evsel) { | 703 | if (evsel) { |
669 | evsel->unit = info.unit; | 704 | evsel->unit = info.unit; |
670 | evsel->scale = info.scale; | 705 | evsel->scale = info.scale; |
@@ -1019,11 +1054,13 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
1019 | return ret; | 1054 | return ret; |
1020 | } | 1055 | } |
1021 | 1056 | ||
1022 | int parse_events(struct perf_evlist *evlist, const char *str) | 1057 | int parse_events(struct perf_evlist *evlist, const char *str, |
1058 | struct parse_events_error *err) | ||
1023 | { | 1059 | { |
1024 | struct parse_events_evlist data = { | 1060 | struct parse_events_evlist data = { |
1025 | .list = LIST_HEAD_INIT(data.list), | 1061 | .list = LIST_HEAD_INIT(data.list), |
1026 | .idx = evlist->nr_entries, | 1062 | .idx = evlist->nr_entries, |
1063 | .error = err, | ||
1027 | }; | 1064 | }; |
1028 | int ret; | 1065 | int ret; |
1029 | 1066 | ||
@@ -1044,16 +1081,87 @@ int parse_events(struct perf_evlist *evlist, const char *str) | |||
1044 | return ret; | 1081 | return ret; |
1045 | } | 1082 | } |
1046 | 1083 | ||
1084 | #define MAX_WIDTH 1000 | ||
1085 | static int get_term_width(void) | ||
1086 | { | ||
1087 | struct winsize ws; | ||
1088 | |||
1089 | get_term_dimensions(&ws); | ||
1090 | return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col; | ||
1091 | } | ||
1092 | |||
1093 | static void parse_events_print_error(struct parse_events_error *err, | ||
1094 | const char *event) | ||
1095 | { | ||
1096 | const char *str = "invalid or unsupported event: "; | ||
1097 | char _buf[MAX_WIDTH]; | ||
1098 | char *buf = (char *) event; | ||
1099 | int idx = 0; | ||
1100 | |||
1101 | if (err->str) { | ||
1102 | /* -2 for extra '' in the final fprintf */ | ||
1103 | int width = get_term_width() - 2; | ||
1104 | int len_event = strlen(event); | ||
1105 | int len_str, max_len, cut = 0; | ||
1106 | |||
1107 | /* | ||
1108 | * Maximum error index indent, we will cut | ||
1109 | * the event string if it's bigger. | ||
1110 | */ | ||
1111 | int max_err_idx = 10; | ||
1112 | |||
1113 | /* | ||
1114 | * Let's be specific with the message when | ||
1115 | * we have the precise error. | ||
1116 | */ | ||
1117 | str = "event syntax error: "; | ||
1118 | len_str = strlen(str); | ||
1119 | max_len = width - len_str; | ||
1120 | |||
1121 | buf = _buf; | ||
1122 | |||
1123 | /* We're cutting from the beggining. */ | ||
1124 | if (err->idx > max_err_idx) | ||
1125 | cut = err->idx - max_err_idx; | ||
1126 | |||
1127 | strncpy(buf, event + cut, max_len); | ||
1128 | |||
1129 | /* Mark cut parts with '..' on both sides. */ | ||
1130 | if (cut) | ||
1131 | buf[0] = buf[1] = '.'; | ||
1132 | |||
1133 | if ((len_event - cut) > max_len) { | ||
1134 | buf[max_len - 1] = buf[max_len - 2] = '.'; | ||
1135 | buf[max_len] = 0; | ||
1136 | } | ||
1137 | |||
1138 | idx = len_str + err->idx - cut; | ||
1139 | } | ||
1140 | |||
1141 | fprintf(stderr, "%s'%s'\n", str, buf); | ||
1142 | if (idx) { | ||
1143 | fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str); | ||
1144 | if (err->help) | ||
1145 | fprintf(stderr, "\n%s\n", err->help); | ||
1146 | free(err->str); | ||
1147 | free(err->help); | ||
1148 | } | ||
1149 | |||
1150 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
1151 | } | ||
1152 | |||
1153 | #undef MAX_WIDTH | ||
1154 | |||
1047 | int parse_events_option(const struct option *opt, const char *str, | 1155 | int parse_events_option(const struct option *opt, const char *str, |
1048 | int unset __maybe_unused) | 1156 | int unset __maybe_unused) |
1049 | { | 1157 | { |
1050 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 1158 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
1051 | int ret = parse_events(evlist, str); | 1159 | struct parse_events_error err = { .idx = 0, }; |
1160 | int ret = parse_events(evlist, str, &err); | ||
1161 | |||
1162 | if (ret) | ||
1163 | parse_events_print_error(&err, str); | ||
1052 | 1164 | ||
1053 | if (ret) { | ||
1054 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
1055 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
1056 | } | ||
1057 | return ret; | 1165 | return ret; |
1058 | } | 1166 | } |
1059 | 1167 | ||
@@ -1460,7 +1568,7 @@ int parse_events__is_hardcoded_term(struct parse_events_term *term) | |||
1460 | 1568 | ||
1461 | static int new_term(struct parse_events_term **_term, int type_val, | 1569 | static int new_term(struct parse_events_term **_term, int type_val, |
1462 | int type_term, char *config, | 1570 | int type_term, char *config, |
1463 | char *str, u64 num) | 1571 | char *str, u64 num, int err_term, int err_val) |
1464 | { | 1572 | { |
1465 | struct parse_events_term *term; | 1573 | struct parse_events_term *term; |
1466 | 1574 | ||
@@ -1472,6 +1580,8 @@ static int new_term(struct parse_events_term **_term, int type_val, | |||
1472 | term->type_val = type_val; | 1580 | term->type_val = type_val; |
1473 | term->type_term = type_term; | 1581 | term->type_term = type_term; |
1474 | term->config = config; | 1582 | term->config = config; |
1583 | term->err_term = err_term; | ||
1584 | term->err_val = err_val; | ||
1475 | 1585 | ||
1476 | switch (type_val) { | 1586 | switch (type_val) { |
1477 | case PARSE_EVENTS__TERM_TYPE_NUM: | 1587 | case PARSE_EVENTS__TERM_TYPE_NUM: |
@@ -1490,17 +1600,23 @@ static int new_term(struct parse_events_term **_term, int type_val, | |||
1490 | } | 1600 | } |
1491 | 1601 | ||
1492 | int parse_events_term__num(struct parse_events_term **term, | 1602 | int parse_events_term__num(struct parse_events_term **term, |
1493 | int type_term, char *config, u64 num) | 1603 | int type_term, char *config, u64 num, |
1604 | YYLTYPE *loc_term, YYLTYPE *loc_val) | ||
1494 | { | 1605 | { |
1495 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, | 1606 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, |
1496 | config, NULL, num); | 1607 | config, NULL, num, |
1608 | loc_term ? loc_term->first_column : 0, | ||
1609 | loc_val ? loc_val->first_column : 0); | ||
1497 | } | 1610 | } |
1498 | 1611 | ||
1499 | int parse_events_term__str(struct parse_events_term **term, | 1612 | int parse_events_term__str(struct parse_events_term **term, |
1500 | int type_term, char *config, char *str) | 1613 | int type_term, char *config, char *str, |
1614 | YYLTYPE *loc_term, YYLTYPE *loc_val) | ||
1501 | { | 1615 | { |
1502 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, | 1616 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, |
1503 | config, str, 0); | 1617 | config, str, 0, |
1618 | loc_term ? loc_term->first_column : 0, | ||
1619 | loc_val ? loc_val->first_column : 0); | ||
1504 | } | 1620 | } |
1505 | 1621 | ||
1506 | int parse_events_term__sym_hw(struct parse_events_term **term, | 1622 | int parse_events_term__sym_hw(struct parse_events_term **term, |
@@ -1514,18 +1630,20 @@ int parse_events_term__sym_hw(struct parse_events_term **term, | |||
1514 | if (config) | 1630 | if (config) |
1515 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | 1631 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, |
1516 | PARSE_EVENTS__TERM_TYPE_USER, config, | 1632 | PARSE_EVENTS__TERM_TYPE_USER, config, |
1517 | (char *) sym->symbol, 0); | 1633 | (char *) sym->symbol, 0, 0, 0); |
1518 | else | 1634 | else |
1519 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | 1635 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, |
1520 | PARSE_EVENTS__TERM_TYPE_USER, | 1636 | PARSE_EVENTS__TERM_TYPE_USER, |
1521 | (char *) "event", (char *) sym->symbol, 0); | 1637 | (char *) "event", (char *) sym->symbol, |
1638 | 0, 0, 0); | ||
1522 | } | 1639 | } |
1523 | 1640 | ||
1524 | int parse_events_term__clone(struct parse_events_term **new, | 1641 | int parse_events_term__clone(struct parse_events_term **new, |
1525 | struct parse_events_term *term) | 1642 | struct parse_events_term *term) |
1526 | { | 1643 | { |
1527 | return new_term(new, term->type_val, term->type_term, term->config, | 1644 | return new_term(new, term->type_val, term->type_term, term->config, |
1528 | term->val.str, term->val.num); | 1645 | term->val.str, term->val.num, |
1646 | term->err_term, term->err_val); | ||
1529 | } | 1647 | } |
1530 | 1648 | ||
1531 | void parse_events__free_terms(struct list_head *terms) | 1649 | void parse_events__free_terms(struct list_head *terms) |
@@ -1535,3 +1653,13 @@ void parse_events__free_terms(struct list_head *terms) | |||
1535 | list_for_each_entry_safe(term, h, terms, list) | 1653 | list_for_each_entry_safe(term, h, terms, list) |
1536 | free(term); | 1654 | free(term); |
1537 | } | 1655 | } |
1656 | |||
1657 | void parse_events_evlist_error(struct parse_events_evlist *data, | ||
1658 | int idx, const char *str) | ||
1659 | { | ||
1660 | struct parse_events_error *err = data->error; | ||
1661 | |||
1662 | err->idx = idx; | ||
1663 | err->str = strdup(str); | ||
1664 | WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); | ||
1665 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 52a2dda4f954..e236f1b6ac6f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -12,6 +12,7 @@ | |||
12 | struct list_head; | 12 | struct list_head; |
13 | struct perf_evsel; | 13 | struct perf_evsel; |
14 | struct perf_evlist; | 14 | struct perf_evlist; |
15 | struct parse_events_error; | ||
15 | 16 | ||
16 | struct option; | 17 | struct option; |
17 | 18 | ||
@@ -29,7 +30,8 @@ const char *event_type(int type); | |||
29 | 30 | ||
30 | extern int parse_events_option(const struct option *opt, const char *str, | 31 | extern int parse_events_option(const struct option *opt, const char *str, |
31 | int unset); | 32 | int unset); |
32 | extern int parse_events(struct perf_evlist *evlist, const char *str); | 33 | extern int parse_events(struct perf_evlist *evlist, const char *str, |
34 | struct parse_events_error *error); | ||
33 | extern int parse_events_terms(struct list_head *terms, const char *str); | 35 | extern int parse_events_terms(struct list_head *terms, const char *str); |
34 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 36 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
35 | 37 | ||
@@ -72,12 +74,23 @@ struct parse_events_term { | |||
72 | int type_term; | 74 | int type_term; |
73 | struct list_head list; | 75 | struct list_head list; |
74 | bool used; | 76 | bool used; |
77 | |||
78 | /* error string indexes for within parsed string */ | ||
79 | int err_term; | ||
80 | int err_val; | ||
81 | }; | ||
82 | |||
83 | struct parse_events_error { | ||
84 | int idx; /* index in the parsed string */ | ||
85 | char *str; /* string to display at the index */ | ||
86 | char *help; /* optional help string */ | ||
75 | }; | 87 | }; |
76 | 88 | ||
77 | struct parse_events_evlist { | 89 | struct parse_events_evlist { |
78 | struct list_head list; | 90 | struct list_head list; |
79 | int idx; | 91 | int idx; |
80 | int nr_groups; | 92 | int nr_groups; |
93 | struct parse_events_error *error; | ||
81 | }; | 94 | }; |
82 | 95 | ||
83 | struct parse_events_terms { | 96 | struct parse_events_terms { |
@@ -85,10 +98,6 @@ struct parse_events_terms { | |||
85 | }; | 98 | }; |
86 | 99 | ||
87 | int parse_events__is_hardcoded_term(struct parse_events_term *term); | 100 | int parse_events__is_hardcoded_term(struct parse_events_term *term); |
88 | int parse_events_term__num(struct parse_events_term **_term, | ||
89 | int type_term, char *config, u64 num); | ||
90 | int parse_events_term__str(struct parse_events_term **_term, | ||
91 | int type_term, char *config, char *str); | ||
92 | int parse_events_term__sym_hw(struct parse_events_term **term, | 101 | int parse_events_term__sym_hw(struct parse_events_term **term, |
93 | char *config, unsigned idx); | 102 | char *config, unsigned idx); |
94 | int parse_events_term__clone(struct parse_events_term **new, | 103 | int parse_events_term__clone(struct parse_events_term **new, |
@@ -99,21 +108,24 @@ int parse_events__modifier_group(struct list_head *list, char *event_mod); | |||
99 | int parse_events_name(struct list_head *list, char *name); | 108 | int parse_events_name(struct list_head *list, char *name); |
100 | int parse_events_add_tracepoint(struct list_head *list, int *idx, | 109 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
101 | char *sys, char *event); | 110 | char *sys, char *event); |
102 | int parse_events_add_numeric(struct list_head *list, int *idx, | 111 | int parse_events_add_numeric(struct parse_events_evlist *data, |
112 | struct list_head *list, | ||
103 | u32 type, u64 config, | 113 | u32 type, u64 config, |
104 | struct list_head *head_config); | 114 | struct list_head *head_config); |
105 | int parse_events_add_cache(struct list_head *list, int *idx, | 115 | int parse_events_add_cache(struct list_head *list, int *idx, |
106 | char *type, char *op_result1, char *op_result2); | 116 | char *type, char *op_result1, char *op_result2); |
107 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 117 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
108 | void *ptr, char *type, u64 len); | 118 | void *ptr, char *type, u64 len); |
109 | int parse_events_add_pmu(struct list_head *list, int *idx, | 119 | int parse_events_add_pmu(struct parse_events_evlist *data, |
110 | char *pmu , struct list_head *head_config); | 120 | struct list_head *list, char *name, |
121 | struct list_head *head_config); | ||
111 | enum perf_pmu_event_symbol_type | 122 | enum perf_pmu_event_symbol_type |
112 | perf_pmu__parse_check(const char *name); | 123 | perf_pmu__parse_check(const char *name); |
113 | void parse_events__set_leader(char *name, struct list_head *list); | 124 | void parse_events__set_leader(char *name, struct list_head *list); |
114 | void parse_events_update_lists(struct list_head *list_event, | 125 | void parse_events_update_lists(struct list_head *list_event, |
115 | struct list_head *list_all); | 126 | struct list_head *list_all); |
116 | void parse_events_error(void *data, void *scanner, char const *msg); | 127 | void parse_events_evlist_error(struct parse_events_evlist *data, |
128 | int idx, const char *str); | ||
117 | 129 | ||
118 | void print_events(const char *event_glob, bool name_only); | 130 | void print_events(const char *event_glob, bool name_only); |
119 | 131 | ||
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 8895cf3132ab..09e738fe9ea2 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -3,6 +3,8 @@ | |||
3 | %option bison-bridge | 3 | %option bison-bridge |
4 | %option prefix="parse_events_" | 4 | %option prefix="parse_events_" |
5 | %option stack | 5 | %option stack |
6 | %option bison-locations | ||
7 | %option yylineno | ||
6 | 8 | ||
7 | %{ | 9 | %{ |
8 | #include <errno.h> | 10 | #include <errno.h> |
@@ -51,6 +53,18 @@ static int str(yyscan_t scanner, int token) | |||
51 | return token; | 53 | return token; |
52 | } | 54 | } |
53 | 55 | ||
56 | #define REWIND(__alloc) \ | ||
57 | do { \ | ||
58 | YYSTYPE *__yylval = parse_events_get_lval(yyscanner); \ | ||
59 | char *text = parse_events_get_text(yyscanner); \ | ||
60 | \ | ||
61 | if (__alloc) \ | ||
62 | __yylval->str = strdup(text); \ | ||
63 | \ | ||
64 | yycolumn -= strlen(text); \ | ||
65 | yyless(0); \ | ||
66 | } while (0) | ||
67 | |||
54 | static int pmu_str_check(yyscan_t scanner) | 68 | static int pmu_str_check(yyscan_t scanner) |
55 | { | 69 | { |
56 | YYSTYPE *yylval = parse_events_get_lval(scanner); | 70 | YYSTYPE *yylval = parse_events_get_lval(scanner); |
@@ -85,6 +99,13 @@ static int term(yyscan_t scanner, int type) | |||
85 | return PE_TERM; | 99 | return PE_TERM; |
86 | } | 100 | } |
87 | 101 | ||
102 | #define YY_USER_ACTION \ | ||
103 | do { \ | ||
104 | yylloc->last_column = yylloc->first_column; \ | ||
105 | yylloc->first_column = yycolumn; \ | ||
106 | yycolumn += yyleng; \ | ||
107 | } while (0); | ||
108 | |||
88 | %} | 109 | %} |
89 | 110 | ||
90 | %x mem | 111 | %x mem |
@@ -119,6 +140,12 @@ modifier_bp [rwx]{1,3} | |||
119 | 140 | ||
120 | if (start_token) { | 141 | if (start_token) { |
121 | parse_events_set_extra(NULL, yyscanner); | 142 | parse_events_set_extra(NULL, yyscanner); |
143 | /* | ||
144 | * The flex parser does not init locations variable | ||
145 | * via the scan_string interface, so we need do the | ||
146 | * init in here. | ||
147 | */ | ||
148 | yycolumn = 0; | ||
122 | return start_token; | 149 | return start_token; |
123 | } | 150 | } |
124 | } | 151 | } |
@@ -127,24 +154,30 @@ modifier_bp [rwx]{1,3} | |||
127 | <event>{ | 154 | <event>{ |
128 | 155 | ||
129 | {group} { | 156 | {group} { |
130 | BEGIN(INITIAL); yyless(0); | 157 | BEGIN(INITIAL); |
158 | REWIND(0); | ||
131 | } | 159 | } |
132 | 160 | ||
133 | {event_pmu} | | 161 | {event_pmu} | |
134 | {event} { | 162 | {event} { |
135 | str(yyscanner, PE_EVENT_NAME); | 163 | BEGIN(INITIAL); |
136 | BEGIN(INITIAL); yyless(0); | 164 | REWIND(1); |
137 | return PE_EVENT_NAME; | 165 | return PE_EVENT_NAME; |
138 | } | 166 | } |
139 | 167 | ||
140 | . | | 168 | . | |
141 | <<EOF>> { | 169 | <<EOF>> { |
142 | BEGIN(INITIAL); yyless(0); | 170 | BEGIN(INITIAL); |
171 | REWIND(0); | ||
143 | } | 172 | } |
144 | 173 | ||
145 | } | 174 | } |
146 | 175 | ||
147 | <config>{ | 176 | <config>{ |
177 | /* | ||
178 | * Please update formats_error_string any time | ||
179 | * new static term is added. | ||
180 | */ | ||
148 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | 181 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } |
149 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | 182 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } |
150 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } | 183 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 72def077dbbf..3d11e00243e3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -2,6 +2,7 @@ | |||
2 | %parse-param {void *_data} | 2 | %parse-param {void *_data} |
3 | %parse-param {void *scanner} | 3 | %parse-param {void *scanner} |
4 | %lex-param {void* scanner} | 4 | %lex-param {void* scanner} |
5 | %locations | ||
5 | 6 | ||
6 | %{ | 7 | %{ |
7 | 8 | ||
@@ -14,8 +15,6 @@ | |||
14 | #include "parse-events.h" | 15 | #include "parse-events.h" |
15 | #include "parse-events-bison.h" | 16 | #include "parse-events-bison.h" |
16 | 17 | ||
17 | extern int parse_events_lex (YYSTYPE* lvalp, void* scanner); | ||
18 | |||
19 | #define ABORT_ON(val) \ | 18 | #define ABORT_ON(val) \ |
20 | do { \ | 19 | do { \ |
21 | if (val) \ | 20 | if (val) \ |
@@ -208,7 +207,7 @@ PE_NAME '/' event_config '/' | |||
208 | struct list_head *list; | 207 | struct list_head *list; |
209 | 208 | ||
210 | ALLOC_LIST(list); | 209 | ALLOC_LIST(list); |
211 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, $3)); | 210 | ABORT_ON(parse_events_add_pmu(data, list, $1, $3)); |
212 | parse_events__free_terms($3); | 211 | parse_events__free_terms($3); |
213 | $$ = list; | 212 | $$ = list; |
214 | } | 213 | } |
@@ -219,7 +218,7 @@ PE_NAME '/' '/' | |||
219 | struct list_head *list; | 218 | struct list_head *list; |
220 | 219 | ||
221 | ALLOC_LIST(list); | 220 | ALLOC_LIST(list); |
222 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); | 221 | ABORT_ON(parse_events_add_pmu(data, list, $1, NULL)); |
223 | $$ = list; | 222 | $$ = list; |
224 | } | 223 | } |
225 | | | 224 | | |
@@ -232,11 +231,11 @@ PE_KERNEL_PMU_EVENT sep_dc | |||
232 | 231 | ||
233 | ALLOC_LIST(head); | 232 | ALLOC_LIST(head); |
234 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 233 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
235 | $1, 1)); | 234 | $1, 1, &@1, NULL)); |
236 | list_add_tail(&term->list, head); | 235 | list_add_tail(&term->list, head); |
237 | 236 | ||
238 | ALLOC_LIST(list); | 237 | ALLOC_LIST(list); |
239 | ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); | 238 | ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); |
240 | parse_events__free_terms(head); | 239 | parse_events__free_terms(head); |
241 | $$ = list; | 240 | $$ = list; |
242 | } | 241 | } |
@@ -252,7 +251,7 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc | |||
252 | 251 | ||
253 | ALLOC_LIST(head); | 252 | ALLOC_LIST(head); |
254 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 253 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
255 | &pmu_name, 1)); | 254 | &pmu_name, 1, &@1, NULL)); |
256 | list_add_tail(&term->list, head); | 255 | list_add_tail(&term->list, head); |
257 | 256 | ||
258 | ALLOC_LIST(list); | 257 | ALLOC_LIST(list); |
@@ -275,8 +274,7 @@ value_sym '/' event_config '/' | |||
275 | int config = $1 & 255; | 274 | int config = $1 & 255; |
276 | 275 | ||
277 | ALLOC_LIST(list); | 276 | ALLOC_LIST(list); |
278 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | 277 | ABORT_ON(parse_events_add_numeric(data, list, type, config, $3)); |
279 | type, config, $3)); | ||
280 | parse_events__free_terms($3); | 278 | parse_events__free_terms($3); |
281 | $$ = list; | 279 | $$ = list; |
282 | } | 280 | } |
@@ -289,8 +287,7 @@ value_sym sep_slash_dc | |||
289 | int config = $1 & 255; | 287 | int config = $1 & 255; |
290 | 288 | ||
291 | ALLOC_LIST(list); | 289 | ALLOC_LIST(list); |
292 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | 290 | ABORT_ON(parse_events_add_numeric(data, list, type, config, NULL)); |
293 | type, config, NULL)); | ||
294 | $$ = list; | 291 | $$ = list; |
295 | } | 292 | } |
296 | 293 | ||
@@ -389,7 +386,13 @@ PE_NAME ':' PE_NAME | |||
389 | struct list_head *list; | 386 | struct list_head *list; |
390 | 387 | ||
391 | ALLOC_LIST(list); | 388 | ALLOC_LIST(list); |
392 | ABORT_ON(parse_events_add_tracepoint(list, &data->idx, $1, $3)); | 389 | if (parse_events_add_tracepoint(list, &data->idx, $1, $3)) { |
390 | struct parse_events_error *error = data->error; | ||
391 | |||
392 | error->idx = @1.first_column; | ||
393 | error->str = strdup("unknown tracepoint"); | ||
394 | return -1; | ||
395 | } | ||
393 | $$ = list; | 396 | $$ = list; |
394 | } | 397 | } |
395 | 398 | ||
@@ -400,7 +403,7 @@ PE_VALUE ':' PE_VALUE | |||
400 | struct list_head *list; | 403 | struct list_head *list; |
401 | 404 | ||
402 | ALLOC_LIST(list); | 405 | ALLOC_LIST(list); |
403 | ABORT_ON(parse_events_add_numeric(list, &data->idx, (u32)$1, $3, NULL)); | 406 | ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, NULL)); |
404 | $$ = list; | 407 | $$ = list; |
405 | } | 408 | } |
406 | 409 | ||
@@ -411,8 +414,7 @@ PE_RAW | |||
411 | struct list_head *list; | 414 | struct list_head *list; |
412 | 415 | ||
413 | ALLOC_LIST(list); | 416 | ALLOC_LIST(list); |
414 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | 417 | ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, NULL)); |
415 | PERF_TYPE_RAW, $1, NULL)); | ||
416 | $$ = list; | 418 | $$ = list; |
417 | } | 419 | } |
418 | 420 | ||
@@ -450,7 +452,7 @@ PE_NAME '=' PE_NAME | |||
450 | struct parse_events_term *term; | 452 | struct parse_events_term *term; |
451 | 453 | ||
452 | ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, | 454 | ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, |
453 | $1, $3)); | 455 | $1, $3, &@1, &@3)); |
454 | $$ = term; | 456 | $$ = term; |
455 | } | 457 | } |
456 | | | 458 | | |
@@ -459,7 +461,7 @@ PE_NAME '=' PE_VALUE | |||
459 | struct parse_events_term *term; | 461 | struct parse_events_term *term; |
460 | 462 | ||
461 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 463 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
462 | $1, $3)); | 464 | $1, $3, &@1, &@3)); |
463 | $$ = term; | 465 | $$ = term; |
464 | } | 466 | } |
465 | | | 467 | | |
@@ -477,7 +479,7 @@ PE_NAME | |||
477 | struct parse_events_term *term; | 479 | struct parse_events_term *term; |
478 | 480 | ||
479 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 481 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
480 | $1, 1)); | 482 | $1, 1, &@1, NULL)); |
481 | $$ = term; | 483 | $$ = term; |
482 | } | 484 | } |
483 | | | 485 | | |
@@ -494,7 +496,7 @@ PE_TERM '=' PE_NAME | |||
494 | { | 496 | { |
495 | struct parse_events_term *term; | 497 | struct parse_events_term *term; |
496 | 498 | ||
497 | ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3)); | 499 | ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3)); |
498 | $$ = term; | 500 | $$ = term; |
499 | } | 501 | } |
500 | | | 502 | | |
@@ -502,7 +504,7 @@ PE_TERM '=' PE_VALUE | |||
502 | { | 504 | { |
503 | struct parse_events_term *term; | 505 | struct parse_events_term *term; |
504 | 506 | ||
505 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3)); | 507 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, &@1, &@3)); |
506 | $$ = term; | 508 | $$ = term; |
507 | } | 509 | } |
508 | | | 510 | | |
@@ -510,7 +512,7 @@ PE_TERM | |||
510 | { | 512 | { |
511 | struct parse_events_term *term; | 513 | struct parse_events_term *term; |
512 | 514 | ||
513 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1)); | 515 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL)); |
514 | $$ = term; | 516 | $$ = term; |
515 | } | 517 | } |
516 | 518 | ||
@@ -520,7 +522,9 @@ sep_slash_dc: '/' | ':' | | |||
520 | 522 | ||
521 | %% | 523 | %% |
522 | 524 | ||
523 | void parse_events_error(void *data __maybe_unused, void *scanner __maybe_unused, | 525 | void parse_events_error(YYLTYPE *loc, void *data, |
526 | void *scanner __maybe_unused, | ||
524 | char const *msg __maybe_unused) | 527 | char const *msg __maybe_unused) |
525 | { | 528 | { |
529 | parse_events_evlist_error(data, loc->last_column, "parser error"); | ||
526 | } | 530 | } |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 59561fd86278..367d8b816cc7 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -123,6 +123,10 @@ struct option { | |||
123 | #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } | 123 | #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } |
124 | #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } | 124 | #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } |
125 | #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } | 125 | #define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } |
126 | #define OPT_STRING_OPTARG(s, l, v, a, h, d) \ | ||
127 | { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ | ||
128 | .value = check_vtype(v, const char **), (a), .help = (h), \ | ||
129 | .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } | ||
126 | #define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} | 130 | #define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} |
127 | #define OPT_DATE(s, l, v, h) \ | 131 | #define OPT_DATE(s, l, v, h) \ |
128 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } | 132 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 48411674da0f..244c66f89891 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -579,6 +579,38 @@ static int pmu_resolve_param_term(struct parse_events_term *term, | |||
579 | return -1; | 579 | return -1; |
580 | } | 580 | } |
581 | 581 | ||
582 | static char *formats_error_string(struct list_head *formats) | ||
583 | { | ||
584 | struct perf_pmu_format *format; | ||
585 | char *err, *str; | ||
586 | static const char *static_terms = "config,config1,config2,name,period,branch_type\n"; | ||
587 | unsigned i = 0; | ||
588 | |||
589 | if (!asprintf(&str, "valid terms:")) | ||
590 | return NULL; | ||
591 | |||
592 | /* sysfs exported terms */ | ||
593 | list_for_each_entry(format, formats, list) { | ||
594 | char c = i++ ? ',' : ' '; | ||
595 | |||
596 | err = str; | ||
597 | if (!asprintf(&str, "%s%c%s", err, c, format->name)) | ||
598 | goto fail; | ||
599 | free(err); | ||
600 | } | ||
601 | |||
602 | /* static terms */ | ||
603 | err = str; | ||
604 | if (!asprintf(&str, "%s,%s", err, static_terms)) | ||
605 | goto fail; | ||
606 | |||
607 | free(err); | ||
608 | return str; | ||
609 | fail: | ||
610 | free(err); | ||
611 | return NULL; | ||
612 | } | ||
613 | |||
582 | /* | 614 | /* |
583 | * Setup one of config[12] attr members based on the | 615 | * Setup one of config[12] attr members based on the |
584 | * user input data - term parameter. | 616 | * user input data - term parameter. |
@@ -587,7 +619,7 @@ static int pmu_config_term(struct list_head *formats, | |||
587 | struct perf_event_attr *attr, | 619 | struct perf_event_attr *attr, |
588 | struct parse_events_term *term, | 620 | struct parse_events_term *term, |
589 | struct list_head *head_terms, | 621 | struct list_head *head_terms, |
590 | bool zero) | 622 | bool zero, struct parse_events_error *err) |
591 | { | 623 | { |
592 | struct perf_pmu_format *format; | 624 | struct perf_pmu_format *format; |
593 | __u64 *vp; | 625 | __u64 *vp; |
@@ -611,6 +643,11 @@ static int pmu_config_term(struct list_head *formats, | |||
611 | if (!format) { | 643 | if (!format) { |
612 | if (verbose) | 644 | if (verbose) |
613 | printf("Invalid event/parameter '%s'\n", term->config); | 645 | printf("Invalid event/parameter '%s'\n", term->config); |
646 | if (err) { | ||
647 | err->idx = term->err_term; | ||
648 | err->str = strdup("unknown term"); | ||
649 | err->help = formats_error_string(formats); | ||
650 | } | ||
614 | return -EINVAL; | 651 | return -EINVAL; |
615 | } | 652 | } |
616 | 653 | ||
@@ -636,9 +673,14 @@ static int pmu_config_term(struct list_head *formats, | |||
636 | val = term->val.num; | 673 | val = term->val.num; |
637 | else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { | 674 | else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { |
638 | if (strcmp(term->val.str, "?")) { | 675 | if (strcmp(term->val.str, "?")) { |
639 | if (verbose) | 676 | if (verbose) { |
640 | pr_info("Invalid sysfs entry %s=%s\n", | 677 | pr_info("Invalid sysfs entry %s=%s\n", |
641 | term->config, term->val.str); | 678 | term->config, term->val.str); |
679 | } | ||
680 | if (err) { | ||
681 | err->idx = term->err_val; | ||
682 | err->str = strdup("expected numeric value"); | ||
683 | } | ||
642 | return -EINVAL; | 684 | return -EINVAL; |
643 | } | 685 | } |
644 | 686 | ||
@@ -654,12 +696,13 @@ static int pmu_config_term(struct list_head *formats, | |||
654 | int perf_pmu__config_terms(struct list_head *formats, | 696 | int perf_pmu__config_terms(struct list_head *formats, |
655 | struct perf_event_attr *attr, | 697 | struct perf_event_attr *attr, |
656 | struct list_head *head_terms, | 698 | struct list_head *head_terms, |
657 | bool zero) | 699 | bool zero, struct parse_events_error *err) |
658 | { | 700 | { |
659 | struct parse_events_term *term; | 701 | struct parse_events_term *term; |
660 | 702 | ||
661 | list_for_each_entry(term, head_terms, list) { | 703 | list_for_each_entry(term, head_terms, list) { |
662 | if (pmu_config_term(formats, attr, term, head_terms, zero)) | 704 | if (pmu_config_term(formats, attr, term, head_terms, |
705 | zero, err)) | ||
663 | return -EINVAL; | 706 | return -EINVAL; |
664 | } | 707 | } |
665 | 708 | ||
@@ -672,12 +715,14 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
672 | * 2) pmu format definitions - specified by pmu parameter | 715 | * 2) pmu format definitions - specified by pmu parameter |
673 | */ | 716 | */ |
674 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 717 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
675 | struct list_head *head_terms) | 718 | struct list_head *head_terms, |
719 | struct parse_events_error *err) | ||
676 | { | 720 | { |
677 | bool zero = !!pmu->default_config; | 721 | bool zero = !!pmu->default_config; |
678 | 722 | ||
679 | attr->type = pmu->type; | 723 | attr->type = pmu->type; |
680 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero); | 724 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, |
725 | zero, err); | ||
681 | } | 726 | } |
682 | 727 | ||
683 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | 728 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 6b1249fbdb5f..7b9c8cf8ae3e 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/bitmap.h> | 4 | #include <linux/bitmap.h> |
5 | #include <linux/perf_event.h> | 5 | #include <linux/perf_event.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "parse-events.h" | ||
7 | 8 | ||
8 | enum { | 9 | enum { |
9 | PERF_PMU_FORMAT_VALUE_CONFIG, | 10 | PERF_PMU_FORMAT_VALUE_CONFIG, |
@@ -47,11 +48,12 @@ struct perf_pmu_alias { | |||
47 | 48 | ||
48 | struct perf_pmu *perf_pmu__find(const char *name); | 49 | struct perf_pmu *perf_pmu__find(const char *name); |
49 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 50 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
50 | struct list_head *head_terms); | 51 | struct list_head *head_terms, |
52 | struct parse_events_error *error); | ||
51 | int perf_pmu__config_terms(struct list_head *formats, | 53 | int perf_pmu__config_terms(struct list_head *formats, |
52 | struct perf_event_attr *attr, | 54 | struct perf_event_attr *attr, |
53 | struct list_head *head_terms, | 55 | struct list_head *head_terms, |
54 | bool zero); | 56 | bool zero, struct parse_events_error *error); |
55 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 57 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
56 | struct perf_pmu_info *info); | 58 | struct perf_pmu_info *info); |
57 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 59 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d8bb616ff57c..abf5845a2acc 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -1077,6 +1077,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
1077 | struct perf_probe_point *pp = &pev->point; | 1077 | struct perf_probe_point *pp = &pev->point; |
1078 | char *ptr, *tmp; | 1078 | char *ptr, *tmp; |
1079 | char c, nc = 0; | 1079 | char c, nc = 0; |
1080 | bool file_spec = false; | ||
1080 | /* | 1081 | /* |
1081 | * <Syntax> | 1082 | * <Syntax> |
1082 | * perf probe [EVENT=]SRC[:LN|;PTN] | 1083 | * perf probe [EVENT=]SRC[:LN|;PTN] |
@@ -1105,6 +1106,23 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
1105 | arg = tmp; | 1106 | arg = tmp; |
1106 | } | 1107 | } |
1107 | 1108 | ||
1109 | /* | ||
1110 | * Check arg is function or file name and copy it. | ||
1111 | * | ||
1112 | * We consider arg to be a file spec if and only if it satisfies | ||
1113 | * all of the below criteria:: | ||
1114 | * - it does not include any of "+@%", | ||
1115 | * - it includes one of ":;", and | ||
1116 | * - it has a period '.' in the name. | ||
1117 | * | ||
1118 | * Otherwise, we consider arg to be a function specification. | ||
1119 | */ | ||
1120 | if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) { | ||
1121 | /* This is a file spec if it includes a '.' before ; or : */ | ||
1122 | if (memchr(arg, '.', ptr - arg)) | ||
1123 | file_spec = true; | ||
1124 | } | ||
1125 | |||
1108 | ptr = strpbrk(arg, ";:+@%"); | 1126 | ptr = strpbrk(arg, ";:+@%"); |
1109 | if (ptr) { | 1127 | if (ptr) { |
1110 | nc = *ptr; | 1128 | nc = *ptr; |
@@ -1115,10 +1133,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
1115 | if (tmp == NULL) | 1133 | if (tmp == NULL) |
1116 | return -ENOMEM; | 1134 | return -ENOMEM; |
1117 | 1135 | ||
1118 | /* Check arg is function or file and copy it */ | 1136 | if (file_spec) |
1119 | if (strchr(tmp, '.')) /* File */ | ||
1120 | pp->file = tmp; | 1137 | pp->file = tmp; |
1121 | else /* Function */ | 1138 | else |
1122 | pp->function = tmp; | 1139 | pp->function = tmp; |
1123 | 1140 | ||
1124 | /* Parse other options */ | 1141 | /* Parse other options */ |
@@ -2129,7 +2146,23 @@ static int show_perf_probe_event(struct perf_probe_event *pev, | |||
2129 | return ret; | 2146 | return ret; |
2130 | } | 2147 | } |
2131 | 2148 | ||
2132 | static int __show_perf_probe_events(int fd, bool is_kprobe) | 2149 | static bool filter_probe_trace_event(struct probe_trace_event *tev, |
2150 | struct strfilter *filter) | ||
2151 | { | ||
2152 | char tmp[128]; | ||
2153 | |||
2154 | /* At first, check the event name itself */ | ||
2155 | if (strfilter__compare(filter, tev->event)) | ||
2156 | return true; | ||
2157 | |||
2158 | /* Next, check the combination of name and group */ | ||
2159 | if (e_snprintf(tmp, 128, "%s:%s", tev->group, tev->event) < 0) | ||
2160 | return false; | ||
2161 | return strfilter__compare(filter, tmp); | ||
2162 | } | ||
2163 | |||
2164 | static int __show_perf_probe_events(int fd, bool is_kprobe, | ||
2165 | struct strfilter *filter) | ||
2133 | { | 2166 | { |
2134 | int ret = 0; | 2167 | int ret = 0; |
2135 | struct probe_trace_event tev; | 2168 | struct probe_trace_event tev; |
@@ -2147,12 +2180,15 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
2147 | strlist__for_each(ent, rawlist) { | 2180 | strlist__for_each(ent, rawlist) { |
2148 | ret = parse_probe_trace_command(ent->s, &tev); | 2181 | ret = parse_probe_trace_command(ent->s, &tev); |
2149 | if (ret >= 0) { | 2182 | if (ret >= 0) { |
2183 | if (!filter_probe_trace_event(&tev, filter)) | ||
2184 | goto next; | ||
2150 | ret = convert_to_perf_probe_event(&tev, &pev, | 2185 | ret = convert_to_perf_probe_event(&tev, &pev, |
2151 | is_kprobe); | 2186 | is_kprobe); |
2152 | if (ret >= 0) | 2187 | if (ret >= 0) |
2153 | ret = show_perf_probe_event(&pev, | 2188 | ret = show_perf_probe_event(&pev, |
2154 | tev.point.module); | 2189 | tev.point.module); |
2155 | } | 2190 | } |
2191 | next: | ||
2156 | clear_perf_probe_event(&pev); | 2192 | clear_perf_probe_event(&pev); |
2157 | clear_probe_trace_event(&tev); | 2193 | clear_probe_trace_event(&tev); |
2158 | if (ret < 0) | 2194 | if (ret < 0) |
@@ -2164,7 +2200,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
2164 | } | 2200 | } |
2165 | 2201 | ||
2166 | /* List up current perf-probe events */ | 2202 | /* List up current perf-probe events */ |
2167 | int show_perf_probe_events(void) | 2203 | int show_perf_probe_events(struct strfilter *filter) |
2168 | { | 2204 | { |
2169 | int kp_fd, up_fd, ret; | 2205 | int kp_fd, up_fd, ret; |
2170 | 2206 | ||
@@ -2176,7 +2212,7 @@ int show_perf_probe_events(void) | |||
2176 | 2212 | ||
2177 | kp_fd = open_kprobe_events(false); | 2213 | kp_fd = open_kprobe_events(false); |
2178 | if (kp_fd >= 0) { | 2214 | if (kp_fd >= 0) { |
2179 | ret = __show_perf_probe_events(kp_fd, true); | 2215 | ret = __show_perf_probe_events(kp_fd, true, filter); |
2180 | close(kp_fd); | 2216 | close(kp_fd); |
2181 | if (ret < 0) | 2217 | if (ret < 0) |
2182 | goto out; | 2218 | goto out; |
@@ -2190,7 +2226,7 @@ int show_perf_probe_events(void) | |||
2190 | } | 2226 | } |
2191 | 2227 | ||
2192 | if (up_fd >= 0) { | 2228 | if (up_fd >= 0) { |
2193 | ret = __show_perf_probe_events(up_fd, false); | 2229 | ret = __show_perf_probe_events(up_fd, false, filter); |
2194 | close(up_fd); | 2230 | close(up_fd); |
2195 | } | 2231 | } |
2196 | out: | 2232 | out: |
@@ -2265,6 +2301,9 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
2265 | { | 2301 | { |
2266 | int i, ret; | 2302 | int i, ret; |
2267 | 2303 | ||
2304 | if (*base == '.') | ||
2305 | base++; | ||
2306 | |||
2268 | /* Try no suffix */ | 2307 | /* Try no suffix */ |
2269 | ret = e_snprintf(buf, len, "%s", base); | 2308 | ret = e_snprintf(buf, len, "%s", base); |
2270 | if (ret < 0) { | 2309 | if (ret < 0) { |
@@ -2447,6 +2486,10 @@ static int find_probe_functions(struct map *map, char *name) | |||
2447 | #define strdup_or_goto(str, label) \ | 2486 | #define strdup_or_goto(str, label) \ |
2448 | ({ char *__p = strdup(str); if (!__p) goto label; __p; }) | 2487 | ({ char *__p = strdup(str); if (!__p) goto label; __p; }) |
2449 | 2488 | ||
2489 | void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, | ||
2490 | struct probe_trace_event *tev __maybe_unused, | ||
2491 | struct map *map __maybe_unused) { } | ||
2492 | |||
2450 | /* | 2493 | /* |
2451 | * Find probe function addresses from map. | 2494 | * Find probe function addresses from map. |
2452 | * Return an error or the number of found probe_trace_event | 2495 | * Return an error or the number of found probe_trace_event |
@@ -2553,6 +2596,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev, | |||
2553 | strdup_or_goto(pev->args[i].type, | 2596 | strdup_or_goto(pev->args[i].type, |
2554 | nomem_out); | 2597 | nomem_out); |
2555 | } | 2598 | } |
2599 | arch__fix_tev_from_maps(pev, tev, map); | ||
2556 | } | 2600 | } |
2557 | 2601 | ||
2558 | out: | 2602 | out: |
@@ -2567,6 +2611,8 @@ err_out: | |||
2567 | goto out; | 2611 | goto out; |
2568 | } | 2612 | } |
2569 | 2613 | ||
2614 | bool __weak arch__prefers_symtab(void) { return false; } | ||
2615 | |||
2570 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 2616 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
2571 | struct probe_trace_event **tevs, | 2617 | struct probe_trace_event **tevs, |
2572 | int max_tevs, const char *target) | 2618 | int max_tevs, const char *target) |
@@ -2582,6 +2628,12 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
2582 | } | 2628 | } |
2583 | } | 2629 | } |
2584 | 2630 | ||
2631 | if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { | ||
2632 | ret = find_probe_trace_events_from_map(pev, tevs, max_tevs, target); | ||
2633 | if (ret > 0) | ||
2634 | return ret; /* Found in symbol table */ | ||
2635 | } | ||
2636 | |||
2585 | /* Convert perf_probe_event with debuginfo */ | 2637 | /* Convert perf_probe_event with debuginfo */ |
2586 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); | 2638 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); |
2587 | if (ret != 0) | 2639 | if (ret != 0) |
@@ -2682,40 +2734,39 @@ error: | |||
2682 | return ret; | 2734 | return ret; |
2683 | } | 2735 | } |
2684 | 2736 | ||
2685 | static int del_trace_probe_event(int fd, const char *buf, | 2737 | static int del_trace_probe_events(int fd, struct strfilter *filter, |
2686 | struct strlist *namelist) | 2738 | struct strlist *namelist) |
2687 | { | 2739 | { |
2688 | struct str_node *ent, *n; | 2740 | struct str_node *ent; |
2689 | int ret = -1; | 2741 | const char *p; |
2742 | int ret = -ENOENT; | ||
2690 | 2743 | ||
2691 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | 2744 | if (!namelist) |
2692 | strlist__for_each_safe(ent, n, namelist) | 2745 | return -ENOENT; |
2693 | if (strglobmatch(ent->s, buf)) { | 2746 | |
2694 | ret = __del_trace_probe_event(fd, ent); | 2747 | strlist__for_each(ent, namelist) { |
2695 | if (ret < 0) | 2748 | p = strchr(ent->s, ':'); |
2696 | break; | 2749 | if ((p && strfilter__compare(filter, p + 1)) || |
2697 | strlist__remove(namelist, ent); | 2750 | strfilter__compare(filter, ent->s)) { |
2698 | } | ||
2699 | } else { | ||
2700 | ent = strlist__find(namelist, buf); | ||
2701 | if (ent) { | ||
2702 | ret = __del_trace_probe_event(fd, ent); | 2751 | ret = __del_trace_probe_event(fd, ent); |
2703 | if (ret >= 0) | 2752 | if (ret < 0) |
2704 | strlist__remove(namelist, ent); | 2753 | break; |
2705 | } | 2754 | } |
2706 | } | 2755 | } |
2707 | 2756 | ||
2708 | return ret; | 2757 | return ret; |
2709 | } | 2758 | } |
2710 | 2759 | ||
2711 | int del_perf_probe_events(struct strlist *dellist) | 2760 | int del_perf_probe_events(struct strfilter *filter) |
2712 | { | 2761 | { |
2713 | int ret = -1, ufd = -1, kfd = -1; | 2762 | int ret, ret2, ufd = -1, kfd = -1; |
2714 | char buf[128]; | ||
2715 | const char *group, *event; | ||
2716 | char *p, *str; | ||
2717 | struct str_node *ent; | ||
2718 | struct strlist *namelist = NULL, *unamelist = NULL; | 2763 | struct strlist *namelist = NULL, *unamelist = NULL; |
2764 | char *str = strfilter__string(filter); | ||
2765 | |||
2766 | if (!str) | ||
2767 | return -EINVAL; | ||
2768 | |||
2769 | pr_debug("Delete filter: \'%s\'\n", str); | ||
2719 | 2770 | ||
2720 | /* Get current event names */ | 2771 | /* Get current event names */ |
2721 | kfd = open_kprobe_events(true); | 2772 | kfd = open_kprobe_events(true); |
@@ -2728,48 +2779,21 @@ int del_perf_probe_events(struct strlist *dellist) | |||
2728 | 2779 | ||
2729 | if (kfd < 0 && ufd < 0) { | 2780 | if (kfd < 0 && ufd < 0) { |
2730 | print_both_open_warning(kfd, ufd); | 2781 | print_both_open_warning(kfd, ufd); |
2782 | ret = kfd; | ||
2731 | goto error; | 2783 | goto error; |
2732 | } | 2784 | } |
2733 | 2785 | ||
2734 | if (namelist == NULL && unamelist == NULL) | 2786 | ret = del_trace_probe_events(kfd, filter, namelist); |
2787 | if (ret < 0 && ret != -ENOENT) | ||
2735 | goto error; | 2788 | goto error; |
2736 | 2789 | ||
2737 | strlist__for_each(ent, dellist) { | 2790 | ret2 = del_trace_probe_events(ufd, filter, unamelist); |
2738 | str = strdup(ent->s); | 2791 | if (ret2 < 0 && ret2 != -ENOENT) |
2739 | if (str == NULL) { | 2792 | ret = ret2; |
2740 | ret = -ENOMEM; | 2793 | else if (ret == -ENOENT && ret2 == -ENOENT) { |
2741 | goto error; | 2794 | pr_debug("\"%s\" does not hit any event.\n", str); |
2742 | } | 2795 | /* Note that this is silently ignored */ |
2743 | pr_debug("Parsing: %s\n", str); | 2796 | ret = 0; |
2744 | p = strchr(str, ':'); | ||
2745 | if (p) { | ||
2746 | group = str; | ||
2747 | *p = '\0'; | ||
2748 | event = p + 1; | ||
2749 | } else { | ||
2750 | group = "*"; | ||
2751 | event = str; | ||
2752 | } | ||
2753 | |||
2754 | ret = e_snprintf(buf, 128, "%s:%s", group, event); | ||
2755 | if (ret < 0) { | ||
2756 | pr_err("Failed to copy event."); | ||
2757 | free(str); | ||
2758 | goto error; | ||
2759 | } | ||
2760 | |||
2761 | pr_debug("Group: %s, Event: %s\n", group, event); | ||
2762 | |||
2763 | if (namelist) | ||
2764 | ret = del_trace_probe_event(kfd, buf, namelist); | ||
2765 | |||
2766 | if (unamelist && ret != 0) | ||
2767 | ret = del_trace_probe_event(ufd, buf, unamelist); | ||
2768 | |||
2769 | if (ret != 0) | ||
2770 | pr_info("Info: Event \"%s\" does not exist.\n", buf); | ||
2771 | |||
2772 | free(str); | ||
2773 | } | 2797 | } |
2774 | 2798 | ||
2775 | error: | 2799 | error: |
@@ -2782,6 +2806,7 @@ error: | |||
2782 | strlist__delete(unamelist); | 2806 | strlist__delete(unamelist); |
2783 | close(ufd); | 2807 | close(ufd); |
2784 | } | 2808 | } |
2809 | free(str); | ||
2785 | 2810 | ||
2786 | return ret; | 2811 | return ret; |
2787 | } | 2812 | } |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index d6b783447be9..e10aedc34570 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -126,8 +126,8 @@ extern const char *kernel_get_module_path(const char *module); | |||
126 | 126 | ||
127 | extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 127 | extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
128 | int max_probe_points, bool force_add); | 128 | int max_probe_points, bool force_add); |
129 | extern int del_perf_probe_events(struct strlist *dellist); | 129 | extern int del_perf_probe_events(struct strfilter *filter); |
130 | extern int show_perf_probe_events(void); | 130 | extern int show_perf_probe_events(struct strfilter *filter); |
131 | extern int show_line_range(struct line_range *lr, const char *module, | 131 | extern int show_line_range(struct line_range *lr, const char *module, |
132 | bool user); | 132 | bool user); |
133 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 133 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
@@ -135,6 +135,9 @@ extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
135 | struct strfilter *filter, bool externs); | 135 | struct strfilter *filter, bool externs); |
136 | extern int show_available_funcs(const char *module, struct strfilter *filter, | 136 | extern int show_available_funcs(const char *module, struct strfilter *filter, |
137 | bool user); | 137 | bool user); |
138 | bool arch__prefers_symtab(void); | ||
139 | void arch__fix_tev_from_maps(struct perf_probe_event *pev, | ||
140 | struct probe_trace_event *tev, struct map *map); | ||
138 | 141 | ||
139 | /* Maximum index number of event-name postfix */ | 142 | /* Maximum index number of event-name postfix */ |
140 | #define MAX_EVENT_INDEX 1024 | 143 | #define MAX_EVENT_INDEX 1024 |
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c index a126e6cc6e73..b234a6e3d0d4 100644 --- a/tools/perf/util/pstack.c +++ b/tools/perf/util/pstack.c | |||
@@ -74,3 +74,10 @@ void *pstack__pop(struct pstack *pstack) | |||
74 | pstack->entries[pstack->top] = NULL; | 74 | pstack->entries[pstack->top] = NULL; |
75 | return ret; | 75 | return ret; |
76 | } | 76 | } |
77 | |||
78 | void *pstack__peek(struct pstack *pstack) | ||
79 | { | ||
80 | if (pstack->top == 0) | ||
81 | return NULL; | ||
82 | return pstack->entries[pstack->top - 1]; | ||
83 | } | ||
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h index c3cb6584d527..ded7f2e36624 100644 --- a/tools/perf/util/pstack.h +++ b/tools/perf/util/pstack.h | |||
@@ -10,5 +10,6 @@ bool pstack__empty(const struct pstack *pstack); | |||
10 | void pstack__remove(struct pstack *pstack, void *key); | 10 | void pstack__remove(struct pstack *pstack, void *key); |
11 | void pstack__push(struct pstack *pstack, void *key); | 11 | void pstack__push(struct pstack *pstack, void *key); |
12 | void *pstack__pop(struct pstack *pstack); | 12 | void *pstack__pop(struct pstack *pstack); |
13 | void *pstack__peek(struct pstack *pstack); | ||
13 | 14 | ||
14 | #endif /* _PERF_PSTACK_ */ | 15 | #endif /* _PERF_PSTACK_ */ |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 8acd0df88b5c..d457c523a33d 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c | |||
@@ -20,7 +20,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
20 | if (!evlist) | 20 | if (!evlist) |
21 | return -ENOMEM; | 21 | return -ENOMEM; |
22 | 22 | ||
23 | if (parse_events(evlist, str)) | 23 | if (parse_events(evlist, str, NULL)) |
24 | goto out_delete; | 24 | goto out_delete; |
25 | 25 | ||
26 | evsel = perf_evlist__first(evlist); | 26 | evsel = perf_evlist__first(evlist); |
@@ -119,7 +119,16 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) | |||
119 | evsel->attr.comm_exec = 1; | 119 | evsel->attr.comm_exec = 1; |
120 | } | 120 | } |
121 | 121 | ||
122 | if (evlist->nr_entries > 1) { | 122 | if (opts->full_auxtrace) { |
123 | /* | ||
124 | * Need to be able to synthesize and parse selected events with | ||
125 | * arbitrary sample types, which requires always being able to | ||
126 | * match the id. | ||
127 | */ | ||
128 | use_sample_identifier = perf_can_sample_identifier(); | ||
129 | evlist__for_each(evlist, evsel) | ||
130 | perf_evsel__set_sample_id(evsel, use_sample_identifier); | ||
131 | } else if (evlist->nr_entries > 1) { | ||
123 | struct perf_evsel *first = perf_evlist__first(evlist); | 132 | struct perf_evsel *first = perf_evlist__first(evlist); |
124 | 133 | ||
125 | evlist__for_each(evlist, evsel) { | 134 | evlist__for_each(evlist, evsel) { |
@@ -207,7 +216,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
207 | if (!temp_evlist) | 216 | if (!temp_evlist) |
208 | return false; | 217 | return false; |
209 | 218 | ||
210 | err = parse_events(temp_evlist, str); | 219 | err = parse_events(temp_evlist, str, NULL); |
211 | if (err) | 220 | if (err) |
212 | goto out_delete; | 221 | goto out_delete; |
213 | 222 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0c74012575ac..e722107f932a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -15,12 +15,13 @@ | |||
15 | #include "cpumap.h" | 15 | #include "cpumap.h" |
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "asm/bug.h" | 17 | #include "asm/bug.h" |
18 | #include "auxtrace.h" | ||
18 | 19 | ||
19 | static int machines__deliver_event(struct machines *machines, | 20 | static int perf_session__deliver_event(struct perf_session *session, |
20 | struct perf_evlist *evlist, | 21 | union perf_event *event, |
21 | union perf_event *event, | 22 | struct perf_sample *sample, |
22 | struct perf_sample *sample, | 23 | struct perf_tool *tool, |
23 | struct perf_tool *tool, u64 file_offset); | 24 | u64 file_offset); |
24 | 25 | ||
25 | static int perf_session__open(struct perf_session *session) | 26 | static int perf_session__open(struct perf_session *session) |
26 | { | 27 | { |
@@ -105,8 +106,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe, | |||
105 | return ret; | 106 | return ret; |
106 | } | 107 | } |
107 | 108 | ||
108 | return machines__deliver_event(&session->machines, session->evlist, event->event, | 109 | return perf_session__deliver_event(session, event->event, &sample, |
109 | &sample, session->tool, event->file_offset); | 110 | session->tool, event->file_offset); |
110 | } | 111 | } |
111 | 112 | ||
112 | struct perf_session *perf_session__new(struct perf_data_file *file, | 113 | struct perf_session *perf_session__new(struct perf_data_file *file, |
@@ -119,6 +120,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
119 | 120 | ||
120 | session->repipe = repipe; | 121 | session->repipe = repipe; |
121 | session->tool = tool; | 122 | session->tool = tool; |
123 | INIT_LIST_HEAD(&session->auxtrace_index); | ||
122 | machines__init(&session->machines); | 124 | machines__init(&session->machines); |
123 | ordered_events__init(&session->ordered_events, ordered_events__deliver_event); | 125 | ordered_events__init(&session->ordered_events, ordered_events__deliver_event); |
124 | 126 | ||
@@ -185,6 +187,8 @@ static void perf_session_env__delete(struct perf_session_env *env) | |||
185 | 187 | ||
186 | void perf_session__delete(struct perf_session *session) | 188 | void perf_session__delete(struct perf_session *session) |
187 | { | 189 | { |
190 | auxtrace__free(session); | ||
191 | auxtrace_index__free(&session->auxtrace_index); | ||
188 | perf_session__destroy_kernel_maps(session); | 192 | perf_session__destroy_kernel_maps(session); |
189 | perf_session__delete_threads(session); | 193 | perf_session__delete_threads(session); |
190 | perf_session_env__delete(&session->header.env); | 194 | perf_session_env__delete(&session->header.env); |
@@ -262,6 +266,49 @@ static int process_id_index_stub(struct perf_tool *tool __maybe_unused, | |||
262 | return 0; | 266 | return 0; |
263 | } | 267 | } |
264 | 268 | ||
269 | static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused, | ||
270 | union perf_event *event __maybe_unused, | ||
271 | struct perf_session *session __maybe_unused) | ||
272 | { | ||
273 | dump_printf(": unhandled!\n"); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int skipn(int fd, off_t n) | ||
278 | { | ||
279 | char buf[4096]; | ||
280 | ssize_t ret; | ||
281 | |||
282 | while (n > 0) { | ||
283 | ret = read(fd, buf, min(n, (off_t)sizeof(buf))); | ||
284 | if (ret <= 0) | ||
285 | return ret; | ||
286 | n -= ret; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, | ||
293 | union perf_event *event, | ||
294 | struct perf_session *session | ||
295 | __maybe_unused) | ||
296 | { | ||
297 | dump_printf(": unhandled!\n"); | ||
298 | if (perf_data_file__is_pipe(session->file)) | ||
299 | skipn(perf_data_file__fd(session->file), event->auxtrace.size); | ||
300 | return event->auxtrace.size; | ||
301 | } | ||
302 | |||
303 | static | ||
304 | int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, | ||
305 | union perf_event *event __maybe_unused, | ||
306 | struct perf_session *session __maybe_unused) | ||
307 | { | ||
308 | dump_printf(": unhandled!\n"); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
265 | void perf_tool__fill_defaults(struct perf_tool *tool) | 312 | void perf_tool__fill_defaults(struct perf_tool *tool) |
266 | { | 313 | { |
267 | if (tool->sample == NULL) | 314 | if (tool->sample == NULL) |
@@ -278,6 +325,10 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
278 | tool->exit = process_event_stub; | 325 | tool->exit = process_event_stub; |
279 | if (tool->lost == NULL) | 326 | if (tool->lost == NULL) |
280 | tool->lost = perf_event__process_lost; | 327 | tool->lost = perf_event__process_lost; |
328 | if (tool->aux == NULL) | ||
329 | tool->aux = perf_event__process_aux; | ||
330 | if (tool->itrace_start == NULL) | ||
331 | tool->itrace_start = perf_event__process_itrace_start; | ||
281 | if (tool->read == NULL) | 332 | if (tool->read == NULL) |
282 | tool->read = process_event_sample_stub; | 333 | tool->read = process_event_sample_stub; |
283 | if (tool->throttle == NULL) | 334 | if (tool->throttle == NULL) |
@@ -298,6 +349,12 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
298 | } | 349 | } |
299 | if (tool->id_index == NULL) | 350 | if (tool->id_index == NULL) |
300 | tool->id_index = process_id_index_stub; | 351 | tool->id_index = process_id_index_stub; |
352 | if (tool->auxtrace_info == NULL) | ||
353 | tool->auxtrace_info = process_event_auxtrace_info_stub; | ||
354 | if (tool->auxtrace == NULL) | ||
355 | tool->auxtrace = process_event_auxtrace_stub; | ||
356 | if (tool->auxtrace_error == NULL) | ||
357 | tool->auxtrace_error = process_event_auxtrace_error_stub; | ||
301 | } | 358 | } |
302 | 359 | ||
303 | static void swap_sample_id_all(union perf_event *event, void *data) | 360 | static void swap_sample_id_all(union perf_event *event, void *data) |
@@ -390,6 +447,26 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all) | |||
390 | swap_sample_id_all(event, &event->read + 1); | 447 | swap_sample_id_all(event, &event->read + 1); |
391 | } | 448 | } |
392 | 449 | ||
450 | static void perf_event__aux_swap(union perf_event *event, bool sample_id_all) | ||
451 | { | ||
452 | event->aux.aux_offset = bswap_64(event->aux.aux_offset); | ||
453 | event->aux.aux_size = bswap_64(event->aux.aux_size); | ||
454 | event->aux.flags = bswap_64(event->aux.flags); | ||
455 | |||
456 | if (sample_id_all) | ||
457 | swap_sample_id_all(event, &event->aux + 1); | ||
458 | } | ||
459 | |||
460 | static void perf_event__itrace_start_swap(union perf_event *event, | ||
461 | bool sample_id_all) | ||
462 | { | ||
463 | event->itrace_start.pid = bswap_32(event->itrace_start.pid); | ||
464 | event->itrace_start.tid = bswap_32(event->itrace_start.tid); | ||
465 | |||
466 | if (sample_id_all) | ||
467 | swap_sample_id_all(event, &event->itrace_start + 1); | ||
468 | } | ||
469 | |||
393 | static void perf_event__throttle_swap(union perf_event *event, | 470 | static void perf_event__throttle_swap(union perf_event *event, |
394 | bool sample_id_all) | 471 | bool sample_id_all) |
395 | { | 472 | { |
@@ -449,6 +526,7 @@ void perf_event__attr_swap(struct perf_event_attr *attr) | |||
449 | attr->branch_sample_type = bswap_64(attr->branch_sample_type); | 526 | attr->branch_sample_type = bswap_64(attr->branch_sample_type); |
450 | attr->sample_regs_user = bswap_64(attr->sample_regs_user); | 527 | attr->sample_regs_user = bswap_64(attr->sample_regs_user); |
451 | attr->sample_stack_user = bswap_32(attr->sample_stack_user); | 528 | attr->sample_stack_user = bswap_32(attr->sample_stack_user); |
529 | attr->aux_watermark = bswap_32(attr->aux_watermark); | ||
452 | 530 | ||
453 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); | 531 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); |
454 | } | 532 | } |
@@ -478,6 +556,40 @@ static void perf_event__tracing_data_swap(union perf_event *event, | |||
478 | event->tracing_data.size = bswap_32(event->tracing_data.size); | 556 | event->tracing_data.size = bswap_32(event->tracing_data.size); |
479 | } | 557 | } |
480 | 558 | ||
559 | static void perf_event__auxtrace_info_swap(union perf_event *event, | ||
560 | bool sample_id_all __maybe_unused) | ||
561 | { | ||
562 | size_t size; | ||
563 | |||
564 | event->auxtrace_info.type = bswap_32(event->auxtrace_info.type); | ||
565 | |||
566 | size = event->header.size; | ||
567 | size -= (void *)&event->auxtrace_info.priv - (void *)event; | ||
568 | mem_bswap_64(event->auxtrace_info.priv, size); | ||
569 | } | ||
570 | |||
571 | static void perf_event__auxtrace_swap(union perf_event *event, | ||
572 | bool sample_id_all __maybe_unused) | ||
573 | { | ||
574 | event->auxtrace.size = bswap_64(event->auxtrace.size); | ||
575 | event->auxtrace.offset = bswap_64(event->auxtrace.offset); | ||
576 | event->auxtrace.reference = bswap_64(event->auxtrace.reference); | ||
577 | event->auxtrace.idx = bswap_32(event->auxtrace.idx); | ||
578 | event->auxtrace.tid = bswap_32(event->auxtrace.tid); | ||
579 | event->auxtrace.cpu = bswap_32(event->auxtrace.cpu); | ||
580 | } | ||
581 | |||
582 | static void perf_event__auxtrace_error_swap(union perf_event *event, | ||
583 | bool sample_id_all __maybe_unused) | ||
584 | { | ||
585 | event->auxtrace_error.type = bswap_32(event->auxtrace_error.type); | ||
586 | event->auxtrace_error.code = bswap_32(event->auxtrace_error.code); | ||
587 | event->auxtrace_error.cpu = bswap_32(event->auxtrace_error.cpu); | ||
588 | event->auxtrace_error.pid = bswap_32(event->auxtrace_error.pid); | ||
589 | event->auxtrace_error.tid = bswap_32(event->auxtrace_error.tid); | ||
590 | event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip); | ||
591 | } | ||
592 | |||
481 | typedef void (*perf_event__swap_op)(union perf_event *event, | 593 | typedef void (*perf_event__swap_op)(union perf_event *event, |
482 | bool sample_id_all); | 594 | bool sample_id_all); |
483 | 595 | ||
@@ -492,11 +604,16 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
492 | [PERF_RECORD_THROTTLE] = perf_event__throttle_swap, | 604 | [PERF_RECORD_THROTTLE] = perf_event__throttle_swap, |
493 | [PERF_RECORD_UNTHROTTLE] = perf_event__throttle_swap, | 605 | [PERF_RECORD_UNTHROTTLE] = perf_event__throttle_swap, |
494 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, | 606 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
607 | [PERF_RECORD_AUX] = perf_event__aux_swap, | ||
608 | [PERF_RECORD_ITRACE_START] = perf_event__itrace_start_swap, | ||
495 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, | 609 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, |
496 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 610 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
497 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 611 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
498 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 612 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
499 | [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, | 613 | [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, |
614 | [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, | ||
615 | [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, | ||
616 | [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, | ||
500 | [PERF_RECORD_HEADER_MAX] = NULL, | 617 | [PERF_RECORD_HEADER_MAX] = NULL, |
501 | }; | 618 | }; |
502 | 619 | ||
@@ -938,12 +1055,34 @@ static int machines__deliver_event(struct machines *machines, | |||
938 | return tool->throttle(tool, event, sample, machine); | 1055 | return tool->throttle(tool, event, sample, machine); |
939 | case PERF_RECORD_UNTHROTTLE: | 1056 | case PERF_RECORD_UNTHROTTLE: |
940 | return tool->unthrottle(tool, event, sample, machine); | 1057 | return tool->unthrottle(tool, event, sample, machine); |
1058 | case PERF_RECORD_AUX: | ||
1059 | return tool->aux(tool, event, sample, machine); | ||
1060 | case PERF_RECORD_ITRACE_START: | ||
1061 | return tool->itrace_start(tool, event, sample, machine); | ||
941 | default: | 1062 | default: |
942 | ++evlist->stats.nr_unknown_events; | 1063 | ++evlist->stats.nr_unknown_events; |
943 | return -1; | 1064 | return -1; |
944 | } | 1065 | } |
945 | } | 1066 | } |
946 | 1067 | ||
1068 | static int perf_session__deliver_event(struct perf_session *session, | ||
1069 | union perf_event *event, | ||
1070 | struct perf_sample *sample, | ||
1071 | struct perf_tool *tool, | ||
1072 | u64 file_offset) | ||
1073 | { | ||
1074 | int ret; | ||
1075 | |||
1076 | ret = auxtrace__process_event(session, event, sample, tool); | ||
1077 | if (ret < 0) | ||
1078 | return ret; | ||
1079 | if (ret > 0) | ||
1080 | return 0; | ||
1081 | |||
1082 | return machines__deliver_event(&session->machines, session->evlist, | ||
1083 | event, sample, tool, file_offset); | ||
1084 | } | ||
1085 | |||
947 | static s64 perf_session__process_user_event(struct perf_session *session, | 1086 | static s64 perf_session__process_user_event(struct perf_session *session, |
948 | union perf_event *event, | 1087 | union perf_event *event, |
949 | u64 file_offset) | 1088 | u64 file_offset) |
@@ -980,6 +1119,15 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
980 | return tool->finished_round(tool, event, oe); | 1119 | return tool->finished_round(tool, event, oe); |
981 | case PERF_RECORD_ID_INDEX: | 1120 | case PERF_RECORD_ID_INDEX: |
982 | return tool->id_index(tool, event, session); | 1121 | return tool->id_index(tool, event, session); |
1122 | case PERF_RECORD_AUXTRACE_INFO: | ||
1123 | return tool->auxtrace_info(tool, event, session); | ||
1124 | case PERF_RECORD_AUXTRACE: | ||
1125 | /* setup for reading amidst mmap */ | ||
1126 | lseek(fd, file_offset + event->header.size, SEEK_SET); | ||
1127 | return tool->auxtrace(tool, event, session); | ||
1128 | case PERF_RECORD_AUXTRACE_ERROR: | ||
1129 | perf_session__auxtrace_error_inc(session, event); | ||
1130 | return tool->auxtrace_error(tool, event, session); | ||
983 | default: | 1131 | default: |
984 | return -EINVAL; | 1132 | return -EINVAL; |
985 | } | 1133 | } |
@@ -1096,8 +1244,8 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
1096 | return ret; | 1244 | return ret; |
1097 | } | 1245 | } |
1098 | 1246 | ||
1099 | return machines__deliver_event(&session->machines, evlist, event, | 1247 | return perf_session__deliver_event(session, event, &sample, tool, |
1100 | &sample, tool, file_offset); | 1248 | file_offset); |
1101 | } | 1249 | } |
1102 | 1250 | ||
1103 | void perf_event_header__bswap(struct perf_event_header *hdr) | 1251 | void perf_event_header__bswap(struct perf_event_header *hdr) |
@@ -1168,6 +1316,8 @@ static void perf_session__warn_about_errors(const struct perf_session *session) | |||
1168 | 1316 | ||
1169 | if (oe->nr_unordered_events != 0) | 1317 | if (oe->nr_unordered_events != 0) |
1170 | ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events); | 1318 | ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events); |
1319 | |||
1320 | events_stats__auxtrace_error_warn(stats); | ||
1171 | } | 1321 | } |
1172 | 1322 | ||
1173 | volatile int session_done; | 1323 | volatile int session_done; |
@@ -1256,10 +1406,14 @@ more: | |||
1256 | done: | 1406 | done: |
1257 | /* do the final flush for ordered samples */ | 1407 | /* do the final flush for ordered samples */ |
1258 | err = ordered_events__flush(oe, OE_FLUSH__FINAL); | 1408 | err = ordered_events__flush(oe, OE_FLUSH__FINAL); |
1409 | if (err) | ||
1410 | goto out_err; | ||
1411 | err = auxtrace__flush_events(session, tool); | ||
1259 | out_err: | 1412 | out_err: |
1260 | free(buf); | 1413 | free(buf); |
1261 | perf_session__warn_about_errors(session); | 1414 | perf_session__warn_about_errors(session); |
1262 | ordered_events__free(&session->ordered_events); | 1415 | ordered_events__free(&session->ordered_events); |
1416 | auxtrace__free_events(session); | ||
1263 | return err; | 1417 | return err; |
1264 | } | 1418 | } |
1265 | 1419 | ||
@@ -1402,10 +1556,14 @@ more: | |||
1402 | out: | 1556 | out: |
1403 | /* do the final flush for ordered samples */ | 1557 | /* do the final flush for ordered samples */ |
1404 | err = ordered_events__flush(oe, OE_FLUSH__FINAL); | 1558 | err = ordered_events__flush(oe, OE_FLUSH__FINAL); |
1559 | if (err) | ||
1560 | goto out_err; | ||
1561 | err = auxtrace__flush_events(session, tool); | ||
1405 | out_err: | 1562 | out_err: |
1406 | ui_progress__finish(); | 1563 | ui_progress__finish(); |
1407 | perf_session__warn_about_errors(session); | 1564 | perf_session__warn_about_errors(session); |
1408 | ordered_events__free(&session->ordered_events); | 1565 | ordered_events__free(&session->ordered_events); |
1566 | auxtrace__free_events(session); | ||
1409 | session->one_mmap = false; | 1567 | session->one_mmap = false; |
1410 | return err; | 1568 | return err; |
1411 | } | 1569 | } |
@@ -1488,7 +1646,13 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp | |||
1488 | 1646 | ||
1489 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1647 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
1490 | { | 1648 | { |
1491 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | 1649 | size_t ret; |
1650 | const char *msg = ""; | ||
1651 | |||
1652 | if (perf_header__has_feat(&session->header, HEADER_AUXTRACE)) | ||
1653 | msg = " (excludes AUX area (e.g. instruction trace) decoded / synthesized events)"; | ||
1654 | |||
1655 | ret = fprintf(fp, "Aggregated stats:%s\n", msg); | ||
1492 | 1656 | ||
1493 | ret += events_stats__fprintf(&session->evlist->stats, fp); | 1657 | ret += events_stats__fprintf(&session->evlist->stats, fp); |
1494 | return ret; | 1658 | return ret; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index d5fa7b7916ef..b44afc75d1cc 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -15,10 +15,16 @@ | |||
15 | struct ip_callchain; | 15 | struct ip_callchain; |
16 | struct thread; | 16 | struct thread; |
17 | 17 | ||
18 | struct auxtrace; | ||
19 | struct itrace_synth_opts; | ||
20 | |||
18 | struct perf_session { | 21 | struct perf_session { |
19 | struct perf_header header; | 22 | struct perf_header header; |
20 | struct machines machines; | 23 | struct machines machines; |
21 | struct perf_evlist *evlist; | 24 | struct perf_evlist *evlist; |
25 | struct auxtrace *auxtrace; | ||
26 | struct itrace_synth_opts *itrace_synth_opts; | ||
27 | struct list_head auxtrace_index; | ||
22 | struct trace_event tevent; | 28 | struct trace_event tevent; |
23 | bool repipe; | 29 | bool repipe; |
24 | bool one_mmap; | 30 | bool one_mmap; |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 846036a921dc..e97cd476d336 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -58,15 +58,16 @@ struct he_stat { | |||
58 | 58 | ||
59 | struct hist_entry_diff { | 59 | struct hist_entry_diff { |
60 | bool computed; | 60 | bool computed; |
61 | union { | ||
62 | /* PERF_HPP__DELTA */ | ||
63 | double period_ratio_delta; | ||
61 | 64 | ||
62 | /* PERF_HPP__DELTA */ | 65 | /* PERF_HPP__RATIO */ |
63 | double period_ratio_delta; | 66 | double period_ratio; |
64 | |||
65 | /* PERF_HPP__RATIO */ | ||
66 | double period_ratio; | ||
67 | 67 | ||
68 | /* HISTC_WEIGHTED_DIFF */ | 68 | /* HISTC_WEIGHTED_DIFF */ |
69 | s64 wdiff; | 69 | s64 wdiff; |
70 | }; | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | /** | 73 | /** |
@@ -92,21 +93,28 @@ struct hist_entry { | |||
92 | s32 cpu; | 93 | s32 cpu; |
93 | u8 cpumode; | 94 | u8 cpumode; |
94 | 95 | ||
95 | struct hist_entry_diff diff; | ||
96 | |||
97 | /* We are added by hists__add_dummy_entry. */ | 96 | /* We are added by hists__add_dummy_entry. */ |
98 | bool dummy; | 97 | bool dummy; |
99 | 98 | ||
100 | /* XXX These two should move to some tree widget lib */ | ||
101 | u16 row_offset; | ||
102 | u16 nr_rows; | ||
103 | |||
104 | bool init_have_children; | ||
105 | char level; | 99 | char level; |
106 | u8 filtered; | 100 | u8 filtered; |
101 | union { | ||
102 | /* | ||
103 | * Since perf diff only supports the stdio output, TUI | ||
104 | * fields are only accessed from perf report (or perf | ||
105 | * top). So make it an union to reduce memory usage. | ||
106 | */ | ||
107 | struct hist_entry_diff diff; | ||
108 | struct /* for TUI */ { | ||
109 | u16 row_offset; | ||
110 | u16 nr_rows; | ||
111 | bool init_have_children; | ||
112 | bool unfolded; | ||
113 | bool has_children; | ||
114 | }; | ||
115 | }; | ||
107 | char *srcline; | 116 | char *srcline; |
108 | struct symbol *parent; | 117 | struct symbol *parent; |
109 | unsigned long position; | ||
110 | struct rb_root sorted_chain; | 118 | struct rb_root sorted_chain; |
111 | struct branch_info *branch_info; | 119 | struct branch_info *branch_info; |
112 | struct hists *hists; | 120 | struct hists *hists; |
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 79a757a2a15c..bcae659b6546 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c | |||
@@ -170,6 +170,46 @@ struct strfilter *strfilter__new(const char *rules, const char **err) | |||
170 | return filter; | 170 | return filter; |
171 | } | 171 | } |
172 | 172 | ||
173 | static int strfilter__append(struct strfilter *filter, bool _or, | ||
174 | const char *rules, const char **err) | ||
175 | { | ||
176 | struct strfilter_node *right, *root; | ||
177 | const char *ep = NULL; | ||
178 | |||
179 | if (!filter || !rules) | ||
180 | return -EINVAL; | ||
181 | |||
182 | right = strfilter_node__new(rules, &ep); | ||
183 | if (!right || *ep != '\0') { | ||
184 | if (err) | ||
185 | *err = ep; | ||
186 | goto error; | ||
187 | } | ||
188 | root = strfilter_node__alloc(_or ? OP_or : OP_and, filter->root, right); | ||
189 | if (!root) { | ||
190 | ep = NULL; | ||
191 | goto error; | ||
192 | } | ||
193 | |||
194 | filter->root = root; | ||
195 | return 0; | ||
196 | |||
197 | error: | ||
198 | strfilter_node__delete(right); | ||
199 | return ep ? -EINVAL : -ENOMEM; | ||
200 | } | ||
201 | |||
202 | int strfilter__or(struct strfilter *filter, const char *rules, const char **err) | ||
203 | { | ||
204 | return strfilter__append(filter, true, rules, err); | ||
205 | } | ||
206 | |||
207 | int strfilter__and(struct strfilter *filter, const char *rules, | ||
208 | const char **err) | ||
209 | { | ||
210 | return strfilter__append(filter, false, rules, err); | ||
211 | } | ||
212 | |||
173 | static bool strfilter_node__compare(struct strfilter_node *node, | 213 | static bool strfilter_node__compare(struct strfilter_node *node, |
174 | const char *str) | 214 | const char *str) |
175 | { | 215 | { |
@@ -197,3 +237,70 @@ bool strfilter__compare(struct strfilter *filter, const char *str) | |||
197 | return false; | 237 | return false; |
198 | return strfilter_node__compare(filter->root, str); | 238 | return strfilter_node__compare(filter->root, str); |
199 | } | 239 | } |
240 | |||
241 | static int strfilter_node__sprint(struct strfilter_node *node, char *buf); | ||
242 | |||
243 | /* sprint node in parenthesis if needed */ | ||
244 | static int strfilter_node__sprint_pt(struct strfilter_node *node, char *buf) | ||
245 | { | ||
246 | int len; | ||
247 | int pt = node->r ? 2 : 0; /* don't need to check node->l */ | ||
248 | |||
249 | if (buf && pt) | ||
250 | *buf++ = '('; | ||
251 | len = strfilter_node__sprint(node, buf); | ||
252 | if (len < 0) | ||
253 | return len; | ||
254 | if (buf && pt) | ||
255 | *(buf + len) = ')'; | ||
256 | return len + pt; | ||
257 | } | ||
258 | |||
259 | static int strfilter_node__sprint(struct strfilter_node *node, char *buf) | ||
260 | { | ||
261 | int len = 0, rlen; | ||
262 | |||
263 | if (!node || !node->p) | ||
264 | return -EINVAL; | ||
265 | |||
266 | switch (*node->p) { | ||
267 | case '|': | ||
268 | case '&': | ||
269 | len = strfilter_node__sprint_pt(node->l, buf); | ||
270 | if (len < 0) | ||
271 | return len; | ||
272 | case '!': | ||
273 | if (buf) { | ||
274 | *(buf + len++) = *node->p; | ||
275 | buf += len; | ||
276 | } else | ||
277 | len++; | ||
278 | rlen = strfilter_node__sprint_pt(node->r, buf); | ||
279 | if (rlen < 0) | ||
280 | return rlen; | ||
281 | len += rlen; | ||
282 | break; | ||
283 | default: | ||
284 | len = strlen(node->p); | ||
285 | if (buf) | ||
286 | strcpy(buf, node->p); | ||
287 | } | ||
288 | |||
289 | return len; | ||
290 | } | ||
291 | |||
292 | char *strfilter__string(struct strfilter *filter) | ||
293 | { | ||
294 | int len; | ||
295 | char *ret = NULL; | ||
296 | |||
297 | len = strfilter_node__sprint(filter->root, NULL); | ||
298 | if (len < 0) | ||
299 | return NULL; | ||
300 | |||
301 | ret = malloc(len + 1); | ||
302 | if (ret) | ||
303 | strfilter_node__sprint(filter->root, ret); | ||
304 | |||
305 | return ret; | ||
306 | } | ||
diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h index fe611f3c9e39..cff5eda88728 100644 --- a/tools/perf/util/strfilter.h +++ b/tools/perf/util/strfilter.h | |||
@@ -29,6 +29,32 @@ struct strfilter { | |||
29 | struct strfilter *strfilter__new(const char *rules, const char **err); | 29 | struct strfilter *strfilter__new(const char *rules, const char **err); |
30 | 30 | ||
31 | /** | 31 | /** |
32 | * strfilter__or - Append an additional rule by logical-or | ||
33 | * @filter: Original string filter | ||
34 | * @rules: Filter rule to be appended at left of the root of | ||
35 | * @filter by using logical-or. | ||
36 | * @err: Pointer which points an error detected on @rules | ||
37 | * | ||
38 | * Parse @rules and join it to the @filter by using logical-or. | ||
39 | * Return 0 if success, or return the error code. | ||
40 | */ | ||
41 | int strfilter__or(struct strfilter *filter, | ||
42 | const char *rules, const char **err); | ||
43 | |||
44 | /** | ||
45 | * strfilter__add - Append an additional rule by logical-and | ||
46 | * @filter: Original string filter | ||
47 | * @rules: Filter rule to be appended at left of the root of | ||
48 | * @filter by using logical-and. | ||
49 | * @err: Pointer which points an error detected on @rules | ||
50 | * | ||
51 | * Parse @rules and join it to the @filter by using logical-and. | ||
52 | * Return 0 if success, or return the error code. | ||
53 | */ | ||
54 | int strfilter__and(struct strfilter *filter, | ||
55 | const char *rules, const char **err); | ||
56 | |||
57 | /** | ||
32 | * strfilter__compare - compare given string and a string filter | 58 | * strfilter__compare - compare given string and a string filter |
33 | * @filter: String filter | 59 | * @filter: String filter |
34 | * @str: target string | 60 | * @str: target string |
@@ -45,4 +71,13 @@ bool strfilter__compare(struct strfilter *filter, const char *str); | |||
45 | */ | 71 | */ |
46 | void strfilter__delete(struct strfilter *filter); | 72 | void strfilter__delete(struct strfilter *filter); |
47 | 73 | ||
74 | /** | ||
75 | * strfilter__string - Reconstruct a rule string from filter | ||
76 | * @filter: String filter to reconstruct | ||
77 | * | ||
78 | * Reconstruct a rule string from @filter. This will be good for | ||
79 | * debug messages. Note that returning string must be freed afterward. | ||
80 | */ | ||
81 | char *strfilter__string(struct strfilter *filter); | ||
82 | |||
48 | #endif | 83 | #endif |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index a7ab6063e038..9d526a5312b1 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -630,6 +630,11 @@ void symsrc__destroy(struct symsrc *ss) | |||
630 | close(ss->fd); | 630 | close(ss->fd); |
631 | } | 631 | } |
632 | 632 | ||
633 | bool __weak elf__needs_adjust_symbols(GElf_Ehdr ehdr) | ||
634 | { | ||
635 | return ehdr.e_type == ET_EXEC || ehdr.e_type == ET_REL; | ||
636 | } | ||
637 | |||
633 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | 638 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, |
634 | enum dso_binary_type type) | 639 | enum dso_binary_type type) |
635 | { | 640 | { |
@@ -678,6 +683,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
678 | } | 683 | } |
679 | 684 | ||
680 | if (!dso__build_id_equal(dso, build_id)) { | 685 | if (!dso__build_id_equal(dso, build_id)) { |
686 | pr_debug("%s: build id mismatch for %s.\n", __func__, name); | ||
681 | dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID; | 687 | dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID; |
682 | goto out_elf_end; | 688 | goto out_elf_end; |
683 | } | 689 | } |
@@ -711,8 +717,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
711 | ".gnu.prelink_undo", | 717 | ".gnu.prelink_undo", |
712 | NULL) != NULL); | 718 | NULL) != NULL); |
713 | } else { | 719 | } else { |
714 | ss->adjust_symbols = ehdr.e_type == ET_EXEC || | 720 | ss->adjust_symbols = elf__needs_adjust_symbols(ehdr); |
715 | ehdr.e_type == ET_REL; | ||
716 | } | 721 | } |
717 | 722 | ||
718 | ss->name = strdup(name); | 723 | ss->name = strdup(name); |
@@ -771,6 +776,8 @@ static bool want_demangle(bool is_kernel_sym) | |||
771 | return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; | 776 | return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; |
772 | } | 777 | } |
773 | 778 | ||
779 | void __weak arch__elf_sym_adjust(GElf_Sym *sym __maybe_unused) { } | ||
780 | |||
774 | int dso__load_sym(struct dso *dso, struct map *map, | 781 | int dso__load_sym(struct dso *dso, struct map *map, |
775 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 782 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
776 | symbol_filter_t filter, int kmodule) | 783 | symbol_filter_t filter, int kmodule) |
@@ -935,6 +942,8 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
935 | (sym.st_value & 1)) | 942 | (sym.st_value & 1)) |
936 | --sym.st_value; | 943 | --sym.st_value; |
937 | 944 | ||
945 | arch__elf_sym_adjust(&sym); | ||
946 | |||
938 | if (dso->kernel || kmodule) { | 947 | if (dso->kernel || kmodule) { |
939 | char dso_name[PATH_MAX]; | 948 | char dso_name[PATH_MAX]; |
940 | 949 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 201f6c4ca738..45ba48a7acb3 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -85,8 +85,17 @@ static int prefix_underscores_count(const char *str) | |||
85 | return tail - str; | 85 | return tail - str; |
86 | } | 86 | } |
87 | 87 | ||
88 | #define SYMBOL_A 0 | 88 | int __weak arch__choose_best_symbol(struct symbol *syma, |
89 | #define SYMBOL_B 1 | 89 | struct symbol *symb __maybe_unused) |
90 | { | ||
91 | /* Avoid "SyS" kernel syscall aliases */ | ||
92 | if (strlen(syma->name) >= 3 && !strncmp(syma->name, "SyS", 3)) | ||
93 | return SYMBOL_B; | ||
94 | if (strlen(syma->name) >= 10 && !strncmp(syma->name, "compat_SyS", 10)) | ||
95 | return SYMBOL_B; | ||
96 | |||
97 | return SYMBOL_A; | ||
98 | } | ||
90 | 99 | ||
91 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | 100 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) |
92 | { | 101 | { |
@@ -134,13 +143,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
134 | else if (na < nb) | 143 | else if (na < nb) |
135 | return SYMBOL_B; | 144 | return SYMBOL_B; |
136 | 145 | ||
137 | /* Avoid "SyS" kernel syscall aliases */ | 146 | return arch__choose_best_symbol(syma, symb); |
138 | if (na >= 3 && !strncmp(syma->name, "SyS", 3)) | ||
139 | return SYMBOL_B; | ||
140 | if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10)) | ||
141 | return SYMBOL_B; | ||
142 | |||
143 | return SYMBOL_A; | ||
144 | } | 147 | } |
145 | 148 | ||
146 | void symbols__fixup_duplicate(struct rb_root *symbols) | 149 | void symbols__fixup_duplicate(struct rb_root *symbols) |
@@ -408,7 +411,7 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols, | |||
408 | int cmp; | 411 | int cmp; |
409 | 412 | ||
410 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | 413 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); |
411 | cmp = strcmp(name, s->sym.name); | 414 | cmp = arch__compare_symbol_names(name, s->sym.name); |
412 | 415 | ||
413 | if (cmp < 0) | 416 | if (cmp < 0) |
414 | n = n->rb_left; | 417 | n = n->rb_left; |
@@ -426,7 +429,7 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols, | |||
426 | struct symbol_name_rb_node *tmp; | 429 | struct symbol_name_rb_node *tmp; |
427 | 430 | ||
428 | tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); | 431 | tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); |
429 | if (strcmp(tmp->sym.name, s->sym.name)) | 432 | if (arch__compare_symbol_names(tmp->sym.name, s->sym.name)) |
430 | break; | 433 | break; |
431 | 434 | ||
432 | s = tmp; | 435 | s = tmp; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 09561500164a..bef47ead1d9b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -158,8 +158,6 @@ struct ref_reloc_sym { | |||
158 | struct map_symbol { | 158 | struct map_symbol { |
159 | struct map *map; | 159 | struct map *map; |
160 | struct symbol *sym; | 160 | struct symbol *sym; |
161 | bool unfolded; | ||
162 | bool has_children; | ||
163 | }; | 161 | }; |
164 | 162 | ||
165 | struct addr_map_symbol { | 163 | struct addr_map_symbol { |
@@ -303,4 +301,14 @@ int setup_list(struct strlist **list, const char *list_str, | |||
303 | int setup_intlist(struct intlist **list, const char *list_str, | 301 | int setup_intlist(struct intlist **list, const char *list_str, |
304 | const char *list_name); | 302 | const char *list_name); |
305 | 303 | ||
304 | #ifdef HAVE_LIBELF_SUPPORT | ||
305 | bool elf__needs_adjust_symbols(GElf_Ehdr ehdr); | ||
306 | void arch__elf_sym_adjust(GElf_Sym *sym); | ||
307 | #endif | ||
308 | |||
309 | #define SYMBOL_A 0 | ||
310 | #define SYMBOL_B 1 | ||
311 | |||
312 | int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb); | ||
313 | |||
306 | #endif /* __PERF_SYMBOL */ | 314 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 51d9e56c0f84..7f282ad1d2bd 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -3,6 +3,8 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | 5 | ||
6 | #include <linux/types.h> | ||
7 | |||
6 | struct perf_session; | 8 | struct perf_session; |
7 | union perf_event; | 9 | union perf_event; |
8 | struct perf_evlist; | 10 | struct perf_evlist; |
@@ -29,6 +31,9 @@ typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event, | |||
29 | typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, | 31 | typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, |
30 | struct ordered_events *oe); | 32 | struct ordered_events *oe); |
31 | 33 | ||
34 | typedef s64 (*event_op3)(struct perf_tool *tool, union perf_event *event, | ||
35 | struct perf_session *session); | ||
36 | |||
32 | struct perf_tool { | 37 | struct perf_tool { |
33 | event_sample sample, | 38 | event_sample sample, |
34 | read; | 39 | read; |
@@ -38,13 +43,18 @@ struct perf_tool { | |||
38 | fork, | 43 | fork, |
39 | exit, | 44 | exit, |
40 | lost, | 45 | lost, |
46 | aux, | ||
47 | itrace_start, | ||
41 | throttle, | 48 | throttle, |
42 | unthrottle; | 49 | unthrottle; |
43 | event_attr_op attr; | 50 | event_attr_op attr; |
44 | event_op2 tracing_data; | 51 | event_op2 tracing_data; |
45 | event_oe finished_round; | 52 | event_oe finished_round; |
46 | event_op2 build_id, | 53 | event_op2 build_id, |
47 | id_index; | 54 | id_index, |
55 | auxtrace_info, | ||
56 | auxtrace_error; | ||
57 | event_op3 auxtrace; | ||
48 | bool ordered_events; | 58 | bool ordered_events; |
49 | bool ordering_requires_timestamps; | 59 | bool ordering_requires_timestamps; |
50 | }; | 60 | }; |