diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-02-10 14:35:36 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2015-02-10 14:35:36 -0500 |
commit | 4ba24fef3eb3b142197135223b90ced2f319cd53 (patch) | |
tree | a20c125b27740ec7b4c761b11d801108e1b316b2 /tools/perf | |
parent | 47c1ffb2b6b630894e9a16442611c056ab21c057 (diff) | |
parent | 98a4a59ee31a12105a2b84f5b8b515ac2cb208ef (diff) |
Merge branch 'next' into for-linus
Prepare first round of input updates for 3.20.
Diffstat (limited to 'tools/perf')
180 files changed, 9959 insertions, 3536 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 782d86e961b9..40399c3d97d6 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
@@ -2,6 +2,8 @@ PERF-CFLAGS | |||
2 | PERF-GUI-VARS | 2 | PERF-GUI-VARS |
3 | PERF-VERSION-FILE | 3 | PERF-VERSION-FILE |
4 | perf | 4 | perf |
5 | perf-read-vdso32 | ||
6 | perf-read-vdsox32 | ||
5 | perf-help | 7 | perf-help |
6 | perf-record | 8 | perf-record |
7 | perf-report | 9 | perf-report |
@@ -15,6 +17,7 @@ perf.data | |||
15 | perf.data.old | 17 | perf.data.old |
16 | output.svg | 18 | output.svg |
17 | perf-archive | 19 | perf-archive |
20 | perf-with-kcore | ||
18 | tags | 21 | tags |
19 | TAGS | 22 | TAGS |
20 | cscope* | 23 | cscope* |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index b3b8abae62b8..e463caa3eb49 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -196,10 +196,10 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as: | |||
196 | 196 | ||
197 | - period being the hist entry period value | 197 | - period being the hist entry period value |
198 | 198 | ||
199 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option | 199 | - WEIGHT-A/WEIGHT-B being user supplied weights in the the '-c' option |
200 | behind ':' separator like '-c wdiff:1,2'. | 200 | behind ':' separator like '-c wdiff:1,2'. |
201 | - WIEGHT-A being the weight of the data file | 201 | - WEIGHT-A being the weight of the data file |
202 | - WIEGHT-B being the weight of the baseline data file | 202 | - WEIGHT-B being the weight of the baseline data file |
203 | 203 | ||
204 | SEE ALSO | 204 | SEE ALSO |
205 | -------- | 205 | -------- |
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 6e689dc89a2f..6252e776009c 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt | |||
@@ -100,7 +100,7 @@ OPTIONS | |||
100 | STAT REPORT OPTIONS | 100 | STAT REPORT OPTIONS |
101 | ------------------- | 101 | ------------------- |
102 | --vcpu=<value>:: | 102 | --vcpu=<value>:: |
103 | analyze events which occures on this vcpu. (default: all vcpus) | 103 | analyze events which occur on this vcpu. (default: all vcpus) |
104 | 104 | ||
105 | --event=<value>:: | 105 | --event=<value>:: |
106 | event to be analyzed. Possible values: vmexit, mmio (x86 only), | 106 | event to be analyzed. Possible values: vmexit, mmio (x86 only), |
@@ -134,7 +134,7 @@ STAT LIVE OPTIONS | |||
134 | Analyze events only for given process ID(s) (comma separated list). | 134 | Analyze events only for given process ID(s) (comma separated list). |
135 | 135 | ||
136 | --vcpu=<value>:: | 136 | --vcpu=<value>:: |
137 | analyze events which occures on this vcpu. (default: all vcpus) | 137 | analyze events which occur on this vcpu. (default: all vcpus) |
138 | 138 | ||
139 | 139 | ||
140 | --event=<value>:: | 140 | --event=<value>:: |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 6fce6a622206..cbb4f743d921 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -19,7 +19,7 @@ various perf commands with the -e option. | |||
19 | EVENT MODIFIERS | 19 | EVENT MODIFIERS |
20 | --------------- | 20 | --------------- |
21 | 21 | ||
22 | Events can optionally have a modifer by appending a colon and one or | 22 | Events can optionally have a modifier by appending a colon and one or |
23 | more modifiers. Modifiers allow the user to restrict the events to be | 23 | more modifiers. Modifiers allow the user to restrict the events to be |
24 | counted. The following modifiers exist: | 24 | counted. The following modifiers exist: |
25 | 25 | ||
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 1513935c399b..aaa869be3dc1 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -104,6 +104,9 @@ OPTIONS | |||
104 | Specify path to the executable or shared library file for user | 104 | Specify path to the executable or shared library file for user |
105 | space tracing. Can also be used with --funcs option. | 105 | space tracing. Can also be used with --funcs option. |
106 | 106 | ||
107 | --demangle-kernel:: | ||
108 | Demangle kernel symbols. | ||
109 | |||
107 | In absence of -m/-x options, perf probe checks if the first argument after | 110 | In absence of -m/-x options, perf probe checks if the first argument after |
108 | the options is an absolute path name. If its an absolute path, perf probe | 111 | the options is an absolute path name. If its an absolute path, perf probe |
109 | uses it as a target module/target user space binary to probe. | 112 | uses it as a target module/target user space binary to probe. |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index d460049cae8e..af9a54ece024 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -146,7 +146,7 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
146 | 146 | ||
147 | -N:: | 147 | -N:: |
148 | --no-buildid-cache:: | 148 | --no-buildid-cache:: |
149 | Do not update the builid cache. This saves some overhead in situations | 149 | Do not update the buildid cache. This saves some overhead in situations |
150 | where the information in the perf.data file (which includes buildids) | 150 | where the information in the perf.data file (which includes buildids) |
151 | is sufficient. | 151 | is sufficient. |
152 | 152 | ||
@@ -214,6 +214,12 @@ if combined with -a or -C options. | |||
214 | After starting the program, wait msecs before measuring. This is useful to | 214 | After starting the program, wait msecs before measuring. This is useful to |
215 | filter out the startup phase of the program, which is often very different. | 215 | filter out the startup phase of the program, which is often very different. |
216 | 216 | ||
217 | -I:: | ||
218 | --intr-regs:: | ||
219 | Capture machine state (registers) at interrupt, i.e., on counter overflows for | ||
220 | each sample. List of captured registers depends on the architecture. This option | ||
221 | is off by default. | ||
222 | |||
217 | SEE ALSO | 223 | SEE ALSO |
218 | -------- | 224 | -------- |
219 | linkperf:perf-stat[1], linkperf:perf-list[1] | 225 | 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 d2b59af62bc0..dd7cccdde498 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -147,7 +147,7 @@ OPTIONS | |||
147 | -w:: | 147 | -w:: |
148 | --column-widths=<width[,width...]>:: | 148 | --column-widths=<width[,width...]>:: |
149 | Force each column width to the provided list, for large terminal | 149 | Force each column width to the provided list, for large terminal |
150 | readability. | 150 | readability. 0 means no limit (default behavior). |
151 | 151 | ||
152 | -t:: | 152 | -t:: |
153 | --field-separator=:: | 153 | --field-separator=:: |
@@ -159,7 +159,7 @@ OPTIONS | |||
159 | --dump-raw-trace:: | 159 | --dump-raw-trace:: |
160 | Dump raw trace in ASCII. | 160 | Dump raw trace in ASCII. |
161 | 161 | ||
162 | -g [type,min[,limit],order[,key]]:: | 162 | -g [type,min[,limit],order[,key][,branch]]:: |
163 | --call-graph:: | 163 | --call-graph:: |
164 | Display call chains using type, min percent threshold, optional print | 164 | Display call chains using type, min percent threshold, optional print |
165 | limit and order. | 165 | limit and order. |
@@ -177,6 +177,11 @@ OPTIONS | |||
177 | - function: compare on functions | 177 | - function: compare on functions |
178 | - address: compare on individual code addresses | 178 | - address: compare on individual code addresses |
179 | 179 | ||
180 | branch can be: | ||
181 | - branch: include last branch information in callgraph | ||
182 | when available. Usually more convenient to use --branch-history | ||
183 | for this. | ||
184 | |||
180 | Default: fractal,0.5,callee,function. | 185 | Default: fractal,0.5,callee,function. |
181 | 186 | ||
182 | --children:: | 187 | --children:: |
@@ -266,6 +271,11 @@ OPTIONS | |||
266 | branch stacks and it will automatically switch to the branch view mode, | 271 | branch stacks and it will automatically switch to the branch view mode, |
267 | unless --no-branch-stack is used. | 272 | unless --no-branch-stack is used. |
268 | 273 | ||
274 | --branch-history:: | ||
275 | Add the addresses of sampled taken branches to the callstack. | ||
276 | This allows to examine the path the program took to each sample. | ||
277 | The data collection must have used -b (or -j) and -g. | ||
278 | |||
269 | --objdump=<path>:: | 279 | --objdump=<path>:: |
270 | Path to objdump binary. | 280 | Path to objdump binary. |
271 | 281 | ||
@@ -276,6 +286,9 @@ OPTIONS | |||
276 | Demangle symbol names to human readable form. It's enabled by default, | 286 | Demangle symbol names to human readable form. It's enabled by default, |
277 | disable with --no-demangle. | 287 | disable with --no-demangle. |
278 | 288 | ||
289 | --demangle-kernel:: | ||
290 | Demangle kernel symbol names to human readable form (for C++ kernels). | ||
291 | |||
279 | --mem-mode:: | 292 | --mem-mode:: |
280 | Use the data addresses of samples in addition to instruction addresses | 293 | Use the data addresses of samples in addition to instruction addresses |
281 | to build the histograms. To generate meaningful output, the perf.data | 294 | to build the histograms. To generate meaningful output, the perf.data |
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt index d00bef231340..dfbb506d2c34 100644 --- a/tools/perf/Documentation/perf-script-perl.txt +++ b/tools/perf/Documentation/perf-script-perl.txt | |||
@@ -181,8 +181,8 @@ strings for flag and symbolic fields. These correspond to the strings | |||
181 | and values parsed from the 'print fmt' fields of the event format | 181 | and values parsed from the 'print fmt' fields of the event format |
182 | files: | 182 | files: |
183 | 183 | ||
184 | flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name | 184 | flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name |
185 | symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name | 185 | symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name |
186 | 186 | ||
187 | Perf::Trace::Context Module | 187 | Perf::Trace::Context Module |
188 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 188 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt index 9f1f054b8432..54acba221558 100644 --- a/tools/perf/Documentation/perf-script-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt | |||
@@ -263,7 +263,7 @@ and having the counts we've tallied as values. | |||
263 | 263 | ||
264 | The print_syscall_totals() function iterates over the entries in the | 264 | The print_syscall_totals() function iterates over the entries in the |
265 | dictionary and displays a line for each entry containing the syscall | 265 | dictionary and displays a line for each entry containing the syscall |
266 | name (the dictonary keys contain the syscall ids, which are passed to | 266 | name (the dictionary keys contain the syscall ids, which are passed to |
267 | the Util function syscall_name(), which translates the raw syscall | 267 | the Util function syscall_name(), which translates the raw syscall |
268 | numbers to the corresponding syscall name strings). The output is | 268 | numbers to the corresponding syscall name strings). The output is |
269 | displayed after all the events in the trace have been processed, by | 269 | displayed after all the events in the trace have been processed, by |
@@ -576,8 +576,8 @@ strings for flag and symbolic fields. These correspond to the strings | |||
576 | and values parsed from the 'print fmt' fields of the event format | 576 | and values parsed from the 'print fmt' fields of the event format |
577 | files: | 577 | files: |
578 | 578 | ||
579 | flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name | 579 | flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name |
580 | symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name | 580 | symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name |
581 | 581 | ||
582 | The *autodict* function returns a special kind of Python | 582 | The *autodict* function returns a special kind of Python |
583 | dictionary that implements Perl's 'autovivifying' hashes in Python | 583 | dictionary that implements Perl's 'autovivifying' hashes in Python |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 05f9a0a6784c..21494806c0ab 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -115,7 +115,7 @@ 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. | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period. |
119 | Field list can be prepended with the type, trace, sw or hw, | 119 | Field list can be prepended with the type, trace, sw or hw, |
120 | to indicate to which event type the field list applies. | 120 | 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 | 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
@@ -140,7 +140,7 @@ OPTIONS | |||
140 | 140 | ||
141 | "Overriding previous field request for all events." | 141 | "Overriding previous field request for all events." |
142 | 142 | ||
143 | Alternativey, consider the order: | 143 | Alternatively, consider the order: |
144 | 144 | ||
145 | -f comm,tid,time,ip,sym -f trace: | 145 | -f comm,tid,time,ip,sym -f trace: |
146 | 146 | ||
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index d1d3e5121f89..31a5c3ea7f74 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt | |||
@@ -25,7 +25,7 @@ OPTIONS | |||
25 | ------- | 25 | ------- |
26 | -s:: | 26 | -s:: |
27 | --skip:: | 27 | --skip:: |
28 | Tests to skip (comma separater numeric list). | 28 | Tests to skip (comma separated numeric list). |
29 | 29 | ||
30 | -v:: | 30 | -v:: |
31 | --verbose:: | 31 | --verbose:: |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 180ae02137a5..3265b1070518 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -98,6 +98,9 @@ Default is to monitor all CPUS. | |||
98 | --hide_user_symbols:: | 98 | --hide_user_symbols:: |
99 | Hide user symbols. | 99 | Hide user symbols. |
100 | 100 | ||
101 | --demangle-kernel:: | ||
102 | Demangle kernel symbols. | ||
103 | |||
101 | -D:: | 104 | -D:: |
102 | --dump-symtab:: | 105 | --dump-symtab:: |
103 | Dump the symbol table used for profiling. | 106 | Dump the symbol table used for profiling. |
@@ -193,6 +196,12 @@ Default is to monitor all CPUS. | |||
193 | sum of shown entries will be always 100%. "absolute" means it retains | 196 | sum of shown entries will be always 100%. "absolute" means it retains |
194 | the original value before and after the filter is applied. | 197 | the original value before and after the filter is applied. |
195 | 198 | ||
199 | -w:: | ||
200 | --column-widths=<width[,width...]>:: | ||
201 | Force each column width to the provided list, for large terminal | ||
202 | readability. 0 means no limit (default behavior). | ||
203 | |||
204 | |||
196 | INTERACTIVE PROMPTING KEYS | 205 | INTERACTIVE PROMPTING KEYS |
197 | -------------------------- | 206 | -------------------------- |
198 | 207 | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 02aac831bdd9..7e1b1f2bb83c 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -20,7 +20,7 @@ scheduling events, etc. | |||
20 | This is a live mode tool in addition to working with perf.data files like | 20 | This is a live mode tool in addition to working with perf.data files like |
21 | the other perf tools. Files can be generated using the 'perf record' command | 21 | the other perf tools. Files can be generated using the 'perf record' command |
22 | but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). | 22 | but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). |
23 | Alernatively, the 'perf trace record' can be used as a shortcut to | 23 | Alternatively, 'perf trace record' can be used as a shortcut to |
24 | automatically include the raw_syscalls events when writing events to a file. | 24 | automatically include the raw_syscalls events when writing events to a file. |
25 | 25 | ||
26 | The following options apply to perf trace; options to perf trace record are | 26 | The following options apply to perf trace; options to perf trace record are |
diff --git a/tools/perf/Documentation/perf.txt b/tools/perf/Documentation/perf.txt index d240bb2e5b22..1e8e400b4493 100644 --- a/tools/perf/Documentation/perf.txt +++ b/tools/perf/Documentation/perf.txt | |||
@@ -18,6 +18,10 @@ OPTIONS | |||
18 | --debug verbose # sets verbose = 1 | 18 | --debug verbose # sets verbose = 1 |
19 | --debug verbose=2 # sets verbose = 2 | 19 | --debug verbose=2 # sets verbose = 2 |
20 | 20 | ||
21 | --buildid-dir:: | ||
22 | Setup buildid cache directory. It has higher priority than | ||
23 | buildid.dir config file option. | ||
24 | |||
21 | DESCRIPTION | 25 | DESCRIPTION |
22 | ----------- | 26 | ----------- |
23 | Performance counters for Linux are a new kernel-based subsystem | 27 | Performance counters for Linux are a new kernel-based subsystem |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 344c4d3d0a4a..83e2887f91a3 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -4,17 +4,31 @@ tools/lib/traceevent | |||
4 | tools/lib/api | 4 | tools/lib/api |
5 | tools/lib/symbol/kallsyms.c | 5 | tools/lib/symbol/kallsyms.c |
6 | tools/lib/symbol/kallsyms.h | 6 | tools/lib/symbol/kallsyms.h |
7 | tools/lib/util/find_next_bit.c | ||
7 | tools/include/asm/bug.h | 8 | tools/include/asm/bug.h |
9 | tools/include/asm-generic/bitops/atomic.h | ||
10 | tools/include/asm-generic/bitops/__ffs.h | ||
11 | tools/include/asm-generic/bitops/__fls.h | ||
12 | tools/include/asm-generic/bitops/find.h | ||
13 | tools/include/asm-generic/bitops/fls64.h | ||
14 | tools/include/asm-generic/bitops/fls.h | ||
15 | tools/include/asm-generic/bitops.h | ||
16 | tools/include/linux/bitops.h | ||
8 | tools/include/linux/compiler.h | 17 | tools/include/linux/compiler.h |
9 | tools/include/linux/hash.h | ||
10 | tools/include/linux/export.h | 18 | tools/include/linux/export.h |
19 | tools/include/linux/hash.h | ||
20 | tools/include/linux/log2.h | ||
11 | tools/include/linux/types.h | 21 | tools/include/linux/types.h |
22 | include/asm-generic/bitops/fls64.h | ||
23 | include/asm-generic/bitops/__fls.h | ||
24 | include/asm-generic/bitops/fls.h | ||
12 | include/linux/const.h | 25 | include/linux/const.h |
13 | include/linux/perf_event.h | 26 | include/linux/perf_event.h |
14 | include/linux/rbtree.h | 27 | include/linux/rbtree.h |
15 | include/linux/list.h | 28 | include/linux/list.h |
16 | include/linux/hash.h | 29 | include/linux/hash.h |
17 | include/linux/stringify.h | 30 | include/linux/stringify.h |
31 | lib/find_next_bit.c | ||
18 | lib/rbtree.c | 32 | lib/rbtree.c |
19 | include/linux/swab.h | 33 | include/linux/swab.h |
20 | arch/*/include/asm/unistd*.h | 34 | arch/*/include/asm/unistd*.h |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 2240974b7745..67a03a825b3c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -60,6 +60,15 @@ include config/utilities.mak | |||
60 | # | 60 | # |
61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support | 61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support |
62 | # for dwarf backtrace post unwind. | 62 | # for dwarf backtrace post unwind. |
63 | # | ||
64 | # Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32 | ||
65 | # for reading the 32-bit compatibility VDSO in 64-bit mode | ||
66 | # | ||
67 | # Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 | ||
68 | # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode | ||
69 | # | ||
70 | # Define NO_ZLIB if you do not want to support compressed kernel modules | ||
71 | |||
63 | 72 | ||
64 | ifeq ($(srctree),) | 73 | ifeq ($(srctree),) |
65 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | 74 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) |
@@ -126,6 +135,7 @@ PYRF_OBJS = | |||
126 | SCRIPT_SH = | 135 | SCRIPT_SH = |
127 | 136 | ||
128 | SCRIPT_SH += perf-archive.sh | 137 | SCRIPT_SH += perf-archive.sh |
138 | SCRIPT_SH += perf-with-kcore.sh | ||
129 | 139 | ||
130 | grep-libs = $(filter -l%,$(1)) | 140 | grep-libs = $(filter -l%,$(1)) |
131 | strip-libs = $(filter-out -l%,$(1)) | 141 | strip-libs = $(filter-out -l%,$(1)) |
@@ -170,11 +180,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | |||
170 | 180 | ||
171 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) | 181 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) |
172 | 182 | ||
173 | # | ||
174 | # Single 'perf' binary right now: | ||
175 | # | ||
176 | PROGRAMS += $(OUTPUT)perf | 183 | PROGRAMS += $(OUTPUT)perf |
177 | 184 | ||
185 | ifndef NO_PERF_READ_VDSO32 | ||
186 | PROGRAMS += $(OUTPUT)perf-read-vdso32 | ||
187 | endif | ||
188 | |||
189 | ifndef NO_PERF_READ_VDSOX32 | ||
190 | PROGRAMS += $(OUTPUT)perf-read-vdsox32 | ||
191 | endif | ||
192 | |||
178 | # what 'all' will build and 'install' will install, in perfexecdir | 193 | # what 'all' will build and 'install' will install, in perfexecdir |
179 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 194 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
180 | 195 | ||
@@ -216,8 +231,16 @@ LIB_H += ../../include/uapi/linux/const.h | |||
216 | LIB_H += ../include/linux/hash.h | 231 | LIB_H += ../include/linux/hash.h |
217 | LIB_H += ../../include/linux/stringify.h | 232 | LIB_H += ../../include/linux/stringify.h |
218 | LIB_H += util/include/linux/bitmap.h | 233 | LIB_H += util/include/linux/bitmap.h |
219 | LIB_H += util/include/linux/bitops.h | 234 | LIB_H += ../include/linux/bitops.h |
235 | LIB_H += ../include/asm-generic/bitops/atomic.h | ||
236 | LIB_H += ../include/asm-generic/bitops/find.h | ||
237 | LIB_H += ../include/asm-generic/bitops/fls64.h | ||
238 | LIB_H += ../include/asm-generic/bitops/fls.h | ||
239 | LIB_H += ../include/asm-generic/bitops/__ffs.h | ||
240 | LIB_H += ../include/asm-generic/bitops/__fls.h | ||
241 | LIB_H += ../include/asm-generic/bitops.h | ||
220 | LIB_H += ../include/linux/compiler.h | 242 | LIB_H += ../include/linux/compiler.h |
243 | LIB_H += ../include/linux/log2.h | ||
221 | LIB_H += util/include/linux/const.h | 244 | LIB_H += util/include/linux/const.h |
222 | LIB_H += util/include/linux/ctype.h | 245 | LIB_H += util/include/linux/ctype.h |
223 | LIB_H += util/include/linux/kernel.h | 246 | LIB_H += util/include/linux/kernel.h |
@@ -246,12 +269,14 @@ LIB_H += util/annotate.h | |||
246 | LIB_H += util/cache.h | 269 | LIB_H += util/cache.h |
247 | LIB_H += util/callchain.h | 270 | LIB_H += util/callchain.h |
248 | LIB_H += util/build-id.h | 271 | LIB_H += util/build-id.h |
272 | LIB_H += util/db-export.h | ||
249 | LIB_H += util/debug.h | 273 | LIB_H += util/debug.h |
250 | LIB_H += util/pmu.h | 274 | LIB_H += util/pmu.h |
251 | LIB_H += util/event.h | 275 | LIB_H += util/event.h |
252 | LIB_H += util/evsel.h | 276 | LIB_H += util/evsel.h |
253 | LIB_H += util/evlist.h | 277 | LIB_H += util/evlist.h |
254 | LIB_H += util/exec_cmd.h | 278 | LIB_H += util/exec_cmd.h |
279 | LIB_H += util/find-vdso-map.c | ||
255 | LIB_H += util/levenshtein.h | 280 | LIB_H += util/levenshtein.h |
256 | LIB_H += util/machine.h | 281 | LIB_H += util/machine.h |
257 | LIB_H += util/map.h | 282 | LIB_H += util/map.h |
@@ -263,6 +288,7 @@ LIB_H += util/xyarray.h | |||
263 | LIB_H += util/header.h | 288 | LIB_H += util/header.h |
264 | LIB_H += util/help.h | 289 | LIB_H += util/help.h |
265 | LIB_H += util/session.h | 290 | LIB_H += util/session.h |
291 | LIB_H += util/ordered-events.h | ||
266 | LIB_H += util/strbuf.h | 292 | LIB_H += util/strbuf.h |
267 | LIB_H += util/strlist.h | 293 | LIB_H += util/strlist.h |
268 | LIB_H += util/strfilter.h | 294 | LIB_H += util/strfilter.h |
@@ -302,6 +328,7 @@ LIB_H += ui/util.h | |||
302 | LIB_H += ui/ui.h | 328 | LIB_H += ui/ui.h |
303 | LIB_H += util/data.h | 329 | LIB_H += util/data.h |
304 | LIB_H += util/kvm-stat.h | 330 | LIB_H += util/kvm-stat.h |
331 | LIB_H += util/thread-stack.h | ||
305 | 332 | ||
306 | LIB_OBJS += $(OUTPUT)util/abspath.o | 333 | LIB_OBJS += $(OUTPUT)util/abspath.o |
307 | LIB_OBJS += $(OUTPUT)util/alias.o | 334 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -309,12 +336,14 @@ LIB_OBJS += $(OUTPUT)util/annotate.o | |||
309 | LIB_OBJS += $(OUTPUT)util/build-id.o | 336 | LIB_OBJS += $(OUTPUT)util/build-id.o |
310 | LIB_OBJS += $(OUTPUT)util/config.o | 337 | LIB_OBJS += $(OUTPUT)util/config.o |
311 | LIB_OBJS += $(OUTPUT)util/ctype.o | 338 | LIB_OBJS += $(OUTPUT)util/ctype.o |
339 | LIB_OBJS += $(OUTPUT)util/db-export.o | ||
312 | LIB_OBJS += $(OUTPUT)util/pmu.o | 340 | LIB_OBJS += $(OUTPUT)util/pmu.o |
313 | LIB_OBJS += $(OUTPUT)util/environment.o | 341 | LIB_OBJS += $(OUTPUT)util/environment.o |
314 | LIB_OBJS += $(OUTPUT)util/event.o | 342 | LIB_OBJS += $(OUTPUT)util/event.o |
315 | LIB_OBJS += $(OUTPUT)util/evlist.o | 343 | LIB_OBJS += $(OUTPUT)util/evlist.o |
316 | LIB_OBJS += $(OUTPUT)util/evsel.o | 344 | LIB_OBJS += $(OUTPUT)util/evsel.o |
317 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 345 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
346 | LIB_OBJS += $(OUTPUT)util/find_next_bit.o | ||
318 | LIB_OBJS += $(OUTPUT)util/help.o | 347 | LIB_OBJS += $(OUTPUT)util/help.o |
319 | LIB_OBJS += $(OUTPUT)util/kallsyms.o | 348 | LIB_OBJS += $(OUTPUT)util/kallsyms.o |
320 | LIB_OBJS += $(OUTPUT)util/levenshtein.o | 349 | LIB_OBJS += $(OUTPUT)util/levenshtein.o |
@@ -347,6 +376,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o | |||
347 | LIB_OBJS += $(OUTPUT)util/map.o | 376 | LIB_OBJS += $(OUTPUT)util/map.o |
348 | LIB_OBJS += $(OUTPUT)util/pstack.o | 377 | LIB_OBJS += $(OUTPUT)util/pstack.o |
349 | LIB_OBJS += $(OUTPUT)util/session.o | 378 | LIB_OBJS += $(OUTPUT)util/session.o |
379 | LIB_OBJS += $(OUTPUT)util/ordered-events.o | ||
350 | LIB_OBJS += $(OUTPUT)util/comm.o | 380 | LIB_OBJS += $(OUTPUT)util/comm.o |
351 | LIB_OBJS += $(OUTPUT)util/thread.o | 381 | LIB_OBJS += $(OUTPUT)util/thread.o |
352 | LIB_OBJS += $(OUTPUT)util/thread_map.o | 382 | LIB_OBJS += $(OUTPUT)util/thread_map.o |
@@ -377,6 +407,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o | |||
377 | LIB_OBJS += $(OUTPUT)util/data.o | 407 | LIB_OBJS += $(OUTPUT)util/data.o |
378 | LIB_OBJS += $(OUTPUT)util/tsc.o | 408 | LIB_OBJS += $(OUTPUT)util/tsc.o |
379 | LIB_OBJS += $(OUTPUT)util/cloexec.o | 409 | LIB_OBJS += $(OUTPUT)util/cloexec.o |
410 | LIB_OBJS += $(OUTPUT)util/thread-stack.o | ||
380 | 411 | ||
381 | LIB_OBJS += $(OUTPUT)ui/setup.o | 412 | LIB_OBJS += $(OUTPUT)ui/setup.o |
382 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 413 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
@@ -399,6 +430,7 @@ LIB_OBJS += $(OUTPUT)tests/perf-record.o | |||
399 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o | 430 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o |
400 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | 431 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o |
401 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | 432 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o |
433 | LIB_OBJS += $(OUTPUT)tests/fdarray.o | ||
402 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 434 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
403 | LIB_OBJS += $(OUTPUT)tests/hists_common.o | 435 | LIB_OBJS += $(OUTPUT)tests/hists_common.o |
404 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | 436 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
@@ -423,6 +455,7 @@ endif | |||
423 | endif | 455 | endif |
424 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o | 456 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o |
425 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o | 457 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o |
458 | LIB_OBJS += $(OUTPUT)tests/switch-tracking.o | ||
426 | 459 | ||
427 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 460 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
428 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 461 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
@@ -434,7 +467,6 @@ BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o | |||
434 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o | 467 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o |
435 | endif | 468 | endif |
436 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o | 469 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o |
437 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o | ||
438 | BUILTIN_OBJS += $(OUTPUT)bench/futex-hash.o | 470 | BUILTIN_OBJS += $(OUTPUT)bench/futex-hash.o |
439 | BUILTIN_OBJS += $(OUTPUT)bench/futex-wake.o | 471 | BUILTIN_OBJS += $(OUTPUT)bench/futex-wake.o |
440 | BUILTIN_OBJS += $(OUTPUT)bench/futex-requeue.o | 472 | BUILTIN_OBJS += $(OUTPUT)bench/futex-requeue.o |
@@ -473,8 +505,6 @@ ifneq ($(OUTPUT),) | |||
473 | endif | 505 | endif |
474 | 506 | ||
475 | ifdef NO_LIBELF | 507 | ifdef NO_LIBELF |
476 | EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) | ||
477 | |||
478 | # Remove ELF/DWARF dependent codes | 508 | # Remove ELF/DWARF dependent codes |
479 | LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) | 509 | LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) |
480 | LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) | 510 | LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) |
@@ -563,6 +593,10 @@ ifndef NO_LIBNUMA | |||
563 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o | 593 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o |
564 | endif | 594 | endif |
565 | 595 | ||
596 | ifndef NO_ZLIB | ||
597 | LIB_OBJS += $(OUTPUT)util/zlib.o | ||
598 | endif | ||
599 | |||
566 | ifdef ASCIIDOC8 | 600 | ifdef ASCIIDOC8 |
567 | export ASCIIDOC8 | 601 | export ASCIIDOC8 |
568 | endif | 602 | endif |
@@ -709,6 +743,9 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c $(OUTPUT)PERF-CFLAGS | |||
709 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 743 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
710 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 744 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
711 | 745 | ||
746 | $(OUTPUT)util/find_next_bit.o: ../lib/util/find_next_bit.c $(OUTPUT)PERF-CFLAGS | ||
747 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | ||
748 | |||
712 | $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS | 749 | $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS |
713 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< | 750 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< |
714 | 751 | ||
@@ -727,6 +764,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti | |||
727 | $(OUTPUT)perf-%: %.o $(PERFLIBS) | 764 | $(OUTPUT)perf-%: %.o $(PERFLIBS) |
728 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) | 765 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) |
729 | 766 | ||
767 | ifndef NO_PERF_READ_VDSO32 | ||
768 | $(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c | ||
769 | $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c | ||
770 | endif | ||
771 | |||
772 | ifndef NO_PERF_READ_VDSOX32 | ||
773 | $(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c | ||
774 | $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c | ||
775 | endif | ||
776 | |||
730 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) | 777 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) |
731 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) | 778 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) |
732 | 779 | ||
@@ -765,7 +812,7 @@ $(LIBTRACEEVENT)-clean: | |||
765 | install-traceevent-plugins: $(LIBTRACEEVENT) | 812 | install-traceevent-plugins: $(LIBTRACEEVENT) |
766 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins | 813 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins |
767 | 814 | ||
768 | LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch]) | 815 | LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch]) |
769 | 816 | ||
770 | # if subdir is set, we've been called from above so target has been built | 817 | # if subdir is set, we've been called from above so target has been built |
771 | # already | 818 | # already |
@@ -871,10 +918,20 @@ install-bin: all install-gtk | |||
871 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ | 918 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ |
872 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ | 919 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ |
873 | $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' | 920 | $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' |
921 | ifndef NO_PERF_READ_VDSO32 | ||
922 | $(call QUIET_INSTALL, perf-read-vdso32) \ | ||
923 | $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)'; | ||
924 | endif | ||
925 | ifndef NO_PERF_READ_VDSOX32 | ||
926 | $(call QUIET_INSTALL, perf-read-vdsox32) \ | ||
927 | $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)'; | ||
928 | endif | ||
874 | $(call QUIET_INSTALL, libexec) \ | 929 | $(call QUIET_INSTALL, libexec) \ |
875 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 930 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
876 | $(call QUIET_INSTALL, perf-archive) \ | 931 | $(call QUIET_INSTALL, perf-archive) \ |
877 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 932 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
933 | $(call QUIET_INSTALL, perf-with-kcore) \ | ||
934 | $(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
878 | ifndef NO_LIBPERL | 935 | ifndef NO_LIBPERL |
879 | $(call QUIET_INSTALL, perl-scripts) \ | 936 | $(call QUIET_INSTALL, perl-scripts) \ |
880 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ | 937 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ |
@@ -920,8 +977,8 @@ config-clean: | |||
920 | @$(MAKE) -C config/feature-checks clean >/dev/null | 977 | @$(MAKE) -C config/feature-checks clean >/dev/null |
921 | 978 | ||
922 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean | 979 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean |
923 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) | 980 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) |
924 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf | 981 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 |
925 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* | 982 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* |
926 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 983 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
927 | $(python-clean) | 984 | $(python-clean) |
diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c index 9f870d27cb39..62eff847f91c 100644 --- a/tools/perf/arch/arm/tests/dwarf-unwind.c +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "thread.h" | 3 | #include "thread.h" |
4 | #include "map.h" | 4 | #include "map.h" |
5 | #include "event.h" | 5 | #include "event.h" |
6 | #include "debug.h" | ||
6 | #include "tests/tests.h" | 7 | #include "tests/tests.h" |
7 | 8 | ||
8 | #define STACK_SIZE 8192 | 9 | #define STACK_SIZE 8192 |
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c index 729ed69a6664..62c397ed3d97 100644 --- a/tools/perf/arch/arm/util/unwind-libunwind.c +++ b/tools/perf/arch/arm/util/unwind-libunwind.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <libunwind.h> | 3 | #include <libunwind.h> |
4 | #include "perf_regs.h" | 4 | #include "perf_regs.h" |
5 | #include "../../util/unwind.h" | 5 | #include "../../util/unwind.h" |
6 | #include "../../util/debug.h" | ||
6 | 7 | ||
7 | int libunwind__arch_reg_id(int regnum) | 8 | int libunwind__arch_reg_id(int regnum) |
8 | { | 9 | { |
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index e9441b9e2a30..1d3f39c3aa56 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h | |||
@@ -6,6 +6,8 @@ | |||
6 | #include <asm/perf_regs.h> | 6 | #include <asm/perf_regs.h> |
7 | 7 | ||
8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) | 8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) |
9 | #define PERF_REGS_MAX PERF_REG_ARM64_MAX | ||
10 | |||
9 | #define PERF_REG_IP PERF_REG_ARM64_PC | 11 | #define PERF_REG_IP PERF_REG_ARM64_PC |
10 | #define PERF_REG_SP PERF_REG_ARM64_SP | 12 | #define PERF_REG_SP PERF_REG_ARM64_SP |
11 | 13 | ||
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c index 436ee43859dc..a87afa91a99e 100644 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <libunwind.h> | 3 | #include <libunwind.h> |
4 | #include "perf_regs.h" | 4 | #include "perf_regs.h" |
5 | #include "../../util/unwind.h" | 5 | #include "../../util/unwind.h" |
6 | #include "../../util/debug.h" | ||
6 | 7 | ||
7 | int libunwind__arch_reg_id(int regnum) | 8 | int libunwind__arch_reg_id(int regnum) |
8 | { | 9 | { |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 42faf369211c..49776f190abf 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c | |||
@@ -12,6 +12,11 @@ const char *const arm_triplets[] = { | |||
12 | NULL | 12 | NULL |
13 | }; | 13 | }; |
14 | 14 | ||
15 | const char *const arm64_triplets[] = { | ||
16 | "aarch64-linux-android-", | ||
17 | NULL | ||
18 | }; | ||
19 | |||
15 | const char *const powerpc_triplets[] = { | 20 | const char *const powerpc_triplets[] = { |
16 | "powerpc-unknown-linux-gnu-", | 21 | "powerpc-unknown-linux-gnu-", |
17 | "powerpc64-unknown-linux-gnu-", | 22 | "powerpc64-unknown-linux-gnu-", |
@@ -105,6 +110,8 @@ static const char *normalize_arch(char *arch) | |||
105 | return "x86"; | 110 | return "x86"; |
106 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) | 111 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) |
107 | return "sparc"; | 112 | return "sparc"; |
113 | if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64")) | ||
114 | return "arm64"; | ||
108 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) | 115 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) |
109 | return "arm"; | 116 | return "arm"; |
110 | if (!strncmp(arch, "s390", 4)) | 117 | if (!strncmp(arch, "s390", 4)) |
@@ -159,6 +166,8 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | |||
159 | 166 | ||
160 | if (!strcmp(arch, "arm")) | 167 | if (!strcmp(arch, "arm")) |
161 | path_list = arm_triplets; | 168 | path_list = arm_triplets; |
169 | else if (!strcmp(arch, "arm64")) | ||
170 | path_list = arm64_triplets; | ||
162 | else if (!strcmp(arch, "powerpc")) | 171 | else if (!strcmp(arch, "powerpc")) |
163 | path_list = powerpc_triplets; | 172 | path_list = powerpc_triplets; |
164 | else if (!strcmp(arch, "sh")) | 173 | else if (!strcmp(arch, "sh")) |
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index b92219b1900d..6f7782bea5dd 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
4 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o | ||
4 | endif | 5 | endif |
5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | 6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o |
6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o | ||
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c index a7c23a4b3778..3bb50eac5542 100644 --- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c +++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c | |||
@@ -15,6 +15,7 @@ | |||
15 | 15 | ||
16 | #include "util/thread.h" | 16 | #include "util/thread.h" |
17 | #include "util/callchain.h" | 17 | #include "util/callchain.h" |
18 | #include "util/debug.h" | ||
18 | 19 | ||
19 | /* | 20 | /* |
20 | * When saving the callchain on Power, the kernel conservatively saves | 21 | * When saving the callchain on Power, the kernel conservatively saves |
@@ -144,7 +145,7 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc) | |||
144 | * yet used) | 145 | * yet used) |
145 | * -1 in case of errors | 146 | * -1 in case of errors |
146 | */ | 147 | */ |
147 | static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | 148 | static int check_return_addr(struct dso *dso, Dwarf_Addr pc) |
148 | { | 149 | { |
149 | int rc = -1; | 150 | int rc = -1; |
150 | Dwfl *dwfl; | 151 | Dwfl *dwfl; |
@@ -155,15 +156,27 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | |||
155 | Dwarf_Addr end = pc; | 156 | Dwarf_Addr end = pc; |
156 | bool signalp; | 157 | bool signalp; |
157 | 158 | ||
158 | dwfl = dwfl_begin(&offline_callbacks); | 159 | dwfl = dso->dwfl; |
159 | if (!dwfl) { | ||
160 | pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); | ||
161 | return -1; | ||
162 | } | ||
163 | 160 | ||
164 | if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) { | 161 | if (!dwfl) { |
165 | pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1)); | 162 | dwfl = dwfl_begin(&offline_callbacks); |
166 | goto out; | 163 | if (!dwfl) { |
164 | pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1)); | ||
165 | return -1; | ||
166 | } | ||
167 | |||
168 | if (dwfl_report_offline(dwfl, "", dso->long_name, -1) == NULL) { | ||
169 | pr_debug("dwfl_report_offline() failed %s\n", | ||
170 | dwarf_errmsg(-1)); | ||
171 | /* | ||
172 | * We normally cache the DWARF debug info and never | ||
173 | * call dwfl_end(). But to prevent fd leak, free in | ||
174 | * case of error. | ||
175 | */ | ||
176 | dwfl_end(dwfl); | ||
177 | goto out; | ||
178 | } | ||
179 | dso->dwfl = dwfl; | ||
167 | } | 180 | } |
168 | 181 | ||
169 | mod = dwfl_addrmodule(dwfl, pc); | 182 | mod = dwfl_addrmodule(dwfl, pc); |
@@ -193,7 +206,6 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc) | |||
193 | rc = check_return_reg(ra_regno, frame); | 206 | rc = check_return_reg(ra_regno, frame); |
194 | 207 | ||
195 | out: | 208 | out: |
196 | dwfl_end(dwfl); | ||
197 | return rc; | 209 | return rc; |
198 | } | 210 | } |
199 | 211 | ||
@@ -220,8 +232,7 @@ out: | |||
220 | * index: of callchain entry that needs to be ignored (if any) | 232 | * index: of callchain entry that needs to be ignored (if any) |
221 | * -1 if no entry needs to be ignored or in case of errors | 233 | * -1 if no entry needs to be ignored or in case of errors |
222 | */ | 234 | */ |
223 | int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | 235 | int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain) |
224 | struct ip_callchain *chain) | ||
225 | { | 236 | { |
226 | struct addr_location al; | 237 | struct addr_location al; |
227 | struct dso *dso = NULL; | 238 | struct dso *dso = NULL; |
@@ -234,7 +245,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | |||
234 | 245 | ||
235 | ip = chain->ips[2]; | 246 | ip = chain->ips[2]; |
236 | 247 | ||
237 | thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER, | 248 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, |
238 | MAP__FUNCTION, ip, &al); | 249 | MAP__FUNCTION, ip, &al); |
239 | 250 | ||
240 | if (al.map) | 251 | if (al.map) |
@@ -245,7 +256,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, | |||
245 | return skip_slot; | 256 | return skip_slot; |
246 | } | 257 | } |
247 | 258 | ||
248 | rc = check_return_addr(dso->long_name, ip); | 259 | rc = check_return_addr(dso, ip); |
249 | 260 | ||
250 | pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", | 261 | pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", |
251 | dso->long_name, chain->nr, ip, rc); | 262 | dso->long_name, chain->nr, ip, rc); |
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index a84206e9c4aa..fc9bebd2cca0 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c | |||
@@ -26,6 +26,7 @@ static unsigned int nsecs = 10; | |||
26 | /* amount of futexes per thread */ | 26 | /* amount of futexes per thread */ |
27 | static unsigned int nfutexes = 1024; | 27 | static unsigned int nfutexes = 1024; |
28 | static bool fshared = false, done = false, silent = false; | 28 | static bool fshared = false, done = false, silent = false; |
29 | static int futex_flag = 0; | ||
29 | 30 | ||
30 | struct timeval start, end, runtime; | 31 | struct timeval start, end, runtime; |
31 | static pthread_mutex_t thread_lock; | 32 | static pthread_mutex_t thread_lock; |
@@ -75,8 +76,7 @@ static void *workerfn(void *arg) | |||
75 | * such as internal waitqueue handling, thus enlarging | 76 | * such as internal waitqueue handling, thus enlarging |
76 | * the critical region protected by hb->lock. | 77 | * the critical region protected by hb->lock. |
77 | */ | 78 | */ |
78 | ret = futex_wait(&w->futex[i], 1234, NULL, | 79 | ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag); |
79 | fshared ? 0 : FUTEX_PRIVATE_FLAG); | ||
80 | if (!silent && | 80 | if (!silent && |
81 | (!ret || errno != EAGAIN || errno != EWOULDBLOCK)) | 81 | (!ret || errno != EAGAIN || errno != EWOULDBLOCK)) |
82 | warn("Non-expected futex return call"); | 82 | warn("Non-expected futex return call"); |
@@ -135,6 +135,9 @@ int bench_futex_hash(int argc, const char **argv, | |||
135 | if (!worker) | 135 | if (!worker) |
136 | goto errmem; | 136 | goto errmem; |
137 | 137 | ||
138 | if (!fshared) | ||
139 | futex_flag = FUTEX_PRIVATE_FLAG; | ||
140 | |||
138 | printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n", | 141 | printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n", |
139 | getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs); | 142 | getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs); |
140 | 143 | ||
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index 732403bfd31a..bedff6b5b3cf 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c | |||
@@ -30,16 +30,18 @@ static u_int32_t futex1 = 0, futex2 = 0; | |||
30 | static unsigned int nrequeue = 1; | 30 | static unsigned int nrequeue = 1; |
31 | 31 | ||
32 | static pthread_t *worker; | 32 | static pthread_t *worker; |
33 | static bool done = 0, silent = 0; | 33 | static bool done = false, silent = false, fshared = false; |
34 | static pthread_mutex_t thread_lock; | 34 | static pthread_mutex_t thread_lock; |
35 | static pthread_cond_t thread_parent, thread_worker; | 35 | static pthread_cond_t thread_parent, thread_worker; |
36 | static struct stats requeuetime_stats, requeued_stats; | 36 | static struct stats requeuetime_stats, requeued_stats; |
37 | static unsigned int ncpus, threads_starting, nthreads = 0; | 37 | static unsigned int ncpus, threads_starting, nthreads = 0; |
38 | static int futex_flag = 0; | ||
38 | 39 | ||
39 | static const struct option options[] = { | 40 | static const struct option options[] = { |
40 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), | 41 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), |
41 | OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), | 42 | OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), |
42 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), | 43 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), |
44 | OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), | ||
43 | OPT_END() | 45 | OPT_END() |
44 | }; | 46 | }; |
45 | 47 | ||
@@ -70,7 +72,7 @@ static void *workerfn(void *arg __maybe_unused) | |||
70 | pthread_cond_wait(&thread_worker, &thread_lock); | 72 | pthread_cond_wait(&thread_worker, &thread_lock); |
71 | pthread_mutex_unlock(&thread_lock); | 73 | pthread_mutex_unlock(&thread_lock); |
72 | 74 | ||
73 | futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG); | 75 | futex_wait(&futex1, 0, NULL, futex_flag); |
74 | return NULL; | 76 | return NULL; |
75 | } | 77 | } |
76 | 78 | ||
@@ -127,9 +129,12 @@ int bench_futex_requeue(int argc, const char **argv, | |||
127 | if (!worker) | 129 | if (!worker) |
128 | err(EXIT_FAILURE, "calloc"); | 130 | err(EXIT_FAILURE, "calloc"); |
129 | 131 | ||
130 | printf("Run summary [PID %d]: Requeuing %d threads (from %p to %p), " | 132 | if (!fshared) |
131 | "%d at a time.\n\n", | 133 | futex_flag = FUTEX_PRIVATE_FLAG; |
132 | getpid(), nthreads, &futex1, &futex2, nrequeue); | 134 | |
135 | printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), " | ||
136 | "%d at a time.\n\n", getpid(), nthreads, | ||
137 | fshared ? "shared":"private", &futex1, &futex2, nrequeue); | ||
133 | 138 | ||
134 | init_stats(&requeued_stats); | 139 | init_stats(&requeued_stats); |
135 | init_stats(&requeuetime_stats); | 140 | init_stats(&requeuetime_stats); |
@@ -156,16 +161,20 @@ int bench_futex_requeue(int argc, const char **argv, | |||
156 | 161 | ||
157 | /* Ok, all threads are patiently blocked, start requeueing */ | 162 | /* Ok, all threads are patiently blocked, start requeueing */ |
158 | gettimeofday(&start, NULL); | 163 | gettimeofday(&start, NULL); |
159 | for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) | 164 | for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) { |
160 | /* | 165 | /* |
161 | * Do not wakeup any tasks blocked on futex1, allowing | 166 | * Do not wakeup any tasks blocked on futex1, allowing |
162 | * us to really measure futex_wait functionality. | 167 | * us to really measure futex_wait functionality. |
163 | */ | 168 | */ |
164 | futex_cmp_requeue(&futex1, 0, &futex2, 0, nrequeue, | 169 | futex_cmp_requeue(&futex1, 0, &futex2, 0, |
165 | FUTEX_PRIVATE_FLAG); | 170 | nrequeue, futex_flag); |
171 | } | ||
166 | gettimeofday(&end, NULL); | 172 | gettimeofday(&end, NULL); |
167 | timersub(&end, &start, &runtime); | 173 | timersub(&end, &start, &runtime); |
168 | 174 | ||
175 | if (nrequeued > nthreads) | ||
176 | nrequeued = nthreads; | ||
177 | |||
169 | update_stats(&requeued_stats, nrequeued); | 178 | update_stats(&requeued_stats, nrequeued); |
170 | update_stats(&requeuetime_stats, runtime.tv_usec); | 179 | update_stats(&requeuetime_stats, runtime.tv_usec); |
171 | 180 | ||
@@ -175,7 +184,7 @@ int bench_futex_requeue(int argc, const char **argv, | |||
175 | } | 184 | } |
176 | 185 | ||
177 | /* everybody should be blocked on futex2, wake'em up */ | 186 | /* everybody should be blocked on futex2, wake'em up */ |
178 | nrequeued = futex_wake(&futex2, nthreads, FUTEX_PRIVATE_FLAG); | 187 | nrequeued = futex_wake(&futex2, nthreads, futex_flag); |
179 | if (nthreads != nrequeued) | 188 | if (nthreads != nrequeued) |
180 | warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads); | 189 | warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads); |
181 | 190 | ||
@@ -184,7 +193,6 @@ int bench_futex_requeue(int argc, const char **argv, | |||
184 | if (ret) | 193 | if (ret) |
185 | err(EXIT_FAILURE, "pthread_join"); | 194 | err(EXIT_FAILURE, "pthread_join"); |
186 | } | 195 | } |
187 | |||
188 | } | 196 | } |
189 | 197 | ||
190 | /* cleanup & report results */ | 198 | /* cleanup & report results */ |
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index 50022cbce87e..929f762be47e 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c | |||
@@ -31,16 +31,18 @@ static u_int32_t futex1 = 0; | |||
31 | static unsigned int nwakes = 1; | 31 | static unsigned int nwakes = 1; |
32 | 32 | ||
33 | pthread_t *worker; | 33 | pthread_t *worker; |
34 | static bool done = false, silent = false; | 34 | static bool done = false, silent = false, fshared = false; |
35 | static pthread_mutex_t thread_lock; | 35 | static pthread_mutex_t thread_lock; |
36 | static pthread_cond_t thread_parent, thread_worker; | 36 | static pthread_cond_t thread_parent, thread_worker; |
37 | static struct stats waketime_stats, wakeup_stats; | 37 | static struct stats waketime_stats, wakeup_stats; |
38 | static unsigned int ncpus, threads_starting, nthreads = 0; | 38 | static unsigned int ncpus, threads_starting, nthreads = 0; |
39 | static int futex_flag = 0; | ||
39 | 40 | ||
40 | static const struct option options[] = { | 41 | static const struct option options[] = { |
41 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), | 42 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), |
42 | OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), | 43 | OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), |
43 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), | 44 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), |
45 | OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), | ||
44 | OPT_END() | 46 | OPT_END() |
45 | }; | 47 | }; |
46 | 48 | ||
@@ -58,7 +60,7 @@ static void *workerfn(void *arg __maybe_unused) | |||
58 | pthread_cond_wait(&thread_worker, &thread_lock); | 60 | pthread_cond_wait(&thread_worker, &thread_lock); |
59 | pthread_mutex_unlock(&thread_lock); | 61 | pthread_mutex_unlock(&thread_lock); |
60 | 62 | ||
61 | futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG); | 63 | futex_wait(&futex1, 0, NULL, futex_flag); |
62 | return NULL; | 64 | return NULL; |
63 | } | 65 | } |
64 | 66 | ||
@@ -130,9 +132,12 @@ int bench_futex_wake(int argc, const char **argv, | |||
130 | if (!worker) | 132 | if (!worker) |
131 | err(EXIT_FAILURE, "calloc"); | 133 | err(EXIT_FAILURE, "calloc"); |
132 | 134 | ||
133 | printf("Run summary [PID %d]: blocking on %d threads (at futex %p), " | 135 | if (!fshared) |
136 | futex_flag = FUTEX_PRIVATE_FLAG; | ||
137 | |||
138 | printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), " | ||
134 | "waking up %d at a time.\n\n", | 139 | "waking up %d at a time.\n\n", |
135 | getpid(), nthreads, &futex1, nwakes); | 140 | getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes); |
136 | 141 | ||
137 | init_stats(&wakeup_stats); | 142 | init_stats(&wakeup_stats); |
138 | init_stats(&waketime_stats); | 143 | init_stats(&waketime_stats); |
@@ -160,7 +165,7 @@ int bench_futex_wake(int argc, const char **argv, | |||
160 | /* Ok, all threads are patiently blocked, start waking folks up */ | 165 | /* Ok, all threads are patiently blocked, start waking folks up */ |
161 | gettimeofday(&start, NULL); | 166 | gettimeofday(&start, NULL); |
162 | while (nwoken != nthreads) | 167 | while (nwoken != nthreads) |
163 | nwoken += futex_wake(&futex1, nwakes, FUTEX_PRIVATE_FLAG); | 168 | nwoken += futex_wake(&futex1, nwakes, futex_flag); |
164 | gettimeofday(&end, NULL); | 169 | gettimeofday(&end, NULL); |
165 | timersub(&end, &start, &runtime); | 170 | timersub(&end, &start, &runtime); |
166 | 171 | ||
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 2465141b554b..6c14afe8c1b1 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "../util/cloexec.h" | 13 | #include "../util/cloexec.h" |
14 | #include "bench.h" | 14 | #include "bench.h" |
15 | #include "mem-memcpy-arch.h" | 15 | #include "mem-memcpy-arch.h" |
16 | #include "mem-memset-arch.h" | ||
16 | 17 | ||
17 | #include <stdio.h> | 18 | #include <stdio.h> |
18 | #include <stdlib.h> | 19 | #include <stdlib.h> |
@@ -48,20 +49,24 @@ static const struct option options[] = { | |||
48 | }; | 49 | }; |
49 | 50 | ||
50 | typedef void *(*memcpy_t)(void *, const void *, size_t); | 51 | typedef void *(*memcpy_t)(void *, const void *, size_t); |
52 | typedef void *(*memset_t)(void *, int, size_t); | ||
51 | 53 | ||
52 | struct routine { | 54 | struct routine { |
53 | const char *name; | 55 | const char *name; |
54 | const char *desc; | 56 | const char *desc; |
55 | memcpy_t fn; | 57 | union { |
58 | memcpy_t memcpy; | ||
59 | memset_t memset; | ||
60 | } fn; | ||
56 | }; | 61 | }; |
57 | 62 | ||
58 | struct routine routines[] = { | 63 | struct routine memcpy_routines[] = { |
59 | { "default", | 64 | { .name = "default", |
60 | "Default memcpy() provided by glibc", | 65 | .desc = "Default memcpy() provided by glibc", |
61 | memcpy }, | 66 | .fn.memcpy = memcpy }, |
62 | #ifdef HAVE_ARCH_X86_64_SUPPORT | 67 | #ifdef HAVE_ARCH_X86_64_SUPPORT |
63 | 68 | ||
64 | #define MEMCPY_FN(fn, name, desc) { name, desc, fn }, | 69 | #define MEMCPY_FN(_fn, _name, _desc) {.name = _name, .desc = _desc, .fn.memcpy = _fn}, |
65 | #include "mem-memcpy-x86-64-asm-def.h" | 70 | #include "mem-memcpy-x86-64-asm-def.h" |
66 | #undef MEMCPY_FN | 71 | #undef MEMCPY_FN |
67 | 72 | ||
@@ -69,7 +74,7 @@ struct routine routines[] = { | |||
69 | 74 | ||
70 | { NULL, | 75 | { NULL, |
71 | NULL, | 76 | NULL, |
72 | NULL } | 77 | {NULL} } |
73 | }; | 78 | }; |
74 | 79 | ||
75 | static const char * const bench_mem_memcpy_usage[] = { | 80 | static const char * const bench_mem_memcpy_usage[] = { |
@@ -110,63 +115,6 @@ static double timeval2double(struct timeval *ts) | |||
110 | (double)ts->tv_usec / (double)1000000; | 115 | (double)ts->tv_usec / (double)1000000; |
111 | } | 116 | } |
112 | 117 | ||
113 | static void alloc_mem(void **dst, void **src, size_t length) | ||
114 | { | ||
115 | *dst = zalloc(length); | ||
116 | if (!*dst) | ||
117 | die("memory allocation failed - maybe length is too large?\n"); | ||
118 | |||
119 | *src = zalloc(length); | ||
120 | if (!*src) | ||
121 | die("memory allocation failed - maybe length is too large?\n"); | ||
122 | /* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */ | ||
123 | memset(*src, 0, length); | ||
124 | } | ||
125 | |||
126 | static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault) | ||
127 | { | ||
128 | u64 cycle_start = 0ULL, cycle_end = 0ULL; | ||
129 | void *src = NULL, *dst = NULL; | ||
130 | int i; | ||
131 | |||
132 | alloc_mem(&src, &dst, len); | ||
133 | |||
134 | if (prefault) | ||
135 | fn(dst, src, len); | ||
136 | |||
137 | cycle_start = get_cycle(); | ||
138 | for (i = 0; i < iterations; ++i) | ||
139 | fn(dst, src, len); | ||
140 | cycle_end = get_cycle(); | ||
141 | |||
142 | free(src); | ||
143 | free(dst); | ||
144 | return cycle_end - cycle_start; | ||
145 | } | ||
146 | |||
147 | static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) | ||
148 | { | ||
149 | struct timeval tv_start, tv_end, tv_diff; | ||
150 | void *src = NULL, *dst = NULL; | ||
151 | int i; | ||
152 | |||
153 | alloc_mem(&src, &dst, len); | ||
154 | |||
155 | if (prefault) | ||
156 | fn(dst, src, len); | ||
157 | |||
158 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
159 | for (i = 0; i < iterations; ++i) | ||
160 | fn(dst, src, len); | ||
161 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
162 | |||
163 | timersub(&tv_end, &tv_start, &tv_diff); | ||
164 | |||
165 | free(src); | ||
166 | free(dst); | ||
167 | return (double)((double)len / timeval2double(&tv_diff)); | ||
168 | } | ||
169 | |||
170 | #define pf (no_prefault ? 0 : 1) | 118 | #define pf (no_prefault ? 0 : 1) |
171 | 119 | ||
172 | #define print_bps(x) do { \ | 120 | #define print_bps(x) do { \ |
@@ -180,16 +128,25 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) | |||
180 | printf(" %14lf GB/Sec", x / K / K / K); \ | 128 | printf(" %14lf GB/Sec", x / K / K / K); \ |
181 | } while (0) | 129 | } while (0) |
182 | 130 | ||
183 | int bench_mem_memcpy(int argc, const char **argv, | 131 | struct bench_mem_info { |
184 | const char *prefix __maybe_unused) | 132 | const struct routine *routines; |
133 | u64 (*do_cycle)(const struct routine *r, size_t len, bool prefault); | ||
134 | double (*do_gettimeofday)(const struct routine *r, size_t len, bool prefault); | ||
135 | const char *const *usage; | ||
136 | }; | ||
137 | |||
138 | static int bench_mem_common(int argc, const char **argv, | ||
139 | const char *prefix __maybe_unused, | ||
140 | struct bench_mem_info *info) | ||
185 | { | 141 | { |
186 | int i; | 142 | int i; |
187 | size_t len; | 143 | size_t len; |
144 | double totallen; | ||
188 | double result_bps[2]; | 145 | double result_bps[2]; |
189 | u64 result_cycle[2]; | 146 | u64 result_cycle[2]; |
190 | 147 | ||
191 | argc = parse_options(argc, argv, options, | 148 | argc = parse_options(argc, argv, options, |
192 | bench_mem_memcpy_usage, 0); | 149 | info->usage, 0); |
193 | 150 | ||
194 | if (no_prefault && only_prefault) { | 151 | if (no_prefault && only_prefault) { |
195 | fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n"); | 152 | fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n"); |
@@ -200,6 +157,7 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
200 | init_cycle(); | 157 | init_cycle(); |
201 | 158 | ||
202 | len = (size_t)perf_atoll((char *)length_str); | 159 | len = (size_t)perf_atoll((char *)length_str); |
160 | totallen = (double)len * iterations; | ||
203 | 161 | ||
204 | result_cycle[0] = result_cycle[1] = 0ULL; | 162 | result_cycle[0] = result_cycle[1] = 0ULL; |
205 | result_bps[0] = result_bps[1] = 0.0; | 163 | result_bps[0] = result_bps[1] = 0.0; |
@@ -213,16 +171,16 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
213 | if (only_prefault && no_prefault) | 171 | if (only_prefault && no_prefault) |
214 | only_prefault = no_prefault = false; | 172 | only_prefault = no_prefault = false; |
215 | 173 | ||
216 | for (i = 0; routines[i].name; i++) { | 174 | for (i = 0; info->routines[i].name; i++) { |
217 | if (!strcmp(routines[i].name, routine)) | 175 | if (!strcmp(info->routines[i].name, routine)) |
218 | break; | 176 | break; |
219 | } | 177 | } |
220 | if (!routines[i].name) { | 178 | if (!info->routines[i].name) { |
221 | printf("Unknown routine:%s\n", routine); | 179 | printf("Unknown routine:%s\n", routine); |
222 | printf("Available routines...\n"); | 180 | printf("Available routines...\n"); |
223 | for (i = 0; routines[i].name; i++) { | 181 | for (i = 0; info->routines[i].name; i++) { |
224 | printf("\t%s ... %s\n", | 182 | printf("\t%s ... %s\n", |
225 | routines[i].name, routines[i].desc); | 183 | info->routines[i].name, info->routines[i].desc); |
226 | } | 184 | } |
227 | return 1; | 185 | return 1; |
228 | } | 186 | } |
@@ -234,25 +192,25 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
234 | /* show both of results */ | 192 | /* show both of results */ |
235 | if (use_cycle) { | 193 | if (use_cycle) { |
236 | result_cycle[0] = | 194 | result_cycle[0] = |
237 | do_memcpy_cycle(routines[i].fn, len, false); | 195 | info->do_cycle(&info->routines[i], len, false); |
238 | result_cycle[1] = | 196 | result_cycle[1] = |
239 | do_memcpy_cycle(routines[i].fn, len, true); | 197 | info->do_cycle(&info->routines[i], len, true); |
240 | } else { | 198 | } else { |
241 | result_bps[0] = | 199 | result_bps[0] = |
242 | do_memcpy_gettimeofday(routines[i].fn, | 200 | info->do_gettimeofday(&info->routines[i], |
243 | len, false); | 201 | len, false); |
244 | result_bps[1] = | 202 | result_bps[1] = |
245 | do_memcpy_gettimeofday(routines[i].fn, | 203 | info->do_gettimeofday(&info->routines[i], |
246 | len, true); | 204 | len, true); |
247 | } | 205 | } |
248 | } else { | 206 | } else { |
249 | if (use_cycle) { | 207 | if (use_cycle) { |
250 | result_cycle[pf] = | 208 | result_cycle[pf] = |
251 | do_memcpy_cycle(routines[i].fn, | 209 | info->do_cycle(&info->routines[i], |
252 | len, only_prefault); | 210 | len, only_prefault); |
253 | } else { | 211 | } else { |
254 | result_bps[pf] = | 212 | result_bps[pf] = |
255 | do_memcpy_gettimeofday(routines[i].fn, | 213 | info->do_gettimeofday(&info->routines[i], |
256 | len, only_prefault); | 214 | len, only_prefault); |
257 | } | 215 | } |
258 | } | 216 | } |
@@ -263,10 +221,10 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
263 | if (use_cycle) { | 221 | if (use_cycle) { |
264 | printf(" %14lf Cycle/Byte\n", | 222 | printf(" %14lf Cycle/Byte\n", |
265 | (double)result_cycle[0] | 223 | (double)result_cycle[0] |
266 | / (double)len); | 224 | / totallen); |
267 | printf(" %14lf Cycle/Byte (with prefault)\n", | 225 | printf(" %14lf Cycle/Byte (with prefault)\n", |
268 | (double)result_cycle[1] | 226 | (double)result_cycle[1] |
269 | / (double)len); | 227 | / totallen); |
270 | } else { | 228 | } else { |
271 | print_bps(result_bps[0]); | 229 | print_bps(result_bps[0]); |
272 | printf("\n"); | 230 | printf("\n"); |
@@ -277,7 +235,7 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
277 | if (use_cycle) { | 235 | if (use_cycle) { |
278 | printf(" %14lf Cycle/Byte", | 236 | printf(" %14lf Cycle/Byte", |
279 | (double)result_cycle[pf] | 237 | (double)result_cycle[pf] |
280 | / (double)len); | 238 | / totallen); |
281 | } else | 239 | } else |
282 | print_bps(result_bps[pf]); | 240 | print_bps(result_bps[pf]); |
283 | 241 | ||
@@ -288,8 +246,8 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
288 | if (!only_prefault && !no_prefault) { | 246 | if (!only_prefault && !no_prefault) { |
289 | if (use_cycle) { | 247 | if (use_cycle) { |
290 | printf("%lf %lf\n", | 248 | printf("%lf %lf\n", |
291 | (double)result_cycle[0] / (double)len, | 249 | (double)result_cycle[0] / totallen, |
292 | (double)result_cycle[1] / (double)len); | 250 | (double)result_cycle[1] / totallen); |
293 | } else { | 251 | } else { |
294 | printf("%lf %lf\n", | 252 | printf("%lf %lf\n", |
295 | result_bps[0], result_bps[1]); | 253 | result_bps[0], result_bps[1]); |
@@ -297,7 +255,7 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
297 | } else { | 255 | } else { |
298 | if (use_cycle) { | 256 | if (use_cycle) { |
299 | printf("%lf\n", (double)result_cycle[pf] | 257 | printf("%lf\n", (double)result_cycle[pf] |
300 | / (double)len); | 258 | / totallen); |
301 | } else | 259 | } else |
302 | printf("%lf\n", result_bps[pf]); | 260 | printf("%lf\n", result_bps[pf]); |
303 | } | 261 | } |
@@ -310,3 +268,163 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
310 | 268 | ||
311 | return 0; | 269 | return 0; |
312 | } | 270 | } |
271 | |||
272 | static void memcpy_alloc_mem(void **dst, void **src, size_t length) | ||
273 | { | ||
274 | *dst = zalloc(length); | ||
275 | if (!*dst) | ||
276 | die("memory allocation failed - maybe length is too large?\n"); | ||
277 | |||
278 | *src = zalloc(length); | ||
279 | if (!*src) | ||
280 | die("memory allocation failed - maybe length is too large?\n"); | ||
281 | /* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */ | ||
282 | memset(*src, 0, length); | ||
283 | } | ||
284 | |||
285 | static u64 do_memcpy_cycle(const struct routine *r, size_t len, bool prefault) | ||
286 | { | ||
287 | u64 cycle_start = 0ULL, cycle_end = 0ULL; | ||
288 | void *src = NULL, *dst = NULL; | ||
289 | memcpy_t fn = r->fn.memcpy; | ||
290 | int i; | ||
291 | |||
292 | memcpy_alloc_mem(&src, &dst, len); | ||
293 | |||
294 | if (prefault) | ||
295 | fn(dst, src, len); | ||
296 | |||
297 | cycle_start = get_cycle(); | ||
298 | for (i = 0; i < iterations; ++i) | ||
299 | fn(dst, src, len); | ||
300 | cycle_end = get_cycle(); | ||
301 | |||
302 | free(src); | ||
303 | free(dst); | ||
304 | return cycle_end - cycle_start; | ||
305 | } | ||
306 | |||
307 | static double do_memcpy_gettimeofday(const struct routine *r, size_t len, | ||
308 | bool prefault) | ||
309 | { | ||
310 | struct timeval tv_start, tv_end, tv_diff; | ||
311 | memcpy_t fn = r->fn.memcpy; | ||
312 | void *src = NULL, *dst = NULL; | ||
313 | int i; | ||
314 | |||
315 | memcpy_alloc_mem(&src, &dst, len); | ||
316 | |||
317 | if (prefault) | ||
318 | fn(dst, src, len); | ||
319 | |||
320 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
321 | for (i = 0; i < iterations; ++i) | ||
322 | fn(dst, src, len); | ||
323 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
324 | |||
325 | timersub(&tv_end, &tv_start, &tv_diff); | ||
326 | |||
327 | free(src); | ||
328 | free(dst); | ||
329 | return (double)(((double)len * iterations) / timeval2double(&tv_diff)); | ||
330 | } | ||
331 | |||
332 | int bench_mem_memcpy(int argc, const char **argv, | ||
333 | const char *prefix __maybe_unused) | ||
334 | { | ||
335 | struct bench_mem_info info = { | ||
336 | .routines = memcpy_routines, | ||
337 | .do_cycle = do_memcpy_cycle, | ||
338 | .do_gettimeofday = do_memcpy_gettimeofday, | ||
339 | .usage = bench_mem_memcpy_usage, | ||
340 | }; | ||
341 | |||
342 | return bench_mem_common(argc, argv, prefix, &info); | ||
343 | } | ||
344 | |||
345 | static void memset_alloc_mem(void **dst, size_t length) | ||
346 | { | ||
347 | *dst = zalloc(length); | ||
348 | if (!*dst) | ||
349 | die("memory allocation failed - maybe length is too large?\n"); | ||
350 | } | ||
351 | |||
352 | static u64 do_memset_cycle(const struct routine *r, size_t len, bool prefault) | ||
353 | { | ||
354 | u64 cycle_start = 0ULL, cycle_end = 0ULL; | ||
355 | memset_t fn = r->fn.memset; | ||
356 | void *dst = NULL; | ||
357 | int i; | ||
358 | |||
359 | memset_alloc_mem(&dst, len); | ||
360 | |||
361 | if (prefault) | ||
362 | fn(dst, -1, len); | ||
363 | |||
364 | cycle_start = get_cycle(); | ||
365 | for (i = 0; i < iterations; ++i) | ||
366 | fn(dst, i, len); | ||
367 | cycle_end = get_cycle(); | ||
368 | |||
369 | free(dst); | ||
370 | return cycle_end - cycle_start; | ||
371 | } | ||
372 | |||
373 | static double do_memset_gettimeofday(const struct routine *r, size_t len, | ||
374 | bool prefault) | ||
375 | { | ||
376 | struct timeval tv_start, tv_end, tv_diff; | ||
377 | memset_t fn = r->fn.memset; | ||
378 | void *dst = NULL; | ||
379 | int i; | ||
380 | |||
381 | memset_alloc_mem(&dst, len); | ||
382 | |||
383 | if (prefault) | ||
384 | fn(dst, -1, len); | ||
385 | |||
386 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
387 | for (i = 0; i < iterations; ++i) | ||
388 | fn(dst, i, len); | ||
389 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
390 | |||
391 | timersub(&tv_end, &tv_start, &tv_diff); | ||
392 | |||
393 | free(dst); | ||
394 | return (double)(((double)len * iterations) / timeval2double(&tv_diff)); | ||
395 | } | ||
396 | |||
397 | static const char * const bench_mem_memset_usage[] = { | ||
398 | "perf bench mem memset <options>", | ||
399 | NULL | ||
400 | }; | ||
401 | |||
402 | static const struct routine memset_routines[] = { | ||
403 | { .name ="default", | ||
404 | .desc = "Default memset() provided by glibc", | ||
405 | .fn.memset = memset }, | ||
406 | #ifdef HAVE_ARCH_X86_64_SUPPORT | ||
407 | |||
408 | #define MEMSET_FN(_fn, _name, _desc) { .name = _name, .desc = _desc, .fn.memset = _fn }, | ||
409 | #include "mem-memset-x86-64-asm-def.h" | ||
410 | #undef MEMSET_FN | ||
411 | |||
412 | #endif | ||
413 | |||
414 | { .name = NULL, | ||
415 | .desc = NULL, | ||
416 | .fn.memset = NULL } | ||
417 | }; | ||
418 | |||
419 | int bench_mem_memset(int argc, const char **argv, | ||
420 | const char *prefix __maybe_unused) | ||
421 | { | ||
422 | struct bench_mem_info info = { | ||
423 | .routines = memset_routines, | ||
424 | .do_cycle = do_memset_cycle, | ||
425 | .do_gettimeofday = do_memset_gettimeofday, | ||
426 | .usage = bench_mem_memset_usage, | ||
427 | }; | ||
428 | |||
429 | return bench_mem_common(argc, argv, prefix, &info); | ||
430 | } | ||
diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c deleted file mode 100644 index 75fc3e65fb2a..000000000000 --- a/tools/perf/bench/mem-memset.c +++ /dev/null | |||
@@ -1,304 +0,0 @@ | |||
1 | /* | ||
2 | * mem-memset.c | ||
3 | * | ||
4 | * memset: Simple memory set in various ways | ||
5 | * | ||
6 | * Trivial clone of mem-memcpy.c. | ||
7 | */ | ||
8 | |||
9 | #include "../perf.h" | ||
10 | #include "../util/util.h" | ||
11 | #include "../util/parse-options.h" | ||
12 | #include "../util/header.h" | ||
13 | #include "../util/cloexec.h" | ||
14 | #include "bench.h" | ||
15 | #include "mem-memset-arch.h" | ||
16 | |||
17 | #include <stdio.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <sys/time.h> | ||
21 | #include <errno.h> | ||
22 | |||
23 | #define K 1024 | ||
24 | |||
25 | static const char *length_str = "1MB"; | ||
26 | static const char *routine = "default"; | ||
27 | static int iterations = 1; | ||
28 | static bool use_cycle; | ||
29 | static int cycle_fd; | ||
30 | static bool only_prefault; | ||
31 | static bool no_prefault; | ||
32 | |||
33 | static const struct option options[] = { | ||
34 | OPT_STRING('l', "length", &length_str, "1MB", | ||
35 | "Specify length of memory to set. " | ||
36 | "Available units: B, KB, MB, GB and TB (upper and lower)"), | ||
37 | OPT_STRING('r', "routine", &routine, "default", | ||
38 | "Specify routine to set"), | ||
39 | OPT_INTEGER('i', "iterations", &iterations, | ||
40 | "repeat memset() invocation this number of times"), | ||
41 | OPT_BOOLEAN('c', "cycle", &use_cycle, | ||
42 | "Use cycles event instead of gettimeofday() for measuring"), | ||
43 | OPT_BOOLEAN('o', "only-prefault", &only_prefault, | ||
44 | "Show only the result with page faults before memset()"), | ||
45 | OPT_BOOLEAN('n', "no-prefault", &no_prefault, | ||
46 | "Show only the result without page faults before memset()"), | ||
47 | OPT_END() | ||
48 | }; | ||
49 | |||
50 | typedef void *(*memset_t)(void *, int, size_t); | ||
51 | |||
52 | struct routine { | ||
53 | const char *name; | ||
54 | const char *desc; | ||
55 | memset_t fn; | ||
56 | }; | ||
57 | |||
58 | static const struct routine routines[] = { | ||
59 | { "default", | ||
60 | "Default memset() provided by glibc", | ||
61 | memset }, | ||
62 | #ifdef HAVE_ARCH_X86_64_SUPPORT | ||
63 | |||
64 | #define MEMSET_FN(fn, name, desc) { name, desc, fn }, | ||
65 | #include "mem-memset-x86-64-asm-def.h" | ||
66 | #undef MEMSET_FN | ||
67 | |||
68 | #endif | ||
69 | |||
70 | { NULL, | ||
71 | NULL, | ||
72 | NULL } | ||
73 | }; | ||
74 | |||
75 | static const char * const bench_mem_memset_usage[] = { | ||
76 | "perf bench mem memset <options>", | ||
77 | NULL | ||
78 | }; | ||
79 | |||
80 | static struct perf_event_attr cycle_attr = { | ||
81 | .type = PERF_TYPE_HARDWARE, | ||
82 | .config = PERF_COUNT_HW_CPU_CYCLES | ||
83 | }; | ||
84 | |||
85 | static void init_cycle(void) | ||
86 | { | ||
87 | cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, | ||
88 | perf_event_open_cloexec_flag()); | ||
89 | |||
90 | if (cycle_fd < 0 && errno == ENOSYS) | ||
91 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
92 | else | ||
93 | BUG_ON(cycle_fd < 0); | ||
94 | } | ||
95 | |||
96 | static u64 get_cycle(void) | ||
97 | { | ||
98 | int ret; | ||
99 | u64 clk; | ||
100 | |||
101 | ret = read(cycle_fd, &clk, sizeof(u64)); | ||
102 | BUG_ON(ret != sizeof(u64)); | ||
103 | |||
104 | return clk; | ||
105 | } | ||
106 | |||
107 | static double timeval2double(struct timeval *ts) | ||
108 | { | ||
109 | return (double)ts->tv_sec + | ||
110 | (double)ts->tv_usec / (double)1000000; | ||
111 | } | ||
112 | |||
113 | static void alloc_mem(void **dst, size_t length) | ||
114 | { | ||
115 | *dst = zalloc(length); | ||
116 | if (!*dst) | ||
117 | die("memory allocation failed - maybe length is too large?\n"); | ||
118 | } | ||
119 | |||
120 | static u64 do_memset_cycle(memset_t fn, size_t len, bool prefault) | ||
121 | { | ||
122 | u64 cycle_start = 0ULL, cycle_end = 0ULL; | ||
123 | void *dst = NULL; | ||
124 | int i; | ||
125 | |||
126 | alloc_mem(&dst, len); | ||
127 | |||
128 | if (prefault) | ||
129 | fn(dst, -1, len); | ||
130 | |||
131 | cycle_start = get_cycle(); | ||
132 | for (i = 0; i < iterations; ++i) | ||
133 | fn(dst, i, len); | ||
134 | cycle_end = get_cycle(); | ||
135 | |||
136 | free(dst); | ||
137 | return cycle_end - cycle_start; | ||
138 | } | ||
139 | |||
140 | static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault) | ||
141 | { | ||
142 | struct timeval tv_start, tv_end, tv_diff; | ||
143 | void *dst = NULL; | ||
144 | int i; | ||
145 | |||
146 | alloc_mem(&dst, len); | ||
147 | |||
148 | if (prefault) | ||
149 | fn(dst, -1, len); | ||
150 | |||
151 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
152 | for (i = 0; i < iterations; ++i) | ||
153 | fn(dst, i, len); | ||
154 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
155 | |||
156 | timersub(&tv_end, &tv_start, &tv_diff); | ||
157 | |||
158 | free(dst); | ||
159 | return (double)((double)len / timeval2double(&tv_diff)); | ||
160 | } | ||
161 | |||
162 | #define pf (no_prefault ? 0 : 1) | ||
163 | |||
164 | #define print_bps(x) do { \ | ||
165 | if (x < K) \ | ||
166 | printf(" %14lf B/Sec", x); \ | ||
167 | else if (x < K * K) \ | ||
168 | printf(" %14lfd KB/Sec", x / K); \ | ||
169 | else if (x < K * K * K) \ | ||
170 | printf(" %14lf MB/Sec", x / K / K); \ | ||
171 | else \ | ||
172 | printf(" %14lf GB/Sec", x / K / K / K); \ | ||
173 | } while (0) | ||
174 | |||
175 | int bench_mem_memset(int argc, const char **argv, | ||
176 | const char *prefix __maybe_unused) | ||
177 | { | ||
178 | int i; | ||
179 | size_t len; | ||
180 | double result_bps[2]; | ||
181 | u64 result_cycle[2]; | ||
182 | |||
183 | argc = parse_options(argc, argv, options, | ||
184 | bench_mem_memset_usage, 0); | ||
185 | |||
186 | if (no_prefault && only_prefault) { | ||
187 | fprintf(stderr, "Invalid options: -o and -n are mutually exclusive\n"); | ||
188 | return 1; | ||
189 | } | ||
190 | |||
191 | if (use_cycle) | ||
192 | init_cycle(); | ||
193 | |||
194 | len = (size_t)perf_atoll((char *)length_str); | ||
195 | |||
196 | result_cycle[0] = result_cycle[1] = 0ULL; | ||
197 | result_bps[0] = result_bps[1] = 0.0; | ||
198 | |||
199 | if ((s64)len <= 0) { | ||
200 | fprintf(stderr, "Invalid length:%s\n", length_str); | ||
201 | return 1; | ||
202 | } | ||
203 | |||
204 | /* same to without specifying either of prefault and no-prefault */ | ||
205 | if (only_prefault && no_prefault) | ||
206 | only_prefault = no_prefault = false; | ||
207 | |||
208 | for (i = 0; routines[i].name; i++) { | ||
209 | if (!strcmp(routines[i].name, routine)) | ||
210 | break; | ||
211 | } | ||
212 | if (!routines[i].name) { | ||
213 | printf("Unknown routine:%s\n", routine); | ||
214 | printf("Available routines...\n"); | ||
215 | for (i = 0; routines[i].name; i++) { | ||
216 | printf("\t%s ... %s\n", | ||
217 | routines[i].name, routines[i].desc); | ||
218 | } | ||
219 | return 1; | ||
220 | } | ||
221 | |||
222 | if (bench_format == BENCH_FORMAT_DEFAULT) | ||
223 | printf("# Copying %s Bytes ...\n\n", length_str); | ||
224 | |||
225 | if (!only_prefault && !no_prefault) { | ||
226 | /* show both of results */ | ||
227 | if (use_cycle) { | ||
228 | result_cycle[0] = | ||
229 | do_memset_cycle(routines[i].fn, len, false); | ||
230 | result_cycle[1] = | ||
231 | do_memset_cycle(routines[i].fn, len, true); | ||
232 | } else { | ||
233 | result_bps[0] = | ||
234 | do_memset_gettimeofday(routines[i].fn, | ||
235 | len, false); | ||
236 | result_bps[1] = | ||
237 | do_memset_gettimeofday(routines[i].fn, | ||
238 | len, true); | ||
239 | } | ||
240 | } else { | ||
241 | if (use_cycle) { | ||
242 | result_cycle[pf] = | ||
243 | do_memset_cycle(routines[i].fn, | ||
244 | len, only_prefault); | ||
245 | } else { | ||
246 | result_bps[pf] = | ||
247 | do_memset_gettimeofday(routines[i].fn, | ||
248 | len, only_prefault); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | switch (bench_format) { | ||
253 | case BENCH_FORMAT_DEFAULT: | ||
254 | if (!only_prefault && !no_prefault) { | ||
255 | if (use_cycle) { | ||
256 | printf(" %14lf Cycle/Byte\n", | ||
257 | (double)result_cycle[0] | ||
258 | / (double)len); | ||
259 | printf(" %14lf Cycle/Byte (with prefault)\n ", | ||
260 | (double)result_cycle[1] | ||
261 | / (double)len); | ||
262 | } else { | ||
263 | print_bps(result_bps[0]); | ||
264 | printf("\n"); | ||
265 | print_bps(result_bps[1]); | ||
266 | printf(" (with prefault)\n"); | ||
267 | } | ||
268 | } else { | ||
269 | if (use_cycle) { | ||
270 | printf(" %14lf Cycle/Byte", | ||
271 | (double)result_cycle[pf] | ||
272 | / (double)len); | ||
273 | } else | ||
274 | print_bps(result_bps[pf]); | ||
275 | |||
276 | printf("%s\n", only_prefault ? " (with prefault)" : ""); | ||
277 | } | ||
278 | break; | ||
279 | case BENCH_FORMAT_SIMPLE: | ||
280 | if (!only_prefault && !no_prefault) { | ||
281 | if (use_cycle) { | ||
282 | printf("%lf %lf\n", | ||
283 | (double)result_cycle[0] / (double)len, | ||
284 | (double)result_cycle[1] / (double)len); | ||
285 | } else { | ||
286 | printf("%lf %lf\n", | ||
287 | result_bps[0], result_bps[1]); | ||
288 | } | ||
289 | } else { | ||
290 | if (use_cycle) { | ||
291 | printf("%lf\n", (double)result_cycle[pf] | ||
292 | / (double)len); | ||
293 | } else | ||
294 | printf("%lf\n", result_bps[pf]); | ||
295 | } | ||
296 | break; | ||
297 | default: | ||
298 | /* reaching this means there's some disaster: */ | ||
299 | die("unknown format: %d\n", bench_format); | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | return 0; | ||
304 | } | ||
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index 52a56599a543..d7f281c2828d 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include <sys/socket.h> | 26 | #include <sys/socket.h> |
27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
28 | #include <sys/time.h> | 28 | #include <sys/time.h> |
29 | #include <sys/poll.h> | 29 | #include <poll.h> |
30 | #include <limits.h> | 30 | #include <limits.h> |
31 | #include <err.h> | 31 | #include <err.h> |
32 | 32 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1ec429fef2be..747f86103599 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -36,7 +36,8 @@ | |||
36 | 36 | ||
37 | struct perf_annotate { | 37 | struct perf_annotate { |
38 | struct perf_tool tool; | 38 | struct perf_tool tool; |
39 | bool force, use_tui, use_stdio, use_gtk; | 39 | struct perf_session *session; |
40 | bool use_tui, use_stdio, use_gtk; | ||
40 | bool full_paths; | 41 | bool full_paths; |
41 | bool print_line; | 42 | bool print_line; |
42 | bool skip_missing; | 43 | bool skip_missing; |
@@ -50,6 +51,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
50 | struct addr_location *al, | 51 | struct addr_location *al, |
51 | struct perf_annotate *ann) | 52 | struct perf_annotate *ann) |
52 | { | 53 | { |
54 | struct hists *hists = evsel__hists(evsel); | ||
53 | struct hist_entry *he; | 55 | struct hist_entry *he; |
54 | int ret; | 56 | int ret; |
55 | 57 | ||
@@ -65,13 +67,12 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
65 | return 0; | 67 | return 0; |
66 | } | 68 | } |
67 | 69 | ||
68 | he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0, | 70 | he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true); |
69 | true); | ||
70 | if (he == NULL) | 71 | if (he == NULL) |
71 | return -ENOMEM; | 72 | return -ENOMEM; |
72 | 73 | ||
73 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 74 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
74 | hists__inc_nr_samples(&evsel->hists, true); | 75 | hists__inc_nr_samples(hists, true); |
75 | return ret; | 76 | return ret; |
76 | } | 77 | } |
77 | 78 | ||
@@ -188,18 +189,9 @@ find_next: | |||
188 | static int __cmd_annotate(struct perf_annotate *ann) | 189 | static int __cmd_annotate(struct perf_annotate *ann) |
189 | { | 190 | { |
190 | int ret; | 191 | int ret; |
191 | struct perf_session *session; | 192 | struct perf_session *session = ann->session; |
192 | struct perf_evsel *pos; | 193 | struct perf_evsel *pos; |
193 | u64 total_nr_samples; | 194 | u64 total_nr_samples; |
194 | struct perf_data_file file = { | ||
195 | .path = input_name, | ||
196 | .mode = PERF_DATA_MODE_READ, | ||
197 | .force = ann->force, | ||
198 | }; | ||
199 | |||
200 | session = perf_session__new(&file, false, &ann->tool); | ||
201 | if (session == NULL) | ||
202 | return -ENOMEM; | ||
203 | 195 | ||
204 | machines__set_symbol_filter(&session->machines, symbol__annotate_init); | 196 | machines__set_symbol_filter(&session->machines, symbol__annotate_init); |
205 | 197 | ||
@@ -207,22 +199,23 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
207 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, | 199 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, |
208 | ann->cpu_bitmap); | 200 | ann->cpu_bitmap); |
209 | if (ret) | 201 | if (ret) |
210 | goto out_delete; | 202 | goto out; |
211 | } | 203 | } |
212 | 204 | ||
213 | if (!objdump_path) { | 205 | if (!objdump_path) { |
214 | ret = perf_session_env__lookup_objdump(&session->header.env); | 206 | ret = perf_session_env__lookup_objdump(&session->header.env); |
215 | if (ret) | 207 | if (ret) |
216 | goto out_delete; | 208 | goto out; |
217 | } | 209 | } |
218 | 210 | ||
219 | ret = perf_session__process_events(session, &ann->tool); | 211 | ret = perf_session__process_events(session, &ann->tool); |
220 | if (ret) | 212 | if (ret) |
221 | goto out_delete; | 213 | goto out; |
222 | 214 | ||
223 | if (dump_trace) { | 215 | if (dump_trace) { |
224 | perf_session__fprintf_nr_events(session, stdout); | 216 | perf_session__fprintf_nr_events(session, stdout); |
225 | goto out_delete; | 217 | perf_evlist__fprintf_nr_events(session->evlist, stdout); |
218 | goto out; | ||
226 | } | 219 | } |
227 | 220 | ||
228 | if (verbose > 3) | 221 | if (verbose > 3) |
@@ -233,13 +226,13 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
233 | 226 | ||
234 | total_nr_samples = 0; | 227 | total_nr_samples = 0; |
235 | evlist__for_each(session->evlist, pos) { | 228 | evlist__for_each(session->evlist, pos) { |
236 | struct hists *hists = &pos->hists; | 229 | struct hists *hists = evsel__hists(pos); |
237 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 230 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
238 | 231 | ||
239 | if (nr_samples > 0) { | 232 | if (nr_samples > 0) { |
240 | total_nr_samples += nr_samples; | 233 | total_nr_samples += nr_samples; |
241 | hists__collapse_resort(hists, NULL); | 234 | hists__collapse_resort(hists, NULL); |
242 | hists__output_resort(hists); | 235 | hists__output_resort(hists, NULL); |
243 | 236 | ||
244 | if (symbol_conf.event_group && | 237 | if (symbol_conf.event_group && |
245 | !perf_evsel__is_group_leader(pos)) | 238 | !perf_evsel__is_group_leader(pos)) |
@@ -250,8 +243,8 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
250 | } | 243 | } |
251 | 244 | ||
252 | if (total_nr_samples == 0) { | 245 | if (total_nr_samples == 0) { |
253 | ui__error("The %s file has no samples!\n", file.path); | 246 | ui__error("The %s file has no samples!\n", session->file->path); |
254 | goto out_delete; | 247 | goto out; |
255 | } | 248 | } |
256 | 249 | ||
257 | if (use_browser == 2) { | 250 | if (use_browser == 2) { |
@@ -261,24 +254,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
261 | "perf_gtk__show_annotations"); | 254 | "perf_gtk__show_annotations"); |
262 | if (show_annotations == NULL) { | 255 | if (show_annotations == NULL) { |
263 | ui__error("GTK browser not found!\n"); | 256 | ui__error("GTK browser not found!\n"); |
264 | goto out_delete; | 257 | goto out; |
265 | } | 258 | } |
266 | show_annotations(); | 259 | show_annotations(); |
267 | } | 260 | } |
268 | 261 | ||
269 | out_delete: | 262 | out: |
270 | /* | ||
271 | * Speed up the exit process, for large files this can | ||
272 | * take quite a while. | ||
273 | * | ||
274 | * XXX Enable this when using valgrind or if we ever | ||
275 | * librarize this command. | ||
276 | * | ||
277 | * Also experiment with obstacks to see how much speed | ||
278 | * up we'll get here. | ||
279 | * | ||
280 | * perf_session__delete(session); | ||
281 | */ | ||
282 | return ret; | 263 | return ret; |
283 | } | 264 | } |
284 | 265 | ||
@@ -297,10 +278,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
297 | .comm = perf_event__process_comm, | 278 | .comm = perf_event__process_comm, |
298 | .exit = perf_event__process_exit, | 279 | .exit = perf_event__process_exit, |
299 | .fork = perf_event__process_fork, | 280 | .fork = perf_event__process_fork, |
300 | .ordered_samples = true, | 281 | .ordered_events = true, |
301 | .ordering_requires_timestamps = true, | 282 | .ordering_requires_timestamps = true, |
302 | }, | 283 | }, |
303 | }; | 284 | }; |
285 | struct perf_data_file file = { | ||
286 | .path = input_name, | ||
287 | .mode = PERF_DATA_MODE_READ, | ||
288 | }; | ||
304 | const struct option options[] = { | 289 | const struct option options[] = { |
305 | OPT_STRING('i', "input", &input_name, "file", | 290 | OPT_STRING('i', "input", &input_name, "file", |
306 | "input file name"), | 291 | "input file name"), |
@@ -308,7 +293,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
308 | "only consider symbols in these dsos"), | 293 | "only consider symbols in these dsos"), |
309 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", | 294 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", |
310 | "symbol to annotate"), | 295 | "symbol to annotate"), |
311 | OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"), | 296 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
312 | OPT_INCR('v', "verbose", &verbose, | 297 | OPT_INCR('v', "verbose", &verbose, |
313 | "be more verbose (show symbol address, etc)"), | 298 | "be more verbose (show symbol address, etc)"), |
314 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 299 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
@@ -341,6 +326,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
341 | "Show event group information together"), | 326 | "Show event group information together"), |
342 | OPT_END() | 327 | OPT_END() |
343 | }; | 328 | }; |
329 | int ret = hists__init(); | ||
330 | |||
331 | if (ret < 0) | ||
332 | return ret; | ||
344 | 333 | ||
345 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 334 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
346 | 335 | ||
@@ -353,11 +342,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
353 | 342 | ||
354 | setup_browser(true); | 343 | setup_browser(true); |
355 | 344 | ||
345 | annotate.session = perf_session__new(&file, false, &annotate.tool); | ||
346 | if (annotate.session == NULL) | ||
347 | return -1; | ||
348 | |||
356 | symbol_conf.priv_size = sizeof(struct annotation); | 349 | symbol_conf.priv_size = sizeof(struct annotation); |
357 | symbol_conf.try_vmlinux_path = true; | 350 | symbol_conf.try_vmlinux_path = true; |
358 | 351 | ||
359 | if (symbol__init() < 0) | 352 | ret = symbol__init(&annotate.session->header.env); |
360 | return -1; | 353 | if (ret < 0) |
354 | goto out_delete; | ||
361 | 355 | ||
362 | if (setup_sorting() < 0) | 356 | if (setup_sorting() < 0) |
363 | usage_with_options(annotate_usage, options); | 357 | usage_with_options(annotate_usage, options); |
@@ -373,5 +367,20 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
373 | annotate.sym_hist_filter = argv[0]; | 367 | annotate.sym_hist_filter = argv[0]; |
374 | } | 368 | } |
375 | 369 | ||
376 | return __cmd_annotate(&annotate); | 370 | ret = __cmd_annotate(&annotate); |
371 | |||
372 | out_delete: | ||
373 | /* | ||
374 | * Speed up the exit process, for large files this can | ||
375 | * take quite a while. | ||
376 | * | ||
377 | * XXX Enable this when using valgrind or if we ever | ||
378 | * librarize this command. | ||
379 | * | ||
380 | * Also experiment with obstacks to see how much speed | ||
381 | * up we'll get here. | ||
382 | * | ||
383 | * perf_session__delete(session); | ||
384 | */ | ||
385 | return ret; | ||
377 | } | 386 | } |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 2a2c78f80876..77d5cae54c6a 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -246,20 +246,9 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | |||
246 | return true; | 246 | return true; |
247 | } | 247 | } |
248 | 248 | ||
249 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) | 249 | static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp) |
250 | { | 250 | { |
251 | struct perf_data_file file = { | ||
252 | .path = filename, | ||
253 | .mode = PERF_DATA_MODE_READ, | ||
254 | .force = force, | ||
255 | }; | ||
256 | struct perf_session *session = perf_session__new(&file, false, NULL); | ||
257 | if (session == NULL) | ||
258 | return -1; | ||
259 | |||
260 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); | 251 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); |
261 | perf_session__delete(session); | ||
262 | |||
263 | return 0; | 252 | return 0; |
264 | } | 253 | } |
265 | 254 | ||
@@ -296,12 +285,17 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
296 | struct str_node *pos; | 285 | struct str_node *pos; |
297 | int ret = 0; | 286 | int ret = 0; |
298 | bool force = false; | 287 | bool force = false; |
299 | char debugdir[PATH_MAX]; | ||
300 | char const *add_name_list_str = NULL, | 288 | char const *add_name_list_str = NULL, |
301 | *remove_name_list_str = NULL, | 289 | *remove_name_list_str = NULL, |
302 | *missing_filename = NULL, | 290 | *missing_filename = NULL, |
303 | *update_name_list_str = NULL, | 291 | *update_name_list_str = NULL, |
304 | *kcore_filename; | 292 | *kcore_filename = NULL; |
293 | char sbuf[STRERR_BUFSIZE]; | ||
294 | |||
295 | struct perf_data_file file = { | ||
296 | .mode = PERF_DATA_MODE_READ, | ||
297 | }; | ||
298 | struct perf_session *session = NULL; | ||
305 | 299 | ||
306 | const struct option buildid_cache_options[] = { | 300 | const struct option buildid_cache_options[] = { |
307 | OPT_STRING('a', "add", &add_name_list_str, | 301 | OPT_STRING('a', "add", &add_name_list_str, |
@@ -326,25 +320,32 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
326 | argc = parse_options(argc, argv, buildid_cache_options, | 320 | argc = parse_options(argc, argv, buildid_cache_options, |
327 | buildid_cache_usage, 0); | 321 | buildid_cache_usage, 0); |
328 | 322 | ||
329 | if (symbol__init() < 0) | 323 | if (missing_filename) { |
330 | return -1; | 324 | file.path = missing_filename; |
325 | file.force = force; | ||
331 | 326 | ||
332 | setup_pager(); | 327 | session = perf_session__new(&file, false, NULL); |
328 | if (session == NULL) | ||
329 | return -1; | ||
330 | } | ||
331 | |||
332 | if (symbol__init(session ? &session->header.env : NULL) < 0) | ||
333 | goto out; | ||
333 | 334 | ||
334 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | 335 | setup_pager(); |
335 | 336 | ||
336 | if (add_name_list_str) { | 337 | if (add_name_list_str) { |
337 | list = strlist__new(true, add_name_list_str); | 338 | list = strlist__new(true, add_name_list_str); |
338 | if (list) { | 339 | if (list) { |
339 | strlist__for_each(pos, list) | 340 | strlist__for_each(pos, list) |
340 | if (build_id_cache__add_file(pos->s, debugdir)) { | 341 | if (build_id_cache__add_file(pos->s, buildid_dir)) { |
341 | if (errno == EEXIST) { | 342 | if (errno == EEXIST) { |
342 | pr_debug("%s already in the cache\n", | 343 | pr_debug("%s already in the cache\n", |
343 | pos->s); | 344 | pos->s); |
344 | continue; | 345 | continue; |
345 | } | 346 | } |
346 | pr_warning("Couldn't add %s: %s\n", | 347 | pr_warning("Couldn't add %s: %s\n", |
347 | pos->s, strerror(errno)); | 348 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
348 | } | 349 | } |
349 | 350 | ||
350 | strlist__delete(list); | 351 | strlist__delete(list); |
@@ -355,14 +356,14 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
355 | list = strlist__new(true, remove_name_list_str); | 356 | list = strlist__new(true, remove_name_list_str); |
356 | if (list) { | 357 | if (list) { |
357 | strlist__for_each(pos, list) | 358 | strlist__for_each(pos, list) |
358 | if (build_id_cache__remove_file(pos->s, debugdir)) { | 359 | if (build_id_cache__remove_file(pos->s, buildid_dir)) { |
359 | if (errno == ENOENT) { | 360 | if (errno == ENOENT) { |
360 | pr_debug("%s wasn't in the cache\n", | 361 | pr_debug("%s wasn't in the cache\n", |
361 | pos->s); | 362 | pos->s); |
362 | continue; | 363 | continue; |
363 | } | 364 | } |
364 | pr_warning("Couldn't remove %s: %s\n", | 365 | pr_warning("Couldn't remove %s: %s\n", |
365 | pos->s, strerror(errno)); | 366 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
366 | } | 367 | } |
367 | 368 | ||
368 | strlist__delete(list); | 369 | strlist__delete(list); |
@@ -370,20 +371,20 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
370 | } | 371 | } |
371 | 372 | ||
372 | if (missing_filename) | 373 | if (missing_filename) |
373 | ret = build_id_cache__fprintf_missing(missing_filename, force, stdout); | 374 | ret = build_id_cache__fprintf_missing(session, stdout); |
374 | 375 | ||
375 | if (update_name_list_str) { | 376 | if (update_name_list_str) { |
376 | list = strlist__new(true, update_name_list_str); | 377 | list = strlist__new(true, update_name_list_str); |
377 | if (list) { | 378 | if (list) { |
378 | strlist__for_each(pos, list) | 379 | strlist__for_each(pos, list) |
379 | if (build_id_cache__update_file(pos->s, debugdir)) { | 380 | if (build_id_cache__update_file(pos->s, buildid_dir)) { |
380 | if (errno == ENOENT) { | 381 | if (errno == ENOENT) { |
381 | pr_debug("%s wasn't in the cache\n", | 382 | pr_debug("%s wasn't in the cache\n", |
382 | pos->s); | 383 | pos->s); |
383 | continue; | 384 | continue; |
384 | } | 385 | } |
385 | pr_warning("Couldn't update %s: %s\n", | 386 | pr_warning("Couldn't update %s: %s\n", |
386 | pos->s, strerror(errno)); | 387 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
387 | } | 388 | } |
388 | 389 | ||
389 | strlist__delete(list); | 390 | strlist__delete(list); |
@@ -391,8 +392,12 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
391 | } | 392 | } |
392 | 393 | ||
393 | if (kcore_filename && | 394 | if (kcore_filename && |
394 | build_id_cache__add_kcore(kcore_filename, debugdir, force)) | 395 | build_id_cache__add_kcore(kcore_filename, buildid_dir, force)) |
395 | pr_warning("Couldn't add %s\n", kcore_filename); | 396 | pr_warning("Couldn't add %s\n", kcore_filename); |
396 | 397 | ||
398 | out: | ||
399 | if (session) | ||
400 | perf_session__delete(session); | ||
401 | |||
397 | return ret; | 402 | return ret; |
398 | } | 403 | } |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 9a5a035cb426..1fd96c13f199 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -327,6 +327,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
327 | struct machine *machine) | 327 | struct machine *machine) |
328 | { | 328 | { |
329 | struct addr_location al; | 329 | struct addr_location al; |
330 | struct hists *hists = evsel__hists(evsel); | ||
330 | 331 | ||
331 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 332 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
332 | pr_warning("problem processing %d event, skipping it.\n", | 333 | pr_warning("problem processing %d event, skipping it.\n", |
@@ -334,7 +335,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
334 | return -1; | 335 | return -1; |
335 | } | 336 | } |
336 | 337 | ||
337 | if (hists__add_entry(&evsel->hists, &al, sample->period, | 338 | if (hists__add_entry(hists, &al, sample->period, |
338 | sample->weight, sample->transaction)) { | 339 | sample->weight, sample->transaction)) { |
339 | pr_warning("problem incrementing symbol period, skipping event\n"); | 340 | pr_warning("problem incrementing symbol period, skipping event\n"); |
340 | return -1; | 341 | return -1; |
@@ -346,9 +347,9 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
346 | * hists__output_resort() and precompute needs the total | 347 | * hists__output_resort() and precompute needs the total |
347 | * period in order to sort entries by percentage delta. | 348 | * period in order to sort entries by percentage delta. |
348 | */ | 349 | */ |
349 | evsel->hists.stats.total_period += sample->period; | 350 | hists->stats.total_period += sample->period; |
350 | if (!al.filtered) | 351 | if (!al.filtered) |
351 | evsel->hists.stats.total_non_filtered_period += sample->period; | 352 | hists->stats.total_non_filtered_period += sample->period; |
352 | 353 | ||
353 | return 0; | 354 | return 0; |
354 | } | 355 | } |
@@ -356,11 +357,12 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
356 | static struct perf_tool tool = { | 357 | static struct perf_tool tool = { |
357 | .sample = diff__process_sample_event, | 358 | .sample = diff__process_sample_event, |
358 | .mmap = perf_event__process_mmap, | 359 | .mmap = perf_event__process_mmap, |
360 | .mmap2 = perf_event__process_mmap2, | ||
359 | .comm = perf_event__process_comm, | 361 | .comm = perf_event__process_comm, |
360 | .exit = perf_event__process_exit, | 362 | .exit = perf_event__process_exit, |
361 | .fork = perf_event__process_fork, | 363 | .fork = perf_event__process_fork, |
362 | .lost = perf_event__process_lost, | 364 | .lost = perf_event__process_lost, |
363 | .ordered_samples = true, | 365 | .ordered_events = true, |
364 | .ordering_requires_timestamps = true, | 366 | .ordering_requires_timestamps = true, |
365 | }; | 367 | }; |
366 | 368 | ||
@@ -382,7 +384,7 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) | |||
382 | struct perf_evsel *evsel; | 384 | struct perf_evsel *evsel; |
383 | 385 | ||
384 | evlist__for_each(evlist, evsel) { | 386 | evlist__for_each(evlist, evsel) { |
385 | struct hists *hists = &evsel->hists; | 387 | struct hists *hists = evsel__hists(evsel); |
386 | 388 | ||
387 | hists__collapse_resort(hists, NULL); | 389 | hists__collapse_resort(hists, NULL); |
388 | } | 390 | } |
@@ -543,6 +545,42 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
543 | return __hist_entry__cmp_compute(p_left, p_right, c); | 545 | return __hist_entry__cmp_compute(p_left, p_right, c); |
544 | } | 546 | } |
545 | 547 | ||
548 | static int64_t | ||
549 | hist_entry__cmp_nop(struct hist_entry *left __maybe_unused, | ||
550 | struct hist_entry *right __maybe_unused) | ||
551 | { | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int64_t | ||
556 | hist_entry__cmp_baseline(struct hist_entry *left, struct hist_entry *right) | ||
557 | { | ||
558 | if (sort_compute) | ||
559 | return 0; | ||
560 | |||
561 | if (left->stat.period == right->stat.period) | ||
562 | return 0; | ||
563 | return left->stat.period > right->stat.period ? 1 : -1; | ||
564 | } | ||
565 | |||
566 | static int64_t | ||
567 | hist_entry__cmp_delta(struct hist_entry *left, struct hist_entry *right) | ||
568 | { | ||
569 | return hist_entry__cmp_compute(right, left, COMPUTE_DELTA); | ||
570 | } | ||
571 | |||
572 | static int64_t | ||
573 | hist_entry__cmp_ratio(struct hist_entry *left, struct hist_entry *right) | ||
574 | { | ||
575 | return hist_entry__cmp_compute(right, left, COMPUTE_RATIO); | ||
576 | } | ||
577 | |||
578 | static int64_t | ||
579 | hist_entry__cmp_wdiff(struct hist_entry *left, struct hist_entry *right) | ||
580 | { | ||
581 | return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF); | ||
582 | } | ||
583 | |||
546 | static void insert_hist_entry_by_compute(struct rb_root *root, | 584 | static void insert_hist_entry_by_compute(struct rb_root *root, |
547 | struct hist_entry *he, | 585 | struct hist_entry *he, |
548 | int c) | 586 | int c) |
@@ -603,7 +641,7 @@ static void hists__process(struct hists *hists) | |||
603 | hists__precompute(hists); | 641 | hists__precompute(hists); |
604 | hists__compute_resort(hists); | 642 | hists__compute_resort(hists); |
605 | } else { | 643 | } else { |
606 | hists__output_resort(hists); | 644 | hists__output_resort(hists, NULL); |
607 | } | 645 | } |
608 | 646 | ||
609 | hists__fprintf(hists, true, 0, 0, 0, stdout); | 647 | hists__fprintf(hists, true, 0, 0, 0, stdout); |
@@ -631,24 +669,26 @@ static void data_process(void) | |||
631 | bool first = true; | 669 | bool first = true; |
632 | 670 | ||
633 | evlist__for_each(evlist_base, evsel_base) { | 671 | evlist__for_each(evlist_base, evsel_base) { |
672 | struct hists *hists_base = evsel__hists(evsel_base); | ||
634 | struct data__file *d; | 673 | struct data__file *d; |
635 | int i; | 674 | int i; |
636 | 675 | ||
637 | data__for_each_file_new(i, d) { | 676 | data__for_each_file_new(i, d) { |
638 | struct perf_evlist *evlist = d->session->evlist; | 677 | struct perf_evlist *evlist = d->session->evlist; |
639 | struct perf_evsel *evsel; | 678 | struct perf_evsel *evsel; |
679 | struct hists *hists; | ||
640 | 680 | ||
641 | evsel = evsel_match(evsel_base, evlist); | 681 | evsel = evsel_match(evsel_base, evlist); |
642 | if (!evsel) | 682 | if (!evsel) |
643 | continue; | 683 | continue; |
644 | 684 | ||
645 | d->hists = &evsel->hists; | 685 | hists = evsel__hists(evsel); |
686 | d->hists = hists; | ||
646 | 687 | ||
647 | hists__match(&evsel_base->hists, &evsel->hists); | 688 | hists__match(hists_base, hists); |
648 | 689 | ||
649 | if (!show_baseline_only) | 690 | if (!show_baseline_only) |
650 | hists__link(&evsel_base->hists, | 691 | hists__link(hists_base, hists); |
651 | &evsel->hists); | ||
652 | } | 692 | } |
653 | 693 | ||
654 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", | 694 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", |
@@ -659,7 +699,7 @@ static void data_process(void) | |||
659 | if (verbose || data__files_cnt > 2) | 699 | if (verbose || data__files_cnt > 2) |
660 | data__fprintf(); | 700 | data__fprintf(); |
661 | 701 | ||
662 | hists__process(&evsel_base->hists); | 702 | hists__process(hists_base); |
663 | } | 703 | } |
664 | } | 704 | } |
665 | 705 | ||
@@ -683,7 +723,7 @@ static int __cmd_diff(void) | |||
683 | d->session = perf_session__new(&d->file, false, &tool); | 723 | d->session = perf_session__new(&d->file, false, &tool); |
684 | if (!d->session) { | 724 | if (!d->session) { |
685 | pr_err("Failed to open %s\n", d->file.path); | 725 | pr_err("Failed to open %s\n", d->file.path); |
686 | ret = -ENOMEM; | 726 | ret = -1; |
687 | goto out_delete; | 727 | goto out_delete; |
688 | } | 728 | } |
689 | 729 | ||
@@ -1034,27 +1074,35 @@ static void data__hpp_register(struct data__file *d, int idx) | |||
1034 | fmt->header = hpp__header; | 1074 | fmt->header = hpp__header; |
1035 | fmt->width = hpp__width; | 1075 | fmt->width = hpp__width; |
1036 | fmt->entry = hpp__entry_global; | 1076 | fmt->entry = hpp__entry_global; |
1077 | fmt->cmp = hist_entry__cmp_nop; | ||
1078 | fmt->collapse = hist_entry__cmp_nop; | ||
1037 | 1079 | ||
1038 | /* TODO more colors */ | 1080 | /* TODO more colors */ |
1039 | switch (idx) { | 1081 | switch (idx) { |
1040 | case PERF_HPP_DIFF__BASELINE: | 1082 | case PERF_HPP_DIFF__BASELINE: |
1041 | fmt->color = hpp__color_baseline; | 1083 | fmt->color = hpp__color_baseline; |
1084 | fmt->sort = hist_entry__cmp_baseline; | ||
1042 | break; | 1085 | break; |
1043 | case PERF_HPP_DIFF__DELTA: | 1086 | case PERF_HPP_DIFF__DELTA: |
1044 | fmt->color = hpp__color_delta; | 1087 | fmt->color = hpp__color_delta; |
1088 | fmt->sort = hist_entry__cmp_delta; | ||
1045 | break; | 1089 | break; |
1046 | case PERF_HPP_DIFF__RATIO: | 1090 | case PERF_HPP_DIFF__RATIO: |
1047 | fmt->color = hpp__color_ratio; | 1091 | fmt->color = hpp__color_ratio; |
1092 | fmt->sort = hist_entry__cmp_ratio; | ||
1048 | break; | 1093 | break; |
1049 | case PERF_HPP_DIFF__WEIGHTED_DIFF: | 1094 | case PERF_HPP_DIFF__WEIGHTED_DIFF: |
1050 | fmt->color = hpp__color_wdiff; | 1095 | fmt->color = hpp__color_wdiff; |
1096 | fmt->sort = hist_entry__cmp_wdiff; | ||
1051 | break; | 1097 | break; |
1052 | default: | 1098 | default: |
1099 | fmt->sort = hist_entry__cmp_nop; | ||
1053 | break; | 1100 | break; |
1054 | } | 1101 | } |
1055 | 1102 | ||
1056 | init_header(d, dfmt); | 1103 | init_header(d, dfmt); |
1057 | perf_hpp__column_register(fmt); | 1104 | perf_hpp__column_register(fmt); |
1105 | perf_hpp__register_sort_field(fmt); | ||
1058 | } | 1106 | } |
1059 | 1107 | ||
1060 | static void ui_init(void) | 1108 | static void ui_init(void) |
@@ -1139,11 +1187,16 @@ static int data_init(int argc, const char **argv) | |||
1139 | 1187 | ||
1140 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 1188 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) |
1141 | { | 1189 | { |
1190 | int ret = hists__init(); | ||
1191 | |||
1192 | if (ret < 0) | ||
1193 | return ret; | ||
1194 | |||
1142 | perf_config(perf_default_config, NULL); | 1195 | perf_config(perf_default_config, NULL); |
1143 | 1196 | ||
1144 | argc = parse_options(argc, argv, options, diff_usage, 0); | 1197 | argc = parse_options(argc, argv, options, diff_usage, 0); |
1145 | 1198 | ||
1146 | if (symbol__init() < 0) | 1199 | if (symbol__init(NULL) < 0) |
1147 | return -1; | 1200 | return -1; |
1148 | 1201 | ||
1149 | if (data_init(argc, argv) < 0) | 1202 | if (data_init(argc, argv) < 0) |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 66e12f55c052..0f93f859b782 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
@@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details | |||
28 | 28 | ||
29 | session = perf_session__new(&file, 0, NULL); | 29 | session = perf_session__new(&file, 0, NULL); |
30 | if (session == NULL) | 30 | if (session == NULL) |
31 | return -ENOMEM; | 31 | return -1; |
32 | 32 | ||
33 | evlist__for_each(session->evlist, pos) | 33 | evlist__for_each(session->evlist, pos) |
34 | perf_evsel__fprintf(pos, details, stdout); | 34 | perf_evsel__fprintf(pos, details, stdout); |
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 0384d930480b..25d20628212e 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c | |||
@@ -103,6 +103,8 @@ static int check_emacsclient_version(void) | |||
103 | 103 | ||
104 | static void exec_woman_emacs(const char *path, const char *page) | 104 | static void exec_woman_emacs(const char *path, const char *page) |
105 | { | 105 | { |
106 | char sbuf[STRERR_BUFSIZE]; | ||
107 | |||
106 | if (!check_emacsclient_version()) { | 108 | if (!check_emacsclient_version()) { |
107 | /* This works only with emacsclient version >= 22. */ | 109 | /* This works only with emacsclient version >= 22. */ |
108 | struct strbuf man_page = STRBUF_INIT; | 110 | struct strbuf man_page = STRBUF_INIT; |
@@ -111,16 +113,19 @@ static void exec_woman_emacs(const char *path, const char *page) | |||
111 | path = "emacsclient"; | 113 | path = "emacsclient"; |
112 | strbuf_addf(&man_page, "(woman \"%s\")", page); | 114 | strbuf_addf(&man_page, "(woman \"%s\")", page); |
113 | execlp(path, "emacsclient", "-e", man_page.buf, NULL); | 115 | execlp(path, "emacsclient", "-e", man_page.buf, NULL); |
114 | warning("failed to exec '%s': %s", path, strerror(errno)); | 116 | warning("failed to exec '%s': %s", path, |
117 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
115 | } | 118 | } |
116 | } | 119 | } |
117 | 120 | ||
118 | static void exec_man_konqueror(const char *path, const char *page) | 121 | static void exec_man_konqueror(const char *path, const char *page) |
119 | { | 122 | { |
120 | const char *display = getenv("DISPLAY"); | 123 | const char *display = getenv("DISPLAY"); |
124 | |||
121 | if (display && *display) { | 125 | if (display && *display) { |
122 | struct strbuf man_page = STRBUF_INIT; | 126 | struct strbuf man_page = STRBUF_INIT; |
123 | const char *filename = "kfmclient"; | 127 | const char *filename = "kfmclient"; |
128 | char sbuf[STRERR_BUFSIZE]; | ||
124 | 129 | ||
125 | /* It's simpler to launch konqueror using kfmclient. */ | 130 | /* It's simpler to launch konqueror using kfmclient. */ |
126 | if (path) { | 131 | if (path) { |
@@ -139,24 +144,31 @@ static void exec_man_konqueror(const char *path, const char *page) | |||
139 | path = "kfmclient"; | 144 | path = "kfmclient"; |
140 | strbuf_addf(&man_page, "man:%s(1)", page); | 145 | strbuf_addf(&man_page, "man:%s(1)", page); |
141 | execlp(path, filename, "newTab", man_page.buf, NULL); | 146 | execlp(path, filename, "newTab", man_page.buf, NULL); |
142 | warning("failed to exec '%s': %s", path, strerror(errno)); | 147 | warning("failed to exec '%s': %s", path, |
148 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
143 | } | 149 | } |
144 | } | 150 | } |
145 | 151 | ||
146 | static void exec_man_man(const char *path, const char *page) | 152 | static void exec_man_man(const char *path, const char *page) |
147 | { | 153 | { |
154 | char sbuf[STRERR_BUFSIZE]; | ||
155 | |||
148 | if (!path) | 156 | if (!path) |
149 | path = "man"; | 157 | path = "man"; |
150 | execlp(path, "man", page, NULL); | 158 | execlp(path, "man", page, NULL); |
151 | warning("failed to exec '%s': %s", path, strerror(errno)); | 159 | warning("failed to exec '%s': %s", path, |
160 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
152 | } | 161 | } |
153 | 162 | ||
154 | static void exec_man_cmd(const char *cmd, const char *page) | 163 | static void exec_man_cmd(const char *cmd, const char *page) |
155 | { | 164 | { |
156 | struct strbuf shell_cmd = STRBUF_INIT; | 165 | struct strbuf shell_cmd = STRBUF_INIT; |
166 | char sbuf[STRERR_BUFSIZE]; | ||
167 | |||
157 | strbuf_addf(&shell_cmd, "%s %s", cmd, page); | 168 | strbuf_addf(&shell_cmd, "%s %s", cmd, page); |
158 | execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); | 169 | execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); |
159 | warning("failed to exec '%s': %s", cmd, strerror(errno)); | 170 | warning("failed to exec '%s': %s", cmd, |
171 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
160 | } | 172 | } |
161 | 173 | ||
162 | static void add_man_viewer(const char *name) | 174 | static void add_man_viewer(const char *name) |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 9a02807387d6..84df2deed988 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | struct perf_inject { | 24 | struct perf_inject { |
25 | struct perf_tool tool; | 25 | struct perf_tool tool; |
26 | struct perf_session *session; | ||
26 | bool build_ids; | 27 | bool build_ids; |
27 | bool sched_stat; | 28 | bool sched_stat; |
28 | const char *input_name; | 29 | const char *input_name; |
@@ -216,8 +217,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
216 | goto repipe; | 217 | goto repipe; |
217 | } | 218 | } |
218 | 219 | ||
219 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 220 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); |
220 | sample->ip, &al); | ||
221 | 221 | ||
222 | if (al.map != NULL) { | 222 | if (al.map != NULL) { |
223 | if (!al.map->dso->hit) { | 223 | if (!al.map->dso->hit) { |
@@ -340,12 +340,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel, | |||
340 | 340 | ||
341 | static int __cmd_inject(struct perf_inject *inject) | 341 | static int __cmd_inject(struct perf_inject *inject) |
342 | { | 342 | { |
343 | struct perf_session *session; | ||
344 | int ret = -EINVAL; | 343 | int ret = -EINVAL; |
345 | struct perf_data_file file = { | 344 | struct perf_session *session = inject->session; |
346 | .path = inject->input_name, | ||
347 | .mode = PERF_DATA_MODE_READ, | ||
348 | }; | ||
349 | struct perf_data_file *file_out = &inject->output; | 345 | struct perf_data_file *file_out = &inject->output; |
350 | 346 | ||
351 | signal(SIGINT, sig_handler); | 347 | signal(SIGINT, sig_handler); |
@@ -357,16 +353,12 @@ static int __cmd_inject(struct perf_inject *inject) | |||
357 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 353 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
358 | } | 354 | } |
359 | 355 | ||
360 | session = perf_session__new(&file, true, &inject->tool); | ||
361 | if (session == NULL) | ||
362 | return -ENOMEM; | ||
363 | |||
364 | if (inject->build_ids) { | 356 | if (inject->build_ids) { |
365 | inject->tool.sample = perf_event__inject_buildid; | 357 | inject->tool.sample = perf_event__inject_buildid; |
366 | } else if (inject->sched_stat) { | 358 | } else if (inject->sched_stat) { |
367 | struct perf_evsel *evsel; | 359 | struct perf_evsel *evsel; |
368 | 360 | ||
369 | inject->tool.ordered_samples = true; | 361 | inject->tool.ordered_events = true; |
370 | 362 | ||
371 | evlist__for_each(session->evlist, evsel) { | 363 | evlist__for_each(session->evlist, evsel) { |
372 | const char *name = perf_evsel__name(evsel); | 364 | const char *name = perf_evsel__name(evsel); |
@@ -396,8 +388,6 @@ static int __cmd_inject(struct perf_inject *inject) | |||
396 | perf_session__write_header(session, session->evlist, file_out->fd, true); | 388 | perf_session__write_header(session, session->evlist, file_out->fd, true); |
397 | } | 389 | } |
398 | 390 | ||
399 | perf_session__delete(session); | ||
400 | |||
401 | return ret; | 391 | return ret; |
402 | } | 392 | } |
403 | 393 | ||
@@ -419,6 +409,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
419 | .tracing_data = perf_event__repipe_op2_synth, | 409 | .tracing_data = perf_event__repipe_op2_synth, |
420 | .finished_round = perf_event__repipe_op2_synth, | 410 | .finished_round = perf_event__repipe_op2_synth, |
421 | .build_id = perf_event__repipe_op2_synth, | 411 | .build_id = perf_event__repipe_op2_synth, |
412 | .id_index = perf_event__repipe_op2_synth, | ||
422 | }, | 413 | }, |
423 | .input_name = "-", | 414 | .input_name = "-", |
424 | .samples = LIST_HEAD_INIT(inject.samples), | 415 | .samples = LIST_HEAD_INIT(inject.samples), |
@@ -427,6 +418,11 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
427 | .mode = PERF_DATA_MODE_WRITE, | 418 | .mode = PERF_DATA_MODE_WRITE, |
428 | }, | 419 | }, |
429 | }; | 420 | }; |
421 | struct perf_data_file file = { | ||
422 | .mode = PERF_DATA_MODE_READ, | ||
423 | }; | ||
424 | int ret; | ||
425 | |||
430 | const struct option options[] = { | 426 | const struct option options[] = { |
431 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 427 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
432 | "Inject build-ids into the output stream"), | 428 | "Inject build-ids into the output stream"), |
@@ -461,8 +457,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
461 | return -1; | 457 | return -1; |
462 | } | 458 | } |
463 | 459 | ||
464 | if (symbol__init() < 0) | 460 | file.path = inject.input_name; |
461 | inject.session = perf_session__new(&file, true, &inject.tool); | ||
462 | if (inject.session == NULL) | ||
463 | return -1; | ||
464 | |||
465 | if (symbol__init(&inject.session->header.env) < 0) | ||
465 | return -1; | 466 | return -1; |
466 | 467 | ||
467 | return __cmd_inject(&inject); | 468 | ret = __cmd_inject(&inject); |
469 | |||
470 | perf_session__delete(inject.session); | ||
471 | |||
472 | return ret; | ||
468 | } | 473 | } |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index bef3376bfaf3..f295141025bc 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -256,7 +256,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
256 | static struct perf_tool perf_kmem = { | 256 | static struct perf_tool perf_kmem = { |
257 | .sample = process_sample_event, | 257 | .sample = process_sample_event, |
258 | .comm = perf_event__process_comm, | 258 | .comm = perf_event__process_comm, |
259 | .ordered_samples = true, | 259 | .mmap = perf_event__process_mmap, |
260 | .mmap2 = perf_event__process_mmap2, | ||
261 | .ordered_events = true, | ||
260 | }; | 262 | }; |
261 | 263 | ||
262 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) | 264 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) |
@@ -403,10 +405,9 @@ static void sort_result(void) | |||
403 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); | 405 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); |
404 | } | 406 | } |
405 | 407 | ||
406 | static int __cmd_kmem(void) | 408 | static int __cmd_kmem(struct perf_session *session) |
407 | { | 409 | { |
408 | int err = -EINVAL; | 410 | int err = -EINVAL; |
409 | struct perf_session *session; | ||
410 | const struct perf_evsel_str_handler kmem_tracepoints[] = { | 411 | const struct perf_evsel_str_handler kmem_tracepoints[] = { |
411 | { "kmem:kmalloc", perf_evsel__process_alloc_event, }, | 412 | { "kmem:kmalloc", perf_evsel__process_alloc_event, }, |
412 | { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, | 413 | { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, |
@@ -415,34 +416,22 @@ static int __cmd_kmem(void) | |||
415 | { "kmem:kfree", perf_evsel__process_free_event, }, | 416 | { "kmem:kfree", perf_evsel__process_free_event, }, |
416 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, | 417 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, |
417 | }; | 418 | }; |
418 | struct perf_data_file file = { | ||
419 | .path = input_name, | ||
420 | .mode = PERF_DATA_MODE_READ, | ||
421 | }; | ||
422 | |||
423 | session = perf_session__new(&file, false, &perf_kmem); | ||
424 | if (session == NULL) | ||
425 | return -ENOMEM; | ||
426 | |||
427 | if (perf_session__create_kernel_maps(session) < 0) | ||
428 | goto out_delete; | ||
429 | 419 | ||
430 | if (!perf_session__has_traces(session, "kmem record")) | 420 | if (!perf_session__has_traces(session, "kmem record")) |
431 | goto out_delete; | 421 | goto out; |
432 | 422 | ||
433 | if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { | 423 | if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { |
434 | pr_err("Initializing perf session tracepoint handlers failed\n"); | 424 | pr_err("Initializing perf session tracepoint handlers failed\n"); |
435 | return -1; | 425 | goto out; |
436 | } | 426 | } |
437 | 427 | ||
438 | setup_pager(); | 428 | setup_pager(); |
439 | err = perf_session__process_events(session, &perf_kmem); | 429 | err = perf_session__process_events(session, &perf_kmem); |
440 | if (err != 0) | 430 | if (err != 0) |
441 | goto out_delete; | 431 | goto out; |
442 | sort_result(); | 432 | sort_result(); |
443 | print_result(session); | 433 | print_result(session); |
444 | out_delete: | 434 | out: |
445 | perf_session__delete(session); | ||
446 | return err; | 435 | return err; |
447 | } | 436 | } |
448 | 437 | ||
@@ -689,29 +678,46 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
689 | NULL, | 678 | NULL, |
690 | NULL | 679 | NULL |
691 | }; | 680 | }; |
681 | struct perf_session *session; | ||
682 | struct perf_data_file file = { | ||
683 | .path = input_name, | ||
684 | .mode = PERF_DATA_MODE_READ, | ||
685 | }; | ||
686 | int ret = -1; | ||
687 | |||
692 | argc = parse_options_subcommand(argc, argv, kmem_options, | 688 | argc = parse_options_subcommand(argc, argv, kmem_options, |
693 | kmem_subcommands, kmem_usage, 0); | 689 | kmem_subcommands, kmem_usage, 0); |
694 | 690 | ||
695 | if (!argc) | 691 | if (!argc) |
696 | usage_with_options(kmem_usage, kmem_options); | 692 | usage_with_options(kmem_usage, kmem_options); |
697 | 693 | ||
698 | symbol__init(); | ||
699 | |||
700 | if (!strncmp(argv[0], "rec", 3)) { | 694 | if (!strncmp(argv[0], "rec", 3)) { |
695 | symbol__init(NULL); | ||
701 | return __cmd_record(argc, argv); | 696 | return __cmd_record(argc, argv); |
702 | } else if (!strcmp(argv[0], "stat")) { | 697 | } |
698 | |||
699 | session = perf_session__new(&file, false, &perf_kmem); | ||
700 | if (session == NULL) | ||
701 | return -1; | ||
702 | |||
703 | symbol__init(&session->header.env); | ||
704 | |||
705 | if (!strcmp(argv[0], "stat")) { | ||
703 | if (cpu__setup_cpunode_map()) | 706 | if (cpu__setup_cpunode_map()) |
704 | return -1; | 707 | goto out_delete; |
705 | 708 | ||
706 | if (list_empty(&caller_sort)) | 709 | if (list_empty(&caller_sort)) |
707 | setup_sorting(&caller_sort, default_sort_order); | 710 | setup_sorting(&caller_sort, default_sort_order); |
708 | if (list_empty(&alloc_sort)) | 711 | if (list_empty(&alloc_sort)) |
709 | setup_sorting(&alloc_sort, default_sort_order); | 712 | setup_sorting(&alloc_sort, default_sort_order); |
710 | 713 | ||
711 | return __cmd_kmem(); | 714 | ret = __cmd_kmem(session); |
712 | } else | 715 | } else |
713 | usage_with_options(kmem_usage, kmem_options); | 716 | usage_with_options(kmem_usage, kmem_options); |
714 | 717 | ||
715 | return 0; | 718 | out_delete: |
719 | perf_session__delete(session); | ||
720 | |||
721 | return ret; | ||
716 | } | 722 | } |
717 | 723 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 43367eb00510..0894a817f67e 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -376,7 +376,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
376 | struct perf_sample *sample) | 376 | struct perf_sample *sample) |
377 | { | 377 | { |
378 | /* Only kvm_entry records vcpu id. */ | 378 | /* Only kvm_entry records vcpu id. */ |
379 | if (!thread->priv && kvm_entry_event(evsel)) { | 379 | if (!thread__priv(thread) && kvm_entry_event(evsel)) { |
380 | struct vcpu_event_record *vcpu_record; | 380 | struct vcpu_event_record *vcpu_record; |
381 | 381 | ||
382 | vcpu_record = zalloc(sizeof(*vcpu_record)); | 382 | vcpu_record = zalloc(sizeof(*vcpu_record)); |
@@ -386,10 +386,10 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
386 | } | 386 | } |
387 | 387 | ||
388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); | 388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); |
389 | thread->priv = vcpu_record; | 389 | thread__set_priv(thread, vcpu_record); |
390 | } | 390 | } |
391 | 391 | ||
392 | return thread->priv; | 392 | return thread__priv(thread); |
393 | } | 393 | } |
394 | 394 | ||
395 | static bool handle_kvm_event(struct perf_kvm_stat *kvm, | 395 | static bool handle_kvm_event(struct perf_kvm_stat *kvm, |
@@ -543,14 +543,12 @@ static void print_vcpu_info(struct perf_kvm_stat *kvm) | |||
543 | 543 | ||
544 | pr_info("Analyze events for "); | 544 | pr_info("Analyze events for "); |
545 | 545 | ||
546 | if (kvm->live) { | 546 | if (kvm->opts.target.system_wide) |
547 | if (kvm->opts.target.system_wide) | 547 | pr_info("all VMs, "); |
548 | pr_info("all VMs, "); | 548 | else if (kvm->opts.target.pid) |
549 | else if (kvm->opts.target.pid) | 549 | pr_info("pid(s) %s, ", kvm->opts.target.pid); |
550 | pr_info("pid(s) %s, ", kvm->opts.target.pid); | 550 | else |
551 | else | 551 | pr_info("dazed and confused on what is monitored, "); |
552 | pr_info("dazed and confused on what is monitored, "); | ||
553 | } | ||
554 | 552 | ||
555 | if (vcpu == -1) | 553 | if (vcpu == -1) |
556 | pr_info("all VCPUs:\n\n"); | 554 | pr_info("all VCPUs:\n\n"); |
@@ -592,8 +590,8 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
592 | pr_info("%9s ", "Samples%"); | 590 | pr_info("%9s ", "Samples%"); |
593 | 591 | ||
594 | pr_info("%9s ", "Time%"); | 592 | pr_info("%9s ", "Time%"); |
595 | pr_info("%10s ", "Min Time"); | 593 | pr_info("%11s ", "Min Time"); |
596 | pr_info("%10s ", "Max Time"); | 594 | pr_info("%11s ", "Max Time"); |
597 | pr_info("%16s ", "Avg time"); | 595 | pr_info("%16s ", "Avg time"); |
598 | pr_info("\n\n"); | 596 | pr_info("\n\n"); |
599 | 597 | ||
@@ -610,8 +608,8 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
610 | pr_info("%10llu ", (unsigned long long)ecount); | 608 | pr_info("%10llu ", (unsigned long long)ecount); |
611 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); | 609 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); |
612 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); | 610 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); |
613 | pr_info("%8" PRIu64 "us ", min / 1000); | 611 | pr_info("%9.2fus ", (double)min / 1e3); |
614 | pr_info("%8" PRIu64 "us ", max / 1000); | 612 | pr_info("%9.2fus ", (double)max / 1e3); |
615 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, | 613 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, |
616 | kvm_event_rel_stddev(vcpu, event)); | 614 | kvm_event_rel_stddev(vcpu, event)); |
617 | pr_info("\n"); | 615 | pr_info("\n"); |
@@ -732,7 +730,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, | |||
732 | return -1; | 730 | return -1; |
733 | } | 731 | } |
734 | 732 | ||
735 | err = perf_session_queue_event(kvm->session, event, &sample, 0); | 733 | err = perf_session_queue_event(kvm->session, event, &kvm->tool, &sample, 0); |
736 | /* | 734 | /* |
737 | * FIXME: Here we can't consume the event, as perf_session_queue_event will | 735 | * FIXME: Here we can't consume the event, as perf_session_queue_event will |
738 | * point to it, and it'll get possibly overwritten by the kernel. | 736 | * point to it, and it'll get possibly overwritten by the kernel. |
@@ -785,7 +783,7 @@ static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm) | |||
785 | 783 | ||
786 | /* flush queue after each round in which we processed events */ | 784 | /* flush queue after each round in which we processed events */ |
787 | if (ntotal) { | 785 | if (ntotal) { |
788 | kvm->session->ordered_samples.next_flush = flush_time; | 786 | kvm->session->ordered_events.next_flush = flush_time; |
789 | err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session); | 787 | err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session); |
790 | if (err) { | 788 | if (err) { |
791 | if (kvm->lost_events) | 789 | if (kvm->lost_events) |
@@ -885,15 +883,11 @@ static int fd_set_nonblock(int fd) | |||
885 | return 0; | 883 | return 0; |
886 | } | 884 | } |
887 | 885 | ||
888 | static | 886 | static int perf_kvm__handle_stdin(void) |
889 | int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save) | ||
890 | { | 887 | { |
891 | int c; | 888 | int c; |
892 | 889 | ||
893 | tcsetattr(0, TCSANOW, tc_now); | ||
894 | c = getc(stdin); | 890 | c = getc(stdin); |
895 | tcsetattr(0, TCSAFLUSH, tc_save); | ||
896 | |||
897 | if (c == 'q') | 891 | if (c == 'q') |
898 | return 1; | 892 | return 1; |
899 | 893 | ||
@@ -902,9 +896,8 @@ int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save) | |||
902 | 896 | ||
903 | static int kvm_events_live_report(struct perf_kvm_stat *kvm) | 897 | static int kvm_events_live_report(struct perf_kvm_stat *kvm) |
904 | { | 898 | { |
905 | struct pollfd *pollfds = NULL; | 899 | int nr_stdin, ret, err = -EINVAL; |
906 | int nr_fds, nr_stdin, ret, err = -EINVAL; | 900 | struct termios save; |
907 | struct termios tc, save; | ||
908 | 901 | ||
909 | /* live flag must be set first */ | 902 | /* live flag must be set first */ |
910 | kvm->live = true; | 903 | kvm->live = true; |
@@ -919,41 +912,25 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
919 | goto out; | 912 | goto out; |
920 | } | 913 | } |
921 | 914 | ||
915 | set_term_quiet_input(&save); | ||
922 | init_kvm_event_record(kvm); | 916 | init_kvm_event_record(kvm); |
923 | 917 | ||
924 | tcgetattr(0, &save); | ||
925 | tc = save; | ||
926 | tc.c_lflag &= ~(ICANON | ECHO); | ||
927 | tc.c_cc[VMIN] = 0; | ||
928 | tc.c_cc[VTIME] = 0; | ||
929 | |||
930 | signal(SIGINT, sig_handler); | 918 | signal(SIGINT, sig_handler); |
931 | signal(SIGTERM, sig_handler); | 919 | signal(SIGTERM, sig_handler); |
932 | 920 | ||
933 | /* copy pollfds -- need to add timerfd and stdin */ | ||
934 | nr_fds = kvm->evlist->nr_fds; | ||
935 | pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2)); | ||
936 | if (!pollfds) { | ||
937 | err = -ENOMEM; | ||
938 | goto out; | ||
939 | } | ||
940 | memcpy(pollfds, kvm->evlist->pollfd, | ||
941 | sizeof(struct pollfd) * kvm->evlist->nr_fds); | ||
942 | |||
943 | /* add timer fd */ | 921 | /* add timer fd */ |
944 | if (perf_kvm__timerfd_create(kvm) < 0) { | 922 | if (perf_kvm__timerfd_create(kvm) < 0) { |
945 | err = -1; | 923 | err = -1; |
946 | goto out; | 924 | goto out; |
947 | } | 925 | } |
948 | 926 | ||
949 | pollfds[nr_fds].fd = kvm->timerfd; | 927 | if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd) < 0) |
950 | pollfds[nr_fds].events = POLLIN; | 928 | goto out; |
951 | nr_fds++; | 929 | |
930 | nr_stdin = perf_evlist__add_pollfd(kvm->evlist, fileno(stdin)); | ||
931 | if (nr_stdin < 0) | ||
932 | goto out; | ||
952 | 933 | ||
953 | pollfds[nr_fds].fd = fileno(stdin); | ||
954 | pollfds[nr_fds].events = POLLIN; | ||
955 | nr_stdin = nr_fds; | ||
956 | nr_fds++; | ||
957 | if (fd_set_nonblock(fileno(stdin)) != 0) | 934 | if (fd_set_nonblock(fileno(stdin)) != 0) |
958 | goto out; | 935 | goto out; |
959 | 936 | ||
@@ -961,6 +938,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
961 | perf_evlist__enable(kvm->evlist); | 938 | perf_evlist__enable(kvm->evlist); |
962 | 939 | ||
963 | while (!done) { | 940 | while (!done) { |
941 | struct fdarray *fda = &kvm->evlist->pollfd; | ||
964 | int rc; | 942 | int rc; |
965 | 943 | ||
966 | rc = perf_kvm__mmap_read(kvm); | 944 | rc = perf_kvm__mmap_read(kvm); |
@@ -971,11 +949,11 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
971 | if (err) | 949 | if (err) |
972 | goto out; | 950 | goto out; |
973 | 951 | ||
974 | if (pollfds[nr_stdin].revents & POLLIN) | 952 | if (fda->entries[nr_stdin].revents & POLLIN) |
975 | done = perf_kvm__handle_stdin(&tc, &save); | 953 | done = perf_kvm__handle_stdin(); |
976 | 954 | ||
977 | if (!rc && !done) | 955 | if (!rc && !done) |
978 | err = poll(pollfds, nr_fds, 100); | 956 | err = fdarray__poll(fda, 100); |
979 | } | 957 | } |
980 | 958 | ||
981 | perf_evlist__disable(kvm->evlist); | 959 | perf_evlist__disable(kvm->evlist); |
@@ -989,7 +967,7 @@ out: | |||
989 | if (kvm->timerfd >= 0) | 967 | if (kvm->timerfd >= 0) |
990 | close(kvm->timerfd); | 968 | close(kvm->timerfd); |
991 | 969 | ||
992 | free(pollfds); | 970 | tcsetattr(0, TCSAFLUSH, &save); |
993 | return err; | 971 | return err; |
994 | } | 972 | } |
995 | 973 | ||
@@ -998,6 +976,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
998 | int err, rc = -1; | 976 | int err, rc = -1; |
999 | struct perf_evsel *pos; | 977 | struct perf_evsel *pos; |
1000 | struct perf_evlist *evlist = kvm->evlist; | 978 | struct perf_evlist *evlist = kvm->evlist; |
979 | char sbuf[STRERR_BUFSIZE]; | ||
1001 | 980 | ||
1002 | perf_evlist__config(evlist, &kvm->opts); | 981 | perf_evlist__config(evlist, &kvm->opts); |
1003 | 982 | ||
@@ -1034,12 +1013,14 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
1034 | 1013 | ||
1035 | err = perf_evlist__open(evlist); | 1014 | err = perf_evlist__open(evlist); |
1036 | if (err < 0) { | 1015 | if (err < 0) { |
1037 | printf("Couldn't create the events: %s\n", strerror(errno)); | 1016 | printf("Couldn't create the events: %s\n", |
1017 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
1038 | goto out; | 1018 | goto out; |
1039 | } | 1019 | } |
1040 | 1020 | ||
1041 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { | 1021 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { |
1042 | ui__error("Failed to mmap the events: %s\n", strerror(errno)); | 1022 | ui__error("Failed to mmap the events: %s\n", |
1023 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
1043 | perf_evlist__close(evlist); | 1024 | perf_evlist__close(evlist); |
1044 | goto out; | 1025 | goto out; |
1045 | } | 1026 | } |
@@ -1058,7 +1039,7 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
1058 | struct perf_tool eops = { | 1039 | struct perf_tool eops = { |
1059 | .sample = process_sample_event, | 1040 | .sample = process_sample_event, |
1060 | .comm = perf_event__process_comm, | 1041 | .comm = perf_event__process_comm, |
1061 | .ordered_samples = true, | 1042 | .ordered_events = true, |
1062 | }; | 1043 | }; |
1063 | struct perf_data_file file = { | 1044 | struct perf_data_file file = { |
1064 | .path = kvm->file_name, | 1045 | .path = kvm->file_name, |
@@ -1069,9 +1050,11 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
1069 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1050 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
1070 | if (!kvm->session) { | 1051 | if (!kvm->session) { |
1071 | pr_err("Initializing perf session failed\n"); | 1052 | pr_err("Initializing perf session failed\n"); |
1072 | return -EINVAL; | 1053 | return -1; |
1073 | } | 1054 | } |
1074 | 1055 | ||
1056 | symbol__init(&kvm->session->header.env); | ||
1057 | |||
1075 | if (!perf_session__has_traces(kvm->session, "kvm record")) | 1058 | if (!perf_session__has_traces(kvm->session, "kvm record")) |
1076 | return -EINVAL; | 1059 | return -EINVAL; |
1077 | 1060 | ||
@@ -1088,8 +1071,8 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
1088 | 1071 | ||
1089 | static int parse_target_str(struct perf_kvm_stat *kvm) | 1072 | static int parse_target_str(struct perf_kvm_stat *kvm) |
1090 | { | 1073 | { |
1091 | if (kvm->pid_str) { | 1074 | if (kvm->opts.target.pid) { |
1092 | kvm->pid_list = intlist__new(kvm->pid_str); | 1075 | kvm->pid_list = intlist__new(kvm->opts.target.pid); |
1093 | if (kvm->pid_list == NULL) { | 1076 | if (kvm->pid_list == NULL) { |
1094 | pr_err("Error parsing process id string\n"); | 1077 | pr_err("Error parsing process id string\n"); |
1095 | return -EINVAL; | 1078 | return -EINVAL; |
@@ -1149,6 +1132,10 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1149 | "-m", "1024", | 1132 | "-m", "1024", |
1150 | "-c", "1", | 1133 | "-c", "1", |
1151 | }; | 1134 | }; |
1135 | const char * const kvm_stat_record_usage[] = { | ||
1136 | "perf kvm stat record [<options>]", | ||
1137 | NULL | ||
1138 | }; | ||
1152 | const char * const *events_tp; | 1139 | const char * const *events_tp; |
1153 | events_tp_size = 0; | 1140 | events_tp_size = 0; |
1154 | 1141 | ||
@@ -1176,6 +1163,27 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1176 | for (j = 1; j < (unsigned int)argc; j++, i++) | 1163 | for (j = 1; j < (unsigned int)argc; j++, i++) |
1177 | rec_argv[i] = argv[j]; | 1164 | rec_argv[i] = argv[j]; |
1178 | 1165 | ||
1166 | set_option_flag(record_options, 'e', "event", PARSE_OPT_HIDDEN); | ||
1167 | set_option_flag(record_options, 0, "filter", PARSE_OPT_HIDDEN); | ||
1168 | set_option_flag(record_options, 'R', "raw-samples", PARSE_OPT_HIDDEN); | ||
1169 | |||
1170 | set_option_flag(record_options, 'F', "freq", PARSE_OPT_DISABLED); | ||
1171 | set_option_flag(record_options, 0, "group", PARSE_OPT_DISABLED); | ||
1172 | set_option_flag(record_options, 'g', NULL, PARSE_OPT_DISABLED); | ||
1173 | set_option_flag(record_options, 0, "call-graph", PARSE_OPT_DISABLED); | ||
1174 | set_option_flag(record_options, 'd', "data", PARSE_OPT_DISABLED); | ||
1175 | set_option_flag(record_options, 'T', "timestamp", PARSE_OPT_DISABLED); | ||
1176 | set_option_flag(record_options, 'P', "period", PARSE_OPT_DISABLED); | ||
1177 | set_option_flag(record_options, 'n', "no-samples", PARSE_OPT_DISABLED); | ||
1178 | set_option_flag(record_options, 'N', "no-buildid-cache", PARSE_OPT_DISABLED); | ||
1179 | set_option_flag(record_options, 'B', "no-buildid", PARSE_OPT_DISABLED); | ||
1180 | set_option_flag(record_options, 'G', "cgroup", PARSE_OPT_DISABLED); | ||
1181 | set_option_flag(record_options, 'b', "branch-any", PARSE_OPT_DISABLED); | ||
1182 | set_option_flag(record_options, 'j', "branch-filter", PARSE_OPT_DISABLED); | ||
1183 | set_option_flag(record_options, 'W', "weight", PARSE_OPT_DISABLED); | ||
1184 | set_option_flag(record_options, 0, "transaction", PARSE_OPT_DISABLED); | ||
1185 | |||
1186 | record_usage = kvm_stat_record_usage; | ||
1179 | return cmd_record(i, rec_argv, NULL); | 1187 | return cmd_record(i, rec_argv, NULL); |
1180 | } | 1188 | } |
1181 | 1189 | ||
@@ -1191,7 +1199,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1191 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1199 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
1192 | "key for sorting: sample(sort by samples number)" | 1200 | "key for sorting: sample(sort by samples number)" |
1193 | " time (sort by avg time)"), | 1201 | " time (sort by avg time)"), |
1194 | OPT_STRING('p', "pid", &kvm->pid_str, "pid", | 1202 | OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid", |
1195 | "analyze events only for given process id(s)"), | 1203 | "analyze events only for given process id(s)"), |
1196 | OPT_END() | 1204 | OPT_END() |
1197 | }; | 1205 | }; |
@@ -1201,8 +1209,6 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1201 | NULL | 1209 | NULL |
1202 | }; | 1210 | }; |
1203 | 1211 | ||
1204 | symbol__init(); | ||
1205 | |||
1206 | if (argc) { | 1212 | if (argc) { |
1207 | argc = parse_options(argc, argv, | 1213 | argc = parse_options(argc, argv, |
1208 | kvm_events_report_options, | 1214 | kvm_events_report_options, |
@@ -1212,6 +1218,9 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1212 | kvm_events_report_options); | 1218 | kvm_events_report_options); |
1213 | } | 1219 | } |
1214 | 1220 | ||
1221 | if (!kvm->opts.target.pid) | ||
1222 | kvm->opts.target.system_wide = true; | ||
1223 | |||
1215 | return kvm_events_report_vcpu(kvm); | 1224 | return kvm_events_report_vcpu(kvm); |
1216 | } | 1225 | } |
1217 | 1226 | ||
@@ -1284,7 +1293,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1284 | OPT_UINTEGER('d', "display", &kvm->display_time, | 1293 | OPT_UINTEGER('d', "display", &kvm->display_time, |
1285 | "time in seconds between display updates"), | 1294 | "time in seconds between display updates"), |
1286 | OPT_STRING(0, "event", &kvm->report_event, "report event", | 1295 | OPT_STRING(0, "event", &kvm->report_event, "report event", |
1287 | "event for reporting: vmexit, mmio, ioport"), | 1296 | "event for reporting: " |
1297 | "vmexit, mmio (x86 only), ioport (x86 only)"), | ||
1288 | OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, | 1298 | OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, |
1289 | "vcpu id to report"), | 1299 | "vcpu id to report"), |
1290 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1300 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
@@ -1311,7 +1321,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1311 | kvm->tool.exit = perf_event__process_exit; | 1321 | kvm->tool.exit = perf_event__process_exit; |
1312 | kvm->tool.fork = perf_event__process_fork; | 1322 | kvm->tool.fork = perf_event__process_fork; |
1313 | kvm->tool.lost = process_lost_event; | 1323 | kvm->tool.lost = process_lost_event; |
1314 | kvm->tool.ordered_samples = true; | 1324 | kvm->tool.ordered_events = true; |
1315 | perf_tool__fill_defaults(&kvm->tool); | 1325 | perf_tool__fill_defaults(&kvm->tool); |
1316 | 1326 | ||
1317 | /* set defaults */ | 1327 | /* set defaults */ |
@@ -1322,7 +1332,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1322 | kvm->opts.target.uid_str = NULL; | 1332 | kvm->opts.target.uid_str = NULL; |
1323 | kvm->opts.target.uid = UINT_MAX; | 1333 | kvm->opts.target.uid = UINT_MAX; |
1324 | 1334 | ||
1325 | symbol__init(); | 1335 | symbol__init(NULL); |
1326 | disable_buildid_cache(); | 1336 | disable_buildid_cache(); |
1327 | 1337 | ||
1328 | use_browser = 0; | 1338 | use_browser = 0; |
@@ -1369,11 +1379,12 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1369 | */ | 1379 | */ |
1370 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1380 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
1371 | if (kvm->session == NULL) { | 1381 | if (kvm->session == NULL) { |
1372 | err = -ENOMEM; | 1382 | err = -1; |
1373 | goto out; | 1383 | goto out; |
1374 | } | 1384 | } |
1375 | kvm->session->evlist = kvm->evlist; | 1385 | kvm->session->evlist = kvm->evlist; |
1376 | perf_session__set_id_hdr_size(kvm->session); | 1386 | perf_session__set_id_hdr_size(kvm->session); |
1387 | ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true); | ||
1377 | machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, | 1388 | machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, |
1378 | kvm->evlist->threads, false); | 1389 | kvm->evlist->threads, false); |
1379 | err = kvm_live_open_events(kvm); | 1390 | err = kvm_live_open_events(kvm); |
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 011195e38f21..198f3c3aff95 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -19,7 +19,9 @@ | |||
19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | 19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) |
20 | { | 20 | { |
21 | int i; | 21 | int i; |
22 | const struct option list_options[] = { | 22 | bool raw_dump = false; |
23 | struct option list_options[] = { | ||
24 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), | ||
23 | OPT_END() | 25 | OPT_END() |
24 | }; | 26 | }; |
25 | const char * const list_usage[] = { | 27 | const char * const list_usage[] = { |
@@ -27,11 +29,18 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
27 | NULL | 29 | NULL |
28 | }; | 30 | }; |
29 | 31 | ||
32 | set_option_flag(list_options, 0, "raw-dump", PARSE_OPT_HIDDEN); | ||
33 | |||
30 | argc = parse_options(argc, argv, list_options, list_usage, | 34 | argc = parse_options(argc, argv, list_options, list_usage, |
31 | PARSE_OPT_STOP_AT_NON_OPTION); | 35 | PARSE_OPT_STOP_AT_NON_OPTION); |
32 | 36 | ||
33 | setup_pager(); | 37 | setup_pager(); |
34 | 38 | ||
39 | if (raw_dump) { | ||
40 | print_events(NULL, true); | ||
41 | return 0; | ||
42 | } | ||
43 | |||
35 | if (argc == 0) { | 44 | if (argc == 0) { |
36 | print_events(NULL, false); | 45 | print_events(NULL, false); |
37 | return 0; | 46 | return 0; |
@@ -53,8 +62,6 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
53 | print_hwcache_events(NULL, false); | 62 | print_hwcache_events(NULL, false); |
54 | else if (strcmp(argv[i], "pmu") == 0) | 63 | else if (strcmp(argv[i], "pmu") == 0) |
55 | print_pmu_events(NULL, false); | 64 | print_pmu_events(NULL, false); |
56 | else if (strcmp(argv[i], "--raw-dump") == 0) | ||
57 | print_events(NULL, true); | ||
58 | else { | 65 | else { |
59 | char *sep = strchr(argv[i], ':'), *s; | 66 | char *sep = strchr(argv[i], ':'), *s; |
60 | int sep_idx; | 67 | int sep_idx; |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6148afc995c6..e7ec71589da6 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -852,7 +852,7 @@ static int __cmd_report(bool display_info) | |||
852 | struct perf_tool eops = { | 852 | struct perf_tool eops = { |
853 | .sample = process_sample_event, | 853 | .sample = process_sample_event, |
854 | .comm = perf_event__process_comm, | 854 | .comm = perf_event__process_comm, |
855 | .ordered_samples = true, | 855 | .ordered_events = true, |
856 | }; | 856 | }; |
857 | struct perf_data_file file = { | 857 | struct perf_data_file file = { |
858 | .path = input_name, | 858 | .path = input_name, |
@@ -862,9 +862,11 @@ static int __cmd_report(bool display_info) | |||
862 | session = perf_session__new(&file, false, &eops); | 862 | session = perf_session__new(&file, false, &eops); |
863 | if (!session) { | 863 | if (!session) { |
864 | pr_err("Initializing perf session failed\n"); | 864 | pr_err("Initializing perf session failed\n"); |
865 | return -ENOMEM; | 865 | return -1; |
866 | } | 866 | } |
867 | 867 | ||
868 | symbol__init(&session->header.env); | ||
869 | |||
868 | if (!perf_session__has_traces(session, "lock record")) | 870 | if (!perf_session__has_traces(session, "lock record")) |
869 | goto out_delete; | 871 | goto out_delete; |
870 | 872 | ||
@@ -974,7 +976,6 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) | |||
974 | unsigned int i; | 976 | unsigned int i; |
975 | int rc = 0; | 977 | int rc = 0; |
976 | 978 | ||
977 | symbol__init(); | ||
978 | for (i = 0; i < LOCKHASH_SIZE; i++) | 979 | for (i = 0; i < LOCKHASH_SIZE; i++) |
979 | INIT_LIST_HEAD(lockhash_table + i); | 980 | INIT_LIST_HEAD(lockhash_table + i); |
980 | 981 | ||
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 4a1a6c94a5eb..24db6ffe2957 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem) | |||
124 | &mem->tool); | 124 | &mem->tool); |
125 | 125 | ||
126 | if (session == NULL) | 126 | if (session == NULL) |
127 | return -ENOMEM; | 127 | return -1; |
128 | 128 | ||
129 | if (mem->cpu_list) { | 129 | if (mem->cpu_list) { |
130 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, | 130 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, |
@@ -133,7 +133,7 @@ static int report_raw_events(struct perf_mem *mem) | |||
133 | goto out_delete; | 133 | goto out_delete; |
134 | } | 134 | } |
135 | 135 | ||
136 | if (symbol__init() < 0) | 136 | if (symbol__init(&session->header.env) < 0) |
137 | return -1; | 137 | return -1; |
138 | 138 | ||
139 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); | 139 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); |
@@ -194,7 +194,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
194 | .lost = perf_event__process_lost, | 194 | .lost = perf_event__process_lost, |
195 | .fork = perf_event__process_fork, | 195 | .fork = perf_event__process_fork, |
196 | .build_id = perf_event__process_build_id, | 196 | .build_id = perf_event__process_build_id, |
197 | .ordered_samples = true, | 197 | .ordered_events = true, |
198 | }, | 198 | }, |
199 | .input_name = "perf.data", | 199 | .input_name = "perf.data", |
200 | }; | 200 | }; |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index c63fa2925075..921bb6942503 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -55,6 +55,7 @@ static struct { | |||
55 | bool show_funcs; | 55 | bool show_funcs; |
56 | bool mod_events; | 56 | bool mod_events; |
57 | bool uprobes; | 57 | bool uprobes; |
58 | bool quiet; | ||
58 | int nevents; | 59 | int nevents; |
59 | struct perf_probe_event events[MAX_PROBES]; | 60 | struct perf_probe_event events[MAX_PROBES]; |
60 | struct strlist *dellist; | 61 | struct strlist *dellist; |
@@ -290,8 +291,11 @@ static void cleanup_params(void) | |||
290 | 291 | ||
291 | static void pr_err_with_code(const char *msg, int err) | 292 | static void pr_err_with_code(const char *msg, int err) |
292 | { | 293 | { |
294 | char sbuf[STRERR_BUFSIZE]; | ||
295 | |||
293 | pr_err("%s", msg); | 296 | pr_err("%s", msg); |
294 | pr_debug(" Reason: %s (Code: %d)", strerror(-err), err); | 297 | pr_debug(" Reason: %s (Code: %d)", |
298 | strerror_r(-err, sbuf, sizeof(sbuf)), err); | ||
295 | pr_err("\n"); | 299 | pr_err("\n"); |
296 | } | 300 | } |
297 | 301 | ||
@@ -309,9 +313,11 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
309 | #endif | 313 | #endif |
310 | NULL | 314 | NULL |
311 | }; | 315 | }; |
312 | const struct option options[] = { | 316 | struct option options[] = { |
313 | OPT_INCR('v', "verbose", &verbose, | 317 | OPT_INCR('v', "verbose", &verbose, |
314 | "be more verbose (show parsed arguments, etc)"), | 318 | "be more verbose (show parsed arguments, etc)"), |
319 | OPT_BOOLEAN('q', "quiet", ¶ms.quiet, | ||
320 | "be quiet (do not show any mesages)"), | ||
315 | OPT_BOOLEAN('l', "list", ¶ms.list_events, | 321 | OPT_BOOLEAN('l', "list", ¶ms.list_events, |
316 | "list up current probe events"), | 322 | "list up current probe events"), |
317 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", | 323 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", |
@@ -372,11 +378,21 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
372 | OPT_CALLBACK('x', "exec", NULL, "executable|path", | 378 | OPT_CALLBACK('x', "exec", NULL, "executable|path", |
373 | "target executable name or path", opt_set_target), | 379 | "target executable name or path", opt_set_target), |
374 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | 380 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, |
375 | "Disable symbol demangling"), | 381 | "Enable symbol demangling"), |
382 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
383 | "Enable kernel symbol demangling"), | ||
376 | OPT_END() | 384 | OPT_END() |
377 | }; | 385 | }; |
378 | int ret; | 386 | int ret; |
379 | 387 | ||
388 | set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE); | ||
389 | set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE); | ||
390 | set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE); | ||
391 | #ifdef HAVE_DWARF_SUPPORT | ||
392 | set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); | ||
393 | set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); | ||
394 | #endif | ||
395 | |||
380 | argc = parse_options(argc, argv, options, probe_usage, | 396 | argc = parse_options(argc, argv, options, probe_usage, |
381 | PARSE_OPT_STOP_AT_NON_OPTION); | 397 | PARSE_OPT_STOP_AT_NON_OPTION); |
382 | if (argc > 0) { | 398 | if (argc > 0) { |
@@ -391,6 +407,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
391 | } | 407 | } |
392 | } | 408 | } |
393 | 409 | ||
410 | if (params.quiet) { | ||
411 | if (verbose != 0) { | ||
412 | pr_err(" Error: -v and -q are exclusive.\n"); | ||
413 | return -EINVAL; | ||
414 | } | ||
415 | verbose = -1; | ||
416 | } | ||
417 | |||
394 | if (params.max_probe_points == 0) | 418 | if (params.max_probe_points == 0) |
395 | params.max_probe_points = MAX_PROBES; | 419 | params.max_probe_points = MAX_PROBES; |
396 | 420 | ||
@@ -404,22 +428,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
404 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 428 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
405 | 429 | ||
406 | if (params.list_events) { | 430 | if (params.list_events) { |
407 | if (params.mod_events) { | ||
408 | pr_err(" Error: Don't use --list with --add/--del.\n"); | ||
409 | usage_with_options(probe_usage, options); | ||
410 | } | ||
411 | if (params.show_lines) { | ||
412 | pr_err(" Error: Don't use --list with --line.\n"); | ||
413 | usage_with_options(probe_usage, options); | ||
414 | } | ||
415 | if (params.show_vars) { | ||
416 | pr_err(" Error: Don't use --list with --vars.\n"); | ||
417 | usage_with_options(probe_usage, options); | ||
418 | } | ||
419 | if (params.show_funcs) { | ||
420 | pr_err(" Error: Don't use --list with --funcs.\n"); | ||
421 | usage_with_options(probe_usage, options); | ||
422 | } | ||
423 | if (params.uprobes) { | 431 | if (params.uprobes) { |
424 | pr_warning(" Error: Don't use --list with --exec.\n"); | 432 | pr_warning(" Error: Don't use --list with --exec.\n"); |
425 | usage_with_options(probe_usage, options); | 433 | usage_with_options(probe_usage, options); |
@@ -430,19 +438,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
430 | return ret; | 438 | return ret; |
431 | } | 439 | } |
432 | if (params.show_funcs) { | 440 | if (params.show_funcs) { |
433 | if (params.nevents != 0 || params.dellist) { | ||
434 | pr_err(" Error: Don't use --funcs with" | ||
435 | " --add/--del.\n"); | ||
436 | usage_with_options(probe_usage, options); | ||
437 | } | ||
438 | if (params.show_lines) { | ||
439 | pr_err(" Error: Don't use --funcs with --line.\n"); | ||
440 | usage_with_options(probe_usage, options); | ||
441 | } | ||
442 | if (params.show_vars) { | ||
443 | pr_err(" Error: Don't use --funcs with --vars.\n"); | ||
444 | usage_with_options(probe_usage, options); | ||
445 | } | ||
446 | if (!params.filter) | 441 | if (!params.filter) |
447 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, | 442 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, |
448 | NULL); | 443 | NULL); |
@@ -457,27 +452,13 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
457 | 452 | ||
458 | #ifdef HAVE_DWARF_SUPPORT | 453 | #ifdef HAVE_DWARF_SUPPORT |
459 | if (params.show_lines) { | 454 | if (params.show_lines) { |
460 | if (params.mod_events) { | 455 | ret = show_line_range(¶ms.line_range, params.target, |
461 | pr_err(" Error: Don't use --line with" | 456 | params.uprobes); |
462 | " --add/--del.\n"); | ||
463 | usage_with_options(probe_usage, options); | ||
464 | } | ||
465 | if (params.show_vars) { | ||
466 | pr_err(" Error: Don't use --line with --vars.\n"); | ||
467 | usage_with_options(probe_usage, options); | ||
468 | } | ||
469 | |||
470 | ret = show_line_range(¶ms.line_range, params.target); | ||
471 | if (ret < 0) | 457 | if (ret < 0) |
472 | pr_err_with_code(" Error: Failed to show lines.", ret); | 458 | pr_err_with_code(" Error: Failed to show lines.", ret); |
473 | return ret; | 459 | return ret; |
474 | } | 460 | } |
475 | if (params.show_vars) { | 461 | if (params.show_vars) { |
476 | if (params.mod_events) { | ||
477 | pr_err(" Error: Don't use --vars with" | ||
478 | " --add/--del.\n"); | ||
479 | usage_with_options(probe_usage, options); | ||
480 | } | ||
481 | if (!params.filter) | 462 | if (!params.filter) |
482 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, | 463 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, |
483 | NULL); | 464 | NULL); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4869050e7194..8648c6d3003d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -14,6 +14,8 @@ | |||
14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
15 | #include "util/parse-events.h" | 15 | #include "util/parse-events.h" |
16 | 16 | ||
17 | #include "util/callchain.h" | ||
18 | #include "util/cgroup.h" | ||
17 | #include "util/header.h" | 19 | #include "util/header.h" |
18 | #include "util/event.h" | 20 | #include "util/event.h" |
19 | #include "util/evlist.h" | 21 | #include "util/evlist.h" |
@@ -65,8 +67,9 @@ static int process_synthesized_event(struct perf_tool *tool, | |||
65 | return record__write(rec, event, event->header.size); | 67 | return record__write(rec, event, event->header.size); |
66 | } | 68 | } |
67 | 69 | ||
68 | static int record__mmap_read(struct record *rec, struct perf_mmap *md) | 70 | static int record__mmap_read(struct record *rec, int idx) |
69 | { | 71 | { |
72 | struct perf_mmap *md = &rec->evlist->mmap[idx]; | ||
70 | unsigned int head = perf_mmap__read_head(md); | 73 | unsigned int head = perf_mmap__read_head(md); |
71 | unsigned int old = md->prev; | 74 | unsigned int old = md->prev; |
72 | unsigned char *data = md->base + page_size; | 75 | unsigned char *data = md->base + page_size; |
@@ -102,8 +105,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md) | |||
102 | } | 105 | } |
103 | 106 | ||
104 | md->prev = old; | 107 | md->prev = old; |
105 | perf_mmap__write_tail(md, old); | 108 | perf_evlist__mmap_consume(rec->evlist, idx); |
106 | |||
107 | out: | 109 | out: |
108 | return rc; | 110 | return rc; |
109 | } | 111 | } |
@@ -161,7 +163,7 @@ try_again: | |||
161 | 163 | ||
162 | if (perf_evlist__apply_filters(evlist)) { | 164 | if (perf_evlist__apply_filters(evlist)) { |
163 | error("failed to set filter with %d (%s)\n", errno, | 165 | error("failed to set filter with %d (%s)\n", errno, |
164 | strerror(errno)); | 166 | strerror_r(errno, msg, sizeof(msg))); |
165 | rc = -1; | 167 | rc = -1; |
166 | goto out; | 168 | goto out; |
167 | } | 169 | } |
@@ -175,7 +177,8 @@ try_again: | |||
175 | "(current value: %u)\n", opts->mmap_pages); | 177 | "(current value: %u)\n", opts->mmap_pages); |
176 | rc = -errno; | 178 | rc = -errno; |
177 | } else { | 179 | } else { |
178 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 180 | pr_err("failed to mmap with %d (%s)\n", errno, |
181 | strerror_r(errno, msg, sizeof(msg))); | ||
179 | rc = -errno; | 182 | rc = -errno; |
180 | } | 183 | } |
181 | goto out; | 184 | goto out; |
@@ -197,6 +200,17 @@ static int process_buildids(struct record *rec) | |||
197 | if (size == 0) | 200 | if (size == 0) |
198 | return 0; | 201 | return 0; |
199 | 202 | ||
203 | /* | ||
204 | * During this process, it'll load kernel map and replace the | ||
205 | * dso->long_name to a real pathname it found. In this case | ||
206 | * we prefer the vmlinux path like | ||
207 | * /lib/modules/3.16.4/build/vmlinux | ||
208 | * | ||
209 | * rather than build-id path (in debug directory). | ||
210 | * $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551 | ||
211 | */ | ||
212 | symbol_conf.ignore_vmlinux_buildid = true; | ||
213 | |||
200 | return __perf_session__process_events(session, start, | 214 | return __perf_session__process_events(session, start, |
201 | size - start, | 215 | size - start, |
202 | size, &build_id__mark_dso_hit_ops); | 216 | size, &build_id__mark_dso_hit_ops); |
@@ -244,7 +258,7 @@ static int record__mmap_read_all(struct record *rec) | |||
244 | 258 | ||
245 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 259 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
246 | if (rec->evlist->mmap[i].base) { | 260 | if (rec->evlist->mmap[i].base) { |
247 | if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { | 261 | if (record__mmap_read(rec, i) != 0) { |
248 | rc = -1; | 262 | rc = -1; |
249 | goto out; | 263 | goto out; |
250 | } | 264 | } |
@@ -307,7 +321,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
307 | struct record_opts *opts = &rec->opts; | 321 | struct record_opts *opts = &rec->opts; |
308 | struct perf_data_file *file = &rec->file; | 322 | struct perf_data_file *file = &rec->file; |
309 | struct perf_session *session; | 323 | struct perf_session *session; |
310 | bool disabled = false; | 324 | bool disabled = false, draining = false; |
311 | 325 | ||
312 | rec->progname = argv[0]; | 326 | rec->progname = argv[0]; |
313 | 327 | ||
@@ -456,9 +470,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
456 | } | 470 | } |
457 | 471 | ||
458 | if (hits == rec->samples) { | 472 | if (hits == rec->samples) { |
459 | if (done) | 473 | if (done || draining) |
460 | break; | 474 | break; |
461 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); | 475 | err = perf_evlist__poll(rec->evlist, -1); |
462 | /* | 476 | /* |
463 | * Propagate error, only if there's any. Ignore positive | 477 | * Propagate error, only if there's any. Ignore positive |
464 | * number of returned events and interrupt error. | 478 | * number of returned events and interrupt error. |
@@ -466,6 +480,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
466 | if (err > 0 || (err < 0 && errno == EINTR)) | 480 | if (err > 0 || (err < 0 && errno == EINTR)) |
467 | err = 0; | 481 | err = 0; |
468 | waking++; | 482 | waking++; |
483 | |||
484 | if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0) | ||
485 | draining = true; | ||
469 | } | 486 | } |
470 | 487 | ||
471 | /* | 488 | /* |
@@ -480,7 +497,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
480 | } | 497 | } |
481 | 498 | ||
482 | if (forks && workload_exec_errno) { | 499 | if (forks && workload_exec_errno) { |
483 | char msg[512]; | 500 | char msg[STRERR_BUFSIZE]; |
484 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); | 501 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); |
485 | pr_err("Workload failed: %s\n", emsg); | 502 | pr_err("Workload failed: %s\n", emsg); |
486 | err = -1; | 503 | err = -1; |
@@ -620,154 +637,66 @@ error: | |||
620 | return ret; | 637 | return ret; |
621 | } | 638 | } |
622 | 639 | ||
623 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 640 | static void callchain_debug(void) |
624 | static int get_stack_size(char *str, unsigned long *_size) | ||
625 | { | ||
626 | char *endptr; | ||
627 | unsigned long size; | ||
628 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
629 | |||
630 | size = strtoul(str, &endptr, 0); | ||
631 | |||
632 | do { | ||
633 | if (*endptr) | ||
634 | break; | ||
635 | |||
636 | size = round_up(size, sizeof(u64)); | ||
637 | if (!size || size > max_size) | ||
638 | break; | ||
639 | |||
640 | *_size = size; | ||
641 | return 0; | ||
642 | |||
643 | } while (0); | ||
644 | |||
645 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", | ||
646 | max_size, str); | ||
647 | return -1; | ||
648 | } | ||
649 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
650 | |||
651 | int record_parse_callchain(const char *arg, struct record_opts *opts) | ||
652 | { | ||
653 | char *tok, *name, *saveptr = NULL; | ||
654 | char *buf; | ||
655 | int ret = -1; | ||
656 | |||
657 | /* We need buffer that we know we can write to. */ | ||
658 | buf = malloc(strlen(arg) + 1); | ||
659 | if (!buf) | ||
660 | return -ENOMEM; | ||
661 | |||
662 | strcpy(buf, arg); | ||
663 | |||
664 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
665 | name = tok ? : (char *)buf; | ||
666 | |||
667 | do { | ||
668 | /* Framepointer style */ | ||
669 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
670 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
671 | opts->call_graph = CALLCHAIN_FP; | ||
672 | ret = 0; | ||
673 | } else | ||
674 | pr_err("callchain: No more arguments " | ||
675 | "needed for -g fp\n"); | ||
676 | break; | ||
677 | |||
678 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
679 | /* Dwarf style */ | ||
680 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | ||
681 | const unsigned long default_stack_dump_size = 8192; | ||
682 | |||
683 | ret = 0; | ||
684 | opts->call_graph = CALLCHAIN_DWARF; | ||
685 | opts->stack_dump_size = default_stack_dump_size; | ||
686 | |||
687 | tok = strtok_r(NULL, ",", &saveptr); | ||
688 | if (tok) { | ||
689 | unsigned long size = 0; | ||
690 | |||
691 | ret = get_stack_size(tok, &size); | ||
692 | opts->stack_dump_size = size; | ||
693 | } | ||
694 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
695 | } else { | ||
696 | pr_err("callchain: Unknown --call-graph option " | ||
697 | "value: %s\n", arg); | ||
698 | break; | ||
699 | } | ||
700 | |||
701 | } while (0); | ||
702 | |||
703 | free(buf); | ||
704 | return ret; | ||
705 | } | ||
706 | |||
707 | static void callchain_debug(struct record_opts *opts) | ||
708 | { | 641 | { |
709 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; | 642 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; |
710 | 643 | ||
711 | pr_debug("callchain: type %s\n", str[opts->call_graph]); | 644 | pr_debug("callchain: type %s\n", str[callchain_param.record_mode]); |
712 | 645 | ||
713 | if (opts->call_graph == CALLCHAIN_DWARF) | 646 | if (callchain_param.record_mode == CALLCHAIN_DWARF) |
714 | pr_debug("callchain: stack dump size %d\n", | 647 | pr_debug("callchain: stack dump size %d\n", |
715 | opts->stack_dump_size); | 648 | callchain_param.dump_size); |
716 | } | 649 | } |
717 | 650 | ||
718 | int record_parse_callchain_opt(const struct option *opt, | 651 | int record_parse_callchain_opt(const struct option *opt __maybe_unused, |
719 | const char *arg, | 652 | const char *arg, |
720 | int unset) | 653 | int unset) |
721 | { | 654 | { |
722 | struct record_opts *opts = opt->value; | ||
723 | int ret; | 655 | int ret; |
724 | 656 | ||
725 | opts->call_graph_enabled = !unset; | 657 | callchain_param.enabled = !unset; |
726 | 658 | ||
727 | /* --no-call-graph */ | 659 | /* --no-call-graph */ |
728 | if (unset) { | 660 | if (unset) { |
729 | opts->call_graph = CALLCHAIN_NONE; | 661 | callchain_param.record_mode = CALLCHAIN_NONE; |
730 | pr_debug("callchain: disabled\n"); | 662 | pr_debug("callchain: disabled\n"); |
731 | return 0; | 663 | return 0; |
732 | } | 664 | } |
733 | 665 | ||
734 | ret = record_parse_callchain(arg, opts); | 666 | ret = parse_callchain_record_opt(arg); |
735 | if (!ret) | 667 | if (!ret) |
736 | callchain_debug(opts); | 668 | callchain_debug(); |
737 | 669 | ||
738 | return ret; | 670 | return ret; |
739 | } | 671 | } |
740 | 672 | ||
741 | int record_callchain_opt(const struct option *opt, | 673 | int record_callchain_opt(const struct option *opt __maybe_unused, |
742 | const char *arg __maybe_unused, | 674 | const char *arg __maybe_unused, |
743 | int unset __maybe_unused) | 675 | int unset __maybe_unused) |
744 | { | 676 | { |
745 | struct record_opts *opts = opt->value; | 677 | callchain_param.enabled = true; |
746 | |||
747 | opts->call_graph_enabled = !unset; | ||
748 | 678 | ||
749 | if (opts->call_graph == CALLCHAIN_NONE) | 679 | if (callchain_param.record_mode == CALLCHAIN_NONE) |
750 | opts->call_graph = CALLCHAIN_FP; | 680 | callchain_param.record_mode = CALLCHAIN_FP; |
751 | 681 | ||
752 | callchain_debug(opts); | 682 | callchain_debug(); |
753 | return 0; | 683 | return 0; |
754 | } | 684 | } |
755 | 685 | ||
756 | static int perf_record_config(const char *var, const char *value, void *cb) | 686 | static int perf_record_config(const char *var, const char *value, void *cb) |
757 | { | 687 | { |
758 | struct record *rec = cb; | ||
759 | |||
760 | if (!strcmp(var, "record.call-graph")) | 688 | if (!strcmp(var, "record.call-graph")) |
761 | return record_parse_callchain(value, &rec->opts); | 689 | var = "call-graph.record-mode"; /* fall-through */ |
762 | 690 | ||
763 | return perf_default_config(var, value, cb); | 691 | return perf_default_config(var, value, cb); |
764 | } | 692 | } |
765 | 693 | ||
766 | static const char * const record_usage[] = { | 694 | static const char * const __record_usage[] = { |
767 | "perf record [<options>] [<command>]", | 695 | "perf record [<options>] [<command>]", |
768 | "perf record [<options>] -- <command> [<options>]", | 696 | "perf record [<options>] -- <command> [<options>]", |
769 | NULL | 697 | NULL |
770 | }; | 698 | }; |
699 | const char * const *record_usage = __record_usage; | ||
771 | 700 | ||
772 | /* | 701 | /* |
773 | * XXX Ideally would be local to cmd_record() and passed to a record__new | 702 | * XXX Ideally would be local to cmd_record() and passed to a record__new |
@@ -781,6 +710,7 @@ static const char * const record_usage[] = { | |||
781 | */ | 710 | */ |
782 | static struct record record = { | 711 | static struct record record = { |
783 | .opts = { | 712 | .opts = { |
713 | .sample_time = true, | ||
784 | .mmap_pages = UINT_MAX, | 714 | .mmap_pages = UINT_MAX, |
785 | .user_freq = UINT_MAX, | 715 | .user_freq = UINT_MAX, |
786 | .user_interval = ULLONG_MAX, | 716 | .user_interval = ULLONG_MAX, |
@@ -807,7 +737,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp"; | |||
807 | * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', | 737 | * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', |
808 | * using pipes, etc. | 738 | * using pipes, etc. |
809 | */ | 739 | */ |
810 | const struct option record_options[] = { | 740 | struct option __record_options[] = { |
811 | OPT_CALLBACK('e', "event", &record.evlist, "event", | 741 | OPT_CALLBACK('e', "event", &record.evlist, "event", |
812 | "event selector. use 'perf list' to list available events", | 742 | "event selector. use 'perf list' to list available events", |
813 | parse_events_option), | 743 | parse_events_option), |
@@ -881,9 +811,13 @@ const struct option record_options[] = { | |||
881 | "sample transaction flags (special events only)"), | 811 | "sample transaction flags (special events only)"), |
882 | OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, | 812 | OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, |
883 | "use per-thread mmaps"), | 813 | "use per-thread mmaps"), |
814 | OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs, | ||
815 | "Sample machine registers on interrupt"), | ||
884 | OPT_END() | 816 | OPT_END() |
885 | }; | 817 | }; |
886 | 818 | ||
819 | struct option *record_options = __record_options; | ||
820 | |||
887 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | 821 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) |
888 | { | 822 | { |
889 | int err = -ENOMEM; | 823 | int err = -ENOMEM; |
@@ -907,7 +841,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
907 | usage_with_options(record_usage, record_options); | 841 | usage_with_options(record_usage, record_options); |
908 | } | 842 | } |
909 | 843 | ||
910 | symbol__init(); | 844 | symbol__init(NULL); |
911 | 845 | ||
912 | if (symbol_conf.kptr_restrict) | 846 | if (symbol_conf.kptr_restrict) |
913 | pr_warning( | 847 | pr_warning( |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 21d830bafff3..072ae8ad67fc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -58,17 +58,19 @@ struct report { | |||
58 | const char *symbol_filter_str; | 58 | const char *symbol_filter_str; |
59 | float min_percent; | 59 | float min_percent; |
60 | u64 nr_entries; | 60 | u64 nr_entries; |
61 | u64 queue_size; | ||
61 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 62 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
62 | }; | 63 | }; |
63 | 64 | ||
64 | static int report__config(const char *var, const char *value, void *cb) | 65 | static int report__config(const char *var, const char *value, void *cb) |
65 | { | 66 | { |
67 | struct report *rep = cb; | ||
68 | |||
66 | if (!strcmp(var, "report.group")) { | 69 | if (!strcmp(var, "report.group")) { |
67 | symbol_conf.event_group = perf_config_bool(var, value); | 70 | symbol_conf.event_group = perf_config_bool(var, value); |
68 | return 0; | 71 | return 0; |
69 | } | 72 | } |
70 | if (!strcmp(var, "report.percent-limit")) { | 73 | if (!strcmp(var, "report.percent-limit")) { |
71 | struct report *rep = cb; | ||
72 | rep->min_percent = strtof(value, NULL); | 74 | rep->min_percent = strtof(value, NULL); |
73 | return 0; | 75 | return 0; |
74 | } | 76 | } |
@@ -76,6 +78,10 @@ static int report__config(const char *var, const char *value, void *cb) | |||
76 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 78 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
77 | return 0; | 79 | return 0; |
78 | } | 80 | } |
81 | if (!strcmp(var, "report.queue-size")) { | ||
82 | rep->queue_size = perf_config_u64(var, value); | ||
83 | return 0; | ||
84 | } | ||
79 | 85 | ||
80 | return perf_default_config(var, value, cb); | 86 | return perf_default_config(var, value, cb); |
81 | } | 87 | } |
@@ -220,8 +226,9 @@ static int report__setup_sample_type(struct report *rep) | |||
220 | return -EINVAL; | 226 | return -EINVAL; |
221 | } | 227 | } |
222 | if (symbol_conf.use_callchain) { | 228 | if (symbol_conf.use_callchain) { |
223 | ui__error("Selected -g but no callchain data. Did " | 229 | ui__error("Selected -g or --branch-history but no " |
224 | "you call 'perf record' without -g?\n"); | 230 | "callchain data. Did\n" |
231 | "you call 'perf record' without -g?\n"); | ||
225 | return -1; | 232 | return -1; |
226 | } | 233 | } |
227 | } else if (!rep->dont_use_callchains && | 234 | } else if (!rep->dont_use_callchains && |
@@ -251,6 +258,13 @@ static int report__setup_sample_type(struct report *rep) | |||
251 | } | 258 | } |
252 | } | 259 | } |
253 | 260 | ||
261 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { | ||
262 | if ((sample_type & PERF_SAMPLE_REGS_USER) && | ||
263 | (sample_type & PERF_SAMPLE_STACK_USER)) | ||
264 | callchain_param.record_mode = CALLCHAIN_DWARF; | ||
265 | else | ||
266 | callchain_param.record_mode = CALLCHAIN_FP; | ||
267 | } | ||
254 | return 0; | 268 | return 0; |
255 | } | 269 | } |
256 | 270 | ||
@@ -282,12 +296,14 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
282 | evname = buf; | 296 | evname = buf; |
283 | 297 | ||
284 | for_each_group_member(pos, evsel) { | 298 | for_each_group_member(pos, evsel) { |
299 | const struct hists *pos_hists = evsel__hists(pos); | ||
300 | |||
285 | if (symbol_conf.filter_relative) { | 301 | if (symbol_conf.filter_relative) { |
286 | nr_samples += pos->hists.stats.nr_non_filtered_samples; | 302 | nr_samples += pos_hists->stats.nr_non_filtered_samples; |
287 | nr_events += pos->hists.stats.total_non_filtered_period; | 303 | nr_events += pos_hists->stats.total_non_filtered_period; |
288 | } else { | 304 | } else { |
289 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 305 | nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
290 | nr_events += pos->hists.stats.total_period; | 306 | nr_events += pos_hists->stats.total_period; |
291 | } | 307 | } |
292 | } | 308 | } |
293 | } | 309 | } |
@@ -312,7 +328,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
312 | struct perf_evsel *pos; | 328 | struct perf_evsel *pos; |
313 | 329 | ||
314 | evlist__for_each(evlist, pos) { | 330 | evlist__for_each(evlist, pos) { |
315 | struct hists *hists = &pos->hists; | 331 | struct hists *hists = evsel__hists(pos); |
316 | const char *evname = perf_evsel__name(pos); | 332 | const char *evname = perf_evsel__name(pos); |
317 | 333 | ||
318 | if (symbol_conf.event_group && | 334 | if (symbol_conf.event_group && |
@@ -421,7 +437,7 @@ static void report__collapse_hists(struct report *rep) | |||
421 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); | 437 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); |
422 | 438 | ||
423 | evlist__for_each(rep->session->evlist, pos) { | 439 | evlist__for_each(rep->session->evlist, pos) { |
424 | struct hists *hists = &pos->hists; | 440 | struct hists *hists = evsel__hists(pos); |
425 | 441 | ||
426 | if (pos->idx == 0) | 442 | if (pos->idx == 0) |
427 | hists->symbol_filter_str = rep->symbol_filter_str; | 443 | hists->symbol_filter_str = rep->symbol_filter_str; |
@@ -431,7 +447,7 @@ static void report__collapse_hists(struct report *rep) | |||
431 | /* Non-group events are considered as leader */ | 447 | /* Non-group events are considered as leader */ |
432 | if (symbol_conf.event_group && | 448 | if (symbol_conf.event_group && |
433 | !perf_evsel__is_group_leader(pos)) { | 449 | !perf_evsel__is_group_leader(pos)) { |
434 | struct hists *leader_hists = &pos->leader->hists; | 450 | struct hists *leader_hists = evsel__hists(pos->leader); |
435 | 451 | ||
436 | hists__match(leader_hists, hists); | 452 | hists__match(leader_hists, hists); |
437 | hists__link(leader_hists, hists); | 453 | hists__link(leader_hists, hists); |
@@ -441,6 +457,19 @@ static void report__collapse_hists(struct report *rep) | |||
441 | ui_progress__finish(); | 457 | ui_progress__finish(); |
442 | } | 458 | } |
443 | 459 | ||
460 | static void report__output_resort(struct report *rep) | ||
461 | { | ||
462 | struct ui_progress prog; | ||
463 | struct perf_evsel *pos; | ||
464 | |||
465 | ui_progress__init(&prog, rep->nr_entries, "Sorting events for output..."); | ||
466 | |||
467 | evlist__for_each(rep->session->evlist, pos) | ||
468 | hists__output_resort(evsel__hists(pos), &prog); | ||
469 | |||
470 | ui_progress__finish(); | ||
471 | } | ||
472 | |||
444 | static int __cmd_report(struct report *rep) | 473 | static int __cmd_report(struct report *rep) |
445 | { | 474 | { |
446 | int ret; | 475 | int ret; |
@@ -479,6 +508,7 @@ static int __cmd_report(struct report *rep) | |||
479 | 508 | ||
480 | if (dump_trace) { | 509 | if (dump_trace) { |
481 | perf_session__fprintf_nr_events(session, stdout); | 510 | perf_session__fprintf_nr_events(session, stdout); |
511 | perf_evlist__fprintf_nr_events(session->evlist, stdout); | ||
482 | return 0; | 512 | return 0; |
483 | } | 513 | } |
484 | } | 514 | } |
@@ -488,13 +518,20 @@ static int __cmd_report(struct report *rep) | |||
488 | if (session_done()) | 518 | if (session_done()) |
489 | return 0; | 519 | return 0; |
490 | 520 | ||
521 | /* | ||
522 | * recalculate number of entries after collapsing since it | ||
523 | * might be changed during the collapse phase. | ||
524 | */ | ||
525 | rep->nr_entries = 0; | ||
526 | evlist__for_each(session->evlist, pos) | ||
527 | rep->nr_entries += evsel__hists(pos)->nr_entries; | ||
528 | |||
491 | if (rep->nr_entries == 0) { | 529 | if (rep->nr_entries == 0) { |
492 | ui__error("The %s file has no samples!\n", file->path); | 530 | ui__error("The %s file has no samples!\n", file->path); |
493 | return 0; | 531 | return 0; |
494 | } | 532 | } |
495 | 533 | ||
496 | evlist__for_each(session->evlist, pos) | 534 | report__output_resort(rep); |
497 | hists__output_resort(&pos->hists); | ||
498 | 535 | ||
499 | return report__browse_hists(rep); | 536 | return report__browse_hists(rep); |
500 | } | 537 | } |
@@ -559,7 +596,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
559 | struct stat st; | 596 | struct stat st; |
560 | bool has_br_stack = false; | 597 | bool has_br_stack = false; |
561 | int branch_mode = -1; | 598 | int branch_mode = -1; |
562 | int ret = -1; | 599 | bool branch_call_mode = false; |
563 | char callchain_default_opt[] = "fractal,0.5,callee"; | 600 | char callchain_default_opt[] = "fractal,0.5,callee"; |
564 | const char * const report_usage[] = { | 601 | const char * const report_usage[] = { |
565 | "perf report [<options>]", | 602 | "perf report [<options>]", |
@@ -578,7 +615,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
578 | .attr = perf_event__process_attr, | 615 | .attr = perf_event__process_attr, |
579 | .tracing_data = perf_event__process_tracing_data, | 616 | .tracing_data = perf_event__process_tracing_data, |
580 | .build_id = perf_event__process_build_id, | 617 | .build_id = perf_event__process_build_id, |
581 | .ordered_samples = true, | 618 | .ordered_events = true, |
582 | .ordering_requires_timestamps = true, | 619 | .ordering_requires_timestamps = true, |
583 | }, | 620 | }, |
584 | .max_stack = PERF_MAX_STACK_DEPTH, | 621 | .max_stack = PERF_MAX_STACK_DEPTH, |
@@ -622,8 +659,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
622 | "regex filter to identify parent, see: '--sort parent'"), | 659 | "regex filter to identify parent, see: '--sort parent'"), |
623 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 660 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
624 | "Only display entries with parent-match"), | 661 | "Only display entries with parent-match"), |
625 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 662 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order[,branch]", |
626 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " | 663 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address), add branches. " |
627 | "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), | 664 | "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), |
628 | OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, | 665 | OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, |
629 | "Accumulate callchains of children and show total overhead as well"), | 666 | "Accumulate callchains of children and show total overhead as well"), |
@@ -669,11 +706,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
669 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, | 706 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, |
670 | "Show event group information together"), | 707 | "Show event group information together"), |
671 | OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "", | 708 | OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "", |
672 | "use branch records for histogram filling", parse_branch_mode), | 709 | "use branch records for per branch histogram filling", |
710 | parse_branch_mode), | ||
711 | OPT_BOOLEAN(0, "branch-history", &branch_call_mode, | ||
712 | "add last branch records to call history"), | ||
673 | OPT_STRING(0, "objdump", &objdump_path, "path", | 713 | OPT_STRING(0, "objdump", &objdump_path, "path", |
674 | "objdump binary to use for disassembly and annotations"), | 714 | "objdump binary to use for disassembly and annotations"), |
675 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | 715 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, |
676 | "Disable symbol demangling"), | 716 | "Disable symbol demangling"), |
717 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
718 | "Enable kernel symbol demangling"), | ||
677 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | 719 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), |
678 | OPT_CALLBACK(0, "percent-limit", &report, "percent", | 720 | OPT_CALLBACK(0, "percent-limit", &report, "percent", |
679 | "Don't show entries under that percent", parse_percent_limit), | 721 | "Don't show entries under that percent", parse_percent_limit), |
@@ -684,6 +726,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
684 | struct perf_data_file file = { | 726 | struct perf_data_file file = { |
685 | .mode = PERF_DATA_MODE_READ, | 727 | .mode = PERF_DATA_MODE_READ, |
686 | }; | 728 | }; |
729 | int ret = hists__init(); | ||
730 | |||
731 | if (ret < 0) | ||
732 | return ret; | ||
687 | 733 | ||
688 | perf_config(report__config, &report); | 734 | perf_config(report__config, &report); |
689 | 735 | ||
@@ -712,17 +758,36 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
712 | repeat: | 758 | repeat: |
713 | session = perf_session__new(&file, false, &report.tool); | 759 | session = perf_session__new(&file, false, &report.tool); |
714 | if (session == NULL) | 760 | if (session == NULL) |
715 | return -ENOMEM; | 761 | return -1; |
762 | |||
763 | if (report.queue_size) { | ||
764 | ordered_events__set_alloc_size(&session->ordered_events, | ||
765 | report.queue_size); | ||
766 | } | ||
716 | 767 | ||
717 | report.session = session; | 768 | report.session = session; |
718 | 769 | ||
719 | has_br_stack = perf_header__has_feat(&session->header, | 770 | has_br_stack = perf_header__has_feat(&session->header, |
720 | HEADER_BRANCH_STACK); | 771 | HEADER_BRANCH_STACK); |
721 | 772 | ||
722 | if (branch_mode == -1 && has_br_stack) { | 773 | /* |
774 | * Branch mode is a tristate: | ||
775 | * -1 means default, so decide based on the file having branch data. | ||
776 | * 0/1 means the user chose a mode. | ||
777 | */ | ||
778 | if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) && | ||
779 | branch_call_mode == -1) { | ||
723 | sort__mode = SORT_MODE__BRANCH; | 780 | sort__mode = SORT_MODE__BRANCH; |
724 | symbol_conf.cumulate_callchain = false; | 781 | symbol_conf.cumulate_callchain = false; |
725 | } | 782 | } |
783 | if (branch_call_mode) { | ||
784 | callchain_param.key = CCKEY_ADDRESS; | ||
785 | callchain_param.branch_callstack = 1; | ||
786 | symbol_conf.use_callchain = true; | ||
787 | callchain_register_param(&callchain_param); | ||
788 | if (sort_order == NULL) | ||
789 | sort_order = "srcline,symbol,dso"; | ||
790 | } | ||
726 | 791 | ||
727 | if (report.mem_mode) { | 792 | if (report.mem_mode) { |
728 | if (sort__mode == SORT_MODE__BRANCH) { | 793 | if (sort__mode == SORT_MODE__BRANCH) { |
@@ -787,7 +852,7 @@ repeat: | |||
787 | } | 852 | } |
788 | } | 853 | } |
789 | 854 | ||
790 | if (symbol__init() < 0) | 855 | if (symbol__init(&session->header.env) < 0) |
791 | goto error; | 856 | goto error; |
792 | 857 | ||
793 | if (argc) { | 858 | if (argc) { |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f83c08c0dd87..891c3930080e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -428,6 +428,7 @@ static u64 get_cpu_usage_nsec_parent(void) | |||
428 | static int self_open_counters(void) | 428 | static int self_open_counters(void) |
429 | { | 429 | { |
430 | struct perf_event_attr attr; | 430 | struct perf_event_attr attr; |
431 | char sbuf[STRERR_BUFSIZE]; | ||
431 | int fd; | 432 | int fd; |
432 | 433 | ||
433 | memset(&attr, 0, sizeof(attr)); | 434 | memset(&attr, 0, sizeof(attr)); |
@@ -440,7 +441,8 @@ static int self_open_counters(void) | |||
440 | 441 | ||
441 | if (fd < 0) | 442 | if (fd < 0) |
442 | pr_err("Error: sys_perf_event_open() syscall returned " | 443 | pr_err("Error: sys_perf_event_open() syscall returned " |
443 | "with %d (%s)\n", fd, strerror(errno)); | 444 | "with %d (%s)\n", fd, |
445 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
444 | return fd; | 446 | return fd; |
445 | } | 447 | } |
446 | 448 | ||
@@ -1429,9 +1431,6 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ | |||
1429 | { | 1431 | { |
1430 | int err = 0; | 1432 | int err = 0; |
1431 | 1433 | ||
1432 | evsel->hists.stats.total_period += sample->period; | ||
1433 | hists__inc_nr_samples(&evsel->hists, true); | ||
1434 | |||
1435 | if (evsel->handler != NULL) { | 1434 | if (evsel->handler != NULL) { |
1436 | tracepoint_handler f = evsel->handler; | 1435 | tracepoint_handler f = evsel->handler; |
1437 | err = f(tool, evsel, sample, machine); | 1436 | err = f(tool, evsel, sample, machine); |
@@ -1462,6 +1461,8 @@ static int perf_sched__read_events(struct perf_sched *sched, | |||
1462 | return -1; | 1461 | return -1; |
1463 | } | 1462 | } |
1464 | 1463 | ||
1464 | symbol__init(&session->header.env); | ||
1465 | |||
1465 | if (perf_session__set_tracepoints_handlers(session, handlers)) | 1466 | if (perf_session__set_tracepoints_handlers(session, handlers)) |
1466 | goto out_delete; | 1467 | goto out_delete; |
1467 | 1468 | ||
@@ -1662,7 +1663,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1662 | .comm = perf_event__process_comm, | 1663 | .comm = perf_event__process_comm, |
1663 | .lost = perf_event__process_lost, | 1664 | .lost = perf_event__process_lost, |
1664 | .fork = perf_sched__process_fork_event, | 1665 | .fork = perf_sched__process_fork_event, |
1665 | .ordered_samples = true, | 1666 | .ordered_events = true, |
1666 | }, | 1667 | }, |
1667 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | 1668 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), |
1668 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | 1669 | .sort_list = LIST_HEAD_INIT(sched.sort_list), |
@@ -1747,7 +1748,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1747 | if (!strcmp(argv[0], "script")) | 1748 | if (!strcmp(argv[0], "script")) |
1748 | return cmd_script(argc, argv, prefix); | 1749 | return cmd_script(argc, argv, prefix); |
1749 | 1750 | ||
1750 | symbol__init(); | ||
1751 | if (!strncmp(argv[0], "rec", 3)) { | 1751 | if (!strncmp(argv[0], "rec", 3)) { |
1752 | return __cmd_record(argc, argv); | 1752 | return __cmd_record(argc, argv); |
1753 | } else if (!strncmp(argv[0], "lat", 3)) { | 1753 | } else if (!strncmp(argv[0], "lat", 3)) { |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f57035b89c15..ce304dfd962a 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -23,7 +23,6 @@ static char const *generate_script_lang; | |||
23 | static bool debug_mode; | 23 | static bool debug_mode; |
24 | static u64 last_timestamp; | 24 | static u64 last_timestamp; |
25 | static u64 nr_unordered; | 25 | static u64 nr_unordered; |
26 | extern const struct option record_options[]; | ||
27 | static bool no_callchain; | 26 | static bool no_callchain; |
28 | static bool latency_format; | 27 | static bool latency_format; |
29 | static bool system_wide; | 28 | static bool system_wide; |
@@ -44,6 +43,7 @@ enum perf_output_field { | |||
44 | PERF_OUTPUT_ADDR = 1U << 10, | 43 | PERF_OUTPUT_ADDR = 1U << 10, |
45 | PERF_OUTPUT_SYMOFFSET = 1U << 11, | 44 | PERF_OUTPUT_SYMOFFSET = 1U << 11, |
46 | PERF_OUTPUT_SRCLINE = 1U << 12, | 45 | PERF_OUTPUT_SRCLINE = 1U << 12, |
46 | PERF_OUTPUT_PERIOD = 1U << 13, | ||
47 | }; | 47 | }; |
48 | 48 | ||
49 | struct output_option { | 49 | struct output_option { |
@@ -63,6 +63,7 @@ struct output_option { | |||
63 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | 63 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, |
64 | {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, | 64 | {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, |
65 | {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, | 65 | {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, |
66 | {.str = "period", .field = PERF_OUTPUT_PERIOD}, | ||
66 | }; | 67 | }; |
67 | 68 | ||
68 | /* default set to maintain compatibility with current format */ | 69 | /* default set to maintain compatibility with current format */ |
@@ -80,7 +81,8 @@ static struct { | |||
80 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 81 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
81 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 82 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
82 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 83 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
83 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 84 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
85 | PERF_OUTPUT_PERIOD, | ||
84 | 86 | ||
85 | .invalid_fields = PERF_OUTPUT_TRACE, | 87 | .invalid_fields = PERF_OUTPUT_TRACE, |
86 | }, | 88 | }, |
@@ -91,7 +93,8 @@ static struct { | |||
91 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 93 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
92 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 94 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
93 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 95 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
94 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 96 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
97 | PERF_OUTPUT_PERIOD, | ||
95 | 98 | ||
96 | .invalid_fields = PERF_OUTPUT_TRACE, | 99 | .invalid_fields = PERF_OUTPUT_TRACE, |
97 | }, | 100 | }, |
@@ -110,7 +113,8 @@ static struct { | |||
110 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 113 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
111 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 114 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
112 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 115 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
113 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 116 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
117 | PERF_OUTPUT_PERIOD, | ||
114 | 118 | ||
115 | .invalid_fields = PERF_OUTPUT_TRACE, | 119 | .invalid_fields = PERF_OUTPUT_TRACE, |
116 | }, | 120 | }, |
@@ -184,10 +188,6 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
184 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", | 188 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", |
185 | PERF_OUTPUT_IP)) | 189 | PERF_OUTPUT_IP)) |
186 | return -EINVAL; | 190 | return -EINVAL; |
187 | |||
188 | if (!no_callchain && | ||
189 | !(attr->sample_type & PERF_SAMPLE_CALLCHAIN)) | ||
190 | symbol_conf.use_callchain = false; | ||
191 | } | 191 | } |
192 | 192 | ||
193 | if (PRINT_FIELD(ADDR) && | 193 | if (PRINT_FIELD(ADDR) && |
@@ -233,6 +233,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
233 | PERF_OUTPUT_CPU)) | 233 | PERF_OUTPUT_CPU)) |
234 | return -EINVAL; | 234 | return -EINVAL; |
235 | 235 | ||
236 | if (PRINT_FIELD(PERIOD) && | ||
237 | perf_evsel__check_stype(evsel, PERF_SAMPLE_PERIOD, "PERIOD", | ||
238 | PERF_OUTPUT_PERIOD)) | ||
239 | return -EINVAL; | ||
240 | |||
236 | return 0; | 241 | return 0; |
237 | } | 242 | } |
238 | 243 | ||
@@ -290,6 +295,19 @@ static int perf_session__check_output_opt(struct perf_session *session) | |||
290 | set_print_ip_opts(&evsel->attr); | 295 | set_print_ip_opts(&evsel->attr); |
291 | } | 296 | } |
292 | 297 | ||
298 | if (!no_callchain) { | ||
299 | bool use_callchain = false; | ||
300 | |||
301 | evlist__for_each(session->evlist, evsel) { | ||
302 | if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
303 | use_callchain = true; | ||
304 | break; | ||
305 | } | ||
306 | } | ||
307 | if (!use_callchain) | ||
308 | symbol_conf.use_callchain = false; | ||
309 | } | ||
310 | |||
293 | /* | 311 | /* |
294 | * set default for tracepoints to print symbols only | 312 | * set default for tracepoints to print symbols only |
295 | * if callchains are present | 313 | * if callchains are present |
@@ -360,7 +378,6 @@ static void print_sample_start(struct perf_sample *sample, | |||
360 | 378 | ||
361 | static void print_sample_addr(union perf_event *event, | 379 | static void print_sample_addr(union perf_event *event, |
362 | struct perf_sample *sample, | 380 | struct perf_sample *sample, |
363 | struct machine *machine, | ||
364 | struct thread *thread, | 381 | struct thread *thread, |
365 | struct perf_event_attr *attr) | 382 | struct perf_event_attr *attr) |
366 | { | 383 | { |
@@ -371,7 +388,7 @@ static void print_sample_addr(union perf_event *event, | |||
371 | if (!sample_addr_correlates_sym(attr)) | 388 | if (!sample_addr_correlates_sym(attr)) |
372 | return; | 389 | return; |
373 | 390 | ||
374 | perf_event__preprocess_sample_addr(event, sample, machine, thread, &al); | 391 | perf_event__preprocess_sample_addr(event, sample, thread, &al); |
375 | 392 | ||
376 | if (PRINT_FIELD(SYM)) { | 393 | if (PRINT_FIELD(SYM)) { |
377 | printf(" "); | 394 | printf(" "); |
@@ -419,7 +436,7 @@ static void print_sample_bts(union perf_event *event, | |||
419 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | 436 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && |
420 | !output[attr->type].user_set)) { | 437 | !output[attr->type].user_set)) { |
421 | printf(" => "); | 438 | printf(" => "); |
422 | print_sample_addr(event, sample, al->machine, thread, attr); | 439 | print_sample_addr(event, sample, thread, attr); |
423 | } | 440 | } |
424 | 441 | ||
425 | if (print_srcline_last) | 442 | if (print_srcline_last) |
@@ -439,6 +456,9 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
439 | 456 | ||
440 | print_sample_start(sample, thread, evsel); | 457 | print_sample_start(sample, thread, evsel); |
441 | 458 | ||
459 | if (PRINT_FIELD(PERIOD)) | ||
460 | printf("%10" PRIu64 " ", sample->period); | ||
461 | |||
442 | if (PRINT_FIELD(EVNAME)) { | 462 | if (PRINT_FIELD(EVNAME)) { |
443 | const char *evname = perf_evsel__name(evsel); | 463 | const char *evname = perf_evsel__name(evsel); |
444 | printf("%s: ", evname ? evname : "[unknown]"); | 464 | printf("%s: ", evname ? evname : "[unknown]"); |
@@ -453,7 +473,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
453 | event_format__print(evsel->tp_format, sample->cpu, | 473 | event_format__print(evsel->tp_format, sample->cpu, |
454 | sample->raw_data, sample->raw_size); | 474 | sample->raw_data, sample->raw_size); |
455 | if (PRINT_FIELD(ADDR)) | 475 | if (PRINT_FIELD(ADDR)) |
456 | print_sample_addr(event, sample, al->machine, thread, attr); | 476 | print_sample_addr(event, sample, thread, attr); |
457 | 477 | ||
458 | if (PRINT_FIELD(IP)) { | 478 | if (PRINT_FIELD(IP)) { |
459 | if (!symbol_conf.use_callchain) | 479 | if (!symbol_conf.use_callchain) |
@@ -476,6 +496,11 @@ static int default_start_script(const char *script __maybe_unused, | |||
476 | return 0; | 496 | return 0; |
477 | } | 497 | } |
478 | 498 | ||
499 | static int default_flush_script(void) | ||
500 | { | ||
501 | return 0; | ||
502 | } | ||
503 | |||
479 | static int default_stop_script(void) | 504 | static int default_stop_script(void) |
480 | { | 505 | { |
481 | return 0; | 506 | return 0; |
@@ -489,6 +514,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused, | |||
489 | 514 | ||
490 | static struct scripting_ops default_scripting_ops = { | 515 | static struct scripting_ops default_scripting_ops = { |
491 | .start_script = default_start_script, | 516 | .start_script = default_start_script, |
517 | .flush_script = default_flush_script, | ||
492 | .stop_script = default_stop_script, | 518 | .stop_script = default_stop_script, |
493 | .process_event = process_event, | 519 | .process_event = process_event, |
494 | .generate_script = default_generate_script, | 520 | .generate_script = default_generate_script, |
@@ -504,6 +530,11 @@ static void setup_scripting(void) | |||
504 | scripting_ops = &default_scripting_ops; | 530 | scripting_ops = &default_scripting_ops; |
505 | } | 531 | } |
506 | 532 | ||
533 | static int flush_scripting(void) | ||
534 | { | ||
535 | return scripting_ops->flush_script(); | ||
536 | } | ||
537 | |||
507 | static int cleanup_scripting(void) | 538 | static int cleanup_scripting(void) |
508 | { | 539 | { |
509 | pr_debug("\nperf script stopped\n"); | 540 | pr_debug("\nperf script stopped\n"); |
@@ -552,7 +583,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
552 | 583 | ||
553 | scripting_ops->process_event(event, sample, evsel, thread, &al); | 584 | scripting_ops->process_event(event, sample, evsel, thread, &al); |
554 | 585 | ||
555 | evsel->hists.stats.total_period += sample->period; | ||
556 | return 0; | 586 | return 0; |
557 | } | 587 | } |
558 | 588 | ||
@@ -1471,12 +1501,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1471 | bool show_full_info = false; | 1501 | bool show_full_info = false; |
1472 | bool header = false; | 1502 | bool header = false; |
1473 | bool header_only = false; | 1503 | bool header_only = false; |
1504 | bool script_started = false; | ||
1474 | char *rec_script_path = NULL; | 1505 | char *rec_script_path = NULL; |
1475 | char *rep_script_path = NULL; | 1506 | char *rep_script_path = NULL; |
1476 | struct perf_session *session; | 1507 | struct perf_session *session; |
1477 | char *script_path = NULL; | 1508 | char *script_path = NULL; |
1478 | const char **__argv; | 1509 | const char **__argv; |
1479 | int i, j, err; | 1510 | int i, j, err = 0; |
1480 | struct perf_script script = { | 1511 | struct perf_script script = { |
1481 | .tool = { | 1512 | .tool = { |
1482 | .sample = process_sample_event, | 1513 | .sample = process_sample_event, |
@@ -1488,7 +1519,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1488 | .attr = process_attr, | 1519 | .attr = process_attr, |
1489 | .tracing_data = perf_event__process_tracing_data, | 1520 | .tracing_data = perf_event__process_tracing_data, |
1490 | .build_id = perf_event__process_build_id, | 1521 | .build_id = perf_event__process_build_id, |
1491 | .ordered_samples = true, | 1522 | .ordered_events = true, |
1492 | .ordering_requires_timestamps = true, | 1523 | .ordering_requires_timestamps = true, |
1493 | }, | 1524 | }, |
1494 | }; | 1525 | }; |
@@ -1523,7 +1554,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1523 | "comma separated output fields prepend with 'type:'. " | 1554 | "comma separated output fields prepend with 'type:'. " |
1524 | "Valid types: hw,sw,trace,raw. " | 1555 | "Valid types: hw,sw,trace,raw. " |
1525 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," | 1556 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," |
1526 | "addr,symoff", parse_output_fields), | 1557 | "addr,symoff,period", parse_output_fields), |
1527 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1558 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1528 | "system-wide collection from all CPUs"), | 1559 | "system-wide collection from all CPUs"), |
1529 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 1560 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
@@ -1718,26 +1749,28 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1718 | exit(-1); | 1749 | exit(-1); |
1719 | } | 1750 | } |
1720 | 1751 | ||
1721 | if (symbol__init() < 0) | ||
1722 | return -1; | ||
1723 | if (!script_name) | 1752 | if (!script_name) |
1724 | setup_pager(); | 1753 | setup_pager(); |
1725 | 1754 | ||
1726 | session = perf_session__new(&file, false, &script.tool); | 1755 | session = perf_session__new(&file, false, &script.tool); |
1727 | if (session == NULL) | 1756 | if (session == NULL) |
1728 | return -ENOMEM; | 1757 | return -1; |
1729 | 1758 | ||
1730 | if (header || header_only) { | 1759 | if (header || header_only) { |
1731 | perf_session__fprintf_info(session, stdout, show_full_info); | 1760 | perf_session__fprintf_info(session, stdout, show_full_info); |
1732 | if (header_only) | 1761 | if (header_only) |
1733 | return 0; | 1762 | goto out_delete; |
1734 | } | 1763 | } |
1735 | 1764 | ||
1765 | if (symbol__init(&session->header.env) < 0) | ||
1766 | goto out_delete; | ||
1767 | |||
1736 | script.session = session; | 1768 | script.session = session; |
1737 | 1769 | ||
1738 | if (cpu_list) { | 1770 | if (cpu_list) { |
1739 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | 1771 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); |
1740 | return -1; | 1772 | if (err < 0) |
1773 | goto out_delete; | ||
1741 | } | 1774 | } |
1742 | 1775 | ||
1743 | if (!no_callchain) | 1776 | if (!no_callchain) |
@@ -1752,53 +1785,62 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1752 | if (output_set_by_user()) { | 1785 | if (output_set_by_user()) { |
1753 | fprintf(stderr, | 1786 | fprintf(stderr, |
1754 | "custom fields not supported for generated scripts"); | 1787 | "custom fields not supported for generated scripts"); |
1755 | return -1; | 1788 | err = -EINVAL; |
1789 | goto out_delete; | ||
1756 | } | 1790 | } |
1757 | 1791 | ||
1758 | input = open(file.path, O_RDONLY); /* input_name */ | 1792 | input = open(file.path, O_RDONLY); /* input_name */ |
1759 | if (input < 0) { | 1793 | if (input < 0) { |
1794 | err = -errno; | ||
1760 | perror("failed to open file"); | 1795 | perror("failed to open file"); |
1761 | return -1; | 1796 | goto out_delete; |
1762 | } | 1797 | } |
1763 | 1798 | ||
1764 | err = fstat(input, &perf_stat); | 1799 | err = fstat(input, &perf_stat); |
1765 | if (err < 0) { | 1800 | if (err < 0) { |
1766 | perror("failed to stat file"); | 1801 | perror("failed to stat file"); |
1767 | return -1; | 1802 | goto out_delete; |
1768 | } | 1803 | } |
1769 | 1804 | ||
1770 | if (!perf_stat.st_size) { | 1805 | if (!perf_stat.st_size) { |
1771 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1806 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
1772 | return 0; | 1807 | goto out_delete; |
1773 | } | 1808 | } |
1774 | 1809 | ||
1775 | scripting_ops = script_spec__lookup(generate_script_lang); | 1810 | scripting_ops = script_spec__lookup(generate_script_lang); |
1776 | if (!scripting_ops) { | 1811 | if (!scripting_ops) { |
1777 | fprintf(stderr, "invalid language specifier"); | 1812 | fprintf(stderr, "invalid language specifier"); |
1778 | return -1; | 1813 | err = -ENOENT; |
1814 | goto out_delete; | ||
1779 | } | 1815 | } |
1780 | 1816 | ||
1781 | err = scripting_ops->generate_script(session->tevent.pevent, | 1817 | err = scripting_ops->generate_script(session->tevent.pevent, |
1782 | "perf-script"); | 1818 | "perf-script"); |
1783 | goto out; | 1819 | goto out_delete; |
1784 | } | 1820 | } |
1785 | 1821 | ||
1786 | if (script_name) { | 1822 | if (script_name) { |
1787 | err = scripting_ops->start_script(script_name, argc, argv); | 1823 | err = scripting_ops->start_script(script_name, argc, argv); |
1788 | if (err) | 1824 | if (err) |
1789 | goto out; | 1825 | goto out_delete; |
1790 | pr_debug("perf script started with script %s\n\n", script_name); | 1826 | pr_debug("perf script started with script %s\n\n", script_name); |
1827 | script_started = true; | ||
1791 | } | 1828 | } |
1792 | 1829 | ||
1793 | 1830 | ||
1794 | err = perf_session__check_output_opt(session); | 1831 | err = perf_session__check_output_opt(session); |
1795 | if (err < 0) | 1832 | if (err < 0) |
1796 | goto out; | 1833 | goto out_delete; |
1797 | 1834 | ||
1798 | err = __cmd_script(&script); | 1835 | err = __cmd_script(&script); |
1799 | 1836 | ||
1837 | flush_scripting(); | ||
1838 | |||
1839 | out_delete: | ||
1800 | perf_session__delete(session); | 1840 | perf_session__delete(session); |
1801 | cleanup_scripting(); | 1841 | |
1842 | if (script_started) | ||
1843 | cleanup_scripting(); | ||
1802 | out: | 1844 | out: |
1803 | return err; | 1845 | return err; |
1804 | } | 1846 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3e80aa10cfd8..891086376381 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -43,6 +43,7 @@ | |||
43 | 43 | ||
44 | #include "perf.h" | 44 | #include "perf.h" |
45 | #include "builtin.h" | 45 | #include "builtin.h" |
46 | #include "util/cgroup.h" | ||
46 | #include "util/util.h" | 47 | #include "util/util.h" |
47 | #include "util/parse-options.h" | 48 | #include "util/parse-options.h" |
48 | #include "util/parse-events.h" | 49 | #include "util/parse-events.h" |
@@ -387,20 +388,102 @@ static void update_shadow_stats(struct perf_evsel *counter, u64 *count) | |||
387 | update_stats(&runtime_itlb_cache_stats[0], count[0]); | 388 | update_stats(&runtime_itlb_cache_stats[0], count[0]); |
388 | } | 389 | } |
389 | 390 | ||
391 | static void zero_per_pkg(struct perf_evsel *counter) | ||
392 | { | ||
393 | if (counter->per_pkg_mask) | ||
394 | memset(counter->per_pkg_mask, 0, MAX_NR_CPUS); | ||
395 | } | ||
396 | |||
397 | static int check_per_pkg(struct perf_evsel *counter, int cpu, bool *skip) | ||
398 | { | ||
399 | unsigned long *mask = counter->per_pkg_mask; | ||
400 | struct cpu_map *cpus = perf_evsel__cpus(counter); | ||
401 | int s; | ||
402 | |||
403 | *skip = false; | ||
404 | |||
405 | if (!counter->per_pkg) | ||
406 | return 0; | ||
407 | |||
408 | if (cpu_map__empty(cpus)) | ||
409 | return 0; | ||
410 | |||
411 | if (!mask) { | ||
412 | mask = zalloc(MAX_NR_CPUS); | ||
413 | if (!mask) | ||
414 | return -ENOMEM; | ||
415 | |||
416 | counter->per_pkg_mask = mask; | ||
417 | } | ||
418 | |||
419 | s = cpu_map__get_socket(cpus, cpu); | ||
420 | if (s < 0) | ||
421 | return -1; | ||
422 | |||
423 | *skip = test_and_set_bit(s, mask) == 1; | ||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused, | ||
428 | struct perf_counts_values *count) | ||
429 | { | ||
430 | struct perf_counts_values *aggr = &evsel->counts->aggr; | ||
431 | static struct perf_counts_values zero; | ||
432 | bool skip = false; | ||
433 | |||
434 | if (check_per_pkg(evsel, cpu, &skip)) { | ||
435 | pr_err("failed to read per-pkg counter\n"); | ||
436 | return -1; | ||
437 | } | ||
438 | |||
439 | if (skip) | ||
440 | count = &zero; | ||
441 | |||
442 | switch (aggr_mode) { | ||
443 | case AGGR_CORE: | ||
444 | case AGGR_SOCKET: | ||
445 | case AGGR_NONE: | ||
446 | if (!evsel->snapshot) | ||
447 | perf_evsel__compute_deltas(evsel, cpu, count); | ||
448 | perf_counts_values__scale(count, scale, NULL); | ||
449 | evsel->counts->cpu[cpu] = *count; | ||
450 | update_shadow_stats(evsel, count->values); | ||
451 | break; | ||
452 | case AGGR_GLOBAL: | ||
453 | aggr->val += count->val; | ||
454 | if (scale) { | ||
455 | aggr->ena += count->ena; | ||
456 | aggr->run += count->run; | ||
457 | } | ||
458 | default: | ||
459 | break; | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int read_counter(struct perf_evsel *counter); | ||
466 | |||
390 | /* | 467 | /* |
391 | * Read out the results of a single counter: | 468 | * Read out the results of a single counter: |
392 | * aggregate counts across CPUs in system-wide mode | 469 | * aggregate counts across CPUs in system-wide mode |
393 | */ | 470 | */ |
394 | static int read_counter_aggr(struct perf_evsel *counter) | 471 | static int read_counter_aggr(struct perf_evsel *counter) |
395 | { | 472 | { |
473 | struct perf_counts_values *aggr = &counter->counts->aggr; | ||
396 | struct perf_stat *ps = counter->priv; | 474 | struct perf_stat *ps = counter->priv; |
397 | u64 *count = counter->counts->aggr.values; | 475 | u64 *count = counter->counts->aggr.values; |
398 | int i; | 476 | int i; |
399 | 477 | ||
400 | if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), | 478 | aggr->val = aggr->ena = aggr->run = 0; |
401 | thread_map__nr(evsel_list->threads), scale) < 0) | 479 | |
480 | if (read_counter(counter)) | ||
402 | return -1; | 481 | return -1; |
403 | 482 | ||
483 | if (!counter->snapshot) | ||
484 | perf_evsel__compute_deltas(counter, -1, aggr); | ||
485 | perf_counts_values__scale(aggr, scale, &counter->counts->scaled); | ||
486 | |||
404 | for (i = 0; i < 3; i++) | 487 | for (i = 0; i < 3; i++) |
405 | update_stats(&ps->res_stats[i], count[i]); | 488 | update_stats(&ps->res_stats[i], count[i]); |
406 | 489 | ||
@@ -423,16 +506,21 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
423 | */ | 506 | */ |
424 | static int read_counter(struct perf_evsel *counter) | 507 | static int read_counter(struct perf_evsel *counter) |
425 | { | 508 | { |
426 | u64 *count; | 509 | int nthreads = thread_map__nr(evsel_list->threads); |
427 | int cpu; | 510 | int ncpus = perf_evsel__nr_cpus(counter); |
511 | int cpu, thread; | ||
428 | 512 | ||
429 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | 513 | if (counter->system_wide) |
430 | if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0) | 514 | nthreads = 1; |
431 | return -1; | ||
432 | 515 | ||
433 | count = counter->counts->cpu[cpu].values; | 516 | if (counter->per_pkg) |
517 | zero_per_pkg(counter); | ||
434 | 518 | ||
435 | update_shadow_stats(counter, count); | 519 | for (thread = 0; thread < nthreads; thread++) { |
520 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
521 | if (perf_evsel__read_cb(counter, cpu, thread, read_cb)) | ||
522 | return -1; | ||
523 | } | ||
436 | } | 524 | } |
437 | 525 | ||
438 | return 0; | 526 | return 0; |
@@ -593,7 +681,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
593 | 681 | ||
594 | if (perf_evlist__apply_filters(evsel_list)) { | 682 | if (perf_evlist__apply_filters(evsel_list)) { |
595 | error("failed to set filter with %d (%s)\n", errno, | 683 | error("failed to set filter with %d (%s)\n", errno, |
596 | strerror(errno)); | 684 | strerror_r(errno, msg, sizeof(msg))); |
597 | return -1; | 685 | return -1; |
598 | } | 686 | } |
599 | 687 | ||
@@ -732,7 +820,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
732 | } | 820 | } |
733 | } | 821 | } |
734 | 822 | ||
735 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 823 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
736 | { | 824 | { |
737 | double msecs = avg / 1e6; | 825 | double msecs = avg / 1e6; |
738 | const char *fmt_v, *fmt_n; | 826 | const char *fmt_v, *fmt_n; |
@@ -741,7 +829,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | |||
741 | fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; | 829 | fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; |
742 | fmt_n = csv_output ? "%s" : "%-25s"; | 830 | fmt_n = csv_output ? "%s" : "%-25s"; |
743 | 831 | ||
744 | aggr_printout(evsel, cpu, nr); | 832 | aggr_printout(evsel, id, nr); |
745 | 833 | ||
746 | scnprintf(name, sizeof(name), "%s%s", | 834 | scnprintf(name, sizeof(name), "%s%s", |
747 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); | 835 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); |
@@ -947,11 +1035,12 @@ static void print_ll_cache_misses(int cpu, | |||
947 | fprintf(output, " of all LL-cache hits "); | 1035 | fprintf(output, " of all LL-cache hits "); |
948 | } | 1036 | } |
949 | 1037 | ||
950 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 1038 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
951 | { | 1039 | { |
952 | double total, ratio = 0.0, total2; | 1040 | double total, ratio = 0.0, total2; |
953 | double sc = evsel->scale; | 1041 | double sc = evsel->scale; |
954 | const char *fmt; | 1042 | const char *fmt; |
1043 | int cpu = cpu_map__id_to_cpu(id); | ||
955 | 1044 | ||
956 | if (csv_output) { | 1045 | if (csv_output) { |
957 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; | 1046 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; |
@@ -962,7 +1051,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | |||
962 | fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; | 1051 | fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; |
963 | } | 1052 | } |
964 | 1053 | ||
965 | aggr_printout(evsel, cpu, nr); | 1054 | aggr_printout(evsel, id, nr); |
966 | 1055 | ||
967 | if (aggr_mode == AGGR_GLOBAL) | 1056 | if (aggr_mode == AGGR_GLOBAL) |
968 | cpu = 0; | 1057 | cpu = 0; |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 2f1a5220c090..f3bb1a4bf060 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -528,7 +528,7 @@ static const char *cat_backtrace(union perf_event *event, | |||
528 | } | 528 | } |
529 | 529 | ||
530 | tal.filtered = 0; | 530 | tal.filtered = 0; |
531 | thread__find_addr_location(al.thread, machine, cpumode, | 531 | thread__find_addr_location(al.thread, cpumode, |
532 | MAP__FUNCTION, ip, &tal); | 532 | MAP__FUNCTION, ip, &tal); |
533 | 533 | ||
534 | if (tal.sym) | 534 | if (tal.sym) |
@@ -1605,7 +1605,9 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) | |||
1605 | int ret = -EINVAL; | 1605 | int ret = -EINVAL; |
1606 | 1606 | ||
1607 | if (session == NULL) | 1607 | if (session == NULL) |
1608 | return -ENOMEM; | 1608 | return -1; |
1609 | |||
1610 | symbol__init(&session->header.env); | ||
1609 | 1611 | ||
1610 | (void)perf_header__process_sections(&session->header, | 1612 | (void)perf_header__process_sections(&session->header, |
1611 | perf_data_file__fd(session->file), | 1613 | perf_data_file__fd(session->file), |
@@ -1920,7 +1922,7 @@ int cmd_timechart(int argc, const char **argv, | |||
1920 | .fork = process_fork_event, | 1922 | .fork = process_fork_event, |
1921 | .exit = process_exit_event, | 1923 | .exit = process_exit_event, |
1922 | .sample = process_sample_event, | 1924 | .sample = process_sample_event, |
1923 | .ordered_samples = true, | 1925 | .ordered_events = true, |
1924 | }, | 1926 | }, |
1925 | .proc_num = 15, | 1927 | .proc_num = 15, |
1926 | .min_time = 1000000, | 1928 | .min_time = 1000000, |
@@ -1961,7 +1963,7 @@ int cmd_timechart(int argc, const char **argv, | |||
1961 | NULL | 1963 | NULL |
1962 | }; | 1964 | }; |
1963 | 1965 | ||
1964 | const struct option record_options[] = { | 1966 | const struct option timechart_record_options[] = { |
1965 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), | 1967 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), |
1966 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, | 1968 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, |
1967 | "output processes data only"), | 1969 | "output processes data only"), |
@@ -1970,7 +1972,7 @@ int cmd_timechart(int argc, const char **argv, | |||
1970 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), | 1972 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), |
1971 | OPT_END() | 1973 | OPT_END() |
1972 | }; | 1974 | }; |
1973 | const char * const record_usage[] = { | 1975 | const char * const timechart_record_usage[] = { |
1974 | "perf timechart record [<options>]", | 1976 | "perf timechart record [<options>]", |
1975 | NULL | 1977 | NULL |
1976 | }; | 1978 | }; |
@@ -1982,10 +1984,9 @@ int cmd_timechart(int argc, const char **argv, | |||
1982 | return -1; | 1984 | return -1; |
1983 | } | 1985 | } |
1984 | 1986 | ||
1985 | symbol__init(); | ||
1986 | |||
1987 | if (argc && !strncmp(argv[0], "rec", 3)) { | 1987 | if (argc && !strncmp(argv[0], "rec", 3)) { |
1988 | argc = parse_options(argc, argv, record_options, record_usage, | 1988 | argc = parse_options(argc, argv, timechart_record_options, |
1989 | timechart_record_usage, | ||
1989 | PARSE_OPT_STOP_AT_NON_OPTION); | 1990 | PARSE_OPT_STOP_AT_NON_OPTION); |
1990 | 1991 | ||
1991 | if (tchart.power_only && tchart.tasks_only) { | 1992 | if (tchart.power_only && tchart.tasks_only) { |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 377971dc89a3..961cea183a83 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -59,7 +59,7 @@ | |||
59 | 59 | ||
60 | #include <sys/syscall.h> | 60 | #include <sys/syscall.h> |
61 | #include <sys/ioctl.h> | 61 | #include <sys/ioctl.h> |
62 | #include <sys/poll.h> | 62 | #include <poll.h> |
63 | #include <sys/prctl.h> | 63 | #include <sys/prctl.h> |
64 | #include <sys/wait.h> | 64 | #include <sys/wait.h> |
65 | #include <sys/uio.h> | 65 | #include <sys/uio.h> |
@@ -251,6 +251,7 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
251 | char bf[160]; | 251 | char bf[160]; |
252 | int printed = 0; | 252 | int printed = 0; |
253 | const int win_width = top->winsize.ws_col - 1; | 253 | const int win_width = top->winsize.ws_col - 1; |
254 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
254 | 255 | ||
255 | puts(CONSOLE_CLEAR); | 256 | puts(CONSOLE_CLEAR); |
256 | 257 | ||
@@ -261,13 +262,13 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
261 | 262 | ||
262 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 263 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
263 | 264 | ||
264 | if (top->sym_evsel->hists.stats.nr_lost_warned != | 265 | if (hists->stats.nr_lost_warned != |
265 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { | 266 | hists->stats.nr_events[PERF_RECORD_LOST]) { |
266 | top->sym_evsel->hists.stats.nr_lost_warned = | 267 | hists->stats.nr_lost_warned = |
267 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 268 | hists->stats.nr_events[PERF_RECORD_LOST]; |
268 | color_fprintf(stdout, PERF_COLOR_RED, | 269 | color_fprintf(stdout, PERF_COLOR_RED, |
269 | "WARNING: LOST %d chunks, Check IO/CPU overload", | 270 | "WARNING: LOST %d chunks, Check IO/CPU overload", |
270 | top->sym_evsel->hists.stats.nr_lost_warned); | 271 | hists->stats.nr_lost_warned); |
271 | ++printed; | 272 | ++printed; |
272 | } | 273 | } |
273 | 274 | ||
@@ -276,16 +277,19 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
276 | return; | 277 | return; |
277 | } | 278 | } |
278 | 279 | ||
279 | hists__collapse_resort(&top->sym_evsel->hists, NULL); | 280 | if (top->zero) { |
280 | hists__output_resort(&top->sym_evsel->hists); | 281 | hists__delete_entries(hists); |
281 | hists__decay_entries(&top->sym_evsel->hists, | 282 | } else { |
282 | top->hide_user_symbols, | 283 | hists__decay_entries(hists, top->hide_user_symbols, |
283 | top->hide_kernel_symbols); | 284 | top->hide_kernel_symbols); |
284 | hists__output_recalc_col_len(&top->sym_evsel->hists, | 285 | } |
285 | top->print_entries - printed); | 286 | |
287 | hists__collapse_resort(hists, NULL); | ||
288 | hists__output_resort(hists, NULL); | ||
289 | |||
290 | hists__output_recalc_col_len(hists, top->print_entries - printed); | ||
286 | putchar('\n'); | 291 | putchar('\n'); |
287 | hists__fprintf(&top->sym_evsel->hists, false, | 292 | hists__fprintf(hists, false, top->print_entries - printed, win_width, |
288 | top->print_entries - printed, win_width, | ||
289 | top->min_percent, stdout); | 293 | top->min_percent, stdout); |
290 | } | 294 | } |
291 | 295 | ||
@@ -328,6 +332,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
328 | { | 332 | { |
329 | char *buf = malloc(0), *p; | 333 | char *buf = malloc(0), *p; |
330 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; | 334 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; |
335 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
331 | struct rb_node *next; | 336 | struct rb_node *next; |
332 | size_t dummy = 0; | 337 | size_t dummy = 0; |
333 | 338 | ||
@@ -345,7 +350,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
345 | if (p) | 350 | if (p) |
346 | *p = 0; | 351 | *p = 0; |
347 | 352 | ||
348 | next = rb_first(&top->sym_evsel->hists.entries); | 353 | next = rb_first(&hists->entries); |
349 | while (next) { | 354 | while (next) { |
350 | n = rb_entry(next, struct hist_entry, rb_node); | 355 | n = rb_entry(next, struct hist_entry, rb_node); |
351 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | 356 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { |
@@ -427,18 +432,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
427 | 432 | ||
428 | if (!perf_top__key_mapped(top, c)) { | 433 | if (!perf_top__key_mapped(top, c)) { |
429 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 434 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
430 | struct termios tc, save; | 435 | struct termios save; |
431 | 436 | ||
432 | perf_top__print_mapped_keys(top); | 437 | perf_top__print_mapped_keys(top); |
433 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | 438 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
434 | fflush(stdout); | 439 | fflush(stdout); |
435 | 440 | ||
436 | tcgetattr(0, &save); | 441 | set_term_quiet_input(&save); |
437 | tc = save; | ||
438 | tc.c_lflag &= ~(ICANON | ECHO); | ||
439 | tc.c_cc[VMIN] = 0; | ||
440 | tc.c_cc[VTIME] = 0; | ||
441 | tcsetattr(0, TCSANOW, &tc); | ||
442 | 442 | ||
443 | poll(&stdin_poll, 1, -1); | 443 | poll(&stdin_poll, 1, -1); |
444 | c = getc(stdin); | 444 | c = getc(stdin); |
@@ -537,16 +537,24 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
537 | static void perf_top__sort_new_samples(void *arg) | 537 | static void perf_top__sort_new_samples(void *arg) |
538 | { | 538 | { |
539 | struct perf_top *t = arg; | 539 | struct perf_top *t = arg; |
540 | struct hists *hists; | ||
541 | |||
540 | perf_top__reset_sample_counters(t); | 542 | perf_top__reset_sample_counters(t); |
541 | 543 | ||
542 | if (t->evlist->selected != NULL) | 544 | if (t->evlist->selected != NULL) |
543 | t->sym_evsel = t->evlist->selected; | 545 | t->sym_evsel = t->evlist->selected; |
544 | 546 | ||
545 | hists__collapse_resort(&t->sym_evsel->hists, NULL); | 547 | hists = evsel__hists(t->sym_evsel); |
546 | hists__output_resort(&t->sym_evsel->hists); | 548 | |
547 | hists__decay_entries(&t->sym_evsel->hists, | 549 | if (t->zero) { |
548 | t->hide_user_symbols, | 550 | hists__delete_entries(hists); |
549 | t->hide_kernel_symbols); | 551 | } else { |
552 | hists__decay_entries(hists, t->hide_user_symbols, | ||
553 | t->hide_kernel_symbols); | ||
554 | } | ||
555 | |||
556 | hists__collapse_resort(hists, NULL); | ||
557 | hists__output_resort(hists, NULL); | ||
550 | } | 558 | } |
551 | 559 | ||
552 | static void *display_thread_tui(void *arg) | 560 | static void *display_thread_tui(void *arg) |
@@ -567,8 +575,10 @@ static void *display_thread_tui(void *arg) | |||
567 | * Zooming in/out UIDs. For now juse use whatever the user passed | 575 | * Zooming in/out UIDs. For now juse use whatever the user passed |
568 | * via --uid. | 576 | * via --uid. |
569 | */ | 577 | */ |
570 | evlist__for_each(top->evlist, pos) | 578 | evlist__for_each(top->evlist, pos) { |
571 | pos->hists.uid_filter_str = top->record_opts.target.uid_str; | 579 | struct hists *hists = evsel__hists(pos); |
580 | hists->uid_filter_str = top->record_opts.target.uid_str; | ||
581 | } | ||
572 | 582 | ||
573 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, | 583 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, |
574 | &top->session->header.env); | 584 | &top->session->header.env); |
@@ -577,23 +587,32 @@ static void *display_thread_tui(void *arg) | |||
577 | return NULL; | 587 | return NULL; |
578 | } | 588 | } |
579 | 589 | ||
590 | static void display_sig(int sig __maybe_unused) | ||
591 | { | ||
592 | done = 1; | ||
593 | } | ||
594 | |||
595 | static void display_setup_sig(void) | ||
596 | { | ||
597 | signal(SIGSEGV, display_sig); | ||
598 | signal(SIGFPE, display_sig); | ||
599 | signal(SIGINT, display_sig); | ||
600 | signal(SIGQUIT, display_sig); | ||
601 | signal(SIGTERM, display_sig); | ||
602 | } | ||
603 | |||
580 | static void *display_thread(void *arg) | 604 | static void *display_thread(void *arg) |
581 | { | 605 | { |
582 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 606 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
583 | struct termios tc, save; | 607 | struct termios save; |
584 | struct perf_top *top = arg; | 608 | struct perf_top *top = arg; |
585 | int delay_msecs, c; | 609 | int delay_msecs, c; |
586 | 610 | ||
587 | tcgetattr(0, &save); | 611 | display_setup_sig(); |
588 | tc = save; | ||
589 | tc.c_lflag &= ~(ICANON | ECHO); | ||
590 | tc.c_cc[VMIN] = 0; | ||
591 | tc.c_cc[VTIME] = 0; | ||
592 | |||
593 | pthread__unblock_sigwinch(); | 612 | pthread__unblock_sigwinch(); |
594 | repeat: | 613 | repeat: |
595 | delay_msecs = top->delay_secs * 1000; | 614 | delay_msecs = top->delay_secs * 1000; |
596 | tcsetattr(0, TCSANOW, &tc); | 615 | set_term_quiet_input(&save); |
597 | /* trash return*/ | 616 | /* trash return*/ |
598 | getc(stdin); | 617 | getc(stdin); |
599 | 618 | ||
@@ -620,13 +639,16 @@ repeat: | |||
620 | } | 639 | } |
621 | } | 640 | } |
622 | 641 | ||
642 | tcsetattr(0, TCSAFLUSH, &save); | ||
623 | return NULL; | 643 | return NULL; |
624 | } | 644 | } |
625 | 645 | ||
626 | static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) | 646 | static int symbol_filter(struct map *map, struct symbol *sym) |
627 | { | 647 | { |
628 | const char *name = sym->name; | 648 | const char *name = sym->name; |
629 | 649 | ||
650 | if (!map->dso->kernel) | ||
651 | return 0; | ||
630 | /* | 652 | /* |
631 | * ppc64 uses function descriptors and appends a '.' to the | 653 | * ppc64 uses function descriptors and appends a '.' to the |
632 | * start of every instruction address. Remove it. | 654 | * start of every instruction address. Remove it. |
@@ -750,6 +772,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
750 | } | 772 | } |
751 | 773 | ||
752 | if (al.sym == NULL || !al.sym->ignore) { | 774 | if (al.sym == NULL || !al.sym->ignore) { |
775 | struct hists *hists = evsel__hists(evsel); | ||
753 | struct hist_entry_iter iter = { | 776 | struct hist_entry_iter iter = { |
754 | .add_entry_cb = hist_iter__top_callback, | 777 | .add_entry_cb = hist_iter__top_callback, |
755 | }; | 778 | }; |
@@ -759,14 +782,14 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
759 | else | 782 | else |
760 | iter.ops = &hist_iter_normal; | 783 | iter.ops = &hist_iter_normal; |
761 | 784 | ||
762 | pthread_mutex_lock(&evsel->hists.lock); | 785 | pthread_mutex_lock(&hists->lock); |
763 | 786 | ||
764 | err = hist_entry_iter__add(&iter, &al, evsel, sample, | 787 | err = hist_entry_iter__add(&iter, &al, evsel, sample, |
765 | top->max_stack, top); | 788 | top->max_stack, top); |
766 | if (err < 0) | 789 | if (err < 0) |
767 | pr_err("Problem incrementing symbol period, skipping event\n"); | 790 | pr_err("Problem incrementing symbol period, skipping event\n"); |
768 | 791 | ||
769 | pthread_mutex_unlock(&evsel->hists.lock); | 792 | pthread_mutex_unlock(&hists->lock); |
770 | } | 793 | } |
771 | 794 | ||
772 | return; | 795 | return; |
@@ -831,7 +854,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
831 | perf_event__process_sample(&top->tool, event, evsel, | 854 | perf_event__process_sample(&top->tool, event, evsel, |
832 | &sample, machine); | 855 | &sample, machine); |
833 | } else if (event->header.type < PERF_RECORD_MAX) { | 856 | } else if (event->header.type < PERF_RECORD_MAX) { |
834 | hists__inc_nr_events(&evsel->hists, event->header.type); | 857 | hists__inc_nr_events(evsel__hists(evsel), event->header.type); |
835 | machine__process_event(machine, event, &sample); | 858 | machine__process_event(machine, event, &sample); |
836 | } else | 859 | } else |
837 | ++session->stats.nr_unknown_events; | 860 | ++session->stats.nr_unknown_events; |
@@ -876,7 +899,7 @@ try_again: | |||
876 | 899 | ||
877 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 900 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
878 | ui__error("Failed to mmap with %d (%s)\n", | 901 | ui__error("Failed to mmap with %d (%s)\n", |
879 | errno, strerror(errno)); | 902 | errno, strerror_r(errno, msg, sizeof(msg))); |
880 | goto out_err; | 903 | goto out_err; |
881 | } | 904 | } |
882 | 905 | ||
@@ -911,7 +934,7 @@ static int __cmd_top(struct perf_top *top) | |||
911 | 934 | ||
912 | top->session = perf_session__new(NULL, false, NULL); | 935 | top->session = perf_session__new(NULL, false, NULL); |
913 | if (top->session == NULL) | 936 | if (top->session == NULL) |
914 | return -ENOMEM; | 937 | return -1; |
915 | 938 | ||
916 | machines__set_symbol_filter(&top->session->machines, symbol_filter); | 939 | machines__set_symbol_filter(&top->session->machines, symbol_filter); |
917 | 940 | ||
@@ -946,7 +969,7 @@ static int __cmd_top(struct perf_top *top) | |||
946 | perf_evlist__enable(top->evlist); | 969 | perf_evlist__enable(top->evlist); |
947 | 970 | ||
948 | /* Wait for a minimal set of events before starting the snapshot */ | 971 | /* Wait for a minimal set of events before starting the snapshot */ |
949 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 972 | perf_evlist__poll(top->evlist, 100); |
950 | 973 | ||
951 | perf_top__mmap_read(top); | 974 | perf_top__mmap_read(top); |
952 | 975 | ||
@@ -963,7 +986,7 @@ static int __cmd_top(struct perf_top *top) | |||
963 | param.sched_priority = top->realtime_prio; | 986 | param.sched_priority = top->realtime_prio; |
964 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 987 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
965 | ui__error("Could not set realtime priority.\n"); | 988 | ui__error("Could not set realtime priority.\n"); |
966 | goto out_delete; | 989 | goto out_join; |
967 | } | 990 | } |
968 | } | 991 | } |
969 | 992 | ||
@@ -973,10 +996,12 @@ static int __cmd_top(struct perf_top *top) | |||
973 | perf_top__mmap_read(top); | 996 | perf_top__mmap_read(top); |
974 | 997 | ||
975 | if (hits == top->samples) | 998 | if (hits == top->samples) |
976 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 999 | ret = perf_evlist__poll(top->evlist, 100); |
977 | } | 1000 | } |
978 | 1001 | ||
979 | ret = 0; | 1002 | ret = 0; |
1003 | out_join: | ||
1004 | pthread_join(thread, NULL); | ||
980 | out_delete: | 1005 | out_delete: |
981 | perf_session__delete(top->session); | 1006 | perf_session__delete(top->session); |
982 | top->session = NULL; | 1007 | top->session = NULL; |
@@ -1000,10 +1025,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
1000 | 1025 | ||
1001 | static int perf_top_config(const char *var, const char *value, void *cb) | 1026 | static int perf_top_config(const char *var, const char *value, void *cb) |
1002 | { | 1027 | { |
1003 | struct perf_top *top = cb; | ||
1004 | |||
1005 | if (!strcmp(var, "top.call-graph")) | 1028 | if (!strcmp(var, "top.call-graph")) |
1006 | return record_parse_callchain(value, &top->record_opts); | 1029 | var = "call-graph.record-mode"; /* fall-through */ |
1007 | if (!strcmp(var, "top.children")) { | 1030 | if (!strcmp(var, "top.children")) { |
1008 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 1031 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
1009 | return 0; | 1032 | return 0; |
@@ -1024,7 +1047,6 @@ parse_percent_limit(const struct option *opt, const char *arg, | |||
1024 | 1047 | ||
1025 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | 1048 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) |
1026 | { | 1049 | { |
1027 | int status = -1; | ||
1028 | char errbuf[BUFSIZ]; | 1050 | char errbuf[BUFSIZ]; |
1029 | struct perf_top top = { | 1051 | struct perf_top top = { |
1030 | .count_filter = 5, | 1052 | .count_filter = 5, |
@@ -1122,6 +1144,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1122 | "Interleave source code with assembly code (default)"), | 1144 | "Interleave source code with assembly code (default)"), |
1123 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | 1145 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
1124 | "Display raw encoding of assembly instructions (default)"), | 1146 | "Display raw encoding of assembly instructions (default)"), |
1147 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
1148 | "Enable kernel symbol demangling"), | ||
1125 | OPT_STRING(0, "objdump", &objdump_path, "path", | 1149 | OPT_STRING(0, "objdump", &objdump_path, "path", |
1126 | "objdump binary to use for disassembly and annotations"), | 1150 | "objdump binary to use for disassembly and annotations"), |
1127 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1151 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
@@ -1131,12 +1155,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1131 | "Don't show entries under that percent", parse_percent_limit), | 1155 | "Don't show entries under that percent", parse_percent_limit), |
1132 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 1156 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
1133 | "How to display percentage of filtered entries", parse_filter_percentage), | 1157 | "How to display percentage of filtered entries", parse_filter_percentage), |
1158 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | ||
1159 | "width[,width...]", | ||
1160 | "don't try to adjust column width, use these fixed values"), | ||
1134 | OPT_END() | 1161 | OPT_END() |
1135 | }; | 1162 | }; |
1136 | const char * const top_usage[] = { | 1163 | const char * const top_usage[] = { |
1137 | "perf top [<options>]", | 1164 | "perf top [<options>]", |
1138 | NULL | 1165 | NULL |
1139 | }; | 1166 | }; |
1167 | int status = hists__init(); | ||
1168 | |||
1169 | if (status < 0) | ||
1170 | return status; | ||
1140 | 1171 | ||
1141 | top.evlist = perf_evlist__new(); | 1172 | top.evlist = perf_evlist__new(); |
1142 | if (top.evlist == NULL) | 1173 | if (top.evlist == NULL) |
@@ -1217,7 +1248,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1217 | symbol_conf.priv_size = sizeof(struct annotation); | 1248 | symbol_conf.priv_size = sizeof(struct annotation); |
1218 | 1249 | ||
1219 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1250 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1220 | if (symbol__init() < 0) | 1251 | if (symbol__init(NULL) < 0) |
1221 | return -1; | 1252 | return -1; |
1222 | 1253 | ||
1223 | sort__setup_elide(stdout); | 1254 | sort__setup_elide(stdout); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index a6c375224f46..badfabc6a01f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -402,6 +402,31 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, | |||
402 | 402 | ||
403 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | 403 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags |
404 | 404 | ||
405 | static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, | ||
406 | struct syscall_arg *arg) | ||
407 | { | ||
408 | int printed = 0, flags = arg->val; | ||
409 | |||
410 | #define P_MREMAP_FLAG(n) \ | ||
411 | if (flags & MREMAP_##n) { \ | ||
412 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
413 | flags &= ~MREMAP_##n; \ | ||
414 | } | ||
415 | |||
416 | P_MREMAP_FLAG(MAYMOVE); | ||
417 | #ifdef MREMAP_FIXED | ||
418 | P_MREMAP_FLAG(FIXED); | ||
419 | #endif | ||
420 | #undef P_MREMAP_FLAG | ||
421 | |||
422 | if (flags) | ||
423 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
424 | |||
425 | return printed; | ||
426 | } | ||
427 | |||
428 | #define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags | ||
429 | |||
405 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, | 430 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, |
406 | struct syscall_arg *arg) | 431 | struct syscall_arg *arg) |
407 | { | 432 | { |
@@ -1004,6 +1029,7 @@ static struct syscall_fmt { | |||
1004 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | 1029 | [2] = SCA_MMAP_PROT, /* prot */ }, }, |
1005 | { .name = "mremap", .hexret = true, | 1030 | { .name = "mremap", .hexret = true, |
1006 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 1031 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
1032 | [3] = SCA_MREMAP_FLAGS, /* flags */ | ||
1007 | [4] = SCA_HEX, /* new_addr */ }, }, | 1033 | [4] = SCA_HEX, /* new_addr */ }, }, |
1008 | { .name = "munlock", .errmsg = true, | 1034 | { .name = "munlock", .errmsg = true, |
1009 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | 1035 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, |
@@ -1163,13 +1189,13 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) | |||
1163 | if (thread == NULL) | 1189 | if (thread == NULL) |
1164 | goto fail; | 1190 | goto fail; |
1165 | 1191 | ||
1166 | if (thread->priv == NULL) | 1192 | if (thread__priv(thread) == NULL) |
1167 | thread->priv = thread_trace__new(); | 1193 | thread__set_priv(thread, thread_trace__new()); |
1168 | 1194 | ||
1169 | if (thread->priv == NULL) | 1195 | if (thread__priv(thread) == NULL) |
1170 | goto fail; | 1196 | goto fail; |
1171 | 1197 | ||
1172 | ttrace = thread->priv; | 1198 | ttrace = thread__priv(thread); |
1173 | ++ttrace->nr_events; | 1199 | ++ttrace->nr_events; |
1174 | 1200 | ||
1175 | return ttrace; | 1201 | return ttrace; |
@@ -1222,7 +1248,7 @@ struct trace { | |||
1222 | 1248 | ||
1223 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) | 1249 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) |
1224 | { | 1250 | { |
1225 | struct thread_trace *ttrace = thread->priv; | 1251 | struct thread_trace *ttrace = thread__priv(thread); |
1226 | 1252 | ||
1227 | if (fd > ttrace->paths.max) { | 1253 | if (fd > ttrace->paths.max) { |
1228 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); | 1254 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); |
@@ -1275,7 +1301,7 @@ static int thread__read_fd_path(struct thread *thread, int fd) | |||
1275 | static const char *thread__fd_path(struct thread *thread, int fd, | 1301 | static const char *thread__fd_path(struct thread *thread, int fd, |
1276 | struct trace *trace) | 1302 | struct trace *trace) |
1277 | { | 1303 | { |
1278 | struct thread_trace *ttrace = thread->priv; | 1304 | struct thread_trace *ttrace = thread__priv(thread); |
1279 | 1305 | ||
1280 | if (ttrace == NULL) | 1306 | if (ttrace == NULL) |
1281 | return NULL; | 1307 | return NULL; |
@@ -1312,7 +1338,7 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, | |||
1312 | { | 1338 | { |
1313 | int fd = arg->val; | 1339 | int fd = arg->val; |
1314 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); | 1340 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); |
1315 | struct thread_trace *ttrace = arg->thread->priv; | 1341 | struct thread_trace *ttrace = thread__priv(arg->thread); |
1316 | 1342 | ||
1317 | if (ttrace && fd >= 0 && fd <= ttrace->paths.max) | 1343 | if (ttrace && fd >= 0 && fd <= ttrace->paths.max) |
1318 | zfree(&ttrace->paths.table[fd]); | 1344 | zfree(&ttrace->paths.table[fd]); |
@@ -1385,7 +1411,7 @@ static int trace__tool_process(struct perf_tool *tool, | |||
1385 | 1411 | ||
1386 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 1412 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
1387 | { | 1413 | { |
1388 | int err = symbol__init(); | 1414 | int err = symbol__init(NULL); |
1389 | 1415 | ||
1390 | if (err) | 1416 | if (err) |
1391 | return err; | 1417 | return err; |
@@ -1669,7 +1695,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1669 | union perf_event *event __maybe_unused, | 1695 | union perf_event *event __maybe_unused, |
1670 | struct perf_sample *sample) | 1696 | struct perf_sample *sample) |
1671 | { | 1697 | { |
1672 | int ret; | 1698 | long ret; |
1673 | u64 duration = 0; | 1699 | u64 duration = 0; |
1674 | struct thread *thread; | 1700 | struct thread *thread; |
1675 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); | 1701 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); |
@@ -1722,9 +1748,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
1722 | 1748 | ||
1723 | if (sc->fmt == NULL) { | 1749 | if (sc->fmt == NULL) { |
1724 | signed_print: | 1750 | signed_print: |
1725 | fprintf(trace->output, ") = %d", ret); | 1751 | fprintf(trace->output, ") = %ld", ret); |
1726 | } else if (ret < 0 && sc->fmt->errmsg) { | 1752 | } else if (ret < 0 && sc->fmt->errmsg) { |
1727 | char bf[256]; | 1753 | char bf[STRERR_BUFSIZE]; |
1728 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 1754 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
1729 | *e = audit_errno_to_name(-ret); | 1755 | *e = audit_errno_to_name(-ret); |
1730 | 1756 | ||
@@ -1732,7 +1758,7 @@ signed_print: | |||
1732 | } else if (ret == 0 && sc->fmt->timeout) | 1758 | } else if (ret == 0 && sc->fmt->timeout) |
1733 | fprintf(trace->output, ") = 0 Timeout"); | 1759 | fprintf(trace->output, ") = 0 Timeout"); |
1734 | else if (sc->fmt->hexret) | 1760 | else if (sc->fmt->hexret) |
1735 | fprintf(trace->output, ") = %#x", ret); | 1761 | fprintf(trace->output, ") = %#lx", ret); |
1736 | else | 1762 | else |
1737 | goto signed_print; | 1763 | goto signed_print; |
1738 | 1764 | ||
@@ -1820,7 +1846,7 @@ static int trace__pgfault(struct trace *trace, | |||
1820 | if (trace->summary_only) | 1846 | if (trace->summary_only) |
1821 | return 0; | 1847 | return 0; |
1822 | 1848 | ||
1823 | thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION, | 1849 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, |
1824 | sample->ip, &al); | 1850 | sample->ip, &al); |
1825 | 1851 | ||
1826 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); | 1852 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); |
@@ -1833,11 +1859,11 @@ static int trace__pgfault(struct trace *trace, | |||
1833 | 1859 | ||
1834 | fprintf(trace->output, "] => "); | 1860 | fprintf(trace->output, "] => "); |
1835 | 1861 | ||
1836 | thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE, | 1862 | thread__find_addr_location(thread, cpumode, MAP__VARIABLE, |
1837 | sample->addr, &al); | 1863 | sample->addr, &al); |
1838 | 1864 | ||
1839 | if (!al.map) { | 1865 | if (!al.map) { |
1840 | thread__find_addr_location(thread, trace->host, cpumode, | 1866 | thread__find_addr_location(thread, cpumode, |
1841 | MAP__FUNCTION, sample->addr, &al); | 1867 | MAP__FUNCTION, sample->addr, &al); |
1842 | 1868 | ||
1843 | if (al.map) | 1869 | if (al.map) |
@@ -2018,6 +2044,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2018 | int err = -1, i; | 2044 | int err = -1, i; |
2019 | unsigned long before; | 2045 | unsigned long before; |
2020 | const bool forks = argc > 0; | 2046 | const bool forks = argc > 0; |
2047 | bool draining = false; | ||
2021 | 2048 | ||
2022 | trace->live = true; | 2049 | trace->live = true; |
2023 | 2050 | ||
@@ -2078,10 +2105,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2078 | goto out_error_open; | 2105 | goto out_error_open; |
2079 | 2106 | ||
2080 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); | 2107 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); |
2081 | if (err < 0) { | 2108 | if (err < 0) |
2082 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); | 2109 | goto out_error_mmap; |
2083 | goto out_delete_evlist; | ||
2084 | } | ||
2085 | 2110 | ||
2086 | perf_evlist__enable(evlist); | 2111 | perf_evlist__enable(evlist); |
2087 | 2112 | ||
@@ -2143,8 +2168,12 @@ next_event: | |||
2143 | if (trace->nr_events == before) { | 2168 | if (trace->nr_events == before) { |
2144 | int timeout = done ? 100 : -1; | 2169 | int timeout = done ? 100 : -1; |
2145 | 2170 | ||
2146 | if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0) | 2171 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { |
2172 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0) | ||
2173 | draining = true; | ||
2174 | |||
2147 | goto again; | 2175 | goto again; |
2176 | } | ||
2148 | } else { | 2177 | } else { |
2149 | goto again; | 2178 | goto again; |
2150 | } | 2179 | } |
@@ -2177,6 +2206,10 @@ out_error_tp: | |||
2177 | perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); | 2206 | perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); |
2178 | goto out_error; | 2207 | goto out_error; |
2179 | 2208 | ||
2209 | out_error_mmap: | ||
2210 | perf_evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf)); | ||
2211 | goto out_error; | ||
2212 | |||
2180 | out_error_open: | 2213 | out_error_open: |
2181 | perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); | 2214 | perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); |
2182 | 2215 | ||
@@ -2209,18 +2242,18 @@ static int trace__replay(struct trace *trace) | |||
2209 | trace->tool.tracing_data = perf_event__process_tracing_data; | 2242 | trace->tool.tracing_data = perf_event__process_tracing_data; |
2210 | trace->tool.build_id = perf_event__process_build_id; | 2243 | trace->tool.build_id = perf_event__process_build_id; |
2211 | 2244 | ||
2212 | trace->tool.ordered_samples = true; | 2245 | trace->tool.ordered_events = true; |
2213 | trace->tool.ordering_requires_timestamps = true; | 2246 | trace->tool.ordering_requires_timestamps = true; |
2214 | 2247 | ||
2215 | /* add tid to output */ | 2248 | /* add tid to output */ |
2216 | trace->multiple_threads = true; | 2249 | trace->multiple_threads = true; |
2217 | 2250 | ||
2218 | if (symbol__init() < 0) | ||
2219 | return -1; | ||
2220 | |||
2221 | session = perf_session__new(&file, false, &trace->tool); | 2251 | session = perf_session__new(&file, false, &trace->tool); |
2222 | if (session == NULL) | 2252 | if (session == NULL) |
2223 | return -ENOMEM; | 2253 | return -1; |
2254 | |||
2255 | if (symbol__init(&session->header.env) < 0) | ||
2256 | goto out; | ||
2224 | 2257 | ||
2225 | trace->host = &session->machines.host; | 2258 | trace->host = &session->machines.host; |
2226 | 2259 | ||
@@ -2348,7 +2381,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
2348 | FILE *fp = data->fp; | 2381 | FILE *fp = data->fp; |
2349 | size_t printed = data->printed; | 2382 | size_t printed = data->printed; |
2350 | struct trace *trace = data->trace; | 2383 | struct trace *trace = data->trace; |
2351 | struct thread_trace *ttrace = thread->priv; | 2384 | struct thread_trace *ttrace = thread__priv(thread); |
2352 | double ratio; | 2385 | double ratio; |
2353 | 2386 | ||
2354 | if (ttrace == NULL) | 2387 | if (ttrace == NULL) |
@@ -2452,7 +2485,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
2452 | .user_freq = UINT_MAX, | 2485 | .user_freq = UINT_MAX, |
2453 | .user_interval = ULLONG_MAX, | 2486 | .user_interval = ULLONG_MAX, |
2454 | .no_buffering = true, | 2487 | .no_buffering = true, |
2455 | .mmap_pages = 1024, | 2488 | .mmap_pages = UINT_MAX, |
2456 | }, | 2489 | }, |
2457 | .output = stdout, | 2490 | .output = stdout, |
2458 | .show_comm = true, | 2491 | .show_comm = true, |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 1f67aa02d240..5d4b039fe1ed 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -48,10 +48,6 @@ ifneq ($(ARCH),$(filter $(ARCH),x86 arm)) | |||
48 | NO_LIBDW_DWARF_UNWIND := 1 | 48 | NO_LIBDW_DWARF_UNWIND := 1 |
49 | endif | 49 | endif |
50 | 50 | ||
51 | ifeq ($(ARCH),powerpc) | ||
52 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX | ||
53 | endif | ||
54 | |||
55 | ifeq ($(LIBUNWIND_LIBS),) | 51 | ifeq ($(LIBUNWIND_LIBS),) |
56 | NO_LIBUNWIND := 1 | 52 | NO_LIBUNWIND := 1 |
57 | else | 53 | else |
@@ -120,6 +116,29 @@ ifdef PARSER_DEBUG | |||
120 | CFLAGS += -DPARSER_DEBUG | 116 | CFLAGS += -DPARSER_DEBUG |
121 | endif | 117 | endif |
122 | 118 | ||
119 | ifndef NO_LIBPYTHON | ||
120 | # Try different combinations to accommodate systems that only have | ||
121 | # python[2][-config] in weird combinations but always preferring | ||
122 | # python2 and python2-config as per pep-0394. If we catch a | ||
123 | # python[-config] in version 3, the version check will kill it. | ||
124 | PYTHON2 := $(if $(call get-executable,python2),python2,python) | ||
125 | override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2)) | ||
126 | PYTHON2_CONFIG := \ | ||
127 | $(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config) | ||
128 | override PYTHON_CONFIG := \ | ||
129 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG)) | ||
130 | |||
131 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | ||
132 | |||
133 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
134 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
135 | |||
136 | FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS) | ||
137 | FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) | ||
138 | FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS) | ||
139 | FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS) | ||
140 | endif | ||
141 | |||
123 | CFLAGS += -fno-omit-frame-pointer | 142 | CFLAGS += -fno-omit-frame-pointer |
124 | CFLAGS += -ggdb3 | 143 | CFLAGS += -ggdb3 |
125 | CFLAGS += -funwind-tables | 144 | CFLAGS += -funwind-tables |
@@ -131,7 +150,7 @@ CFLAGS += -std=gnu99 | |||
131 | # adding assembler files missing the .GNU-stack linker note. | 150 | # adding assembler files missing the .GNU-stack linker note. |
132 | LDFLAGS += -Wl,-z,noexecstack | 151 | LDFLAGS += -Wl,-z,noexecstack |
133 | 152 | ||
134 | EXTLIBS = -lelf -lpthread -lrt -lm -ldl | 153 | EXTLIBS = -lpthread -lrt -lm -ldl |
135 | 154 | ||
136 | ifneq ($(OUTPUT),) | 155 | ifneq ($(OUTPUT),) |
137 | OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ | 156 | OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ |
@@ -181,7 +200,8 @@ CORE_FEATURE_TESTS = \ | |||
181 | libunwind \ | 200 | libunwind \ |
182 | stackprotector-all \ | 201 | stackprotector-all \ |
183 | timerfd \ | 202 | timerfd \ |
184 | libdw-dwarf-unwind | 203 | libdw-dwarf-unwind \ |
204 | zlib | ||
185 | 205 | ||
186 | LIB_FEATURE_TESTS = \ | 206 | LIB_FEATURE_TESTS = \ |
187 | dwarf \ | 207 | dwarf \ |
@@ -195,7 +215,8 @@ LIB_FEATURE_TESTS = \ | |||
195 | libpython \ | 215 | libpython \ |
196 | libslang \ | 216 | libslang \ |
197 | libunwind \ | 217 | libunwind \ |
198 | libdw-dwarf-unwind | 218 | libdw-dwarf-unwind \ |
219 | zlib | ||
199 | 220 | ||
200 | VF_FEATURE_TESTS = \ | 221 | VF_FEATURE_TESTS = \ |
201 | backtrace \ | 222 | backtrace \ |
@@ -211,7 +232,9 @@ VF_FEATURE_TESTS = \ | |||
211 | bionic \ | 232 | bionic \ |
212 | liberty \ | 233 | liberty \ |
213 | liberty-z \ | 234 | liberty-z \ |
214 | cplus-demangle | 235 | cplus-demangle \ |
236 | compile-32 \ | ||
237 | compile-x32 | ||
215 | 238 | ||
216 | # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. | 239 | # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. |
217 | # If in the future we need per-feature checks/flags for features not | 240 | # If in the future we need per-feature checks/flags for features not |
@@ -331,6 +354,7 @@ endif # NO_LIBELF | |||
331 | 354 | ||
332 | ifndef NO_LIBELF | 355 | ifndef NO_LIBELF |
333 | CFLAGS += -DHAVE_LIBELF_SUPPORT | 356 | CFLAGS += -DHAVE_LIBELF_SUPPORT |
357 | EXTLIBS += -lelf | ||
334 | 358 | ||
335 | ifeq ($(feature-libelf-mmap), 1) | 359 | ifeq ($(feature-libelf-mmap), 1) |
336 | CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT | 360 | CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT |
@@ -350,11 +374,17 @@ ifndef NO_LIBELF | |||
350 | else | 374 | else |
351 | CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) | 375 | CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) |
352 | LDFLAGS += $(LIBDW_LDFLAGS) | 376 | LDFLAGS += $(LIBDW_LDFLAGS) |
353 | EXTLIBS += -lelf -ldw | 377 | EXTLIBS += -ldw |
354 | endif # PERF_HAVE_DWARF_REGS | 378 | endif # PERF_HAVE_DWARF_REGS |
355 | endif # NO_DWARF | 379 | endif # NO_DWARF |
356 | endif # NO_LIBELF | 380 | endif # NO_LIBELF |
357 | 381 | ||
382 | ifeq ($(ARCH),powerpc) | ||
383 | ifndef NO_DWARF | ||
384 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX | ||
385 | endif | ||
386 | endif | ||
387 | |||
358 | ifndef NO_LIBUNWIND | 388 | ifndef NO_LIBUNWIND |
359 | ifneq ($(feature-libunwind), 1) | 389 | ifneq ($(feature-libunwind), 1) |
360 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); | 390 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); |
@@ -482,21 +512,14 @@ define disable-python_code | |||
482 | NO_LIBPYTHON := 1 | 512 | NO_LIBPYTHON := 1 |
483 | endef | 513 | endef |
484 | 514 | ||
485 | override PYTHON := \ | 515 | ifdef NO_LIBPYTHON |
486 | $(call get-executable-or-default,PYTHON,python) | 516 | $(call disable-python) |
487 | |||
488 | ifndef PYTHON | ||
489 | $(call disable-python,python interpreter) | ||
490 | else | 517 | else |
491 | 518 | ||
492 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | 519 | ifndef PYTHON |
493 | 520 | $(call disable-python,python interpreter) | |
494 | ifdef NO_LIBPYTHON | ||
495 | $(call disable-python) | ||
496 | else | 521 | else |
497 | 522 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | |
498 | override PYTHON_CONFIG := \ | ||
499 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) | ||
500 | 523 | ||
501 | ifndef PYTHON_CONFIG | 524 | ifndef PYTHON_CONFIG |
502 | $(call disable-python,python-config tool) | 525 | $(call disable-python,python-config tool) |
@@ -584,6 +607,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),) | |||
584 | CFLAGS += -DHAVE_LIBBFD_SUPPORT | 607 | CFLAGS += -DHAVE_LIBBFD_SUPPORT |
585 | endif | 608 | endif |
586 | 609 | ||
610 | ifndef NO_ZLIB | ||
611 | ifeq ($(feature-zlib), 1) | ||
612 | CFLAGS += -DHAVE_ZLIB_SUPPORT | ||
613 | EXTLIBS += -lz | ||
614 | else | ||
615 | NO_ZLIB := 1 | ||
616 | endif | ||
617 | endif | ||
618 | |||
587 | ifndef NO_BACKTRACE | 619 | ifndef NO_BACKTRACE |
588 | ifeq ($(feature-backtrace), 1) | 620 | ifeq ($(feature-backtrace), 1) |
589 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT | 621 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT |
@@ -604,6 +636,31 @@ ifdef HAVE_KVM_STAT_SUPPORT | |||
604 | CFLAGS += -DHAVE_KVM_STAT_SUPPORT | 636 | CFLAGS += -DHAVE_KVM_STAT_SUPPORT |
605 | endif | 637 | endif |
606 | 638 | ||
639 | ifeq (${IS_64_BIT}, 1) | ||
640 | ifndef NO_PERF_READ_VDSO32 | ||
641 | $(call feature_check,compile-32) | ||
642 | ifeq ($(feature-compile-32), 1) | ||
643 | CFLAGS += -DHAVE_PERF_READ_VDSO32 | ||
644 | else | ||
645 | NO_PERF_READ_VDSO32 := 1 | ||
646 | endif | ||
647 | endif | ||
648 | ifneq (${IS_X86_64}, 1) | ||
649 | NO_PERF_READ_VDSOX32 := 1 | ||
650 | endif | ||
651 | ifndef NO_PERF_READ_VDSOX32 | ||
652 | $(call feature_check,compile-x32) | ||
653 | ifeq ($(feature-compile-x32), 1) | ||
654 | CFLAGS += -DHAVE_PERF_READ_VDSOX32 | ||
655 | else | ||
656 | NO_PERF_READ_VDSOX32 := 1 | ||
657 | endif | ||
658 | endif | ||
659 | else | ||
660 | NO_PERF_READ_VDSO32 := 1 | ||
661 | NO_PERF_READ_VDSOX32 := 1 | ||
662 | endif | ||
663 | |||
607 | # Among the variables below, these: | 664 | # Among the variables below, these: |
608 | # perfexecdir | 665 | # perfexecdir |
609 | # template_dir | 666 | # template_dir |
@@ -635,11 +692,13 @@ else | |||
635 | sysconfdir = $(prefix)/etc | 692 | sysconfdir = $(prefix)/etc |
636 | ETC_PERFCONFIG = etc/perfconfig | 693 | ETC_PERFCONFIG = etc/perfconfig |
637 | endif | 694 | endif |
695 | ifndef lib | ||
638 | ifeq ($(IS_X86_64),1) | 696 | ifeq ($(IS_X86_64),1) |
639 | lib = lib64 | 697 | lib = lib64 |
640 | else | 698 | else |
641 | lib = lib | 699 | lib = lib |
642 | endif | 700 | endif |
701 | endif # lib | ||
643 | libdir = $(prefix)/$(lib) | 702 | libdir = $(prefix)/$(lib) |
644 | 703 | ||
645 | # Shell quote (do not use $(call) to accommodate ancient setups); | 704 | # Shell quote (do not use $(call) to accommodate ancient setups); |
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch index 4b06719ee984..851cd0172a76 100644 --- a/tools/perf/config/Makefile.arch +++ b/tools/perf/config/Makefile.arch | |||
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64) | |||
21 | RAW_ARCH := x86_64 | 21 | RAW_ARCH := x86_64 |
22 | endif | 22 | endif |
23 | endif | 23 | endif |
24 | |||
25 | ifeq (${IS_X86_64}, 1) | ||
26 | IS_64_BIT := 1 | ||
27 | else ifeq ($(ARCH),x86) | ||
28 | IS_64_BIT := 0 | ||
29 | else | ||
30 | IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) | ||
31 | endif | ||
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 6088f8d8a434..53f19b5dbc37 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
@@ -27,7 +27,10 @@ FILES= \ | |||
27 | test-libunwind-debug-frame.bin \ | 27 | test-libunwind-debug-frame.bin \ |
28 | test-stackprotector-all.bin \ | 28 | test-stackprotector-all.bin \ |
29 | test-timerfd.bin \ | 29 | test-timerfd.bin \ |
30 | test-libdw-dwarf-unwind.bin | 30 | test-libdw-dwarf-unwind.bin \ |
31 | test-compile-32.bin \ | ||
32 | test-compile-x32.bin \ | ||
33 | test-zlib.bin | ||
31 | 34 | ||
32 | CC := $(CROSS_COMPILE)gcc -MD | 35 | CC := $(CROSS_COMPILE)gcc -MD |
33 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 36 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
@@ -39,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) | |||
39 | ############################### | 42 | ############################### |
40 | 43 | ||
41 | test-all.bin: | 44 | test-all.bin: |
42 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl | 45 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz |
43 | 46 | ||
44 | test-hello.bin: | 47 | test-hello.bin: |
45 | $(BUILD) | 48 | $(BUILD) |
@@ -101,25 +104,11 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | |||
101 | test-libperl.bin: | 104 | test-libperl.bin: |
102 | $(BUILD) $(FLAGS_PERL_EMBED) | 105 | $(BUILD) $(FLAGS_PERL_EMBED) |
103 | 106 | ||
104 | override PYTHON := python | ||
105 | override PYTHON_CONFIG := python-config | ||
106 | |||
107 | escape-for-shell-sq = $(subst ','\'',$(1)) | ||
108 | shell-sq = '$(escape-for-shell-sq)' | ||
109 | |||
110 | PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG)) | ||
111 | |||
112 | PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
113 | PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) | ||
114 | PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) | ||
115 | PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
116 | FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | ||
117 | |||
118 | test-libpython.bin: | 107 | test-libpython.bin: |
119 | $(BUILD) $(FLAGS_PYTHON_EMBED) | 108 | $(BUILD) |
120 | 109 | ||
121 | test-libpython-version.bin: | 110 | test-libpython-version.bin: |
122 | $(BUILD) $(FLAGS_PYTHON_EMBED) | 111 | $(BUILD) |
123 | 112 | ||
124 | test-libbfd.bin: | 113 | test-libbfd.bin: |
125 | $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl | 114 | $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl |
@@ -145,6 +134,15 @@ test-libdw-dwarf-unwind.bin: | |||
145 | test-sync-compare-and-swap.bin: | 134 | test-sync-compare-and-swap.bin: |
146 | $(BUILD) -Werror | 135 | $(BUILD) -Werror |
147 | 136 | ||
137 | test-compile-32.bin: | ||
138 | $(CC) -m32 -o $(OUTPUT)$@ test-compile.c | ||
139 | |||
140 | test-compile-x32.bin: | ||
141 | $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c | ||
142 | |||
143 | test-zlib.bin: | ||
144 | $(BUILD) -lz | ||
145 | |||
148 | -include *.d | 146 | -include *.d |
149 | 147 | ||
150 | ############################### | 148 | ############################### |
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index a7d022e161c0..652e0098eba6 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
@@ -93,6 +93,10 @@ | |||
93 | # include "test-sync-compare-and-swap.c" | 93 | # include "test-sync-compare-and-swap.c" |
94 | #undef main | 94 | #undef main |
95 | 95 | ||
96 | #define main main_test_zlib | ||
97 | # include "test-zlib.c" | ||
98 | #undef main | ||
99 | |||
96 | int main(int argc, char *argv[]) | 100 | int main(int argc, char *argv[]) |
97 | { | 101 | { |
98 | main_test_libpython(); | 102 | main_test_libpython(); |
@@ -116,6 +120,7 @@ int main(int argc, char *argv[]) | |||
116 | main_test_stackprotector_all(); | 120 | main_test_stackprotector_all(); |
117 | main_test_libdw_dwarf_unwind(); | 121 | main_test_libdw_dwarf_unwind(); |
118 | main_test_sync_compare_and_swap(argc, argv); | 122 | main_test_sync_compare_and_swap(argc, argv); |
123 | main_test_zlib(); | ||
119 | 124 | ||
120 | return 0; | 125 | return 0; |
121 | } | 126 | } |
diff --git a/tools/perf/config/feature-checks/test-compile.c b/tools/perf/config/feature-checks/test-compile.c new file mode 100644 index 000000000000..31dbf45bf99c --- /dev/null +++ b/tools/perf/config/feature-checks/test-compile.c | |||
@@ -0,0 +1,4 @@ | |||
1 | int main(void) | ||
2 | { | ||
3 | return 0; | ||
4 | } | ||
diff --git a/tools/perf/config/feature-checks/test-zlib.c b/tools/perf/config/feature-checks/test-zlib.c new file mode 100644 index 000000000000..e111fff6240e --- /dev/null +++ b/tools/perf/config/feature-checks/test-zlib.c | |||
@@ -0,0 +1,9 @@ | |||
1 | #include <zlib.h> | ||
2 | |||
3 | int main(void) | ||
4 | { | ||
5 | z_stream zs; | ||
6 | |||
7 | inflateInit(&zs); | ||
8 | return 0; | ||
9 | } | ||
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 4d985e0f03f5..7076a62d0ff7 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak | |||
@@ -132,7 +132,7 @@ endef | |||
132 | # | 132 | # |
133 | # Usage: bool-value = $(call is-absolute,path) | 133 | # Usage: bool-value = $(call is-absolute,path) |
134 | # | 134 | # |
135 | is-absolute = $(shell echo $(shell-sq) | grep ^/ -q && echo y) | 135 | is-absolute = $(shell echo $(shell-sq) | grep -q ^/ && echo y) |
136 | 136 | ||
137 | # lookup | 137 | # lookup |
138 | # | 138 | # |
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c new file mode 100644 index 000000000000..764e2547c25a --- /dev/null +++ b/tools/perf/perf-read-vdso.c | |||
@@ -0,0 +1,34 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | |||
4 | #define VDSO__MAP_NAME "[vdso]" | ||
5 | |||
6 | /* | ||
7 | * Include definition of find_vdso_map() also used in util/vdso.c for | ||
8 | * building perf. | ||
9 | */ | ||
10 | #include "util/find-vdso-map.c" | ||
11 | |||
12 | int main(void) | ||
13 | { | ||
14 | void *start, *end; | ||
15 | size_t size, written; | ||
16 | |||
17 | if (find_vdso_map(&start, &end)) | ||
18 | return 1; | ||
19 | |||
20 | size = end - start; | ||
21 | |||
22 | while (size) { | ||
23 | written = fwrite(start, 1, size, stdout); | ||
24 | if (!written) | ||
25 | return 1; | ||
26 | start += written; | ||
27 | size -= written; | ||
28 | } | ||
29 | |||
30 | if (fflush(stdout)) | ||
31 | return 1; | ||
32 | |||
33 | return 0; | ||
34 | } | ||
diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h index 937e4324ad94..a3b13d7dc1d4 100644 --- a/tools/perf/perf-sys.h +++ b/tools/perf/perf-sys.h | |||
@@ -13,7 +13,7 @@ | |||
13 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 13 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
14 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 14 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
15 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 15 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
16 | #define CPUINFO_PROC "model name" | 16 | #define CPUINFO_PROC {"model name"} |
17 | #ifndef __NR_perf_event_open | 17 | #ifndef __NR_perf_event_open |
18 | # define __NR_perf_event_open 336 | 18 | # define __NR_perf_event_open 336 |
19 | #endif | 19 | #endif |
@@ -30,7 +30,7 @@ | |||
30 | #define wmb() asm volatile("sfence" ::: "memory") | 30 | #define wmb() asm volatile("sfence" ::: "memory") |
31 | #define rmb() asm volatile("lfence" ::: "memory") | 31 | #define rmb() asm volatile("lfence" ::: "memory") |
32 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 32 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
33 | #define CPUINFO_PROC "model name" | 33 | #define CPUINFO_PROC {"model name"} |
34 | #ifndef __NR_perf_event_open | 34 | #ifndef __NR_perf_event_open |
35 | # define __NR_perf_event_open 298 | 35 | # define __NR_perf_event_open 298 |
36 | #endif | 36 | #endif |
@@ -47,14 +47,14 @@ | |||
47 | #define mb() asm volatile ("sync" ::: "memory") | 47 | #define mb() asm volatile ("sync" ::: "memory") |
48 | #define wmb() asm volatile ("sync" ::: "memory") | 48 | #define wmb() asm volatile ("sync" ::: "memory") |
49 | #define rmb() asm volatile ("sync" ::: "memory") | 49 | #define rmb() asm volatile ("sync" ::: "memory") |
50 | #define CPUINFO_PROC "cpu" | 50 | #define CPUINFO_PROC {"cpu"} |
51 | #endif | 51 | #endif |
52 | 52 | ||
53 | #ifdef __s390__ | 53 | #ifdef __s390__ |
54 | #define mb() asm volatile("bcr 15,0" ::: "memory") | 54 | #define mb() asm volatile("bcr 15,0" ::: "memory") |
55 | #define wmb() asm volatile("bcr 15,0" ::: "memory") | 55 | #define wmb() asm volatile("bcr 15,0" ::: "memory") |
56 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | 56 | #define rmb() asm volatile("bcr 15,0" ::: "memory") |
57 | #define CPUINFO_PROC "vendor_id" | 57 | #define CPUINFO_PROC {"vendor_id"} |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | #ifdef __sh__ | 60 | #ifdef __sh__ |
@@ -67,14 +67,14 @@ | |||
67 | # define wmb() asm volatile("" ::: "memory") | 67 | # define wmb() asm volatile("" ::: "memory") |
68 | # define rmb() asm volatile("" ::: "memory") | 68 | # define rmb() asm volatile("" ::: "memory") |
69 | #endif | 69 | #endif |
70 | #define CPUINFO_PROC "cpu type" | 70 | #define CPUINFO_PROC {"cpu type"} |
71 | #endif | 71 | #endif |
72 | 72 | ||
73 | #ifdef __hppa__ | 73 | #ifdef __hppa__ |
74 | #define mb() asm volatile("" ::: "memory") | 74 | #define mb() asm volatile("" ::: "memory") |
75 | #define wmb() asm volatile("" ::: "memory") | 75 | #define wmb() asm volatile("" ::: "memory") |
76 | #define rmb() asm volatile("" ::: "memory") | 76 | #define rmb() asm volatile("" ::: "memory") |
77 | #define CPUINFO_PROC "cpu" | 77 | #define CPUINFO_PROC {"cpu"} |
78 | #endif | 78 | #endif |
79 | 79 | ||
80 | #ifdef __sparc__ | 80 | #ifdef __sparc__ |
@@ -87,14 +87,14 @@ | |||
87 | #endif | 87 | #endif |
88 | #define wmb() asm volatile("":::"memory") | 88 | #define wmb() asm volatile("":::"memory") |
89 | #define rmb() asm volatile("":::"memory") | 89 | #define rmb() asm volatile("":::"memory") |
90 | #define CPUINFO_PROC "cpu" | 90 | #define CPUINFO_PROC {"cpu"} |
91 | #endif | 91 | #endif |
92 | 92 | ||
93 | #ifdef __alpha__ | 93 | #ifdef __alpha__ |
94 | #define mb() asm volatile("mb" ::: "memory") | 94 | #define mb() asm volatile("mb" ::: "memory") |
95 | #define wmb() asm volatile("wmb" ::: "memory") | 95 | #define wmb() asm volatile("wmb" ::: "memory") |
96 | #define rmb() asm volatile("mb" ::: "memory") | 96 | #define rmb() asm volatile("mb" ::: "memory") |
97 | #define CPUINFO_PROC "cpu model" | 97 | #define CPUINFO_PROC {"cpu model"} |
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | #ifdef __ia64__ | 100 | #ifdef __ia64__ |
@@ -102,7 +102,7 @@ | |||
102 | #define wmb() asm volatile ("mf" ::: "memory") | 102 | #define wmb() asm volatile ("mf" ::: "memory") |
103 | #define rmb() asm volatile ("mf" ::: "memory") | 103 | #define rmb() asm volatile ("mf" ::: "memory") |
104 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | 104 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") |
105 | #define CPUINFO_PROC "model name" | 105 | #define CPUINFO_PROC {"model name"} |
106 | #endif | 106 | #endif |
107 | 107 | ||
108 | #ifdef __arm__ | 108 | #ifdef __arm__ |
@@ -113,7 +113,7 @@ | |||
113 | #define mb() ((void(*)(void))0xffff0fa0)() | 113 | #define mb() ((void(*)(void))0xffff0fa0)() |
114 | #define wmb() ((void(*)(void))0xffff0fa0)() | 114 | #define wmb() ((void(*)(void))0xffff0fa0)() |
115 | #define rmb() ((void(*)(void))0xffff0fa0)() | 115 | #define rmb() ((void(*)(void))0xffff0fa0)() |
116 | #define CPUINFO_PROC "Processor" | 116 | #define CPUINFO_PROC {"model name", "Processor"} |
117 | #endif | 117 | #endif |
118 | 118 | ||
119 | #ifdef __aarch64__ | 119 | #ifdef __aarch64__ |
@@ -133,28 +133,28 @@ | |||
133 | : "memory") | 133 | : "memory") |
134 | #define wmb() mb() | 134 | #define wmb() mb() |
135 | #define rmb() mb() | 135 | #define rmb() mb() |
136 | #define CPUINFO_PROC "cpu model" | 136 | #define CPUINFO_PROC {"cpu model"} |
137 | #endif | 137 | #endif |
138 | 138 | ||
139 | #ifdef __arc__ | 139 | #ifdef __arc__ |
140 | #define mb() asm volatile("" ::: "memory") | 140 | #define mb() asm volatile("" ::: "memory") |
141 | #define wmb() asm volatile("" ::: "memory") | 141 | #define wmb() asm volatile("" ::: "memory") |
142 | #define rmb() asm volatile("" ::: "memory") | 142 | #define rmb() asm volatile("" ::: "memory") |
143 | #define CPUINFO_PROC "Processor" | 143 | #define CPUINFO_PROC {"Processor"} |
144 | #endif | 144 | #endif |
145 | 145 | ||
146 | #ifdef __metag__ | 146 | #ifdef __metag__ |
147 | #define mb() asm volatile("" ::: "memory") | 147 | #define mb() asm volatile("" ::: "memory") |
148 | #define wmb() asm volatile("" ::: "memory") | 148 | #define wmb() asm volatile("" ::: "memory") |
149 | #define rmb() asm volatile("" ::: "memory") | 149 | #define rmb() asm volatile("" ::: "memory") |
150 | #define CPUINFO_PROC "CPU" | 150 | #define CPUINFO_PROC {"CPU"} |
151 | #endif | 151 | #endif |
152 | 152 | ||
153 | #ifdef __xtensa__ | 153 | #ifdef __xtensa__ |
154 | #define mb() asm volatile("memw" ::: "memory") | 154 | #define mb() asm volatile("memw" ::: "memory") |
155 | #define wmb() asm volatile("memw" ::: "memory") | 155 | #define wmb() asm volatile("memw" ::: "memory") |
156 | #define rmb() asm volatile("" ::: "memory") | 156 | #define rmb() asm volatile("" ::: "memory") |
157 | #define CPUINFO_PROC "core ID" | 157 | #define CPUINFO_PROC {"core ID"} |
158 | #endif | 158 | #endif |
159 | 159 | ||
160 | #ifdef __tile__ | 160 | #ifdef __tile__ |
@@ -162,7 +162,7 @@ | |||
162 | #define wmb() asm volatile ("mf" ::: "memory") | 162 | #define wmb() asm volatile ("mf" ::: "memory") |
163 | #define rmb() asm volatile ("mf" ::: "memory") | 163 | #define rmb() asm volatile ("mf" ::: "memory") |
164 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") | 164 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") |
165 | #define CPUINFO_PROC "model name" | 165 | #define CPUINFO_PROC {"model name"} |
166 | #endif | 166 | #endif |
167 | 167 | ||
168 | #define barrier() asm volatile ("" ::: "memory") | 168 | #define barrier() asm volatile ("" ::: "memory") |
diff --git a/tools/perf/perf-with-kcore.sh b/tools/perf/perf-with-kcore.sh new file mode 100644 index 000000000000..c7ff90a90e4e --- /dev/null +++ b/tools/perf/perf-with-kcore.sh | |||
@@ -0,0 +1,259 @@ | |||
1 | #!/bin/bash | ||
2 | # perf-with-kcore: use perf with a copy of kcore | ||
3 | # Copyright (c) 2014, 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 | set -e | ||
15 | |||
16 | usage() | ||
17 | { | ||
18 | echo "Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]" >&2 | ||
19 | echo " <perf sub-command> can be record, script, report or inject" >&2 | ||
20 | echo " or: perf-with-kcore fix_buildid_cache_permissions" >&2 | ||
21 | exit 1 | ||
22 | } | ||
23 | |||
24 | find_perf() | ||
25 | { | ||
26 | if [ -n "$PERF" ] ; then | ||
27 | return | ||
28 | fi | ||
29 | PERF=`which perf || true` | ||
30 | if [ -z "$PERF" ] ; then | ||
31 | echo "Failed to find perf" >&2 | ||
32 | exit 1 | ||
33 | fi | ||
34 | if [ ! -x "$PERF" ] ; then | ||
35 | echo "Failed to find perf" >&2 | ||
36 | exit 1 | ||
37 | fi | ||
38 | echo "Using $PERF" | ||
39 | "$PERF" version | ||
40 | } | ||
41 | |||
42 | copy_kcore() | ||
43 | { | ||
44 | echo "Copying kcore" | ||
45 | |||
46 | if [ $EUID -eq 0 ] ; then | ||
47 | SUDO="" | ||
48 | else | ||
49 | SUDO="sudo" | ||
50 | fi | ||
51 | |||
52 | rm -f perf.data.junk | ||
53 | ("$PERF" record -o perf.data.junk $PERF_OPTIONS -- sleep 60) >/dev/null 2>/dev/null & | ||
54 | PERF_PID=$! | ||
55 | |||
56 | # Need to make sure that perf has started | ||
57 | sleep 1 | ||
58 | |||
59 | KCORE=$(($SUDO "$PERF" buildid-cache -v -f -k /proc/kcore >/dev/null) 2>&1) | ||
60 | case "$KCORE" in | ||
61 | "kcore added to build-id cache directory "*) | ||
62 | KCORE_DIR=${KCORE#"kcore added to build-id cache directory "} | ||
63 | ;; | ||
64 | *) | ||
65 | kill $PERF_PID | ||
66 | wait >/dev/null 2>/dev/null || true | ||
67 | rm perf.data.junk | ||
68 | echo "$KCORE" | ||
69 | echo "Failed to find kcore" >&2 | ||
70 | exit 1 | ||
71 | ;; | ||
72 | esac | ||
73 | |||
74 | kill $PERF_PID | ||
75 | wait >/dev/null 2>/dev/null || true | ||
76 | rm perf.data.junk | ||
77 | |||
78 | $SUDO cp -a "$KCORE_DIR" "$(pwd)/$PERF_DATA_DIR" | ||
79 | $SUDO rm -f "$KCORE_DIR/kcore" | ||
80 | $SUDO rm -f "$KCORE_DIR/kallsyms" | ||
81 | $SUDO rm -f "$KCORE_DIR/modules" | ||
82 | $SUDO rmdir "$KCORE_DIR" | ||
83 | |||
84 | KCORE_DIR_BASENAME=$(basename "$KCORE_DIR") | ||
85 | KCORE_DIR="$(pwd)/$PERF_DATA_DIR/$KCORE_DIR_BASENAME" | ||
86 | |||
87 | $SUDO chown $UID "$KCORE_DIR" | ||
88 | $SUDO chown $UID "$KCORE_DIR/kcore" | ||
89 | $SUDO chown $UID "$KCORE_DIR/kallsyms" | ||
90 | $SUDO chown $UID "$KCORE_DIR/modules" | ||
91 | |||
92 | $SUDO chgrp $GROUPS "$KCORE_DIR" | ||
93 | $SUDO chgrp $GROUPS "$KCORE_DIR/kcore" | ||
94 | $SUDO chgrp $GROUPS "$KCORE_DIR/kallsyms" | ||
95 | $SUDO chgrp $GROUPS "$KCORE_DIR/modules" | ||
96 | |||
97 | ln -s "$KCORE_DIR_BASENAME" "$PERF_DATA_DIR/kcore_dir" | ||
98 | } | ||
99 | |||
100 | fix_buildid_cache_permissions() | ||
101 | { | ||
102 | if [ $EUID -ne 0 ] ; then | ||
103 | echo "This script must be run as root via sudo " >&2 | ||
104 | exit 1 | ||
105 | fi | ||
106 | |||
107 | if [ -z "$SUDO_USER" ] ; then | ||
108 | echo "This script must be run via sudo" >&2 | ||
109 | exit 1 | ||
110 | fi | ||
111 | |||
112 | USER_HOME=$(bash <<< "echo ~$SUDO_USER") | ||
113 | |||
114 | if [ "$HOME" != "$USER_HOME" ] ; then | ||
115 | echo "Fix unnecessary because root has a home: $HOME" >&2 | ||
116 | exit 1 | ||
117 | fi | ||
118 | |||
119 | echo "Fixing buildid cache permissions" | ||
120 | |||
121 | find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \; | ||
122 | find "$USER_HOME/.debug" -xdev -type f -links 1 ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \; | ||
123 | find "$USER_HOME/.debug" -xdev -type l ! -user "$SUDO_USER" -ls -exec chown -h "$SUDO_USER" \{\} \; | ||
124 | |||
125 | if [ -n "$SUDO_GID" ] ; then | ||
126 | find "$USER_HOME/.debug" -xdev -type d ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \; | ||
127 | find "$USER_HOME/.debug" -xdev -type f -links 1 ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \; | ||
128 | find "$USER_HOME/.debug" -xdev -type l ! -group "$SUDO_GID" -ls -exec chgrp -h "$SUDO_GID" \{\} \; | ||
129 | fi | ||
130 | |||
131 | echo "Done" | ||
132 | } | ||
133 | |||
134 | check_buildid_cache_permissions() | ||
135 | { | ||
136 | if [ $EUID -eq 0 ] ; then | ||
137 | return | ||
138 | fi | ||
139 | |||
140 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -user "$USER" -print -quit) | ||
141 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -user "$USER" -print -quit) | ||
142 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -user "$USER" -print -quit) | ||
143 | |||
144 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -group "$GROUPS" -print -quit) | ||
145 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -group "$GROUPS" -print -quit) | ||
146 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -group "$GROUPS" -print -quit) | ||
147 | |||
148 | if [ -n "$PERMISSIONS_OK" ] ; then | ||
149 | echo "*** WARNING *** buildid cache permissions may need fixing" >&2 | ||
150 | fi | ||
151 | } | ||
152 | |||
153 | record() | ||
154 | { | ||
155 | echo "Recording" | ||
156 | |||
157 | if [ $EUID -ne 0 ] ; then | ||
158 | |||
159 | if [ "$(cat /proc/sys/kernel/kptr_restrict)" -ne 0 ] ; then | ||
160 | echo "*** WARNING *** /proc/sys/kernel/kptr_restrict prevents access to kernel addresses" >&2 | ||
161 | fi | ||
162 | |||
163 | if echo "$PERF_OPTIONS" | grep -q ' -a \|^-a \| -a$\|^-a$\| --all-cpus \|^--all-cpus \| --all-cpus$\|^--all-cpus$' ; then | ||
164 | echo "*** WARNING *** system-wide tracing without root access will not be able to read all necessary information from /proc" >&2 | ||
165 | fi | ||
166 | |||
167 | if echo "$PERF_OPTIONS" | grep -q 'intel_pt\|intel_bts\| -I\|^-I' ; then | ||
168 | if [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt -1 ] ; then | ||
169 | echo "*** WARNING *** /proc/sys/kernel/perf_event_paranoid restricts buffer size and tracepoint (sched_switch) use" >&2 | ||
170 | fi | ||
171 | |||
172 | if echo "$PERF_OPTIONS" | grep -q ' --per-thread \|^--per-thread \| --per-thread$\|^--per-thread$' ; then | ||
173 | true | ||
174 | elif echo "$PERF_OPTIONS" | grep -q ' -t \|^-t \| -t$\|^-t$' ; then | ||
175 | true | ||
176 | elif [ ! -r /sys/kernel/debug -o ! -x /sys/kernel/debug ] ; then | ||
177 | echo "*** WARNING *** /sys/kernel/debug permissions prevent tracepoint (sched_switch) use" >&2 | ||
178 | fi | ||
179 | fi | ||
180 | fi | ||
181 | |||
182 | if [ -z "$1" ] ; then | ||
183 | echo "Workload is required for recording" >&2 | ||
184 | usage | ||
185 | fi | ||
186 | |||
187 | if [ -e "$PERF_DATA_DIR" ] ; then | ||
188 | echo "'$PERF_DATA_DIR' exists" >&2 | ||
189 | exit 1 | ||
190 | fi | ||
191 | |||
192 | find_perf | ||
193 | |||
194 | mkdir "$PERF_DATA_DIR" | ||
195 | |||
196 | echo "$PERF record -o $PERF_DATA_DIR/perf.data $PERF_OPTIONS -- $*" | ||
197 | "$PERF" record -o "$PERF_DATA_DIR/perf.data" $PERF_OPTIONS -- $* || true | ||
198 | |||
199 | if rmdir "$PERF_DATA_DIR" > /dev/null 2>/dev/null ; then | ||
200 | exit 1 | ||
201 | fi | ||
202 | |||
203 | copy_kcore | ||
204 | |||
205 | echo "Done" | ||
206 | } | ||
207 | |||
208 | subcommand() | ||
209 | { | ||
210 | find_perf | ||
211 | check_buildid_cache_permissions | ||
212 | echo "$PERF $PERF_SUB_COMMAND -i $PERF_DATA_DIR/perf.data --kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms $*" | ||
213 | "$PERF" $PERF_SUB_COMMAND -i "$PERF_DATA_DIR/perf.data" "--kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms" $* | ||
214 | } | ||
215 | |||
216 | if [ "$1" = "fix_buildid_cache_permissions" ] ; then | ||
217 | fix_buildid_cache_permissions | ||
218 | exit 0 | ||
219 | fi | ||
220 | |||
221 | PERF_SUB_COMMAND=$1 | ||
222 | PERF_DATA_DIR=$2 | ||
223 | shift || true | ||
224 | shift || true | ||
225 | |||
226 | if [ -z "$PERF_SUB_COMMAND" ] ; then | ||
227 | usage | ||
228 | fi | ||
229 | |||
230 | if [ -z "$PERF_DATA_DIR" ] ; then | ||
231 | usage | ||
232 | fi | ||
233 | |||
234 | case "$PERF_SUB_COMMAND" in | ||
235 | "record") | ||
236 | while [ "$1" != "--" ] ; do | ||
237 | PERF_OPTIONS+="$1 " | ||
238 | shift || break | ||
239 | done | ||
240 | if [ "$1" != "--" ] ; then | ||
241 | echo "Options and workload are required for recording" >&2 | ||
242 | usage | ||
243 | fi | ||
244 | shift | ||
245 | record $* | ||
246 | ;; | ||
247 | "script") | ||
248 | subcommand $* | ||
249 | ;; | ||
250 | "report") | ||
251 | subcommand $* | ||
252 | ;; | ||
253 | "inject") | ||
254 | subcommand $* | ||
255 | ;; | ||
256 | *) | ||
257 | usage | ||
258 | ;; | ||
259 | esac | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 2282d41879a2..3700a7faca6c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -200,6 +200,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) | |||
200 | *envchanged = 1; | 200 | *envchanged = 1; |
201 | (*argv)++; | 201 | (*argv)++; |
202 | (*argc)--; | 202 | (*argc)--; |
203 | } else if (!strcmp(cmd, "--buildid-dir")) { | ||
204 | if (*argc < 2) { | ||
205 | fprintf(stderr, "No directory given for --buildid-dir.\n"); | ||
206 | usage(perf_usage_string); | ||
207 | } | ||
208 | set_buildid_dir((*argv)[1]); | ||
209 | if (envchanged) | ||
210 | *envchanged = 1; | ||
211 | (*argv)++; | ||
212 | (*argc)--; | ||
203 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { | 213 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { |
204 | perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); | 214 | perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); |
205 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); | 215 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); |
@@ -313,6 +323,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
313 | int status; | 323 | int status; |
314 | struct stat st; | 324 | struct stat st; |
315 | const char *prefix; | 325 | const char *prefix; |
326 | char sbuf[STRERR_BUFSIZE]; | ||
316 | 327 | ||
317 | prefix = NULL; | 328 | prefix = NULL; |
318 | if (p->option & RUN_SETUP) | 329 | if (p->option & RUN_SETUP) |
@@ -343,7 +354,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
343 | status = 1; | 354 | status = 1; |
344 | /* Check for ENOSPC and EIO errors.. */ | 355 | /* Check for ENOSPC and EIO errors.. */ |
345 | if (fflush(stdout)) { | 356 | if (fflush(stdout)) { |
346 | fprintf(stderr, "write failure on standard output: %s", strerror(errno)); | 357 | fprintf(stderr, "write failure on standard output: %s", |
358 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
347 | goto out; | 359 | goto out; |
348 | } | 360 | } |
349 | if (ferror(stdout)) { | 361 | if (ferror(stdout)) { |
@@ -351,7 +363,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
351 | goto out; | 363 | goto out; |
352 | } | 364 | } |
353 | if (fclose(stdout)) { | 365 | if (fclose(stdout)) { |
354 | fprintf(stderr, "close failed on standard output: %s", strerror(errno)); | 366 | fprintf(stderr, "close failed on standard output: %s", |
367 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
355 | goto out; | 368 | goto out; |
356 | } | 369 | } |
357 | status = 0; | 370 | status = 0; |
@@ -466,6 +479,7 @@ void pthread__unblock_sigwinch(void) | |||
466 | int main(int argc, const char **argv) | 479 | int main(int argc, const char **argv) |
467 | { | 480 | { |
468 | const char *cmd; | 481 | const char *cmd; |
482 | char sbuf[STRERR_BUFSIZE]; | ||
469 | 483 | ||
470 | /* The page_size is placed in util object. */ | 484 | /* The page_size is placed in util object. */ |
471 | page_size = sysconf(_SC_PAGE_SIZE); | 485 | page_size = sysconf(_SC_PAGE_SIZE); |
@@ -495,7 +509,7 @@ int main(int argc, const char **argv) | |||
495 | } | 509 | } |
496 | if (!prefixcmp(cmd, "trace")) { | 510 | if (!prefixcmp(cmd, "trace")) { |
497 | #ifdef HAVE_LIBAUDIT_SUPPORT | 511 | #ifdef HAVE_LIBAUDIT_SUPPORT |
498 | set_buildid_dir(); | 512 | set_buildid_dir(NULL); |
499 | setup_path(); | 513 | setup_path(); |
500 | argv[0] = "trace"; | 514 | argv[0] = "trace"; |
501 | return cmd_trace(argc, argv, NULL); | 515 | return cmd_trace(argc, argv, NULL); |
@@ -510,7 +524,7 @@ int main(int argc, const char **argv) | |||
510 | argc--; | 524 | argc--; |
511 | handle_options(&argv, &argc, NULL); | 525 | handle_options(&argv, &argc, NULL); |
512 | commit_pager_choice(); | 526 | commit_pager_choice(); |
513 | set_buildid_dir(); | 527 | set_buildid_dir(NULL); |
514 | 528 | ||
515 | if (argc > 0) { | 529 | if (argc > 0) { |
516 | if (!prefixcmp(argv[0], "--")) | 530 | if (!prefixcmp(argv[0], "--")) |
@@ -561,7 +575,7 @@ int main(int argc, const char **argv) | |||
561 | } | 575 | } |
562 | 576 | ||
563 | fprintf(stderr, "Failed to run command '%s': %s\n", | 577 | fprintf(stderr, "Failed to run command '%s': %s\n", |
564 | cmd, strerror(errno)); | 578 | cmd, strerror_r(errno, sbuf, sizeof(sbuf))); |
565 | out: | 579 | out: |
566 | return 1; | 580 | return 1; |
567 | } | 581 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 510c65f72858..1dabb8553499 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void); | |||
41 | 41 | ||
42 | struct record_opts { | 42 | struct record_opts { |
43 | struct target target; | 43 | struct target target; |
44 | int call_graph; | ||
45 | bool call_graph_enabled; | ||
46 | bool group; | 44 | bool group; |
47 | bool inherit_stat; | 45 | bool inherit_stat; |
48 | bool no_buffering; | 46 | bool no_buffering; |
@@ -54,15 +52,18 @@ struct record_opts { | |||
54 | bool sample_weight; | 52 | bool sample_weight; |
55 | bool sample_time; | 53 | bool sample_time; |
56 | bool period; | 54 | bool period; |
55 | bool sample_intr_regs; | ||
57 | unsigned int freq; | 56 | unsigned int freq; |
58 | unsigned int mmap_pages; | 57 | unsigned int mmap_pages; |
59 | unsigned int user_freq; | 58 | unsigned int user_freq; |
60 | u64 branch_stack; | 59 | u64 branch_stack; |
61 | u64 default_interval; | 60 | u64 default_interval; |
62 | u64 user_interval; | 61 | u64 user_interval; |
63 | u16 stack_dump_size; | ||
64 | bool sample_transaction; | 62 | bool sample_transaction; |
65 | unsigned initial_delay; | 63 | unsigned initial_delay; |
66 | }; | 64 | }; |
67 | 65 | ||
66 | struct option; | ||
67 | extern const char * const *record_usage; | ||
68 | extern struct option *record_options; | ||
68 | #endif | 69 | #endif |
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record new file mode 100644 index 000000000000..221d66e05713 --- /dev/null +++ b/tools/perf/scripts/python/bin/export-to-postgresql-record | |||
@@ -0,0 +1,8 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | # | ||
4 | # export perf data to a postgresql database. Can cover | ||
5 | # perf ip samples (excluding the tracepoints). No special | ||
6 | # record requirements, just record what you want to export. | ||
7 | # | ||
8 | perf record $@ | ||
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report new file mode 100644 index 000000000000..cd335b6e2a01 --- /dev/null +++ b/tools/perf/scripts/python/bin/export-to-postgresql-report | |||
@@ -0,0 +1,29 @@ | |||
1 | #!/bin/bash | ||
2 | # description: export perf data to a postgresql database | ||
3 | # args: [database name] [columns] [calls] | ||
4 | n_args=0 | ||
5 | for i in "$@" | ||
6 | do | ||
7 | if expr match "$i" "-" > /dev/null ; then | ||
8 | break | ||
9 | fi | ||
10 | n_args=$(( $n_args + 1 )) | ||
11 | done | ||
12 | if [ "$n_args" -gt 3 ] ; then | ||
13 | echo "usage: export-to-postgresql-report [database name] [columns] [calls]" | ||
14 | exit | ||
15 | fi | ||
16 | if [ "$n_args" -gt 2 ] ; then | ||
17 | dbname=$1 | ||
18 | columns=$2 | ||
19 | calls=$3 | ||
20 | shift 3 | ||
21 | elif [ "$n_args" -gt 1 ] ; then | ||
22 | dbname=$1 | ||
23 | columns=$2 | ||
24 | shift 2 | ||
25 | elif [ "$n_args" -gt 0 ] ; then | ||
26 | dbname=$1 | ||
27 | shift | ||
28 | fi | ||
29 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls | ||
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py new file mode 100644 index 000000000000..4cdafd880074 --- /dev/null +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
@@ -0,0 +1,444 @@ | |||
1 | # export-to-postgresql.py: export perf data to a postgresql database | ||
2 | # Copyright (c) 2014, Intel Corporation. | ||
3 | # | ||
4 | # This program is free software; you can redistribute it and/or modify it | ||
5 | # under the terms and conditions of the GNU General Public License, | ||
6 | # version 2, as published by the Free Software Foundation. | ||
7 | # | ||
8 | # This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | # more details. | ||
12 | |||
13 | import os | ||
14 | import sys | ||
15 | import struct | ||
16 | import datetime | ||
17 | |||
18 | from PySide.QtSql import * | ||
19 | |||
20 | # Need to access PostgreSQL C library directly to use COPY FROM STDIN | ||
21 | from ctypes import * | ||
22 | libpq = CDLL("libpq.so.5") | ||
23 | PQconnectdb = libpq.PQconnectdb | ||
24 | PQconnectdb.restype = c_void_p | ||
25 | PQfinish = libpq.PQfinish | ||
26 | PQstatus = libpq.PQstatus | ||
27 | PQexec = libpq.PQexec | ||
28 | PQexec.restype = c_void_p | ||
29 | PQresultStatus = libpq.PQresultStatus | ||
30 | PQputCopyData = libpq.PQputCopyData | ||
31 | PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ] | ||
32 | PQputCopyEnd = libpq.PQputCopyEnd | ||
33 | PQputCopyEnd.argtypes = [ c_void_p, c_void_p ] | ||
34 | |||
35 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
36 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
37 | |||
38 | # These perf imports are not used at present | ||
39 | #from perf_trace_context import * | ||
40 | #from Core import * | ||
41 | |||
42 | perf_db_export_mode = True | ||
43 | perf_db_export_calls = False | ||
44 | |||
45 | def usage(): | ||
46 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]" | ||
47 | print >> sys.stderr, "where: columns 'all' or 'branches'" | ||
48 | print >> sys.stderr, " calls 'calls' => create calls table" | ||
49 | raise Exception("Too few arguments") | ||
50 | |||
51 | if (len(sys.argv) < 2): | ||
52 | usage() | ||
53 | |||
54 | dbname = sys.argv[1] | ||
55 | |||
56 | if (len(sys.argv) >= 3): | ||
57 | columns = sys.argv[2] | ||
58 | else: | ||
59 | columns = "all" | ||
60 | |||
61 | if columns not in ("all", "branches"): | ||
62 | usage() | ||
63 | |||
64 | branches = (columns == "branches") | ||
65 | |||
66 | if (len(sys.argv) >= 4): | ||
67 | if (sys.argv[3] == "calls"): | ||
68 | perf_db_export_calls = True | ||
69 | else: | ||
70 | usage() | ||
71 | |||
72 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" | ||
73 | os.mkdir(output_dir_name) | ||
74 | |||
75 | def do_query(q, s): | ||
76 | if (q.exec_(s)): | ||
77 | return | ||
78 | raise Exception("Query failed: " + q.lastError().text()) | ||
79 | |||
80 | print datetime.datetime.today(), "Creating database..." | ||
81 | |||
82 | db = QSqlDatabase.addDatabase('QPSQL') | ||
83 | query = QSqlQuery(db) | ||
84 | db.setDatabaseName('postgres') | ||
85 | db.open() | ||
86 | try: | ||
87 | do_query(query, 'CREATE DATABASE ' + dbname) | ||
88 | except: | ||
89 | os.rmdir(output_dir_name) | ||
90 | raise | ||
91 | query.finish() | ||
92 | query.clear() | ||
93 | db.close() | ||
94 | |||
95 | db.setDatabaseName(dbname) | ||
96 | db.open() | ||
97 | |||
98 | query = QSqlQuery(db) | ||
99 | do_query(query, 'SET client_min_messages TO WARNING') | ||
100 | |||
101 | do_query(query, 'CREATE TABLE selected_events (' | ||
102 | 'id bigint NOT NULL,' | ||
103 | 'name varchar(80))') | ||
104 | do_query(query, 'CREATE TABLE machines (' | ||
105 | 'id bigint NOT NULL,' | ||
106 | 'pid integer,' | ||
107 | 'root_dir varchar(4096))') | ||
108 | do_query(query, 'CREATE TABLE threads (' | ||
109 | 'id bigint NOT NULL,' | ||
110 | 'machine_id bigint,' | ||
111 | 'process_id bigint,' | ||
112 | 'pid integer,' | ||
113 | 'tid integer)') | ||
114 | do_query(query, 'CREATE TABLE comms (' | ||
115 | 'id bigint NOT NULL,' | ||
116 | 'comm varchar(16))') | ||
117 | do_query(query, 'CREATE TABLE comm_threads (' | ||
118 | 'id bigint NOT NULL,' | ||
119 | 'comm_id bigint,' | ||
120 | 'thread_id bigint)') | ||
121 | do_query(query, 'CREATE TABLE dsos (' | ||
122 | 'id bigint NOT NULL,' | ||
123 | 'machine_id bigint,' | ||
124 | 'short_name varchar(256),' | ||
125 | 'long_name varchar(4096),' | ||
126 | 'build_id varchar(64))') | ||
127 | do_query(query, 'CREATE TABLE symbols (' | ||
128 | 'id bigint NOT NULL,' | ||
129 | 'dso_id bigint,' | ||
130 | 'sym_start bigint,' | ||
131 | 'sym_end bigint,' | ||
132 | 'binding integer,' | ||
133 | 'name varchar(2048))') | ||
134 | do_query(query, 'CREATE TABLE branch_types (' | ||
135 | 'id integer NOT NULL,' | ||
136 | 'name varchar(80))') | ||
137 | |||
138 | if branches: | ||
139 | do_query(query, 'CREATE TABLE samples (' | ||
140 | 'id bigint NOT NULL,' | ||
141 | 'evsel_id bigint,' | ||
142 | 'machine_id bigint,' | ||
143 | 'thread_id bigint,' | ||
144 | 'comm_id bigint,' | ||
145 | 'dso_id bigint,' | ||
146 | 'symbol_id bigint,' | ||
147 | 'sym_offset bigint,' | ||
148 | 'ip bigint,' | ||
149 | 'time bigint,' | ||
150 | 'cpu integer,' | ||
151 | 'to_dso_id bigint,' | ||
152 | 'to_symbol_id bigint,' | ||
153 | 'to_sym_offset bigint,' | ||
154 | 'to_ip bigint,' | ||
155 | 'branch_type integer,' | ||
156 | 'in_tx boolean)') | ||
157 | else: | ||
158 | do_query(query, 'CREATE TABLE samples (' | ||
159 | 'id bigint NOT NULL,' | ||
160 | 'evsel_id bigint,' | ||
161 | 'machine_id bigint,' | ||
162 | 'thread_id bigint,' | ||
163 | 'comm_id bigint,' | ||
164 | 'dso_id bigint,' | ||
165 | 'symbol_id bigint,' | ||
166 | 'sym_offset bigint,' | ||
167 | 'ip bigint,' | ||
168 | 'time bigint,' | ||
169 | 'cpu integer,' | ||
170 | 'to_dso_id bigint,' | ||
171 | 'to_symbol_id bigint,' | ||
172 | 'to_sym_offset bigint,' | ||
173 | 'to_ip bigint,' | ||
174 | 'period bigint,' | ||
175 | 'weight bigint,' | ||
176 | 'transaction bigint,' | ||
177 | 'data_src bigint,' | ||
178 | 'branch_type integer,' | ||
179 | 'in_tx boolean)') | ||
180 | |||
181 | if perf_db_export_calls: | ||
182 | do_query(query, 'CREATE TABLE call_paths (' | ||
183 | 'id bigint NOT NULL,' | ||
184 | 'parent_id bigint,' | ||
185 | 'symbol_id bigint,' | ||
186 | 'ip bigint)') | ||
187 | do_query(query, 'CREATE TABLE calls (' | ||
188 | 'id bigint NOT NULL,' | ||
189 | 'thread_id bigint,' | ||
190 | 'comm_id bigint,' | ||
191 | 'call_path_id bigint,' | ||
192 | 'call_time bigint,' | ||
193 | 'return_time bigint,' | ||
194 | 'branch_count bigint,' | ||
195 | 'call_id bigint,' | ||
196 | 'return_id bigint,' | ||
197 | 'parent_call_path_id bigint,' | ||
198 | 'flags integer)') | ||
199 | |||
200 | do_query(query, 'CREATE VIEW samples_view AS ' | ||
201 | 'SELECT ' | ||
202 | 'id,' | ||
203 | 'time,' | ||
204 | 'cpu,' | ||
205 | '(SELECT pid FROM threads WHERE id = thread_id) AS pid,' | ||
206 | '(SELECT tid FROM threads WHERE id = thread_id) AS tid,' | ||
207 | '(SELECT comm FROM comms WHERE id = comm_id) AS command,' | ||
208 | '(SELECT name FROM selected_events WHERE id = evsel_id) AS event,' | ||
209 | 'to_hex(ip) AS ip_hex,' | ||
210 | '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,' | ||
211 | 'sym_offset,' | ||
212 | '(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,' | ||
213 | 'to_hex(to_ip) AS to_ip_hex,' | ||
214 | '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' | ||
215 | 'to_sym_offset,' | ||
216 | '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' | ||
217 | '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' | ||
218 | 'in_tx' | ||
219 | ' FROM samples') | ||
220 | |||
221 | |||
222 | file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0) | ||
223 | file_trailer = "\377\377" | ||
224 | |||
225 | def open_output_file(file_name): | ||
226 | path_name = output_dir_name + "/" + file_name | ||
227 | file = open(path_name, "w+") | ||
228 | file.write(file_header) | ||
229 | return file | ||
230 | |||
231 | def close_output_file(file): | ||
232 | file.write(file_trailer) | ||
233 | file.close() | ||
234 | |||
235 | def copy_output_file_direct(file, table_name): | ||
236 | close_output_file(file) | ||
237 | sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')" | ||
238 | do_query(query, sql) | ||
239 | |||
240 | # Use COPY FROM STDIN because security may prevent postgres from accessing the files directly | ||
241 | def copy_output_file(file, table_name): | ||
242 | conn = PQconnectdb("dbname = " + dbname) | ||
243 | if (PQstatus(conn)): | ||
244 | raise Exception("COPY FROM STDIN PQconnectdb failed") | ||
245 | file.write(file_trailer) | ||
246 | file.seek(0) | ||
247 | sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" | ||
248 | res = PQexec(conn, sql) | ||
249 | if (PQresultStatus(res) != 4): | ||
250 | raise Exception("COPY FROM STDIN PQexec failed") | ||
251 | data = file.read(65536) | ||
252 | while (len(data)): | ||
253 | ret = PQputCopyData(conn, data, len(data)) | ||
254 | if (ret != 1): | ||
255 | raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret)) | ||
256 | data = file.read(65536) | ||
257 | ret = PQputCopyEnd(conn, None) | ||
258 | if (ret != 1): | ||
259 | raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret)) | ||
260 | PQfinish(conn) | ||
261 | |||
262 | def remove_output_file(file): | ||
263 | name = file.name | ||
264 | file.close() | ||
265 | os.unlink(name) | ||
266 | |||
267 | evsel_file = open_output_file("evsel_table.bin") | ||
268 | machine_file = open_output_file("machine_table.bin") | ||
269 | thread_file = open_output_file("thread_table.bin") | ||
270 | comm_file = open_output_file("comm_table.bin") | ||
271 | comm_thread_file = open_output_file("comm_thread_table.bin") | ||
272 | dso_file = open_output_file("dso_table.bin") | ||
273 | symbol_file = open_output_file("symbol_table.bin") | ||
274 | branch_type_file = open_output_file("branch_type_table.bin") | ||
275 | sample_file = open_output_file("sample_table.bin") | ||
276 | if perf_db_export_calls: | ||
277 | call_path_file = open_output_file("call_path_table.bin") | ||
278 | call_file = open_output_file("call_table.bin") | ||
279 | |||
280 | def trace_begin(): | ||
281 | print datetime.datetime.today(), "Writing to intermediate files..." | ||
282 | # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs | ||
283 | evsel_table(0, "unknown") | ||
284 | machine_table(0, 0, "unknown") | ||
285 | thread_table(0, 0, 0, -1, -1) | ||
286 | comm_table(0, "unknown") | ||
287 | dso_table(0, 0, "unknown", "unknown", "") | ||
288 | symbol_table(0, 0, 0, 0, 0, "unknown") | ||
289 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
290 | if perf_db_export_calls: | ||
291 | call_path_table(0, 0, 0, 0) | ||
292 | |||
293 | unhandled_count = 0 | ||
294 | |||
295 | def trace_end(): | ||
296 | print datetime.datetime.today(), "Copying to database..." | ||
297 | copy_output_file(evsel_file, "selected_events") | ||
298 | copy_output_file(machine_file, "machines") | ||
299 | copy_output_file(thread_file, "threads") | ||
300 | copy_output_file(comm_file, "comms") | ||
301 | copy_output_file(comm_thread_file, "comm_threads") | ||
302 | copy_output_file(dso_file, "dsos") | ||
303 | copy_output_file(symbol_file, "symbols") | ||
304 | copy_output_file(branch_type_file, "branch_types") | ||
305 | copy_output_file(sample_file, "samples") | ||
306 | if perf_db_export_calls: | ||
307 | copy_output_file(call_path_file, "call_paths") | ||
308 | copy_output_file(call_file, "calls") | ||
309 | |||
310 | print datetime.datetime.today(), "Removing intermediate files..." | ||
311 | remove_output_file(evsel_file) | ||
312 | remove_output_file(machine_file) | ||
313 | remove_output_file(thread_file) | ||
314 | remove_output_file(comm_file) | ||
315 | remove_output_file(comm_thread_file) | ||
316 | remove_output_file(dso_file) | ||
317 | remove_output_file(symbol_file) | ||
318 | remove_output_file(branch_type_file) | ||
319 | remove_output_file(sample_file) | ||
320 | if perf_db_export_calls: | ||
321 | remove_output_file(call_path_file) | ||
322 | remove_output_file(call_file) | ||
323 | os.rmdir(output_dir_name) | ||
324 | print datetime.datetime.today(), "Adding primary keys" | ||
325 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') | ||
326 | do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') | ||
327 | do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') | ||
328 | do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)') | ||
329 | do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') | ||
330 | do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') | ||
331 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') | ||
332 | do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') | ||
333 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') | ||
334 | if perf_db_export_calls: | ||
335 | do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') | ||
336 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') | ||
337 | |||
338 | print datetime.datetime.today(), "Adding foreign keys" | ||
339 | do_query(query, 'ALTER TABLE threads ' | ||
340 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' | ||
341 | 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') | ||
342 | do_query(query, 'ALTER TABLE comm_threads ' | ||
343 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
344 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)') | ||
345 | do_query(query, 'ALTER TABLE dsos ' | ||
346 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id)') | ||
347 | do_query(query, 'ALTER TABLE symbols ' | ||
348 | 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id)') | ||
349 | do_query(query, 'ALTER TABLE samples ' | ||
350 | 'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES selected_events (id),' | ||
351 | 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' | ||
352 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
353 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
354 | 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id),' | ||
355 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' | ||
356 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' | ||
357 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') | ||
358 | if perf_db_export_calls: | ||
359 | do_query(query, 'ALTER TABLE call_paths ' | ||
360 | 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' | ||
361 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') | ||
362 | do_query(query, 'ALTER TABLE calls ' | ||
363 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
364 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
365 | 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' | ||
366 | 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' | ||
367 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' | ||
368 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') | ||
369 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') | ||
370 | |||
371 | if (unhandled_count): | ||
372 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" | ||
373 | print datetime.datetime.today(), "Done" | ||
374 | |||
375 | def trace_unhandled(event_name, context, event_fields_dict): | ||
376 | global unhandled_count | ||
377 | unhandled_count += 1 | ||
378 | |||
379 | def sched__sched_switch(*x): | ||
380 | pass | ||
381 | |||
382 | def evsel_table(evsel_id, evsel_name, *x): | ||
383 | n = len(evsel_name) | ||
384 | fmt = "!hiqi" + str(n) + "s" | ||
385 | value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) | ||
386 | evsel_file.write(value) | ||
387 | |||
388 | def machine_table(machine_id, pid, root_dir, *x): | ||
389 | n = len(root_dir) | ||
390 | fmt = "!hiqiii" + str(n) + "s" | ||
391 | value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) | ||
392 | machine_file.write(value) | ||
393 | |||
394 | def thread_table(thread_id, machine_id, process_id, pid, tid, *x): | ||
395 | value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid) | ||
396 | thread_file.write(value) | ||
397 | |||
398 | def comm_table(comm_id, comm_str, *x): | ||
399 | n = len(comm_str) | ||
400 | fmt = "!hiqi" + str(n) + "s" | ||
401 | value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) | ||
402 | comm_file.write(value) | ||
403 | |||
404 | def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): | ||
405 | fmt = "!hiqiqiq" | ||
406 | value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id) | ||
407 | comm_thread_file.write(value) | ||
408 | |||
409 | def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): | ||
410 | n1 = len(short_name) | ||
411 | n2 = len(long_name) | ||
412 | n3 = len(build_id) | ||
413 | fmt = "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s" | ||
414 | value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id) | ||
415 | dso_file.write(value) | ||
416 | |||
417 | def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x): | ||
418 | n = len(symbol_name) | ||
419 | fmt = "!hiqiqiqiqiii" + str(n) + "s" | ||
420 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) | ||
421 | symbol_file.write(value) | ||
422 | |||
423 | def branch_type_table(branch_type, name, *x): | ||
424 | n = len(name) | ||
425 | fmt = "!hiii" + str(n) + "s" | ||
426 | value = struct.pack(fmt, 2, 4, branch_type, n, name) | ||
427 | branch_type_file.write(value) | ||
428 | |||
429 | def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x): | ||
430 | if branches: | ||
431 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx) | ||
432 | else: | ||
433 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx) | ||
434 | sample_file.write(value) | ||
435 | |||
436 | def call_path_table(cp_id, parent_id, symbol_id, ip, *x): | ||
437 | fmt = "!hiqiqiqiq" | ||
438 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) | ||
439 | call_path_file.write(value) | ||
440 | |||
441 | def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x): | ||
442 | fmt = "!hiqiqiqiqiqiqiqiqiqiqii" | ||
443 | value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags) | ||
444 | call_file.write(value) | ||
diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record index f710b92ccff6..d3095dafed36 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/attr/base-record | |||
@@ -5,7 +5,7 @@ group_fd=-1 | |||
5 | flags=0|8 | 5 | flags=0|8 |
6 | cpu=* | 6 | cpu=* |
7 | type=0|1 | 7 | type=0|1 |
8 | size=96 | 8 | size=104 |
9 | config=0 | 9 | config=0 |
10 | sample_period=4000 | 10 | sample_period=4000 |
11 | sample_type=263 | 11 | sample_type=263 |
diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat index dc3ada2470c0..872ed7e24c7c 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/attr/base-stat | |||
@@ -5,7 +5,7 @@ group_fd=-1 | |||
5 | flags=0|8 | 5 | flags=0|8 |
6 | cpu=* | 6 | cpu=* |
7 | type=0 | 7 | type=0 |
8 | size=96 | 8 | size=104 |
9 | config=0 | 9 | config=0 |
10 | sample_period=0 | 10 | sample_period=0 |
11 | sample_type=0 | 11 | sample_type=0 |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 6f8b01bc6033..4b7d9ab0f049 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <unistd.h> | 6 | #include <unistd.h> |
7 | #include <string.h> | 7 | #include <string.h> |
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | #include "hist.h" | ||
9 | #include "intlist.h" | 10 | #include "intlist.h" |
10 | #include "tests.h" | 11 | #include "tests.h" |
11 | #include "debug.h" | 12 | #include "debug.h" |
@@ -84,7 +85,7 @@ static struct test { | |||
84 | .func = test__hists_link, | 85 | .func = test__hists_link, |
85 | }, | 86 | }, |
86 | { | 87 | { |
87 | .desc = "Try 'use perf' in python, checking link problems", | 88 | .desc = "Try 'import perf' in python, checking link problems", |
88 | .func = test__python_use, | 89 | .func = test__python_use, |
89 | }, | 90 | }, |
90 | { | 91 | { |
@@ -154,6 +155,18 @@ static struct test { | |||
154 | .func = test__hists_cumulate, | 155 | .func = test__hists_cumulate, |
155 | }, | 156 | }, |
156 | { | 157 | { |
158 | .desc = "Test tracking with sched_switch", | ||
159 | .func = test__switch_tracking, | ||
160 | }, | ||
161 | { | ||
162 | .desc = "Filter fds with revents mask in a fdarray", | ||
163 | .func = test__fdarray__filter, | ||
164 | }, | ||
165 | { | ||
166 | .desc = "Add fd to a fdarray, making it autogrow", | ||
167 | .func = test__fdarray__add, | ||
168 | }, | ||
169 | { | ||
157 | .func = NULL, | 170 | .func = NULL, |
158 | }, | 171 | }, |
159 | }; | 172 | }; |
@@ -185,9 +198,11 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
185 | static int run_test(struct test *test) | 198 | static int run_test(struct test *test) |
186 | { | 199 | { |
187 | int status, err = -1, child = fork(); | 200 | int status, err = -1, child = fork(); |
201 | char sbuf[STRERR_BUFSIZE]; | ||
188 | 202 | ||
189 | if (child < 0) { | 203 | if (child < 0) { |
190 | pr_err("failed to fork test: %s\n", strerror(errno)); | 204 | pr_err("failed to fork test: %s\n", |
205 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
191 | return -1; | 206 | return -1; |
192 | } | 207 | } |
193 | 208 | ||
@@ -288,6 +303,10 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
288 | OPT_END() | 303 | OPT_END() |
289 | }; | 304 | }; |
290 | struct intlist *skiplist = NULL; | 305 | struct intlist *skiplist = NULL; |
306 | int ret = hists__init(); | ||
307 | |||
308 | if (ret < 0) | ||
309 | return ret; | ||
291 | 310 | ||
292 | argc = parse_options(argc, argv, test_options, test_usage, 0); | 311 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
293 | if (argc >= 1 && !strcmp(argv[0], "list")) | 312 | if (argc >= 1 && !strcmp(argv[0], "list")) |
@@ -297,7 +316,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
297 | symbol_conf.sort_by_name = true; | 316 | symbol_conf.sort_by_name = true; |
298 | symbol_conf.try_vmlinux_path = true; | 317 | symbol_conf.try_vmlinux_path = true; |
299 | 318 | ||
300 | if (symbol__init() < 0) | 319 | if (symbol__init(NULL) < 0) |
301 | return -1; | 320 | return -1; |
302 | 321 | ||
303 | if (skip != NULL) | 322 | if (skip != NULL) |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 67f2d6323558..f671ec37a7c4 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -133,8 +133,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf, | |||
133 | } | 133 | } |
134 | 134 | ||
135 | static int read_object_code(u64 addr, size_t len, u8 cpumode, | 135 | static int read_object_code(u64 addr, size_t len, u8 cpumode, |
136 | struct thread *thread, struct machine *machine, | 136 | struct thread *thread, struct state *state) |
137 | struct state *state) | ||
138 | { | 137 | { |
139 | struct addr_location al; | 138 | struct addr_location al; |
140 | unsigned char buf1[BUFSZ]; | 139 | unsigned char buf1[BUFSZ]; |
@@ -145,8 +144,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, | |||
145 | 144 | ||
146 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); | 145 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); |
147 | 146 | ||
148 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr, | 147 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, addr, &al); |
149 | &al); | ||
150 | if (!al.map || !al.map->dso) { | 148 | if (!al.map || !al.map->dso) { |
151 | pr_debug("thread__find_addr_map failed\n"); | 149 | pr_debug("thread__find_addr_map failed\n"); |
152 | return -1; | 150 | return -1; |
@@ -170,8 +168,8 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, | |||
170 | len = al.map->end - addr; | 168 | len = al.map->end - addr; |
171 | 169 | ||
172 | /* Read the object code using perf */ | 170 | /* Read the object code using perf */ |
173 | ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1, | 171 | ret_len = dso__data_read_offset(al.map->dso, thread->mg->machine, |
174 | len); | 172 | al.addr, buf1, len); |
175 | if (ret_len != len) { | 173 | if (ret_len != len) { |
176 | pr_debug("dso__data_read_offset failed\n"); | 174 | pr_debug("dso__data_read_offset failed\n"); |
177 | return -1; | 175 | return -1; |
@@ -264,8 +262,7 @@ static int process_sample_event(struct machine *machine, | |||
264 | 262 | ||
265 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 263 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
266 | 264 | ||
267 | return read_object_code(sample.ip, READLEN, cpumode, thread, machine, | 265 | return read_object_code(sample.ip, READLEN, cpumode, thread, state); |
268 | state); | ||
269 | } | 266 | } |
270 | 267 | ||
271 | static int process_event(struct machine *machine, struct perf_evlist *evlist, | 268 | static int process_event(struct machine *machine, struct perf_evlist *evlist, |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 96adb730b744..ab28cca2cb97 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "perf_regs.h" | 9 | #include "perf_regs.h" |
10 | #include "map.h" | 10 | #include "map.h" |
11 | #include "thread.h" | 11 | #include "thread.h" |
12 | #include "callchain.h" | ||
12 | 13 | ||
13 | static int mmap_handler(struct perf_tool *tool __maybe_unused, | 14 | static int mmap_handler(struct perf_tool *tool __maybe_unused, |
14 | union perf_event *event, | 15 | union perf_event *event, |
@@ -58,7 +59,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
58 | } | 59 | } |
59 | 60 | ||
60 | __attribute__ ((noinline)) | 61 | __attribute__ ((noinline)) |
61 | static int unwind_thread(struct thread *thread, struct machine *machine) | 62 | static int unwind_thread(struct thread *thread) |
62 | { | 63 | { |
63 | struct perf_sample sample; | 64 | struct perf_sample sample; |
64 | unsigned long cnt = 0; | 65 | unsigned long cnt = 0; |
@@ -71,7 +72,7 @@ static int unwind_thread(struct thread *thread, struct machine *machine) | |||
71 | goto out; | 72 | goto out; |
72 | } | 73 | } |
73 | 74 | ||
74 | err = unwind__get_entries(unwind_entry, &cnt, machine, thread, | 75 | err = unwind__get_entries(unwind_entry, &cnt, thread, |
75 | &sample, MAX_STACK); | 76 | &sample, MAX_STACK); |
76 | if (err) | 77 | if (err) |
77 | pr_debug("unwind failed\n"); | 78 | pr_debug("unwind failed\n"); |
@@ -88,21 +89,21 @@ static int unwind_thread(struct thread *thread, struct machine *machine) | |||
88 | } | 89 | } |
89 | 90 | ||
90 | __attribute__ ((noinline)) | 91 | __attribute__ ((noinline)) |
91 | static int krava_3(struct thread *thread, struct machine *machine) | 92 | static int krava_3(struct thread *thread) |
92 | { | 93 | { |
93 | return unwind_thread(thread, machine); | 94 | return unwind_thread(thread); |
94 | } | 95 | } |
95 | 96 | ||
96 | __attribute__ ((noinline)) | 97 | __attribute__ ((noinline)) |
97 | static int krava_2(struct thread *thread, struct machine *machine) | 98 | static int krava_2(struct thread *thread) |
98 | { | 99 | { |
99 | return krava_3(thread, machine); | 100 | return krava_3(thread); |
100 | } | 101 | } |
101 | 102 | ||
102 | __attribute__ ((noinline)) | 103 | __attribute__ ((noinline)) |
103 | static int krava_1(struct thread *thread, struct machine *machine) | 104 | static int krava_1(struct thread *thread) |
104 | { | 105 | { |
105 | return krava_2(thread, machine); | 106 | return krava_2(thread); |
106 | } | 107 | } |
107 | 108 | ||
108 | int test__dwarf_unwind(void) | 109 | int test__dwarf_unwind(void) |
@@ -120,6 +121,8 @@ int test__dwarf_unwind(void) | |||
120 | return -1; | 121 | return -1; |
121 | } | 122 | } |
122 | 123 | ||
124 | callchain_param.record_mode = CALLCHAIN_DWARF; | ||
125 | |||
123 | if (init_live_machine(machine)) { | 126 | if (init_live_machine(machine)) { |
124 | pr_err("Could not init machine\n"); | 127 | pr_err("Could not init machine\n"); |
125 | goto out; | 128 | goto out; |
@@ -134,7 +137,7 @@ int test__dwarf_unwind(void) | |||
134 | goto out; | 137 | goto out; |
135 | } | 138 | } |
136 | 139 | ||
137 | err = krava_1(thread, machine); | 140 | err = krava_1(thread); |
138 | 141 | ||
139 | out: | 142 | out: |
140 | machine__delete_threads(machine); | 143 | machine__delete_threads(machine); |
diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c new file mode 100644 index 000000000000..d24b837951d4 --- /dev/null +++ b/tools/perf/tests/fdarray.c | |||
@@ -0,0 +1,174 @@ | |||
1 | #include <api/fd/array.h> | ||
2 | #include "util/debug.h" | ||
3 | #include "tests/tests.h" | ||
4 | |||
5 | static void fdarray__init_revents(struct fdarray *fda, short revents) | ||
6 | { | ||
7 | int fd; | ||
8 | |||
9 | fda->nr = fda->nr_alloc; | ||
10 | |||
11 | for (fd = 0; fd < fda->nr; ++fd) { | ||
12 | fda->entries[fd].fd = fda->nr - fd; | ||
13 | fda->entries[fd].revents = revents; | ||
14 | } | ||
15 | } | ||
16 | |||
17 | static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE *fp) | ||
18 | { | ||
19 | int printed = 0; | ||
20 | |||
21 | if (!verbose) | ||
22 | return 0; | ||
23 | |||
24 | printed += fprintf(fp, "\n%s: ", prefix); | ||
25 | return printed + fdarray__fprintf(fda, fp); | ||
26 | } | ||
27 | |||
28 | int test__fdarray__filter(void) | ||
29 | { | ||
30 | int nr_fds, expected_fd[2], fd, err = TEST_FAIL; | ||
31 | struct fdarray *fda = fdarray__new(5, 5); | ||
32 | |||
33 | if (fda == NULL) { | ||
34 | pr_debug("\nfdarray__new() failed!"); | ||
35 | goto out; | ||
36 | } | ||
37 | |||
38 | fdarray__init_revents(fda, POLLIN); | ||
39 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
40 | if (nr_fds != fda->nr_alloc) { | ||
41 | pr_debug("\nfdarray__filter()=%d != %d shouldn't have filtered anything", | ||
42 | nr_fds, fda->nr_alloc); | ||
43 | goto out_delete; | ||
44 | } | ||
45 | |||
46 | fdarray__init_revents(fda, POLLHUP); | ||
47 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
48 | if (nr_fds != 0) { | ||
49 | pr_debug("\nfdarray__filter()=%d != %d, should have filtered all fds", | ||
50 | nr_fds, fda->nr_alloc); | ||
51 | goto out_delete; | ||
52 | } | ||
53 | |||
54 | fdarray__init_revents(fda, POLLHUP); | ||
55 | fda->entries[2].revents = POLLIN; | ||
56 | expected_fd[0] = fda->entries[2].fd; | ||
57 | |||
58 | pr_debug("\nfiltering all but fda->entries[2]:"); | ||
59 | fdarray__fprintf_prefix(fda, "before", stderr); | ||
60 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
61 | fdarray__fprintf_prefix(fda, " after", stderr); | ||
62 | if (nr_fds != 1) { | ||
63 | pr_debug("\nfdarray__filter()=%d != 1, should have left just one event", nr_fds); | ||
64 | goto out_delete; | ||
65 | } | ||
66 | |||
67 | if (fda->entries[0].fd != expected_fd[0]) { | ||
68 | pr_debug("\nfda->entries[0].fd=%d != %d\n", | ||
69 | fda->entries[0].fd, expected_fd[0]); | ||
70 | goto out_delete; | ||
71 | } | ||
72 | |||
73 | fdarray__init_revents(fda, POLLHUP); | ||
74 | fda->entries[0].revents = POLLIN; | ||
75 | expected_fd[0] = fda->entries[0].fd; | ||
76 | fda->entries[3].revents = POLLIN; | ||
77 | expected_fd[1] = fda->entries[3].fd; | ||
78 | |||
79 | pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):"); | ||
80 | fdarray__fprintf_prefix(fda, "before", stderr); | ||
81 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
82 | fdarray__fprintf_prefix(fda, " after", stderr); | ||
83 | if (nr_fds != 2) { | ||
84 | pr_debug("\nfdarray__filter()=%d != 2, should have left just two events", | ||
85 | nr_fds); | ||
86 | goto out_delete; | ||
87 | } | ||
88 | |||
89 | for (fd = 0; fd < 2; ++fd) { | ||
90 | if (fda->entries[fd].fd != expected_fd[fd]) { | ||
91 | pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd, | ||
92 | fda->entries[fd].fd, expected_fd[fd]); | ||
93 | goto out_delete; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | pr_debug("\n"); | ||
98 | |||
99 | err = 0; | ||
100 | out_delete: | ||
101 | fdarray__delete(fda); | ||
102 | out: | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | int test__fdarray__add(void) | ||
107 | { | ||
108 | int err = TEST_FAIL; | ||
109 | struct fdarray *fda = fdarray__new(2, 2); | ||
110 | |||
111 | if (fda == NULL) { | ||
112 | pr_debug("\nfdarray__new() failed!"); | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | #define FDA_CHECK(_idx, _fd, _revents) \ | ||
117 | if (fda->entries[_idx].fd != _fd) { \ | ||
118 | pr_debug("\n%d: fda->entries[%d](%d) != %d!", \ | ||
119 | __LINE__, _idx, fda->entries[1].fd, _fd); \ | ||
120 | goto out_delete; \ | ||
121 | } \ | ||
122 | if (fda->entries[_idx].events != (_revents)) { \ | ||
123 | pr_debug("\n%d: fda->entries[%d].revents(%d) != %d!", \ | ||
124 | __LINE__, _idx, fda->entries[_idx].fd, _revents); \ | ||
125 | goto out_delete; \ | ||
126 | } | ||
127 | |||
128 | #define FDA_ADD(_idx, _fd, _revents, _nr) \ | ||
129 | if (fdarray__add(fda, _fd, _revents) < 0) { \ | ||
130 | pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \ | ||
131 | __LINE__,_fd, _revents); \ | ||
132 | goto out_delete; \ | ||
133 | } \ | ||
134 | if (fda->nr != _nr) { \ | ||
135 | pr_debug("\n%d: fdarray__add(fda, %d, %d)=%d != %d", \ | ||
136 | __LINE__,_fd, _revents, fda->nr, _nr); \ | ||
137 | goto out_delete; \ | ||
138 | } \ | ||
139 | FDA_CHECK(_idx, _fd, _revents) | ||
140 | |||
141 | FDA_ADD(0, 1, POLLIN, 1); | ||
142 | FDA_ADD(1, 2, POLLERR, 2); | ||
143 | |||
144 | fdarray__fprintf_prefix(fda, "before growing array", stderr); | ||
145 | |||
146 | FDA_ADD(2, 35, POLLHUP, 3); | ||
147 | |||
148 | if (fda->entries == NULL) { | ||
149 | pr_debug("\nfdarray__add(fda, 35, POLLHUP) should have allocated fda->pollfd!"); | ||
150 | goto out_delete; | ||
151 | } | ||
152 | |||
153 | fdarray__fprintf_prefix(fda, "after 3rd add", stderr); | ||
154 | |||
155 | FDA_ADD(3, 88, POLLIN | POLLOUT, 4); | ||
156 | |||
157 | fdarray__fprintf_prefix(fda, "after 4th add", stderr); | ||
158 | |||
159 | FDA_CHECK(0, 1, POLLIN); | ||
160 | FDA_CHECK(1, 2, POLLERR); | ||
161 | FDA_CHECK(2, 35, POLLHUP); | ||
162 | FDA_CHECK(3, 88, POLLIN | POLLOUT); | ||
163 | |||
164 | #undef FDA_ADD | ||
165 | #undef FDA_CHECK | ||
166 | |||
167 | pr_debug("\n"); | ||
168 | |||
169 | err = 0; | ||
170 | out_delete: | ||
171 | fdarray__delete(fda); | ||
172 | out: | ||
173 | return err; | ||
174 | } | ||
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 0ac240db2e24..8d110dec393e 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
@@ -187,7 +187,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec | |||
187 | * function since TEST_ASSERT_VAL() returns in case of failure. | 187 | * function since TEST_ASSERT_VAL() returns in case of failure. |
188 | */ | 188 | */ |
189 | hists__collapse_resort(hists, NULL); | 189 | hists__collapse_resort(hists, NULL); |
190 | hists__output_resort(hists); | 190 | hists__output_resort(hists, NULL); |
191 | 191 | ||
192 | if (verbose > 2) { | 192 | if (verbose > 2) { |
193 | pr_info("use callchain: %d, cumulate callchain: %d\n", | 193 | pr_info("use callchain: %d, cumulate callchain: %d\n", |
@@ -245,7 +245,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec | |||
245 | static int test1(struct perf_evsel *evsel, struct machine *machine) | 245 | static int test1(struct perf_evsel *evsel, struct machine *machine) |
246 | { | 246 | { |
247 | int err; | 247 | int err; |
248 | struct hists *hists = &evsel->hists; | 248 | struct hists *hists = evsel__hists(evsel); |
249 | /* | 249 | /* |
250 | * expected output: | 250 | * expected output: |
251 | * | 251 | * |
@@ -295,7 +295,7 @@ out: | |||
295 | static int test2(struct perf_evsel *evsel, struct machine *machine) | 295 | static int test2(struct perf_evsel *evsel, struct machine *machine) |
296 | { | 296 | { |
297 | int err; | 297 | int err; |
298 | struct hists *hists = &evsel->hists; | 298 | struct hists *hists = evsel__hists(evsel); |
299 | /* | 299 | /* |
300 | * expected output: | 300 | * expected output: |
301 | * | 301 | * |
@@ -442,7 +442,7 @@ out: | |||
442 | static int test3(struct perf_evsel *evsel, struct machine *machine) | 442 | static int test3(struct perf_evsel *evsel, struct machine *machine) |
443 | { | 443 | { |
444 | int err; | 444 | int err; |
445 | struct hists *hists = &evsel->hists; | 445 | struct hists *hists = evsel__hists(evsel); |
446 | /* | 446 | /* |
447 | * expected output: | 447 | * expected output: |
448 | * | 448 | * |
@@ -454,12 +454,12 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
454 | * 30.00% 10.00% perf perf [.] cmd_record | 454 | * 30.00% 10.00% perf perf [.] cmd_record |
455 | * 20.00% 0.00% bash libc [.] malloc | 455 | * 20.00% 0.00% bash libc [.] malloc |
456 | * 10.00% 10.00% bash [kernel] [k] page_fault | 456 | * 10.00% 10.00% bash [kernel] [k] page_fault |
457 | * 10.00% 10.00% perf [kernel] [k] schedule | 457 | * 10.00% 10.00% bash bash [.] xmalloc |
458 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | ||
459 | * 10.00% 10.00% perf [kernel] [k] page_fault | 458 | * 10.00% 10.00% perf [kernel] [k] page_fault |
460 | * 10.00% 10.00% perf libc [.] free | ||
461 | * 10.00% 10.00% perf libc [.] malloc | 459 | * 10.00% 10.00% perf libc [.] malloc |
462 | * 10.00% 10.00% bash bash [.] xmalloc | 460 | * 10.00% 10.00% perf [kernel] [k] schedule |
461 | * 10.00% 10.00% perf libc [.] free | ||
462 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | ||
463 | */ | 463 | */ |
464 | struct result expected[] = { | 464 | struct result expected[] = { |
465 | { 7000, 2000, "perf", "perf", "main" }, | 465 | { 7000, 2000, "perf", "perf", "main" }, |
@@ -468,12 +468,12 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
468 | { 3000, 1000, "perf", "perf", "cmd_record" }, | 468 | { 3000, 1000, "perf", "perf", "cmd_record" }, |
469 | { 2000, 0, "bash", "libc", "malloc" }, | 469 | { 2000, 0, "bash", "libc", "malloc" }, |
470 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, | 470 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, |
471 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | 471 | { 1000, 1000, "bash", "bash", "xmalloc" }, |
472 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, | ||
473 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, | 472 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, |
473 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | ||
474 | { 1000, 1000, "perf", "libc", "free" }, | 474 | { 1000, 1000, "perf", "libc", "free" }, |
475 | { 1000, 1000, "perf", "libc", "malloc" }, | 475 | { 1000, 1000, "perf", "libc", "malloc" }, |
476 | { 1000, 1000, "bash", "bash", "xmalloc" }, | 476 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, |
477 | }; | 477 | }; |
478 | 478 | ||
479 | symbol_conf.use_callchain = false; | 479 | symbol_conf.use_callchain = false; |
@@ -498,7 +498,7 @@ out: | |||
498 | static int test4(struct perf_evsel *evsel, struct machine *machine) | 498 | static int test4(struct perf_evsel *evsel, struct machine *machine) |
499 | { | 499 | { |
500 | int err; | 500 | int err; |
501 | struct hists *hists = &evsel->hists; | 501 | struct hists *hists = evsel__hists(evsel); |
502 | /* | 502 | /* |
503 | * expected output: | 503 | * expected output: |
504 | * | 504 | * |
@@ -537,10 +537,13 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
537 | * malloc | 537 | * malloc |
538 | * main | 538 | * main |
539 | * | 539 | * |
540 | * 10.00% 10.00% perf [kernel] [k] schedule | 540 | * 10.00% 10.00% bash bash [.] xmalloc |
541 | * | | 541 | * | |
542 | * --- schedule | 542 | * --- xmalloc |
543 | * run_command | 543 | * malloc |
544 | * xmalloc <--- NOTE: there's a cycle | ||
545 | * malloc | ||
546 | * xmalloc | ||
544 | * main | 547 | * main |
545 | * | 548 | * |
546 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | 549 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open |
@@ -556,6 +559,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
556 | * run_command | 559 | * run_command |
557 | * main | 560 | * main |
558 | * | 561 | * |
562 | * 10.00% 10.00% perf [kernel] [k] schedule | ||
563 | * | | ||
564 | * --- schedule | ||
565 | * run_command | ||
566 | * main | ||
567 | * | ||
559 | * 10.00% 10.00% perf libc [.] free | 568 | * 10.00% 10.00% perf libc [.] free |
560 | * | | 569 | * | |
561 | * --- free | 570 | * --- free |
@@ -570,15 +579,6 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
570 | * run_command | 579 | * run_command |
571 | * main | 580 | * main |
572 | * | 581 | * |
573 | * 10.00% 10.00% bash bash [.] xmalloc | ||
574 | * | | ||
575 | * --- xmalloc | ||
576 | * malloc | ||
577 | * xmalloc <--- NOTE: there's a cycle | ||
578 | * malloc | ||
579 | * xmalloc | ||
580 | * main | ||
581 | * | ||
582 | */ | 582 | */ |
583 | struct result expected[] = { | 583 | struct result expected[] = { |
584 | { 7000, 2000, "perf", "perf", "main" }, | 584 | { 7000, 2000, "perf", "perf", "main" }, |
@@ -587,12 +587,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
587 | { 3000, 1000, "perf", "perf", "cmd_record" }, | 587 | { 3000, 1000, "perf", "perf", "cmd_record" }, |
588 | { 2000, 0, "bash", "libc", "malloc" }, | 588 | { 2000, 0, "bash", "libc", "malloc" }, |
589 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, | 589 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, |
590 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | 590 | { 1000, 1000, "bash", "bash", "xmalloc" }, |
591 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, | 591 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, |
592 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, | 592 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, |
593 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | ||
593 | { 1000, 1000, "perf", "libc", "free" }, | 594 | { 1000, 1000, "perf", "libc", "free" }, |
594 | { 1000, 1000, "perf", "libc", "malloc" }, | 595 | { 1000, 1000, "perf", "libc", "malloc" }, |
595 | { 1000, 1000, "bash", "bash", "xmalloc" }, | ||
596 | }; | 596 | }; |
597 | struct callchain_result expected_callchain[] = { | 597 | struct callchain_result expected_callchain[] = { |
598 | { | 598 | { |
@@ -622,9 +622,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
622 | { "bash", "main" }, }, | 622 | { "bash", "main" }, }, |
623 | }, | 623 | }, |
624 | { | 624 | { |
625 | 3, { { "[kernel]", "schedule" }, | 625 | 6, { { "bash", "xmalloc" }, |
626 | { "perf", "run_command" }, | 626 | { "libc", "malloc" }, |
627 | { "perf", "main" }, }, | 627 | { "bash", "xmalloc" }, |
628 | { "libc", "malloc" }, | ||
629 | { "bash", "xmalloc" }, | ||
630 | { "bash", "main" }, }, | ||
628 | }, | 631 | }, |
629 | { | 632 | { |
630 | 3, { { "[kernel]", "sys_perf_event_open" }, | 633 | 3, { { "[kernel]", "sys_perf_event_open" }, |
@@ -638,6 +641,11 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
638 | { "perf", "main" }, }, | 641 | { "perf", "main" }, }, |
639 | }, | 642 | }, |
640 | { | 643 | { |
644 | 3, { { "[kernel]", "schedule" }, | ||
645 | { "perf", "run_command" }, | ||
646 | { "perf", "main" }, }, | ||
647 | }, | ||
648 | { | ||
641 | 4, { { "libc", "free" }, | 649 | 4, { { "libc", "free" }, |
642 | { "perf", "cmd_record" }, | 650 | { "perf", "cmd_record" }, |
643 | { "perf", "run_command" }, | 651 | { "perf", "run_command" }, |
@@ -649,14 +657,6 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
649 | { "perf", "run_command" }, | 657 | { "perf", "run_command" }, |
650 | { "perf", "main" }, }, | 658 | { "perf", "main" }, }, |
651 | }, | 659 | }, |
652 | { | ||
653 | 6, { { "bash", "xmalloc" }, | ||
654 | { "libc", "malloc" }, | ||
655 | { "bash", "xmalloc" }, | ||
656 | { "libc", "malloc" }, | ||
657 | { "bash", "xmalloc" }, | ||
658 | { "bash", "main" }, }, | ||
659 | }, | ||
660 | }; | 660 | }; |
661 | 661 | ||
662 | symbol_conf.use_callchain = true; | 662 | symbol_conf.use_callchain = true; |
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 821f581fd930..59e53db7914c 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
@@ -43,7 +43,7 @@ static struct sample fake_samples[] = { | |||
43 | }; | 43 | }; |
44 | 44 | ||
45 | static int add_hist_entries(struct perf_evlist *evlist, | 45 | static int add_hist_entries(struct perf_evlist *evlist, |
46 | struct machine *machine __maybe_unused) | 46 | struct machine *machine) |
47 | { | 47 | { |
48 | struct perf_evsel *evsel; | 48 | struct perf_evsel *evsel; |
49 | struct addr_location al; | 49 | struct addr_location al; |
@@ -66,11 +66,12 @@ static int add_hist_entries(struct perf_evlist *evlist, | |||
66 | .ops = &hist_iter_normal, | 66 | .ops = &hist_iter_normal, |
67 | .hide_unresolved = false, | 67 | .hide_unresolved = false, |
68 | }; | 68 | }; |
69 | struct hists *hists = evsel__hists(evsel); | ||
69 | 70 | ||
70 | /* make sure it has no filter at first */ | 71 | /* make sure it has no filter at first */ |
71 | evsel->hists.thread_filter = NULL; | 72 | hists->thread_filter = NULL; |
72 | evsel->hists.dso_filter = NULL; | 73 | hists->dso_filter = NULL; |
73 | evsel->hists.symbol_filter_str = NULL; | 74 | hists->symbol_filter_str = NULL; |
74 | 75 | ||
75 | sample.pid = fake_samples[i].pid; | 76 | sample.pid = fake_samples[i].pid; |
76 | sample.tid = fake_samples[i].pid; | 77 | sample.tid = fake_samples[i].pid; |
@@ -134,10 +135,10 @@ int test__hists_filter(void) | |||
134 | goto out; | 135 | goto out; |
135 | 136 | ||
136 | evlist__for_each(evlist, evsel) { | 137 | evlist__for_each(evlist, evsel) { |
137 | struct hists *hists = &evsel->hists; | 138 | struct hists *hists = evsel__hists(evsel); |
138 | 139 | ||
139 | hists__collapse_resort(hists, NULL); | 140 | hists__collapse_resort(hists, NULL); |
140 | hists__output_resort(hists); | 141 | hists__output_resort(hists, NULL); |
141 | 142 | ||
142 | if (verbose > 2) { | 143 | if (verbose > 2) { |
143 | pr_info("Normal histogram\n"); | 144 | pr_info("Normal histogram\n"); |
@@ -160,7 +161,7 @@ int test__hists_filter(void) | |||
160 | hists->stats.total_non_filtered_period); | 161 | hists->stats.total_non_filtered_period); |
161 | 162 | ||
162 | /* now applying thread filter for 'bash' */ | 163 | /* now applying thread filter for 'bash' */ |
163 | evsel->hists.thread_filter = fake_samples[9].thread; | 164 | hists->thread_filter = fake_samples[9].thread; |
164 | hists__filter_by_thread(hists); | 165 | hists__filter_by_thread(hists); |
165 | 166 | ||
166 | if (verbose > 2) { | 167 | if (verbose > 2) { |
@@ -185,11 +186,11 @@ int test__hists_filter(void) | |||
185 | hists->stats.total_non_filtered_period == 400); | 186 | hists->stats.total_non_filtered_period == 400); |
186 | 187 | ||
187 | /* remove thread filter first */ | 188 | /* remove thread filter first */ |
188 | evsel->hists.thread_filter = NULL; | 189 | hists->thread_filter = NULL; |
189 | hists__filter_by_thread(hists); | 190 | hists__filter_by_thread(hists); |
190 | 191 | ||
191 | /* now applying dso filter for 'kernel' */ | 192 | /* now applying dso filter for 'kernel' */ |
192 | evsel->hists.dso_filter = fake_samples[0].map->dso; | 193 | hists->dso_filter = fake_samples[0].map->dso; |
193 | hists__filter_by_dso(hists); | 194 | hists__filter_by_dso(hists); |
194 | 195 | ||
195 | if (verbose > 2) { | 196 | if (verbose > 2) { |
@@ -214,7 +215,7 @@ int test__hists_filter(void) | |||
214 | hists->stats.total_non_filtered_period == 300); | 215 | hists->stats.total_non_filtered_period == 300); |
215 | 216 | ||
216 | /* remove dso filter first */ | 217 | /* remove dso filter first */ |
217 | evsel->hists.dso_filter = NULL; | 218 | hists->dso_filter = NULL; |
218 | hists__filter_by_dso(hists); | 219 | hists__filter_by_dso(hists); |
219 | 220 | ||
220 | /* | 221 | /* |
@@ -224,7 +225,7 @@ int test__hists_filter(void) | |||
224 | * be counted as a separate entry but the sample count and | 225 | * be counted as a separate entry but the sample count and |
225 | * total period will be remained. | 226 | * total period will be remained. |
226 | */ | 227 | */ |
227 | evsel->hists.symbol_filter_str = "main"; | 228 | hists->symbol_filter_str = "main"; |
228 | hists__filter_by_symbol(hists); | 229 | hists__filter_by_symbol(hists); |
229 | 230 | ||
230 | if (verbose > 2) { | 231 | if (verbose > 2) { |
@@ -249,8 +250,8 @@ int test__hists_filter(void) | |||
249 | hists->stats.total_non_filtered_period == 300); | 250 | hists->stats.total_non_filtered_period == 300); |
250 | 251 | ||
251 | /* now applying all filters at once. */ | 252 | /* now applying all filters at once. */ |
252 | evsel->hists.thread_filter = fake_samples[1].thread; | 253 | hists->thread_filter = fake_samples[1].thread; |
253 | evsel->hists.dso_filter = fake_samples[1].map->dso; | 254 | hists->dso_filter = fake_samples[1].map->dso; |
254 | hists__filter_by_thread(hists); | 255 | hists__filter_by_thread(hists); |
255 | hists__filter_by_dso(hists); | 256 | hists__filter_by_dso(hists); |
256 | 257 | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index d4b34b0f50a2..278ba8344c23 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -73,6 +73,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
73 | * "bash [libc] malloc" so total 9 entries will be in the tree. | 73 | * "bash [libc] malloc" so total 9 entries will be in the tree. |
74 | */ | 74 | */ |
75 | evlist__for_each(evlist, evsel) { | 75 | evlist__for_each(evlist, evsel) { |
76 | struct hists *hists = evsel__hists(evsel); | ||
77 | |||
76 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | 78 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { |
77 | const union perf_event event = { | 79 | const union perf_event event = { |
78 | .header = { | 80 | .header = { |
@@ -87,7 +89,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
87 | &sample) < 0) | 89 | &sample) < 0) |
88 | goto out; | 90 | goto out; |
89 | 91 | ||
90 | he = __hists__add_entry(&evsel->hists, &al, NULL, | 92 | he = __hists__add_entry(hists, &al, NULL, |
91 | NULL, NULL, 1, 1, 0, true); | 93 | NULL, NULL, 1, 1, 0, true); |
92 | if (he == NULL) | 94 | if (he == NULL) |
93 | goto out; | 95 | goto out; |
@@ -111,7 +113,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
111 | &sample) < 0) | 113 | &sample) < 0) |
112 | goto out; | 114 | goto out; |
113 | 115 | ||
114 | he = __hists__add_entry(&evsel->hists, &al, NULL, | 116 | he = __hists__add_entry(hists, &al, NULL, |
115 | NULL, NULL, 1, 1, 0, true); | 117 | NULL, NULL, 1, 1, 0, true); |
116 | if (he == NULL) | 118 | if (he == NULL) |
117 | goto out; | 119 | goto out; |
@@ -271,6 +273,7 @@ static int validate_link(struct hists *leader, struct hists *other) | |||
271 | int test__hists_link(void) | 273 | int test__hists_link(void) |
272 | { | 274 | { |
273 | int err = -1; | 275 | int err = -1; |
276 | struct hists *hists, *first_hists; | ||
274 | struct machines machines; | 277 | struct machines machines; |
275 | struct machine *machine = NULL; | 278 | struct machine *machine = NULL; |
276 | struct perf_evsel *evsel, *first; | 279 | struct perf_evsel *evsel, *first; |
@@ -306,24 +309,28 @@ int test__hists_link(void) | |||
306 | goto out; | 309 | goto out; |
307 | 310 | ||
308 | evlist__for_each(evlist, evsel) { | 311 | evlist__for_each(evlist, evsel) { |
309 | hists__collapse_resort(&evsel->hists, NULL); | 312 | hists = evsel__hists(evsel); |
313 | hists__collapse_resort(hists, NULL); | ||
310 | 314 | ||
311 | if (verbose > 2) | 315 | if (verbose > 2) |
312 | print_hists_in(&evsel->hists); | 316 | print_hists_in(hists); |
313 | } | 317 | } |
314 | 318 | ||
315 | first = perf_evlist__first(evlist); | 319 | first = perf_evlist__first(evlist); |
316 | evsel = perf_evlist__last(evlist); | 320 | evsel = perf_evlist__last(evlist); |
317 | 321 | ||
322 | first_hists = evsel__hists(first); | ||
323 | hists = evsel__hists(evsel); | ||
324 | |||
318 | /* match common entries */ | 325 | /* match common entries */ |
319 | hists__match(&first->hists, &evsel->hists); | 326 | hists__match(first_hists, hists); |
320 | err = validate_match(&first->hists, &evsel->hists); | 327 | err = validate_match(first_hists, hists); |
321 | if (err) | 328 | if (err) |
322 | goto out; | 329 | goto out; |
323 | 330 | ||
324 | /* link common and/or dummy entries */ | 331 | /* link common and/or dummy entries */ |
325 | hists__link(&first->hists, &evsel->hists); | 332 | hists__link(first_hists, hists); |
326 | err = validate_link(&first->hists, &evsel->hists); | 333 | err = validate_link(first_hists, hists); |
327 | if (err) | 334 | if (err) |
328 | goto out; | 335 | goto out; |
329 | 336 | ||
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index e3bbd6c54c1b..f5547610da02 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
@@ -122,7 +122,7 @@ typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); | |||
122 | static int test1(struct perf_evsel *evsel, struct machine *machine) | 122 | static int test1(struct perf_evsel *evsel, struct machine *machine) |
123 | { | 123 | { |
124 | int err; | 124 | int err; |
125 | struct hists *hists = &evsel->hists; | 125 | struct hists *hists = evsel__hists(evsel); |
126 | struct hist_entry *he; | 126 | struct hist_entry *he; |
127 | struct rb_root *root; | 127 | struct rb_root *root; |
128 | struct rb_node *node; | 128 | struct rb_node *node; |
@@ -152,14 +152,14 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) | |||
152 | goto out; | 152 | goto out; |
153 | 153 | ||
154 | hists__collapse_resort(hists, NULL); | 154 | hists__collapse_resort(hists, NULL); |
155 | hists__output_resort(hists); | 155 | hists__output_resort(hists, NULL); |
156 | 156 | ||
157 | if (verbose > 2) { | 157 | if (verbose > 2) { |
158 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 158 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
159 | print_hists_out(hists); | 159 | print_hists_out(hists); |
160 | } | 160 | } |
161 | 161 | ||
162 | root = &evsel->hists.entries; | 162 | root = &hists->entries; |
163 | node = rb_first(root); | 163 | node = rb_first(root); |
164 | he = rb_entry(node, struct hist_entry, rb_node); | 164 | he = rb_entry(node, struct hist_entry, rb_node); |
165 | TEST_ASSERT_VAL("Invalid hist entry", | 165 | TEST_ASSERT_VAL("Invalid hist entry", |
@@ -224,7 +224,7 @@ out: | |||
224 | static int test2(struct perf_evsel *evsel, struct machine *machine) | 224 | static int test2(struct perf_evsel *evsel, struct machine *machine) |
225 | { | 225 | { |
226 | int err; | 226 | int err; |
227 | struct hists *hists = &evsel->hists; | 227 | struct hists *hists = evsel__hists(evsel); |
228 | struct hist_entry *he; | 228 | struct hist_entry *he; |
229 | struct rb_root *root; | 229 | struct rb_root *root; |
230 | struct rb_node *node; | 230 | struct rb_node *node; |
@@ -252,14 +252,14 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) | |||
252 | goto out; | 252 | goto out; |
253 | 253 | ||
254 | hists__collapse_resort(hists, NULL); | 254 | hists__collapse_resort(hists, NULL); |
255 | hists__output_resort(hists); | 255 | hists__output_resort(hists, NULL); |
256 | 256 | ||
257 | if (verbose > 2) { | 257 | if (verbose > 2) { |
258 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 258 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
259 | print_hists_out(hists); | 259 | print_hists_out(hists); |
260 | } | 260 | } |
261 | 261 | ||
262 | root = &evsel->hists.entries; | 262 | root = &hists->entries; |
263 | node = rb_first(root); | 263 | node = rb_first(root); |
264 | he = rb_entry(node, struct hist_entry, rb_node); | 264 | he = rb_entry(node, struct hist_entry, rb_node); |
265 | TEST_ASSERT_VAL("Invalid hist entry", | 265 | TEST_ASSERT_VAL("Invalid hist entry", |
@@ -280,7 +280,7 @@ out: | |||
280 | static int test3(struct perf_evsel *evsel, struct machine *machine) | 280 | static int test3(struct perf_evsel *evsel, struct machine *machine) |
281 | { | 281 | { |
282 | int err; | 282 | int err; |
283 | struct hists *hists = &evsel->hists; | 283 | struct hists *hists = evsel__hists(evsel); |
284 | struct hist_entry *he; | 284 | struct hist_entry *he; |
285 | struct rb_root *root; | 285 | struct rb_root *root; |
286 | struct rb_node *node; | 286 | struct rb_node *node; |
@@ -306,14 +306,14 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
306 | goto out; | 306 | goto out; |
307 | 307 | ||
308 | hists__collapse_resort(hists, NULL); | 308 | hists__collapse_resort(hists, NULL); |
309 | hists__output_resort(hists); | 309 | hists__output_resort(hists, NULL); |
310 | 310 | ||
311 | if (verbose > 2) { | 311 | if (verbose > 2) { |
312 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 312 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
313 | print_hists_out(hists); | 313 | print_hists_out(hists); |
314 | } | 314 | } |
315 | 315 | ||
316 | root = &evsel->hists.entries; | 316 | root = &hists->entries; |
317 | node = rb_first(root); | 317 | node = rb_first(root); |
318 | he = rb_entry(node, struct hist_entry, rb_node); | 318 | he = rb_entry(node, struct hist_entry, rb_node); |
319 | TEST_ASSERT_VAL("Invalid hist entry", | 319 | TEST_ASSERT_VAL("Invalid hist entry", |
@@ -354,7 +354,7 @@ out: | |||
354 | static int test4(struct perf_evsel *evsel, struct machine *machine) | 354 | static int test4(struct perf_evsel *evsel, struct machine *machine) |
355 | { | 355 | { |
356 | int err; | 356 | int err; |
357 | struct hists *hists = &evsel->hists; | 357 | struct hists *hists = evsel__hists(evsel); |
358 | struct hist_entry *he; | 358 | struct hist_entry *he; |
359 | struct rb_root *root; | 359 | struct rb_root *root; |
360 | struct rb_node *node; | 360 | struct rb_node *node; |
@@ -384,14 +384,14 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
384 | goto out; | 384 | goto out; |
385 | 385 | ||
386 | hists__collapse_resort(hists, NULL); | 386 | hists__collapse_resort(hists, NULL); |
387 | hists__output_resort(hists); | 387 | hists__output_resort(hists, NULL); |
388 | 388 | ||
389 | if (verbose > 2) { | 389 | if (verbose > 2) { |
390 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 390 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
391 | print_hists_out(hists); | 391 | print_hists_out(hists); |
392 | } | 392 | } |
393 | 393 | ||
394 | root = &evsel->hists.entries; | 394 | root = &hists->entries; |
395 | node = rb_first(root); | 395 | node = rb_first(root); |
396 | he = rb_entry(node, struct hist_entry, rb_node); | 396 | he = rb_entry(node, struct hist_entry, rb_node); |
397 | TEST_ASSERT_VAL("Invalid hist entry", | 397 | TEST_ASSERT_VAL("Invalid hist entry", |
@@ -456,7 +456,7 @@ out: | |||
456 | static int test5(struct perf_evsel *evsel, struct machine *machine) | 456 | static int test5(struct perf_evsel *evsel, struct machine *machine) |
457 | { | 457 | { |
458 | int err; | 458 | int err; |
459 | struct hists *hists = &evsel->hists; | 459 | struct hists *hists = evsel__hists(evsel); |
460 | struct hist_entry *he; | 460 | struct hist_entry *he; |
461 | struct rb_root *root; | 461 | struct rb_root *root; |
462 | struct rb_node *node; | 462 | struct rb_node *node; |
@@ -487,14 +487,14 @@ static int test5(struct perf_evsel *evsel, struct machine *machine) | |||
487 | goto out; | 487 | goto out; |
488 | 488 | ||
489 | hists__collapse_resort(hists, NULL); | 489 | hists__collapse_resort(hists, NULL); |
490 | hists__output_resort(hists); | 490 | hists__output_resort(hists, NULL); |
491 | 491 | ||
492 | if (verbose > 2) { | 492 | if (verbose > 2) { |
493 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 493 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
494 | print_hists_out(hists); | 494 | print_hists_out(hists); |
495 | } | 495 | } |
496 | 496 | ||
497 | root = &evsel->hists.entries; | 497 | root = &hists->entries; |
498 | node = rb_first(root); | 498 | node = rb_first(root); |
499 | he = rb_entry(node, struct hist_entry, rb_node); | 499 | he = rb_entry(node, struct hist_entry, rb_node); |
500 | 500 | ||
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 142263492f6f..9b9622a33932 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -31,6 +31,7 @@ int test__basic_mmap(void) | |||
31 | unsigned int nr_events[nsyscalls], | 31 | unsigned int nr_events[nsyscalls], |
32 | expected_nr_events[nsyscalls], i, j; | 32 | expected_nr_events[nsyscalls], i, j; |
33 | struct perf_evsel *evsels[nsyscalls], *evsel; | 33 | struct perf_evsel *evsels[nsyscalls], *evsel; |
34 | char sbuf[STRERR_BUFSIZE]; | ||
34 | 35 | ||
35 | threads = thread_map__new(-1, getpid(), UINT_MAX); | 36 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
36 | if (threads == NULL) { | 37 | if (threads == NULL) { |
@@ -49,7 +50,7 @@ int test__basic_mmap(void) | |||
49 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | 50 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); |
50 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | 51 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { |
51 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | 52 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", |
52 | cpus->map[0], strerror(errno)); | 53 | cpus->map[0], strerror_r(errno, sbuf, sizeof(sbuf))); |
53 | goto out_free_cpus; | 54 | goto out_free_cpus; |
54 | } | 55 | } |
55 | 56 | ||
@@ -79,7 +80,7 @@ int test__basic_mmap(void) | |||
79 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | 80 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { |
80 | pr_debug("failed to open counter: %s, " | 81 | pr_debug("failed to open counter: %s, " |
81 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 82 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
82 | strerror(errno)); | 83 | strerror_r(errno, sbuf, sizeof(sbuf))); |
83 | goto out_delete_evlist; | 84 | goto out_delete_evlist; |
84 | } | 85 | } |
85 | 86 | ||
@@ -89,7 +90,7 @@ int test__basic_mmap(void) | |||
89 | 90 | ||
90 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | 91 | if (perf_evlist__mmap(evlist, 128, true) < 0) { |
91 | pr_debug("failed to mmap events: %d (%s)\n", errno, | 92 | pr_debug("failed to mmap events: %d (%s)\n", errno, |
92 | strerror(errno)); | 93 | strerror_r(errno, sbuf, sizeof(sbuf))); |
93 | goto out_delete_evlist; | 94 | goto out_delete_evlist; |
94 | } | 95 | } |
95 | 96 | ||
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 4a456fef66ca..2113f1c8611f 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c | |||
@@ -187,7 +187,7 @@ static int mmap_events(synth_cb synth) | |||
187 | 187 | ||
188 | pr_debug("looking for map %p\n", td->map); | 188 | pr_debug("looking for map %p\n", td->map); |
189 | 189 | ||
190 | thread__find_addr_map(thread, machine, | 190 | thread__find_addr_map(thread, |
191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, | 191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, |
192 | (unsigned long) (td->map + 1), &al); | 192 | (unsigned long) (td->map + 1), &al); |
193 | 193 | ||
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c index 5fecdbd2f5f7..8fa82d1700c7 100644 --- a/tools/perf/tests/open-syscall-all-cpus.c +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
@@ -12,6 +12,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
12 | unsigned int nr_open_calls = 111, i; | 12 | unsigned int nr_open_calls = 111, i; |
13 | cpu_set_t cpu_set; | 13 | cpu_set_t cpu_set; |
14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | 14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
15 | char sbuf[STRERR_BUFSIZE]; | ||
15 | 16 | ||
16 | if (threads == NULL) { | 17 | if (threads == NULL) { |
17 | pr_debug("thread_map__new\n"); | 18 | pr_debug("thread_map__new\n"); |
@@ -35,7 +36,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
35 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | 36 | if (perf_evsel__open(evsel, cpus, threads) < 0) { |
36 | pr_debug("failed to open counter: %s, " | 37 | pr_debug("failed to open counter: %s, " |
37 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 38 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
38 | strerror(errno)); | 39 | strerror_r(errno, sbuf, sizeof(sbuf))); |
39 | goto out_evsel_delete; | 40 | goto out_evsel_delete; |
40 | } | 41 | } |
41 | 42 | ||
@@ -56,7 +57,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
56 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | 57 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { |
57 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | 58 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", |
58 | cpus->map[cpu], | 59 | cpus->map[cpu], |
59 | strerror(errno)); | 60 | strerror_r(errno, sbuf, sizeof(sbuf))); |
60 | goto out_close_fd; | 61 | goto out_close_fd; |
61 | } | 62 | } |
62 | for (i = 0; i < ncalls; ++i) { | 63 | for (i = 0; i < ncalls; ++i) { |
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 0785b64ffd6c..127dcae0b760 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -22,6 +22,7 @@ int test__syscall_open_tp_fields(void) | |||
22 | struct perf_evlist *evlist = perf_evlist__new(); | 22 | struct perf_evlist *evlist = perf_evlist__new(); |
23 | struct perf_evsel *evsel; | 23 | struct perf_evsel *evsel; |
24 | int err = -1, i, nr_events = 0, nr_polls = 0; | 24 | int err = -1, i, nr_events = 0, nr_polls = 0; |
25 | char sbuf[STRERR_BUFSIZE]; | ||
25 | 26 | ||
26 | if (evlist == NULL) { | 27 | if (evlist == NULL) { |
27 | pr_debug("%s: perf_evlist__new\n", __func__); | 28 | pr_debug("%s: perf_evlist__new\n", __func__); |
@@ -48,13 +49,15 @@ int test__syscall_open_tp_fields(void) | |||
48 | 49 | ||
49 | err = perf_evlist__open(evlist); | 50 | err = perf_evlist__open(evlist); |
50 | if (err < 0) { | 51 | if (err < 0) { |
51 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 52 | pr_debug("perf_evlist__open: %s\n", |
53 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
52 | goto out_delete_evlist; | 54 | goto out_delete_evlist; |
53 | } | 55 | } |
54 | 56 | ||
55 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 57 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
56 | if (err < 0) { | 58 | if (err < 0) { |
57 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 59 | pr_debug("perf_evlist__mmap: %s\n", |
60 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
58 | goto out_delete_evlist; | 61 | goto out_delete_evlist; |
59 | } | 62 | } |
60 | 63 | ||
@@ -102,7 +105,7 @@ int test__syscall_open_tp_fields(void) | |||
102 | } | 105 | } |
103 | 106 | ||
104 | if (nr_events == before) | 107 | if (nr_events == before) |
105 | poll(evlist->pollfd, evlist->nr_fds, 10); | 108 | perf_evlist__poll(evlist, 10); |
106 | 109 | ||
107 | if (++nr_polls > 5) { | 110 | if (++nr_polls > 5) { |
108 | pr_debug("%s: no events!\n", __func__); | 111 | pr_debug("%s: no events!\n", __func__); |
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c index c1dc7d25f38c..a33b2daae40f 100644 --- a/tools/perf/tests/open-syscall.c +++ b/tools/perf/tests/open-syscall.c | |||
@@ -9,6 +9,7 @@ int test__open_syscall_event(void) | |||
9 | struct perf_evsel *evsel; | 9 | struct perf_evsel *evsel; |
10 | unsigned int nr_open_calls = 111, i; | 10 | unsigned int nr_open_calls = 111, i; |
11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | 11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
12 | char sbuf[STRERR_BUFSIZE]; | ||
12 | 13 | ||
13 | if (threads == NULL) { | 14 | if (threads == NULL) { |
14 | pr_debug("thread_map__new\n"); | 15 | pr_debug("thread_map__new\n"); |
@@ -24,7 +25,7 @@ int test__open_syscall_event(void) | |||
24 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | 25 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { |
25 | pr_debug("failed to open counter: %s, " | 26 | pr_debug("failed to open counter: %s, " |
26 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 27 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
27 | strerror(errno)); | 28 | strerror_r(errno, sbuf, sizeof(sbuf))); |
28 | goto out_evsel_delete; | 29 | goto out_evsel_delete; |
29 | } | 30 | } |
30 | 31 | ||
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 5941927a4b7f..7f2f51f93619 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -457,6 +457,36 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) | |||
457 | return 0; | 457 | return 0; |
458 | } | 458 | } |
459 | 459 | ||
460 | |||
461 | static int test__checkevent_pmu_events_mix(struct perf_evlist *evlist) | ||
462 | { | ||
463 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
464 | |||
465 | /* pmu-event:u */ | ||
466 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
467 | TEST_ASSERT_VAL("wrong exclude_user", | ||
468 | !evsel->attr.exclude_user); | ||
469 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
470 | evsel->attr.exclude_kernel); | ||
471 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
472 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
473 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
474 | |||
475 | /* cpu/pmu-event/u*/ | ||
476 | evsel = perf_evsel__next(evsel); | ||
477 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
478 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
479 | TEST_ASSERT_VAL("wrong exclude_user", | ||
480 | !evsel->attr.exclude_user); | ||
481 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
482 | evsel->attr.exclude_kernel); | ||
483 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
484 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
485 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | |||
460 | static int test__checkterms_simple(struct list_head *terms) | 490 | static int test__checkterms_simple(struct list_head *terms) |
461 | { | 491 | { |
462 | struct parse_events_term *term; | 492 | struct parse_events_term *term; |
@@ -1554,6 +1584,12 @@ static int test_pmu_events(void) | |||
1554 | e.check = test__checkevent_pmu_events; | 1584 | e.check = test__checkevent_pmu_events; |
1555 | 1585 | ||
1556 | ret = test_event(&e); | 1586 | ret = test_event(&e); |
1587 | if (ret) | ||
1588 | break; | ||
1589 | snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); | ||
1590 | e.name = name; | ||
1591 | e.check = test__checkevent_pmu_events_mix; | ||
1592 | ret = test_event(&e); | ||
1557 | #undef MAX_NAME | 1593 | #undef MAX_NAME |
1558 | } | 1594 | } |
1559 | 1595 | ||
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index aca1a83dd13a..7a228a2a070b 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c | |||
@@ -59,6 +59,7 @@ int test__PERF_RECORD(void) | |||
59 | int err = -1, errs = 0, i, wakeups = 0; | 59 | int err = -1, errs = 0, i, wakeups = 0; |
60 | u32 cpu; | 60 | u32 cpu; |
61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | 61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; |
62 | char sbuf[STRERR_BUFSIZE]; | ||
62 | 63 | ||
63 | if (evlist == NULL || argv == NULL) { | 64 | if (evlist == NULL || argv == NULL) { |
64 | pr_debug("Not enough memory to create evlist\n"); | 65 | pr_debug("Not enough memory to create evlist\n"); |
@@ -100,7 +101,8 @@ int test__PERF_RECORD(void) | |||
100 | 101 | ||
101 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); | 102 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); |
102 | if (err < 0) { | 103 | if (err < 0) { |
103 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | 104 | pr_debug("sched__get_first_possible_cpu: %s\n", |
105 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
104 | goto out_delete_evlist; | 106 | goto out_delete_evlist; |
105 | } | 107 | } |
106 | 108 | ||
@@ -110,7 +112,8 @@ int test__PERF_RECORD(void) | |||
110 | * So that we can check perf_sample.cpu on all the samples. | 112 | * So that we can check perf_sample.cpu on all the samples. |
111 | */ | 113 | */ |
112 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { | 114 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { |
113 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | 115 | pr_debug("sched_setaffinity: %s\n", |
116 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
114 | goto out_delete_evlist; | 117 | goto out_delete_evlist; |
115 | } | 118 | } |
116 | 119 | ||
@@ -120,7 +123,8 @@ int test__PERF_RECORD(void) | |||
120 | */ | 123 | */ |
121 | err = perf_evlist__open(evlist); | 124 | err = perf_evlist__open(evlist); |
122 | if (err < 0) { | 125 | if (err < 0) { |
123 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 126 | pr_debug("perf_evlist__open: %s\n", |
127 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
124 | goto out_delete_evlist; | 128 | goto out_delete_evlist; |
125 | } | 129 | } |
126 | 130 | ||
@@ -131,7 +135,8 @@ int test__PERF_RECORD(void) | |||
131 | */ | 135 | */ |
132 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | 136 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); |
133 | if (err < 0) { | 137 | if (err < 0) { |
134 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 138 | pr_debug("perf_evlist__mmap: %s\n", |
139 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
135 | goto out_delete_evlist; | 140 | goto out_delete_evlist; |
136 | } | 141 | } |
137 | 142 | ||
@@ -263,7 +268,7 @@ int test__PERF_RECORD(void) | |||
263 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | 268 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. |
264 | */ | 269 | */ |
265 | if (total_events == before && false) | 270 | if (total_events == before && false) |
266 | poll(evlist->pollfd, evlist->nr_fds, -1); | 271 | perf_evlist__poll(evlist, -1); |
267 | 272 | ||
268 | sleep(1); | 273 | sleep(1); |
269 | if (++wakeups > 5) { | 274 | if (++wakeups > 5) { |
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 12b322fa3475..eeb68bb1972d 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c | |||
@@ -152,7 +152,7 @@ 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); | 155 | ret = perf_pmu__config_terms(&formats, &attr, terms, false); |
156 | if (ret) | 156 | if (ret) |
157 | break; | 157 | break; |
158 | 158 | ||
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index c04d1f268576..d31f2c4d9f64 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c | |||
@@ -100,6 +100,7 @@ static int __test__rdpmc(void) | |||
100 | }; | 100 | }; |
101 | u64 delta_sum = 0; | 101 | u64 delta_sum = 0; |
102 | struct sigaction sa; | 102 | struct sigaction sa; |
103 | char sbuf[STRERR_BUFSIZE]; | ||
103 | 104 | ||
104 | sigfillset(&sa.sa_mask); | 105 | sigfillset(&sa.sa_mask); |
105 | sa.sa_sigaction = segfault_handler; | 106 | sa.sa_sigaction = segfault_handler; |
@@ -109,14 +110,15 @@ static int __test__rdpmc(void) | |||
109 | perf_event_open_cloexec_flag()); | 110 | perf_event_open_cloexec_flag()); |
110 | if (fd < 0) { | 111 | if (fd < 0) { |
111 | pr_err("Error: sys_perf_event_open() syscall returned " | 112 | pr_err("Error: sys_perf_event_open() syscall returned " |
112 | "with %d (%s)\n", fd, strerror(errno)); | 113 | "with %d (%s)\n", fd, |
114 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
113 | return -1; | 115 | return -1; |
114 | } | 116 | } |
115 | 117 | ||
116 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | 118 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); |
117 | if (addr == (void *)(-1)) { | 119 | if (addr == (void *)(-1)) { |
118 | pr_err("Error: mmap() syscall returned with (%s)\n", | 120 | pr_err("Error: mmap() syscall returned with (%s)\n", |
119 | strerror(errno)); | 121 | strerror_r(errno, sbuf, sizeof(sbuf))); |
120 | goto out_close; | 122 | goto out_close; |
121 | } | 123 | } |
122 | 124 | ||
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index ca292f9a4ae2..4908c648a597 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c | |||
@@ -126,16 +126,28 @@ static bool samples_same(const struct perf_sample *s1, | |||
126 | if (type & PERF_SAMPLE_TRANSACTION) | 126 | if (type & PERF_SAMPLE_TRANSACTION) |
127 | COMP(transaction); | 127 | COMP(transaction); |
128 | 128 | ||
129 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
130 | size_t sz = hweight_long(s1->intr_regs.mask) * sizeof(u64); | ||
131 | |||
132 | COMP(intr_regs.mask); | ||
133 | COMP(intr_regs.abi); | ||
134 | if (s1->intr_regs.abi && | ||
135 | (!s1->intr_regs.regs || !s2->intr_regs.regs || | ||
136 | memcmp(s1->intr_regs.regs, s2->intr_regs.regs, sz))) { | ||
137 | pr_debug("Samples differ at 'intr_regs'\n"); | ||
138 | return false; | ||
139 | } | ||
140 | } | ||
141 | |||
129 | return true; | 142 | return true; |
130 | } | 143 | } |
131 | 144 | ||
132 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | 145 | static int do_test(u64 sample_type, u64 sample_regs, u64 read_format) |
133 | { | 146 | { |
134 | struct perf_evsel evsel = { | 147 | struct perf_evsel evsel = { |
135 | .needs_swap = false, | 148 | .needs_swap = false, |
136 | .attr = { | 149 | .attr = { |
137 | .sample_type = sample_type, | 150 | .sample_type = sample_type, |
138 | .sample_regs_user = sample_regs_user, | ||
139 | .read_format = read_format, | 151 | .read_format = read_format, |
140 | }, | 152 | }, |
141 | }; | 153 | }; |
@@ -154,7 +166,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
154 | /* 1 branch_entry */ | 166 | /* 1 branch_entry */ |
155 | .data = {1, 211, 212, 213}, | 167 | .data = {1, 211, 212, 213}, |
156 | }; | 168 | }; |
157 | u64 user_regs[64]; | 169 | u64 regs[64]; |
158 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | 170 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; |
159 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | 171 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; |
160 | struct perf_sample sample = { | 172 | struct perf_sample sample = { |
@@ -176,8 +188,8 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
176 | .branch_stack = &branch_stack.branch_stack, | 188 | .branch_stack = &branch_stack.branch_stack, |
177 | .user_regs = { | 189 | .user_regs = { |
178 | .abi = PERF_SAMPLE_REGS_ABI_64, | 190 | .abi = PERF_SAMPLE_REGS_ABI_64, |
179 | .mask = sample_regs_user, | 191 | .mask = sample_regs, |
180 | .regs = user_regs, | 192 | .regs = regs, |
181 | }, | 193 | }, |
182 | .user_stack = { | 194 | .user_stack = { |
183 | .size = sizeof(data), | 195 | .size = sizeof(data), |
@@ -187,14 +199,25 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | |||
187 | .time_enabled = 0x030a59d664fca7deULL, | 199 | .time_enabled = 0x030a59d664fca7deULL, |
188 | .time_running = 0x011b6ae553eb98edULL, | 200 | .time_running = 0x011b6ae553eb98edULL, |
189 | }, | 201 | }, |
202 | .intr_regs = { | ||
203 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
204 | .mask = sample_regs, | ||
205 | .regs = regs, | ||
206 | }, | ||
190 | }; | 207 | }; |
191 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | 208 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; |
192 | struct perf_sample sample_out; | 209 | struct perf_sample sample_out; |
193 | size_t i, sz, bufsz; | 210 | size_t i, sz, bufsz; |
194 | int err, ret = -1; | 211 | int err, ret = -1; |
195 | 212 | ||
196 | for (i = 0; i < sizeof(user_regs); i++) | 213 | if (sample_type & PERF_SAMPLE_REGS_USER) |
197 | *(i + (u8 *)user_regs) = i & 0xfe; | 214 | evsel.attr.sample_regs_user = sample_regs; |
215 | |||
216 | if (sample_type & PERF_SAMPLE_REGS_INTR) | ||
217 | evsel.attr.sample_regs_intr = sample_regs; | ||
218 | |||
219 | for (i = 0; i < sizeof(regs); i++) | ||
220 | *(i + (u8 *)regs) = i & 0xfe; | ||
198 | 221 | ||
199 | if (read_format & PERF_FORMAT_GROUP) { | 222 | if (read_format & PERF_FORMAT_GROUP) { |
200 | sample.read.group.nr = 4; | 223 | sample.read.group.nr = 4; |
@@ -271,7 +294,7 @@ int test__sample_parsing(void) | |||
271 | { | 294 | { |
272 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | 295 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; |
273 | u64 sample_type; | 296 | u64 sample_type; |
274 | u64 sample_regs_user; | 297 | u64 sample_regs; |
275 | size_t i; | 298 | size_t i; |
276 | int err; | 299 | int err; |
277 | 300 | ||
@@ -280,7 +303,7 @@ int test__sample_parsing(void) | |||
280 | * were added. Please actually update the test rather than just change | 303 | * were added. Please actually update the test rather than just change |
281 | * the condition below. | 304 | * the condition below. |
282 | */ | 305 | */ |
283 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { | 306 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_REGS_INTR << 1) { |
284 | pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); | 307 | pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); |
285 | return -1; | 308 | return -1; |
286 | } | 309 | } |
@@ -297,22 +320,24 @@ int test__sample_parsing(void) | |||
297 | } | 320 | } |
298 | continue; | 321 | continue; |
299 | } | 322 | } |
323 | sample_regs = 0; | ||
300 | 324 | ||
301 | if (sample_type == PERF_SAMPLE_REGS_USER) | 325 | if (sample_type == PERF_SAMPLE_REGS_USER) |
302 | sample_regs_user = 0x3fff; | 326 | sample_regs = 0x3fff; |
303 | else | 327 | |
304 | sample_regs_user = 0; | 328 | if (sample_type == PERF_SAMPLE_REGS_INTR) |
329 | sample_regs = 0xff0fff; | ||
305 | 330 | ||
306 | err = do_test(sample_type, sample_regs_user, 0); | 331 | err = do_test(sample_type, sample_regs, 0); |
307 | if (err) | 332 | if (err) |
308 | return err; | 333 | return err; |
309 | } | 334 | } |
310 | 335 | ||
311 | /* Test all sample format bits together */ | 336 | /* Test all sample format bits together */ |
312 | sample_type = PERF_SAMPLE_MAX - 1; | 337 | sample_type = PERF_SAMPLE_MAX - 1; |
313 | sample_regs_user = 0x3fff; | 338 | sample_regs = 0x3fff; /* shared yb intr and user regs */ |
314 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | 339 | for (i = 0; i < ARRAY_SIZE(rf); i++) { |
315 | err = do_test(sample_type, sample_regs_user, rf[i]); | 340 | err = do_test(sample_type, sample_regs, rf[i]); |
316 | if (err) | 341 | if (err) |
317 | return err; | 342 | return err; |
318 | } | 343 | } |
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 983d6b8562a8..1aa21c90731b 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c | |||
@@ -22,6 +22,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
22 | volatile int tmp = 0; | 22 | volatile int tmp = 0; |
23 | u64 total_periods = 0; | 23 | u64 total_periods = 0; |
24 | int nr_samples = 0; | 24 | int nr_samples = 0; |
25 | char sbuf[STRERR_BUFSIZE]; | ||
25 | union perf_event *event; | 26 | union perf_event *event; |
26 | struct perf_evsel *evsel; | 27 | struct perf_evsel *evsel; |
27 | struct perf_evlist *evlist; | 28 | struct perf_evlist *evlist; |
@@ -62,14 +63,15 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
62 | 63 | ||
63 | err = -errno; | 64 | err = -errno; |
64 | pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", | 65 | pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", |
65 | strerror(errno), knob, (u64)attr.sample_freq); | 66 | strerror_r(errno, sbuf, sizeof(sbuf)), |
67 | knob, (u64)attr.sample_freq); | ||
66 | goto out_delete_evlist; | 68 | goto out_delete_evlist; |
67 | } | 69 | } |
68 | 70 | ||
69 | err = perf_evlist__mmap(evlist, 128, true); | 71 | err = perf_evlist__mmap(evlist, 128, true); |
70 | if (err < 0) { | 72 | if (err < 0) { |
71 | pr_debug("failed to mmap event: %d (%s)\n", errno, | 73 | pr_debug("failed to mmap event: %d (%s)\n", errno, |
72 | strerror(errno)); | 74 | strerror_r(errno, sbuf, sizeof(sbuf))); |
73 | goto out_delete_evlist; | 75 | goto out_delete_evlist; |
74 | } | 76 | } |
75 | 77 | ||
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c new file mode 100644 index 000000000000..cc68648c7c55 --- /dev/null +++ b/tools/perf/tests/switch-tracking.c | |||
@@ -0,0 +1,572 @@ | |||
1 | #include <sys/time.h> | ||
2 | #include <sys/prctl.h> | ||
3 | #include <time.h> | ||
4 | #include <stdlib.h> | ||
5 | |||
6 | #include "parse-events.h" | ||
7 | #include "evlist.h" | ||
8 | #include "evsel.h" | ||
9 | #include "thread_map.h" | ||
10 | #include "cpumap.h" | ||
11 | #include "tests.h" | ||
12 | |||
13 | static int spin_sleep(void) | ||
14 | { | ||
15 | struct timeval start, now, diff, maxtime; | ||
16 | struct timespec ts; | ||
17 | int err, i; | ||
18 | |||
19 | maxtime.tv_sec = 0; | ||
20 | maxtime.tv_usec = 50000; | ||
21 | |||
22 | err = gettimeofday(&start, NULL); | ||
23 | if (err) | ||
24 | return err; | ||
25 | |||
26 | /* Spin for 50ms */ | ||
27 | while (1) { | ||
28 | for (i = 0; i < 1000; i++) | ||
29 | barrier(); | ||
30 | |||
31 | err = gettimeofday(&now, NULL); | ||
32 | if (err) | ||
33 | return err; | ||
34 | |||
35 | timersub(&now, &start, &diff); | ||
36 | if (timercmp(&diff, &maxtime, > /* For checkpatch */)) | ||
37 | break; | ||
38 | } | ||
39 | |||
40 | ts.tv_nsec = 50 * 1000 * 1000; | ||
41 | ts.tv_sec = 0; | ||
42 | |||
43 | /* Sleep for 50ms */ | ||
44 | err = nanosleep(&ts, NULL); | ||
45 | if (err == EINTR) | ||
46 | err = 0; | ||
47 | |||
48 | return err; | ||
49 | } | ||
50 | |||
51 | struct switch_tracking { | ||
52 | struct perf_evsel *switch_evsel; | ||
53 | struct perf_evsel *cycles_evsel; | ||
54 | pid_t *tids; | ||
55 | int nr_tids; | ||
56 | int comm_seen[4]; | ||
57 | int cycles_before_comm_1; | ||
58 | int cycles_between_comm_2_and_comm_3; | ||
59 | int cycles_after_comm_4; | ||
60 | }; | ||
61 | |||
62 | static int check_comm(struct switch_tracking *switch_tracking, | ||
63 | union perf_event *event, const char *comm, int nr) | ||
64 | { | ||
65 | if (event->header.type == PERF_RECORD_COMM && | ||
66 | (pid_t)event->comm.pid == getpid() && | ||
67 | (pid_t)event->comm.tid == getpid() && | ||
68 | strcmp(event->comm.comm, comm) == 0) { | ||
69 | if (switch_tracking->comm_seen[nr]) { | ||
70 | pr_debug("Duplicate comm event\n"); | ||
71 | return -1; | ||
72 | } | ||
73 | switch_tracking->comm_seen[nr] = 1; | ||
74 | pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr); | ||
75 | return 1; | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int check_cpu(struct switch_tracking *switch_tracking, int cpu) | ||
81 | { | ||
82 | int i, nr = cpu + 1; | ||
83 | |||
84 | if (cpu < 0) | ||
85 | return -1; | ||
86 | |||
87 | if (!switch_tracking->tids) { | ||
88 | switch_tracking->tids = calloc(nr, sizeof(pid_t)); | ||
89 | if (!switch_tracking->tids) | ||
90 | return -1; | ||
91 | for (i = 0; i < nr; i++) | ||
92 | switch_tracking->tids[i] = -1; | ||
93 | switch_tracking->nr_tids = nr; | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | if (cpu >= switch_tracking->nr_tids) { | ||
98 | void *addr; | ||
99 | |||
100 | addr = realloc(switch_tracking->tids, nr * sizeof(pid_t)); | ||
101 | if (!addr) | ||
102 | return -1; | ||
103 | switch_tracking->tids = addr; | ||
104 | for (i = switch_tracking->nr_tids; i < nr; i++) | ||
105 | switch_tracking->tids[i] = -1; | ||
106 | switch_tracking->nr_tids = nr; | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int process_sample_event(struct perf_evlist *evlist, | ||
114 | union perf_event *event, | ||
115 | struct switch_tracking *switch_tracking) | ||
116 | { | ||
117 | struct perf_sample sample; | ||
118 | struct perf_evsel *evsel; | ||
119 | pid_t next_tid, prev_tid; | ||
120 | int cpu, err; | ||
121 | |||
122 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | ||
123 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
124 | return -1; | ||
125 | } | ||
126 | |||
127 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
128 | if (evsel == switch_tracking->switch_evsel) { | ||
129 | next_tid = perf_evsel__intval(evsel, &sample, "next_pid"); | ||
130 | prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid"); | ||
131 | cpu = sample.cpu; | ||
132 | pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n", | ||
133 | cpu, prev_tid, next_tid); | ||
134 | err = check_cpu(switch_tracking, cpu); | ||
135 | if (err) | ||
136 | return err; | ||
137 | /* | ||
138 | * Check for no missing sched_switch events i.e. that the | ||
139 | * evsel->system_wide flag has worked. | ||
140 | */ | ||
141 | if (switch_tracking->tids[cpu] != -1 && | ||
142 | switch_tracking->tids[cpu] != prev_tid) { | ||
143 | pr_debug("Missing sched_switch events\n"); | ||
144 | return -1; | ||
145 | } | ||
146 | switch_tracking->tids[cpu] = next_tid; | ||
147 | } | ||
148 | |||
149 | if (evsel == switch_tracking->cycles_evsel) { | ||
150 | pr_debug3("cycles event\n"); | ||
151 | if (!switch_tracking->comm_seen[0]) | ||
152 | switch_tracking->cycles_before_comm_1 = 1; | ||
153 | if (switch_tracking->comm_seen[1] && | ||
154 | !switch_tracking->comm_seen[2]) | ||
155 | switch_tracking->cycles_between_comm_2_and_comm_3 = 1; | ||
156 | if (switch_tracking->comm_seen[3]) | ||
157 | switch_tracking->cycles_after_comm_4 = 1; | ||
158 | } | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int process_event(struct perf_evlist *evlist, union perf_event *event, | ||
164 | struct switch_tracking *switch_tracking) | ||
165 | { | ||
166 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
167 | return process_sample_event(evlist, event, switch_tracking); | ||
168 | |||
169 | if (event->header.type == PERF_RECORD_COMM) { | ||
170 | int err, done = 0; | ||
171 | |||
172 | err = check_comm(switch_tracking, event, "Test COMM 1", 0); | ||
173 | if (err < 0) | ||
174 | return -1; | ||
175 | done += err; | ||
176 | err = check_comm(switch_tracking, event, "Test COMM 2", 1); | ||
177 | if (err < 0) | ||
178 | return -1; | ||
179 | done += err; | ||
180 | err = check_comm(switch_tracking, event, "Test COMM 3", 2); | ||
181 | if (err < 0) | ||
182 | return -1; | ||
183 | done += err; | ||
184 | err = check_comm(switch_tracking, event, "Test COMM 4", 3); | ||
185 | if (err < 0) | ||
186 | return -1; | ||
187 | done += err; | ||
188 | if (done != 1) { | ||
189 | pr_debug("Unexpected comm event\n"); | ||
190 | return -1; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | struct event_node { | ||
198 | struct list_head list; | ||
199 | union perf_event *event; | ||
200 | u64 event_time; | ||
201 | }; | ||
202 | |||
203 | static int add_event(struct perf_evlist *evlist, struct list_head *events, | ||
204 | union perf_event *event) | ||
205 | { | ||
206 | struct perf_sample sample; | ||
207 | struct event_node *node; | ||
208 | |||
209 | node = malloc(sizeof(struct event_node)); | ||
210 | if (!node) { | ||
211 | pr_debug("malloc failed\n"); | ||
212 | return -1; | ||
213 | } | ||
214 | node->event = event; | ||
215 | list_add(&node->list, events); | ||
216 | |||
217 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | ||
218 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
219 | return -1; | ||
220 | } | ||
221 | |||
222 | if (!sample.time) { | ||
223 | pr_debug("event with no time\n"); | ||
224 | return -1; | ||
225 | } | ||
226 | |||
227 | node->event_time = sample.time; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static void free_event_nodes(struct list_head *events) | ||
233 | { | ||
234 | struct event_node *node; | ||
235 | |||
236 | while (!list_empty(events)) { | ||
237 | node = list_entry(events->next, struct event_node, list); | ||
238 | list_del(&node->list); | ||
239 | free(node); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | static int compar(const void *a, const void *b) | ||
244 | { | ||
245 | const struct event_node *nodea = a; | ||
246 | const struct event_node *nodeb = b; | ||
247 | s64 cmp = nodea->event_time - nodeb->event_time; | ||
248 | |||
249 | return cmp; | ||
250 | } | ||
251 | |||
252 | static int process_events(struct perf_evlist *evlist, | ||
253 | struct switch_tracking *switch_tracking) | ||
254 | { | ||
255 | union perf_event *event; | ||
256 | unsigned pos, cnt = 0; | ||
257 | LIST_HEAD(events); | ||
258 | struct event_node *events_array, *node; | ||
259 | int i, ret; | ||
260 | |||
261 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
262 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
263 | cnt += 1; | ||
264 | ret = add_event(evlist, &events, event); | ||
265 | perf_evlist__mmap_consume(evlist, i); | ||
266 | if (ret < 0) | ||
267 | goto out_free_nodes; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | events_array = calloc(cnt, sizeof(struct event_node)); | ||
272 | if (!events_array) { | ||
273 | pr_debug("calloc failed\n"); | ||
274 | ret = -1; | ||
275 | goto out_free_nodes; | ||
276 | } | ||
277 | |||
278 | pos = 0; | ||
279 | list_for_each_entry(node, &events, list) | ||
280 | events_array[pos++] = *node; | ||
281 | |||
282 | qsort(events_array, cnt, sizeof(struct event_node), compar); | ||
283 | |||
284 | for (pos = 0; pos < cnt; pos++) { | ||
285 | ret = process_event(evlist, events_array[pos].event, | ||
286 | switch_tracking); | ||
287 | if (ret < 0) | ||
288 | goto out_free; | ||
289 | } | ||
290 | |||
291 | ret = 0; | ||
292 | out_free: | ||
293 | pr_debug("%u events recorded\n", cnt); | ||
294 | free(events_array); | ||
295 | out_free_nodes: | ||
296 | free_event_nodes(&events); | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * test__switch_tracking - test using sched_switch and tracking events. | ||
302 | * | ||
303 | * This function implements a test that checks that sched_switch events and | ||
304 | * tracking events can be recorded for a workload (current process) using the | ||
305 | * evsel->system_wide and evsel->tracking flags (respectively) with other events | ||
306 | * sometimes enabled or disabled. | ||
307 | */ | ||
308 | int test__switch_tracking(void) | ||
309 | { | ||
310 | const char *sched_switch = "sched:sched_switch"; | ||
311 | struct switch_tracking switch_tracking = { .tids = NULL, }; | ||
312 | struct record_opts opts = { | ||
313 | .mmap_pages = UINT_MAX, | ||
314 | .user_freq = UINT_MAX, | ||
315 | .user_interval = ULLONG_MAX, | ||
316 | .freq = 4000, | ||
317 | .target = { | ||
318 | .uses_mmap = true, | ||
319 | }, | ||
320 | }; | ||
321 | struct thread_map *threads = NULL; | ||
322 | struct cpu_map *cpus = NULL; | ||
323 | struct perf_evlist *evlist = NULL; | ||
324 | struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel; | ||
325 | struct perf_evsel *switch_evsel, *tracking_evsel; | ||
326 | const char *comm; | ||
327 | int err = -1; | ||
328 | |||
329 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
330 | if (!threads) { | ||
331 | pr_debug("thread_map__new failed!\n"); | ||
332 | goto out_err; | ||
333 | } | ||
334 | |||
335 | cpus = cpu_map__new(NULL); | ||
336 | if (!cpus) { | ||
337 | pr_debug("cpu_map__new failed!\n"); | ||
338 | goto out_err; | ||
339 | } | ||
340 | |||
341 | evlist = perf_evlist__new(); | ||
342 | if (!evlist) { | ||
343 | pr_debug("perf_evlist__new failed!\n"); | ||
344 | goto out_err; | ||
345 | } | ||
346 | |||
347 | perf_evlist__set_maps(evlist, cpus, threads); | ||
348 | |||
349 | /* First event */ | ||
350 | err = parse_events(evlist, "cpu-clock:u"); | ||
351 | if (err) { | ||
352 | pr_debug("Failed to parse event dummy:u\n"); | ||
353 | goto out_err; | ||
354 | } | ||
355 | |||
356 | cpu_clocks_evsel = perf_evlist__last(evlist); | ||
357 | |||
358 | /* Second event */ | ||
359 | err = parse_events(evlist, "cycles:u"); | ||
360 | if (err) { | ||
361 | pr_debug("Failed to parse event cycles:u\n"); | ||
362 | goto out_err; | ||
363 | } | ||
364 | |||
365 | cycles_evsel = perf_evlist__last(evlist); | ||
366 | |||
367 | /* Third event */ | ||
368 | if (!perf_evlist__can_select_event(evlist, sched_switch)) { | ||
369 | fprintf(stderr, " (no sched_switch)"); | ||
370 | err = 0; | ||
371 | goto out; | ||
372 | } | ||
373 | |||
374 | err = parse_events(evlist, sched_switch); | ||
375 | if (err) { | ||
376 | pr_debug("Failed to parse event %s\n", sched_switch); | ||
377 | goto out_err; | ||
378 | } | ||
379 | |||
380 | switch_evsel = perf_evlist__last(evlist); | ||
381 | |||
382 | perf_evsel__set_sample_bit(switch_evsel, CPU); | ||
383 | perf_evsel__set_sample_bit(switch_evsel, TIME); | ||
384 | |||
385 | switch_evsel->system_wide = true; | ||
386 | switch_evsel->no_aux_samples = true; | ||
387 | switch_evsel->immediate = true; | ||
388 | |||
389 | /* Test moving an event to the front */ | ||
390 | if (cycles_evsel == perf_evlist__first(evlist)) { | ||
391 | pr_debug("cycles event already at front"); | ||
392 | goto out_err; | ||
393 | } | ||
394 | perf_evlist__to_front(evlist, cycles_evsel); | ||
395 | if (cycles_evsel != perf_evlist__first(evlist)) { | ||
396 | pr_debug("Failed to move cycles event to front"); | ||
397 | goto out_err; | ||
398 | } | ||
399 | |||
400 | perf_evsel__set_sample_bit(cycles_evsel, CPU); | ||
401 | perf_evsel__set_sample_bit(cycles_evsel, TIME); | ||
402 | |||
403 | /* Fourth event */ | ||
404 | err = parse_events(evlist, "dummy:u"); | ||
405 | if (err) { | ||
406 | pr_debug("Failed to parse event dummy:u\n"); | ||
407 | goto out_err; | ||
408 | } | ||
409 | |||
410 | tracking_evsel = perf_evlist__last(evlist); | ||
411 | |||
412 | perf_evlist__set_tracking_event(evlist, tracking_evsel); | ||
413 | |||
414 | tracking_evsel->attr.freq = 0; | ||
415 | tracking_evsel->attr.sample_period = 1; | ||
416 | |||
417 | perf_evsel__set_sample_bit(tracking_evsel, TIME); | ||
418 | |||
419 | /* Config events */ | ||
420 | perf_evlist__config(evlist, &opts); | ||
421 | |||
422 | /* Check moved event is still at the front */ | ||
423 | if (cycles_evsel != perf_evlist__first(evlist)) { | ||
424 | pr_debug("Front event no longer at front"); | ||
425 | goto out_err; | ||
426 | } | ||
427 | |||
428 | /* Check tracking event is tracking */ | ||
429 | if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) { | ||
430 | pr_debug("Tracking event not tracking\n"); | ||
431 | goto out_err; | ||
432 | } | ||
433 | |||
434 | /* Check non-tracking events are not tracking */ | ||
435 | evlist__for_each(evlist, evsel) { | ||
436 | if (evsel != tracking_evsel) { | ||
437 | if (evsel->attr.mmap || evsel->attr.comm) { | ||
438 | pr_debug("Non-tracking event is tracking\n"); | ||
439 | goto out_err; | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | if (perf_evlist__open(evlist) < 0) { | ||
445 | fprintf(stderr, " (not supported)"); | ||
446 | err = 0; | ||
447 | goto out; | ||
448 | } | ||
449 | |||
450 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
451 | if (err) { | ||
452 | pr_debug("perf_evlist__mmap failed!\n"); | ||
453 | goto out_err; | ||
454 | } | ||
455 | |||
456 | perf_evlist__enable(evlist); | ||
457 | |||
458 | err = perf_evlist__disable_event(evlist, cpu_clocks_evsel); | ||
459 | if (err) { | ||
460 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
461 | goto out_err; | ||
462 | } | ||
463 | |||
464 | err = spin_sleep(); | ||
465 | if (err) { | ||
466 | pr_debug("spin_sleep failed!\n"); | ||
467 | goto out_err; | ||
468 | } | ||
469 | |||
470 | comm = "Test COMM 1"; | ||
471 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
472 | if (err) { | ||
473 | pr_debug("PR_SET_NAME failed!\n"); | ||
474 | goto out_err; | ||
475 | } | ||
476 | |||
477 | err = perf_evlist__disable_event(evlist, cycles_evsel); | ||
478 | if (err) { | ||
479 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
480 | goto out_err; | ||
481 | } | ||
482 | |||
483 | comm = "Test COMM 2"; | ||
484 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
485 | if (err) { | ||
486 | pr_debug("PR_SET_NAME failed!\n"); | ||
487 | goto out_err; | ||
488 | } | ||
489 | |||
490 | err = spin_sleep(); | ||
491 | if (err) { | ||
492 | pr_debug("spin_sleep failed!\n"); | ||
493 | goto out_err; | ||
494 | } | ||
495 | |||
496 | comm = "Test COMM 3"; | ||
497 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
498 | if (err) { | ||
499 | pr_debug("PR_SET_NAME failed!\n"); | ||
500 | goto out_err; | ||
501 | } | ||
502 | |||
503 | err = perf_evlist__enable_event(evlist, cycles_evsel); | ||
504 | if (err) { | ||
505 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
506 | goto out_err; | ||
507 | } | ||
508 | |||
509 | comm = "Test COMM 4"; | ||
510 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
511 | if (err) { | ||
512 | pr_debug("PR_SET_NAME failed!\n"); | ||
513 | goto out_err; | ||
514 | } | ||
515 | |||
516 | err = spin_sleep(); | ||
517 | if (err) { | ||
518 | pr_debug("spin_sleep failed!\n"); | ||
519 | goto out_err; | ||
520 | } | ||
521 | |||
522 | perf_evlist__disable(evlist); | ||
523 | |||
524 | switch_tracking.switch_evsel = switch_evsel; | ||
525 | switch_tracking.cycles_evsel = cycles_evsel; | ||
526 | |||
527 | err = process_events(evlist, &switch_tracking); | ||
528 | |||
529 | zfree(&switch_tracking.tids); | ||
530 | |||
531 | if (err) | ||
532 | goto out_err; | ||
533 | |||
534 | /* Check all 4 comm events were seen i.e. that evsel->tracking works */ | ||
535 | if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] || | ||
536 | !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) { | ||
537 | pr_debug("Missing comm events\n"); | ||
538 | goto out_err; | ||
539 | } | ||
540 | |||
541 | /* Check cycles event got enabled */ | ||
542 | if (!switch_tracking.cycles_before_comm_1) { | ||
543 | pr_debug("Missing cycles events\n"); | ||
544 | goto out_err; | ||
545 | } | ||
546 | |||
547 | /* Check cycles event got disabled */ | ||
548 | if (switch_tracking.cycles_between_comm_2_and_comm_3) { | ||
549 | pr_debug("cycles events even though event was disabled\n"); | ||
550 | goto out_err; | ||
551 | } | ||
552 | |||
553 | /* Check cycles event got enabled again */ | ||
554 | if (!switch_tracking.cycles_after_comm_4) { | ||
555 | pr_debug("Missing cycles events\n"); | ||
556 | goto out_err; | ||
557 | } | ||
558 | out: | ||
559 | if (evlist) { | ||
560 | perf_evlist__disable(evlist); | ||
561 | perf_evlist__delete(evlist); | ||
562 | } else { | ||
563 | cpu_map__delete(cpus); | ||
564 | thread_map__delete(threads); | ||
565 | } | ||
566 | |||
567 | return err; | ||
568 | |||
569 | out_err: | ||
570 | err = -1; | ||
571 | goto out; | ||
572 | } | ||
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 5ff3db318f12..3a8fedef83bc 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c | |||
@@ -42,6 +42,7 @@ int test__task_exit(void) | |||
42 | .uses_mmap = true, | 42 | .uses_mmap = true, |
43 | }; | 43 | }; |
44 | const char *argv[] = { "true", NULL }; | 44 | const char *argv[] = { "true", NULL }; |
45 | char sbuf[STRERR_BUFSIZE]; | ||
45 | 46 | ||
46 | signal(SIGCHLD, sig_handler); | 47 | signal(SIGCHLD, sig_handler); |
47 | 48 | ||
@@ -82,13 +83,14 @@ int test__task_exit(void) | |||
82 | 83 | ||
83 | err = perf_evlist__open(evlist); | 84 | err = perf_evlist__open(evlist); |
84 | if (err < 0) { | 85 | if (err < 0) { |
85 | pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); | 86 | pr_debug("Couldn't open the evlist: %s\n", |
87 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
86 | goto out_delete_evlist; | 88 | goto out_delete_evlist; |
87 | } | 89 | } |
88 | 90 | ||
89 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | 91 | if (perf_evlist__mmap(evlist, 128, true) < 0) { |
90 | pr_debug("failed to mmap events: %d (%s)\n", errno, | 92 | pr_debug("failed to mmap events: %d (%s)\n", errno, |
91 | strerror(errno)); | 93 | strerror_r(errno, sbuf, sizeof(sbuf))); |
92 | goto out_delete_evlist; | 94 | goto out_delete_evlist; |
93 | } | 95 | } |
94 | 96 | ||
@@ -103,7 +105,7 @@ retry: | |||
103 | } | 105 | } |
104 | 106 | ||
105 | if (!exited || !nr_exit) { | 107 | if (!exited || !nr_exit) { |
106 | poll(evlist->pollfd, evlist->nr_fds, -1); | 108 | perf_evlist__poll(evlist, -1); |
107 | goto retry; | 109 | goto retry; |
108 | } | 110 | } |
109 | 111 | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index ed64790a395f..00e776a87a9c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -48,6 +48,9 @@ int test__mmap_thread_lookup(void); | |||
48 | int test__thread_mg_share(void); | 48 | int test__thread_mg_share(void); |
49 | int test__hists_output(void); | 49 | int test__hists_output(void); |
50 | int test__hists_cumulate(void); | 50 | int test__hists_cumulate(void); |
51 | int test__switch_tracking(void); | ||
52 | int test__fdarray__filter(void); | ||
53 | int test__fdarray__add(void); | ||
51 | 54 | ||
52 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) | 55 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) |
53 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 56 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index f0697a3aede0..1e0a2fd80115 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -27,6 +27,7 @@ static struct annotate_browser_opt { | |||
27 | bool hide_src_code, | 27 | bool hide_src_code, |
28 | use_offset, | 28 | use_offset, |
29 | jump_arrows, | 29 | jump_arrows, |
30 | show_linenr, | ||
30 | show_nr_jumps; | 31 | show_nr_jumps; |
31 | } annotate_browser__opts = { | 32 | } annotate_browser__opts = { |
32 | .use_offset = true, | 33 | .use_offset = true, |
@@ -128,7 +129,11 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
128 | if (!*dl->line) | 129 | if (!*dl->line) |
129 | slsmg_write_nstring(" ", width - pcnt_width); | 130 | slsmg_write_nstring(" ", width - pcnt_width); |
130 | else if (dl->offset == -1) { | 131 | else if (dl->offset == -1) { |
131 | printed = scnprintf(bf, sizeof(bf), "%*s ", | 132 | if (dl->line_nr && annotate_browser__opts.show_linenr) |
133 | printed = scnprintf(bf, sizeof(bf), "%-*d ", | ||
134 | ab->addr_width + 1, dl->line_nr); | ||
135 | else | ||
136 | printed = scnprintf(bf, sizeof(bf), "%*s ", | ||
132 | ab->addr_width, " "); | 137 | ab->addr_width, " "); |
133 | slsmg_write_nstring(bf, printed); | 138 | slsmg_write_nstring(bf, printed); |
134 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); | 139 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); |
@@ -733,6 +738,7 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
733 | "o Toggle disassembler output/simplified view\n" | 738 | "o Toggle disassembler output/simplified view\n" |
734 | "s Toggle source code view\n" | 739 | "s Toggle source code view\n" |
735 | "/ Search string\n" | 740 | "/ Search string\n" |
741 | "k Toggle line numbers\n" | ||
736 | "r Run available scripts\n" | 742 | "r Run available scripts\n" |
737 | "? Search string backwards\n"); | 743 | "? Search string backwards\n"); |
738 | continue; | 744 | continue; |
@@ -741,6 +747,10 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
741 | script_browse(NULL); | 747 | script_browse(NULL); |
742 | continue; | 748 | continue; |
743 | } | 749 | } |
750 | case 'k': | ||
751 | annotate_browser__opts.show_linenr = | ||
752 | !annotate_browser__opts.show_linenr; | ||
753 | break; | ||
744 | case 'H': | 754 | case 'H': |
745 | nd = browser->curr_hot; | 755 | nd = browser->curr_hot; |
746 | break; | 756 | break; |
@@ -984,6 +994,7 @@ static struct annotate_config { | |||
984 | } annotate__configs[] = { | 994 | } annotate__configs[] = { |
985 | ANNOTATE_CFG(hide_src_code), | 995 | ANNOTATE_CFG(hide_src_code), |
986 | ANNOTATE_CFG(jump_arrows), | 996 | ANNOTATE_CFG(jump_arrows), |
997 | ANNOTATE_CFG(show_linenr), | ||
987 | ANNOTATE_CFG(show_nr_jumps), | 998 | ANNOTATE_CFG(show_nr_jumps), |
988 | ANNOTATE_CFG(use_offset), | 999 | ANNOTATE_CFG(use_offset), |
989 | }; | 1000 | }; |
diff --git a/tools/perf/ui/browsers/header.c b/tools/perf/ui/browsers/header.c index 89c16b988618..e8278c558d4a 100644 --- a/tools/perf/ui/browsers/header.c +++ b/tools/perf/ui/browsers/header.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "util/cache.h" | 1 | #include "util/cache.h" |
2 | #include "util/debug.h" | 2 | #include "util/debug.h" |
3 | #include "ui/browser.h" | 3 | #include "ui/browser.h" |
4 | #include "ui/keysyms.h" | ||
4 | #include "ui/ui.h" | 5 | #include "ui/ui.h" |
5 | #include "ui/util.h" | 6 | #include "ui/util.h" |
6 | #include "ui/libslang.h" | 7 | #include "ui/libslang.h" |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a94b11fc5e00..788506eef567 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "../../util/pstack.h" | 10 | #include "../../util/pstack.h" |
11 | #include "../../util/sort.h" | 11 | #include "../../util/sort.h" |
12 | #include "../../util/util.h" | 12 | #include "../../util/util.h" |
13 | #include "../../util/top.h" | ||
13 | #include "../../arch/common.h" | 14 | #include "../../arch/common.h" |
14 | 15 | ||
15 | #include "../browser.h" | 16 | #include "../browser.h" |
@@ -34,7 +35,9 @@ struct hist_browser { | |||
34 | 35 | ||
35 | extern void hist_browser__init_hpp(void); | 36 | extern void hist_browser__init_hpp(void); |
36 | 37 | ||
37 | static int hists__browser_title(struct hists *hists, char *bf, size_t size); | 38 | static int hists__browser_title(struct hists *hists, |
39 | struct hist_browser_timer *hbt, | ||
40 | char *bf, size_t size); | ||
38 | static void hist_browser__update_nr_entries(struct hist_browser *hb); | 41 | static void hist_browser__update_nr_entries(struct hist_browser *hb); |
39 | 42 | ||
40 | static struct rb_node *hists__filter_entries(struct rb_node *nd, | 43 | static struct rb_node *hists__filter_entries(struct rb_node *nd, |
@@ -224,23 +227,30 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no | |||
224 | } | 227 | } |
225 | } | 228 | } |
226 | 229 | ||
227 | static void callchain_node__init_have_children(struct callchain_node *node) | 230 | static void callchain_node__init_have_children(struct callchain_node *node, |
231 | bool has_sibling) | ||
228 | { | 232 | { |
229 | struct callchain_list *chain; | 233 | struct callchain_list *chain; |
230 | 234 | ||
231 | list_for_each_entry(chain, &node->val, list) | 235 | chain = list_entry(node->val.next, struct callchain_list, list); |
236 | chain->ms.has_children = has_sibling; | ||
237 | |||
238 | if (!list_empty(&node->val)) { | ||
239 | chain = list_entry(node->val.prev, struct callchain_list, list); | ||
232 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); | 240 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); |
241 | } | ||
233 | 242 | ||
234 | callchain_node__init_have_children_rb_tree(node); | 243 | callchain_node__init_have_children_rb_tree(node); |
235 | } | 244 | } |
236 | 245 | ||
237 | static void callchain__init_have_children(struct rb_root *root) | 246 | static void callchain__init_have_children(struct rb_root *root) |
238 | { | 247 | { |
239 | struct rb_node *nd; | 248 | struct rb_node *nd = rb_first(root); |
249 | bool has_sibling = nd && rb_next(nd); | ||
240 | 250 | ||
241 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | 251 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { |
242 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | 252 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); |
243 | callchain_node__init_have_children(node); | 253 | callchain_node__init_have_children(node, has_sibling); |
244 | } | 254 | } |
245 | } | 255 | } |
246 | 256 | ||
@@ -387,7 +397,7 @@ static int hist_browser__run(struct hist_browser *browser, | |||
387 | browser->b.entries = &browser->hists->entries; | 397 | browser->b.entries = &browser->hists->entries; |
388 | browser->b.nr_entries = hist_browser__nr_entries(browser); | 398 | browser->b.nr_entries = hist_browser__nr_entries(browser); |
389 | 399 | ||
390 | hists__browser_title(browser->hists, title, sizeof(title)); | 400 | hists__browser_title(browser->hists, hbt, title, sizeof(title)); |
391 | 401 | ||
392 | if (ui_browser__show(&browser->b, title, | 402 | if (ui_browser__show(&browser->b, title, |
393 | "Press '?' for help on key bindings") < 0) | 403 | "Press '?' for help on key bindings") < 0) |
@@ -414,7 +424,8 @@ static int hist_browser__run(struct hist_browser *browser, | |||
414 | ui_browser__warn_lost_events(&browser->b); | 424 | ui_browser__warn_lost_events(&browser->b); |
415 | } | 425 | } |
416 | 426 | ||
417 | hists__browser_title(browser->hists, title, sizeof(title)); | 427 | hists__browser_title(browser->hists, |
428 | hbt, title, sizeof(title)); | ||
418 | ui_browser__show_title(&browser->b, title); | 429 | ui_browser__show_title(&browser->b, title); |
419 | continue; | 430 | continue; |
420 | } | 431 | } |
@@ -457,43 +468,90 @@ out: | |||
457 | return key; | 468 | return key; |
458 | } | 469 | } |
459 | 470 | ||
460 | static char *callchain_list__sym_name(struct callchain_list *cl, | 471 | struct callchain_print_arg { |
461 | char *bf, size_t bfsize, bool show_dso) | 472 | /* for hists browser */ |
473 | off_t row_offset; | ||
474 | bool is_current_entry; | ||
475 | |||
476 | /* for file dump */ | ||
477 | FILE *fp; | ||
478 | int printed; | ||
479 | }; | ||
480 | |||
481 | typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, | ||
482 | struct callchain_list *chain, | ||
483 | const char *str, int offset, | ||
484 | unsigned short row, | ||
485 | struct callchain_print_arg *arg); | ||
486 | |||
487 | static void hist_browser__show_callchain_entry(struct hist_browser *browser, | ||
488 | struct callchain_list *chain, | ||
489 | const char *str, int offset, | ||
490 | unsigned short row, | ||
491 | struct callchain_print_arg *arg) | ||
492 | { | ||
493 | int color, width; | ||
494 | char folded_sign = callchain_list__folded(chain); | ||
495 | |||
496 | color = HE_COLORSET_NORMAL; | ||
497 | width = browser->b.width - (offset + 2); | ||
498 | if (ui_browser__is_current_entry(&browser->b, row)) { | ||
499 | browser->selection = &chain->ms; | ||
500 | color = HE_COLORSET_SELECTED; | ||
501 | arg->is_current_entry = true; | ||
502 | } | ||
503 | |||
504 | ui_browser__set_color(&browser->b, color); | ||
505 | hist_browser__gotorc(browser, row, 0); | ||
506 | slsmg_write_nstring(" ", offset); | ||
507 | slsmg_printf("%c ", folded_sign); | ||
508 | slsmg_write_nstring(str, width); | ||
509 | } | ||
510 | |||
511 | static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, | ||
512 | struct callchain_list *chain, | ||
513 | const char *str, int offset, | ||
514 | unsigned short row __maybe_unused, | ||
515 | struct callchain_print_arg *arg) | ||
462 | { | 516 | { |
463 | int printed; | 517 | char folded_sign = callchain_list__folded(chain); |
464 | 518 | ||
465 | if (cl->ms.sym) | 519 | arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", |
466 | printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | 520 | folded_sign, str); |
467 | else | 521 | } |
468 | printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | 522 | |
523 | typedef bool (*check_output_full_fn)(struct hist_browser *browser, | ||
524 | unsigned short row); | ||
469 | 525 | ||
470 | if (show_dso) | 526 | static bool hist_browser__check_output_full(struct hist_browser *browser, |
471 | scnprintf(bf + printed, bfsize - printed, " %s", | 527 | unsigned short row) |
472 | cl->ms.map ? cl->ms.map->dso->short_name : "unknown"); | 528 | { |
529 | return browser->b.rows == row; | ||
530 | } | ||
473 | 531 | ||
474 | return bf; | 532 | static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, |
533 | unsigned short row __maybe_unused) | ||
534 | { | ||
535 | return false; | ||
475 | } | 536 | } |
476 | 537 | ||
477 | #define LEVEL_OFFSET_STEP 3 | 538 | #define LEVEL_OFFSET_STEP 3 |
478 | 539 | ||
479 | static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser, | 540 | static int hist_browser__show_callchain(struct hist_browser *browser, |
480 | struct callchain_node *chain_node, | 541 | struct rb_root *root, int level, |
481 | u64 total, int level, | 542 | unsigned short row, u64 total, |
482 | unsigned short row, | 543 | print_callchain_entry_fn print, |
483 | off_t *row_offset, | 544 | struct callchain_print_arg *arg, |
484 | bool *is_current_entry) | 545 | check_output_full_fn is_output_full) |
485 | { | 546 | { |
486 | struct rb_node *node; | 547 | struct rb_node *node; |
487 | int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; | 548 | int first_row = row, offset = level * LEVEL_OFFSET_STEP; |
488 | u64 new_total, remaining; | 549 | u64 new_total; |
550 | bool need_percent; | ||
489 | 551 | ||
490 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 552 | node = rb_first(root); |
491 | new_total = chain_node->children_hit; | 553 | need_percent = node && rb_next(node); |
492 | else | ||
493 | new_total = total; | ||
494 | 554 | ||
495 | remaining = new_total; | ||
496 | node = rb_first(&chain_node->rb_root); | ||
497 | while (node) { | 555 | while (node) { |
498 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 556 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
499 | struct rb_node *next = rb_next(node); | 557 | struct rb_node *next = rb_next(node); |
@@ -503,30 +561,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
503 | int first = true; | 561 | int first = true; |
504 | int extra_offset = 0; | 562 | int extra_offset = 0; |
505 | 563 | ||
506 | remaining -= cumul; | ||
507 | |||
508 | list_for_each_entry(chain, &child->val, list) { | 564 | list_for_each_entry(chain, &child->val, list) { |
509 | char bf[1024], *alloc_str; | 565 | char bf[1024], *alloc_str; |
510 | const char *str; | 566 | const char *str; |
511 | int color; | ||
512 | bool was_first = first; | 567 | bool was_first = first; |
513 | 568 | ||
514 | if (first) | 569 | if (first) |
515 | first = false; | 570 | first = false; |
516 | else | 571 | else if (need_percent) |
517 | extra_offset = LEVEL_OFFSET_STEP; | 572 | extra_offset = LEVEL_OFFSET_STEP; |
518 | 573 | ||
519 | folded_sign = callchain_list__folded(chain); | 574 | folded_sign = callchain_list__folded(chain); |
520 | if (*row_offset != 0) { | 575 | if (arg->row_offset != 0) { |
521 | --*row_offset; | 576 | arg->row_offset--; |
522 | goto do_next; | 577 | goto do_next; |
523 | } | 578 | } |
524 | 579 | ||
525 | alloc_str = NULL; | 580 | alloc_str = NULL; |
526 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | 581 | str = callchain_list__sym_name(chain, bf, sizeof(bf), |
527 | browser->show_dso); | 582 | browser->show_dso); |
528 | if (was_first) { | 583 | |
529 | double percent = cumul * 100.0 / new_total; | 584 | if (was_first && need_percent) { |
585 | double percent = cumul * 100.0 / total; | ||
530 | 586 | ||
531 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | 587 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) |
532 | str = "Not enough memory!"; | 588 | str = "Not enough memory!"; |
@@ -534,22 +590,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
534 | str = alloc_str; | 590 | str = alloc_str; |
535 | } | 591 | } |
536 | 592 | ||
537 | color = HE_COLORSET_NORMAL; | 593 | print(browser, chain, str, offset + extra_offset, row, arg); |
538 | width = browser->b.width - (offset + extra_offset + 2); | ||
539 | if (ui_browser__is_current_entry(&browser->b, row)) { | ||
540 | browser->selection = &chain->ms; | ||
541 | color = HE_COLORSET_SELECTED; | ||
542 | *is_current_entry = true; | ||
543 | } | ||
544 | 594 | ||
545 | ui_browser__set_color(&browser->b, color); | ||
546 | hist_browser__gotorc(browser, row, 0); | ||
547 | slsmg_write_nstring(" ", offset + extra_offset); | ||
548 | slsmg_printf("%c ", folded_sign); | ||
549 | slsmg_write_nstring(str, width); | ||
550 | free(alloc_str); | 595 | free(alloc_str); |
551 | 596 | ||
552 | if (++row == browser->b.rows) | 597 | if (is_output_full(browser, ++row)) |
553 | goto out; | 598 | goto out; |
554 | do_next: | 599 | do_next: |
555 | if (folded_sign == '+') | 600 | if (folded_sign == '+') |
@@ -558,89 +603,21 @@ do_next: | |||
558 | 603 | ||
559 | if (folded_sign == '-') { | 604 | if (folded_sign == '-') { |
560 | const int new_level = level + (extra_offset ? 2 : 1); | 605 | const int new_level = level + (extra_offset ? 2 : 1); |
561 | row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total, | ||
562 | new_level, row, row_offset, | ||
563 | is_current_entry); | ||
564 | } | ||
565 | if (row == browser->b.rows) | ||
566 | goto out; | ||
567 | node = next; | ||
568 | } | ||
569 | out: | ||
570 | return row - first_row; | ||
571 | } | ||
572 | 606 | ||
573 | static int hist_browser__show_callchain_node(struct hist_browser *browser, | 607 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
574 | struct callchain_node *node, | 608 | new_total = child->children_hit; |
575 | int level, unsigned short row, | 609 | else |
576 | off_t *row_offset, | 610 | new_total = total; |
577 | bool *is_current_entry) | ||
578 | { | ||
579 | struct callchain_list *chain; | ||
580 | int first_row = row, | ||
581 | offset = level * LEVEL_OFFSET_STEP, | ||
582 | width = browser->b.width - offset; | ||
583 | char folded_sign = ' '; | ||
584 | |||
585 | list_for_each_entry(chain, &node->val, list) { | ||
586 | char bf[1024], *s; | ||
587 | int color; | ||
588 | |||
589 | folded_sign = callchain_list__folded(chain); | ||
590 | |||
591 | if (*row_offset != 0) { | ||
592 | --*row_offset; | ||
593 | continue; | ||
594 | } | ||
595 | 611 | ||
596 | color = HE_COLORSET_NORMAL; | 612 | row += hist_browser__show_callchain(browser, &child->rb_root, |
597 | if (ui_browser__is_current_entry(&browser->b, row)) { | 613 | new_level, row, new_total, |
598 | browser->selection = &chain->ms; | 614 | print, arg, is_output_full); |
599 | color = HE_COLORSET_SELECTED; | ||
600 | *is_current_entry = true; | ||
601 | } | 615 | } |
602 | 616 | if (is_output_full(browser, row)) | |
603 | s = callchain_list__sym_name(chain, bf, sizeof(bf), | ||
604 | browser->show_dso); | ||
605 | hist_browser__gotorc(browser, row, 0); | ||
606 | ui_browser__set_color(&browser->b, color); | ||
607 | slsmg_write_nstring(" ", offset); | ||
608 | slsmg_printf("%c ", folded_sign); | ||
609 | slsmg_write_nstring(s, width - 2); | ||
610 | |||
611 | if (++row == browser->b.rows) | ||
612 | goto out; | ||
613 | } | ||
614 | |||
615 | if (folded_sign == '-') | ||
616 | row += hist_browser__show_callchain_node_rb_tree(browser, node, | ||
617 | browser->hists->stats.total_period, | ||
618 | level + 1, row, | ||
619 | row_offset, | ||
620 | is_current_entry); | ||
621 | out: | ||
622 | return row - first_row; | ||
623 | } | ||
624 | |||
625 | static int hist_browser__show_callchain(struct hist_browser *browser, | ||
626 | struct rb_root *chain, | ||
627 | int level, unsigned short row, | ||
628 | off_t *row_offset, | ||
629 | bool *is_current_entry) | ||
630 | { | ||
631 | struct rb_node *nd; | ||
632 | int first_row = row; | ||
633 | |||
634 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
635 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
636 | |||
637 | row += hist_browser__show_callchain_node(browser, node, level, | ||
638 | row, row_offset, | ||
639 | is_current_entry); | ||
640 | if (row == browser->b.rows) | ||
641 | break; | 617 | break; |
618 | node = next; | ||
642 | } | 619 | } |
643 | 620 | out: | |
644 | return row - first_row; | 621 | return row - first_row; |
645 | } | 622 | } |
646 | 623 | ||
@@ -653,17 +630,18 @@ struct hpp_arg { | |||
653 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) | 630 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) |
654 | { | 631 | { |
655 | struct hpp_arg *arg = hpp->ptr; | 632 | struct hpp_arg *arg = hpp->ptr; |
656 | int ret; | 633 | int ret, len; |
657 | va_list args; | 634 | va_list args; |
658 | double percent; | 635 | double percent; |
659 | 636 | ||
660 | va_start(args, fmt); | 637 | va_start(args, fmt); |
638 | len = va_arg(args, int); | ||
661 | percent = va_arg(args, double); | 639 | percent = va_arg(args, double); |
662 | va_end(args); | 640 | va_end(args); |
663 | 641 | ||
664 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | 642 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); |
665 | 643 | ||
666 | ret = scnprintf(hpp->buf, hpp->size, fmt, percent); | 644 | ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); |
667 | slsmg_printf("%s", hpp->buf); | 645 | slsmg_printf("%s", hpp->buf); |
668 | 646 | ||
669 | advance_hpp(hpp, ret); | 647 | advance_hpp(hpp, ret); |
@@ -677,12 +655,12 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \ | |||
677 | } \ | 655 | } \ |
678 | \ | 656 | \ |
679 | static int \ | 657 | static int \ |
680 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 658 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
681 | struct perf_hpp *hpp, \ | 659 | struct perf_hpp *hpp, \ |
682 | struct hist_entry *he) \ | 660 | struct hist_entry *he) \ |
683 | { \ | 661 | { \ |
684 | return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ | 662 | return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ |
685 | __hpp__slsmg_color_printf, true); \ | 663 | __hpp__slsmg_color_printf, true); \ |
686 | } | 664 | } |
687 | 665 | ||
688 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 666 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
@@ -692,18 +670,20 @@ static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ | |||
692 | } \ | 670 | } \ |
693 | \ | 671 | \ |
694 | static int \ | 672 | static int \ |
695 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 673 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
696 | struct perf_hpp *hpp, \ | 674 | struct perf_hpp *hpp, \ |
697 | struct hist_entry *he) \ | 675 | struct hist_entry *he) \ |
698 | { \ | 676 | { \ |
699 | if (!symbol_conf.cumulate_callchain) { \ | 677 | if (!symbol_conf.cumulate_callchain) { \ |
700 | int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \ | 678 | int len = fmt->user_len ?: fmt->len; \ |
679 | int ret = scnprintf(hpp->buf, hpp->size, \ | ||
680 | "%*s", len, "N/A"); \ | ||
701 | slsmg_printf("%s", hpp->buf); \ | 681 | slsmg_printf("%s", hpp->buf); \ |
702 | \ | 682 | \ |
703 | return ret; \ | 683 | return ret; \ |
704 | } \ | 684 | } \ |
705 | return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \ | 685 | return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ |
706 | __hpp__slsmg_color_printf, true); \ | 686 | " %*.2f%%", __hpp__slsmg_color_printf, true); \ |
707 | } | 687 | } |
708 | 688 | ||
709 | __HPP_COLOR_PERCENT_FN(overhead, period) | 689 | __HPP_COLOR_PERCENT_FN(overhead, period) |
@@ -812,10 +792,25 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
812 | --row_offset; | 792 | --row_offset; |
813 | 793 | ||
814 | if (folded_sign == '-' && row != browser->b.rows) { | 794 | if (folded_sign == '-' && row != browser->b.rows) { |
815 | printed += hist_browser__show_callchain(browser, &entry->sorted_chain, | 795 | u64 total = hists__total_period(entry->hists); |
816 | 1, row, &row_offset, | 796 | struct callchain_print_arg arg = { |
817 | ¤t_entry); | 797 | .row_offset = row_offset, |
818 | if (current_entry) | 798 | .is_current_entry = current_entry, |
799 | }; | ||
800 | |||
801 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
802 | if (symbol_conf.cumulate_callchain) | ||
803 | total = entry->stat_acc->period; | ||
804 | else | ||
805 | total = entry->stat.period; | ||
806 | } | ||
807 | |||
808 | printed += hist_browser__show_callchain(browser, | ||
809 | &entry->sorted_chain, 1, row, total, | ||
810 | hist_browser__show_callchain_entry, &arg, | ||
811 | hist_browser__check_output_full); | ||
812 | |||
813 | if (arg.is_current_entry) | ||
819 | browser->he_selection = entry; | 814 | browser->he_selection = entry; |
820 | } | 815 | } |
821 | 816 | ||
@@ -847,9 +842,6 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) | |||
847 | if (perf_hpp__should_skip(fmt)) | 842 | if (perf_hpp__should_skip(fmt)) |
848 | continue; | 843 | continue; |
849 | 844 | ||
850 | /* We need to add the length of the columns header. */ | ||
851 | perf_hpp__reset_width(fmt, hists); | ||
852 | |||
853 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); | 845 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); |
854 | if (advance_hpp_check(&dummy_hpp, ret)) | 846 | if (advance_hpp_check(&dummy_hpp, ret)) |
855 | break; | 847 | break; |
@@ -1074,113 +1066,21 @@ do_offset: | |||
1074 | } | 1066 | } |
1075 | } | 1067 | } |
1076 | 1068 | ||
1077 | static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, | ||
1078 | struct callchain_node *chain_node, | ||
1079 | u64 total, int level, | ||
1080 | FILE *fp) | ||
1081 | { | ||
1082 | struct rb_node *node; | ||
1083 | int offset = level * LEVEL_OFFSET_STEP; | ||
1084 | u64 new_total, remaining; | ||
1085 | int printed = 0; | ||
1086 | |||
1087 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
1088 | new_total = chain_node->children_hit; | ||
1089 | else | ||
1090 | new_total = total; | ||
1091 | |||
1092 | remaining = new_total; | ||
1093 | node = rb_first(&chain_node->rb_root); | ||
1094 | while (node) { | ||
1095 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
1096 | struct rb_node *next = rb_next(node); | ||
1097 | u64 cumul = callchain_cumul_hits(child); | ||
1098 | struct callchain_list *chain; | ||
1099 | char folded_sign = ' '; | ||
1100 | int first = true; | ||
1101 | int extra_offset = 0; | ||
1102 | |||
1103 | remaining -= cumul; | ||
1104 | |||
1105 | list_for_each_entry(chain, &child->val, list) { | ||
1106 | char bf[1024], *alloc_str; | ||
1107 | const char *str; | ||
1108 | bool was_first = first; | ||
1109 | |||
1110 | if (first) | ||
1111 | first = false; | ||
1112 | else | ||
1113 | extra_offset = LEVEL_OFFSET_STEP; | ||
1114 | |||
1115 | folded_sign = callchain_list__folded(chain); | ||
1116 | |||
1117 | alloc_str = NULL; | ||
1118 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | ||
1119 | browser->show_dso); | ||
1120 | if (was_first) { | ||
1121 | double percent = cumul * 100.0 / new_total; | ||
1122 | |||
1123 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
1124 | str = "Not enough memory!"; | ||
1125 | else | ||
1126 | str = alloc_str; | ||
1127 | } | ||
1128 | |||
1129 | printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); | ||
1130 | free(alloc_str); | ||
1131 | if (folded_sign == '+') | ||
1132 | break; | ||
1133 | } | ||
1134 | |||
1135 | if (folded_sign == '-') { | ||
1136 | const int new_level = level + (extra_offset ? 2 : 1); | ||
1137 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, | ||
1138 | new_level, fp); | ||
1139 | } | ||
1140 | |||
1141 | node = next; | ||
1142 | } | ||
1143 | |||
1144 | return printed; | ||
1145 | } | ||
1146 | |||
1147 | static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, | ||
1148 | struct callchain_node *node, | ||
1149 | int level, FILE *fp) | ||
1150 | { | ||
1151 | struct callchain_list *chain; | ||
1152 | int offset = level * LEVEL_OFFSET_STEP; | ||
1153 | char folded_sign = ' '; | ||
1154 | int printed = 0; | ||
1155 | |||
1156 | list_for_each_entry(chain, &node->val, list) { | ||
1157 | char bf[1024], *s; | ||
1158 | |||
1159 | folded_sign = callchain_list__folded(chain); | ||
1160 | s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso); | ||
1161 | printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); | ||
1162 | } | ||
1163 | |||
1164 | if (folded_sign == '-') | ||
1165 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, | ||
1166 | browser->hists->stats.total_period, | ||
1167 | level + 1, fp); | ||
1168 | return printed; | ||
1169 | } | ||
1170 | |||
1171 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, | 1069 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, |
1172 | struct rb_root *chain, int level, FILE *fp) | 1070 | struct hist_entry *he, FILE *fp) |
1173 | { | 1071 | { |
1174 | struct rb_node *nd; | 1072 | u64 total = hists__total_period(he->hists); |
1175 | int printed = 0; | 1073 | struct callchain_print_arg arg = { |
1176 | 1074 | .fp = fp, | |
1177 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | 1075 | }; |
1178 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1179 | 1076 | ||
1180 | printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); | 1077 | if (symbol_conf.cumulate_callchain) |
1181 | } | 1078 | total = he->stat_acc->period; |
1182 | 1079 | ||
1183 | return printed; | 1080 | hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, |
1081 | hist_browser__fprintf_callchain_entry, &arg, | ||
1082 | hist_browser__check_dump_full); | ||
1083 | return arg.printed; | ||
1184 | } | 1084 | } |
1185 | 1085 | ||
1186 | static int hist_browser__fprintf_entry(struct hist_browser *browser, | 1086 | static int hist_browser__fprintf_entry(struct hist_browser *browser, |
@@ -1219,7 +1119,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
1219 | printed += fprintf(fp, "%s\n", rtrim(s)); | 1119 | printed += fprintf(fp, "%s\n", rtrim(s)); |
1220 | 1120 | ||
1221 | if (folded_sign == '-') | 1121 | if (folded_sign == '-') |
1222 | printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); | 1122 | printed += hist_browser__fprintf_callchain(browser, he, fp); |
1223 | 1123 | ||
1224 | return printed; | 1124 | return printed; |
1225 | } | 1125 | } |
@@ -1305,7 +1205,15 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser | |||
1305 | return browser->he_selection->thread; | 1205 | return browser->he_selection->thread; |
1306 | } | 1206 | } |
1307 | 1207 | ||
1308 | static int hists__browser_title(struct hists *hists, char *bf, size_t size) | 1208 | /* Check whether the browser is for 'top' or 'report' */ |
1209 | static inline bool is_report_browser(void *timer) | ||
1210 | { | ||
1211 | return timer == NULL; | ||
1212 | } | ||
1213 | |||
1214 | static int hists__browser_title(struct hists *hists, | ||
1215 | struct hist_browser_timer *hbt, | ||
1216 | char *bf, size_t size) | ||
1309 | { | 1217 | { |
1310 | char unit; | 1218 | char unit; |
1311 | int printed; | 1219 | int printed; |
@@ -1330,19 +1238,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size) | |||
1330 | ev_name = buf; | 1238 | ev_name = buf; |
1331 | 1239 | ||
1332 | for_each_group_member(pos, evsel) { | 1240 | for_each_group_member(pos, evsel) { |
1241 | struct hists *pos_hists = evsel__hists(pos); | ||
1242 | |||
1333 | if (symbol_conf.filter_relative) { | 1243 | if (symbol_conf.filter_relative) { |
1334 | nr_samples += pos->hists.stats.nr_non_filtered_samples; | 1244 | nr_samples += pos_hists->stats.nr_non_filtered_samples; |
1335 | nr_events += pos->hists.stats.total_non_filtered_period; | 1245 | nr_events += pos_hists->stats.total_non_filtered_period; |
1336 | } else { | 1246 | } else { |
1337 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1247 | nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
1338 | nr_events += pos->hists.stats.total_period; | 1248 | nr_events += pos_hists->stats.total_period; |
1339 | } | 1249 | } |
1340 | } | 1250 | } |
1341 | } | 1251 | } |
1342 | 1252 | ||
1343 | nr_samples = convert_unit(nr_samples, &unit); | 1253 | nr_samples = convert_unit(nr_samples, &unit); |
1344 | printed = scnprintf(bf, size, | 1254 | printed = scnprintf(bf, size, |
1345 | "Samples: %lu%c of event '%s', Event count (approx.): %lu", | 1255 | "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64, |
1346 | nr_samples, unit, ev_name, nr_events); | 1256 | nr_samples, unit, ev_name, nr_events); |
1347 | 1257 | ||
1348 | 1258 | ||
@@ -1357,6 +1267,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size) | |||
1357 | if (dso) | 1267 | if (dso) |
1358 | printed += scnprintf(bf + printed, size - printed, | 1268 | printed += scnprintf(bf + printed, size - printed, |
1359 | ", DSO: %s", dso->short_name); | 1269 | ", DSO: %s", dso->short_name); |
1270 | if (!is_report_browser(hbt)) { | ||
1271 | struct perf_top *top = hbt->arg; | ||
1272 | |||
1273 | if (top->zero) | ||
1274 | printed += scnprintf(bf + printed, size - printed, " [z]"); | ||
1275 | } | ||
1276 | |||
1360 | return printed; | 1277 | return printed; |
1361 | } | 1278 | } |
1362 | 1279 | ||
@@ -1368,12 +1285,6 @@ static inline void free_popup_options(char **options, int n) | |||
1368 | zfree(&options[i]); | 1285 | zfree(&options[i]); |
1369 | } | 1286 | } |
1370 | 1287 | ||
1371 | /* Check whether the browser is for 'top' or 'report' */ | ||
1372 | static inline bool is_report_browser(void *timer) | ||
1373 | { | ||
1374 | return timer == NULL; | ||
1375 | } | ||
1376 | |||
1377 | /* | 1288 | /* |
1378 | * Only runtime switching of perf data file will make "input_name" point | 1289 | * Only runtime switching of perf data file will make "input_name" point |
1379 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide | 1290 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide |
@@ -1488,7 +1399,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1488 | float min_pcnt, | 1399 | float min_pcnt, |
1489 | struct perf_session_env *env) | 1400 | struct perf_session_env *env) |
1490 | { | 1401 | { |
1491 | struct hists *hists = &evsel->hists; | 1402 | struct hists *hists = evsel__hists(evsel); |
1492 | struct hist_browser *browser = hist_browser__new(hists); | 1403 | struct hist_browser *browser = hist_browser__new(hists); |
1493 | struct branch_info *bi; | 1404 | struct branch_info *bi; |
1494 | struct pstack *fstack; | 1405 | struct pstack *fstack; |
@@ -1498,6 +1409,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1498 | char buf[64]; | 1409 | char buf[64]; |
1499 | char script_opt[64]; | 1410 | char script_opt[64]; |
1500 | int delay_secs = hbt ? hbt->refresh : 0; | 1411 | int delay_secs = hbt ? hbt->refresh : 0; |
1412 | struct perf_hpp_fmt *fmt; | ||
1501 | 1413 | ||
1502 | #define HIST_BROWSER_HELP_COMMON \ | 1414 | #define HIST_BROWSER_HELP_COMMON \ |
1503 | "h/?/F1 Show this window\n" \ | 1415 | "h/?/F1 Show this window\n" \ |
@@ -1529,6 +1441,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1529 | "P Print histograms to perf.hist.N\n" | 1441 | "P Print histograms to perf.hist.N\n" |
1530 | "t Zoom into current Thread\n" | 1442 | "t Zoom into current Thread\n" |
1531 | "V Verbose (DSO names in callchains, etc)\n" | 1443 | "V Verbose (DSO names in callchains, etc)\n" |
1444 | "z Toggle zeroing of samples\n" | ||
1532 | "/ Filter symbol by name"; | 1445 | "/ Filter symbol by name"; |
1533 | 1446 | ||
1534 | if (browser == NULL) | 1447 | if (browser == NULL) |
@@ -1547,6 +1460,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1547 | 1460 | ||
1548 | memset(options, 0, sizeof(options)); | 1461 | memset(options, 0, sizeof(options)); |
1549 | 1462 | ||
1463 | perf_hpp__for_each_format(fmt) | ||
1464 | perf_hpp__reset_width(fmt, hists); | ||
1465 | |||
1466 | if (symbol_conf.col_width_list_str) | ||
1467 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | ||
1468 | |||
1550 | while (1) { | 1469 | while (1) { |
1551 | const struct thread *thread = NULL; | 1470 | const struct thread *thread = NULL; |
1552 | const struct dso *dso = NULL; | 1471 | const struct dso *dso = NULL; |
@@ -1623,6 +1542,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1623 | case 'F': | 1542 | case 'F': |
1624 | symbol_conf.filter_relative ^= 1; | 1543 | symbol_conf.filter_relative ^= 1; |
1625 | continue; | 1544 | continue; |
1545 | case 'z': | ||
1546 | if (!is_report_browser(hbt)) { | ||
1547 | struct perf_top *top = hbt->arg; | ||
1548 | |||
1549 | top->zero = !top->zero; | ||
1550 | } | ||
1551 | continue; | ||
1626 | case K_F1: | 1552 | case K_F1: |
1627 | case 'h': | 1553 | case 'h': |
1628 | case '?': | 1554 | case '?': |
@@ -1888,8 +1814,9 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1888 | struct perf_evsel_menu *menu = container_of(browser, | 1814 | struct perf_evsel_menu *menu = container_of(browser, |
1889 | struct perf_evsel_menu, b); | 1815 | struct perf_evsel_menu, b); |
1890 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | 1816 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
1817 | struct hists *hists = evsel__hists(evsel); | ||
1891 | bool current_entry = ui_browser__is_current_entry(browser, row); | 1818 | bool current_entry = ui_browser__is_current_entry(browser, row); |
1892 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1819 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
1893 | const char *ev_name = perf_evsel__name(evsel); | 1820 | const char *ev_name = perf_evsel__name(evsel); |
1894 | char bf[256], unit; | 1821 | char bf[256], unit; |
1895 | const char *warn = " "; | 1822 | const char *warn = " "; |
@@ -1904,7 +1831,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1904 | ev_name = perf_evsel__group_name(evsel); | 1831 | ev_name = perf_evsel__group_name(evsel); |
1905 | 1832 | ||
1906 | for_each_group_member(pos, evsel) { | 1833 | for_each_group_member(pos, evsel) { |
1907 | nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1834 | struct hists *pos_hists = evsel__hists(pos); |
1835 | nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1908 | } | 1836 | } |
1909 | } | 1837 | } |
1910 | 1838 | ||
@@ -1913,7 +1841,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1913 | unit, unit == ' ' ? "" : " ", ev_name); | 1841 | unit, unit == ' ' ? "" : " ", ev_name); |
1914 | slsmg_printf("%s", bf); | 1842 | slsmg_printf("%s", bf); |
1915 | 1843 | ||
1916 | nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 1844 | nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; |
1917 | if (nr_events != 0) { | 1845 | if (nr_events != 0) { |
1918 | menu->lost_events = true; | 1846 | menu->lost_events = true; |
1919 | if (!current_entry) | 1847 | if (!current_entry) |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 6ca60e482cdc..4b3585eed1e8 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -11,6 +11,7 @@ | |||
11 | static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | 11 | static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) |
12 | { | 12 | { |
13 | int ret = 0; | 13 | int ret = 0; |
14 | int len; | ||
14 | va_list args; | 15 | va_list args; |
15 | double percent; | 16 | double percent; |
16 | const char *markup; | 17 | const char *markup; |
@@ -18,6 +19,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
18 | size_t size = hpp->size; | 19 | size_t size = hpp->size; |
19 | 20 | ||
20 | va_start(args, fmt); | 21 | va_start(args, fmt); |
22 | len = va_arg(args, int); | ||
21 | percent = va_arg(args, double); | 23 | percent = va_arg(args, double); |
22 | va_end(args); | 24 | va_end(args); |
23 | 25 | ||
@@ -25,7 +27,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
25 | if (markup) | 27 | if (markup) |
26 | ret += scnprintf(buf, size, markup); | 28 | ret += scnprintf(buf, size, markup); |
27 | 29 | ||
28 | ret += scnprintf(buf + ret, size - ret, fmt, percent); | 30 | ret += scnprintf(buf + ret, size - ret, fmt, len, percent); |
29 | 31 | ||
30 | if (markup) | 32 | if (markup) |
31 | ret += scnprintf(buf + ret, size - ret, "</span>"); | 33 | ret += scnprintf(buf + ret, size - ret, "</span>"); |
@@ -39,12 +41,12 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
39 | return he->stat._field; \ | 41 | return he->stat._field; \ |
40 | } \ | 42 | } \ |
41 | \ | 43 | \ |
42 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 44 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
43 | struct perf_hpp *hpp, \ | 45 | struct perf_hpp *hpp, \ |
44 | struct hist_entry *he) \ | 46 | struct hist_entry *he) \ |
45 | { \ | 47 | { \ |
46 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | 48 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
47 | __percent_color_snprintf, true); \ | 49 | __percent_color_snprintf, true); \ |
48 | } | 50 | } |
49 | 51 | ||
50 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 52 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
@@ -57,8 +59,8 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, | |||
57 | struct perf_hpp *hpp, \ | 59 | struct perf_hpp *hpp, \ |
58 | struct hist_entry *he) \ | 60 | struct hist_entry *he) \ |
59 | { \ | 61 | { \ |
60 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ | 62 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
61 | __percent_color_snprintf, true); \ | 63 | __percent_color_snprintf, true); \ |
62 | } | 64 | } |
63 | 65 | ||
64 | __HPP_COLOR_PERCENT_FN(overhead, period) | 66 | __HPP_COLOR_PERCENT_FN(overhead, period) |
@@ -87,15 +89,6 @@ void perf_gtk__init_hpp(void) | |||
87 | perf_gtk__hpp_color_overhead_acc; | 89 | perf_gtk__hpp_color_overhead_acc; |
88 | } | 90 | } |
89 | 91 | ||
90 | static void callchain_list__sym_name(struct callchain_list *cl, | ||
91 | char *bf, size_t bfsize) | ||
92 | { | ||
93 | if (cl->ms.sym) | ||
94 | scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
95 | else | ||
96 | scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
97 | } | ||
98 | |||
99 | static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, | 92 | static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, |
100 | GtkTreeIter *parent, int col, u64 total) | 93 | GtkTreeIter *parent, int col, u64 total) |
101 | { | 94 | { |
@@ -126,7 +119,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, | |||
126 | scnprintf(buf, sizeof(buf), "%5.2f%%", percent); | 119 | scnprintf(buf, sizeof(buf), "%5.2f%%", percent); |
127 | gtk_tree_store_set(store, &iter, 0, buf, -1); | 120 | gtk_tree_store_set(store, &iter, 0, buf, -1); |
128 | 121 | ||
129 | callchain_list__sym_name(chain, buf, sizeof(buf)); | 122 | callchain_list__sym_name(chain, buf, sizeof(buf), false); |
130 | gtk_tree_store_set(store, &iter, col, buf, -1); | 123 | gtk_tree_store_set(store, &iter, col, buf, -1); |
131 | 124 | ||
132 | if (need_new_parent) { | 125 | if (need_new_parent) { |
@@ -205,10 +198,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
205 | if (perf_hpp__is_sort_entry(fmt)) | 198 | if (perf_hpp__is_sort_entry(fmt)) |
206 | sym_col = col_idx; | 199 | sym_col = col_idx; |
207 | 200 | ||
208 | fmt->header(fmt, &hpp, hists_to_evsel(hists)); | ||
209 | |||
210 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | 201 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), |
211 | -1, ltrim(s), | 202 | -1, fmt->name, |
212 | renderer, "markup", | 203 | renderer, "markup", |
213 | col_idx++, NULL); | 204 | col_idx++, NULL); |
214 | } | 205 | } |
@@ -319,7 +310,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | |||
319 | gtk_container_add(GTK_CONTAINER(window), vbox); | 310 | gtk_container_add(GTK_CONTAINER(window), vbox); |
320 | 311 | ||
321 | evlist__for_each(evlist, pos) { | 312 | evlist__for_each(evlist, pos) { |
322 | struct hists *hists = &pos->hists; | 313 | struct hists *hists = evsel__hists(pos); |
323 | const char *evname = perf_evsel__name(pos); | 314 | const char *evname = perf_evsel__name(pos); |
324 | GtkWidget *scrolled_window; | 315 | GtkWidget *scrolled_window; |
325 | GtkWidget *tab_label; | 316 | GtkWidget *tab_label; |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 498adb23c02e..482adae3cc44 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -15,9 +15,9 @@ | |||
15 | __ret; \ | 15 | __ret; \ |
16 | }) | 16 | }) |
17 | 17 | ||
18 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 18 | static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
19 | hpp_field_fn get_field, const char *fmt, | 19 | hpp_field_fn get_field, const char *fmt, int len, |
20 | hpp_snprint_fn print_fn, bool fmt_percent) | 20 | hpp_snprint_fn print_fn, bool fmt_percent) |
21 | { | 21 | { |
22 | int ret; | 22 | int ret; |
23 | struct hists *hists = he->hists; | 23 | struct hists *hists = he->hists; |
@@ -32,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
32 | if (total) | 32 | if (total) |
33 | percent = 100.0 * get_field(he) / total; | 33 | percent = 100.0 * get_field(he) / total; |
34 | 34 | ||
35 | ret = hpp__call_print_fn(hpp, print_fn, fmt, percent); | 35 | ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent); |
36 | } else | 36 | } else |
37 | ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); | 37 | ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he)); |
38 | 38 | ||
39 | if (perf_evsel__is_group_event(evsel)) { | 39 | if (perf_evsel__is_group_event(evsel)) { |
40 | int prev_idx, idx_delta; | 40 | int prev_idx, idx_delta; |
@@ -60,19 +60,19 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
60 | */ | 60 | */ |
61 | if (fmt_percent) { | 61 | if (fmt_percent) { |
62 | ret += hpp__call_print_fn(hpp, print_fn, | 62 | ret += hpp__call_print_fn(hpp, print_fn, |
63 | fmt, 0.0); | 63 | fmt, len, 0.0); |
64 | } else { | 64 | } else { |
65 | ret += hpp__call_print_fn(hpp, print_fn, | 65 | ret += hpp__call_print_fn(hpp, print_fn, |
66 | fmt, 0ULL); | 66 | fmt, len, 0ULL); |
67 | } | 67 | } |
68 | } | 68 | } |
69 | 69 | ||
70 | if (fmt_percent) { | 70 | if (fmt_percent) { |
71 | ret += hpp__call_print_fn(hpp, print_fn, fmt, | 71 | ret += hpp__call_print_fn(hpp, print_fn, fmt, len, |
72 | 100.0 * period / total); | 72 | 100.0 * period / total); |
73 | } else { | 73 | } else { |
74 | ret += hpp__call_print_fn(hpp, print_fn, fmt, | 74 | ret += hpp__call_print_fn(hpp, print_fn, fmt, |
75 | period); | 75 | len, period); |
76 | } | 76 | } |
77 | 77 | ||
78 | prev_idx = perf_evsel__group_idx(evsel); | 78 | prev_idx = perf_evsel__group_idx(evsel); |
@@ -86,10 +86,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
86 | */ | 86 | */ |
87 | if (fmt_percent) { | 87 | if (fmt_percent) { |
88 | ret += hpp__call_print_fn(hpp, print_fn, | 88 | ret += hpp__call_print_fn(hpp, print_fn, |
89 | fmt, 0.0); | 89 | fmt, len, 0.0); |
90 | } else { | 90 | } else { |
91 | ret += hpp__call_print_fn(hpp, print_fn, | 91 | ret += hpp__call_print_fn(hpp, print_fn, |
92 | fmt, 0ULL); | 92 | fmt, len, 0ULL); |
93 | } | 93 | } |
94 | } | 94 | } |
95 | } | 95 | } |
@@ -104,16 +104,35 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
104 | return ret; | 104 | return ret; |
105 | } | 105 | } |
106 | 106 | ||
107 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 107 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
108 | hpp_field_fn get_field, const char *fmt, | 108 | struct hist_entry *he, hpp_field_fn get_field, |
109 | hpp_snprint_fn print_fn, bool fmt_percent) | 109 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) |
110 | { | ||
111 | int len = fmt->user_len ?: fmt->len; | ||
112 | |||
113 | if (symbol_conf.field_sep) { | ||
114 | return __hpp__fmt(hpp, he, get_field, fmtstr, 1, | ||
115 | print_fn, fmt_percent); | ||
116 | } | ||
117 | |||
118 | if (fmt_percent) | ||
119 | len -= 2; /* 2 for a space and a % sign */ | ||
120 | else | ||
121 | len -= 1; | ||
122 | |||
123 | return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent); | ||
124 | } | ||
125 | |||
126 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
127 | struct hist_entry *he, hpp_field_fn get_field, | ||
128 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) | ||
110 | { | 129 | { |
111 | if (!symbol_conf.cumulate_callchain) { | 130 | if (!symbol_conf.cumulate_callchain) { |
112 | return snprintf(hpp->buf, hpp->size, "%*s", | 131 | int len = fmt->user_len ?: fmt->len; |
113 | fmt_percent ? 8 : 12, "N/A"); | 132 | return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A"); |
114 | } | 133 | } |
115 | 134 | ||
116 | return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent); | 135 | return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent); |
117 | } | 136 | } |
118 | 137 | ||
119 | static int field_cmp(u64 field_a, u64 field_b) | 138 | static int field_cmp(u64 field_a, u64 field_b) |
@@ -143,8 +162,8 @@ static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, | |||
143 | return ret; | 162 | return ret; |
144 | 163 | ||
145 | nr_members = evsel->nr_members; | 164 | nr_members = evsel->nr_members; |
146 | fields_a = calloc(sizeof(*fields_a), nr_members); | 165 | fields_a = calloc(nr_members, sizeof(*fields_a)); |
147 | fields_b = calloc(sizeof(*fields_b), nr_members); | 166 | fields_b = calloc(nr_members, sizeof(*fields_b)); |
148 | 167 | ||
149 | if (!fields_a || !fields_b) | 168 | if (!fields_a || !fields_b) |
150 | goto out; | 169 | goto out; |
@@ -185,35 +204,34 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, | |||
185 | if (ret) | 204 | if (ret) |
186 | return ret; | 205 | return ret; |
187 | 206 | ||
207 | if (a->thread != b->thread || !symbol_conf.use_callchain) | ||
208 | return 0; | ||
209 | |||
188 | ret = b->callchain->max_depth - a->callchain->max_depth; | 210 | ret = b->callchain->max_depth - a->callchain->max_depth; |
189 | } | 211 | } |
190 | return ret; | 212 | return ret; |
191 | } | 213 | } |
192 | 214 | ||
193 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 215 | static int hpp__width_fn(struct perf_hpp_fmt *fmt, |
194 | static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 216 | struct perf_hpp *hpp __maybe_unused, |
195 | struct perf_hpp *hpp, \ | 217 | struct perf_evsel *evsel) |
196 | struct perf_evsel *evsel) \ | 218 | { |
197 | { \ | 219 | int len = fmt->user_len ?: fmt->len; |
198 | int len = _min_width; \ | 220 | |
199 | \ | 221 | if (symbol_conf.event_group) |
200 | if (symbol_conf.event_group) \ | 222 | len = max(len, evsel->nr_members * fmt->len); |
201 | len = max(len, evsel->nr_members * _unit_width); \ | 223 | |
202 | \ | 224 | if (len < (int)strlen(fmt->name)) |
203 | return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ | 225 | len = strlen(fmt->name); |
204 | } | 226 | |
205 | 227 | return len; | |
206 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | 228 | } |
207 | static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 229 | |
208 | struct perf_hpp *hpp __maybe_unused, \ | 230 | static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
209 | struct perf_evsel *evsel) \ | 231 | struct perf_evsel *evsel) |
210 | { \ | 232 | { |
211 | int len = _min_width; \ | 233 | int len = hpp__width_fn(fmt, hpp, evsel); |
212 | \ | 234 | return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); |
213 | if (symbol_conf.event_group) \ | ||
214 | len = max(len, evsel->nr_members * _unit_width); \ | ||
215 | \ | ||
216 | return len; \ | ||
217 | } | 235 | } |
218 | 236 | ||
219 | static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) | 237 | static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) |
@@ -221,11 +239,12 @@ static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
221 | va_list args; | 239 | va_list args; |
222 | ssize_t ssize = hpp->size; | 240 | ssize_t ssize = hpp->size; |
223 | double percent; | 241 | double percent; |
224 | int ret; | 242 | int ret, len; |
225 | 243 | ||
226 | va_start(args, fmt); | 244 | va_start(args, fmt); |
245 | len = va_arg(args, int); | ||
227 | percent = va_arg(args, double); | 246 | percent = va_arg(args, double); |
228 | ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent); | 247 | ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent); |
229 | va_end(args); | 248 | va_end(args); |
230 | 249 | ||
231 | return (ret >= ssize) ? (ssize - 1) : ret; | 250 | return (ret >= ssize) ? (ssize - 1) : ret; |
@@ -250,20 +269,19 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
250 | return he->stat._field; \ | 269 | return he->stat._field; \ |
251 | } \ | 270 | } \ |
252 | \ | 271 | \ |
253 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 272 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ |
254 | struct perf_hpp *hpp, struct hist_entry *he) \ | 273 | struct perf_hpp *hpp, struct hist_entry *he) \ |
255 | { \ | 274 | { \ |
256 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | 275 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
257 | hpp_color_scnprintf, true); \ | 276 | hpp_color_scnprintf, true); \ |
258 | } | 277 | } |
259 | 278 | ||
260 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ | 279 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
261 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 280 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
262 | struct perf_hpp *hpp, struct hist_entry *he) \ | 281 | struct perf_hpp *hpp, struct hist_entry *he) \ |
263 | { \ | 282 | { \ |
264 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 283 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
265 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ | 284 | hpp_entry_scnprintf, true); \ |
266 | hpp_entry_scnprintf, true); \ | ||
267 | } | 285 | } |
268 | 286 | ||
269 | #define __HPP_SORT_FN(_type, _field) \ | 287 | #define __HPP_SORT_FN(_type, _field) \ |
@@ -278,20 +296,19 @@ static u64 he_get_acc_##_field(struct hist_entry *he) \ | |||
278 | return he->stat_acc->_field; \ | 296 | return he->stat_acc->_field; \ |
279 | } \ | 297 | } \ |
280 | \ | 298 | \ |
281 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 299 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ |
282 | struct perf_hpp *hpp, struct hist_entry *he) \ | 300 | struct perf_hpp *hpp, struct hist_entry *he) \ |
283 | { \ | 301 | { \ |
284 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ | 302 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
285 | hpp_color_scnprintf, true); \ | 303 | hpp_color_scnprintf, true); \ |
286 | } | 304 | } |
287 | 305 | ||
288 | #define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ | 306 | #define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ |
289 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 307 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
290 | struct perf_hpp *hpp, struct hist_entry *he) \ | 308 | struct perf_hpp *hpp, struct hist_entry *he) \ |
291 | { \ | 309 | { \ |
292 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 310 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
293 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \ | 311 | hpp_entry_scnprintf, true); \ |
294 | hpp_entry_scnprintf, true); \ | ||
295 | } | 312 | } |
296 | 313 | ||
297 | #define __HPP_SORT_ACC_FN(_type, _field) \ | 314 | #define __HPP_SORT_ACC_FN(_type, _field) \ |
@@ -306,12 +323,11 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \ | |||
306 | return he->stat._field; \ | 323 | return he->stat._field; \ |
307 | } \ | 324 | } \ |
308 | \ | 325 | \ |
309 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 326 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
310 | struct perf_hpp *hpp, struct hist_entry *he) \ | 327 | struct perf_hpp *hpp, struct hist_entry *he) \ |
311 | { \ | 328 | { \ |
312 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | 329 | return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \ |
313 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \ | 330 | hpp_entry_scnprintf, false); \ |
314 | hpp_entry_scnprintf, false); \ | ||
315 | } | 331 | } |
316 | 332 | ||
317 | #define __HPP_SORT_RAW_FN(_type, _field) \ | 333 | #define __HPP_SORT_RAW_FN(_type, _field) \ |
@@ -321,37 +337,29 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | |||
321 | } | 337 | } |
322 | 338 | ||
323 | 339 | ||
324 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ | 340 | #define HPP_PERCENT_FNS(_type, _field) \ |
325 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
326 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
327 | __HPP_COLOR_PERCENT_FN(_type, _field) \ | 341 | __HPP_COLOR_PERCENT_FN(_type, _field) \ |
328 | __HPP_ENTRY_PERCENT_FN(_type, _field) \ | 342 | __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
329 | __HPP_SORT_FN(_type, _field) | 343 | __HPP_SORT_FN(_type, _field) |
330 | 344 | ||
331 | #define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\ | 345 | #define HPP_PERCENT_ACC_FNS(_type, _field) \ |
332 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
333 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
334 | __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 346 | __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
335 | __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ | 347 | __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ |
336 | __HPP_SORT_ACC_FN(_type, _field) | 348 | __HPP_SORT_ACC_FN(_type, _field) |
337 | 349 | ||
338 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ | 350 | #define HPP_RAW_FNS(_type, _field) \ |
339 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
340 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
341 | __HPP_ENTRY_RAW_FN(_type, _field) \ | 351 | __HPP_ENTRY_RAW_FN(_type, _field) \ |
342 | __HPP_SORT_RAW_FN(_type, _field) | 352 | __HPP_SORT_RAW_FN(_type, _field) |
343 | 353 | ||
344 | __HPP_HEADER_FN(overhead_self, "Self", 8, 8) | 354 | HPP_PERCENT_FNS(overhead, period) |
345 | 355 | HPP_PERCENT_FNS(overhead_sys, period_sys) | |
346 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | 356 | HPP_PERCENT_FNS(overhead_us, period_us) |
347 | HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) | 357 | HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys) |
348 | HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) | 358 | HPP_PERCENT_FNS(overhead_guest_us, period_guest_us) |
349 | HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) | 359 | HPP_PERCENT_ACC_FNS(overhead_acc, period) |
350 | HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | ||
351 | HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8) | ||
352 | 360 | ||
353 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | 361 | HPP_RAW_FNS(samples, nr_events) |
354 | HPP_RAW_FNS(period, "Period", period, 12, 12) | 362 | HPP_RAW_FNS(period, period) |
355 | 363 | ||
356 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | 364 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, |
357 | struct hist_entry *b __maybe_unused) | 365 | struct hist_entry *b __maybe_unused) |
@@ -359,47 +367,50 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | |||
359 | return 0; | 367 | return 0; |
360 | } | 368 | } |
361 | 369 | ||
362 | #define HPP__COLOR_PRINT_FNS(_name) \ | 370 | #define HPP__COLOR_PRINT_FNS(_name, _fn) \ |
363 | { \ | 371 | { \ |
364 | .header = hpp__header_ ## _name, \ | 372 | .name = _name, \ |
365 | .width = hpp__width_ ## _name, \ | 373 | .header = hpp__header_fn, \ |
366 | .color = hpp__color_ ## _name, \ | 374 | .width = hpp__width_fn, \ |
367 | .entry = hpp__entry_ ## _name, \ | 375 | .color = hpp__color_ ## _fn, \ |
376 | .entry = hpp__entry_ ## _fn, \ | ||
368 | .cmp = hpp__nop_cmp, \ | 377 | .cmp = hpp__nop_cmp, \ |
369 | .collapse = hpp__nop_cmp, \ | 378 | .collapse = hpp__nop_cmp, \ |
370 | .sort = hpp__sort_ ## _name, \ | 379 | .sort = hpp__sort_ ## _fn, \ |
371 | } | 380 | } |
372 | 381 | ||
373 | #define HPP__COLOR_ACC_PRINT_FNS(_name) \ | 382 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \ |
374 | { \ | 383 | { \ |
375 | .header = hpp__header_ ## _name, \ | 384 | .name = _name, \ |
376 | .width = hpp__width_ ## _name, \ | 385 | .header = hpp__header_fn, \ |
377 | .color = hpp__color_ ## _name, \ | 386 | .width = hpp__width_fn, \ |
378 | .entry = hpp__entry_ ## _name, \ | 387 | .color = hpp__color_ ## _fn, \ |
388 | .entry = hpp__entry_ ## _fn, \ | ||
379 | .cmp = hpp__nop_cmp, \ | 389 | .cmp = hpp__nop_cmp, \ |
380 | .collapse = hpp__nop_cmp, \ | 390 | .collapse = hpp__nop_cmp, \ |
381 | .sort = hpp__sort_ ## _name, \ | 391 | .sort = hpp__sort_ ## _fn, \ |
382 | } | 392 | } |
383 | 393 | ||
384 | #define HPP__PRINT_FNS(_name) \ | 394 | #define HPP__PRINT_FNS(_name, _fn) \ |
385 | { \ | 395 | { \ |
386 | .header = hpp__header_ ## _name, \ | 396 | .name = _name, \ |
387 | .width = hpp__width_ ## _name, \ | 397 | .header = hpp__header_fn, \ |
388 | .entry = hpp__entry_ ## _name, \ | 398 | .width = hpp__width_fn, \ |
399 | .entry = hpp__entry_ ## _fn, \ | ||
389 | .cmp = hpp__nop_cmp, \ | 400 | .cmp = hpp__nop_cmp, \ |
390 | .collapse = hpp__nop_cmp, \ | 401 | .collapse = hpp__nop_cmp, \ |
391 | .sort = hpp__sort_ ## _name, \ | 402 | .sort = hpp__sort_ ## _fn, \ |
392 | } | 403 | } |
393 | 404 | ||
394 | struct perf_hpp_fmt perf_hpp__format[] = { | 405 | struct perf_hpp_fmt perf_hpp__format[] = { |
395 | HPP__COLOR_PRINT_FNS(overhead), | 406 | HPP__COLOR_PRINT_FNS("Overhead", overhead), |
396 | HPP__COLOR_PRINT_FNS(overhead_sys), | 407 | HPP__COLOR_PRINT_FNS("sys", overhead_sys), |
397 | HPP__COLOR_PRINT_FNS(overhead_us), | 408 | HPP__COLOR_PRINT_FNS("usr", overhead_us), |
398 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), | 409 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys), |
399 | HPP__COLOR_PRINT_FNS(overhead_guest_us), | 410 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us), |
400 | HPP__COLOR_ACC_PRINT_FNS(overhead_acc), | 411 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc), |
401 | HPP__PRINT_FNS(samples), | 412 | HPP__PRINT_FNS("Samples", samples), |
402 | HPP__PRINT_FNS(period) | 413 | HPP__PRINT_FNS("Period", period) |
403 | }; | 414 | }; |
404 | 415 | ||
405 | LIST_HEAD(perf_hpp__list); | 416 | LIST_HEAD(perf_hpp__list); |
@@ -444,14 +455,12 @@ void perf_hpp__init(void) | |||
444 | /* | 455 | /* |
445 | * If user specified field order, no need to setup default fields. | 456 | * If user specified field order, no need to setup default fields. |
446 | */ | 457 | */ |
447 | if (field_order) | 458 | if (is_strict_order(field_order)) |
448 | return; | 459 | return; |
449 | 460 | ||
450 | if (symbol_conf.cumulate_callchain) { | 461 | if (symbol_conf.cumulate_callchain) { |
451 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); | 462 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); |
452 | 463 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self"; | |
453 | perf_hpp__format[PERF_HPP__OVERHEAD].header = | ||
454 | hpp__header_overhead_self; | ||
455 | } | 464 | } |
456 | 465 | ||
457 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | 466 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); |
@@ -513,11 +522,11 @@ void perf_hpp__column_disable(unsigned col) | |||
513 | 522 | ||
514 | void perf_hpp__cancel_cumulate(void) | 523 | void perf_hpp__cancel_cumulate(void) |
515 | { | 524 | { |
516 | if (field_order) | 525 | if (is_strict_order(field_order)) |
517 | return; | 526 | return; |
518 | 527 | ||
519 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); | 528 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); |
520 | perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead; | 529 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead"; |
521 | } | 530 | } |
522 | 531 | ||
523 | void perf_hpp__setup_output_field(void) | 532 | void perf_hpp__setup_output_field(void) |
@@ -622,3 +631,59 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
622 | 631 | ||
623 | return ret; | 632 | return ret; |
624 | } | 633 | } |
634 | |||
635 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
636 | { | ||
637 | int idx; | ||
638 | |||
639 | if (perf_hpp__is_sort_entry(fmt)) | ||
640 | return perf_hpp__reset_sort_width(fmt, hists); | ||
641 | |||
642 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
643 | if (fmt == &perf_hpp__format[idx]) | ||
644 | break; | ||
645 | } | ||
646 | |||
647 | if (idx == PERF_HPP__MAX_INDEX) | ||
648 | return; | ||
649 | |||
650 | switch (idx) { | ||
651 | case PERF_HPP__OVERHEAD: | ||
652 | case PERF_HPP__OVERHEAD_SYS: | ||
653 | case PERF_HPP__OVERHEAD_US: | ||
654 | case PERF_HPP__OVERHEAD_ACC: | ||
655 | fmt->len = 8; | ||
656 | break; | ||
657 | |||
658 | case PERF_HPP__OVERHEAD_GUEST_SYS: | ||
659 | case PERF_HPP__OVERHEAD_GUEST_US: | ||
660 | fmt->len = 9; | ||
661 | break; | ||
662 | |||
663 | case PERF_HPP__SAMPLES: | ||
664 | case PERF_HPP__PERIOD: | ||
665 | fmt->len = 12; | ||
666 | break; | ||
667 | |||
668 | default: | ||
669 | break; | ||
670 | } | ||
671 | } | ||
672 | |||
673 | void perf_hpp__set_user_width(const char *width_list_str) | ||
674 | { | ||
675 | struct perf_hpp_fmt *fmt; | ||
676 | const char *ptr = width_list_str; | ||
677 | |||
678 | perf_hpp__for_each_format(fmt) { | ||
679 | char *p; | ||
680 | |||
681 | int len = strtol(ptr, &p, 10); | ||
682 | fmt->user_len = len; | ||
683 | |||
684 | if (*p == ',') | ||
685 | ptr = p + 1; | ||
686 | else | ||
687 | break; | ||
688 | } | ||
689 | } | ||
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 40af0acb4fe9..dfcbc90146ef 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -41,6 +41,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
41 | { | 41 | { |
42 | int i; | 42 | int i; |
43 | size_t ret = 0; | 43 | size_t ret = 0; |
44 | char bf[1024]; | ||
44 | 45 | ||
45 | ret += callchain__fprintf_left_margin(fp, left_margin); | 46 | ret += callchain__fprintf_left_margin(fp, left_margin); |
46 | for (i = 0; i < depth; i++) { | 47 | for (i = 0; i < depth; i++) { |
@@ -56,11 +57,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
56 | } else | 57 | } else |
57 | ret += fprintf(fp, "%s", " "); | 58 | ret += fprintf(fp, "%s", " "); |
58 | } | 59 | } |
59 | if (chain->ms.sym) | 60 | fputs(callchain_list__sym_name(chain, bf, sizeof(bf), false), fp); |
60 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); | 61 | fputc('\n', fp); |
61 | else | ||
62 | ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip); | ||
63 | |||
64 | return ret; | 62 | return ret; |
65 | } | 63 | } |
66 | 64 | ||
@@ -168,6 +166,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
168 | struct rb_node *node; | 166 | struct rb_node *node; |
169 | int i = 0; | 167 | int i = 0; |
170 | int ret = 0; | 168 | int ret = 0; |
169 | char bf[1024]; | ||
171 | 170 | ||
172 | /* | 171 | /* |
173 | * If have one single callchain root, don't bother printing | 172 | * If have one single callchain root, don't bother printing |
@@ -196,10 +195,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
196 | } else | 195 | } else |
197 | ret += callchain__fprintf_left_margin(fp, left_margin); | 196 | ret += callchain__fprintf_left_margin(fp, left_margin); |
198 | 197 | ||
199 | if (chain->ms.sym) | 198 | ret += fprintf(fp, "%s\n", callchain_list__sym_name(chain, bf, sizeof(bf), |
200 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 199 | false)); |
201 | else | ||
202 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
203 | 200 | ||
204 | if (++entries_printed == callchain_param.print_limit) | 201 | if (++entries_printed == callchain_param.print_limit) |
205 | break; | 202 | break; |
@@ -219,6 +216,7 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, | |||
219 | { | 216 | { |
220 | struct callchain_list *chain; | 217 | struct callchain_list *chain; |
221 | size_t ret = 0; | 218 | size_t ret = 0; |
219 | char bf[1024]; | ||
222 | 220 | ||
223 | if (!node) | 221 | if (!node) |
224 | return 0; | 222 | return 0; |
@@ -229,11 +227,8 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, | |||
229 | list_for_each_entry(chain, &node->val, list) { | 227 | list_for_each_entry(chain, &node->val, list) { |
230 | if (chain->ip >= PERF_CONTEXT_MAX) | 228 | if (chain->ip >= PERF_CONTEXT_MAX) |
231 | continue; | 229 | continue; |
232 | if (chain->ms.sym) | 230 | ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain, |
233 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 231 | bf, sizeof(bf), false)); |
234 | else | ||
235 | ret += fprintf(fp, " %p\n", | ||
236 | (void *)(long)chain->ip); | ||
237 | } | 232 | } |
238 | 233 | ||
239 | return ret; | 234 | return ret; |
@@ -395,10 +390,12 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
395 | 390 | ||
396 | init_rem_hits(); | 391 | init_rem_hits(); |
397 | 392 | ||
398 | |||
399 | perf_hpp__for_each_format(fmt) | 393 | perf_hpp__for_each_format(fmt) |
400 | perf_hpp__reset_width(fmt, hists); | 394 | perf_hpp__reset_width(fmt, hists); |
401 | 395 | ||
396 | if (symbol_conf.col_width_list_str) | ||
397 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | ||
398 | |||
402 | if (!show_header) | 399 | if (!show_header) |
403 | goto print_entries; | 400 | goto print_entries; |
404 | 401 | ||
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 2f612562978c..3c38f25b1695 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include <signal.h> | 1 | #include <signal.h> |
2 | #include <stdbool.h> | 2 | #include <stdbool.h> |
3 | #ifdef HAVE_BACKTRACE_SUPPORT | ||
4 | #include <execinfo.h> | ||
5 | #endif | ||
3 | 6 | ||
4 | #include "../../util/cache.h" | 7 | #include "../../util/cache.h" |
5 | #include "../../util/debug.h" | 8 | #include "../../util/debug.h" |
@@ -88,6 +91,25 @@ int ui__getch(int delay_secs) | |||
88 | return SLkp_getkey(); | 91 | return SLkp_getkey(); |
89 | } | 92 | } |
90 | 93 | ||
94 | #ifdef HAVE_BACKTRACE_SUPPORT | ||
95 | static void ui__signal_backtrace(int sig) | ||
96 | { | ||
97 | void *stackdump[32]; | ||
98 | size_t size; | ||
99 | |||
100 | ui__exit(false); | ||
101 | psignal(sig, "perf"); | ||
102 | |||
103 | printf("-------- backtrace --------\n"); | ||
104 | size = backtrace(stackdump, ARRAY_SIZE(stackdump)); | ||
105 | backtrace_symbols_fd(stackdump, size, STDOUT_FILENO); | ||
106 | |||
107 | exit(0); | ||
108 | } | ||
109 | #else | ||
110 | # define ui__signal_backtrace ui__signal | ||
111 | #endif | ||
112 | |||
91 | static void ui__signal(int sig) | 113 | static void ui__signal(int sig) |
92 | { | 114 | { |
93 | ui__exit(false); | 115 | ui__exit(false); |
@@ -122,8 +144,8 @@ int ui__init(void) | |||
122 | ui_browser__init(); | 144 | ui_browser__init(); |
123 | tui_progress__init(); | 145 | tui_progress__init(); |
124 | 146 | ||
125 | signal(SIGSEGV, ui__signal); | 147 | signal(SIGSEGV, ui__signal_backtrace); |
126 | signal(SIGFPE, ui__signal); | 148 | signal(SIGFPE, ui__signal_backtrace); |
127 | signal(SIGINT, ui__signal); | 149 | signal(SIGINT, ui__signal); |
128 | signal(SIGQUIT, ui__signal); | 150 | signal(SIGQUIT, ui__signal); |
129 | signal(SIGTERM, ui__signal); | 151 | signal(SIGTERM, ui__signal); |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 809b4c50beae..79999ceaf2be 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -17,11 +17,13 @@ | |||
17 | #include "debug.h" | 17 | #include "debug.h" |
18 | #include "annotate.h" | 18 | #include "annotate.h" |
19 | #include "evsel.h" | 19 | #include "evsel.h" |
20 | #include <regex.h> | ||
20 | #include <pthread.h> | 21 | #include <pthread.h> |
21 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
22 | 23 | ||
23 | const char *disassembler_style; | 24 | const char *disassembler_style; |
24 | const char *objdump_path; | 25 | const char *objdump_path; |
26 | static regex_t file_lineno; | ||
25 | 27 | ||
26 | static struct ins *ins__find(const char *name); | 28 | static struct ins *ins__find(const char *name); |
27 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 29 | static int disasm_line__parse(char *line, char **namep, char **rawp); |
@@ -232,9 +234,16 @@ static int mov__parse(struct ins_operands *ops) | |||
232 | return -1; | 234 | return -1; |
233 | 235 | ||
234 | target = ++s; | 236 | target = ++s; |
237 | comment = strchr(s, '#'); | ||
235 | 238 | ||
236 | while (s[0] != '\0' && !isspace(s[0])) | 239 | if (comment != NULL) |
237 | ++s; | 240 | s = comment - 1; |
241 | else | ||
242 | s = strchr(s, '\0') - 1; | ||
243 | |||
244 | while (s > target && isspace(s[0])) | ||
245 | --s; | ||
246 | s++; | ||
238 | prev = *s; | 247 | prev = *s; |
239 | *s = '\0'; | 248 | *s = '\0'; |
240 | 249 | ||
@@ -244,7 +253,6 @@ static int mov__parse(struct ins_operands *ops) | |||
244 | if (ops->target.raw == NULL) | 253 | if (ops->target.raw == NULL) |
245 | goto out_free_source; | 254 | goto out_free_source; |
246 | 255 | ||
247 | comment = strchr(s, '#'); | ||
248 | if (comment == NULL) | 256 | if (comment == NULL) |
249 | return 0; | 257 | return 0; |
250 | 258 | ||
@@ -472,7 +480,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
472 | 480 | ||
473 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 481 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
474 | 482 | ||
475 | if (addr < sym->start || addr > sym->end) | 483 | if (addr < sym->start || addr >= sym->end) |
476 | return -ERANGE; | 484 | return -ERANGE; |
477 | 485 | ||
478 | offset = addr - sym->start; | 486 | offset = addr - sym->start; |
@@ -564,13 +572,15 @@ out_free_name: | |||
564 | return -1; | 572 | return -1; |
565 | } | 573 | } |
566 | 574 | ||
567 | static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) | 575 | static struct disasm_line *disasm_line__new(s64 offset, char *line, |
576 | size_t privsize, int line_nr) | ||
568 | { | 577 | { |
569 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 578 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
570 | 579 | ||
571 | if (dl != NULL) { | 580 | if (dl != NULL) { |
572 | dl->offset = offset; | 581 | dl->offset = offset; |
573 | dl->line = strdup(line); | 582 | dl->line = strdup(line); |
583 | dl->line_nr = line_nr; | ||
574 | if (dl->line == NULL) | 584 | if (dl->line == NULL) |
575 | goto out_delete; | 585 | goto out_delete; |
576 | 586 | ||
@@ -782,13 +792,15 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
782 | * The ops.raw part will be parsed further according to type of the instruction. | 792 | * The ops.raw part will be parsed further according to type of the instruction. |
783 | */ | 793 | */ |
784 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 794 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
785 | FILE *file, size_t privsize) | 795 | FILE *file, size_t privsize, |
796 | int *line_nr) | ||
786 | { | 797 | { |
787 | struct annotation *notes = symbol__annotation(sym); | 798 | struct annotation *notes = symbol__annotation(sym); |
788 | struct disasm_line *dl; | 799 | struct disasm_line *dl; |
789 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; | 800 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; |
790 | size_t line_len; | 801 | size_t line_len; |
791 | s64 line_ip, offset = -1; | 802 | s64 line_ip, offset = -1; |
803 | regmatch_t match[2]; | ||
792 | 804 | ||
793 | if (getline(&line, &line_len, file) < 0) | 805 | if (getline(&line, &line_len, file) < 0) |
794 | return -1; | 806 | return -1; |
@@ -806,6 +818,12 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
806 | line_ip = -1; | 818 | line_ip = -1; |
807 | parsed_line = line; | 819 | parsed_line = line; |
808 | 820 | ||
821 | /* /filename:linenr ? Save line number and ignore. */ | ||
822 | if (regexec(&file_lineno, line, 2, match, 0) == 0) { | ||
823 | *line_nr = atoi(line + match[1].rm_so); | ||
824 | return 0; | ||
825 | } | ||
826 | |||
809 | /* | 827 | /* |
810 | * Strip leading spaces: | 828 | * Strip leading spaces: |
811 | */ | 829 | */ |
@@ -830,14 +848,15 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
830 | end = map__rip_2objdump(map, sym->end); | 848 | end = map__rip_2objdump(map, sym->end); |
831 | 849 | ||
832 | offset = line_ip - start; | 850 | offset = line_ip - start; |
833 | if ((u64)line_ip < start || (u64)line_ip > end) | 851 | if ((u64)line_ip < start || (u64)line_ip >= end) |
834 | offset = -1; | 852 | offset = -1; |
835 | else | 853 | else |
836 | parsed_line = tmp2 + 1; | 854 | parsed_line = tmp2 + 1; |
837 | } | 855 | } |
838 | 856 | ||
839 | dl = disasm_line__new(offset, parsed_line, privsize); | 857 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr); |
840 | free(line); | 858 | free(line); |
859 | (*line_nr)++; | ||
841 | 860 | ||
842 | if (dl == NULL) | 861 | if (dl == NULL) |
843 | return -1; | 862 | return -1; |
@@ -863,6 +882,11 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
863 | return 0; | 882 | return 0; |
864 | } | 883 | } |
865 | 884 | ||
885 | static __attribute__((constructor)) void symbol__init_regexpr(void) | ||
886 | { | ||
887 | regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED); | ||
888 | } | ||
889 | |||
866 | static void delete_last_nop(struct symbol *sym) | 890 | static void delete_last_nop(struct symbol *sym) |
867 | { | 891 | { |
868 | struct annotation *notes = symbol__annotation(sym); | 892 | struct annotation *notes = symbol__annotation(sym); |
@@ -898,11 +922,10 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
898 | char symfs_filename[PATH_MAX]; | 922 | char symfs_filename[PATH_MAX]; |
899 | struct kcore_extract kce; | 923 | struct kcore_extract kce; |
900 | bool delete_extract = false; | 924 | bool delete_extract = false; |
925 | int lineno = 0; | ||
901 | 926 | ||
902 | if (filename) { | 927 | if (filename) |
903 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 928 | symbol__join_symfs(symfs_filename, filename); |
904 | symbol_conf.symfs, filename); | ||
905 | } | ||
906 | 929 | ||
907 | if (filename == NULL) { | 930 | if (filename == NULL) { |
908 | if (dso->has_build_id) { | 931 | if (dso->has_build_id) { |
@@ -911,6 +934,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
911 | return -ENOMEM; | 934 | return -ENOMEM; |
912 | } | 935 | } |
913 | goto fallback; | 936 | goto fallback; |
937 | } else if (dso__is_kcore(dso)) { | ||
938 | goto fallback; | ||
914 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | 939 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || |
915 | strstr(command, "[kernel.kallsyms]") || | 940 | strstr(command, "[kernel.kallsyms]") || |
916 | access(symfs_filename, R_OK)) { | 941 | access(symfs_filename, R_OK)) { |
@@ -922,8 +947,7 @@ fallback: | |||
922 | * DSO is the same as when 'perf record' ran. | 947 | * DSO is the same as when 'perf record' ran. |
923 | */ | 948 | */ |
924 | filename = (char *)dso->long_name; | 949 | filename = (char *)dso->long_name; |
925 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 950 | symbol__join_symfs(symfs_filename, filename); |
926 | symbol_conf.symfs, filename); | ||
927 | free_filename = false; | 951 | free_filename = false; |
928 | } | 952 | } |
929 | 953 | ||
@@ -963,7 +987,7 @@ fallback: | |||
963 | kce.kcore_filename = symfs_filename; | 987 | kce.kcore_filename = symfs_filename; |
964 | kce.addr = map__rip_2objdump(map, sym->start); | 988 | kce.addr = map__rip_2objdump(map, sym->start); |
965 | kce.offs = sym->start; | 989 | kce.offs = sym->start; |
966 | kce.len = sym->end + 1 - sym->start; | 990 | kce.len = sym->end - sym->start; |
967 | if (!kcore_extract__create(&kce)) { | 991 | if (!kcore_extract__create(&kce)) { |
968 | delete_extract = true; | 992 | delete_extract = true; |
969 | strlcpy(symfs_filename, kce.extract_filename, | 993 | strlcpy(symfs_filename, kce.extract_filename, |
@@ -979,12 +1003,12 @@ fallback: | |||
979 | snprintf(command, sizeof(command), | 1003 | snprintf(command, sizeof(command), |
980 | "%s %s%s --start-address=0x%016" PRIx64 | 1004 | "%s %s%s --start-address=0x%016" PRIx64 |
981 | " --stop-address=0x%016" PRIx64 | 1005 | " --stop-address=0x%016" PRIx64 |
982 | " -d %s %s -C %s 2>/dev/null|grep -v %s|expand", | 1006 | " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand", |
983 | objdump_path ? objdump_path : "objdump", | 1007 | objdump_path ? objdump_path : "objdump", |
984 | disassembler_style ? "-M " : "", | 1008 | disassembler_style ? "-M " : "", |
985 | disassembler_style ? disassembler_style : "", | 1009 | disassembler_style ? disassembler_style : "", |
986 | map__rip_2objdump(map, sym->start), | 1010 | map__rip_2objdump(map, sym->start), |
987 | map__rip_2objdump(map, sym->end+1), | 1011 | map__rip_2objdump(map, sym->end), |
988 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | 1012 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", |
989 | symbol_conf.annotate_src ? "-S" : "", | 1013 | symbol_conf.annotate_src ? "-S" : "", |
990 | symfs_filename, filename); | 1014 | symfs_filename, filename); |
@@ -996,7 +1020,8 @@ fallback: | |||
996 | goto out_free_filename; | 1020 | goto out_free_filename; |
997 | 1021 | ||
998 | while (!feof(file)) | 1022 | while (!feof(file)) |
999 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) | 1023 | if (symbol__parse_objdump_line(sym, map, file, privsize, |
1024 | &lineno) < 0) | ||
1000 | break; | 1025 | break; |
1001 | 1026 | ||
1002 | /* | 1027 | /* |
@@ -1167,7 +1192,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
1167 | goto next; | 1192 | goto next; |
1168 | 1193 | ||
1169 | offset = start + i; | 1194 | offset = start + i; |
1170 | src_line->path = get_srcline(map->dso, offset); | 1195 | src_line->path = get_srcline(map->dso, offset, NULL, false); |
1171 | insert_source_line(&tmp_root, src_line); | 1196 | insert_source_line(&tmp_root, src_line); |
1172 | 1197 | ||
1173 | next: | 1198 | next: |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 112d6e268150..0784a9420528 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -58,6 +58,7 @@ struct disasm_line { | |||
58 | char *line; | 58 | char *line; |
59 | char *name; | 59 | char *name; |
60 | struct ins *ins; | 60 | struct ins *ins; |
61 | int line_nr; | ||
61 | struct ins_operands ops; | 62 | struct ins_operands ops; |
62 | }; | 63 | }; |
63 | 64 | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index a904a4cfe7d3..0c72680a977f 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -15,6 +15,11 @@ | |||
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "session.h" | 16 | #include "session.h" |
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | #include "header.h" | ||
19 | #include "vdso.h" | ||
20 | |||
21 | |||
22 | static bool no_buildid_cache; | ||
18 | 23 | ||
19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 24 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 25 | union perf_event *event, |
@@ -33,8 +38,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
33 | return -1; | 38 | return -1; |
34 | } | 39 | } |
35 | 40 | ||
36 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 41 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); |
37 | sample->ip, &al); | ||
38 | 42 | ||
39 | if (al.map != NULL) | 43 | if (al.map != NULL) |
40 | al.map->dso->hit = 1; | 44 | al.map->dso->hit = 1; |
@@ -106,3 +110,340 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) | |||
106 | build_id_hex, build_id_hex + 2); | 110 | build_id_hex, build_id_hex + 2); |
107 | return bf; | 111 | return bf; |
108 | } | 112 | } |
113 | |||
114 | #define dsos__for_each_with_build_id(pos, head) \ | ||
115 | list_for_each_entry(pos, head, node) \ | ||
116 | if (!pos->has_build_id) \ | ||
117 | continue; \ | ||
118 | else | ||
119 | |||
120 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
121 | pid_t pid, u16 misc, int fd) | ||
122 | { | ||
123 | int err; | ||
124 | struct build_id_event b; | ||
125 | size_t len; | ||
126 | |||
127 | len = name_len + 1; | ||
128 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
129 | |||
130 | memset(&b, 0, sizeof(b)); | ||
131 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
132 | b.pid = pid; | ||
133 | b.header.misc = misc; | ||
134 | b.header.size = sizeof(b) + len; | ||
135 | |||
136 | err = writen(fd, &b, sizeof(b)); | ||
137 | if (err < 0) | ||
138 | return err; | ||
139 | |||
140 | return write_padded(fd, name, name_len + 1, len); | ||
141 | } | ||
142 | |||
143 | static int __dsos__write_buildid_table(struct list_head *head, | ||
144 | struct machine *machine, | ||
145 | pid_t pid, u16 misc, int fd) | ||
146 | { | ||
147 | char nm[PATH_MAX]; | ||
148 | struct dso *pos; | ||
149 | |||
150 | dsos__for_each_with_build_id(pos, head) { | ||
151 | int err; | ||
152 | const char *name; | ||
153 | size_t name_len; | ||
154 | |||
155 | if (!pos->hit) | ||
156 | continue; | ||
157 | |||
158 | if (dso__is_vdso(pos)) { | ||
159 | name = pos->short_name; | ||
160 | name_len = pos->short_name_len + 1; | ||
161 | } else if (dso__is_kcore(pos)) { | ||
162 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
163 | name = nm; | ||
164 | name_len = strlen(nm) + 1; | ||
165 | } else { | ||
166 | name = pos->long_name; | ||
167 | name_len = pos->long_name_len + 1; | ||
168 | } | ||
169 | |||
170 | err = write_buildid(name, name_len, pos->build_id, | ||
171 | pid, misc, fd); | ||
172 | if (err) | ||
173 | return err; | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
180 | { | ||
181 | int err; | ||
182 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
183 | umisc = PERF_RECORD_MISC_USER; | ||
184 | |||
185 | if (!machine__is_host(machine)) { | ||
186 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
187 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
188 | } | ||
189 | |||
190 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, | ||
191 | machine->pid, kmisc, fd); | ||
192 | if (err == 0) | ||
193 | err = __dsos__write_buildid_table(&machine->user_dsos.head, | ||
194 | machine, machine->pid, umisc, | ||
195 | fd); | ||
196 | return err; | ||
197 | } | ||
198 | |||
199 | int perf_session__write_buildid_table(struct perf_session *session, int fd) | ||
200 | { | ||
201 | struct rb_node *nd; | ||
202 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
203 | |||
204 | if (err) | ||
205 | return err; | ||
206 | |||
207 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
208 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
209 | err = machine__write_buildid_table(pos, fd); | ||
210 | if (err) | ||
211 | break; | ||
212 | } | ||
213 | return err; | ||
214 | } | ||
215 | |||
216 | static int __dsos__hit_all(struct list_head *head) | ||
217 | { | ||
218 | struct dso *pos; | ||
219 | |||
220 | list_for_each_entry(pos, head, node) | ||
221 | pos->hit = true; | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int machine__hit_all_dsos(struct machine *machine) | ||
227 | { | ||
228 | int err; | ||
229 | |||
230 | err = __dsos__hit_all(&machine->kernel_dsos.head); | ||
231 | if (err) | ||
232 | return err; | ||
233 | |||
234 | return __dsos__hit_all(&machine->user_dsos.head); | ||
235 | } | ||
236 | |||
237 | int dsos__hit_all(struct perf_session *session) | ||
238 | { | ||
239 | struct rb_node *nd; | ||
240 | int err; | ||
241 | |||
242 | err = machine__hit_all_dsos(&session->machines.host); | ||
243 | if (err) | ||
244 | return err; | ||
245 | |||
246 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
247 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
248 | |||
249 | err = machine__hit_all_dsos(pos); | ||
250 | if (err) | ||
251 | return err; | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | void disable_buildid_cache(void) | ||
258 | { | ||
259 | no_buildid_cache = true; | ||
260 | } | ||
261 | |||
262 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
263 | const char *name, bool is_kallsyms, bool is_vdso) | ||
264 | { | ||
265 | const size_t size = PATH_MAX; | ||
266 | char *realname, *filename = zalloc(size), | ||
267 | *linkname = zalloc(size), *targetname; | ||
268 | int len, err = -1; | ||
269 | bool slash = is_kallsyms || is_vdso; | ||
270 | |||
271 | if (is_kallsyms) { | ||
272 | if (symbol_conf.kptr_restrict) { | ||
273 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
274 | err = 0; | ||
275 | goto out_free; | ||
276 | } | ||
277 | realname = (char *) name; | ||
278 | } else | ||
279 | realname = realpath(name, NULL); | ||
280 | |||
281 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
282 | goto out_free; | ||
283 | |||
284 | len = scnprintf(filename, size, "%s%s%s", | ||
285 | debugdir, slash ? "/" : "", | ||
286 | is_vdso ? DSO__NAME_VDSO : realname); | ||
287 | if (mkdir_p(filename, 0755)) | ||
288 | goto out_free; | ||
289 | |||
290 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
291 | |||
292 | if (access(filename, F_OK)) { | ||
293 | if (is_kallsyms) { | ||
294 | if (copyfile("/proc/kallsyms", filename)) | ||
295 | goto out_free; | ||
296 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
297 | goto out_free; | ||
298 | } | ||
299 | |||
300 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
301 | debugdir, sbuild_id); | ||
302 | |||
303 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
304 | goto out_free; | ||
305 | |||
306 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
307 | targetname = filename + strlen(debugdir) - 5; | ||
308 | memcpy(targetname, "../..", 5); | ||
309 | |||
310 | if (symlink(targetname, linkname) == 0) | ||
311 | err = 0; | ||
312 | out_free: | ||
313 | if (!is_kallsyms) | ||
314 | free(realname); | ||
315 | free(filename); | ||
316 | free(linkname); | ||
317 | return err; | ||
318 | } | ||
319 | |||
320 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
321 | const char *name, const char *debugdir, | ||
322 | bool is_kallsyms, bool is_vdso) | ||
323 | { | ||
324 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
325 | |||
326 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
327 | |||
328 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
329 | is_kallsyms, is_vdso); | ||
330 | } | ||
331 | |||
332 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
333 | { | ||
334 | const size_t size = PATH_MAX; | ||
335 | char *filename = zalloc(size), | ||
336 | *linkname = zalloc(size); | ||
337 | int err = -1; | ||
338 | |||
339 | if (filename == NULL || linkname == NULL) | ||
340 | goto out_free; | ||
341 | |||
342 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
343 | debugdir, sbuild_id, sbuild_id + 2); | ||
344 | |||
345 | if (access(linkname, F_OK)) | ||
346 | goto out_free; | ||
347 | |||
348 | if (readlink(linkname, filename, size - 1) < 0) | ||
349 | goto out_free; | ||
350 | |||
351 | if (unlink(linkname)) | ||
352 | goto out_free; | ||
353 | |||
354 | /* | ||
355 | * Since the link is relative, we must make it absolute: | ||
356 | */ | ||
357 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
358 | debugdir, sbuild_id, filename); | ||
359 | |||
360 | if (unlink(linkname)) | ||
361 | goto out_free; | ||
362 | |||
363 | err = 0; | ||
364 | out_free: | ||
365 | free(filename); | ||
366 | free(linkname); | ||
367 | return err; | ||
368 | } | ||
369 | |||
370 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
371 | const char *debugdir) | ||
372 | { | ||
373 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
374 | bool is_vdso = dso__is_vdso(dso); | ||
375 | const char *name = dso->long_name; | ||
376 | char nm[PATH_MAX]; | ||
377 | |||
378 | if (dso__is_kcore(dso)) { | ||
379 | is_kallsyms = true; | ||
380 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
381 | name = nm; | ||
382 | } | ||
383 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
384 | debugdir, is_kallsyms, is_vdso); | ||
385 | } | ||
386 | |||
387 | static int __dsos__cache_build_ids(struct list_head *head, | ||
388 | struct machine *machine, const char *debugdir) | ||
389 | { | ||
390 | struct dso *pos; | ||
391 | int err = 0; | ||
392 | |||
393 | dsos__for_each_with_build_id(pos, head) | ||
394 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
395 | err = -1; | ||
396 | |||
397 | return err; | ||
398 | } | ||
399 | |||
400 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
401 | { | ||
402 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, | ||
403 | debugdir); | ||
404 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, | ||
405 | debugdir); | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | int perf_session__cache_build_ids(struct perf_session *session) | ||
410 | { | ||
411 | struct rb_node *nd; | ||
412 | int ret; | ||
413 | |||
414 | if (no_buildid_cache) | ||
415 | return 0; | ||
416 | |||
417 | if (mkdir(buildid_dir, 0755) != 0 && errno != EEXIST) | ||
418 | return -1; | ||
419 | |||
420 | ret = machine__cache_build_ids(&session->machines.host, buildid_dir); | ||
421 | |||
422 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
423 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
424 | ret |= machine__cache_build_ids(pos, buildid_dir); | ||
425 | } | ||
426 | return ret ? -1 : 0; | ||
427 | } | ||
428 | |||
429 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
430 | { | ||
431 | bool ret; | ||
432 | |||
433 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
434 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
435 | return ret; | ||
436 | } | ||
437 | |||
438 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
439 | { | ||
440 | struct rb_node *nd; | ||
441 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
442 | |||
443 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
444 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
445 | ret |= machine__read_build_ids(pos, with_hits); | ||
446 | } | ||
447 | |||
448 | return ret; | ||
449 | } | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index ae392561470b..8236319514d5 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -15,4 +15,16 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); | |||
15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | 15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, |
16 | struct perf_sample *sample, struct perf_evsel *evsel, | 16 | struct perf_sample *sample, struct perf_evsel *evsel, |
17 | struct machine *machine); | 17 | struct machine *machine); |
18 | |||
19 | int dsos__hit_all(struct perf_session *session); | ||
20 | |||
21 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); | ||
22 | int perf_session__write_buildid_table(struct perf_session *session, int fd); | ||
23 | int perf_session__cache_build_ids(struct perf_session *session); | ||
24 | |||
25 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
26 | const char *name, bool is_kallsyms, bool is_vdso); | ||
27 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
28 | void disable_buildid_cache(void); | ||
29 | |||
18 | #endif | 30 | #endif |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 7b176dd02e1a..5cf9e1b5989d 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -22,6 +22,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *); | |||
22 | extern int perf_default_config(const char *, const char *, void *); | 22 | extern int perf_default_config(const char *, const char *, void *); |
23 | extern int perf_config(config_fn_t fn, void *); | 23 | extern int perf_config(config_fn_t fn, void *); |
24 | extern int perf_config_int(const char *, const char *); | 24 | extern int perf_config_int(const char *, const char *); |
25 | extern u64 perf_config_u64(const char *, const char *); | ||
25 | extern int perf_config_bool(const char *, const char *); | 26 | extern int perf_config_bool(const char *, const char *); |
26 | extern int config_error_nonbool(const char *); | 27 | extern int config_error_nonbool(const char *); |
27 | extern const char *perf_config_dirname(const char *, const char *); | 28 | extern const char *perf_config_dirname(const char *, const char *); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 437ee09727e6..14e7a123d43b 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -25,77 +25,176 @@ | |||
25 | 25 | ||
26 | __thread struct callchain_cursor callchain_cursor; | 26 | __thread struct callchain_cursor callchain_cursor; |
27 | 27 | ||
28 | int | 28 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
29 | parse_callchain_report_opt(const char *arg) | 29 | static int get_stack_size(const char *str, unsigned long *_size) |
30 | { | 30 | { |
31 | char *tok, *tok2; | ||
32 | char *endptr; | 31 | char *endptr; |
32 | unsigned long size; | ||
33 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
33 | 34 | ||
34 | symbol_conf.use_callchain = true; | 35 | size = strtoul(str, &endptr, 0); |
35 | 36 | ||
36 | if (!arg) | 37 | do { |
38 | if (*endptr) | ||
39 | break; | ||
40 | |||
41 | size = round_up(size, sizeof(u64)); | ||
42 | if (!size || size > max_size) | ||
43 | break; | ||
44 | |||
45 | *_size = size; | ||
37 | return 0; | 46 | return 0; |
38 | 47 | ||
39 | tok = strtok((char *)arg, ","); | 48 | } while (0); |
40 | if (!tok) | ||
41 | return -1; | ||
42 | 49 | ||
43 | /* get the output mode */ | 50 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", |
44 | if (!strncmp(tok, "graph", strlen(arg))) { | 51 | max_size, str); |
45 | callchain_param.mode = CHAIN_GRAPH_ABS; | 52 | return -1; |
53 | } | ||
54 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
46 | 55 | ||
47 | } else if (!strncmp(tok, "flat", strlen(arg))) { | 56 | int parse_callchain_record_opt(const char *arg) |
48 | callchain_param.mode = CHAIN_FLAT; | 57 | { |
49 | } else if (!strncmp(tok, "fractal", strlen(arg))) { | 58 | char *tok, *name, *saveptr = NULL; |
50 | callchain_param.mode = CHAIN_GRAPH_REL; | 59 | char *buf; |
51 | } else if (!strncmp(tok, "none", strlen(arg))) { | 60 | int ret = -1; |
52 | callchain_param.mode = CHAIN_NONE; | 61 | |
53 | symbol_conf.use_callchain = false; | 62 | /* We need buffer that we know we can write to. */ |
54 | return 0; | 63 | buf = malloc(strlen(arg) + 1); |
55 | } else { | 64 | if (!buf) |
56 | return -1; | 65 | return -ENOMEM; |
57 | } | 66 | |
67 | strcpy(buf, arg); | ||
68 | |||
69 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
70 | name = tok ? : (char *)buf; | ||
71 | |||
72 | do { | ||
73 | /* Framepointer style */ | ||
74 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
75 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
76 | callchain_param.record_mode = CALLCHAIN_FP; | ||
77 | ret = 0; | ||
78 | } else | ||
79 | pr_err("callchain: No more arguments " | ||
80 | "needed for --call-graph fp\n"); | ||
81 | break; | ||
58 | 82 | ||
59 | /* get the min percentage */ | 83 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
60 | tok = strtok(NULL, ","); | 84 | /* Dwarf style */ |
61 | if (!tok) | 85 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { |
62 | goto setup; | 86 | const unsigned long default_stack_dump_size = 8192; |
63 | 87 | ||
64 | callchain_param.min_percent = strtod(tok, &endptr); | 88 | ret = 0; |
65 | if (tok == endptr) | 89 | callchain_param.record_mode = CALLCHAIN_DWARF; |
66 | return -1; | 90 | callchain_param.dump_size = default_stack_dump_size; |
91 | |||
92 | tok = strtok_r(NULL, ",", &saveptr); | ||
93 | if (tok) { | ||
94 | unsigned long size = 0; | ||
95 | |||
96 | ret = get_stack_size(tok, &size); | ||
97 | callchain_param.dump_size = size; | ||
98 | } | ||
99 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
100 | } else { | ||
101 | pr_err("callchain: Unknown --call-graph option " | ||
102 | "value: %s\n", arg); | ||
103 | break; | ||
104 | } | ||
67 | 105 | ||
68 | /* get the print limit */ | 106 | } while (0); |
69 | tok2 = strtok(NULL, ","); | ||
70 | if (!tok2) | ||
71 | goto setup; | ||
72 | 107 | ||
73 | if (tok2[0] != 'c') { | 108 | free(buf); |
74 | callchain_param.print_limit = strtoul(tok2, &endptr, 0); | 109 | return ret; |
75 | tok2 = strtok(NULL, ","); | 110 | } |
76 | if (!tok2) | 111 | |
77 | goto setup; | 112 | static int parse_callchain_mode(const char *value) |
113 | { | ||
114 | if (!strncmp(value, "graph", strlen(value))) { | ||
115 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
116 | return 0; | ||
117 | } | ||
118 | if (!strncmp(value, "flat", strlen(value))) { | ||
119 | callchain_param.mode = CHAIN_FLAT; | ||
120 | return 0; | ||
78 | } | 121 | } |
122 | if (!strncmp(value, "fractal", strlen(value))) { | ||
123 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
124 | return 0; | ||
125 | } | ||
126 | return -1; | ||
127 | } | ||
79 | 128 | ||
80 | /* get the call chain order */ | 129 | static int parse_callchain_order(const char *value) |
81 | if (!strncmp(tok2, "caller", strlen("caller"))) | 130 | { |
131 | if (!strncmp(value, "caller", strlen(value))) { | ||
82 | callchain_param.order = ORDER_CALLER; | 132 | callchain_param.order = ORDER_CALLER; |
83 | else if (!strncmp(tok2, "callee", strlen("callee"))) | 133 | return 0; |
134 | } | ||
135 | if (!strncmp(value, "callee", strlen(value))) { | ||
84 | callchain_param.order = ORDER_CALLEE; | 136 | callchain_param.order = ORDER_CALLEE; |
85 | else | 137 | return 0; |
86 | return -1; | 138 | } |
139 | return -1; | ||
140 | } | ||
87 | 141 | ||
88 | /* Get the sort key */ | 142 | static int parse_callchain_sort_key(const char *value) |
89 | tok2 = strtok(NULL, ","); | 143 | { |
90 | if (!tok2) | 144 | if (!strncmp(value, "function", strlen(value))) { |
91 | goto setup; | ||
92 | if (!strncmp(tok2, "function", strlen("function"))) | ||
93 | callchain_param.key = CCKEY_FUNCTION; | 145 | callchain_param.key = CCKEY_FUNCTION; |
94 | else if (!strncmp(tok2, "address", strlen("address"))) | 146 | return 0; |
147 | } | ||
148 | if (!strncmp(value, "address", strlen(value))) { | ||
95 | callchain_param.key = CCKEY_ADDRESS; | 149 | callchain_param.key = CCKEY_ADDRESS; |
96 | else | 150 | return 0; |
97 | return -1; | 151 | } |
98 | setup: | 152 | if (!strncmp(value, "branch", strlen(value))) { |
153 | callchain_param.branch_callstack = 1; | ||
154 | return 0; | ||
155 | } | ||
156 | return -1; | ||
157 | } | ||
158 | |||
159 | int | ||
160 | parse_callchain_report_opt(const char *arg) | ||
161 | { | ||
162 | char *tok; | ||
163 | char *endptr; | ||
164 | bool minpcnt_set = false; | ||
165 | |||
166 | symbol_conf.use_callchain = true; | ||
167 | |||
168 | if (!arg) | ||
169 | return 0; | ||
170 | |||
171 | while ((tok = strtok((char *)arg, ",")) != NULL) { | ||
172 | if (!strncmp(tok, "none", strlen(tok))) { | ||
173 | callchain_param.mode = CHAIN_NONE; | ||
174 | symbol_conf.use_callchain = false; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | if (!parse_callchain_mode(tok) || | ||
179 | !parse_callchain_order(tok) || | ||
180 | !parse_callchain_sort_key(tok)) { | ||
181 | /* parsing ok - move on to the next */ | ||
182 | } else if (!minpcnt_set) { | ||
183 | /* try to get the min percent */ | ||
184 | callchain_param.min_percent = strtod(tok, &endptr); | ||
185 | if (tok == endptr) | ||
186 | return -1; | ||
187 | minpcnt_set = true; | ||
188 | } else { | ||
189 | /* try print limit at last */ | ||
190 | callchain_param.print_limit = strtoul(tok, &endptr, 0); | ||
191 | if (tok == endptr) | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | arg = NULL; | ||
196 | } | ||
197 | |||
99 | if (callchain_register_param(&callchain_param) < 0) { | 198 | if (callchain_register_param(&callchain_param) < 0) { |
100 | pr_err("Can't register callchain params\n"); | 199 | pr_err("Can't register callchain params\n"); |
101 | return -1; | 200 | return -1; |
@@ -103,6 +202,47 @@ setup: | |||
103 | return 0; | 202 | return 0; |
104 | } | 203 | } |
105 | 204 | ||
205 | int perf_callchain_config(const char *var, const char *value) | ||
206 | { | ||
207 | char *endptr; | ||
208 | |||
209 | if (prefixcmp(var, "call-graph.")) | ||
210 | return 0; | ||
211 | var += sizeof("call-graph.") - 1; | ||
212 | |||
213 | if (!strcmp(var, "record-mode")) | ||
214 | return parse_callchain_record_opt(value); | ||
215 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
216 | if (!strcmp(var, "dump-size")) { | ||
217 | unsigned long size = 0; | ||
218 | int ret; | ||
219 | |||
220 | ret = get_stack_size(value, &size); | ||
221 | callchain_param.dump_size = size; | ||
222 | |||
223 | return ret; | ||
224 | } | ||
225 | #endif | ||
226 | if (!strcmp(var, "print-type")) | ||
227 | return parse_callchain_mode(value); | ||
228 | if (!strcmp(var, "order")) | ||
229 | return parse_callchain_order(value); | ||
230 | if (!strcmp(var, "sort-key")) | ||
231 | return parse_callchain_sort_key(value); | ||
232 | if (!strcmp(var, "threshold")) { | ||
233 | callchain_param.min_percent = strtod(value, &endptr); | ||
234 | if (value == endptr) | ||
235 | return -1; | ||
236 | } | ||
237 | if (!strcmp(var, "print-limit")) { | ||
238 | callchain_param.print_limit = strtod(value, &endptr); | ||
239 | if (value == endptr) | ||
240 | return -1; | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
106 | static void | 246 | static void |
107 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 247 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
108 | enum chain_mode mode) | 248 | enum chain_mode mode) |
@@ -618,8 +758,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent | |||
618 | 758 | ||
619 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || | 759 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || |
620 | sort__has_parent) { | 760 | sort__has_parent) { |
621 | return machine__resolve_callchain(al->machine, evsel, al->thread, | 761 | return thread__resolve_callchain(al->thread, evsel, sample, |
622 | sample, parent, al, max_stack); | 762 | parent, al, max_stack); |
623 | } | 763 | } |
624 | return 0; | 764 | return 0; |
625 | } | 765 | } |
@@ -672,3 +812,62 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
672 | out: | 812 | out: |
673 | return 1; | 813 | return 1; |
674 | } | 814 | } |
815 | |||
816 | char *callchain_list__sym_name(struct callchain_list *cl, | ||
817 | char *bf, size_t bfsize, bool show_dso) | ||
818 | { | ||
819 | int printed; | ||
820 | |||
821 | if (cl->ms.sym) { | ||
822 | if (callchain_param.key == CCKEY_ADDRESS && | ||
823 | cl->ms.map && !cl->srcline) | ||
824 | cl->srcline = get_srcline(cl->ms.map->dso, | ||
825 | map__rip_2objdump(cl->ms.map, | ||
826 | cl->ip), | ||
827 | cl->ms.sym, false); | ||
828 | if (cl->srcline) | ||
829 | printed = scnprintf(bf, bfsize, "%s %s", | ||
830 | cl->ms.sym->name, cl->srcline); | ||
831 | else | ||
832 | printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
833 | } else | ||
834 | printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
835 | |||
836 | if (show_dso) | ||
837 | scnprintf(bf + printed, bfsize - printed, " %s", | ||
838 | cl->ms.map ? | ||
839 | cl->ms.map->dso->short_name : | ||
840 | "unknown"); | ||
841 | |||
842 | return bf; | ||
843 | } | ||
844 | |||
845 | static void free_callchain_node(struct callchain_node *node) | ||
846 | { | ||
847 | struct callchain_list *list, *tmp; | ||
848 | struct callchain_node *child; | ||
849 | struct rb_node *n; | ||
850 | |||
851 | list_for_each_entry_safe(list, tmp, &node->val, list) { | ||
852 | list_del(&list->list); | ||
853 | free(list); | ||
854 | } | ||
855 | |||
856 | n = rb_first(&node->rb_root_in); | ||
857 | while (n) { | ||
858 | child = container_of(n, struct callchain_node, rb_node_in); | ||
859 | n = rb_next(n); | ||
860 | rb_erase(&child->rb_node_in, &node->rb_root_in); | ||
861 | |||
862 | free_callchain_node(child); | ||
863 | free(child); | ||
864 | } | ||
865 | } | ||
866 | |||
867 | void free_callchain(struct callchain_root *root) | ||
868 | { | ||
869 | if (!symbol_conf.use_callchain) | ||
870 | return; | ||
871 | |||
872 | free_callchain_node(&root->node); | ||
873 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index da43619d6173..c0ec1acc38e4 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -54,17 +54,24 @@ enum chain_key { | |||
54 | }; | 54 | }; |
55 | 55 | ||
56 | struct callchain_param { | 56 | struct callchain_param { |
57 | bool enabled; | ||
58 | enum perf_call_graph_mode record_mode; | ||
59 | u32 dump_size; | ||
57 | enum chain_mode mode; | 60 | enum chain_mode mode; |
58 | u32 print_limit; | 61 | u32 print_limit; |
59 | double min_percent; | 62 | double min_percent; |
60 | sort_chain_func_t sort; | 63 | sort_chain_func_t sort; |
61 | enum chain_order order; | 64 | enum chain_order order; |
62 | enum chain_key key; | 65 | enum chain_key key; |
66 | bool branch_callstack; | ||
63 | }; | 67 | }; |
64 | 68 | ||
69 | extern struct callchain_param callchain_param; | ||
70 | |||
65 | struct callchain_list { | 71 | struct callchain_list { |
66 | u64 ip; | 72 | u64 ip; |
67 | struct map_symbol ms; | 73 | struct map_symbol ms; |
74 | char *srcline; | ||
68 | struct list_head list; | 75 | struct list_head list; |
69 | }; | 76 | }; |
70 | 77 | ||
@@ -154,7 +161,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
154 | struct option; | 161 | struct option; |
155 | struct hist_entry; | 162 | struct hist_entry; |
156 | 163 | ||
157 | int record_parse_callchain(const char *arg, struct record_opts *opts); | ||
158 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 164 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
159 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | 165 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); |
160 | 166 | ||
@@ -166,7 +172,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
166 | bool hide_unresolved); | 172 | bool hide_unresolved); |
167 | 173 | ||
168 | extern const char record_callchain_help[]; | 174 | extern const char record_callchain_help[]; |
175 | int parse_callchain_record_opt(const char *arg); | ||
169 | int parse_callchain_report_opt(const char *arg); | 176 | int parse_callchain_report_opt(const char *arg); |
177 | int perf_callchain_config(const char *var, const char *value); | ||
170 | 178 | ||
171 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | 179 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, |
172 | struct callchain_cursor *src) | 180 | struct callchain_cursor *src) |
@@ -178,15 +186,18 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | |||
178 | } | 186 | } |
179 | 187 | ||
180 | #ifdef HAVE_SKIP_CALLCHAIN_IDX | 188 | #ifdef HAVE_SKIP_CALLCHAIN_IDX |
181 | extern int arch_skip_callchain_idx(struct machine *machine, | 189 | extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain); |
182 | struct thread *thread, struct ip_callchain *chain); | ||
183 | #else | 190 | #else |
184 | static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused, | 191 | static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, |
185 | struct thread *thread __maybe_unused, | ||
186 | struct ip_callchain *chain __maybe_unused) | 192 | struct ip_callchain *chain __maybe_unused) |
187 | { | 193 | { |
188 | return -1; | 194 | return -1; |
189 | } | 195 | } |
190 | #endif | 196 | #endif |
191 | 197 | ||
198 | char *callchain_list__sym_name(struct callchain_list *cl, | ||
199 | char *bf, size_t bfsize, bool show_dso); | ||
200 | |||
201 | void free_callchain(struct callchain_root *root); | ||
202 | |||
192 | #endif /* __PERF_CALLCHAIN_H */ | 203 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c index c5d05ec17220..47b78b3f0325 100644 --- a/tools/perf/util/cloexec.c +++ b/tools/perf/util/cloexec.c | |||
@@ -1,7 +1,9 @@ | |||
1 | #include <sched.h> | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "cloexec.h" | 4 | #include "cloexec.h" |
4 | #include "asm/bug.h" | 5 | #include "asm/bug.h" |
6 | #include "debug.h" | ||
5 | 7 | ||
6 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; | 8 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; |
7 | 9 | ||
@@ -9,15 +11,30 @@ static int perf_flag_probe(void) | |||
9 | { | 11 | { |
10 | /* use 'safest' configuration as used in perf_evsel__fallback() */ | 12 | /* use 'safest' configuration as used in perf_evsel__fallback() */ |
11 | struct perf_event_attr attr = { | 13 | struct perf_event_attr attr = { |
12 | .type = PERF_COUNT_SW_CPU_CLOCK, | 14 | .type = PERF_TYPE_SOFTWARE, |
13 | .config = PERF_COUNT_SW_CPU_CLOCK, | 15 | .config = PERF_COUNT_SW_CPU_CLOCK, |
16 | .exclude_kernel = 1, | ||
14 | }; | 17 | }; |
15 | int fd; | 18 | int fd; |
16 | int err; | 19 | int err; |
20 | int cpu; | ||
21 | pid_t pid = -1; | ||
22 | char sbuf[STRERR_BUFSIZE]; | ||
17 | 23 | ||
18 | /* check cloexec flag */ | 24 | cpu = sched_getcpu(); |
19 | fd = sys_perf_event_open(&attr, 0, -1, -1, | 25 | if (cpu < 0) |
20 | PERF_FLAG_FD_CLOEXEC); | 26 | cpu = 0; |
27 | |||
28 | while (1) { | ||
29 | /* check cloexec flag */ | ||
30 | fd = sys_perf_event_open(&attr, pid, cpu, -1, | ||
31 | PERF_FLAG_FD_CLOEXEC); | ||
32 | if (fd < 0 && pid == -1 && errno == EACCES) { | ||
33 | pid = 0; | ||
34 | continue; | ||
35 | } | ||
36 | break; | ||
37 | } | ||
21 | err = errno; | 38 | err = errno; |
22 | 39 | ||
23 | if (fd >= 0) { | 40 | if (fd >= 0) { |
@@ -25,17 +42,17 @@ static int perf_flag_probe(void) | |||
25 | return 1; | 42 | return 1; |
26 | } | 43 | } |
27 | 44 | ||
28 | WARN_ONCE(err != EINVAL, | 45 | WARN_ONCE(err != EINVAL && err != EBUSY, |
29 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", | 46 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", |
30 | err, strerror(err)); | 47 | err, strerror_r(err, sbuf, sizeof(sbuf))); |
31 | 48 | ||
32 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ | 49 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ |
33 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | 50 | fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); |
34 | err = errno; | 51 | err = errno; |
35 | 52 | ||
36 | if (WARN_ONCE(fd < 0, | 53 | if (WARN_ONCE(fd < 0 && err != EBUSY, |
37 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", | 54 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", |
38 | err, strerror(err))) | 55 | err, strerror_r(err, sbuf, sizeof(sbuf)))) |
39 | return -1; | 56 | return -1; |
40 | 57 | ||
41 | close(fd); | 58 | close(fd); |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 87b8672eb413..f4654183d391 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -335,3 +335,19 @@ int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
335 | va_end(args); | 335 | va_end(args); |
336 | return value_color_snprintf(bf, size, fmt, percent); | 336 | return value_color_snprintf(bf, size, fmt, percent); |
337 | } | 337 | } |
338 | |||
339 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...) | ||
340 | { | ||
341 | va_list args; | ||
342 | int len; | ||
343 | double percent; | ||
344 | const char *color; | ||
345 | |||
346 | va_start(args, fmt); | ||
347 | len = va_arg(args, int); | ||
348 | percent = va_arg(args, double); | ||
349 | va_end(args); | ||
350 | |||
351 | color = get_percent_color(percent); | ||
352 | return color_snprintf(bf, size, color, fmt, len, percent); | ||
353 | } | ||
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 7ff30a62a132..0a594b8a0c26 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
@@ -41,6 +41,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); | |||
41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); | 41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); |
42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); | 42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); |
43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); | 43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); |
44 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...); | ||
44 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 45 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
45 | const char *get_percent_color(double percent); | 46 | const char *get_percent_color(double percent); |
46 | 47 | ||
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index f9e777629e21..b2bb59df65e1 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c | |||
@@ -74,7 +74,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) | |||
74 | return new; | 74 | return new; |
75 | } | 75 | } |
76 | 76 | ||
77 | struct comm *comm__new(const char *str, u64 timestamp) | 77 | struct comm *comm__new(const char *str, u64 timestamp, bool exec) |
78 | { | 78 | { |
79 | struct comm *comm = zalloc(sizeof(*comm)); | 79 | struct comm *comm = zalloc(sizeof(*comm)); |
80 | 80 | ||
@@ -82,6 +82,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
82 | return NULL; | 82 | return NULL; |
83 | 83 | ||
84 | comm->start = timestamp; | 84 | comm->start = timestamp; |
85 | comm->exec = exec; | ||
85 | 86 | ||
86 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | 87 | comm->comm_str = comm_str__findnew(str, &comm_str_root); |
87 | if (!comm->comm_str) { | 88 | if (!comm->comm_str) { |
@@ -94,7 +95,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
94 | return comm; | 95 | return comm; |
95 | } | 96 | } |
96 | 97 | ||
97 | int comm__override(struct comm *comm, const char *str, u64 timestamp) | 98 | int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec) |
98 | { | 99 | { |
99 | struct comm_str *new, *old = comm->comm_str; | 100 | struct comm_str *new, *old = comm->comm_str; |
100 | 101 | ||
@@ -106,6 +107,8 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp) | |||
106 | comm_str__put(old); | 107 | comm_str__put(old); |
107 | comm->comm_str = new; | 108 | comm->comm_str = new; |
108 | comm->start = timestamp; | 109 | comm->start = timestamp; |
110 | if (exec) | ||
111 | comm->exec = true; | ||
109 | 112 | ||
110 | return 0; | 113 | return 0; |
111 | } | 114 | } |
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index fac5bd51befc..71c9c39340d4 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h | |||
@@ -11,11 +11,17 @@ struct comm { | |||
11 | struct comm_str *comm_str; | 11 | struct comm_str *comm_str; |
12 | u64 start; | 12 | u64 start; |
13 | struct list_head list; | 13 | struct list_head list; |
14 | bool exec; | ||
15 | union { /* Tool specific area */ | ||
16 | void *priv; | ||
17 | u64 db_id; | ||
18 | }; | ||
14 | }; | 19 | }; |
15 | 20 | ||
16 | void comm__free(struct comm *comm); | 21 | void comm__free(struct comm *comm); |
17 | struct comm *comm__new(const char *str, u64 timestamp); | 22 | struct comm *comm__new(const char *str, u64 timestamp, bool exec); |
18 | const char *comm__str(const struct comm *comm); | 23 | const char *comm__str(const struct comm *comm); |
19 | int comm__override(struct comm *comm, const char *str, u64 timestamp); | 24 | int comm__override(struct comm *comm, const char *str, u64 timestamp, |
25 | bool exec); | ||
20 | 26 | ||
21 | #endif /* __PERF_COMM_H */ | 27 | #endif /* __PERF_COMM_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 1e5e2e5af6b1..e18f653cd7db 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
222 | const unsigned char *bomptr = utf8_bom; | 222 | const unsigned char *bomptr = utf8_bom; |
223 | 223 | ||
224 | for (;;) { | 224 | for (;;) { |
225 | int c = get_next_char(); | 225 | int line, c = get_next_char(); |
226 | |||
226 | if (bomptr && *bomptr) { | 227 | if (bomptr && *bomptr) { |
227 | /* We are at the file beginning; skip UTF8-encoded BOM | 228 | /* We are at the file beginning; skip UTF8-encoded BOM |
228 | * if present. Sane editors won't put this in on their | 229 | * if present. Sane editors won't put this in on their |
@@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
261 | if (!isalpha(c)) | 262 | if (!isalpha(c)) |
262 | break; | 263 | break; |
263 | var[baselen] = tolower(c); | 264 | var[baselen] = tolower(c); |
264 | if (get_value(fn, data, var, baselen+1) < 0) | 265 | |
266 | /* | ||
267 | * The get_value function might or might not reach the '\n', | ||
268 | * so saving the current line number for error reporting. | ||
269 | */ | ||
270 | line = config_linenr; | ||
271 | if (get_value(fn, data, var, baselen+1) < 0) { | ||
272 | config_linenr = line; | ||
265 | break; | 273 | break; |
274 | } | ||
266 | } | 275 | } |
267 | die("bad config file line %d in %s", config_linenr, config_file_name); | 276 | die("bad config file line %d in %s", config_linenr, config_file_name); |
268 | } | 277 | } |
@@ -286,6 +295,21 @@ static int parse_unit_factor(const char *end, unsigned long *val) | |||
286 | return 0; | 295 | return 0; |
287 | } | 296 | } |
288 | 297 | ||
298 | static int perf_parse_llong(const char *value, long long *ret) | ||
299 | { | ||
300 | if (value && *value) { | ||
301 | char *end; | ||
302 | long long val = strtoll(value, &end, 0); | ||
303 | unsigned long factor = 1; | ||
304 | |||
305 | if (!parse_unit_factor(end, &factor)) | ||
306 | return 0; | ||
307 | *ret = val * factor; | ||
308 | return 1; | ||
309 | } | ||
310 | return 0; | ||
311 | } | ||
312 | |||
289 | static int perf_parse_long(const char *value, long *ret) | 313 | static int perf_parse_long(const char *value, long *ret) |
290 | { | 314 | { |
291 | if (value && *value) { | 315 | if (value && *value) { |
@@ -307,6 +331,15 @@ static void die_bad_config(const char *name) | |||
307 | die("bad config value for '%s'", name); | 331 | die("bad config value for '%s'", name); |
308 | } | 332 | } |
309 | 333 | ||
334 | u64 perf_config_u64(const char *name, const char *value) | ||
335 | { | ||
336 | long long ret = 0; | ||
337 | |||
338 | if (!perf_parse_llong(value, &ret)) | ||
339 | die_bad_config(name); | ||
340 | return (u64) ret; | ||
341 | } | ||
342 | |||
310 | int perf_config_int(const char *name, const char *value) | 343 | int perf_config_int(const char *name, const char *value) |
311 | { | 344 | { |
312 | long ret = 0; | 345 | long ret = 0; |
@@ -372,6 +405,9 @@ int perf_default_config(const char *var, const char *value, | |||
372 | if (!prefixcmp(var, "ui.")) | 405 | if (!prefixcmp(var, "ui.")) |
373 | return perf_ui_config(var, value); | 406 | return perf_ui_config(var, value); |
374 | 407 | ||
408 | if (!prefixcmp(var, "call-graph.")) | ||
409 | return perf_callchain_config(var, value); | ||
410 | |||
375 | /* Add other config variables here. */ | 411 | /* Add other config variables here. */ |
376 | return 0; | 412 | return 0; |
377 | } | 413 | } |
@@ -486,7 +522,7 @@ static int buildid_dir_command_config(const char *var, const char *value, | |||
486 | const char *v; | 522 | const char *v; |
487 | 523 | ||
488 | /* same dir for all commands */ | 524 | /* same dir for all commands */ |
489 | if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { | 525 | if (!strcmp(var, "buildid.dir")) { |
490 | v = perf_config_dirname(var, value); | 526 | v = perf_config_dirname(var, value); |
491 | if (!v) | 527 | if (!v) |
492 | return -1; | 528 | return -1; |
@@ -503,12 +539,14 @@ static void check_buildid_dir_config(void) | |||
503 | perf_config(buildid_dir_command_config, &c); | 539 | perf_config(buildid_dir_command_config, &c); |
504 | } | 540 | } |
505 | 541 | ||
506 | void set_buildid_dir(void) | 542 | void set_buildid_dir(const char *dir) |
507 | { | 543 | { |
508 | buildid_dir[0] = '\0'; | 544 | if (dir) |
545 | scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir); | ||
509 | 546 | ||
510 | /* try config file */ | 547 | /* try config file */ |
511 | check_buildid_dir_config(); | 548 | if (buildid_dir[0] == '\0') |
549 | check_buildid_dir_config(); | ||
512 | 550 | ||
513 | /* default to $HOME/.debug */ | 551 | /* default to $HOME/.debug */ |
514 | if (buildid_dir[0] == '\0') { | 552 | if (buildid_dir[0] == '\0') { |
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 29d720cf5844..1921942fc2e0 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c | |||
@@ -50,12 +50,14 @@ static int open_file_read(struct perf_data_file *file) | |||
50 | { | 50 | { |
51 | struct stat st; | 51 | struct stat st; |
52 | int fd; | 52 | int fd; |
53 | char sbuf[STRERR_BUFSIZE]; | ||
53 | 54 | ||
54 | fd = open(file->path, O_RDONLY); | 55 | fd = open(file->path, O_RDONLY); |
55 | if (fd < 0) { | 56 | if (fd < 0) { |
56 | int err = errno; | 57 | int err = errno; |
57 | 58 | ||
58 | pr_err("failed to open %s: %s", file->path, strerror(err)); | 59 | pr_err("failed to open %s: %s", file->path, |
60 | strerror_r(err, sbuf, sizeof(sbuf))); | ||
59 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | 61 | if (err == ENOENT && !strcmp(file->path, "perf.data")) |
60 | pr_err(" (try 'perf record' first)"); | 62 | pr_err(" (try 'perf record' first)"); |
61 | pr_err("\n"); | 63 | pr_err("\n"); |
@@ -88,6 +90,7 @@ static int open_file_read(struct perf_data_file *file) | |||
88 | static int open_file_write(struct perf_data_file *file) | 90 | static int open_file_write(struct perf_data_file *file) |
89 | { | 91 | { |
90 | int fd; | 92 | int fd; |
93 | char sbuf[STRERR_BUFSIZE]; | ||
91 | 94 | ||
92 | if (check_backup(file)) | 95 | if (check_backup(file)) |
93 | return -1; | 96 | return -1; |
@@ -95,7 +98,8 @@ static int open_file_write(struct perf_data_file *file) | |||
95 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | 98 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); |
96 | 99 | ||
97 | if (fd < 0) | 100 | if (fd < 0) |
98 | pr_err("failed to open %s : %s\n", file->path, strerror(errno)); | 101 | pr_err("failed to open %s : %s\n", file->path, |
102 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
99 | 103 | ||
100 | return fd; | 104 | return fd; |
101 | } | 105 | } |
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c new file mode 100644 index 000000000000..c81dae399763 --- /dev/null +++ b/tools/perf/util/db-export.c | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * db-export.c: Support for exporting data suitable for import to a database | ||
3 | * Copyright (c) 2014, 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 <errno.h> | ||
17 | |||
18 | #include "evsel.h" | ||
19 | #include "machine.h" | ||
20 | #include "thread.h" | ||
21 | #include "comm.h" | ||
22 | #include "symbol.h" | ||
23 | #include "event.h" | ||
24 | #include "util.h" | ||
25 | #include "thread-stack.h" | ||
26 | #include "db-export.h" | ||
27 | |||
28 | struct deferred_export { | ||
29 | struct list_head node; | ||
30 | struct comm *comm; | ||
31 | }; | ||
32 | |||
33 | static int db_export__deferred(struct db_export *dbe) | ||
34 | { | ||
35 | struct deferred_export *de; | ||
36 | int err; | ||
37 | |||
38 | while (!list_empty(&dbe->deferred)) { | ||
39 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
40 | node); | ||
41 | err = dbe->export_comm(dbe, de->comm); | ||
42 | list_del(&de->node); | ||
43 | free(de); | ||
44 | if (err) | ||
45 | return err; | ||
46 | } | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static void db_export__free_deferred(struct db_export *dbe) | ||
52 | { | ||
53 | struct deferred_export *de; | ||
54 | |||
55 | while (!list_empty(&dbe->deferred)) { | ||
56 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
57 | node); | ||
58 | list_del(&de->node); | ||
59 | free(de); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | static int db_export__defer_comm(struct db_export *dbe, struct comm *comm) | ||
64 | { | ||
65 | struct deferred_export *de; | ||
66 | |||
67 | de = zalloc(sizeof(struct deferred_export)); | ||
68 | if (!de) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | de->comm = comm; | ||
72 | list_add_tail(&de->node, &dbe->deferred); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | int db_export__init(struct db_export *dbe) | ||
78 | { | ||
79 | memset(dbe, 0, sizeof(struct db_export)); | ||
80 | INIT_LIST_HEAD(&dbe->deferred); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | int db_export__flush(struct db_export *dbe) | ||
85 | { | ||
86 | return db_export__deferred(dbe); | ||
87 | } | ||
88 | |||
89 | void db_export__exit(struct db_export *dbe) | ||
90 | { | ||
91 | db_export__free_deferred(dbe); | ||
92 | call_return_processor__free(dbe->crp); | ||
93 | dbe->crp = NULL; | ||
94 | } | ||
95 | |||
96 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel) | ||
97 | { | ||
98 | if (evsel->db_id) | ||
99 | return 0; | ||
100 | |||
101 | evsel->db_id = ++dbe->evsel_last_db_id; | ||
102 | |||
103 | if (dbe->export_evsel) | ||
104 | return dbe->export_evsel(dbe, evsel); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | int db_export__machine(struct db_export *dbe, struct machine *machine) | ||
110 | { | ||
111 | if (machine->db_id) | ||
112 | return 0; | ||
113 | |||
114 | machine->db_id = ++dbe->machine_last_db_id; | ||
115 | |||
116 | if (dbe->export_machine) | ||
117 | return dbe->export_machine(dbe, machine); | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | int db_export__thread(struct db_export *dbe, struct thread *thread, | ||
123 | struct machine *machine, struct comm *comm) | ||
124 | { | ||
125 | u64 main_thread_db_id = 0; | ||
126 | int err; | ||
127 | |||
128 | if (thread->db_id) | ||
129 | return 0; | ||
130 | |||
131 | thread->db_id = ++dbe->thread_last_db_id; | ||
132 | |||
133 | if (thread->pid_ != -1) { | ||
134 | struct thread *main_thread; | ||
135 | |||
136 | if (thread->pid_ == thread->tid) { | ||
137 | main_thread = thread; | ||
138 | } else { | ||
139 | main_thread = machine__findnew_thread(machine, | ||
140 | thread->pid_, | ||
141 | thread->pid_); | ||
142 | if (!main_thread) | ||
143 | return -ENOMEM; | ||
144 | err = db_export__thread(dbe, main_thread, machine, | ||
145 | comm); | ||
146 | if (err) | ||
147 | return err; | ||
148 | if (comm) { | ||
149 | err = db_export__comm_thread(dbe, comm, thread); | ||
150 | if (err) | ||
151 | return err; | ||
152 | } | ||
153 | } | ||
154 | main_thread_db_id = main_thread->db_id; | ||
155 | } | ||
156 | |||
157 | if (dbe->export_thread) | ||
158 | return dbe->export_thread(dbe, thread, main_thread_db_id, | ||
159 | machine); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | int db_export__comm(struct db_export *dbe, struct comm *comm, | ||
165 | struct thread *main_thread) | ||
166 | { | ||
167 | int err; | ||
168 | |||
169 | if (comm->db_id) | ||
170 | return 0; | ||
171 | |||
172 | comm->db_id = ++dbe->comm_last_db_id; | ||
173 | |||
174 | if (dbe->export_comm) { | ||
175 | if (main_thread->comm_set) | ||
176 | err = dbe->export_comm(dbe, comm); | ||
177 | else | ||
178 | err = db_export__defer_comm(dbe, comm); | ||
179 | if (err) | ||
180 | return err; | ||
181 | } | ||
182 | |||
183 | return db_export__comm_thread(dbe, comm, main_thread); | ||
184 | } | ||
185 | |||
186 | int db_export__comm_thread(struct db_export *dbe, struct comm *comm, | ||
187 | struct thread *thread) | ||
188 | { | ||
189 | u64 db_id; | ||
190 | |||
191 | db_id = ++dbe->comm_thread_last_db_id; | ||
192 | |||
193 | if (dbe->export_comm_thread) | ||
194 | return dbe->export_comm_thread(dbe, db_id, comm, thread); | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | int db_export__dso(struct db_export *dbe, struct dso *dso, | ||
200 | struct machine *machine) | ||
201 | { | ||
202 | if (dso->db_id) | ||
203 | return 0; | ||
204 | |||
205 | dso->db_id = ++dbe->dso_last_db_id; | ||
206 | |||
207 | if (dbe->export_dso) | ||
208 | return dbe->export_dso(dbe, dso, machine); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, | ||
214 | struct dso *dso) | ||
215 | { | ||
216 | u64 *sym_db_id = symbol__priv(sym); | ||
217 | |||
218 | if (*sym_db_id) | ||
219 | return 0; | ||
220 | |||
221 | *sym_db_id = ++dbe->symbol_last_db_id; | ||
222 | |||
223 | if (dbe->export_symbol) | ||
224 | return dbe->export_symbol(dbe, sym, dso); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static struct thread *get_main_thread(struct machine *machine, struct thread *thread) | ||
230 | { | ||
231 | if (thread->pid_ == thread->tid) | ||
232 | return thread; | ||
233 | |||
234 | if (thread->pid_ == -1) | ||
235 | return NULL; | ||
236 | |||
237 | return machine__find_thread(machine, thread->pid_, thread->pid_); | ||
238 | } | ||
239 | |||
240 | static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, | ||
241 | u64 *dso_db_id, u64 *sym_db_id, u64 *offset) | ||
242 | { | ||
243 | int err; | ||
244 | |||
245 | if (al->map) { | ||
246 | struct dso *dso = al->map->dso; | ||
247 | |||
248 | err = db_export__dso(dbe, dso, al->machine); | ||
249 | if (err) | ||
250 | return err; | ||
251 | *dso_db_id = dso->db_id; | ||
252 | |||
253 | if (!al->sym) { | ||
254 | al->sym = symbol__new(al->addr, 0, 0, "unknown"); | ||
255 | if (al->sym) | ||
256 | symbols__insert(&dso->symbols[al->map->type], | ||
257 | al->sym); | ||
258 | } | ||
259 | |||
260 | if (al->sym) { | ||
261 | u64 *db_id = symbol__priv(al->sym); | ||
262 | |||
263 | err = db_export__symbol(dbe, al->sym, dso); | ||
264 | if (err) | ||
265 | return err; | ||
266 | *sym_db_id = *db_id; | ||
267 | *offset = al->addr - al->sym->start; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
275 | const char *name) | ||
276 | { | ||
277 | if (dbe->export_branch_type) | ||
278 | return dbe->export_branch_type(dbe, branch_type, name); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | int db_export__sample(struct db_export *dbe, union perf_event *event, | ||
284 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
285 | struct thread *thread, struct addr_location *al) | ||
286 | { | ||
287 | struct export_sample es = { | ||
288 | .event = event, | ||
289 | .sample = sample, | ||
290 | .evsel = evsel, | ||
291 | .thread = thread, | ||
292 | .al = al, | ||
293 | }; | ||
294 | struct thread *main_thread; | ||
295 | struct comm *comm = NULL; | ||
296 | int err; | ||
297 | |||
298 | err = db_export__evsel(dbe, evsel); | ||
299 | if (err) | ||
300 | return err; | ||
301 | |||
302 | err = db_export__machine(dbe, al->machine); | ||
303 | if (err) | ||
304 | return err; | ||
305 | |||
306 | main_thread = get_main_thread(al->machine, thread); | ||
307 | if (main_thread) | ||
308 | comm = machine__thread_exec_comm(al->machine, main_thread); | ||
309 | |||
310 | err = db_export__thread(dbe, thread, al->machine, comm); | ||
311 | if (err) | ||
312 | return err; | ||
313 | |||
314 | if (comm) { | ||
315 | err = db_export__comm(dbe, comm, main_thread); | ||
316 | if (err) | ||
317 | return err; | ||
318 | es.comm_db_id = comm->db_id; | ||
319 | } | ||
320 | |||
321 | es.db_id = ++dbe->sample_last_db_id; | ||
322 | |||
323 | err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset); | ||
324 | if (err) | ||
325 | return err; | ||
326 | |||
327 | if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | ||
328 | sample_addr_correlates_sym(&evsel->attr)) { | ||
329 | struct addr_location addr_al; | ||
330 | |||
331 | perf_event__preprocess_sample_addr(event, sample, thread, &addr_al); | ||
332 | err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id, | ||
333 | &es.addr_sym_db_id, &es.addr_offset); | ||
334 | if (err) | ||
335 | return err; | ||
336 | if (dbe->crp) { | ||
337 | err = thread_stack__process(thread, comm, sample, al, | ||
338 | &addr_al, es.db_id, | ||
339 | dbe->crp); | ||
340 | if (err) | ||
341 | return err; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | if (dbe->export_sample) | ||
346 | return dbe->export_sample(dbe, &es); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static struct { | ||
352 | u32 branch_type; | ||
353 | const char *name; | ||
354 | } branch_types[] = { | ||
355 | {0, "no branch"}, | ||
356 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"}, | ||
357 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"}, | ||
358 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"}, | ||
359 | {PERF_IP_FLAG_BRANCH, "unconditional jump"}, | ||
360 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, | ||
361 | "software interrupt"}, | ||
362 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, | ||
363 | "return from interrupt"}, | ||
364 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, | ||
365 | "system call"}, | ||
366 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, | ||
367 | "return from system call"}, | ||
368 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"}, | ||
369 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | | ||
370 | PERF_IP_FLAG_INTERRUPT, "hardware interrupt"}, | ||
371 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"}, | ||
372 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"}, | ||
373 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"}, | ||
374 | {0, NULL} | ||
375 | }; | ||
376 | |||
377 | int db_export__branch_types(struct db_export *dbe) | ||
378 | { | ||
379 | int i, err = 0; | ||
380 | |||
381 | for (i = 0; branch_types[i].name ; i++) { | ||
382 | err = db_export__branch_type(dbe, branch_types[i].branch_type, | ||
383 | branch_types[i].name); | ||
384 | if (err) | ||
385 | break; | ||
386 | } | ||
387 | return err; | ||
388 | } | ||
389 | |||
390 | int db_export__call_path(struct db_export *dbe, struct call_path *cp) | ||
391 | { | ||
392 | int err; | ||
393 | |||
394 | if (cp->db_id) | ||
395 | return 0; | ||
396 | |||
397 | if (cp->parent) { | ||
398 | err = db_export__call_path(dbe, cp->parent); | ||
399 | if (err) | ||
400 | return err; | ||
401 | } | ||
402 | |||
403 | cp->db_id = ++dbe->call_path_last_db_id; | ||
404 | |||
405 | if (dbe->export_call_path) | ||
406 | return dbe->export_call_path(dbe, cp); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | int db_export__call_return(struct db_export *dbe, struct call_return *cr) | ||
412 | { | ||
413 | int err; | ||
414 | |||
415 | if (cr->db_id) | ||
416 | return 0; | ||
417 | |||
418 | err = db_export__call_path(dbe, cr->cp); | ||
419 | if (err) | ||
420 | return err; | ||
421 | |||
422 | cr->db_id = ++dbe->call_return_last_db_id; | ||
423 | |||
424 | if (dbe->export_call_return) | ||
425 | return dbe->export_call_return(dbe, cr); | ||
426 | |||
427 | return 0; | ||
428 | } | ||
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h new file mode 100644 index 000000000000..adbd22d66798 --- /dev/null +++ b/tools/perf/util/db-export.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * db-export.h: Support for exporting data suitable for import to a database | ||
3 | * Copyright (c) 2014, 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_DB_EXPORT_H | ||
17 | #define __PERF_DB_EXPORT_H | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | #include <linux/list.h> | ||
21 | |||
22 | struct perf_evsel; | ||
23 | struct machine; | ||
24 | struct thread; | ||
25 | struct comm; | ||
26 | struct dso; | ||
27 | struct perf_sample; | ||
28 | struct addr_location; | ||
29 | struct call_return_processor; | ||
30 | struct call_path; | ||
31 | struct call_return; | ||
32 | |||
33 | struct export_sample { | ||
34 | union perf_event *event; | ||
35 | struct perf_sample *sample; | ||
36 | struct perf_evsel *evsel; | ||
37 | struct thread *thread; | ||
38 | struct addr_location *al; | ||
39 | u64 db_id; | ||
40 | u64 comm_db_id; | ||
41 | u64 dso_db_id; | ||
42 | u64 sym_db_id; | ||
43 | u64 offset; /* ip offset from symbol start */ | ||
44 | u64 addr_dso_db_id; | ||
45 | u64 addr_sym_db_id; | ||
46 | u64 addr_offset; /* addr offset from symbol start */ | ||
47 | }; | ||
48 | |||
49 | struct db_export { | ||
50 | int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel); | ||
51 | int (*export_machine)(struct db_export *dbe, struct machine *machine); | ||
52 | int (*export_thread)(struct db_export *dbe, struct thread *thread, | ||
53 | u64 main_thread_db_id, struct machine *machine); | ||
54 | int (*export_comm)(struct db_export *dbe, struct comm *comm); | ||
55 | int (*export_comm_thread)(struct db_export *dbe, u64 db_id, | ||
56 | struct comm *comm, struct thread *thread); | ||
57 | int (*export_dso)(struct db_export *dbe, struct dso *dso, | ||
58 | struct machine *machine); | ||
59 | int (*export_symbol)(struct db_export *dbe, struct symbol *sym, | ||
60 | struct dso *dso); | ||
61 | int (*export_branch_type)(struct db_export *dbe, u32 branch_type, | ||
62 | const char *name); | ||
63 | int (*export_sample)(struct db_export *dbe, struct export_sample *es); | ||
64 | int (*export_call_path)(struct db_export *dbe, struct call_path *cp); | ||
65 | int (*export_call_return)(struct db_export *dbe, | ||
66 | struct call_return *cr); | ||
67 | struct call_return_processor *crp; | ||
68 | u64 evsel_last_db_id; | ||
69 | u64 machine_last_db_id; | ||
70 | u64 thread_last_db_id; | ||
71 | u64 comm_last_db_id; | ||
72 | u64 comm_thread_last_db_id; | ||
73 | u64 dso_last_db_id; | ||
74 | u64 symbol_last_db_id; | ||
75 | u64 sample_last_db_id; | ||
76 | u64 call_path_last_db_id; | ||
77 | u64 call_return_last_db_id; | ||
78 | struct list_head deferred; | ||
79 | }; | ||
80 | |||
81 | int db_export__init(struct db_export *dbe); | ||
82 | int db_export__flush(struct db_export *dbe); | ||
83 | void db_export__exit(struct db_export *dbe); | ||
84 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel); | ||
85 | int db_export__machine(struct db_export *dbe, struct machine *machine); | ||
86 | int db_export__thread(struct db_export *dbe, struct thread *thread, | ||
87 | struct machine *machine, struct comm *comm); | ||
88 | int db_export__comm(struct db_export *dbe, struct comm *comm, | ||
89 | struct thread *main_thread); | ||
90 | int db_export__comm_thread(struct db_export *dbe, struct comm *comm, | ||
91 | struct thread *thread); | ||
92 | int db_export__dso(struct db_export *dbe, struct dso *dso, | ||
93 | struct machine *machine); | ||
94 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, | ||
95 | struct dso *dso); | ||
96 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
97 | const char *name); | ||
98 | int db_export__sample(struct db_export *dbe, union perf_event *event, | ||
99 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
100 | struct thread *thread, struct addr_location *al); | ||
101 | |||
102 | int db_export__branch_types(struct db_export *dbe); | ||
103 | |||
104 | int db_export__call_path(struct db_export *dbe, struct call_path *cp); | ||
105 | int db_export__call_return(struct db_export *dbe, struct call_return *cr); | ||
106 | |||
107 | #endif | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 71d419362634..ad60b2f20258 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -13,15 +13,20 @@ | |||
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "target.h" | 14 | #include "target.h" |
15 | 15 | ||
16 | #define NSECS_PER_SEC 1000000000ULL | ||
17 | #define NSECS_PER_USEC 1000ULL | ||
18 | |||
16 | int verbose; | 19 | int verbose; |
17 | bool dump_trace = false, quiet = false; | 20 | bool dump_trace = false, quiet = false; |
21 | int debug_ordered_events; | ||
22 | static int redirect_to_stderr; | ||
18 | 23 | ||
19 | static int _eprintf(int level, int var, const char *fmt, va_list args) | 24 | static int _eprintf(int level, int var, const char *fmt, va_list args) |
20 | { | 25 | { |
21 | int ret = 0; | 26 | int ret = 0; |
22 | 27 | ||
23 | if (var >= level) { | 28 | if (var >= level) { |
24 | if (use_browser >= 1) | 29 | if (use_browser >= 1 && !redirect_to_stderr) |
25 | ui_helpline__vshow(fmt, args); | 30 | ui_helpline__vshow(fmt, args); |
26 | else | 31 | else |
27 | ret = vfprintf(stderr, fmt, args); | 32 | ret = vfprintf(stderr, fmt, args); |
@@ -42,6 +47,35 @@ int eprintf(int level, int var, const char *fmt, ...) | |||
42 | return ret; | 47 | return ret; |
43 | } | 48 | } |
44 | 49 | ||
50 | static int __eprintf_time(u64 t, const char *fmt, va_list args) | ||
51 | { | ||
52 | int ret = 0; | ||
53 | u64 secs, usecs, nsecs = t; | ||
54 | |||
55 | secs = nsecs / NSECS_PER_SEC; | ||
56 | nsecs -= secs * NSECS_PER_SEC; | ||
57 | usecs = nsecs / NSECS_PER_USEC; | ||
58 | |||
59 | ret = fprintf(stderr, "[%13" PRIu64 ".%06" PRIu64 "] ", | ||
60 | secs, usecs); | ||
61 | ret += vfprintf(stderr, fmt, args); | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) | ||
66 | { | ||
67 | int ret = 0; | ||
68 | va_list args; | ||
69 | |||
70 | if (var >= level) { | ||
71 | va_start(args, fmt); | ||
72 | ret = __eprintf_time(t, fmt, args); | ||
73 | va_end(args); | ||
74 | } | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | |||
45 | /* | 79 | /* |
46 | * Overloading libtraceevent standard info print | 80 | * Overloading libtraceevent standard info print |
47 | * function, display with -v in perf. | 81 | * function, display with -v in perf. |
@@ -110,7 +144,9 @@ static struct debug_variable { | |||
110 | const char *name; | 144 | const char *name; |
111 | int *ptr; | 145 | int *ptr; |
112 | } debug_variables[] = { | 146 | } debug_variables[] = { |
113 | { .name = "verbose", .ptr = &verbose }, | 147 | { .name = "verbose", .ptr = &verbose }, |
148 | { .name = "ordered-events", .ptr = &debug_ordered_events}, | ||
149 | { .name = "stderr", .ptr = &redirect_to_stderr}, | ||
114 | { .name = NULL, } | 150 | { .name = NULL, } |
115 | }; | 151 | }; |
116 | 152 | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 89fb6b0f7ab2..be264d6f3b30 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #define __PERF_DEBUG_H | 3 | #define __PERF_DEBUG_H |
4 | 4 | ||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <string.h> | ||
6 | #include "event.h" | 7 | #include "event.h" |
7 | #include "../ui/helpline.h" | 8 | #include "../ui/helpline.h" |
8 | #include "../ui/progress.h" | 9 | #include "../ui/progress.h" |
@@ -10,6 +11,7 @@ | |||
10 | 11 | ||
11 | extern int verbose; | 12 | extern int verbose; |
12 | extern bool quiet, dump_trace; | 13 | extern bool quiet, dump_trace; |
14 | extern int debug_ordered_events; | ||
13 | 15 | ||
14 | #ifndef pr_fmt | 16 | #ifndef pr_fmt |
15 | #define pr_fmt(fmt) fmt | 17 | #define pr_fmt(fmt) fmt |
@@ -29,6 +31,14 @@ extern bool quiet, dump_trace; | |||
29 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | 31 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) |
30 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | 32 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) |
31 | 33 | ||
34 | #define pr_time_N(n, var, t, fmt, ...) \ | ||
35 | eprintf_time(n, var, t, fmt, ##__VA_ARGS__) | ||
36 | |||
37 | #define pr_oe_time(t, fmt, ...) pr_time_N(1, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
38 | #define pr_oe_time2(t, fmt, ...) pr_time_N(2, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
39 | |||
40 | #define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ | ||
41 | |||
32 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 42 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
33 | void trace_event(union perf_event *event); | 43 | void trace_event(union perf_event *event); |
34 | 44 | ||
@@ -38,6 +48,7 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | |||
38 | void pr_stat(const char *fmt, ...); | 48 | void pr_stat(const char *fmt, ...); |
39 | 49 | ||
40 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); | 50 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); |
51 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5))); | ||
41 | 52 | ||
42 | int perf_debug_option(const char *str); | 53 | int perf_debug_option(const char *str); |
43 | 54 | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 90d02c661dd4..45be944d450a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso) | |||
21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | 21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', |
22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | 22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', |
23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | 23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', |
24 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm', | ||
24 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | 25 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', |
25 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | 26 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', |
27 | [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M', | ||
26 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | 28 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', |
27 | }; | 29 | }; |
28 | 30 | ||
@@ -37,6 +39,7 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
37 | { | 39 | { |
38 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 40 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
39 | int ret = 0; | 41 | int ret = 0; |
42 | size_t len; | ||
40 | 43 | ||
41 | switch (type) { | 44 | switch (type) { |
42 | case DSO_BINARY_TYPE__DEBUGLINK: { | 45 | case DSO_BINARY_TYPE__DEBUGLINK: { |
@@ -60,26 +63,25 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
60 | break; | 63 | break; |
61 | 64 | ||
62 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | 65 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: |
63 | snprintf(filename, size, "%s/usr/lib/debug%s.debug", | 66 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
64 | symbol_conf.symfs, dso->long_name); | 67 | snprintf(filename + len, size - len, "%s.debug", dso->long_name); |
65 | break; | 68 | break; |
66 | 69 | ||
67 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | 70 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: |
68 | snprintf(filename, size, "%s/usr/lib/debug%s", | 71 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
69 | symbol_conf.symfs, dso->long_name); | 72 | snprintf(filename + len, size - len, "%s", dso->long_name); |
70 | break; | 73 | break; |
71 | 74 | ||
72 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: | 75 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: |
73 | { | 76 | { |
74 | const char *last_slash; | 77 | const char *last_slash; |
75 | size_t len; | ||
76 | size_t dir_size; | 78 | size_t dir_size; |
77 | 79 | ||
78 | last_slash = dso->long_name + dso->long_name_len; | 80 | last_slash = dso->long_name + dso->long_name_len; |
79 | while (last_slash != dso->long_name && *last_slash != '/') | 81 | while (last_slash != dso->long_name && *last_slash != '/') |
80 | last_slash--; | 82 | last_slash--; |
81 | 83 | ||
82 | len = scnprintf(filename, size, "%s", symbol_conf.symfs); | 84 | len = __symbol__join_symfs(filename, size, ""); |
83 | dir_size = last_slash - dso->long_name + 2; | 85 | dir_size = last_slash - dso->long_name + 2; |
84 | if (dir_size > (size - len)) { | 86 | if (dir_size > (size - len)) { |
85 | ret = -1; | 87 | ret = -1; |
@@ -100,26 +102,26 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
100 | build_id__sprintf(dso->build_id, | 102 | build_id__sprintf(dso->build_id, |
101 | sizeof(dso->build_id), | 103 | sizeof(dso->build_id), |
102 | build_id_hex); | 104 | build_id_hex); |
103 | snprintf(filename, size, | 105 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); |
104 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | 106 | snprintf(filename + len, size - len, "%.2s/%s.debug", |
105 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 107 | build_id_hex, build_id_hex + 2); |
106 | break; | 108 | break; |
107 | 109 | ||
108 | case DSO_BINARY_TYPE__VMLINUX: | 110 | case DSO_BINARY_TYPE__VMLINUX: |
109 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | 111 | case DSO_BINARY_TYPE__GUEST_VMLINUX: |
110 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | 112 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: |
111 | snprintf(filename, size, "%s%s", | 113 | __symbol__join_symfs(filename, size, dso->long_name); |
112 | symbol_conf.symfs, dso->long_name); | ||
113 | break; | 114 | break; |
114 | 115 | ||
115 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 116 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
116 | snprintf(filename, size, "%s%s%s", symbol_conf.symfs, | 117 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: |
117 | root_dir, dso->long_name); | 118 | path__join3(filename, size, symbol_conf.symfs, |
119 | root_dir, dso->long_name); | ||
118 | break; | 120 | break; |
119 | 121 | ||
120 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 122 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
121 | snprintf(filename, size, "%s%s", symbol_conf.symfs, | 123 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: |
122 | dso->long_name); | 124 | __symbol__join_symfs(filename, size, dso->long_name); |
123 | break; | 125 | break; |
124 | 126 | ||
125 | case DSO_BINARY_TYPE__KCORE: | 127 | case DSO_BINARY_TYPE__KCORE: |
@@ -139,6 +141,73 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
139 | return ret; | 141 | return ret; |
140 | } | 142 | } |
141 | 143 | ||
144 | static const struct { | ||
145 | const char *fmt; | ||
146 | int (*decompress)(const char *input, int output); | ||
147 | } compressions[] = { | ||
148 | #ifdef HAVE_ZLIB_SUPPORT | ||
149 | { "gz", gzip_decompress_to_file }, | ||
150 | #endif | ||
151 | { NULL, NULL }, | ||
152 | }; | ||
153 | |||
154 | bool is_supported_compression(const char *ext) | ||
155 | { | ||
156 | unsigned i; | ||
157 | |||
158 | for (i = 0; compressions[i].fmt; i++) { | ||
159 | if (!strcmp(ext, compressions[i].fmt)) | ||
160 | return true; | ||
161 | } | ||
162 | return false; | ||
163 | } | ||
164 | |||
165 | bool is_kmodule_extension(const char *ext) | ||
166 | { | ||
167 | if (strncmp(ext, "ko", 2)) | ||
168 | return false; | ||
169 | |||
170 | if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3))) | ||
171 | return true; | ||
172 | |||
173 | return false; | ||
174 | } | ||
175 | |||
176 | bool is_kernel_module(const char *pathname, bool *compressed) | ||
177 | { | ||
178 | const char *ext = strrchr(pathname, '.'); | ||
179 | |||
180 | if (ext == NULL) | ||
181 | return false; | ||
182 | |||
183 | if (is_supported_compression(ext + 1)) { | ||
184 | if (compressed) | ||
185 | *compressed = true; | ||
186 | ext -= 3; | ||
187 | } else if (compressed) | ||
188 | *compressed = false; | ||
189 | |||
190 | return is_kmodule_extension(ext + 1); | ||
191 | } | ||
192 | |||
193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd) | ||
194 | { | ||
195 | unsigned i; | ||
196 | |||
197 | for (i = 0; compressions[i].fmt; i++) { | ||
198 | if (!strcmp(ext, compressions[i].fmt)) | ||
199 | return !compressions[i].decompress(filename, | ||
200 | output_fd); | ||
201 | } | ||
202 | return false; | ||
203 | } | ||
204 | |||
205 | bool dso__needs_decompress(struct dso *dso) | ||
206 | { | ||
207 | return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || | ||
208 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
209 | } | ||
210 | |||
142 | /* | 211 | /* |
143 | * Global list of open DSOs and the counter. | 212 | * Global list of open DSOs and the counter. |
144 | */ | 213 | */ |
@@ -164,13 +233,15 @@ static void close_first_dso(void); | |||
164 | static int do_open(char *name) | 233 | static int do_open(char *name) |
165 | { | 234 | { |
166 | int fd; | 235 | int fd; |
236 | char sbuf[STRERR_BUFSIZE]; | ||
167 | 237 | ||
168 | do { | 238 | do { |
169 | fd = open(name, O_RDONLY); | 239 | fd = open(name, O_RDONLY); |
170 | if (fd >= 0) | 240 | if (fd >= 0) |
171 | return fd; | 241 | return fd; |
172 | 242 | ||
173 | pr_debug("dso open failed, mmap: %s\n", strerror(errno)); | 243 | pr_debug("dso open failed, mmap: %s\n", |
244 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
174 | if (!dso__data_open_cnt || errno != EMFILE) | 245 | if (!dso__data_open_cnt || errno != EMFILE) |
175 | break; | 246 | break; |
176 | 247 | ||
@@ -532,10 +603,12 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | |||
532 | static int data_file_size(struct dso *dso) | 603 | static int data_file_size(struct dso *dso) |
533 | { | 604 | { |
534 | struct stat st; | 605 | struct stat st; |
606 | char sbuf[STRERR_BUFSIZE]; | ||
535 | 607 | ||
536 | if (!dso->data.file_size) { | 608 | if (!dso->data.file_size) { |
537 | if (fstat(dso->data.fd, &st)) { | 609 | if (fstat(dso->data.fd, &st)) { |
538 | pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); | 610 | pr_err("dso mmap failed, fstat: %s\n", |
611 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
539 | return -1; | 612 | return -1; |
540 | } | 613 | } |
541 | dso->data.file_size = st.st_size; | 614 | dso->data.file_size = st.st_size; |
@@ -651,6 +724,65 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | |||
651 | return dso; | 724 | return dso; |
652 | } | 725 | } |
653 | 726 | ||
727 | /* | ||
728 | * Find a matching entry and/or link current entry to RB tree. | ||
729 | * Either one of the dso or name parameter must be non-NULL or the | ||
730 | * function will not work. | ||
731 | */ | ||
732 | static struct dso *dso__findlink_by_longname(struct rb_root *root, | ||
733 | struct dso *dso, const char *name) | ||
734 | { | ||
735 | struct rb_node **p = &root->rb_node; | ||
736 | struct rb_node *parent = NULL; | ||
737 | |||
738 | if (!name) | ||
739 | name = dso->long_name; | ||
740 | /* | ||
741 | * Find node with the matching name | ||
742 | */ | ||
743 | while (*p) { | ||
744 | struct dso *this = rb_entry(*p, struct dso, rb_node); | ||
745 | int rc = strcmp(name, this->long_name); | ||
746 | |||
747 | parent = *p; | ||
748 | if (rc == 0) { | ||
749 | /* | ||
750 | * In case the new DSO is a duplicate of an existing | ||
751 | * one, print an one-time warning & put the new entry | ||
752 | * at the end of the list of duplicates. | ||
753 | */ | ||
754 | if (!dso || (dso == this)) | ||
755 | return this; /* Find matching dso */ | ||
756 | /* | ||
757 | * The core kernel DSOs may have duplicated long name. | ||
758 | * In this case, the short name should be different. | ||
759 | * Comparing the short names to differentiate the DSOs. | ||
760 | */ | ||
761 | rc = strcmp(dso->short_name, this->short_name); | ||
762 | if (rc == 0) { | ||
763 | pr_err("Duplicated dso name: %s\n", name); | ||
764 | return NULL; | ||
765 | } | ||
766 | } | ||
767 | if (rc < 0) | ||
768 | p = &parent->rb_left; | ||
769 | else | ||
770 | p = &parent->rb_right; | ||
771 | } | ||
772 | if (dso) { | ||
773 | /* Add new node and rebalance tree */ | ||
774 | rb_link_node(&dso->rb_node, parent, p); | ||
775 | rb_insert_color(&dso->rb_node, root); | ||
776 | } | ||
777 | return NULL; | ||
778 | } | ||
779 | |||
780 | static inline struct dso * | ||
781 | dso__find_by_longname(const struct rb_root *root, const char *name) | ||
782 | { | ||
783 | return dso__findlink_by_longname((struct rb_root *)root, NULL, name); | ||
784 | } | ||
785 | |||
654 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) | 786 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) |
655 | { | 787 | { |
656 | if (name == NULL) | 788 | if (name == NULL) |
@@ -753,6 +885,7 @@ struct dso *dso__new(const char *name) | |||
753 | dso->a2l_fails = 1; | 885 | dso->a2l_fails = 1; |
754 | dso->kernel = DSO_TYPE_USER; | 886 | dso->kernel = DSO_TYPE_USER; |
755 | dso->needs_swap = DSO_SWAP__UNSET; | 887 | dso->needs_swap = DSO_SWAP__UNSET; |
888 | RB_CLEAR_NODE(&dso->rb_node); | ||
756 | INIT_LIST_HEAD(&dso->node); | 889 | INIT_LIST_HEAD(&dso->node); |
757 | INIT_LIST_HEAD(&dso->data.open_entry); | 890 | INIT_LIST_HEAD(&dso->data.open_entry); |
758 | } | 891 | } |
@@ -763,6 +896,10 @@ struct dso *dso__new(const char *name) | |||
763 | void dso__delete(struct dso *dso) | 896 | void dso__delete(struct dso *dso) |
764 | { | 897 | { |
765 | int i; | 898 | int i; |
899 | |||
900 | if (!RB_EMPTY_NODE(&dso->rb_node)) | ||
901 | pr_err("DSO %s is still in rbtree when being deleted!\n", | ||
902 | dso->long_name); | ||
766 | for (i = 0; i < MAP__NR_TYPES; ++i) | 903 | for (i = 0; i < MAP__NR_TYPES; ++i) |
767 | symbols__delete(&dso->symbols[i]); | 904 | symbols__delete(&dso->symbols[i]); |
768 | 905 | ||
@@ -849,35 +986,34 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
849 | return have_build_id; | 986 | return have_build_id; |
850 | } | 987 | } |
851 | 988 | ||
852 | void dsos__add(struct list_head *head, struct dso *dso) | 989 | void dsos__add(struct dsos *dsos, struct dso *dso) |
853 | { | 990 | { |
854 | list_add_tail(&dso->node, head); | 991 | list_add_tail(&dso->node, &dsos->head); |
992 | dso__findlink_by_longname(&dsos->root, dso, NULL); | ||
855 | } | 993 | } |
856 | 994 | ||
857 | struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short) | 995 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
996 | bool cmp_short) | ||
858 | { | 997 | { |
859 | struct dso *pos; | 998 | struct dso *pos; |
860 | 999 | ||
861 | if (cmp_short) { | 1000 | if (cmp_short) { |
862 | list_for_each_entry(pos, head, node) | 1001 | list_for_each_entry(pos, &dsos->head, node) |
863 | if (strcmp(pos->short_name, name) == 0) | 1002 | if (strcmp(pos->short_name, name) == 0) |
864 | return pos; | 1003 | return pos; |
865 | return NULL; | 1004 | return NULL; |
866 | } | 1005 | } |
867 | list_for_each_entry(pos, head, node) | 1006 | return dso__find_by_longname(&dsos->root, name); |
868 | if (strcmp(pos->long_name, name) == 0) | ||
869 | return pos; | ||
870 | return NULL; | ||
871 | } | 1007 | } |
872 | 1008 | ||
873 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | 1009 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name) |
874 | { | 1010 | { |
875 | struct dso *dso = dsos__find(head, name, false); | 1011 | struct dso *dso = dsos__find(dsos, name, false); |
876 | 1012 | ||
877 | if (!dso) { | 1013 | if (!dso) { |
878 | dso = dso__new(name); | 1014 | dso = dso__new(name); |
879 | if (dso != NULL) { | 1015 | if (dso != NULL) { |
880 | dsos__add(head, dso); | 1016 | dsos__add(dsos, dso); |
881 | dso__set_basename(dso); | 1017 | dso__set_basename(dso); |
882 | } | 1018 | } |
883 | } | 1019 | } |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 5e463c0964d4..3782c82c6e44 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -22,7 +22,9 @@ enum dso_binary_type { | |||
22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
24 | DSO_BINARY_TYPE__GUEST_KMODULE, | 24 | DSO_BINARY_TYPE__GUEST_KMODULE, |
25 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
25 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 26 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
27 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
26 | DSO_BINARY_TYPE__KCORE, | 28 | DSO_BINARY_TYPE__KCORE, |
27 | DSO_BINARY_TYPE__GUEST_KCORE, | 29 | DSO_BINARY_TYPE__GUEST_KCORE, |
28 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 30 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
@@ -90,8 +92,18 @@ struct dso_cache { | |||
90 | char data[0]; | 92 | char data[0]; |
91 | }; | 93 | }; |
92 | 94 | ||
95 | /* | ||
96 | * DSOs are put into both a list for fast iteration and rbtree for fast | ||
97 | * long name lookup. | ||
98 | */ | ||
99 | struct dsos { | ||
100 | struct list_head head; | ||
101 | struct rb_root root; /* rbtree root sorted by long name */ | ||
102 | }; | ||
103 | |||
93 | struct dso { | 104 | struct dso { |
94 | struct list_head node; | 105 | struct list_head node; |
106 | struct rb_node rb_node; /* rbtree node sorted by long name */ | ||
95 | struct rb_root symbols[MAP__NR_TYPES]; | 107 | struct rb_root symbols[MAP__NR_TYPES]; |
96 | struct rb_root symbol_names[MAP__NR_TYPES]; | 108 | struct rb_root symbol_names[MAP__NR_TYPES]; |
97 | void *a2l; | 109 | void *a2l; |
@@ -117,6 +129,7 @@ struct dso { | |||
117 | const char *long_name; | 129 | const char *long_name; |
118 | u16 long_name_len; | 130 | u16 long_name_len; |
119 | u16 short_name_len; | 131 | u16 short_name_len; |
132 | void *dwfl; /* DWARF debug info */ | ||
120 | 133 | ||
121 | /* dso data file */ | 134 | /* dso data file */ |
122 | struct { | 135 | struct { |
@@ -128,6 +141,11 @@ struct dso { | |||
128 | struct list_head open_entry; | 141 | struct list_head open_entry; |
129 | } data; | 142 | } data; |
130 | 143 | ||
144 | union { /* Tool specific area */ | ||
145 | void *priv; | ||
146 | u64 db_id; | ||
147 | }; | ||
148 | |||
131 | char name[0]; | 149 | char name[0]; |
132 | }; | 150 | }; |
133 | 151 | ||
@@ -169,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); | |||
169 | char dso__symtab_origin(const struct dso *dso); | 187 | char dso__symtab_origin(const struct dso *dso); |
170 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 188 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
171 | char *root_dir, char *filename, size_t size); | 189 | char *root_dir, char *filename, size_t size); |
190 | bool is_supported_compression(const char *ext); | ||
191 | bool is_kmodule_extension(const char *ext); | ||
192 | bool is_kernel_module(const char *pathname, bool *compressed); | ||
193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); | ||
194 | bool dso__needs_decompress(struct dso *dso); | ||
172 | 195 | ||
173 | /* | 196 | /* |
174 | * The dso__data_* external interface provides following functions: | 197 | * The dso__data_* external interface provides following functions: |
@@ -224,10 +247,10 @@ struct map *dso__new_map(const char *name); | |||
224 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | 247 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, |
225 | const char *short_name, int dso_type); | 248 | const char *short_name, int dso_type); |
226 | 249 | ||
227 | void dsos__add(struct list_head *head, struct dso *dso); | 250 | void dsos__add(struct dsos *dsos, struct dso *dso); |
228 | struct dso *dsos__find(const struct list_head *head, const char *name, | 251 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
229 | bool cmp_short); | 252 | bool cmp_short); |
230 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | 253 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name); |
231 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | 254 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); |
232 | 255 | ||
233 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 256 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1398c83d896d..6c6d044e959a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = { | |||
28 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 28 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
29 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | 29 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", |
30 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 30 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
31 | [PERF_RECORD_ID_INDEX] = "ID_INDEX", | ||
31 | }; | 32 | }; |
32 | 33 | ||
33 | const char *perf_event__name(unsigned int id) | 34 | const char *perf_event__name(unsigned int id) |
@@ -558,13 +559,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
558 | struct map *map; | 559 | struct map *map; |
559 | struct kmap *kmap; | 560 | struct kmap *kmap; |
560 | int err; | 561 | int err; |
562 | union perf_event *event; | ||
563 | |||
564 | if (machine->vmlinux_maps[0] == NULL) | ||
565 | return -1; | ||
566 | |||
561 | /* | 567 | /* |
562 | * We should get this from /sys/kernel/sections/.text, but till that is | 568 | * We should get this from /sys/kernel/sections/.text, but till that is |
563 | * available use this, and after it is use this as a fallback for older | 569 | * available use this, and after it is use this as a fallback for older |
564 | * kernels. | 570 | * kernels. |
565 | */ | 571 | */ |
566 | union perf_event *event = zalloc((sizeof(event->mmap) + | 572 | event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); |
567 | machine->id_hdr_size)); | ||
568 | if (event == NULL) { | 573 | if (event == NULL) { |
569 | pr_debug("Not enough memory synthesizing mmap event " | 574 | pr_debug("Not enough memory synthesizing mmap event " |
570 | "for kernel modules\n"); | 575 | "for kernel modules\n"); |
@@ -726,12 +731,12 @@ int perf_event__process(struct perf_tool *tool __maybe_unused, | |||
726 | return machine__process_event(machine, event, sample); | 731 | return machine__process_event(machine, event, sample); |
727 | } | 732 | } |
728 | 733 | ||
729 | void thread__find_addr_map(struct thread *thread, | 734 | void thread__find_addr_map(struct thread *thread, u8 cpumode, |
730 | struct machine *machine, u8 cpumode, | ||
731 | enum map_type type, u64 addr, | 735 | enum map_type type, u64 addr, |
732 | struct addr_location *al) | 736 | struct addr_location *al) |
733 | { | 737 | { |
734 | struct map_groups *mg = thread->mg; | 738 | struct map_groups *mg = thread->mg; |
739 | struct machine *machine = mg->machine; | ||
735 | bool load_map = false; | 740 | bool load_map = false; |
736 | 741 | ||
737 | al->machine = machine; | 742 | al->machine = machine; |
@@ -784,9 +789,9 @@ try_again: | |||
784 | * "[vdso]" dso, but for now lets use the old trick of looking | 789 | * "[vdso]" dso, but for now lets use the old trick of looking |
785 | * in the whole kernel symbol list. | 790 | * in the whole kernel symbol list. |
786 | */ | 791 | */ |
787 | if ((long long)al->addr < 0 && | 792 | if (cpumode == PERF_RECORD_MISC_USER && machine && |
788 | cpumode == PERF_RECORD_MISC_USER && | 793 | mg != &machine->kmaps && |
789 | machine && mg != &machine->kmaps) { | 794 | machine__kernel_ip(machine, al->addr)) { |
790 | mg = &machine->kmaps; | 795 | mg = &machine->kmaps; |
791 | load_map = true; | 796 | load_map = true; |
792 | goto try_again; | 797 | goto try_again; |
@@ -802,14 +807,14 @@ try_again: | |||
802 | } | 807 | } |
803 | } | 808 | } |
804 | 809 | ||
805 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 810 | void thread__find_addr_location(struct thread *thread, |
806 | u8 cpumode, enum map_type type, u64 addr, | 811 | u8 cpumode, enum map_type type, u64 addr, |
807 | struct addr_location *al) | 812 | struct addr_location *al) |
808 | { | 813 | { |
809 | thread__find_addr_map(thread, machine, cpumode, type, addr, al); | 814 | thread__find_addr_map(thread, cpumode, type, addr, al); |
810 | if (al->map != NULL) | 815 | if (al->map != NULL) |
811 | al->sym = map__find_symbol(al->map, al->addr, | 816 | al->sym = map__find_symbol(al->map, al->addr, |
812 | machine->symbol_filter); | 817 | thread->mg->machine->symbol_filter); |
813 | else | 818 | else |
814 | al->sym = NULL; | 819 | al->sym = NULL; |
815 | } | 820 | } |
@@ -838,8 +843,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
838 | machine->vmlinux_maps[MAP__FUNCTION] == NULL) | 843 | machine->vmlinux_maps[MAP__FUNCTION] == NULL) |
839 | machine__create_kernel_maps(machine); | 844 | machine__create_kernel_maps(machine); |
840 | 845 | ||
841 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 846 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al); |
842 | sample->ip, al); | ||
843 | dump_printf(" ...... dso: %s\n", | 847 | dump_printf(" ...... dso: %s\n", |
844 | al->map ? al->map->dso->long_name : | 848 | al->map ? al->map->dso->long_name : |
845 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 849 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
@@ -898,16 +902,14 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr) | |||
898 | 902 | ||
899 | void perf_event__preprocess_sample_addr(union perf_event *event, | 903 | void perf_event__preprocess_sample_addr(union perf_event *event, |
900 | struct perf_sample *sample, | 904 | struct perf_sample *sample, |
901 | struct machine *machine, | ||
902 | struct thread *thread, | 905 | struct thread *thread, |
903 | struct addr_location *al) | 906 | struct addr_location *al) |
904 | { | 907 | { |
905 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 908 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
906 | 909 | ||
907 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 910 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al); |
908 | sample->addr, al); | ||
909 | if (!al->map) | 911 | if (!al->map) |
910 | thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE, | 912 | thread__find_addr_map(thread, cpumode, MAP__VARIABLE, |
911 | sample->addr, al); | 913 | sample->addr, al); |
912 | 914 | ||
913 | al->cpu = sample->cpu; | 915 | al->cpu = sample->cpu; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 94d6976180da..c4ffe2bd0738 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -143,6 +143,32 @@ struct branch_stack { | |||
143 | struct branch_entry entries[0]; | 143 | struct branch_entry entries[0]; |
144 | }; | 144 | }; |
145 | 145 | ||
146 | enum { | ||
147 | PERF_IP_FLAG_BRANCH = 1ULL << 0, | ||
148 | PERF_IP_FLAG_CALL = 1ULL << 1, | ||
149 | PERF_IP_FLAG_RETURN = 1ULL << 2, | ||
150 | PERF_IP_FLAG_CONDITIONAL = 1ULL << 3, | ||
151 | PERF_IP_FLAG_SYSCALLRET = 1ULL << 4, | ||
152 | PERF_IP_FLAG_ASYNC = 1ULL << 5, | ||
153 | PERF_IP_FLAG_INTERRUPT = 1ULL << 6, | ||
154 | PERF_IP_FLAG_TX_ABORT = 1ULL << 7, | ||
155 | PERF_IP_FLAG_TRACE_BEGIN = 1ULL << 8, | ||
156 | PERF_IP_FLAG_TRACE_END = 1ULL << 9, | ||
157 | PERF_IP_FLAG_IN_TX = 1ULL << 10, | ||
158 | }; | ||
159 | |||
160 | #define PERF_BRANCH_MASK (\ | ||
161 | PERF_IP_FLAG_BRANCH |\ | ||
162 | PERF_IP_FLAG_CALL |\ | ||
163 | PERF_IP_FLAG_RETURN |\ | ||
164 | PERF_IP_FLAG_CONDITIONAL |\ | ||
165 | PERF_IP_FLAG_SYSCALLRET |\ | ||
166 | PERF_IP_FLAG_ASYNC |\ | ||
167 | PERF_IP_FLAG_INTERRUPT |\ | ||
168 | PERF_IP_FLAG_TX_ABORT |\ | ||
169 | PERF_IP_FLAG_TRACE_BEGIN |\ | ||
170 | PERF_IP_FLAG_TRACE_END) | ||
171 | |||
146 | struct perf_sample { | 172 | struct perf_sample { |
147 | u64 ip; | 173 | u64 ip; |
148 | u32 pid, tid; | 174 | u32 pid, tid; |
@@ -156,10 +182,13 @@ struct perf_sample { | |||
156 | u32 cpu; | 182 | u32 cpu; |
157 | u32 raw_size; | 183 | u32 raw_size; |
158 | u64 data_src; | 184 | u64 data_src; |
185 | u32 flags; | ||
186 | u16 insn_len; | ||
159 | void *raw_data; | 187 | void *raw_data; |
160 | struct ip_callchain *callchain; | 188 | struct ip_callchain *callchain; |
161 | struct branch_stack *branch_stack; | 189 | struct branch_stack *branch_stack; |
162 | struct regs_dump user_regs; | 190 | struct regs_dump user_regs; |
191 | struct regs_dump intr_regs; | ||
163 | struct stack_dump user_stack; | 192 | struct stack_dump user_stack; |
164 | struct sample_read read; | 193 | struct sample_read read; |
165 | }; | 194 | }; |
@@ -185,9 +214,37 @@ enum perf_user_event_type { /* above any possible kernel type */ | |||
185 | PERF_RECORD_HEADER_TRACING_DATA = 66, | 214 | PERF_RECORD_HEADER_TRACING_DATA = 66, |
186 | PERF_RECORD_HEADER_BUILD_ID = 67, | 215 | PERF_RECORD_HEADER_BUILD_ID = 67, |
187 | PERF_RECORD_FINISHED_ROUND = 68, | 216 | PERF_RECORD_FINISHED_ROUND = 68, |
217 | PERF_RECORD_ID_INDEX = 69, | ||
188 | PERF_RECORD_HEADER_MAX | 218 | PERF_RECORD_HEADER_MAX |
189 | }; | 219 | }; |
190 | 220 | ||
221 | /* | ||
222 | * 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 | ||
224 | * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while | ||
225 | * total_lost tells exactly how many events the kernel in fact lost, i.e. it is | ||
226 | * the sum of all struct lost_event.lost fields reported. | ||
227 | * | ||
228 | * The total_period is needed because by default auto-freq is used, so | ||
229 | * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get | ||
230 | * the total number of low level events, it is necessary to to sum all struct | ||
231 | * sample_event.period and stash the result in total_period. | ||
232 | */ | ||
233 | struct events_stats { | ||
234 | u64 total_period; | ||
235 | u64 total_non_filtered_period; | ||
236 | u64 total_lost; | ||
237 | u64 total_invalid_chains; | ||
238 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | ||
239 | u32 nr_non_filtered_samples; | ||
240 | u32 nr_lost_warned; | ||
241 | u32 nr_unknown_events; | ||
242 | u32 nr_invalid_chains; | ||
243 | u32 nr_unknown_id; | ||
244 | u32 nr_unprocessable_samples; | ||
245 | u32 nr_unordered_events; | ||
246 | }; | ||
247 | |||
191 | struct attr_event { | 248 | struct attr_event { |
192 | struct perf_event_header header; | 249 | struct perf_event_header header; |
193 | struct perf_event_attr attr; | 250 | struct perf_event_attr attr; |
@@ -211,6 +268,19 @@ struct tracing_data_event { | |||
211 | u32 size; | 268 | u32 size; |
212 | }; | 269 | }; |
213 | 270 | ||
271 | struct id_index_entry { | ||
272 | u64 id; | ||
273 | u64 idx; | ||
274 | u64 cpu; | ||
275 | u64 tid; | ||
276 | }; | ||
277 | |||
278 | struct id_index_event { | ||
279 | struct perf_event_header header; | ||
280 | u64 nr; | ||
281 | struct id_index_entry entries[0]; | ||
282 | }; | ||
283 | |||
214 | union perf_event { | 284 | union perf_event { |
215 | struct perf_event_header header; | 285 | struct perf_event_header header; |
216 | struct mmap_event mmap; | 286 | struct mmap_event mmap; |
@@ -225,6 +295,7 @@ union perf_event { | |||
225 | struct event_type_event event_type; | 295 | struct event_type_event event_type; |
226 | struct tracing_data_event tracing_data; | 296 | struct tracing_data_event tracing_data; |
227 | struct build_id_event build_id; | 297 | struct build_id_event build_id; |
298 | struct id_index_event id_index; | ||
228 | }; | 299 | }; |
229 | 300 | ||
230 | void perf_event__print_totals(void); | 301 | void perf_event__print_totals(void); |
@@ -294,7 +365,6 @@ bool is_bts_event(struct perf_event_attr *attr); | |||
294 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); | 365 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); |
295 | void perf_event__preprocess_sample_addr(union perf_event *event, | 366 | void perf_event__preprocess_sample_addr(union perf_event *event, |
296 | struct perf_sample *sample, | 367 | struct perf_sample *sample, |
297 | struct machine *machine, | ||
298 | struct thread *thread, | 368 | struct thread *thread, |
299 | struct addr_location *al); | 369 | struct addr_location *al); |
300 | 370 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 814e954c1318..cbab1fb77b1d 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -8,6 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include <api/fs/debugfs.h> | 10 | #include <api/fs/debugfs.h> |
11 | #include <api/fs/fs.h> | ||
11 | #include <poll.h> | 12 | #include <poll.h> |
12 | #include "cpumap.h" | 13 | #include "cpumap.h" |
13 | #include "thread_map.h" | 14 | #include "thread_map.h" |
@@ -24,6 +25,10 @@ | |||
24 | 25 | ||
25 | #include <linux/bitops.h> | 26 | #include <linux/bitops.h> |
26 | #include <linux/hash.h> | 27 | #include <linux/hash.h> |
28 | #include <linux/log2.h> | ||
29 | |||
30 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx); | ||
31 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx); | ||
27 | 32 | ||
28 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
29 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | 34 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
@@ -37,6 +42,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
37 | INIT_HLIST_HEAD(&evlist->heads[i]); | 42 | INIT_HLIST_HEAD(&evlist->heads[i]); |
38 | INIT_LIST_HEAD(&evlist->entries); | 43 | INIT_LIST_HEAD(&evlist->entries); |
39 | perf_evlist__set_maps(evlist, cpus, threads); | 44 | perf_evlist__set_maps(evlist, cpus, threads); |
45 | fdarray__init(&evlist->pollfd, 64); | ||
40 | evlist->workload.pid = -1; | 46 | evlist->workload.pid = -1; |
41 | } | 47 | } |
42 | 48 | ||
@@ -102,7 +108,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist) | |||
102 | void perf_evlist__exit(struct perf_evlist *evlist) | 108 | void perf_evlist__exit(struct perf_evlist *evlist) |
103 | { | 109 | { |
104 | zfree(&evlist->mmap); | 110 | zfree(&evlist->mmap); |
105 | zfree(&evlist->pollfd); | 111 | fdarray__exit(&evlist->pollfd); |
106 | } | 112 | } |
107 | 113 | ||
108 | void perf_evlist__delete(struct perf_evlist *evlist) | 114 | void perf_evlist__delete(struct perf_evlist *evlist) |
@@ -122,6 +128,7 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
122 | { | 128 | { |
123 | list_add_tail(&entry->node, &evlist->entries); | 129 | list_add_tail(&entry->node, &evlist->entries); |
124 | entry->idx = evlist->nr_entries; | 130 | entry->idx = evlist->nr_entries; |
131 | entry->tracking = !entry->idx; | ||
125 | 132 | ||
126 | if (!evlist->nr_entries++) | 133 | if (!evlist->nr_entries++) |
127 | perf_evlist__set_id_pos(evlist); | 134 | perf_evlist__set_id_pos(evlist); |
@@ -265,17 +272,27 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, | |||
265 | return 0; | 272 | return 0; |
266 | } | 273 | } |
267 | 274 | ||
275 | static int perf_evlist__nr_threads(struct perf_evlist *evlist, | ||
276 | struct perf_evsel *evsel) | ||
277 | { | ||
278 | if (evsel->system_wide) | ||
279 | return 1; | ||
280 | else | ||
281 | return thread_map__nr(evlist->threads); | ||
282 | } | ||
283 | |||
268 | void perf_evlist__disable(struct perf_evlist *evlist) | 284 | void perf_evlist__disable(struct perf_evlist *evlist) |
269 | { | 285 | { |
270 | int cpu, thread; | 286 | int cpu, thread; |
271 | struct perf_evsel *pos; | 287 | struct perf_evsel *pos; |
272 | int nr_cpus = cpu_map__nr(evlist->cpus); | 288 | int nr_cpus = cpu_map__nr(evlist->cpus); |
273 | int nr_threads = thread_map__nr(evlist->threads); | 289 | int nr_threads; |
274 | 290 | ||
275 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 291 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
276 | evlist__for_each(evlist, pos) { | 292 | evlist__for_each(evlist, pos) { |
277 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 293 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
278 | continue; | 294 | continue; |
295 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
279 | for (thread = 0; thread < nr_threads; thread++) | 296 | for (thread = 0; thread < nr_threads; thread++) |
280 | ioctl(FD(pos, cpu, thread), | 297 | ioctl(FD(pos, cpu, thread), |
281 | PERF_EVENT_IOC_DISABLE, 0); | 298 | PERF_EVENT_IOC_DISABLE, 0); |
@@ -288,12 +305,13 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
288 | int cpu, thread; | 305 | int cpu, thread; |
289 | struct perf_evsel *pos; | 306 | struct perf_evsel *pos; |
290 | int nr_cpus = cpu_map__nr(evlist->cpus); | 307 | int nr_cpus = cpu_map__nr(evlist->cpus); |
291 | int nr_threads = thread_map__nr(evlist->threads); | 308 | int nr_threads; |
292 | 309 | ||
293 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 310 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
294 | evlist__for_each(evlist, pos) { | 311 | evlist__for_each(evlist, pos) { |
295 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 312 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
296 | continue; | 313 | continue; |
314 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
297 | for (thread = 0; thread < nr_threads; thread++) | 315 | for (thread = 0; thread < nr_threads; thread++) |
298 | ioctl(FD(pos, cpu, thread), | 316 | ioctl(FD(pos, cpu, thread), |
299 | PERF_EVENT_IOC_ENABLE, 0); | 317 | PERF_EVENT_IOC_ENABLE, 0); |
@@ -305,12 +323,14 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
305 | struct perf_evsel *evsel) | 323 | struct perf_evsel *evsel) |
306 | { | 324 | { |
307 | int cpu, thread, err; | 325 | int cpu, thread, err; |
326 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
327 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
308 | 328 | ||
309 | if (!evsel->fd) | 329 | if (!evsel->fd) |
310 | return 0; | 330 | return 0; |
311 | 331 | ||
312 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 332 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
313 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 333 | for (thread = 0; thread < nr_threads; thread++) { |
314 | err = ioctl(FD(evsel, cpu, thread), | 334 | err = ioctl(FD(evsel, cpu, thread), |
315 | PERF_EVENT_IOC_DISABLE, 0); | 335 | PERF_EVENT_IOC_DISABLE, 0); |
316 | if (err) | 336 | if (err) |
@@ -324,12 +344,14 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
324 | struct perf_evsel *evsel) | 344 | struct perf_evsel *evsel) |
325 | { | 345 | { |
326 | int cpu, thread, err; | 346 | int cpu, thread, err; |
347 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
348 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
327 | 349 | ||
328 | if (!evsel->fd) | 350 | if (!evsel->fd) |
329 | return -EINVAL; | 351 | return -EINVAL; |
330 | 352 | ||
331 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 353 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
332 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 354 | for (thread = 0; thread < nr_threads; thread++) { |
333 | err = ioctl(FD(evsel, cpu, thread), | 355 | err = ioctl(FD(evsel, cpu, thread), |
334 | PERF_EVENT_IOC_ENABLE, 0); | 356 | PERF_EVENT_IOC_ENABLE, 0); |
335 | if (err) | 357 | if (err) |
@@ -339,21 +361,111 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
339 | return 0; | 361 | return 0; |
340 | } | 362 | } |
341 | 363 | ||
342 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 364 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
365 | struct perf_evsel *evsel, int cpu) | ||
366 | { | ||
367 | int thread, err; | ||
368 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
369 | |||
370 | if (!evsel->fd) | ||
371 | return -EINVAL; | ||
372 | |||
373 | for (thread = 0; thread < nr_threads; thread++) { | ||
374 | err = ioctl(FD(evsel, cpu, thread), | ||
375 | PERF_EVENT_IOC_ENABLE, 0); | ||
376 | if (err) | ||
377 | return err; | ||
378 | } | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | ||
383 | struct perf_evsel *evsel, | ||
384 | int thread) | ||
385 | { | ||
386 | int cpu, err; | ||
387 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
388 | |||
389 | if (!evsel->fd) | ||
390 | return -EINVAL; | ||
391 | |||
392 | for (cpu = 0; cpu < nr_cpus; cpu++) { | ||
393 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | ||
394 | if (err) | ||
395 | return err; | ||
396 | } | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
401 | struct perf_evsel *evsel, int idx) | ||
402 | { | ||
403 | bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus); | ||
404 | |||
405 | if (per_cpu_mmaps) | ||
406 | return perf_evlist__enable_event_cpu(evlist, evsel, idx); | ||
407 | else | ||
408 | return perf_evlist__enable_event_thread(evlist, evsel, idx); | ||
409 | } | ||
410 | |||
411 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | ||
343 | { | 412 | { |
344 | int nr_cpus = cpu_map__nr(evlist->cpus); | 413 | int nr_cpus = cpu_map__nr(evlist->cpus); |
345 | int nr_threads = thread_map__nr(evlist->threads); | 414 | int nr_threads = thread_map__nr(evlist->threads); |
346 | int nfds = nr_cpus * nr_threads * evlist->nr_entries; | 415 | int nfds = 0; |
347 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 416 | struct perf_evsel *evsel; |
348 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 417 | |
418 | evlist__for_each(evlist, evsel) { | ||
419 | if (evsel->system_wide) | ||
420 | nfds += nr_cpus; | ||
421 | else | ||
422 | nfds += nr_cpus * nr_threads; | ||
423 | } | ||
424 | |||
425 | if (fdarray__available_entries(&evlist->pollfd) < nfds && | ||
426 | fdarray__grow(&evlist->pollfd, nfds) < 0) | ||
427 | return -ENOMEM; | ||
428 | |||
429 | return 0; | ||
349 | } | 430 | } |
350 | 431 | ||
351 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | 432 | static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx) |
352 | { | 433 | { |
353 | fcntl(fd, F_SETFL, O_NONBLOCK); | 434 | int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP); |
354 | evlist->pollfd[evlist->nr_fds].fd = fd; | 435 | /* |
355 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | 436 | * Save the idx so that when we filter out fds POLLHUP'ed we can |
356 | evlist->nr_fds++; | 437 | * close the associated evlist->mmap[] entry. |
438 | */ | ||
439 | if (pos >= 0) { | ||
440 | evlist->pollfd.priv[pos].idx = idx; | ||
441 | |||
442 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
443 | } | ||
444 | |||
445 | return pos; | ||
446 | } | ||
447 | |||
448 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
449 | { | ||
450 | return __perf_evlist__add_pollfd(evlist, fd, -1); | ||
451 | } | ||
452 | |||
453 | static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd) | ||
454 | { | ||
455 | struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd); | ||
456 | |||
457 | perf_evlist__mmap_put(evlist, fda->priv[fd].idx); | ||
458 | } | ||
459 | |||
460 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) | ||
461 | { | ||
462 | return fdarray__filter(&evlist->pollfd, revents_and_mask, | ||
463 | perf_evlist__munmap_filtered); | ||
464 | } | ||
465 | |||
466 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout) | ||
467 | { | ||
468 | return fdarray__poll(&evlist->pollfd, timeout); | ||
357 | } | 469 | } |
358 | 470 | ||
359 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | 471 | static void perf_evlist__id_hash(struct perf_evlist *evlist, |
@@ -417,6 +529,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
417 | return 0; | 529 | return 0; |
418 | } | 530 | } |
419 | 531 | ||
532 | static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, | ||
533 | struct perf_evsel *evsel, int idx, int cpu, | ||
534 | int thread) | ||
535 | { | ||
536 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | ||
537 | sid->idx = idx; | ||
538 | if (evlist->cpus && cpu >= 0) | ||
539 | sid->cpu = evlist->cpus->map[cpu]; | ||
540 | else | ||
541 | sid->cpu = -1; | ||
542 | if (!evsel->system_wide && evlist->threads && thread >= 0) | ||
543 | sid->tid = evlist->threads->map[thread]; | ||
544 | else | ||
545 | sid->tid = -1; | ||
546 | } | ||
547 | |||
420 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) | 548 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) |
421 | { | 549 | { |
422 | struct hlist_head *head; | 550 | struct hlist_head *head; |
@@ -566,14 +694,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
566 | return event; | 694 | return event; |
567 | } | 695 | } |
568 | 696 | ||
697 | static bool perf_mmap__empty(struct perf_mmap *md) | ||
698 | { | ||
699 | return perf_mmap__read_head(md) != md->prev; | ||
700 | } | ||
701 | |||
702 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) | ||
703 | { | ||
704 | ++evlist->mmap[idx].refcnt; | ||
705 | } | ||
706 | |||
707 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx) | ||
708 | { | ||
709 | BUG_ON(evlist->mmap[idx].refcnt == 0); | ||
710 | |||
711 | if (--evlist->mmap[idx].refcnt == 0) | ||
712 | __perf_evlist__munmap(evlist, idx); | ||
713 | } | ||
714 | |||
569 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | 715 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) |
570 | { | 716 | { |
717 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
718 | |||
571 | if (!evlist->overwrite) { | 719 | if (!evlist->overwrite) { |
572 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
573 | unsigned int old = md->prev; | 720 | unsigned int old = md->prev; |
574 | 721 | ||
575 | perf_mmap__write_tail(md, old); | 722 | perf_mmap__write_tail(md, old); |
576 | } | 723 | } |
724 | |||
725 | if (md->refcnt == 1 && perf_mmap__empty(md)) | ||
726 | perf_evlist__mmap_put(evlist, idx); | ||
577 | } | 727 | } |
578 | 728 | ||
579 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 729 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
@@ -581,6 +731,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | |||
581 | if (evlist->mmap[idx].base != NULL) { | 731 | if (evlist->mmap[idx].base != NULL) { |
582 | munmap(evlist->mmap[idx].base, evlist->mmap_len); | 732 | munmap(evlist->mmap[idx].base, evlist->mmap_len); |
583 | evlist->mmap[idx].base = NULL; | 733 | evlist->mmap[idx].base = NULL; |
734 | evlist->mmap[idx].refcnt = 0; | ||
584 | } | 735 | } |
585 | } | 736 | } |
586 | 737 | ||
@@ -614,6 +765,20 @@ struct mmap_params { | |||
614 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | 765 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, |
615 | struct mmap_params *mp, int fd) | 766 | struct mmap_params *mp, int fd) |
616 | { | 767 | { |
768 | /* | ||
769 | * The last one will be done at perf_evlist__mmap_consume(), so that we | ||
770 | * make sure we don't prevent tools from consuming every last event in | ||
771 | * the ring buffer. | ||
772 | * | ||
773 | * I.e. we can get the POLLHUP meaning that the fd doesn't exist | ||
774 | * anymore, but the last events for it are still in the ring buffer, | ||
775 | * waiting to be consumed. | ||
776 | * | ||
777 | * Tools can chose to ignore this at their own discretion, but the | ||
778 | * evlist layer can't just drop it when filtering events in | ||
779 | * perf_evlist__filter_pollfd(). | ||
780 | */ | ||
781 | evlist->mmap[idx].refcnt = 2; | ||
617 | evlist->mmap[idx].prev = 0; | 782 | evlist->mmap[idx].prev = 0; |
618 | evlist->mmap[idx].mask = mp->mask; | 783 | evlist->mmap[idx].mask = mp->mask; |
619 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, | 784 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, |
@@ -625,7 +790,6 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | |||
625 | return -1; | 790 | return -1; |
626 | } | 791 | } |
627 | 792 | ||
628 | perf_evlist__add_pollfd(evlist, fd); | ||
629 | return 0; | 793 | return 0; |
630 | } | 794 | } |
631 | 795 | ||
@@ -636,7 +800,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
636 | struct perf_evsel *evsel; | 800 | struct perf_evsel *evsel; |
637 | 801 | ||
638 | evlist__for_each(evlist, evsel) { | 802 | evlist__for_each(evlist, evsel) { |
639 | int fd = FD(evsel, cpu, thread); | 803 | int fd; |
804 | |||
805 | if (evsel->system_wide && thread) | ||
806 | continue; | ||
807 | |||
808 | fd = FD(evsel, cpu, thread); | ||
640 | 809 | ||
641 | if (*output == -1) { | 810 | if (*output == -1) { |
642 | *output = fd; | 811 | *output = fd; |
@@ -645,11 +814,30 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
645 | } else { | 814 | } else { |
646 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | 815 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) |
647 | return -1; | 816 | return -1; |
817 | |||
818 | perf_evlist__mmap_get(evlist, idx); | ||
648 | } | 819 | } |
649 | 820 | ||
650 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 821 | /* |
651 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | 822 | * The system_wide flag causes a selected event to be opened |
823 | * always without a pid. Consequently it will never get a | ||
824 | * POLLHUP, but it is used for tracking in combination with | ||
825 | * other events, so it should not need to be polled anyway. | ||
826 | * Therefore don't add it for polling. | ||
827 | */ | ||
828 | if (!evsel->system_wide && | ||
829 | __perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | ||
830 | perf_evlist__mmap_put(evlist, idx); | ||
652 | return -1; | 831 | return -1; |
832 | } | ||
833 | |||
834 | if (evsel->attr.read_format & PERF_FORMAT_ID) { | ||
835 | if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, | ||
836 | fd) < 0) | ||
837 | return -1; | ||
838 | perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, | ||
839 | thread); | ||
840 | } | ||
653 | } | 841 | } |
654 | 842 | ||
655 | return 0; | 843 | return 0; |
@@ -706,10 +894,24 @@ out_unmap: | |||
706 | 894 | ||
707 | static size_t perf_evlist__mmap_size(unsigned long pages) | 895 | static size_t perf_evlist__mmap_size(unsigned long pages) |
708 | { | 896 | { |
709 | /* 512 kiB: default amount of unprivileged mlocked memory */ | 897 | if (pages == UINT_MAX) { |
710 | if (pages == UINT_MAX) | 898 | int max; |
711 | pages = (512 * 1024) / page_size; | 899 | |
712 | else if (!is_power_of_2(pages)) | 900 | if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { |
901 | /* | ||
902 | * Pick a once upon a time good value, i.e. things look | ||
903 | * strange since we can't read a sysctl value, but lets not | ||
904 | * die yet... | ||
905 | */ | ||
906 | max = 512; | ||
907 | } else { | ||
908 | max -= (page_size / 1024); | ||
909 | } | ||
910 | |||
911 | pages = (max * 1024) / page_size; | ||
912 | if (!is_power_of_2(pages)) | ||
913 | pages = rounddown_pow_of_two(pages); | ||
914 | } else if (!is_power_of_2(pages)) | ||
713 | return 0; | 915 | return 0; |
714 | 916 | ||
715 | return (pages + 1) * page_size; | 917 | return (pages + 1) * page_size; |
@@ -746,7 +948,7 @@ static long parse_pages_arg(const char *str, unsigned long min, | |||
746 | /* leave number of pages at 0 */ | 948 | /* leave number of pages at 0 */ |
747 | } else if (!is_power_of_2(pages)) { | 949 | } else if (!is_power_of_2(pages)) { |
748 | /* round pages up to next power of 2 */ | 950 | /* round pages up to next power of 2 */ |
749 | pages = next_pow2_l(pages); | 951 | pages = roundup_pow_of_two(pages); |
750 | if (!pages) | 952 | if (!pages) |
751 | return -EINVAL; | 953 | return -EINVAL; |
752 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", | 954 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", |
@@ -804,7 +1006,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
804 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 1006 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
805 | return -ENOMEM; | 1007 | return -ENOMEM; |
806 | 1008 | ||
807 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | 1009 | if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) |
808 | return -ENOMEM; | 1010 | return -ENOMEM; |
809 | 1011 | ||
810 | evlist->overwrite = overwrite; | 1012 | evlist->overwrite = overwrite; |
@@ -845,6 +1047,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) | |||
845 | 1047 | ||
846 | out_delete_threads: | 1048 | out_delete_threads: |
847 | thread_map__delete(evlist->threads); | 1049 | thread_map__delete(evlist->threads); |
1050 | evlist->threads = NULL; | ||
848 | return -1; | 1051 | return -1; |
849 | } | 1052 | } |
850 | 1053 | ||
@@ -1017,11 +1220,51 @@ void perf_evlist__close(struct perf_evlist *evlist) | |||
1017 | } | 1220 | } |
1018 | } | 1221 | } |
1019 | 1222 | ||
1223 | static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist) | ||
1224 | { | ||
1225 | int err = -ENOMEM; | ||
1226 | |||
1227 | /* | ||
1228 | * Try reading /sys/devices/system/cpu/online to get | ||
1229 | * an all cpus map. | ||
1230 | * | ||
1231 | * FIXME: -ENOMEM is the best we can do here, the cpu_map | ||
1232 | * code needs an overhaul to properly forward the | ||
1233 | * error, and we may not want to do that fallback to a | ||
1234 | * default cpu identity map :-\ | ||
1235 | */ | ||
1236 | evlist->cpus = cpu_map__new(NULL); | ||
1237 | if (evlist->cpus == NULL) | ||
1238 | goto out; | ||
1239 | |||
1240 | evlist->threads = thread_map__new_dummy(); | ||
1241 | if (evlist->threads == NULL) | ||
1242 | goto out_free_cpus; | ||
1243 | |||
1244 | err = 0; | ||
1245 | out: | ||
1246 | return err; | ||
1247 | out_free_cpus: | ||
1248 | cpu_map__delete(evlist->cpus); | ||
1249 | evlist->cpus = NULL; | ||
1250 | goto out; | ||
1251 | } | ||
1252 | |||
1020 | int perf_evlist__open(struct perf_evlist *evlist) | 1253 | int perf_evlist__open(struct perf_evlist *evlist) |
1021 | { | 1254 | { |
1022 | struct perf_evsel *evsel; | 1255 | struct perf_evsel *evsel; |
1023 | int err; | 1256 | int err; |
1024 | 1257 | ||
1258 | /* | ||
1259 | * Default: one fd per CPU, all threads, aka systemwide | ||
1260 | * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL | ||
1261 | */ | ||
1262 | if (evlist->threads == NULL && evlist->cpus == NULL) { | ||
1263 | err = perf_evlist__create_syswide_maps(evlist); | ||
1264 | if (err < 0) | ||
1265 | goto out_err; | ||
1266 | } | ||
1267 | |||
1025 | perf_evlist__update_id_pos(evlist); | 1268 | perf_evlist__update_id_pos(evlist); |
1026 | 1269 | ||
1027 | evlist__for_each(evlist, evsel) { | 1270 | evlist__for_each(evlist, evsel) { |
@@ -1061,6 +1304,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
1061 | } | 1304 | } |
1062 | 1305 | ||
1063 | if (!evlist->workload.pid) { | 1306 | if (!evlist->workload.pid) { |
1307 | int ret; | ||
1308 | |||
1064 | if (pipe_output) | 1309 | if (pipe_output) |
1065 | dup2(2, 1); | 1310 | dup2(2, 1); |
1066 | 1311 | ||
@@ -1078,8 +1323,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
1078 | /* | 1323 | /* |
1079 | * Wait until the parent tells us to go. | 1324 | * Wait until the parent tells us to go. |
1080 | */ | 1325 | */ |
1081 | if (read(go_pipe[0], &bf, 1) == -1) | 1326 | ret = read(go_pipe[0], &bf, 1); |
1082 | perror("unable to read pipe"); | 1327 | /* |
1328 | * The parent will ask for the execvp() to be performed by | ||
1329 | * writing exactly one byte, in workload.cork_fd, usually via | ||
1330 | * perf_evlist__start_workload(). | ||
1331 | * | ||
1332 | * For cancelling the workload without actuallin running it, | ||
1333 | * the parent will just close workload.cork_fd, without writing | ||
1334 | * anything, i.e. read will return zero and we just exit() | ||
1335 | * here. | ||
1336 | */ | ||
1337 | if (ret != 1) { | ||
1338 | if (ret == -1) | ||
1339 | perror("unable to read pipe"); | ||
1340 | exit(ret); | ||
1341 | } | ||
1083 | 1342 | ||
1084 | execvp(argv[0], (char **)argv); | 1343 | execvp(argv[0], (char **)argv); |
1085 | 1344 | ||
@@ -1102,8 +1361,14 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
1102 | sigaction(SIGUSR1, &act, NULL); | 1361 | sigaction(SIGUSR1, &act, NULL); |
1103 | } | 1362 | } |
1104 | 1363 | ||
1105 | if (target__none(target)) | 1364 | if (target__none(target)) { |
1365 | if (evlist->threads == NULL) { | ||
1366 | fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n", | ||
1367 | __func__, __LINE__); | ||
1368 | goto out_close_pipes; | ||
1369 | } | ||
1106 | evlist->threads->map[0] = evlist->workload.pid; | 1370 | evlist->threads->map[0] = evlist->workload.pid; |
1371 | } | ||
1107 | 1372 | ||
1108 | close(child_ready_pipe[1]); | 1373 | close(child_ready_pipe[1]); |
1109 | close(go_pipe[0]); | 1374 | close(go_pipe[0]); |
@@ -1202,7 +1467,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
1202 | int err, char *buf, size_t size) | 1467 | int err, char *buf, size_t size) |
1203 | { | 1468 | { |
1204 | int printed, value; | 1469 | int printed, value; |
1205 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1470 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
1206 | 1471 | ||
1207 | switch (err) { | 1472 | switch (err) { |
1208 | case EACCES: | 1473 | case EACCES: |
@@ -1234,6 +1499,37 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
1234 | return 0; | 1499 | return 0; |
1235 | } | 1500 | } |
1236 | 1501 | ||
1502 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size) | ||
1503 | { | ||
1504 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | ||
1505 | int pages_attempted = evlist->mmap_len / 1024, pages_max_per_user, printed = 0; | ||
1506 | |||
1507 | switch (err) { | ||
1508 | case EPERM: | ||
1509 | sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); | ||
1510 | printed += scnprintf(buf + printed, size - printed, | ||
1511 | "Error:\t%s.\n" | ||
1512 | "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" | ||
1513 | "Hint:\tTried using %zd kB.\n", | ||
1514 | emsg, pages_max_per_user, pages_attempted); | ||
1515 | |||
1516 | if (pages_attempted >= pages_max_per_user) { | ||
1517 | printed += scnprintf(buf + printed, size - printed, | ||
1518 | "Hint:\tTry 'sudo sh -c \"echo %d > /proc/sys/kernel/perf_event_mlock_kb\"', or\n", | ||
1519 | pages_max_per_user + pages_attempted); | ||
1520 | } | ||
1521 | |||
1522 | printed += scnprintf(buf + printed, size - printed, | ||
1523 | "Hint:\tTry using a smaller -m/--mmap-pages value."); | ||
1524 | break; | ||
1525 | default: | ||
1526 | scnprintf(buf, size, "%s", emsg); | ||
1527 | break; | ||
1528 | } | ||
1529 | |||
1530 | return 0; | ||
1531 | } | ||
1532 | |||
1237 | void perf_evlist__to_front(struct perf_evlist *evlist, | 1533 | void perf_evlist__to_front(struct perf_evlist *evlist, |
1238 | struct perf_evsel *move_evsel) | 1534 | struct perf_evsel *move_evsel) |
1239 | { | 1535 | { |
@@ -1250,3 +1546,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
1250 | 1546 | ||
1251 | list_splice(&move, &evlist->entries); | 1547 | list_splice(&move, &evlist->entries); |
1252 | } | 1548 | } |
1549 | |||
1550 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
1551 | struct perf_evsel *tracking_evsel) | ||
1552 | { | ||
1553 | struct perf_evsel *evsel; | ||
1554 | |||
1555 | if (tracking_evsel->tracking) | ||
1556 | return; | ||
1557 | |||
1558 | evlist__for_each(evlist, evsel) { | ||
1559 | if (evsel != tracking_evsel) | ||
1560 | evsel->tracking = false; | ||
1561 | } | ||
1562 | |||
1563 | tracking_evsel->tracking = true; | ||
1564 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f5173cd63693..0ba93f67ab94 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_EVLIST_H 1 | 2 | #define __PERF_EVLIST_H 1 |
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include <api/fd/array.h> | ||
5 | #include <stdio.h> | 6 | #include <stdio.h> |
6 | #include "../perf.h" | 7 | #include "../perf.h" |
7 | #include "event.h" | 8 | #include "event.h" |
@@ -17,9 +18,15 @@ struct record_opts; | |||
17 | #define PERF_EVLIST__HLIST_BITS 8 | 18 | #define PERF_EVLIST__HLIST_BITS 8 |
18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 19 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
19 | 20 | ||
21 | /** | ||
22 | * struct perf_mmap - perf's ring buffer mmap details | ||
23 | * | ||
24 | * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this | ||
25 | */ | ||
20 | struct perf_mmap { | 26 | struct perf_mmap { |
21 | void *base; | 27 | void *base; |
22 | int mask; | 28 | int mask; |
29 | int refcnt; | ||
23 | unsigned int prev; | 30 | unsigned int prev; |
24 | char event_copy[PERF_SAMPLE_MAX_SIZE]; | 31 | char event_copy[PERF_SAMPLE_MAX_SIZE]; |
25 | }; | 32 | }; |
@@ -29,7 +36,6 @@ struct perf_evlist { | |||
29 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 36 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
30 | int nr_entries; | 37 | int nr_entries; |
31 | int nr_groups; | 38 | int nr_groups; |
32 | int nr_fds; | ||
33 | int nr_mmaps; | 39 | int nr_mmaps; |
34 | size_t mmap_len; | 40 | size_t mmap_len; |
35 | int id_pos; | 41 | int id_pos; |
@@ -40,8 +46,8 @@ struct perf_evlist { | |||
40 | pid_t pid; | 46 | pid_t pid; |
41 | } workload; | 47 | } workload; |
42 | bool overwrite; | 48 | bool overwrite; |
49 | struct fdarray pollfd; | ||
43 | struct perf_mmap *mmap; | 50 | struct perf_mmap *mmap; |
44 | struct pollfd *pollfd; | ||
45 | struct thread_map *threads; | 51 | struct thread_map *threads; |
46 | struct cpu_map *cpus; | 52 | struct cpu_map *cpus; |
47 | struct perf_evsel *selected; | 53 | struct perf_evsel *selected; |
@@ -82,7 +88,11 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | |||
82 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 88 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
83 | int cpu, int thread, u64 id); | 89 | int cpu, int thread, u64 id); |
84 | 90 | ||
85 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | 91 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); |
92 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | ||
93 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask); | ||
94 | |||
95 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout); | ||
86 | 96 | ||
87 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | 97 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); |
88 | 98 | ||
@@ -107,6 +117,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
107 | void *ucontext)); | 117 | void *ucontext)); |
108 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 118 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
109 | 119 | ||
120 | struct option; | ||
121 | |||
110 | int perf_evlist__parse_mmap_pages(const struct option *opt, | 122 | int perf_evlist__parse_mmap_pages(const struct option *opt, |
111 | const char *str, | 123 | const char *str, |
112 | int unset); | 124 | int unset); |
@@ -122,6 +134,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
122 | struct perf_evsel *evsel); | 134 | struct perf_evsel *evsel); |
123 | int perf_evlist__enable_event(struct perf_evlist *evlist, | 135 | int perf_evlist__enable_event(struct perf_evlist *evlist, |
124 | struct perf_evsel *evsel); | 136 | struct perf_evsel *evsel); |
137 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
138 | struct perf_evsel *evsel, int idx); | ||
125 | 139 | ||
126 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 140 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
127 | struct perf_evsel *evsel); | 141 | struct perf_evsel *evsel); |
@@ -171,6 +185,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | |||
171 | 185 | ||
172 | int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); | 186 | int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); |
173 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); | 187 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); |
188 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
174 | 189 | ||
175 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | 190 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) |
176 | { | 191 | { |
@@ -262,4 +277,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
262 | #define evlist__for_each_safe(evlist, tmp, evsel) \ | 277 | #define evlist__for_each_safe(evlist, tmp, evsel) \ |
263 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) | 278 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) |
264 | 279 | ||
280 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
281 | struct perf_evsel *tracking_evsel); | ||
282 | |||
265 | #endif /* __PERF_EVLIST_H */ | 283 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21a373ebea22..1e90c8557ede 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/perf_event.h> | 15 | #include <linux/perf_event.h> |
16 | #include <sys/resource.h> | 16 | #include <sys/resource.h> |
17 | #include "asm/bug.h" | 17 | #include "asm/bug.h" |
18 | #include "callchain.h" | ||
19 | #include "cgroup.h" | ||
18 | #include "evsel.h" | 20 | #include "evsel.h" |
19 | #include "evlist.h" | 21 | #include "evlist.h" |
20 | #include "util.h" | 22 | #include "util.h" |
@@ -32,6 +34,48 @@ static struct { | |||
32 | bool cloexec; | 34 | bool cloexec; |
33 | } perf_missing_features; | 35 | } perf_missing_features; |
34 | 36 | ||
37 | static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused) | ||
38 | { | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static void perf_evsel__no_extra_fini(struct perf_evsel *evsel __maybe_unused) | ||
43 | { | ||
44 | } | ||
45 | |||
46 | static struct { | ||
47 | size_t size; | ||
48 | int (*init)(struct perf_evsel *evsel); | ||
49 | void (*fini)(struct perf_evsel *evsel); | ||
50 | } perf_evsel__object = { | ||
51 | .size = sizeof(struct perf_evsel), | ||
52 | .init = perf_evsel__no_extra_init, | ||
53 | .fini = perf_evsel__no_extra_fini, | ||
54 | }; | ||
55 | |||
56 | int perf_evsel__object_config(size_t object_size, | ||
57 | int (*init)(struct perf_evsel *evsel), | ||
58 | void (*fini)(struct perf_evsel *evsel)) | ||
59 | { | ||
60 | |||
61 | if (object_size == 0) | ||
62 | goto set_methods; | ||
63 | |||
64 | if (perf_evsel__object.size > object_size) | ||
65 | return -EINVAL; | ||
66 | |||
67 | perf_evsel__object.size = object_size; | ||
68 | |||
69 | set_methods: | ||
70 | if (init != NULL) | ||
71 | perf_evsel__object.init = init; | ||
72 | |||
73 | if (fini != NULL) | ||
74 | perf_evsel__object.fini = fini; | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
35 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 79 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
36 | 80 | ||
37 | int __perf_evsel__sample_size(u64 sample_type) | 81 | int __perf_evsel__sample_size(u64 sample_type) |
@@ -116,16 +160,6 @@ void perf_evsel__calc_id_pos(struct perf_evsel *evsel) | |||
116 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); | 160 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); |
117 | } | 161 | } |
118 | 162 | ||
119 | void hists__init(struct hists *hists) | ||
120 | { | ||
121 | memset(hists, 0, sizeof(*hists)); | ||
122 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
123 | hists->entries_in = &hists->entries_in_array[0]; | ||
124 | hists->entries_collapsed = RB_ROOT; | ||
125 | hists->entries = RB_ROOT; | ||
126 | pthread_mutex_init(&hists->lock, NULL); | ||
127 | } | ||
128 | |||
129 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | 163 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, |
130 | enum perf_event_sample_format bit) | 164 | enum perf_event_sample_format bit) |
131 | { | 165 | { |
@@ -162,19 +196,20 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
162 | struct perf_event_attr *attr, int idx) | 196 | struct perf_event_attr *attr, int idx) |
163 | { | 197 | { |
164 | evsel->idx = idx; | 198 | evsel->idx = idx; |
199 | evsel->tracking = !idx; | ||
165 | evsel->attr = *attr; | 200 | evsel->attr = *attr; |
166 | evsel->leader = evsel; | 201 | evsel->leader = evsel; |
167 | evsel->unit = ""; | 202 | evsel->unit = ""; |
168 | evsel->scale = 1.0; | 203 | evsel->scale = 1.0; |
169 | INIT_LIST_HEAD(&evsel->node); | 204 | INIT_LIST_HEAD(&evsel->node); |
170 | hists__init(&evsel->hists); | 205 | perf_evsel__object.init(evsel); |
171 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 206 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
172 | perf_evsel__calc_id_pos(evsel); | 207 | perf_evsel__calc_id_pos(evsel); |
173 | } | 208 | } |
174 | 209 | ||
175 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | 210 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) |
176 | { | 211 | { |
177 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 212 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); |
178 | 213 | ||
179 | if (evsel != NULL) | 214 | if (evsel != NULL) |
180 | perf_evsel__init(evsel, attr, idx); | 215 | perf_evsel__init(evsel, attr, idx); |
@@ -184,7 +219,7 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
184 | 219 | ||
185 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) | 220 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) |
186 | { | 221 | { |
187 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 222 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); |
188 | 223 | ||
189 | if (evsel != NULL) { | 224 | if (evsel != NULL) { |
190 | struct perf_event_attr attr = { | 225 | struct perf_event_attr attr = { |
@@ -502,20 +537,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
502 | } | 537 | } |
503 | 538 | ||
504 | static void | 539 | static void |
505 | perf_evsel__config_callgraph(struct perf_evsel *evsel, | 540 | perf_evsel__config_callgraph(struct perf_evsel *evsel) |
506 | struct record_opts *opts) | ||
507 | { | 541 | { |
508 | bool function = perf_evsel__is_function_event(evsel); | 542 | bool function = perf_evsel__is_function_event(evsel); |
509 | struct perf_event_attr *attr = &evsel->attr; | 543 | struct perf_event_attr *attr = &evsel->attr; |
510 | 544 | ||
511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); | 545 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
512 | 546 | ||
513 | if (opts->call_graph == CALLCHAIN_DWARF) { | 547 | if (callchain_param.record_mode == CALLCHAIN_DWARF) { |
514 | if (!function) { | 548 | if (!function) { |
515 | perf_evsel__set_sample_bit(evsel, REGS_USER); | 549 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
516 | perf_evsel__set_sample_bit(evsel, STACK_USER); | 550 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
517 | attr->sample_regs_user = PERF_REGS_MASK; | 551 | attr->sample_regs_user = PERF_REGS_MASK; |
518 | attr->sample_stack_user = opts->stack_dump_size; | 552 | attr->sample_stack_user = callchain_param.dump_size; |
519 | attr->exclude_callchain_user = 1; | 553 | attr->exclude_callchain_user = 1; |
520 | } else { | 554 | } else { |
521 | pr_info("Cannot use DWARF unwind for function trace event," | 555 | pr_info("Cannot use DWARF unwind for function trace event," |
@@ -561,7 +595,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
561 | { | 595 | { |
562 | struct perf_evsel *leader = evsel->leader; | 596 | struct perf_evsel *leader = evsel->leader; |
563 | struct perf_event_attr *attr = &evsel->attr; | 597 | struct perf_event_attr *attr = &evsel->attr; |
564 | int track = !evsel->idx; /* only the first counter needs these */ | 598 | int track = evsel->tracking; |
565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; | 599 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; |
566 | 600 | ||
567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; | 601 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
@@ -624,8 +658,21 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
624 | attr->mmap_data = track; | 658 | attr->mmap_data = track; |
625 | } | 659 | } |
626 | 660 | ||
627 | if (opts->call_graph_enabled && !evsel->no_aux_samples) | 661 | /* |
628 | perf_evsel__config_callgraph(evsel, opts); | 662 | * We don't allow user space callchains for function trace |
663 | * event, due to issues with page faults while tracing page | ||
664 | * fault handler and its overall trickiness nature. | ||
665 | */ | ||
666 | if (perf_evsel__is_function_event(evsel)) | ||
667 | evsel->attr.exclude_callchain_user = 1; | ||
668 | |||
669 | if (callchain_param.enabled && !evsel->no_aux_samples) | ||
670 | perf_evsel__config_callgraph(evsel); | ||
671 | |||
672 | if (opts->sample_intr_regs) { | ||
673 | attr->sample_regs_intr = PERF_REGS_MASK; | ||
674 | perf_evsel__set_sample_bit(evsel, REGS_INTR); | ||
675 | } | ||
629 | 676 | ||
630 | if (target__has_cpu(&opts->target)) | 677 | if (target__has_cpu(&opts->target)) |
631 | perf_evsel__set_sample_bit(evsel, CPU); | 678 | perf_evsel__set_sample_bit(evsel, CPU); |
@@ -633,9 +680,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
633 | if (opts->period) | 680 | if (opts->period) |
634 | perf_evsel__set_sample_bit(evsel, PERIOD); | 681 | perf_evsel__set_sample_bit(evsel, PERIOD); |
635 | 682 | ||
636 | if (!perf_missing_features.sample_id_all && | 683 | /* |
637 | (opts->sample_time || !opts->no_inherit || | 684 | * When the user explicitely disabled time don't force it here. |
638 | target__has_cpu(&opts->target) || per_cpu)) | 685 | */ |
686 | if (opts->sample_time && | ||
687 | (!perf_missing_features.sample_id_all && | ||
688 | (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu))) | ||
639 | perf_evsel__set_sample_bit(evsel, TIME); | 689 | perf_evsel__set_sample_bit(evsel, TIME); |
640 | 690 | ||
641 | if (opts->raw_samples && !evsel->no_aux_samples) { | 691 | if (opts->raw_samples && !evsel->no_aux_samples) { |
@@ -689,9 +739,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
689 | } | 739 | } |
690 | } | 740 | } |
691 | 741 | ||
692 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 742 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
693 | { | 743 | { |
694 | int cpu, thread; | 744 | int cpu, thread; |
745 | |||
746 | if (evsel->system_wide) | ||
747 | nthreads = 1; | ||
748 | |||
695 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 749 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
696 | 750 | ||
697 | if (evsel->fd) { | 751 | if (evsel->fd) { |
@@ -710,6 +764,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea | |||
710 | { | 764 | { |
711 | int cpu, thread; | 765 | int cpu, thread; |
712 | 766 | ||
767 | if (evsel->system_wide) | ||
768 | nthreads = 1; | ||
769 | |||
713 | for (cpu = 0; cpu < ncpus; cpu++) { | 770 | for (cpu = 0; cpu < ncpus; cpu++) { |
714 | for (thread = 0; thread < nthreads; thread++) { | 771 | for (thread = 0; thread < nthreads; thread++) { |
715 | int fd = FD(evsel, cpu, thread), | 772 | int fd = FD(evsel, cpu, thread), |
@@ -740,6 +797,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
740 | 797 | ||
741 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 798 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
742 | { | 799 | { |
800 | if (evsel->system_wide) | ||
801 | nthreads = 1; | ||
802 | |||
743 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 803 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
744 | if (evsel->sample_id == NULL) | 804 | if (evsel->sample_id == NULL) |
745 | return -ENOMEM; | 805 | return -ENOMEM; |
@@ -767,13 +827,13 @@ int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | |||
767 | return evsel->counts != NULL ? 0 : -ENOMEM; | 827 | return evsel->counts != NULL ? 0 : -ENOMEM; |
768 | } | 828 | } |
769 | 829 | ||
770 | void perf_evsel__free_fd(struct perf_evsel *evsel) | 830 | static void perf_evsel__free_fd(struct perf_evsel *evsel) |
771 | { | 831 | { |
772 | xyarray__delete(evsel->fd); | 832 | xyarray__delete(evsel->fd); |
773 | evsel->fd = NULL; | 833 | evsel->fd = NULL; |
774 | } | 834 | } |
775 | 835 | ||
776 | void perf_evsel__free_id(struct perf_evsel *evsel) | 836 | static void perf_evsel__free_id(struct perf_evsel *evsel) |
777 | { | 837 | { |
778 | xyarray__delete(evsel->sample_id); | 838 | xyarray__delete(evsel->sample_id); |
779 | evsel->sample_id = NULL; | 839 | evsel->sample_id = NULL; |
@@ -784,6 +844,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
784 | { | 844 | { |
785 | int cpu, thread; | 845 | int cpu, thread; |
786 | 846 | ||
847 | if (evsel->system_wide) | ||
848 | nthreads = 1; | ||
849 | |||
787 | for (cpu = 0; cpu < ncpus; cpu++) | 850 | for (cpu = 0; cpu < ncpus; cpu++) |
788 | for (thread = 0; thread < nthreads; ++thread) { | 851 | for (thread = 0; thread < nthreads; ++thread) { |
789 | close(FD(evsel, cpu, thread)); | 852 | close(FD(evsel, cpu, thread)); |
@@ -801,22 +864,20 @@ void perf_evsel__exit(struct perf_evsel *evsel) | |||
801 | assert(list_empty(&evsel->node)); | 864 | assert(list_empty(&evsel->node)); |
802 | perf_evsel__free_fd(evsel); | 865 | perf_evsel__free_fd(evsel); |
803 | perf_evsel__free_id(evsel); | 866 | perf_evsel__free_id(evsel); |
867 | close_cgroup(evsel->cgrp); | ||
868 | zfree(&evsel->group_name); | ||
869 | zfree(&evsel->name); | ||
870 | perf_evsel__object.fini(evsel); | ||
804 | } | 871 | } |
805 | 872 | ||
806 | void perf_evsel__delete(struct perf_evsel *evsel) | 873 | void perf_evsel__delete(struct perf_evsel *evsel) |
807 | { | 874 | { |
808 | perf_evsel__exit(evsel); | 875 | perf_evsel__exit(evsel); |
809 | close_cgroup(evsel->cgrp); | ||
810 | zfree(&evsel->group_name); | ||
811 | if (evsel->tp_format) | ||
812 | pevent_free_format(evsel->tp_format); | ||
813 | zfree(&evsel->name); | ||
814 | free(evsel); | 876 | free(evsel); |
815 | } | 877 | } |
816 | 878 | ||
817 | static inline void compute_deltas(struct perf_evsel *evsel, | 879 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, |
818 | int cpu, | 880 | struct perf_counts_values *count) |
819 | struct perf_counts_values *count) | ||
820 | { | 881 | { |
821 | struct perf_counts_values tmp; | 882 | struct perf_counts_values tmp; |
822 | 883 | ||
@@ -836,78 +897,60 @@ static inline void compute_deltas(struct perf_evsel *evsel, | |||
836 | count->run = count->run - tmp.run; | 897 | count->run = count->run - tmp.run; |
837 | } | 898 | } |
838 | 899 | ||
839 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 900 | void perf_counts_values__scale(struct perf_counts_values *count, |
840 | int cpu, int thread, bool scale) | 901 | bool scale, s8 *pscaled) |
841 | { | 902 | { |
842 | struct perf_counts_values count; | 903 | s8 scaled = 0; |
843 | size_t nv = scale ? 3 : 1; | ||
844 | |||
845 | if (FD(evsel, cpu, thread) < 0) | ||
846 | return -EINVAL; | ||
847 | |||
848 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) | ||
849 | return -ENOMEM; | ||
850 | |||
851 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | ||
852 | return -errno; | ||
853 | |||
854 | compute_deltas(evsel, cpu, &count); | ||
855 | 904 | ||
856 | if (scale) { | 905 | if (scale) { |
857 | if (count.run == 0) | 906 | if (count->run == 0) { |
858 | count.val = 0; | 907 | scaled = -1; |
859 | else if (count.run < count.ena) | 908 | count->val = 0; |
860 | count.val = (u64)((double)count.val * count.ena / count.run + 0.5); | 909 | } else if (count->run < count->ena) { |
910 | scaled = 1; | ||
911 | count->val = (u64)((double) count->val * count->ena / count->run + 0.5); | ||
912 | } | ||
861 | } else | 913 | } else |
862 | count.ena = count.run = 0; | 914 | count->ena = count->run = 0; |
863 | 915 | ||
864 | evsel->counts->cpu[cpu] = count; | 916 | if (pscaled) |
865 | return 0; | 917 | *pscaled = scaled; |
866 | } | 918 | } |
867 | 919 | ||
868 | int __perf_evsel__read(struct perf_evsel *evsel, | 920 | int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, |
869 | int ncpus, int nthreads, bool scale) | 921 | perf_evsel__read_cb_t cb) |
870 | { | 922 | { |
871 | size_t nv = scale ? 3 : 1; | 923 | struct perf_counts_values count; |
872 | int cpu, thread; | ||
873 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | ||
874 | 924 | ||
875 | aggr->val = aggr->ena = aggr->run = 0; | 925 | memset(&count, 0, sizeof(count)); |
876 | 926 | ||
877 | for (cpu = 0; cpu < ncpus; cpu++) { | 927 | if (FD(evsel, cpu, thread) < 0) |
878 | for (thread = 0; thread < nthreads; thread++) { | 928 | return -EINVAL; |
879 | if (FD(evsel, cpu, thread) < 0) | ||
880 | continue; | ||
881 | 929 | ||
882 | if (readn(FD(evsel, cpu, thread), | 930 | if (readn(FD(evsel, cpu, thread), &count, sizeof(count)) < 0) |
883 | &count, nv * sizeof(u64)) < 0) | 931 | return -errno; |
884 | return -errno; | ||
885 | 932 | ||
886 | aggr->val += count.val; | 933 | return cb(evsel, cpu, thread, &count); |
887 | if (scale) { | 934 | } |
888 | aggr->ena += count.ena; | 935 | |
889 | aggr->run += count.run; | 936 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
890 | } | 937 | int cpu, int thread, bool scale) |
891 | } | 938 | { |
892 | } | 939 | struct perf_counts_values count; |
940 | size_t nv = scale ? 3 : 1; | ||
893 | 941 | ||
894 | compute_deltas(evsel, -1, aggr); | 942 | if (FD(evsel, cpu, thread) < 0) |
943 | return -EINVAL; | ||
895 | 944 | ||
896 | evsel->counts->scaled = 0; | 945 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) |
897 | if (scale) { | 946 | return -ENOMEM; |
898 | if (aggr->run == 0) { | ||
899 | evsel->counts->scaled = -1; | ||
900 | aggr->val = 0; | ||
901 | return 0; | ||
902 | } | ||
903 | 947 | ||
904 | if (aggr->run < aggr->ena) { | 948 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) |
905 | evsel->counts->scaled = 1; | 949 | return -errno; |
906 | aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5); | ||
907 | } | ||
908 | } else | ||
909 | aggr->ena = aggr->run = 0; | ||
910 | 950 | ||
951 | perf_evsel__compute_deltas(evsel, cpu, &count); | ||
952 | perf_counts_values__scale(&count, scale, NULL); | ||
953 | evsel->counts->cpu[cpu] = count; | ||
911 | return 0; | 954 | return 0; |
912 | } | 955 | } |
913 | 956 | ||
@@ -985,6 +1028,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
985 | ret += PRINT_ATTR_X64(branch_sample_type); | 1028 | ret += PRINT_ATTR_X64(branch_sample_type); |
986 | ret += PRINT_ATTR_X64(sample_regs_user); | 1029 | ret += PRINT_ATTR_X64(sample_regs_user); |
987 | ret += PRINT_ATTR_U32(sample_stack_user); | 1030 | ret += PRINT_ATTR_U32(sample_stack_user); |
1031 | ret += PRINT_ATTR_X64(sample_regs_intr); | ||
988 | 1032 | ||
989 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | 1033 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); |
990 | 1034 | ||
@@ -994,13 +1038,18 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
994 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1038 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
995 | struct thread_map *threads) | 1039 | struct thread_map *threads) |
996 | { | 1040 | { |
997 | int cpu, thread; | 1041 | int cpu, thread, nthreads; |
998 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; | 1042 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; |
999 | int pid = -1, err; | 1043 | int pid = -1, err; |
1000 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | 1044 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; |
1001 | 1045 | ||
1046 | if (evsel->system_wide) | ||
1047 | nthreads = 1; | ||
1048 | else | ||
1049 | nthreads = threads->nr; | ||
1050 | |||
1002 | if (evsel->fd == NULL && | 1051 | if (evsel->fd == NULL && |
1003 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 1052 | perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0) |
1004 | return -ENOMEM; | 1053 | return -ENOMEM; |
1005 | 1054 | ||
1006 | if (evsel->cgrp) { | 1055 | if (evsel->cgrp) { |
@@ -1024,10 +1073,10 @@ retry_sample_id: | |||
1024 | 1073 | ||
1025 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1074 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
1026 | 1075 | ||
1027 | for (thread = 0; thread < threads->nr; thread++) { | 1076 | for (thread = 0; thread < nthreads; thread++) { |
1028 | int group_fd; | 1077 | int group_fd; |
1029 | 1078 | ||
1030 | if (!evsel->cgrp) | 1079 | if (!evsel->cgrp && !evsel->system_wide) |
1031 | pid = threads->map[thread]; | 1080 | pid = threads->map[thread]; |
1032 | 1081 | ||
1033 | group_fd = get_group_fd(evsel, cpu, thread); | 1082 | group_fd = get_group_fd(evsel, cpu, thread); |
@@ -1100,7 +1149,7 @@ out_close: | |||
1100 | close(FD(evsel, cpu, thread)); | 1149 | close(FD(evsel, cpu, thread)); |
1101 | FD(evsel, cpu, thread) = -1; | 1150 | FD(evsel, cpu, thread) = -1; |
1102 | } | 1151 | } |
1103 | thread = threads->nr; | 1152 | thread = nthreads; |
1104 | } while (--cpu >= 0); | 1153 | } while (--cpu >= 0); |
1105 | return err; | 1154 | return err; |
1106 | } | 1155 | } |
@@ -1479,6 +1528,23 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1479 | array++; | 1528 | array++; |
1480 | } | 1529 | } |
1481 | 1530 | ||
1531 | data->intr_regs.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
1532 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
1533 | OVERFLOW_CHECK_u64(array); | ||
1534 | data->intr_regs.abi = *array; | ||
1535 | array++; | ||
1536 | |||
1537 | if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) { | ||
1538 | u64 mask = evsel->attr.sample_regs_intr; | ||
1539 | |||
1540 | sz = hweight_long(mask) * sizeof(u64); | ||
1541 | OVERFLOW_CHECK(array, sz, max_size); | ||
1542 | data->intr_regs.mask = mask; | ||
1543 | data->intr_regs.regs = (u64 *)array; | ||
1544 | array = (void *)array + sz; | ||
1545 | } | ||
1546 | } | ||
1547 | |||
1482 | return 0; | 1548 | return 0; |
1483 | } | 1549 | } |
1484 | 1550 | ||
@@ -1574,6 +1640,16 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | |||
1574 | if (type & PERF_SAMPLE_TRANSACTION) | 1640 | if (type & PERF_SAMPLE_TRANSACTION) |
1575 | result += sizeof(u64); | 1641 | result += sizeof(u64); |
1576 | 1642 | ||
1643 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
1644 | if (sample->intr_regs.abi) { | ||
1645 | result += sizeof(u64); | ||
1646 | sz = hweight_long(sample->intr_regs.mask) * sizeof(u64); | ||
1647 | result += sz; | ||
1648 | } else { | ||
1649 | result += sizeof(u64); | ||
1650 | } | ||
1651 | } | ||
1652 | |||
1577 | return result; | 1653 | return result; |
1578 | } | 1654 | } |
1579 | 1655 | ||
@@ -1752,6 +1828,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1752 | array++; | 1828 | array++; |
1753 | } | 1829 | } |
1754 | 1830 | ||
1831 | if (type & PERF_SAMPLE_REGS_INTR) { | ||
1832 | if (sample->intr_regs.abi) { | ||
1833 | *array++ = sample->intr_regs.abi; | ||
1834 | sz = hweight_long(sample->intr_regs.mask) * sizeof(u64); | ||
1835 | memcpy(array, sample->intr_regs.regs, sz); | ||
1836 | array = (void *)array + sz; | ||
1837 | } else { | ||
1838 | *array++ = 0; | ||
1839 | } | ||
1840 | } | ||
1841 | |||
1755 | return 0; | 1842 | return 0; |
1756 | } | 1843 | } |
1757 | 1844 | ||
@@ -1881,7 +1968,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | |||
1881 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | 1968 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), |
1882 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | 1969 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), |
1883 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | 1970 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), |
1884 | bit_name(IDENTIFIER), | 1971 | bit_name(IDENTIFIER), bit_name(REGS_INTR), |
1885 | { .name = NULL, } | 1972 | { .name = NULL, } |
1886 | }; | 1973 | }; |
1887 | #undef bit_name | 1974 | #undef bit_name |
@@ -2002,6 +2089,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
2002 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | 2089 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
2003 | int err, char *msg, size_t size) | 2090 | int err, char *msg, size_t size) |
2004 | { | 2091 | { |
2092 | char sbuf[STRERR_BUFSIZE]; | ||
2093 | |||
2005 | switch (err) { | 2094 | switch (err) { |
2006 | case EPERM: | 2095 | case EPERM: |
2007 | case EACCES: | 2096 | case EACCES: |
@@ -2036,13 +2125,20 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
2036 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); | 2125 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); |
2037 | #endif | 2126 | #endif |
2038 | break; | 2127 | break; |
2128 | case EBUSY: | ||
2129 | if (find_process("oprofiled")) | ||
2130 | return scnprintf(msg, size, | ||
2131 | "The PMU counters are busy/taken by another profiler.\n" | ||
2132 | "We found oprofile daemon running, please stop it and try again."); | ||
2133 | break; | ||
2039 | default: | 2134 | default: |
2040 | break; | 2135 | break; |
2041 | } | 2136 | } |
2042 | 2137 | ||
2043 | return scnprintf(msg, size, | 2138 | return scnprintf(msg, size, |
2044 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | 2139 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" |
2045 | "/bin/dmesg may provide additional information.\n" | 2140 | "/bin/dmesg may provide additional information.\n" |
2046 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | 2141 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", |
2047 | err, strerror(err), perf_evsel__name(evsel)); | 2142 | err, strerror_r(err, sbuf, sizeof(sbuf)), |
2143 | perf_evsel__name(evsel)); | ||
2048 | } | 2144 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index d7f93ce0ebc1..38622747d130 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -7,8 +7,6 @@ | |||
7 | #include <linux/perf_event.h> | 7 | #include <linux/perf_event.h> |
8 | #include <linux/types.h> | 8 | #include <linux/types.h> |
9 | #include "xyarray.h" | 9 | #include "xyarray.h" |
10 | #include "cgroup.h" | ||
11 | #include "hist.h" | ||
12 | #include "symbol.h" | 10 | #include "symbol.h" |
13 | 11 | ||
14 | struct perf_counts_values { | 12 | struct perf_counts_values { |
@@ -38,11 +36,16 @@ struct perf_sample_id { | |||
38 | struct hlist_node node; | 36 | struct hlist_node node; |
39 | u64 id; | 37 | u64 id; |
40 | struct perf_evsel *evsel; | 38 | struct perf_evsel *evsel; |
39 | int idx; | ||
40 | int cpu; | ||
41 | pid_t tid; | ||
41 | 42 | ||
42 | /* Holds total ID period value for PERF_SAMPLE_READ processing. */ | 43 | /* Holds total ID period value for PERF_SAMPLE_READ processing. */ |
43 | u64 period; | 44 | u64 period; |
44 | }; | 45 | }; |
45 | 46 | ||
47 | struct cgroup_sel; | ||
48 | |||
46 | /** struct perf_evsel - event selector | 49 | /** struct perf_evsel - event selector |
47 | * | 50 | * |
48 | * @name - Can be set to retain the original event name passed by the user, | 51 | * @name - Can be set to retain the original event name passed by the user, |
@@ -54,6 +57,7 @@ struct perf_sample_id { | |||
54 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or | 57 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or |
55 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all | 58 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all |
56 | * is used there is an id sample appended to non-sample events | 59 | * is used there is an id sample appended to non-sample events |
60 | * @priv: And what is in its containing unnamed union are tool specific | ||
57 | */ | 61 | */ |
58 | struct perf_evsel { | 62 | struct perf_evsel { |
59 | struct list_head node; | 63 | struct list_head node; |
@@ -66,14 +70,15 @@ struct perf_evsel { | |||
66 | struct perf_counts *prev_raw_counts; | 70 | struct perf_counts *prev_raw_counts; |
67 | int idx; | 71 | int idx; |
68 | u32 ids; | 72 | u32 ids; |
69 | struct hists hists; | ||
70 | char *name; | 73 | char *name; |
71 | double scale; | 74 | double scale; |
72 | const char *unit; | 75 | const char *unit; |
76 | bool snapshot; | ||
73 | struct event_format *tp_format; | 77 | struct event_format *tp_format; |
74 | union { | 78 | union { |
75 | void *priv; | 79 | void *priv; |
76 | off_t id_offset; | 80 | off_t id_offset; |
81 | u64 db_id; | ||
77 | }; | 82 | }; |
78 | struct cgroup_sel *cgrp; | 83 | struct cgroup_sel *cgrp; |
79 | void *handler; | 84 | void *handler; |
@@ -85,6 +90,10 @@ struct perf_evsel { | |||
85 | bool needs_swap; | 90 | bool needs_swap; |
86 | bool no_aux_samples; | 91 | bool no_aux_samples; |
87 | bool immediate; | 92 | bool immediate; |
93 | bool system_wide; | ||
94 | bool tracking; | ||
95 | bool per_pkg; | ||
96 | unsigned long *per_pkg_mask; | ||
88 | /* parse modifier helper */ | 97 | /* parse modifier helper */ |
89 | int exclude_GH; | 98 | int exclude_GH; |
90 | int nr_members; | 99 | int nr_members; |
@@ -98,13 +107,22 @@ union u64_swap { | |||
98 | u32 val32[2]; | 107 | u32 val32[2]; |
99 | }; | 108 | }; |
100 | 109 | ||
101 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) | ||
102 | |||
103 | struct cpu_map; | 110 | struct cpu_map; |
111 | struct target; | ||
104 | struct thread_map; | 112 | struct thread_map; |
105 | struct perf_evlist; | 113 | struct perf_evlist; |
106 | struct record_opts; | 114 | struct record_opts; |
107 | 115 | ||
116 | void perf_counts_values__scale(struct perf_counts_values *count, | ||
117 | bool scale, s8 *pscaled); | ||
118 | |||
119 | void perf_evsel__compute_deltas(struct perf_evsel *evsel, int cpu, | ||
120 | struct perf_counts_values *count); | ||
121 | |||
122 | int perf_evsel__object_config(size_t object_size, | ||
123 | int (*init)(struct perf_evsel *evsel), | ||
124 | void (*fini)(struct perf_evsel *evsel)); | ||
125 | |||
108 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); | 126 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); |
109 | 127 | ||
110 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) | 128 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) |
@@ -151,12 +169,9 @@ const char *perf_evsel__name(struct perf_evsel *evsel); | |||
151 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | 169 | const char *perf_evsel__group_name(struct perf_evsel *evsel); |
152 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | 170 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); |
153 | 171 | ||
154 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
155 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 172 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
156 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 173 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
157 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); | 174 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); |
158 | void perf_evsel__free_fd(struct perf_evsel *evsel); | ||
159 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
160 | void perf_evsel__free_counts(struct perf_evsel *evsel); | 175 | void perf_evsel__free_counts(struct perf_evsel *evsel); |
161 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 176 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
162 | 177 | ||
@@ -221,6 +236,13 @@ static inline bool perf_evsel__match2(struct perf_evsel *e1, | |||
221 | (a)->attr.type == (b)->attr.type && \ | 236 | (a)->attr.type == (b)->attr.type && \ |
222 | (a)->attr.config == (b)->attr.config) | 237 | (a)->attr.config == (b)->attr.config) |
223 | 238 | ||
239 | typedef int (perf_evsel__read_cb_t)(struct perf_evsel *evsel, | ||
240 | int cpu, int thread, | ||
241 | struct perf_counts_values *count); | ||
242 | |||
243 | int perf_evsel__read_cb(struct perf_evsel *evsel, int cpu, int thread, | ||
244 | perf_evsel__read_cb_t cb); | ||
245 | |||
224 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 246 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
225 | int cpu, int thread, bool scale); | 247 | int cpu, int thread, bool scale); |
226 | 248 | ||
@@ -250,37 +272,6 @@ static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel, | |||
250 | return __perf_evsel__read_on_cpu(evsel, cpu, thread, true); | 272 | return __perf_evsel__read_on_cpu(evsel, cpu, thread, true); |
251 | } | 273 | } |
252 | 274 | ||
253 | int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
254 | bool scale); | ||
255 | |||
256 | /** | ||
257 | * perf_evsel__read - Read the aggregate results on all CPUs | ||
258 | * | ||
259 | * @evsel - event selector to read value | ||
260 | * @ncpus - Number of cpus affected, from zero | ||
261 | * @nthreads - Number of threads affected, from zero | ||
262 | */ | ||
263 | static inline int perf_evsel__read(struct perf_evsel *evsel, | ||
264 | int ncpus, int nthreads) | ||
265 | { | ||
266 | return __perf_evsel__read(evsel, ncpus, nthreads, false); | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * perf_evsel__read_scaled - Read the aggregate results on all CPUs, scaled | ||
271 | * | ||
272 | * @evsel - event selector to read value | ||
273 | * @ncpus - Number of cpus affected, from zero | ||
274 | * @nthreads - Number of threads affected, from zero | ||
275 | */ | ||
276 | static inline int perf_evsel__read_scaled(struct perf_evsel *evsel, | ||
277 | int ncpus, int nthreads) | ||
278 | { | ||
279 | return __perf_evsel__read(evsel, ncpus, nthreads, true); | ||
280 | } | ||
281 | |||
282 | void hists__init(struct hists *hists); | ||
283 | |||
284 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 275 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
285 | struct perf_sample *sample); | 276 | struct perf_sample *sample); |
286 | 277 | ||
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c new file mode 100644 index 000000000000..95ef1cffc056 --- /dev/null +++ b/tools/perf/util/find-vdso-map.c | |||
@@ -0,0 +1,30 @@ | |||
1 | static int find_vdso_map(void **start, void **end) | ||
2 | { | ||
3 | FILE *maps; | ||
4 | char line[128]; | ||
5 | int found = 0; | ||
6 | |||
7 | maps = fopen("/proc/self/maps", "r"); | ||
8 | if (!maps) { | ||
9 | fprintf(stderr, "vdso: cannot open maps\n"); | ||
10 | return -1; | ||
11 | } | ||
12 | |||
13 | while (!found && fgets(line, sizeof(line), maps)) { | ||
14 | int m = -1; | ||
15 | |||
16 | /* We care only about private r-x mappings. */ | ||
17 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
18 | start, end, &m)) | ||
19 | continue; | ||
20 | if (m < 0) | ||
21 | continue; | ||
22 | |||
23 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
24 | sizeof(VDSO__MAP_NAME) - 1)) | ||
25 | found = 1; | ||
26 | } | ||
27 | |||
28 | fclose(maps); | ||
29 | return !found; | ||
30 | } | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 158c787ce0c4..b20e40c74468 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -24,8 +24,6 @@ | |||
24 | #include "build-id.h" | 24 | #include "build-id.h" |
25 | #include "data.h" | 25 | #include "data.h" |
26 | 26 | ||
27 | static bool no_buildid_cache = false; | ||
28 | |||
29 | static u32 header_argc; | 27 | static u32 header_argc; |
30 | static const char **header_argv; | 28 | static const char **header_argv; |
31 | 29 | ||
@@ -79,10 +77,7 @@ static int do_write(int fd, const void *buf, size_t size) | |||
79 | return 0; | 77 | return 0; |
80 | } | 78 | } |
81 | 79 | ||
82 | #define NAME_ALIGN 64 | 80 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned) |
83 | |||
84 | static int write_padded(int fd, const void *bf, size_t count, | ||
85 | size_t count_aligned) | ||
86 | { | 81 | { |
87 | static const char zero_buf[NAME_ALIGN]; | 82 | static const char zero_buf[NAME_ALIGN]; |
88 | int err = do_write(fd, bf, count); | 83 | int err = do_write(fd, bf, count); |
@@ -171,336 +166,6 @@ perf_header__set_cmdline(int argc, const char **argv) | |||
171 | return 0; | 166 | return 0; |
172 | } | 167 | } |
173 | 168 | ||
174 | #define dsos__for_each_with_build_id(pos, head) \ | ||
175 | list_for_each_entry(pos, head, node) \ | ||
176 | if (!pos->has_build_id) \ | ||
177 | continue; \ | ||
178 | else | ||
179 | |||
180 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
181 | pid_t pid, u16 misc, int fd) | ||
182 | { | ||
183 | int err; | ||
184 | struct build_id_event b; | ||
185 | size_t len; | ||
186 | |||
187 | len = name_len + 1; | ||
188 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
189 | |||
190 | memset(&b, 0, sizeof(b)); | ||
191 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
192 | b.pid = pid; | ||
193 | b.header.misc = misc; | ||
194 | b.header.size = sizeof(b) + len; | ||
195 | |||
196 | err = do_write(fd, &b, sizeof(b)); | ||
197 | if (err < 0) | ||
198 | return err; | ||
199 | |||
200 | return write_padded(fd, name, name_len + 1, len); | ||
201 | } | ||
202 | |||
203 | static int __dsos__hit_all(struct list_head *head) | ||
204 | { | ||
205 | struct dso *pos; | ||
206 | |||
207 | list_for_each_entry(pos, head, node) | ||
208 | pos->hit = true; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int machine__hit_all_dsos(struct machine *machine) | ||
214 | { | ||
215 | int err; | ||
216 | |||
217 | err = __dsos__hit_all(&machine->kernel_dsos); | ||
218 | if (err) | ||
219 | return err; | ||
220 | |||
221 | return __dsos__hit_all(&machine->user_dsos); | ||
222 | } | ||
223 | |||
224 | int dsos__hit_all(struct perf_session *session) | ||
225 | { | ||
226 | struct rb_node *nd; | ||
227 | int err; | ||
228 | |||
229 | err = machine__hit_all_dsos(&session->machines.host); | ||
230 | if (err) | ||
231 | return err; | ||
232 | |||
233 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
234 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
235 | |||
236 | err = machine__hit_all_dsos(pos); | ||
237 | if (err) | ||
238 | return err; | ||
239 | } | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int __dsos__write_buildid_table(struct list_head *head, | ||
245 | struct machine *machine, | ||
246 | pid_t pid, u16 misc, int fd) | ||
247 | { | ||
248 | char nm[PATH_MAX]; | ||
249 | struct dso *pos; | ||
250 | |||
251 | dsos__for_each_with_build_id(pos, head) { | ||
252 | int err; | ||
253 | const char *name; | ||
254 | size_t name_len; | ||
255 | |||
256 | if (!pos->hit) | ||
257 | continue; | ||
258 | |||
259 | if (dso__is_vdso(pos)) { | ||
260 | name = pos->short_name; | ||
261 | name_len = pos->short_name_len + 1; | ||
262 | } else if (dso__is_kcore(pos)) { | ||
263 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
264 | name = nm; | ||
265 | name_len = strlen(nm) + 1; | ||
266 | } else { | ||
267 | name = pos->long_name; | ||
268 | name_len = pos->long_name_len + 1; | ||
269 | } | ||
270 | |||
271 | err = write_buildid(name, name_len, pos->build_id, | ||
272 | pid, misc, fd); | ||
273 | if (err) | ||
274 | return err; | ||
275 | } | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
281 | { | ||
282 | int err; | ||
283 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
284 | umisc = PERF_RECORD_MISC_USER; | ||
285 | |||
286 | if (!machine__is_host(machine)) { | ||
287 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
288 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
289 | } | ||
290 | |||
291 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, | ||
292 | machine->pid, kmisc, fd); | ||
293 | if (err == 0) | ||
294 | err = __dsos__write_buildid_table(&machine->user_dsos, machine, | ||
295 | machine->pid, umisc, fd); | ||
296 | return err; | ||
297 | } | ||
298 | |||
299 | static int dsos__write_buildid_table(struct perf_header *header, int fd) | ||
300 | { | ||
301 | struct perf_session *session = container_of(header, | ||
302 | struct perf_session, header); | ||
303 | struct rb_node *nd; | ||
304 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
305 | |||
306 | if (err) | ||
307 | return err; | ||
308 | |||
309 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
310 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
311 | err = machine__write_buildid_table(pos, fd); | ||
312 | if (err) | ||
313 | break; | ||
314 | } | ||
315 | return err; | ||
316 | } | ||
317 | |||
318 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
319 | const char *name, bool is_kallsyms, bool is_vdso) | ||
320 | { | ||
321 | const size_t size = PATH_MAX; | ||
322 | char *realname, *filename = zalloc(size), | ||
323 | *linkname = zalloc(size), *targetname; | ||
324 | int len, err = -1; | ||
325 | bool slash = is_kallsyms || is_vdso; | ||
326 | |||
327 | if (is_kallsyms) { | ||
328 | if (symbol_conf.kptr_restrict) { | ||
329 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
330 | err = 0; | ||
331 | goto out_free; | ||
332 | } | ||
333 | realname = (char *) name; | ||
334 | } else | ||
335 | realname = realpath(name, NULL); | ||
336 | |||
337 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
338 | goto out_free; | ||
339 | |||
340 | len = scnprintf(filename, size, "%s%s%s", | ||
341 | debugdir, slash ? "/" : "", | ||
342 | is_vdso ? DSO__NAME_VDSO : realname); | ||
343 | if (mkdir_p(filename, 0755)) | ||
344 | goto out_free; | ||
345 | |||
346 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
347 | |||
348 | if (access(filename, F_OK)) { | ||
349 | if (is_kallsyms) { | ||
350 | if (copyfile("/proc/kallsyms", filename)) | ||
351 | goto out_free; | ||
352 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
353 | goto out_free; | ||
354 | } | ||
355 | |||
356 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
357 | debugdir, sbuild_id); | ||
358 | |||
359 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
360 | goto out_free; | ||
361 | |||
362 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
363 | targetname = filename + strlen(debugdir) - 5; | ||
364 | memcpy(targetname, "../..", 5); | ||
365 | |||
366 | if (symlink(targetname, linkname) == 0) | ||
367 | err = 0; | ||
368 | out_free: | ||
369 | if (!is_kallsyms) | ||
370 | free(realname); | ||
371 | free(filename); | ||
372 | free(linkname); | ||
373 | return err; | ||
374 | } | ||
375 | |||
376 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
377 | const char *name, const char *debugdir, | ||
378 | bool is_kallsyms, bool is_vdso) | ||
379 | { | ||
380 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
381 | |||
382 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
383 | |||
384 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
385 | is_kallsyms, is_vdso); | ||
386 | } | ||
387 | |||
388 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
389 | { | ||
390 | const size_t size = PATH_MAX; | ||
391 | char *filename = zalloc(size), | ||
392 | *linkname = zalloc(size); | ||
393 | int err = -1; | ||
394 | |||
395 | if (filename == NULL || linkname == NULL) | ||
396 | goto out_free; | ||
397 | |||
398 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
399 | debugdir, sbuild_id, sbuild_id + 2); | ||
400 | |||
401 | if (access(linkname, F_OK)) | ||
402 | goto out_free; | ||
403 | |||
404 | if (readlink(linkname, filename, size - 1) < 0) | ||
405 | goto out_free; | ||
406 | |||
407 | if (unlink(linkname)) | ||
408 | goto out_free; | ||
409 | |||
410 | /* | ||
411 | * Since the link is relative, we must make it absolute: | ||
412 | */ | ||
413 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
414 | debugdir, sbuild_id, filename); | ||
415 | |||
416 | if (unlink(linkname)) | ||
417 | goto out_free; | ||
418 | |||
419 | err = 0; | ||
420 | out_free: | ||
421 | free(filename); | ||
422 | free(linkname); | ||
423 | return err; | ||
424 | } | ||
425 | |||
426 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
427 | const char *debugdir) | ||
428 | { | ||
429 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
430 | bool is_vdso = dso__is_vdso(dso); | ||
431 | const char *name = dso->long_name; | ||
432 | char nm[PATH_MAX]; | ||
433 | |||
434 | if (dso__is_kcore(dso)) { | ||
435 | is_kallsyms = true; | ||
436 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
437 | name = nm; | ||
438 | } | ||
439 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
440 | debugdir, is_kallsyms, is_vdso); | ||
441 | } | ||
442 | |||
443 | static int __dsos__cache_build_ids(struct list_head *head, | ||
444 | struct machine *machine, const char *debugdir) | ||
445 | { | ||
446 | struct dso *pos; | ||
447 | int err = 0; | ||
448 | |||
449 | dsos__for_each_with_build_id(pos, head) | ||
450 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
451 | err = -1; | ||
452 | |||
453 | return err; | ||
454 | } | ||
455 | |||
456 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
457 | { | ||
458 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, | ||
459 | debugdir); | ||
460 | ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir); | ||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | static int perf_session__cache_build_ids(struct perf_session *session) | ||
465 | { | ||
466 | struct rb_node *nd; | ||
467 | int ret; | ||
468 | char debugdir[PATH_MAX]; | ||
469 | |||
470 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | ||
471 | |||
472 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
473 | return -1; | ||
474 | |||
475 | ret = machine__cache_build_ids(&session->machines.host, debugdir); | ||
476 | |||
477 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
478 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
479 | ret |= machine__cache_build_ids(pos, debugdir); | ||
480 | } | ||
481 | return ret ? -1 : 0; | ||
482 | } | ||
483 | |||
484 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
485 | { | ||
486 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); | ||
487 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); | ||
488 | return ret; | ||
489 | } | ||
490 | |||
491 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
492 | { | ||
493 | struct rb_node *nd; | ||
494 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
495 | |||
496 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
497 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
498 | ret |= machine__read_build_ids(pos, with_hits); | ||
499 | } | ||
500 | |||
501 | return ret; | ||
502 | } | ||
503 | |||
504 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, | 169 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, |
505 | struct perf_evlist *evlist) | 170 | struct perf_evlist *evlist) |
506 | { | 171 | { |
@@ -519,13 +184,12 @@ static int write_build_id(int fd, struct perf_header *h, | |||
519 | if (!perf_session__read_build_ids(session, true)) | 184 | if (!perf_session__read_build_ids(session, true)) |
520 | return -1; | 185 | return -1; |
521 | 186 | ||
522 | err = dsos__write_buildid_table(h, fd); | 187 | err = perf_session__write_buildid_table(session, fd); |
523 | if (err < 0) { | 188 | if (err < 0) { |
524 | pr_debug("failed to write buildid table\n"); | 189 | pr_debug("failed to write buildid table\n"); |
525 | return err; | 190 | return err; |
526 | } | 191 | } |
527 | if (!no_buildid_cache) | 192 | perf_session__cache_build_ids(session); |
528 | perf_session__cache_build_ids(session); | ||
529 | 193 | ||
530 | return 0; | 194 | return 0; |
531 | } | 195 | } |
@@ -575,16 +239,12 @@ static int write_version(int fd, struct perf_header *h __maybe_unused, | |||
575 | return do_write_string(fd, perf_version_string); | 239 | return do_write_string(fd, perf_version_string); |
576 | } | 240 | } |
577 | 241 | ||
578 | static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, | 242 | static int __write_cpudesc(int fd, const char *cpuinfo_proc) |
579 | struct perf_evlist *evlist __maybe_unused) | ||
580 | { | 243 | { |
581 | #ifndef CPUINFO_PROC | ||
582 | #define CPUINFO_PROC NULL | ||
583 | #endif | ||
584 | FILE *file; | 244 | FILE *file; |
585 | char *buf = NULL; | 245 | char *buf = NULL; |
586 | char *s, *p; | 246 | char *s, *p; |
587 | const char *search = CPUINFO_PROC; | 247 | const char *search = cpuinfo_proc; |
588 | size_t len = 0; | 248 | size_t len = 0; |
589 | int ret = -1; | 249 | int ret = -1; |
590 | 250 | ||
@@ -601,8 +261,10 @@ static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, | |||
601 | break; | 261 | break; |
602 | } | 262 | } |
603 | 263 | ||
604 | if (ret) | 264 | if (ret) { |
265 | ret = -1; | ||
605 | goto done; | 266 | goto done; |
267 | } | ||
606 | 268 | ||
607 | s = buf; | 269 | s = buf; |
608 | 270 | ||
@@ -634,6 +296,25 @@ done: | |||
634 | return ret; | 296 | return ret; |
635 | } | 297 | } |
636 | 298 | ||
299 | static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, | ||
300 | struct perf_evlist *evlist __maybe_unused) | ||
301 | { | ||
302 | #ifndef CPUINFO_PROC | ||
303 | #define CPUINFO_PROC {"model name", } | ||
304 | #endif | ||
305 | const char *cpuinfo_procs[] = CPUINFO_PROC; | ||
306 | unsigned int i; | ||
307 | |||
308 | for (i = 0; i < ARRAY_SIZE(cpuinfo_procs); i++) { | ||
309 | int ret; | ||
310 | ret = __write_cpudesc(fd, cpuinfo_procs[i]); | ||
311 | if (ret >= 0) | ||
312 | return ret; | ||
313 | } | ||
314 | return -1; | ||
315 | } | ||
316 | |||
317 | |||
637 | static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, | 318 | static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, |
638 | struct perf_evlist *evlist __maybe_unused) | 319 | struct perf_evlist *evlist __maybe_unused) |
639 | { | 320 | { |
@@ -946,7 +627,8 @@ static int write_total_mem(int fd, struct perf_header *h __maybe_unused, | |||
946 | n = sscanf(buf, "%*s %"PRIu64, &mem); | 627 | n = sscanf(buf, "%*s %"PRIu64, &mem); |
947 | if (n == 1) | 628 | if (n == 1) |
948 | ret = do_write(fd, &mem, sizeof(mem)); | 629 | ret = do_write(fd, &mem, sizeof(mem)); |
949 | } | 630 | } else |
631 | ret = -1; | ||
950 | free(buf); | 632 | free(buf); |
951 | fclose(fp); | 633 | fclose(fp); |
952 | return ret; | 634 | return ret; |
@@ -1548,7 +1230,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1548 | struct perf_session *session) | 1230 | struct perf_session *session) |
1549 | { | 1231 | { |
1550 | int err = -1; | 1232 | int err = -1; |
1551 | struct list_head *head; | 1233 | struct dsos *dsos; |
1552 | struct machine *machine; | 1234 | struct machine *machine; |
1553 | u16 misc; | 1235 | u16 misc; |
1554 | struct dso *dso; | 1236 | struct dso *dso; |
@@ -1563,28 +1245,28 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1563 | switch (misc) { | 1245 | switch (misc) { |
1564 | case PERF_RECORD_MISC_KERNEL: | 1246 | case PERF_RECORD_MISC_KERNEL: |
1565 | dso_type = DSO_TYPE_KERNEL; | 1247 | dso_type = DSO_TYPE_KERNEL; |
1566 | head = &machine->kernel_dsos; | 1248 | dsos = &machine->kernel_dsos; |
1567 | break; | 1249 | break; |
1568 | case PERF_RECORD_MISC_GUEST_KERNEL: | 1250 | case PERF_RECORD_MISC_GUEST_KERNEL: |
1569 | dso_type = DSO_TYPE_GUEST_KERNEL; | 1251 | dso_type = DSO_TYPE_GUEST_KERNEL; |
1570 | head = &machine->kernel_dsos; | 1252 | dsos = &machine->kernel_dsos; |
1571 | break; | 1253 | break; |
1572 | case PERF_RECORD_MISC_USER: | 1254 | case PERF_RECORD_MISC_USER: |
1573 | case PERF_RECORD_MISC_GUEST_USER: | 1255 | case PERF_RECORD_MISC_GUEST_USER: |
1574 | dso_type = DSO_TYPE_USER; | 1256 | dso_type = DSO_TYPE_USER; |
1575 | head = &machine->user_dsos; | 1257 | dsos = &machine->user_dsos; |
1576 | break; | 1258 | break; |
1577 | default: | 1259 | default: |
1578 | goto out; | 1260 | goto out; |
1579 | } | 1261 | } |
1580 | 1262 | ||
1581 | dso = __dsos__findnew(head, filename); | 1263 | dso = __dsos__findnew(dsos, filename); |
1582 | if (dso != NULL) { | 1264 | if (dso != NULL) { |
1583 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1265 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
1584 | 1266 | ||
1585 | dso__set_build_id(dso, &bev->build_id); | 1267 | dso__set_build_id(dso, &bev->build_id); |
1586 | 1268 | ||
1587 | if (filename[0] == '[') | 1269 | if (!is_kernel_module(filename, NULL)) |
1588 | dso->kernel = dso_type; | 1270 | dso->kernel = dso_type; |
1589 | 1271 | ||
1590 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1272 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
@@ -2458,6 +2140,7 @@ static const int attr_file_abi_sizes[] = { | |||
2458 | [1] = PERF_ATTR_SIZE_VER1, | 2140 | [1] = PERF_ATTR_SIZE_VER1, |
2459 | [2] = PERF_ATTR_SIZE_VER2, | 2141 | [2] = PERF_ATTR_SIZE_VER2, |
2460 | [3] = PERF_ATTR_SIZE_VER3, | 2142 | [3] = PERF_ATTR_SIZE_VER3, |
2143 | [4] = PERF_ATTR_SIZE_VER4, | ||
2461 | 0, | 2144 | 0, |
2462 | }; | 2145 | }; |
2463 | 2146 | ||
@@ -3105,8 +2788,3 @@ int perf_event__process_build_id(struct perf_tool *tool __maybe_unused, | |||
3105 | session); | 2788 | session); |
3106 | return 0; | 2789 | return 0; |
3107 | } | 2790 | } |
3108 | |||
3109 | void disable_buildid_cache(void) | ||
3110 | { | ||
3111 | no_buildid_cache = true; | ||
3112 | } | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 8f5cbaea64a5..3bb90ac172a1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
122 | 122 | ||
123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | 123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); |
124 | 124 | ||
125 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
126 | const char *name, bool is_kallsyms, bool is_vdso); | ||
127 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
128 | |||
129 | int perf_event__synthesize_attr(struct perf_tool *tool, | 125 | int perf_event__synthesize_attr(struct perf_tool *tool, |
130 | struct perf_event_attr *attr, u32 ids, u64 *id, | 126 | struct perf_event_attr *attr, u32 ids, u64 *id, |
131 | perf_event__handler_t process); | 127 | perf_event__handler_t process); |
@@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool, | |||
151 | struct perf_session *session); | 147 | struct perf_session *session); |
152 | bool is_perf_magic(u64 magic); | 148 | bool is_perf_magic(u64 magic); |
153 | 149 | ||
154 | int dsos__hit_all(struct perf_session *session); | 150 | #define NAME_ALIGN 64 |
151 | |||
152 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); | ||
155 | 153 | ||
156 | /* | 154 | /* |
157 | * arch specific callback | 155 | * arch specific callback |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30df6187ee02..182395546ddc 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -3,8 +3,10 @@ | |||
3 | #include "hist.h" | 3 | #include "hist.h" |
4 | #include "session.h" | 4 | #include "session.h" |
5 | #include "sort.h" | 5 | #include "sort.h" |
6 | #include "evlist.h" | ||
6 | #include "evsel.h" | 7 | #include "evsel.h" |
7 | #include "annotate.h" | 8 | #include "annotate.h" |
9 | #include "ui/progress.h" | ||
8 | #include <math.h> | 10 | #include <math.h> |
9 | 11 | ||
10 | static bool hists__filter_entry_by_dso(struct hists *hists, | 12 | static bool hists__filter_entry_by_dso(struct hists *hists, |
@@ -14,13 +16,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists, | |||
14 | static bool hists__filter_entry_by_symbol(struct hists *hists, | 16 | static bool hists__filter_entry_by_symbol(struct hists *hists, |
15 | struct hist_entry *he); | 17 | struct hist_entry *he); |
16 | 18 | ||
17 | struct callchain_param callchain_param = { | ||
18 | .mode = CHAIN_GRAPH_REL, | ||
19 | .min_percent = 0.5, | ||
20 | .order = ORDER_CALLEE, | ||
21 | .key = CCKEY_FUNCTION | ||
22 | }; | ||
23 | |||
24 | u16 hists__col_len(struct hists *hists, enum hist_column col) | 19 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
25 | { | 20 | { |
26 | return hists->col_len[col]; | 21 | return hists->col_len[col]; |
@@ -277,6 +272,28 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
277 | } | 272 | } |
278 | } | 273 | } |
279 | 274 | ||
275 | void hists__delete_entries(struct hists *hists) | ||
276 | { | ||
277 | struct rb_node *next = rb_first(&hists->entries); | ||
278 | struct hist_entry *n; | ||
279 | |||
280 | while (next) { | ||
281 | n = rb_entry(next, struct hist_entry, rb_node); | ||
282 | next = rb_next(&n->rb_node); | ||
283 | |||
284 | rb_erase(&n->rb_node, &hists->entries); | ||
285 | |||
286 | if (sort__need_collapse) | ||
287 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
288 | |||
289 | --hists->nr_entries; | ||
290 | if (!n->filtered) | ||
291 | --hists->nr_non_filtered_entries; | ||
292 | |||
293 | hist_entry__free(n); | ||
294 | } | ||
295 | } | ||
296 | |||
280 | /* | 297 | /* |
281 | * histogram, sorted on item, collects periods | 298 | * histogram, sorted on item, collects periods |
282 | */ | 299 | */ |
@@ -287,7 +304,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
287 | size_t callchain_size = 0; | 304 | size_t callchain_size = 0; |
288 | struct hist_entry *he; | 305 | struct hist_entry *he; |
289 | 306 | ||
290 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) | 307 | if (symbol_conf.use_callchain) |
291 | callchain_size = sizeof(struct callchain_root); | 308 | callchain_size = sizeof(struct callchain_root); |
292 | 309 | ||
293 | he = zalloc(sizeof(*he) + callchain_size); | 310 | he = zalloc(sizeof(*he) + callchain_size); |
@@ -494,6 +511,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
494 | { | 511 | { |
495 | u64 cost; | 512 | u64 cost; |
496 | struct mem_info *mi = iter->priv; | 513 | struct mem_info *mi = iter->priv; |
514 | struct hists *hists = evsel__hists(iter->evsel); | ||
497 | struct hist_entry *he; | 515 | struct hist_entry *he; |
498 | 516 | ||
499 | if (mi == NULL) | 517 | if (mi == NULL) |
@@ -510,7 +528,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
510 | * and this is indirectly achieved by passing period=weight here | 528 | * and this is indirectly achieved by passing period=weight here |
511 | * and the he_stat__add_period() function. | 529 | * and the he_stat__add_period() function. |
512 | */ | 530 | */ |
513 | he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, | 531 | he = __hists__add_entry(hists, al, iter->parent, NULL, mi, |
514 | cost, cost, 0, true); | 532 | cost, cost, 0, true); |
515 | if (!he) | 533 | if (!he) |
516 | return -ENOMEM; | 534 | return -ENOMEM; |
@@ -524,13 +542,14 @@ iter_finish_mem_entry(struct hist_entry_iter *iter, | |||
524 | struct addr_location *al __maybe_unused) | 542 | struct addr_location *al __maybe_unused) |
525 | { | 543 | { |
526 | struct perf_evsel *evsel = iter->evsel; | 544 | struct perf_evsel *evsel = iter->evsel; |
545 | struct hists *hists = evsel__hists(evsel); | ||
527 | struct hist_entry *he = iter->he; | 546 | struct hist_entry *he = iter->he; |
528 | int err = -EINVAL; | 547 | int err = -EINVAL; |
529 | 548 | ||
530 | if (he == NULL) | 549 | if (he == NULL) |
531 | goto out; | 550 | goto out; |
532 | 551 | ||
533 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 552 | hists__inc_nr_samples(hists, he->filtered); |
534 | 553 | ||
535 | err = hist_entry__append_callchain(he, iter->sample); | 554 | err = hist_entry__append_callchain(he, iter->sample); |
536 | 555 | ||
@@ -596,6 +615,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a | |||
596 | { | 615 | { |
597 | struct branch_info *bi; | 616 | struct branch_info *bi; |
598 | struct perf_evsel *evsel = iter->evsel; | 617 | struct perf_evsel *evsel = iter->evsel; |
618 | struct hists *hists = evsel__hists(evsel); | ||
599 | struct hist_entry *he = NULL; | 619 | struct hist_entry *he = NULL; |
600 | int i = iter->curr; | 620 | int i = iter->curr; |
601 | int err = 0; | 621 | int err = 0; |
@@ -609,12 +629,12 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a | |||
609 | * The report shows the percentage of total branches captured | 629 | * The report shows the percentage of total branches captured |
610 | * and not events sampled. Thus we use a pseudo period of 1. | 630 | * and not events sampled. Thus we use a pseudo period of 1. |
611 | */ | 631 | */ |
612 | he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, | 632 | he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, |
613 | 1, 1, 0, true); | 633 | 1, 1, 0, true); |
614 | if (he == NULL) | 634 | if (he == NULL) |
615 | return -ENOMEM; | 635 | return -ENOMEM; |
616 | 636 | ||
617 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 637 | hists__inc_nr_samples(hists, he->filtered); |
618 | 638 | ||
619 | out: | 639 | out: |
620 | iter->he = he; | 640 | iter->he = he; |
@@ -646,7 +666,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location | |||
646 | struct perf_sample *sample = iter->sample; | 666 | struct perf_sample *sample = iter->sample; |
647 | struct hist_entry *he; | 667 | struct hist_entry *he; |
648 | 668 | ||
649 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 669 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
650 | sample->period, sample->weight, | 670 | sample->period, sample->weight, |
651 | sample->transaction, true); | 671 | sample->transaction, true); |
652 | if (he == NULL) | 672 | if (he == NULL) |
@@ -669,7 +689,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, | |||
669 | 689 | ||
670 | iter->he = NULL; | 690 | iter->he = NULL; |
671 | 691 | ||
672 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 692 | hists__inc_nr_samples(evsel__hists(evsel), he->filtered); |
673 | 693 | ||
674 | return hist_entry__append_callchain(he, sample); | 694 | return hist_entry__append_callchain(he, sample); |
675 | } | 695 | } |
@@ -702,12 +722,13 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
702 | struct addr_location *al) | 722 | struct addr_location *al) |
703 | { | 723 | { |
704 | struct perf_evsel *evsel = iter->evsel; | 724 | struct perf_evsel *evsel = iter->evsel; |
725 | struct hists *hists = evsel__hists(evsel); | ||
705 | struct perf_sample *sample = iter->sample; | 726 | struct perf_sample *sample = iter->sample; |
706 | struct hist_entry **he_cache = iter->priv; | 727 | struct hist_entry **he_cache = iter->priv; |
707 | struct hist_entry *he; | 728 | struct hist_entry *he; |
708 | int err = 0; | 729 | int err = 0; |
709 | 730 | ||
710 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 731 | he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, |
711 | sample->period, sample->weight, | 732 | sample->period, sample->weight, |
712 | sample->transaction, true); | 733 | sample->transaction, true); |
713 | if (he == NULL) | 734 | if (he == NULL) |
@@ -716,7 +737,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
716 | iter->he = he; | 737 | iter->he = he; |
717 | he_cache[iter->curr++] = he; | 738 | he_cache[iter->curr++] = he; |
718 | 739 | ||
719 | callchain_append(he->callchain, &callchain_cursor, sample->period); | 740 | hist_entry__append_callchain(he, sample); |
720 | 741 | ||
721 | /* | 742 | /* |
722 | * We need to re-initialize the cursor since callchain_append() | 743 | * We need to re-initialize the cursor since callchain_append() |
@@ -724,7 +745,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
724 | */ | 745 | */ |
725 | callchain_cursor_commit(&callchain_cursor); | 746 | callchain_cursor_commit(&callchain_cursor); |
726 | 747 | ||
727 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 748 | hists__inc_nr_samples(hists, he->filtered); |
728 | 749 | ||
729 | return err; | 750 | return err; |
730 | } | 751 | } |
@@ -780,7 +801,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, | |||
780 | } | 801 | } |
781 | } | 802 | } |
782 | 803 | ||
783 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 804 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
784 | sample->period, sample->weight, | 805 | sample->period, sample->weight, |
785 | sample->transaction, false); | 806 | sample->transaction, false); |
786 | if (he == NULL) | 807 | if (he == NULL) |
@@ -789,7 +810,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, | |||
789 | iter->he = he; | 810 | iter->he = he; |
790 | he_cache[iter->curr++] = he; | 811 | he_cache[iter->curr++] = he; |
791 | 812 | ||
792 | callchain_append(he->callchain, &cursor, sample->period); | 813 | if (symbol_conf.use_callchain) |
814 | callchain_append(he->callchain, &cursor, sample->period); | ||
793 | return 0; | 815 | return 0; |
794 | } | 816 | } |
795 | 817 | ||
@@ -925,6 +947,7 @@ void hist_entry__free(struct hist_entry *he) | |||
925 | zfree(&he->mem_info); | 947 | zfree(&he->mem_info); |
926 | zfree(&he->stat_acc); | 948 | zfree(&he->stat_acc); |
927 | free_srcline(he->srcline); | 949 | free_srcline(he->srcline); |
950 | free_callchain(he->callchain); | ||
928 | free(he); | 951 | free(he); |
929 | } | 952 | } |
930 | 953 | ||
@@ -967,6 +990,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
967 | else | 990 | else |
968 | p = &(*p)->rb_right; | 991 | p = &(*p)->rb_right; |
969 | } | 992 | } |
993 | hists->nr_entries++; | ||
970 | 994 | ||
971 | rb_link_node(&he->rb_node_in, parent, p); | 995 | rb_link_node(&he->rb_node_in, parent, p); |
972 | rb_insert_color(&he->rb_node_in, root); | 996 | rb_insert_color(&he->rb_node_in, root); |
@@ -1004,7 +1028,10 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1004 | if (!sort__need_collapse) | 1028 | if (!sort__need_collapse) |
1005 | return; | 1029 | return; |
1006 | 1030 | ||
1031 | hists->nr_entries = 0; | ||
1032 | |||
1007 | root = hists__get_rotate_entries_in(hists); | 1033 | root = hists__get_rotate_entries_in(hists); |
1034 | |||
1008 | next = rb_first(root); | 1035 | next = rb_first(root); |
1009 | 1036 | ||
1010 | while (next) { | 1037 | while (next) { |
@@ -1099,7 +1126,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1099 | rb_insert_color(&he->rb_node, entries); | 1126 | rb_insert_color(&he->rb_node, entries); |
1100 | } | 1127 | } |
1101 | 1128 | ||
1102 | void hists__output_resort(struct hists *hists) | 1129 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) |
1103 | { | 1130 | { |
1104 | struct rb_root *root; | 1131 | struct rb_root *root; |
1105 | struct rb_node *next; | 1132 | struct rb_node *next; |
@@ -1128,6 +1155,9 @@ void hists__output_resort(struct hists *hists) | |||
1128 | 1155 | ||
1129 | if (!n->filtered) | 1156 | if (!n->filtered) |
1130 | hists__calc_col_len(hists, n); | 1157 | hists__calc_col_len(hists, n); |
1158 | |||
1159 | if (prog) | ||
1160 | ui_progress__update(prog, 1); | ||
1131 | } | 1161 | } |
1132 | } | 1162 | } |
1133 | 1163 | ||
@@ -1386,6 +1416,21 @@ int hists__link(struct hists *leader, struct hists *other) | |||
1386 | return 0; | 1416 | return 0; |
1387 | } | 1417 | } |
1388 | 1418 | ||
1419 | |||
1420 | size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp) | ||
1421 | { | ||
1422 | struct perf_evsel *pos; | ||
1423 | size_t ret = 0; | ||
1424 | |||
1425 | evlist__for_each(evlist, pos) { | ||
1426 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | ||
1427 | ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp); | ||
1428 | } | ||
1429 | |||
1430 | return ret; | ||
1431 | } | ||
1432 | |||
1433 | |||
1389 | u64 hists__total_period(struct hists *hists) | 1434 | u64 hists__total_period(struct hists *hists) |
1390 | { | 1435 | { |
1391 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : | 1436 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : |
@@ -1412,3 +1457,31 @@ int perf_hist_config(const char *var, const char *value) | |||
1412 | 1457 | ||
1413 | return 0; | 1458 | return 0; |
1414 | } | 1459 | } |
1460 | |||
1461 | static int hists_evsel__init(struct perf_evsel *evsel) | ||
1462 | { | ||
1463 | struct hists *hists = evsel__hists(evsel); | ||
1464 | |||
1465 | memset(hists, 0, sizeof(*hists)); | ||
1466 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
1467 | hists->entries_in = &hists->entries_in_array[0]; | ||
1468 | hists->entries_collapsed = RB_ROOT; | ||
1469 | hists->entries = RB_ROOT; | ||
1470 | pthread_mutex_init(&hists->lock, NULL); | ||
1471 | return 0; | ||
1472 | } | ||
1473 | |||
1474 | /* | ||
1475 | * XXX We probably need a hists_evsel__exit() to free the hist_entries | ||
1476 | * stored in the rbtree... | ||
1477 | */ | ||
1478 | |||
1479 | int hists__init(void) | ||
1480 | { | ||
1481 | int err = perf_evsel__object_config(sizeof(struct hists_evsel), | ||
1482 | hists_evsel__init, NULL); | ||
1483 | if (err) | ||
1484 | fputs("FATAL ERROR: Couldn't setup hists class\n", stderr); | ||
1485 | |||
1486 | return err; | ||
1487 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 742f49a85725..46bd50344f85 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -4,12 +4,11 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | 5 | #include <pthread.h> |
6 | #include "callchain.h" | 6 | #include "callchain.h" |
7 | #include "evsel.h" | ||
7 | #include "header.h" | 8 | #include "header.h" |
8 | #include "color.h" | 9 | #include "color.h" |
9 | #include "ui/progress.h" | 10 | #include "ui/progress.h" |
10 | 11 | ||
11 | extern struct callchain_param callchain_param; | ||
12 | |||
13 | struct hist_entry; | 12 | struct hist_entry; |
14 | struct addr_location; | 13 | struct addr_location; |
15 | struct symbol; | 14 | struct symbol; |
@@ -23,32 +22,6 @@ enum hist_filter { | |||
23 | HIST_FILTER__HOST, | 22 | HIST_FILTER__HOST, |
24 | }; | 23 | }; |
25 | 24 | ||
26 | /* | ||
27 | * The kernel collects the number of events it couldn't send in a stretch and | ||
28 | * when possible sends this number in a PERF_RECORD_LOST event. The number of | ||
29 | * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while | ||
30 | * total_lost tells exactly how many events the kernel in fact lost, i.e. it is | ||
31 | * the sum of all struct lost_event.lost fields reported. | ||
32 | * | ||
33 | * The total_period is needed because by default auto-freq is used, so | ||
34 | * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get | ||
35 | * the total number of low level events, it is necessary to to sum all struct | ||
36 | * sample_event.period and stash the result in total_period. | ||
37 | */ | ||
38 | struct events_stats { | ||
39 | u64 total_period; | ||
40 | u64 total_non_filtered_period; | ||
41 | u64 total_lost; | ||
42 | u64 total_invalid_chains; | ||
43 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | ||
44 | u32 nr_non_filtered_samples; | ||
45 | u32 nr_lost_warned; | ||
46 | u32 nr_unknown_events; | ||
47 | u32 nr_invalid_chains; | ||
48 | u32 nr_unknown_id; | ||
49 | u32 nr_unprocessable_samples; | ||
50 | }; | ||
51 | |||
52 | enum hist_column { | 25 | enum hist_column { |
53 | HISTC_SYMBOL, | 26 | HISTC_SYMBOL, |
54 | HISTC_DSO, | 27 | HISTC_DSO, |
@@ -148,10 +121,11 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, | |||
148 | struct hists *hists); | 121 | struct hists *hists); |
149 | void hist_entry__free(struct hist_entry *); | 122 | void hist_entry__free(struct hist_entry *); |
150 | 123 | ||
151 | void hists__output_resort(struct hists *hists); | 124 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); |
152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 125 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
153 | 126 | ||
154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 127 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
128 | void hists__delete_entries(struct hists *hists); | ||
155 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 129 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
156 | 130 | ||
157 | u64 hists__total_period(struct hists *hists); | 131 | u64 hists__total_period(struct hists *hists); |
@@ -164,6 +138,7 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | |||
164 | 138 | ||
165 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | 139 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
166 | int max_cols, float min_pcnt, FILE *fp); | 140 | int max_cols, float min_pcnt, FILE *fp); |
141 | size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp); | ||
167 | 142 | ||
168 | void hists__filter_by_dso(struct hists *hists); | 143 | void hists__filter_by_dso(struct hists *hists); |
169 | void hists__filter_by_thread(struct hists *hists); | 144 | void hists__filter_by_thread(struct hists *hists); |
@@ -184,6 +159,25 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | |||
184 | void hists__match(struct hists *leader, struct hists *other); | 159 | void hists__match(struct hists *leader, struct hists *other); |
185 | int hists__link(struct hists *leader, struct hists *other); | 160 | int hists__link(struct hists *leader, struct hists *other); |
186 | 161 | ||
162 | struct hists_evsel { | ||
163 | struct perf_evsel evsel; | ||
164 | struct hists hists; | ||
165 | }; | ||
166 | |||
167 | static inline struct perf_evsel *hists_to_evsel(struct hists *hists) | ||
168 | { | ||
169 | struct hists_evsel *hevsel = container_of(hists, struct hists_evsel, hists); | ||
170 | return &hevsel->evsel; | ||
171 | } | ||
172 | |||
173 | static inline struct hists *evsel__hists(struct perf_evsel *evsel) | ||
174 | { | ||
175 | struct hists_evsel *hevsel = (struct hists_evsel *)evsel; | ||
176 | return &hevsel->hists; | ||
177 | } | ||
178 | |||
179 | int hists__init(void); | ||
180 | |||
187 | struct perf_hpp { | 181 | struct perf_hpp { |
188 | char *buf; | 182 | char *buf; |
189 | size_t size; | 183 | size_t size; |
@@ -192,6 +186,7 @@ struct perf_hpp { | |||
192 | }; | 186 | }; |
193 | 187 | ||
194 | struct perf_hpp_fmt { | 188 | struct perf_hpp_fmt { |
189 | const char *name; | ||
195 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 190 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
196 | struct perf_evsel *evsel); | 191 | struct perf_evsel *evsel); |
197 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 192 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
@@ -207,6 +202,8 @@ struct perf_hpp_fmt { | |||
207 | struct list_head list; | 202 | struct list_head list; |
208 | struct list_head sort_list; | 203 | struct list_head sort_list; |
209 | bool elide; | 204 | bool elide; |
205 | int len; | ||
206 | int user_len; | ||
210 | }; | 207 | }; |
211 | 208 | ||
212 | extern struct list_head perf_hpp__list; | 209 | extern struct list_head perf_hpp__list; |
@@ -261,17 +258,19 @@ static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) | |||
261 | } | 258 | } |
262 | 259 | ||
263 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); | 260 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); |
261 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists); | ||
262 | void perf_hpp__set_user_width(const char *width_list_str); | ||
264 | 263 | ||
265 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | 264 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); |
266 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | 265 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); |
267 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | 266 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); |
268 | 267 | ||
269 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 268 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
270 | hpp_field_fn get_field, const char *fmt, | 269 | struct hist_entry *he, hpp_field_fn get_field, |
271 | hpp_snprint_fn print_fn, bool fmt_percent); | 270 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
272 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 271 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
273 | hpp_field_fn get_field, const char *fmt, | 272 | struct hist_entry *he, hpp_field_fn get_field, |
274 | hpp_snprint_fn print_fn, bool fmt_percent); | 273 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
275 | 274 | ||
276 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 275 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
277 | { | 276 | { |
diff --git a/tools/perf/util/include/asm/hash.h b/tools/perf/util/include/asm/hash.h deleted file mode 100644 index d82b170bb216..000000000000 --- a/tools/perf/util/include/asm/hash.h +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | #ifndef __ASM_GENERIC_HASH_H | ||
2 | #define __ASM_GENERIC_HASH_H | ||
3 | |||
4 | /* Stub */ | ||
5 | |||
6 | #endif /* __ASM_GENERIC_HASH_H */ | ||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 01ffd12dc791..40bd21488032 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, | |||
46 | __bitmap_or(dst, src1, src2, nbits); | 46 | __bitmap_or(dst, src1, src2, nbits); |
47 | } | 47 | } |
48 | 48 | ||
49 | /** | ||
50 | * test_and_set_bit - Set a bit and return its old value | ||
51 | * @nr: Bit to set | ||
52 | * @addr: Address to count from | ||
53 | */ | ||
54 | static inline int test_and_set_bit(int nr, unsigned long *addr) | ||
55 | { | ||
56 | unsigned long mask = BIT_MASK(nr); | ||
57 | unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); | ||
58 | unsigned long old; | ||
59 | |||
60 | old = *p; | ||
61 | *p = old | mask; | ||
62 | |||
63 | return (old & mask) != 0; | ||
64 | } | ||
65 | |||
49 | #endif /* _PERF_BITOPS_H */ | 66 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h deleted file mode 100644 index dadfa7e54287..000000000000 --- a/tools/perf/util/include/linux/bitops.h +++ /dev/null | |||
@@ -1,160 +0,0 @@ | |||
1 | #ifndef _PERF_LINUX_BITOPS_H_ | ||
2 | #define _PERF_LINUX_BITOPS_H_ | ||
3 | |||
4 | #include <linux/kernel.h> | ||
5 | #include <linux/compiler.h> | ||
6 | #include <asm/hweight.h> | ||
7 | |||
8 | #ifndef __WORDSIZE | ||
9 | #define __WORDSIZE (__SIZEOF_LONG__ * 8) | ||
10 | #endif | ||
11 | |||
12 | #define BITS_PER_LONG __WORDSIZE | ||
13 | #define BITS_PER_BYTE 8 | ||
14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) | ||
15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) | ||
16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) | ||
17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) | ||
18 | |||
19 | #define for_each_set_bit(bit, addr, size) \ | ||
20 | for ((bit) = find_first_bit((addr), (size)); \ | ||
21 | (bit) < (size); \ | ||
22 | (bit) = find_next_bit((addr), (size), (bit) + 1)) | ||
23 | |||
24 | /* same as for_each_set_bit() but use bit as value to start with */ | ||
25 | #define for_each_set_bit_from(bit, addr, size) \ | ||
26 | for ((bit) = find_next_bit((addr), (size), (bit)); \ | ||
27 | (bit) < (size); \ | ||
28 | (bit) = find_next_bit((addr), (size), (bit) + 1)) | ||
29 | |||
30 | static inline void set_bit(int nr, unsigned long *addr) | ||
31 | { | ||
32 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); | ||
33 | } | ||
34 | |||
35 | static inline void clear_bit(int nr, unsigned long *addr) | ||
36 | { | ||
37 | addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG)); | ||
38 | } | ||
39 | |||
40 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) | ||
41 | { | ||
42 | return ((1UL << (nr % BITS_PER_LONG)) & | ||
43 | (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; | ||
44 | } | ||
45 | |||
46 | static inline unsigned long hweight_long(unsigned long w) | ||
47 | { | ||
48 | return sizeof(w) == 4 ? hweight32(w) : hweight64(w); | ||
49 | } | ||
50 | |||
51 | #define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) | ||
52 | |||
53 | /** | ||
54 | * __ffs - find first bit in word. | ||
55 | * @word: The word to search | ||
56 | * | ||
57 | * Undefined if no bit exists, so code should check against 0 first. | ||
58 | */ | ||
59 | static __always_inline unsigned long __ffs(unsigned long word) | ||
60 | { | ||
61 | int num = 0; | ||
62 | |||
63 | #if BITS_PER_LONG == 64 | ||
64 | if ((word & 0xffffffff) == 0) { | ||
65 | num += 32; | ||
66 | word >>= 32; | ||
67 | } | ||
68 | #endif | ||
69 | if ((word & 0xffff) == 0) { | ||
70 | num += 16; | ||
71 | word >>= 16; | ||
72 | } | ||
73 | if ((word & 0xff) == 0) { | ||
74 | num += 8; | ||
75 | word >>= 8; | ||
76 | } | ||
77 | if ((word & 0xf) == 0) { | ||
78 | num += 4; | ||
79 | word >>= 4; | ||
80 | } | ||
81 | if ((word & 0x3) == 0) { | ||
82 | num += 2; | ||
83 | word >>= 2; | ||
84 | } | ||
85 | if ((word & 0x1) == 0) | ||
86 | num += 1; | ||
87 | return num; | ||
88 | } | ||
89 | |||
90 | typedef const unsigned long __attribute__((__may_alias__)) long_alias_t; | ||
91 | |||
92 | /* | ||
93 | * Find the first set bit in a memory region. | ||
94 | */ | ||
95 | static inline unsigned long | ||
96 | find_first_bit(const unsigned long *addr, unsigned long size) | ||
97 | { | ||
98 | long_alias_t *p = (long_alias_t *) addr; | ||
99 | unsigned long result = 0; | ||
100 | unsigned long tmp; | ||
101 | |||
102 | while (size & ~(BITS_PER_LONG-1)) { | ||
103 | if ((tmp = *(p++))) | ||
104 | goto found; | ||
105 | result += BITS_PER_LONG; | ||
106 | size -= BITS_PER_LONG; | ||
107 | } | ||
108 | if (!size) | ||
109 | return result; | ||
110 | |||
111 | tmp = (*p) & (~0UL >> (BITS_PER_LONG - size)); | ||
112 | if (tmp == 0UL) /* Are any bits set? */ | ||
113 | return result + size; /* Nope. */ | ||
114 | found: | ||
115 | return result + __ffs(tmp); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Find the next set bit in a memory region. | ||
120 | */ | ||
121 | static inline unsigned long | ||
122 | find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) | ||
123 | { | ||
124 | const unsigned long *p = addr + BITOP_WORD(offset); | ||
125 | unsigned long result = offset & ~(BITS_PER_LONG-1); | ||
126 | unsigned long tmp; | ||
127 | |||
128 | if (offset >= size) | ||
129 | return size; | ||
130 | size -= result; | ||
131 | offset %= BITS_PER_LONG; | ||
132 | if (offset) { | ||
133 | tmp = *(p++); | ||
134 | tmp &= (~0UL << offset); | ||
135 | if (size < BITS_PER_LONG) | ||
136 | goto found_first; | ||
137 | if (tmp) | ||
138 | goto found_middle; | ||
139 | size -= BITS_PER_LONG; | ||
140 | result += BITS_PER_LONG; | ||
141 | } | ||
142 | while (size & ~(BITS_PER_LONG-1)) { | ||
143 | if ((tmp = *(p++))) | ||
144 | goto found_middle; | ||
145 | result += BITS_PER_LONG; | ||
146 | size -= BITS_PER_LONG; | ||
147 | } | ||
148 | if (!size) | ||
149 | return result; | ||
150 | tmp = *p; | ||
151 | |||
152 | found_first: | ||
153 | tmp &= (~0UL >> (BITS_PER_LONG - size)); | ||
154 | if (tmp == 0UL) /* Are any bits set? */ | ||
155 | return result + size; /* Nope. */ | ||
156 | found_middle: | ||
157 | return result + __ffs(tmp); | ||
158 | } | ||
159 | |||
160 | #endif | ||
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h index 97a800738226..6f19c548ecc0 100644 --- a/tools/perf/util/include/linux/string.h +++ b/tools/perf/util/include/linux/string.h | |||
@@ -1,4 +1,3 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | 2 | ||
3 | void *memdup(const void *src, size_t len); | 3 | void *memdup(const void *src, size_t len); |
4 | int str_append(char **s, int *len, const char *a); | ||
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index 0b5a8cd2ee79..cf1d7913783b 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h | |||
@@ -92,7 +92,6 @@ struct perf_kvm_stat { | |||
92 | u64 lost_events; | 92 | u64 lost_events; |
93 | u64 duration; | 93 | u64 duration; |
94 | 94 | ||
95 | const char *pid_str; | ||
96 | struct intlist *pid_list; | 95 | struct intlist *pid_list; |
97 | 96 | ||
98 | struct rb_root result; | 97 | struct rb_root result; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 16bba9fff2c8..94de3e48b490 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -12,13 +12,20 @@ | |||
12 | #include <stdbool.h> | 12 | #include <stdbool.h> |
13 | #include <symbol/kallsyms.h> | 13 | #include <symbol/kallsyms.h> |
14 | #include "unwind.h" | 14 | #include "unwind.h" |
15 | #include "linux/hash.h" | ||
16 | |||
17 | static void dsos__init(struct dsos *dsos) | ||
18 | { | ||
19 | INIT_LIST_HEAD(&dsos->head); | ||
20 | dsos->root = RB_ROOT; | ||
21 | } | ||
15 | 22 | ||
16 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 23 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
17 | { | 24 | { |
18 | map_groups__init(&machine->kmaps); | 25 | map_groups__init(&machine->kmaps, machine); |
19 | RB_CLEAR_NODE(&machine->rb_node); | 26 | RB_CLEAR_NODE(&machine->rb_node); |
20 | INIT_LIST_HEAD(&machine->user_dsos); | 27 | dsos__init(&machine->user_dsos); |
21 | INIT_LIST_HEAD(&machine->kernel_dsos); | 28 | dsos__init(&machine->kernel_dsos); |
22 | 29 | ||
23 | machine->threads = RB_ROOT; | 30 | machine->threads = RB_ROOT; |
24 | INIT_LIST_HEAD(&machine->dead_threads); | 31 | INIT_LIST_HEAD(&machine->dead_threads); |
@@ -26,11 +33,12 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
26 | 33 | ||
27 | machine->vdso_info = NULL; | 34 | machine->vdso_info = NULL; |
28 | 35 | ||
29 | machine->kmaps.machine = machine; | ||
30 | machine->pid = pid; | 36 | machine->pid = pid; |
31 | 37 | ||
32 | machine->symbol_filter = NULL; | 38 | machine->symbol_filter = NULL; |
33 | machine->id_hdr_size = 0; | 39 | machine->id_hdr_size = 0; |
40 | machine->comm_exec = false; | ||
41 | machine->kernel_start = 0; | ||
34 | 42 | ||
35 | machine->root_dir = strdup(root_dir); | 43 | machine->root_dir = strdup(root_dir); |
36 | if (machine->root_dir == NULL) | 44 | if (machine->root_dir == NULL) |
@@ -70,11 +78,12 @@ out_delete: | |||
70 | return NULL; | 78 | return NULL; |
71 | } | 79 | } |
72 | 80 | ||
73 | static void dsos__delete(struct list_head *dsos) | 81 | static void dsos__delete(struct dsos *dsos) |
74 | { | 82 | { |
75 | struct dso *pos, *n; | 83 | struct dso *pos, *n; |
76 | 84 | ||
77 | list_for_each_entry_safe(pos, n, dsos, node) { | 85 | list_for_each_entry_safe(pos, n, &dsos->head, node) { |
86 | RB_CLEAR_NODE(&pos->rb_node); | ||
78 | list_del(&pos->node); | 87 | list_del(&pos->node); |
79 | dso__delete(pos); | 88 | dso__delete(pos); |
80 | } | 89 | } |
@@ -179,6 +188,19 @@ void machines__set_symbol_filter(struct machines *machines, | |||
179 | } | 188 | } |
180 | } | 189 | } |
181 | 190 | ||
191 | void machines__set_comm_exec(struct machines *machines, bool comm_exec) | ||
192 | { | ||
193 | struct rb_node *nd; | ||
194 | |||
195 | machines->host.comm_exec = comm_exec; | ||
196 | |||
197 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
198 | struct machine *machine = rb_entry(nd, struct machine, rb_node); | ||
199 | |||
200 | machine->comm_exec = comm_exec; | ||
201 | } | ||
202 | } | ||
203 | |||
182 | struct machine *machines__find(struct machines *machines, pid_t pid) | 204 | struct machine *machines__find(struct machines *machines, pid_t pid) |
183 | { | 205 | { |
184 | struct rb_node **p = &machines->guests.rb_node; | 206 | struct rb_node **p = &machines->guests.rb_node; |
@@ -297,7 +319,7 @@ static void machine__update_thread_pid(struct machine *machine, | |||
297 | goto out_err; | 319 | goto out_err; |
298 | 320 | ||
299 | if (!leader->mg) | 321 | if (!leader->mg) |
300 | leader->mg = map_groups__new(); | 322 | leader->mg = map_groups__new(machine); |
301 | 323 | ||
302 | if (!leader->mg) | 324 | if (!leader->mg) |
303 | goto out_err; | 325 | goto out_err; |
@@ -398,17 +420,31 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid, | |||
398 | return __machine__findnew_thread(machine, pid, tid, false); | 420 | return __machine__findnew_thread(machine, pid, tid, false); |
399 | } | 421 | } |
400 | 422 | ||
423 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
424 | struct thread *thread) | ||
425 | { | ||
426 | if (machine->comm_exec) | ||
427 | return thread__exec_comm(thread); | ||
428 | else | ||
429 | return thread__comm(thread); | ||
430 | } | ||
431 | |||
401 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 432 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
402 | struct perf_sample *sample) | 433 | struct perf_sample *sample) |
403 | { | 434 | { |
404 | struct thread *thread = machine__findnew_thread(machine, | 435 | struct thread *thread = machine__findnew_thread(machine, |
405 | event->comm.pid, | 436 | event->comm.pid, |
406 | event->comm.tid); | 437 | event->comm.tid); |
438 | bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; | ||
439 | |||
440 | if (exec) | ||
441 | machine->comm_exec = true; | ||
407 | 442 | ||
408 | if (dump_trace) | 443 | if (dump_trace) |
409 | perf_event__fprintf_comm(event, stdout); | 444 | perf_event__fprintf_comm(event, stdout); |
410 | 445 | ||
411 | if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { | 446 | if (thread == NULL || |
447 | __thread__set_comm(thread, event->comm.comm, sample->time, exec)) { | ||
412 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 448 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
413 | return -1; | 449 | return -1; |
414 | } | 450 | } |
@@ -429,6 +465,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
429 | { | 465 | { |
430 | struct map *map; | 466 | struct map *map; |
431 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | 467 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); |
468 | bool compressed; | ||
432 | 469 | ||
433 | if (dso == NULL) | 470 | if (dso == NULL) |
434 | return NULL; | 471 | return NULL; |
@@ -441,6 +478,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
441 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | 478 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; |
442 | else | 479 | else |
443 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | 480 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; |
481 | |||
482 | /* _KMODULE_COMP should be next to _KMODULE */ | ||
483 | if (is_kernel_module(filename, &compressed) && compressed) | ||
484 | dso->symtab_type++; | ||
485 | |||
444 | map_groups__insert(&machine->kmaps, map); | 486 | map_groups__insert(&machine->kmaps, map); |
445 | return map; | 487 | return map; |
446 | } | 488 | } |
@@ -448,23 +490,23 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
448 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | 490 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) |
449 | { | 491 | { |
450 | struct rb_node *nd; | 492 | struct rb_node *nd; |
451 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | 493 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) + |
452 | __dsos__fprintf(&machines->host.user_dsos, fp); | 494 | __dsos__fprintf(&machines->host.user_dsos.head, fp); |
453 | 495 | ||
454 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | 496 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { |
455 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 497 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
456 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | 498 | ret += __dsos__fprintf(&pos->kernel_dsos.head, fp); |
457 | ret += __dsos__fprintf(&pos->user_dsos, fp); | 499 | ret += __dsos__fprintf(&pos->user_dsos.head, fp); |
458 | } | 500 | } |
459 | 501 | ||
460 | return ret; | 502 | return ret; |
461 | } | 503 | } |
462 | 504 | ||
463 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 505 | size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp, |
464 | bool (skip)(struct dso *dso, int parm), int parm) | 506 | bool (skip)(struct dso *dso, int parm), int parm) |
465 | { | 507 | { |
466 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | 508 | return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) + |
467 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | 509 | __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm); |
468 | } | 510 | } |
469 | 511 | ||
470 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | 512 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, |
@@ -565,8 +607,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; | |||
565 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as | 607 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as |
566 | * symbol_name if it's not that important. | 608 | * symbol_name if it's not that important. |
567 | */ | 609 | */ |
568 | static u64 machine__get_kernel_start_addr(struct machine *machine, | 610 | static u64 machine__get_running_kernel_start(struct machine *machine, |
569 | const char **symbol_name) | 611 | const char **symbol_name) |
570 | { | 612 | { |
571 | char filename[PATH_MAX]; | 613 | char filename[PATH_MAX]; |
572 | int i; | 614 | int i; |
@@ -593,7 +635,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine, | |||
593 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | 635 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) |
594 | { | 636 | { |
595 | enum map_type type; | 637 | enum map_type type; |
596 | u64 start = machine__get_kernel_start_addr(machine, NULL); | 638 | u64 start = machine__get_running_kernel_start(machine, NULL); |
597 | 639 | ||
598 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 640 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
599 | struct kmap *kmap; | 641 | struct kmap *kmap; |
@@ -826,8 +868,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, | |||
826 | struct map *map; | 868 | struct map *map; |
827 | char *long_name; | 869 | char *long_name; |
828 | 870 | ||
829 | if (dot == NULL || strcmp(dot, ".ko")) | 871 | if (dot == NULL) |
830 | continue; | 872 | continue; |
873 | |||
874 | /* On some system, modules are compressed like .ko.gz */ | ||
875 | if (is_supported_compression(dot + 1) && | ||
876 | is_kmodule_extension(dot - 2)) | ||
877 | dot -= 3; | ||
878 | |||
831 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 879 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", |
832 | (int)(dot - dent->d_name), dent->d_name); | 880 | (int)(dot - dent->d_name), dent->d_name); |
833 | 881 | ||
@@ -912,7 +960,7 @@ int machine__create_kernel_maps(struct machine *machine) | |||
912 | { | 960 | { |
913 | struct dso *kernel = machine__get_kernel(machine); | 961 | struct dso *kernel = machine__get_kernel(machine); |
914 | const char *name; | 962 | const char *name; |
915 | u64 addr = machine__get_kernel_start_addr(machine, &name); | 963 | u64 addr = machine__get_running_kernel_start(machine, &name); |
916 | if (!addr) | 964 | if (!addr) |
917 | return -1; | 965 | return -1; |
918 | 966 | ||
@@ -965,7 +1013,7 @@ static bool machine__uses_kcore(struct machine *machine) | |||
965 | { | 1013 | { |
966 | struct dso *dso; | 1014 | struct dso *dso; |
967 | 1015 | ||
968 | list_for_each_entry(dso, &machine->kernel_dsos, node) { | 1016 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { |
969 | if (dso__is_kcore(dso)) | 1017 | if (dso__is_kcore(dso)) |
970 | return true; | 1018 | return true; |
971 | } | 1019 | } |
@@ -1009,6 +1057,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1009 | dot = strrchr(name, '.'); | 1057 | dot = strrchr(name, '.'); |
1010 | if (dot == NULL) | 1058 | if (dot == NULL) |
1011 | goto out_problem; | 1059 | goto out_problem; |
1060 | /* On some system, modules are compressed like .ko.gz */ | ||
1061 | if (is_supported_compression(dot + 1)) | ||
1062 | dot -= 3; | ||
1063 | if (!is_kmodule_extension(dot + 1)) | ||
1064 | goto out_problem; | ||
1012 | snprintf(short_module_name, sizeof(short_module_name), | 1065 | snprintf(short_module_name, sizeof(short_module_name), |
1013 | "[%.*s]", (int)(dot - name), name); | 1066 | "[%.*s]", (int)(dot - name), name); |
1014 | strxfrchar(short_module_name, '-', '_'); | 1067 | strxfrchar(short_module_name, '-', '_'); |
@@ -1033,8 +1086,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1033 | * Should be there already, from the build-id table in | 1086 | * Should be there already, from the build-id table in |
1034 | * the header. | 1087 | * the header. |
1035 | */ | 1088 | */ |
1036 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | 1089 | struct dso *kernel = NULL; |
1037 | kmmap_prefix); | 1090 | struct dso *dso; |
1091 | |||
1092 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { | ||
1093 | if (is_kernel_module(dso->long_name, NULL)) | ||
1094 | continue; | ||
1095 | |||
1096 | kernel = dso; | ||
1097 | break; | ||
1098 | } | ||
1099 | |||
1100 | if (kernel == NULL) | ||
1101 | kernel = __dsos__findnew(&machine->kernel_dsos, | ||
1102 | kmmap_prefix); | ||
1038 | if (kernel == NULL) | 1103 | if (kernel == NULL) |
1039 | goto out_problem; | 1104 | goto out_problem; |
1040 | 1105 | ||
@@ -1042,6 +1107,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1042 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 1107 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
1043 | goto out_problem; | 1108 | goto out_problem; |
1044 | 1109 | ||
1110 | if (strstr(kernel->long_name, "vmlinux")) | ||
1111 | dso__set_short_name(kernel, "[kernel.vmlinux]", false); | ||
1112 | |||
1045 | machine__set_kernel_mmap_len(machine, event); | 1113 | machine__set_kernel_mmap_len(machine, event); |
1046 | 1114 | ||
1047 | /* | 1115 | /* |
@@ -1254,7 +1322,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex) | |||
1254 | return 0; | 1322 | return 0; |
1255 | } | 1323 | } |
1256 | 1324 | ||
1257 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | 1325 | static void ip__resolve_ams(struct thread *thread, |
1258 | struct addr_map_symbol *ams, | 1326 | struct addr_map_symbol *ams, |
1259 | u64 ip) | 1327 | u64 ip) |
1260 | { | 1328 | { |
@@ -1268,7 +1336,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
1268 | * Thus, we have to try consecutively until we find a match | 1336 | * Thus, we have to try consecutively until we find a match |
1269 | * or else, the symbol is unknown | 1337 | * or else, the symbol is unknown |
1270 | */ | 1338 | */ |
1271 | thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); | 1339 | thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al); |
1272 | 1340 | ||
1273 | ams->addr = ip; | 1341 | ams->addr = ip; |
1274 | ams->al_addr = al.addr; | 1342 | ams->al_addr = al.addr; |
@@ -1276,15 +1344,23 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
1276 | ams->map = al.map; | 1344 | ams->map = al.map; |
1277 | } | 1345 | } |
1278 | 1346 | ||
1279 | static void ip__resolve_data(struct machine *machine, struct thread *thread, | 1347 | static void ip__resolve_data(struct thread *thread, |
1280 | u8 m, struct addr_map_symbol *ams, u64 addr) | 1348 | u8 m, struct addr_map_symbol *ams, u64 addr) |
1281 | { | 1349 | { |
1282 | struct addr_location al; | 1350 | struct addr_location al; |
1283 | 1351 | ||
1284 | memset(&al, 0, sizeof(al)); | 1352 | memset(&al, 0, sizeof(al)); |
1285 | 1353 | ||
1286 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1354 | thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al); |
1287 | &al); | 1355 | if (al.map == NULL) { |
1356 | /* | ||
1357 | * some shared data regions have execute bit set which puts | ||
1358 | * their mapping in the MAP__FUNCTION type array. | ||
1359 | * Check there as a fallback option before dropping the sample. | ||
1360 | */ | ||
1361 | thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al); | ||
1362 | } | ||
1363 | |||
1288 | ams->addr = addr; | 1364 | ams->addr = addr; |
1289 | ams->al_addr = al.addr; | 1365 | ams->al_addr = al.addr; |
1290 | ams->sym = al.sym; | 1366 | ams->sym = al.sym; |
@@ -1299,14 +1375,72 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, | |||
1299 | if (!mi) | 1375 | if (!mi) |
1300 | return NULL; | 1376 | return NULL; |
1301 | 1377 | ||
1302 | ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); | 1378 | ip__resolve_ams(al->thread, &mi->iaddr, sample->ip); |
1303 | ip__resolve_data(al->machine, al->thread, al->cpumode, | 1379 | ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr); |
1304 | &mi->daddr, sample->addr); | ||
1305 | mi->data_src.val = sample->data_src; | 1380 | mi->data_src.val = sample->data_src; |
1306 | 1381 | ||
1307 | return mi; | 1382 | return mi; |
1308 | } | 1383 | } |
1309 | 1384 | ||
1385 | static int add_callchain_ip(struct thread *thread, | ||
1386 | struct symbol **parent, | ||
1387 | struct addr_location *root_al, | ||
1388 | bool branch_history, | ||
1389 | u64 ip) | ||
1390 | { | ||
1391 | struct addr_location al; | ||
1392 | |||
1393 | al.filtered = 0; | ||
1394 | al.sym = NULL; | ||
1395 | if (branch_history) | ||
1396 | thread__find_cpumode_addr_location(thread, MAP__FUNCTION, | ||
1397 | ip, &al); | ||
1398 | else { | ||
1399 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
1400 | |||
1401 | if (ip >= PERF_CONTEXT_MAX) { | ||
1402 | switch (ip) { | ||
1403 | case PERF_CONTEXT_HV: | ||
1404 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
1405 | break; | ||
1406 | case PERF_CONTEXT_KERNEL: | ||
1407 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
1408 | break; | ||
1409 | case PERF_CONTEXT_USER: | ||
1410 | cpumode = PERF_RECORD_MISC_USER; | ||
1411 | break; | ||
1412 | default: | ||
1413 | pr_debug("invalid callchain context: " | ||
1414 | "%"PRId64"\n", (s64) ip); | ||
1415 | /* | ||
1416 | * It seems the callchain is corrupted. | ||
1417 | * Discard all. | ||
1418 | */ | ||
1419 | callchain_cursor_reset(&callchain_cursor); | ||
1420 | return 1; | ||
1421 | } | ||
1422 | return 0; | ||
1423 | } | ||
1424 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | ||
1425 | ip, &al); | ||
1426 | } | ||
1427 | |||
1428 | if (al.sym != NULL) { | ||
1429 | if (sort__has_parent && !*parent && | ||
1430 | symbol__match_regex(al.sym, &parent_regex)) | ||
1431 | *parent = al.sym; | ||
1432 | else if (have_ignore_callees && root_al && | ||
1433 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
1434 | /* Treat this symbol as the root, | ||
1435 | forgetting its callees. */ | ||
1436 | *root_al = al; | ||
1437 | callchain_cursor_reset(&callchain_cursor); | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); | ||
1442 | } | ||
1443 | |||
1310 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | 1444 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, |
1311 | struct addr_location *al) | 1445 | struct addr_location *al) |
1312 | { | 1446 | { |
@@ -1318,43 +1452,140 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
1318 | return NULL; | 1452 | return NULL; |
1319 | 1453 | ||
1320 | for (i = 0; i < bs->nr; i++) { | 1454 | for (i = 0; i < bs->nr; i++) { |
1321 | ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); | 1455 | ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to); |
1322 | ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from); | 1456 | ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from); |
1323 | bi[i].flags = bs->entries[i].flags; | 1457 | bi[i].flags = bs->entries[i].flags; |
1324 | } | 1458 | } |
1325 | return bi; | 1459 | return bi; |
1326 | } | 1460 | } |
1327 | 1461 | ||
1328 | static int machine__resolve_callchain_sample(struct machine *machine, | 1462 | #define CHASHSZ 127 |
1329 | struct thread *thread, | 1463 | #define CHASHBITS 7 |
1464 | #define NO_ENTRY 0xff | ||
1465 | |||
1466 | #define PERF_MAX_BRANCH_DEPTH 127 | ||
1467 | |||
1468 | /* Remove loops. */ | ||
1469 | static int remove_loops(struct branch_entry *l, int nr) | ||
1470 | { | ||
1471 | int i, j, off; | ||
1472 | unsigned char chash[CHASHSZ]; | ||
1473 | |||
1474 | memset(chash, NO_ENTRY, sizeof(chash)); | ||
1475 | |||
1476 | BUG_ON(PERF_MAX_BRANCH_DEPTH > 255); | ||
1477 | |||
1478 | for (i = 0; i < nr; i++) { | ||
1479 | int h = hash_64(l[i].from, CHASHBITS) % CHASHSZ; | ||
1480 | |||
1481 | /* no collision handling for now */ | ||
1482 | if (chash[h] == NO_ENTRY) { | ||
1483 | chash[h] = i; | ||
1484 | } else if (l[chash[h]].from == l[i].from) { | ||
1485 | bool is_loop = true; | ||
1486 | /* check if it is a real loop */ | ||
1487 | off = 0; | ||
1488 | for (j = chash[h]; j < i && i + off < nr; j++, off++) | ||
1489 | if (l[j].from != l[i + off].from) { | ||
1490 | is_loop = false; | ||
1491 | break; | ||
1492 | } | ||
1493 | if (is_loop) { | ||
1494 | memmove(l + i, l + i + off, | ||
1495 | (nr - (i + off)) * sizeof(*l)); | ||
1496 | nr -= off; | ||
1497 | } | ||
1498 | } | ||
1499 | } | ||
1500 | return nr; | ||
1501 | } | ||
1502 | |||
1503 | static int thread__resolve_callchain_sample(struct thread *thread, | ||
1330 | struct ip_callchain *chain, | 1504 | struct ip_callchain *chain, |
1505 | struct branch_stack *branch, | ||
1331 | struct symbol **parent, | 1506 | struct symbol **parent, |
1332 | struct addr_location *root_al, | 1507 | struct addr_location *root_al, |
1333 | int max_stack) | 1508 | int max_stack) |
1334 | { | 1509 | { |
1335 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
1336 | int chain_nr = min(max_stack, (int)chain->nr); | 1510 | int chain_nr = min(max_stack, (int)chain->nr); |
1337 | int i; | 1511 | int i, j, err; |
1338 | int j; | 1512 | int skip_idx = -1; |
1339 | int err; | 1513 | int first_call = 0; |
1340 | int skip_idx __maybe_unused; | 1514 | |
1515 | /* | ||
1516 | * Based on DWARF debug information, some architectures skip | ||
1517 | * a callchain entry saved by the kernel. | ||
1518 | */ | ||
1519 | if (chain->nr < PERF_MAX_STACK_DEPTH) | ||
1520 | skip_idx = arch_skip_callchain_idx(thread, chain); | ||
1341 | 1521 | ||
1342 | callchain_cursor_reset(&callchain_cursor); | 1522 | callchain_cursor_reset(&callchain_cursor); |
1343 | 1523 | ||
1524 | /* | ||
1525 | * Add branches to call stack for easier browsing. This gives | ||
1526 | * more context for a sample than just the callers. | ||
1527 | * | ||
1528 | * This uses individual histograms of paths compared to the | ||
1529 | * aggregated histograms the normal LBR mode uses. | ||
1530 | * | ||
1531 | * Limitations for now: | ||
1532 | * - No extra filters | ||
1533 | * - No annotations (should annotate somehow) | ||
1534 | */ | ||
1535 | |||
1536 | if (branch && callchain_param.branch_callstack) { | ||
1537 | int nr = min(max_stack, (int)branch->nr); | ||
1538 | struct branch_entry be[nr]; | ||
1539 | |||
1540 | if (branch->nr > PERF_MAX_BRANCH_DEPTH) { | ||
1541 | pr_warning("corrupted branch chain. skipping...\n"); | ||
1542 | goto check_calls; | ||
1543 | } | ||
1544 | |||
1545 | for (i = 0; i < nr; i++) { | ||
1546 | if (callchain_param.order == ORDER_CALLEE) { | ||
1547 | be[i] = branch->entries[i]; | ||
1548 | /* | ||
1549 | * Check for overlap into the callchain. | ||
1550 | * The return address is one off compared to | ||
1551 | * the branch entry. To adjust for this | ||
1552 | * assume the calling instruction is not longer | ||
1553 | * than 8 bytes. | ||
1554 | */ | ||
1555 | if (i == skip_idx || | ||
1556 | chain->ips[first_call] >= PERF_CONTEXT_MAX) | ||
1557 | first_call++; | ||
1558 | else if (be[i].from < chain->ips[first_call] && | ||
1559 | be[i].from >= chain->ips[first_call] - 8) | ||
1560 | first_call++; | ||
1561 | } else | ||
1562 | be[i] = branch->entries[branch->nr - i - 1]; | ||
1563 | } | ||
1564 | |||
1565 | nr = remove_loops(be, nr); | ||
1566 | |||
1567 | for (i = 0; i < nr; i++) { | ||
1568 | err = add_callchain_ip(thread, parent, root_al, | ||
1569 | true, be[i].to); | ||
1570 | if (!err) | ||
1571 | err = add_callchain_ip(thread, parent, root_al, | ||
1572 | true, be[i].from); | ||
1573 | if (err == -EINVAL) | ||
1574 | break; | ||
1575 | if (err) | ||
1576 | return err; | ||
1577 | } | ||
1578 | chain_nr -= nr; | ||
1579 | } | ||
1580 | |||
1581 | check_calls: | ||
1344 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | 1582 | if (chain->nr > PERF_MAX_STACK_DEPTH) { |
1345 | pr_warning("corrupted callchain. skipping...\n"); | 1583 | pr_warning("corrupted callchain. skipping...\n"); |
1346 | return 0; | 1584 | return 0; |
1347 | } | 1585 | } |
1348 | 1586 | ||
1349 | /* | 1587 | for (i = first_call; i < chain_nr; i++) { |
1350 | * Based on DWARF debug information, some architectures skip | ||
1351 | * a callchain entry saved by the kernel. | ||
1352 | */ | ||
1353 | skip_idx = arch_skip_callchain_idx(machine, thread, chain); | ||
1354 | |||
1355 | for (i = 0; i < chain_nr; i++) { | ||
1356 | u64 ip; | 1588 | u64 ip; |
1357 | struct addr_location al; | ||
1358 | 1589 | ||
1359 | if (callchain_param.order == ORDER_CALLEE) | 1590 | if (callchain_param.order == ORDER_CALLEE) |
1360 | j = i; | 1591 | j = i; |
@@ -1367,50 +1598,10 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
1367 | #endif | 1598 | #endif |
1368 | ip = chain->ips[j]; | 1599 | ip = chain->ips[j]; |
1369 | 1600 | ||
1370 | if (ip >= PERF_CONTEXT_MAX) { | 1601 | err = add_callchain_ip(thread, parent, root_al, false, ip); |
1371 | switch (ip) { | ||
1372 | case PERF_CONTEXT_HV: | ||
1373 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
1374 | break; | ||
1375 | case PERF_CONTEXT_KERNEL: | ||
1376 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
1377 | break; | ||
1378 | case PERF_CONTEXT_USER: | ||
1379 | cpumode = PERF_RECORD_MISC_USER; | ||
1380 | break; | ||
1381 | default: | ||
1382 | pr_debug("invalid callchain context: " | ||
1383 | "%"PRId64"\n", (s64) ip); | ||
1384 | /* | ||
1385 | * It seems the callchain is corrupted. | ||
1386 | * Discard all. | ||
1387 | */ | ||
1388 | callchain_cursor_reset(&callchain_cursor); | ||
1389 | return 0; | ||
1390 | } | ||
1391 | continue; | ||
1392 | } | ||
1393 | |||
1394 | al.filtered = 0; | ||
1395 | thread__find_addr_location(thread, machine, cpumode, | ||
1396 | MAP__FUNCTION, ip, &al); | ||
1397 | if (al.sym != NULL) { | ||
1398 | if (sort__has_parent && !*parent && | ||
1399 | symbol__match_regex(al.sym, &parent_regex)) | ||
1400 | *parent = al.sym; | ||
1401 | else if (have_ignore_callees && root_al && | ||
1402 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
1403 | /* Treat this symbol as the root, | ||
1404 | forgetting its callees. */ | ||
1405 | *root_al = al; | ||
1406 | callchain_cursor_reset(&callchain_cursor); | ||
1407 | } | ||
1408 | } | ||
1409 | 1602 | ||
1410 | err = callchain_cursor_append(&callchain_cursor, | ||
1411 | ip, al.map, al.sym); | ||
1412 | if (err) | 1603 | if (err) |
1413 | return err; | 1604 | return (err < 0) ? err : 0; |
1414 | } | 1605 | } |
1415 | 1606 | ||
1416 | return 0; | 1607 | return 0; |
@@ -1423,19 +1614,16 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
1423 | entry->map, entry->sym); | 1614 | entry->map, entry->sym); |
1424 | } | 1615 | } |
1425 | 1616 | ||
1426 | int machine__resolve_callchain(struct machine *machine, | 1617 | int thread__resolve_callchain(struct thread *thread, |
1427 | struct perf_evsel *evsel, | 1618 | struct perf_evsel *evsel, |
1428 | struct thread *thread, | 1619 | struct perf_sample *sample, |
1429 | struct perf_sample *sample, | 1620 | struct symbol **parent, |
1430 | struct symbol **parent, | 1621 | struct addr_location *root_al, |
1431 | struct addr_location *root_al, | 1622 | int max_stack) |
1432 | int max_stack) | ||
1433 | { | 1623 | { |
1434 | int ret; | 1624 | int ret = thread__resolve_callchain_sample(thread, sample->callchain, |
1435 | 1625 | sample->branch_stack, | |
1436 | ret = machine__resolve_callchain_sample(machine, thread, | 1626 | parent, root_al, max_stack); |
1437 | sample->callchain, parent, | ||
1438 | root_al, max_stack); | ||
1439 | if (ret) | 1627 | if (ret) |
1440 | return ret; | 1628 | return ret; |
1441 | 1629 | ||
@@ -1449,7 +1637,7 @@ int machine__resolve_callchain(struct machine *machine, | |||
1449 | (!sample->user_stack.size)) | 1637 | (!sample->user_stack.size)) |
1450 | return 0; | 1638 | return 0; |
1451 | 1639 | ||
1452 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | 1640 | return unwind__get_entries(unwind_entry, &callchain_cursor, |
1453 | thread, sample, max_stack); | 1641 | thread, sample, max_stack); |
1454 | 1642 | ||
1455 | } | 1643 | } |
@@ -1531,3 +1719,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, | |||
1531 | 1719 | ||
1532 | return 0; | 1720 | return 0; |
1533 | } | 1721 | } |
1722 | |||
1723 | int machine__get_kernel_start(struct machine *machine) | ||
1724 | { | ||
1725 | struct map *map = machine__kernel_map(machine, MAP__FUNCTION); | ||
1726 | int err = 0; | ||
1727 | |||
1728 | /* | ||
1729 | * The only addresses above 2^63 are kernel addresses of a 64-bit | ||
1730 | * kernel. Note that addresses are unsigned so that on a 32-bit system | ||
1731 | * all addresses including kernel addresses are less than 2^32. In | ||
1732 | * that case (32-bit system), if the kernel mapping is unknown, all | ||
1733 | * addresses will be assumed to be in user space - see | ||
1734 | * machine__kernel_ip(). | ||
1735 | */ | ||
1736 | machine->kernel_start = 1ULL << 63; | ||
1737 | if (map) { | ||
1738 | err = map__load(map, machine->symbol_filter); | ||
1739 | if (map->start) | ||
1740 | machine->kernel_start = map->start; | ||
1741 | } | ||
1742 | return err; | ||
1743 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index b972824e6294..e8b7779a0a3f 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include "map.h" | 6 | #include "map.h" |
7 | #include "dso.h" | ||
7 | #include "event.h" | 8 | #include "event.h" |
8 | 9 | ||
9 | struct addr_location; | 10 | struct addr_location; |
@@ -26,17 +27,23 @@ struct machine { | |||
26 | struct rb_node rb_node; | 27 | struct rb_node rb_node; |
27 | pid_t pid; | 28 | pid_t pid; |
28 | u16 id_hdr_size; | 29 | u16 id_hdr_size; |
30 | bool comm_exec; | ||
29 | char *root_dir; | 31 | char *root_dir; |
30 | struct rb_root threads; | 32 | struct rb_root threads; |
31 | struct list_head dead_threads; | 33 | struct list_head dead_threads; |
32 | struct thread *last_match; | 34 | struct thread *last_match; |
33 | struct vdso_info *vdso_info; | 35 | struct vdso_info *vdso_info; |
34 | struct list_head user_dsos; | 36 | struct dsos user_dsos; |
35 | struct list_head kernel_dsos; | 37 | struct dsos kernel_dsos; |
36 | struct map_groups kmaps; | 38 | struct map_groups kmaps; |
37 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 39 | struct map *vmlinux_maps[MAP__NR_TYPES]; |
40 | u64 kernel_start; | ||
38 | symbol_filter_t symbol_filter; | 41 | symbol_filter_t symbol_filter; |
39 | pid_t *current_tid; | 42 | pid_t *current_tid; |
43 | union { /* Tool specific area */ | ||
44 | void *priv; | ||
45 | u64 db_id; | ||
46 | }; | ||
40 | }; | 47 | }; |
41 | 48 | ||
42 | static inline | 49 | static inline |
@@ -45,8 +52,26 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
45 | return machine->vmlinux_maps[type]; | 52 | return machine->vmlinux_maps[type]; |
46 | } | 53 | } |
47 | 54 | ||
55 | int machine__get_kernel_start(struct machine *machine); | ||
56 | |||
57 | static inline u64 machine__kernel_start(struct machine *machine) | ||
58 | { | ||
59 | if (!machine->kernel_start) | ||
60 | machine__get_kernel_start(machine); | ||
61 | return machine->kernel_start; | ||
62 | } | ||
63 | |||
64 | static inline bool machine__kernel_ip(struct machine *machine, u64 ip) | ||
65 | { | ||
66 | u64 kernel_start = machine__kernel_start(machine); | ||
67 | |||
68 | return ip >= kernel_start; | ||
69 | } | ||
70 | |||
48 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, | 71 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
49 | pid_t tid); | 72 | pid_t tid); |
73 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
74 | struct thread *thread); | ||
50 | 75 | ||
51 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 76 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
52 | struct perf_sample *sample); | 77 | struct perf_sample *sample); |
@@ -88,6 +113,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | |||
88 | 113 | ||
89 | void machines__set_symbol_filter(struct machines *machines, | 114 | void machines__set_symbol_filter(struct machines *machines, |
90 | symbol_filter_t symbol_filter); | 115 | symbol_filter_t symbol_filter); |
116 | void machines__set_comm_exec(struct machines *machines, bool comm_exec); | ||
91 | 117 | ||
92 | struct machine *machine__new_host(void); | 118 | struct machine *machine__new_host(void); |
93 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 119 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
@@ -100,13 +126,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
100 | struct addr_location *al); | 126 | struct addr_location *al); |
101 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, | 127 | struct mem_info *sample__resolve_mem(struct perf_sample *sample, |
102 | struct addr_location *al); | 128 | struct addr_location *al); |
103 | int machine__resolve_callchain(struct machine *machine, | 129 | int thread__resolve_callchain(struct thread *thread, |
104 | struct perf_evsel *evsel, | 130 | struct perf_evsel *evsel, |
105 | struct thread *thread, | 131 | struct perf_sample *sample, |
106 | struct perf_sample *sample, | 132 | struct symbol **parent, |
107 | struct symbol **parent, | 133 | struct addr_location *root_al, |
108 | struct addr_location *root_al, | 134 | int max_stack); |
109 | int max_stack); | ||
110 | 135 | ||
111 | /* | 136 | /* |
112 | * Default guest kernel is defined by parameter --guestkallsyms | 137 | * Default guest kernel is defined by parameter --guestkallsyms |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 31b8905dd863..62ca9f2607d5 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename) | |||
31 | static inline int is_no_dso_memory(const char *filename) | 31 | static inline int is_no_dso_memory(const char *filename) |
32 | { | 32 | { |
33 | return !strncmp(filename, "[stack", 6) || | 33 | return !strncmp(filename, "[stack", 6) || |
34 | !strncmp(filename, "/SYSV",5) || | ||
34 | !strcmp(filename, "[heap]"); | 35 | !strcmp(filename, "[heap]"); |
35 | } | 36 | } |
36 | 37 | ||
@@ -359,7 +360,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, | |||
359 | 360 | ||
360 | if (map && map->dso) { | 361 | if (map && map->dso) { |
361 | srcline = get_srcline(map->dso, | 362 | srcline = get_srcline(map->dso, |
362 | map__rip_2objdump(map, addr)); | 363 | map__rip_2objdump(map, addr), NULL, true); |
363 | if (srcline != SRCLINE_UNKNOWN) | 364 | if (srcline != SRCLINE_UNKNOWN) |
364 | ret = fprintf(fp, "%s%s", prefix, srcline); | 365 | ret = fprintf(fp, "%s%s", prefix, srcline); |
365 | free_srcline(srcline); | 366 | free_srcline(srcline); |
@@ -412,14 +413,14 @@ u64 map__objdump_2mem(struct map *map, u64 ip) | |||
412 | return ip + map->reloc; | 413 | return ip + map->reloc; |
413 | } | 414 | } |
414 | 415 | ||
415 | void map_groups__init(struct map_groups *mg) | 416 | void map_groups__init(struct map_groups *mg, struct machine *machine) |
416 | { | 417 | { |
417 | int i; | 418 | int i; |
418 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 419 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
419 | mg->maps[i] = RB_ROOT; | 420 | mg->maps[i] = RB_ROOT; |
420 | INIT_LIST_HEAD(&mg->removed_maps[i]); | 421 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
421 | } | 422 | } |
422 | mg->machine = NULL; | 423 | mg->machine = machine; |
423 | mg->refcnt = 1; | 424 | mg->refcnt = 1; |
424 | } | 425 | } |
425 | 426 | ||
@@ -470,12 +471,12 @@ bool map_groups__empty(struct map_groups *mg) | |||
470 | return true; | 471 | return true; |
471 | } | 472 | } |
472 | 473 | ||
473 | struct map_groups *map_groups__new(void) | 474 | struct map_groups *map_groups__new(struct machine *machine) |
474 | { | 475 | { |
475 | struct map_groups *mg = malloc(sizeof(*mg)); | 476 | struct map_groups *mg = malloc(sizeof(*mg)); |
476 | 477 | ||
477 | if (mg != NULL) | 478 | if (mg != NULL) |
478 | map_groups__init(mg); | 479 | map_groups__init(mg, machine); |
479 | 480 | ||
480 | return mg; | 481 | return mg; |
481 | } | 482 | } |
@@ -555,7 +556,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
555 | 556 | ||
556 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) | 557 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) |
557 | { | 558 | { |
558 | if (ams->addr < ams->map->start || ams->addr > ams->map->end) { | 559 | if (ams->addr < ams->map->start || ams->addr >= ams->map->end) { |
559 | if (ams->map->groups == NULL) | 560 | if (ams->map->groups == NULL) |
560 | return -1; | 561 | return -1; |
561 | ams->map = map_groups__find(ams->map->groups, ams->map->type, | 562 | ams->map = map_groups__find(ams->map->groups, ams->map->type, |
@@ -663,7 +664,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | |||
663 | goto move_map; | 664 | goto move_map; |
664 | } | 665 | } |
665 | 666 | ||
666 | before->end = map->start - 1; | 667 | before->end = map->start; |
667 | map_groups__insert(mg, before); | 668 | map_groups__insert(mg, before); |
668 | if (verbose >= 2) | 669 | if (verbose >= 2) |
669 | map__fprintf(before, fp); | 670 | map__fprintf(before, fp); |
@@ -677,7 +678,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | |||
677 | goto move_map; | 678 | goto move_map; |
678 | } | 679 | } |
679 | 680 | ||
680 | after->start = map->end + 1; | 681 | after->start = map->end; |
681 | map_groups__insert(mg, after); | 682 | map_groups__insert(mg, after); |
682 | if (verbose >= 2) | 683 | if (verbose >= 2) |
683 | map__fprintf(after, fp); | 684 | map__fprintf(after, fp); |
@@ -751,7 +752,7 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
751 | m = rb_entry(parent, struct map, rb_node); | 752 | m = rb_entry(parent, struct map, rb_node); |
752 | if (ip < m->start) | 753 | if (ip < m->start) |
753 | p = &(*p)->rb_left; | 754 | p = &(*p)->rb_left; |
754 | else if (ip > m->end) | 755 | else if (ip >= m->end) |
755 | p = &(*p)->rb_right; | 756 | p = &(*p)->rb_right; |
756 | else | 757 | else |
757 | return m; | 758 | return m; |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2f83954af050..6951a9d42339 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -64,7 +64,7 @@ struct map_groups { | |||
64 | int refcnt; | 64 | int refcnt; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | struct map_groups *map_groups__new(void); | 67 | struct map_groups *map_groups__new(struct machine *machine); |
68 | void map_groups__delete(struct map_groups *mg); | 68 | void map_groups__delete(struct map_groups *mg); |
69 | bool map_groups__empty(struct map_groups *mg); | 69 | bool map_groups__empty(struct map_groups *mg); |
70 | 70 | ||
@@ -150,7 +150,7 @@ void maps__remove(struct rb_root *maps, struct map *map); | |||
150 | struct map *maps__find(struct rb_root *maps, u64 addr); | 150 | struct map *maps__find(struct rb_root *maps, u64 addr); |
151 | struct map *maps__first(struct rb_root *maps); | 151 | struct map *maps__first(struct rb_root *maps); |
152 | struct map *maps__next(struct map *map); | 152 | struct map *maps__next(struct map *map); |
153 | void map_groups__init(struct map_groups *mg); | 153 | void map_groups__init(struct map_groups *mg, struct machine *machine); |
154 | void map_groups__exit(struct map_groups *mg); | 154 | void map_groups__exit(struct map_groups *mg); |
155 | int map_groups__clone(struct map_groups *mg, | 155 | int map_groups__clone(struct map_groups *mg, |
156 | struct map_groups *parent, enum map_type type); | 156 | struct map_groups *parent, enum map_type type); |
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c new file mode 100644 index 000000000000..fd4be94125fb --- /dev/null +++ b/tools/perf/util/ordered-events.c | |||
@@ -0,0 +1,286 @@ | |||
1 | #include <linux/list.h> | ||
2 | #include <linux/compiler.h> | ||
3 | #include <linux/string.h> | ||
4 | #include "ordered-events.h" | ||
5 | #include "evlist.h" | ||
6 | #include "session.h" | ||
7 | #include "asm/bug.h" | ||
8 | #include "debug.h" | ||
9 | |||
10 | #define pr_N(n, fmt, ...) \ | ||
11 | eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__) | ||
12 | |||
13 | #define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
14 | |||
15 | static void queue_event(struct ordered_events *oe, struct ordered_event *new) | ||
16 | { | ||
17 | struct ordered_event *last = oe->last; | ||
18 | u64 timestamp = new->timestamp; | ||
19 | struct list_head *p; | ||
20 | |||
21 | ++oe->nr_events; | ||
22 | oe->last = new; | ||
23 | |||
24 | pr_oe_time2(timestamp, "queue_event nr_events %u\n", oe->nr_events); | ||
25 | |||
26 | if (!last) { | ||
27 | list_add(&new->list, &oe->events); | ||
28 | oe->max_timestamp = timestamp; | ||
29 | return; | ||
30 | } | ||
31 | |||
32 | /* | ||
33 | * last event might point to some random place in the list as it's | ||
34 | * the last queued event. We expect that the new event is close to | ||
35 | * this. | ||
36 | */ | ||
37 | if (last->timestamp <= timestamp) { | ||
38 | while (last->timestamp <= timestamp) { | ||
39 | p = last->list.next; | ||
40 | if (p == &oe->events) { | ||
41 | list_add_tail(&new->list, &oe->events); | ||
42 | oe->max_timestamp = timestamp; | ||
43 | return; | ||
44 | } | ||
45 | last = list_entry(p, struct ordered_event, list); | ||
46 | } | ||
47 | list_add_tail(&new->list, &last->list); | ||
48 | } else { | ||
49 | while (last->timestamp > timestamp) { | ||
50 | p = last->list.prev; | ||
51 | if (p == &oe->events) { | ||
52 | list_add(&new->list, &oe->events); | ||
53 | return; | ||
54 | } | ||
55 | last = list_entry(p, struct ordered_event, list); | ||
56 | } | ||
57 | list_add(&new->list, &last->list); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | static union perf_event *__dup_event(struct ordered_events *oe, | ||
62 | union perf_event *event) | ||
63 | { | ||
64 | union perf_event *new_event = NULL; | ||
65 | |||
66 | if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
67 | new_event = memdup(event, event->header.size); | ||
68 | if (new_event) | ||
69 | oe->cur_alloc_size += event->header.size; | ||
70 | } | ||
71 | |||
72 | return new_event; | ||
73 | } | ||
74 | |||
75 | static union perf_event *dup_event(struct ordered_events *oe, | ||
76 | union perf_event *event) | ||
77 | { | ||
78 | return oe->copy_on_queue ? __dup_event(oe, event) : event; | ||
79 | } | ||
80 | |||
81 | static void free_dup_event(struct ordered_events *oe, union perf_event *event) | ||
82 | { | ||
83 | if (oe->copy_on_queue) { | ||
84 | oe->cur_alloc_size -= event->header.size; | ||
85 | free(event); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) | ||
90 | static struct ordered_event *alloc_event(struct ordered_events *oe, | ||
91 | union perf_event *event) | ||
92 | { | ||
93 | struct list_head *cache = &oe->cache; | ||
94 | struct ordered_event *new = NULL; | ||
95 | union perf_event *new_event; | ||
96 | |||
97 | new_event = dup_event(oe, event); | ||
98 | if (!new_event) | ||
99 | return NULL; | ||
100 | |||
101 | if (!list_empty(cache)) { | ||
102 | new = list_entry(cache->next, struct ordered_event, list); | ||
103 | list_del(&new->list); | ||
104 | } else if (oe->buffer) { | ||
105 | new = oe->buffer + oe->buffer_idx; | ||
106 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) | ||
107 | oe->buffer = NULL; | ||
108 | } else if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
109 | size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); | ||
110 | |||
111 | oe->buffer = malloc(size); | ||
112 | if (!oe->buffer) { | ||
113 | free_dup_event(oe, new_event); | ||
114 | return NULL; | ||
115 | } | ||
116 | |||
117 | pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", | ||
118 | oe->cur_alloc_size, size, oe->max_alloc_size); | ||
119 | |||
120 | oe->cur_alloc_size += size; | ||
121 | list_add(&oe->buffer->list, &oe->to_free); | ||
122 | |||
123 | /* First entry is abused to maintain the to_free list. */ | ||
124 | oe->buffer_idx = 2; | ||
125 | new = oe->buffer + 1; | ||
126 | } else { | ||
127 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); | ||
128 | } | ||
129 | |||
130 | new->event = new_event; | ||
131 | return new; | ||
132 | } | ||
133 | |||
134 | struct ordered_event * | ||
135 | ordered_events__new(struct ordered_events *oe, u64 timestamp, | ||
136 | union perf_event *event) | ||
137 | { | ||
138 | struct ordered_event *new; | ||
139 | |||
140 | new = alloc_event(oe, event); | ||
141 | if (new) { | ||
142 | new->timestamp = timestamp; | ||
143 | queue_event(oe, new); | ||
144 | } | ||
145 | |||
146 | return new; | ||
147 | } | ||
148 | |||
149 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event) | ||
150 | { | ||
151 | list_move(&event->list, &oe->cache); | ||
152 | oe->nr_events--; | ||
153 | free_dup_event(oe, event->event); | ||
154 | } | ||
155 | |||
156 | static int __ordered_events__flush(struct perf_session *s, | ||
157 | struct perf_tool *tool) | ||
158 | { | ||
159 | struct ordered_events *oe = &s->ordered_events; | ||
160 | struct list_head *head = &oe->events; | ||
161 | struct ordered_event *tmp, *iter; | ||
162 | struct perf_sample sample; | ||
163 | u64 limit = oe->next_flush; | ||
164 | u64 last_ts = oe->last ? oe->last->timestamp : 0ULL; | ||
165 | bool show_progress = limit == ULLONG_MAX; | ||
166 | struct ui_progress prog; | ||
167 | int ret; | ||
168 | |||
169 | if (!tool->ordered_events || !limit) | ||
170 | return 0; | ||
171 | |||
172 | if (show_progress) | ||
173 | ui_progress__init(&prog, oe->nr_events, "Processing time ordered events..."); | ||
174 | |||
175 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
176 | if (session_done()) | ||
177 | return 0; | ||
178 | |||
179 | if (iter->timestamp > limit) | ||
180 | break; | ||
181 | |||
182 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
183 | if (ret) | ||
184 | pr_err("Can't parse sample, err = %d\n", ret); | ||
185 | else { | ||
186 | ret = perf_session__deliver_event(s, iter->event, &sample, tool, | ||
187 | iter->file_offset); | ||
188 | if (ret) | ||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | ordered_events__delete(oe, iter); | ||
193 | oe->last_flush = iter->timestamp; | ||
194 | |||
195 | if (show_progress) | ||
196 | ui_progress__update(&prog, 1); | ||
197 | } | ||
198 | |||
199 | if (list_empty(head)) | ||
200 | oe->last = NULL; | ||
201 | else if (last_ts <= limit) | ||
202 | oe->last = list_entry(head->prev, struct ordered_event, list); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
208 | enum oe_flush how) | ||
209 | { | ||
210 | struct ordered_events *oe = &s->ordered_events; | ||
211 | static const char * const str[] = { | ||
212 | "NONE", | ||
213 | "FINAL", | ||
214 | "ROUND", | ||
215 | "HALF ", | ||
216 | }; | ||
217 | int err; | ||
218 | |||
219 | switch (how) { | ||
220 | case OE_FLUSH__FINAL: | ||
221 | oe->next_flush = ULLONG_MAX; | ||
222 | break; | ||
223 | |||
224 | case OE_FLUSH__HALF: | ||
225 | { | ||
226 | struct ordered_event *first, *last; | ||
227 | struct list_head *head = &oe->events; | ||
228 | |||
229 | first = list_entry(head->next, struct ordered_event, list); | ||
230 | last = oe->last; | ||
231 | |||
232 | /* Warn if we are called before any event got allocated. */ | ||
233 | if (WARN_ONCE(!last || list_empty(head), "empty queue")) | ||
234 | return 0; | ||
235 | |||
236 | oe->next_flush = first->timestamp; | ||
237 | oe->next_flush += (last->timestamp - first->timestamp) / 2; | ||
238 | break; | ||
239 | } | ||
240 | |||
241 | case OE_FLUSH__ROUND: | ||
242 | case OE_FLUSH__NONE: | ||
243 | default: | ||
244 | break; | ||
245 | }; | ||
246 | |||
247 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE %s, nr_events %u\n", | ||
248 | str[how], oe->nr_events); | ||
249 | pr_oe_time(oe->max_timestamp, "max_timestamp\n"); | ||
250 | |||
251 | err = __ordered_events__flush(s, tool); | ||
252 | |||
253 | if (!err) { | ||
254 | if (how == OE_FLUSH__ROUND) | ||
255 | oe->next_flush = oe->max_timestamp; | ||
256 | |||
257 | oe->last_flush_type = how; | ||
258 | } | ||
259 | |||
260 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush POST %s, nr_events %u\n", | ||
261 | str[how], oe->nr_events); | ||
262 | pr_oe_time(oe->last_flush, "last_flush\n"); | ||
263 | |||
264 | return err; | ||
265 | } | ||
266 | |||
267 | void ordered_events__init(struct ordered_events *oe) | ||
268 | { | ||
269 | INIT_LIST_HEAD(&oe->events); | ||
270 | INIT_LIST_HEAD(&oe->cache); | ||
271 | INIT_LIST_HEAD(&oe->to_free); | ||
272 | oe->max_alloc_size = (u64) -1; | ||
273 | oe->cur_alloc_size = 0; | ||
274 | } | ||
275 | |||
276 | void ordered_events__free(struct ordered_events *oe) | ||
277 | { | ||
278 | while (!list_empty(&oe->to_free)) { | ||
279 | struct ordered_event *event; | ||
280 | |||
281 | event = list_entry(oe->to_free.next, struct ordered_event, list); | ||
282 | list_del(&event->list); | ||
283 | free_dup_event(oe, event->event); | ||
284 | free(event); | ||
285 | } | ||
286 | } | ||
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h new file mode 100644 index 000000000000..7b8f9b011f38 --- /dev/null +++ b/tools/perf/util/ordered-events.h | |||
@@ -0,0 +1,59 @@ | |||
1 | #ifndef __ORDERED_EVENTS_H | ||
2 | #define __ORDERED_EVENTS_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "tool.h" | ||
6 | |||
7 | struct perf_session; | ||
8 | |||
9 | struct ordered_event { | ||
10 | u64 timestamp; | ||
11 | u64 file_offset; | ||
12 | union perf_event *event; | ||
13 | struct list_head list; | ||
14 | }; | ||
15 | |||
16 | enum oe_flush { | ||
17 | OE_FLUSH__NONE, | ||
18 | OE_FLUSH__FINAL, | ||
19 | OE_FLUSH__ROUND, | ||
20 | OE_FLUSH__HALF, | ||
21 | }; | ||
22 | |||
23 | struct ordered_events { | ||
24 | u64 last_flush; | ||
25 | u64 next_flush; | ||
26 | u64 max_timestamp; | ||
27 | u64 max_alloc_size; | ||
28 | u64 cur_alloc_size; | ||
29 | struct list_head events; | ||
30 | struct list_head cache; | ||
31 | struct list_head to_free; | ||
32 | struct ordered_event *buffer; | ||
33 | struct ordered_event *last; | ||
34 | int buffer_idx; | ||
35 | unsigned int nr_events; | ||
36 | enum oe_flush last_flush_type; | ||
37 | bool copy_on_queue; | ||
38 | }; | ||
39 | |||
40 | struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp, | ||
41 | union perf_event *event); | ||
42 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); | ||
43 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
44 | enum oe_flush how); | ||
45 | void ordered_events__init(struct ordered_events *oe); | ||
46 | void ordered_events__free(struct ordered_events *oe); | ||
47 | |||
48 | static inline | ||
49 | void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) | ||
50 | { | ||
51 | oe->max_alloc_size = size; | ||
52 | } | ||
53 | |||
54 | static inline | ||
55 | void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy) | ||
56 | { | ||
57 | oe->copy_on_queue = copy; | ||
58 | } | ||
59 | #endif /* __ORDERED_EVENTS_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1e15df10a88c..77b43fe43d55 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -6,10 +6,11 @@ | |||
6 | #include "parse-options.h" | 6 | #include "parse-options.h" |
7 | #include "parse-events.h" | 7 | #include "parse-events.h" |
8 | #include "exec_cmd.h" | 8 | #include "exec_cmd.h" |
9 | #include "linux/string.h" | 9 | #include "string.h" |
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debug.h" | ||
13 | #include <api/fs/debugfs.h> | 14 | #include <api/fs/debugfs.h> |
14 | #include "parse-events-bison.h" | 15 | #include "parse-events-bison.h" |
15 | #define YY_EXTRA_TYPE int | 16 | #define YY_EXTRA_TYPE int |
@@ -29,6 +30,15 @@ extern int parse_events_debug; | |||
29 | #endif | 30 | #endif |
30 | int parse_events_parse(void *data, void *scanner); | 31 | int parse_events_parse(void *data, void *scanner); |
31 | 32 | ||
33 | static struct perf_pmu_event_symbol *perf_pmu_events_list; | ||
34 | /* | ||
35 | * The variable indicates the number of supported pmu event symbols. | ||
36 | * 0 means not initialized and ready to init | ||
37 | * -1 means failed to init, don't try anymore | ||
38 | * >0 is the number of supported pmu event symbols | ||
39 | */ | ||
40 | static int perf_pmu_events_list_num; | ||
41 | |||
32 | static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { | 42 | static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { |
33 | [PERF_COUNT_HW_CPU_CYCLES] = { | 43 | [PERF_COUNT_HW_CPU_CYCLES] = { |
34 | .symbol = "cpu-cycles", | 44 | .symbol = "cpu-cycles", |
@@ -633,18 +643,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
633 | char *name, struct list_head *head_config) | 643 | char *name, struct list_head *head_config) |
634 | { | 644 | { |
635 | struct perf_event_attr attr; | 645 | struct perf_event_attr attr; |
646 | struct perf_pmu_info info; | ||
636 | struct perf_pmu *pmu; | 647 | struct perf_pmu *pmu; |
637 | struct perf_evsel *evsel; | 648 | struct perf_evsel *evsel; |
638 | const char *unit; | ||
639 | double scale; | ||
640 | 649 | ||
641 | pmu = perf_pmu__find(name); | 650 | pmu = perf_pmu__find(name); |
642 | if (!pmu) | 651 | if (!pmu) |
643 | return -EINVAL; | 652 | return -EINVAL; |
644 | 653 | ||
645 | memset(&attr, 0, sizeof(attr)); | 654 | if (pmu->default_config) { |
655 | memcpy(&attr, pmu->default_config, | ||
656 | sizeof(struct perf_event_attr)); | ||
657 | } else { | ||
658 | memset(&attr, 0, sizeof(attr)); | ||
659 | } | ||
646 | 660 | ||
647 | if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) | 661 | if (!head_config) { |
662 | attr.type = pmu->type; | ||
663 | evsel = __add_event(list, idx, &attr, NULL, pmu->cpus); | ||
664 | return evsel ? 0 : -ENOMEM; | ||
665 | } | ||
666 | |||
667 | if (perf_pmu__check_alias(pmu, head_config, &info)) | ||
648 | return -EINVAL; | 668 | return -EINVAL; |
649 | 669 | ||
650 | /* | 670 | /* |
@@ -659,8 +679,10 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
659 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), | 679 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), |
660 | pmu->cpus); | 680 | pmu->cpus); |
661 | if (evsel) { | 681 | if (evsel) { |
662 | evsel->unit = unit; | 682 | evsel->unit = info.unit; |
663 | evsel->scale = scale; | 683 | evsel->scale = info.scale; |
684 | evsel->per_pkg = info.per_pkg; | ||
685 | evsel->snapshot = info.snapshot; | ||
664 | } | 686 | } |
665 | 687 | ||
666 | return evsel ? 0 : -ENOMEM; | 688 | return evsel ? 0 : -ENOMEM; |
@@ -852,30 +874,111 @@ int parse_events_name(struct list_head *list, char *name) | |||
852 | return 0; | 874 | return 0; |
853 | } | 875 | } |
854 | 876 | ||
855 | static int parse_events__scanner(const char *str, void *data, int start_token); | 877 | static int |
878 | comp_pmu(const void *p1, const void *p2) | ||
879 | { | ||
880 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; | ||
881 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; | ||
856 | 882 | ||
857 | static int parse_events_fixup(int ret, const char *str, void *data, | 883 | return strcmp(pmu1->symbol, pmu2->symbol); |
858 | int start_token) | 884 | } |
885 | |||
886 | static void perf_pmu__parse_cleanup(void) | ||
859 | { | 887 | { |
860 | char *o = strdup(str); | 888 | if (perf_pmu_events_list_num > 0) { |
861 | char *s = NULL; | 889 | struct perf_pmu_event_symbol *p; |
862 | char *t = o; | 890 | int i; |
863 | char *p; | 891 | |
892 | for (i = 0; i < perf_pmu_events_list_num; i++) { | ||
893 | p = perf_pmu_events_list + i; | ||
894 | free(p->symbol); | ||
895 | } | ||
896 | free(perf_pmu_events_list); | ||
897 | perf_pmu_events_list = NULL; | ||
898 | perf_pmu_events_list_num = 0; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | #define SET_SYMBOL(str, stype) \ | ||
903 | do { \ | ||
904 | p->symbol = str; \ | ||
905 | if (!p->symbol) \ | ||
906 | goto err; \ | ||
907 | p->type = stype; \ | ||
908 | } while (0) | ||
909 | |||
910 | /* | ||
911 | * Read the pmu events list from sysfs | ||
912 | * Save it into perf_pmu_events_list | ||
913 | */ | ||
914 | static void perf_pmu__parse_init(void) | ||
915 | { | ||
916 | |||
917 | struct perf_pmu *pmu = NULL; | ||
918 | struct perf_pmu_alias *alias; | ||
864 | int len = 0; | 919 | int len = 0; |
865 | 920 | ||
866 | if (!o) | 921 | pmu = perf_pmu__find("cpu"); |
867 | return ret; | 922 | if ((pmu == NULL) || list_empty(&pmu->aliases)) { |
868 | while ((p = strsep(&t, ",")) != NULL) { | 923 | perf_pmu_events_list_num = -1; |
869 | if (s) | 924 | return; |
870 | str_append(&s, &len, ","); | ||
871 | str_append(&s, &len, "cpu/"); | ||
872 | str_append(&s, &len, p); | ||
873 | str_append(&s, &len, "/"); | ||
874 | } | 925 | } |
875 | free(o); | 926 | list_for_each_entry(alias, &pmu->aliases, list) { |
876 | if (!s) | 927 | if (strchr(alias->name, '-')) |
877 | return -ENOMEM; | 928 | len++; |
878 | return parse_events__scanner(s, data, start_token); | 929 | len++; |
930 | } | ||
931 | perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); | ||
932 | if (!perf_pmu_events_list) | ||
933 | return; | ||
934 | perf_pmu_events_list_num = len; | ||
935 | |||
936 | len = 0; | ||
937 | list_for_each_entry(alias, &pmu->aliases, list) { | ||
938 | struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; | ||
939 | char *tmp = strchr(alias->name, '-'); | ||
940 | |||
941 | if (tmp != NULL) { | ||
942 | SET_SYMBOL(strndup(alias->name, tmp - alias->name), | ||
943 | PMU_EVENT_SYMBOL_PREFIX); | ||
944 | p++; | ||
945 | SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); | ||
946 | len += 2; | ||
947 | } else { | ||
948 | SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); | ||
949 | len++; | ||
950 | } | ||
951 | } | ||
952 | qsort(perf_pmu_events_list, len, | ||
953 | sizeof(struct perf_pmu_event_symbol), comp_pmu); | ||
954 | |||
955 | return; | ||
956 | err: | ||
957 | perf_pmu__parse_cleanup(); | ||
958 | } | ||
959 | |||
960 | enum perf_pmu_event_symbol_type | ||
961 | perf_pmu__parse_check(const char *name) | ||
962 | { | ||
963 | struct perf_pmu_event_symbol p, *r; | ||
964 | |||
965 | /* scan kernel pmu events from sysfs if needed */ | ||
966 | if (perf_pmu_events_list_num == 0) | ||
967 | perf_pmu__parse_init(); | ||
968 | /* | ||
969 | * name "cpu" could be prefix of cpu-cycles or cpu// events. | ||
970 | * cpu-cycles has been handled by hardcode. | ||
971 | * So it must be cpu// events, not kernel pmu event. | ||
972 | */ | ||
973 | if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu")) | ||
974 | return PMU_EVENT_SYMBOL_ERR; | ||
975 | |||
976 | p.symbol = strdup(name); | ||
977 | r = bsearch(&p, perf_pmu_events_list, | ||
978 | (size_t) perf_pmu_events_list_num, | ||
979 | sizeof(struct perf_pmu_event_symbol), comp_pmu); | ||
980 | free(p.symbol); | ||
981 | return r ? r->type : PMU_EVENT_SYMBOL_ERR; | ||
879 | } | 982 | } |
880 | 983 | ||
881 | static int parse_events__scanner(const char *str, void *data, int start_token) | 984 | static int parse_events__scanner(const char *str, void *data, int start_token) |
@@ -898,8 +1001,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token) | |||
898 | parse_events__flush_buffer(buffer, scanner); | 1001 | parse_events__flush_buffer(buffer, scanner); |
899 | parse_events__delete_buffer(buffer, scanner); | 1002 | parse_events__delete_buffer(buffer, scanner); |
900 | parse_events_lex_destroy(scanner); | 1003 | parse_events_lex_destroy(scanner); |
901 | if (ret && !strchr(str, '/')) | ||
902 | ret = parse_events_fixup(ret, str, data, start_token); | ||
903 | return ret; | 1004 | return ret; |
904 | } | 1005 | } |
905 | 1006 | ||
@@ -934,6 +1035,7 @@ int parse_events(struct perf_evlist *evlist, const char *str) | |||
934 | int ret; | 1035 | int ret; |
935 | 1036 | ||
936 | ret = parse_events__scanner(str, &data, PE_START_EVENTS); | 1037 | ret = parse_events__scanner(str, &data, PE_START_EVENTS); |
1038 | perf_pmu__parse_cleanup(); | ||
937 | if (!ret) { | 1039 | if (!ret) { |
938 | int entries = data.idx - evlist->nr_entries; | 1040 | int entries = data.idx - evlist->nr_entries; |
939 | perf_evlist__splice_list_tail(evlist, &data.list, entries); | 1041 | perf_evlist__splice_list_tail(evlist, &data.list, entries); |
@@ -973,7 +1075,7 @@ int parse_filter(const struct option *opt, const char *str, | |||
973 | 1075 | ||
974 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 1076 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
975 | fprintf(stderr, | 1077 | fprintf(stderr, |
976 | "-F option should follow a -e tracepoint option\n"); | 1078 | "--filter option should follow a -e tracepoint option\n"); |
977 | return -1; | 1079 | return -1; |
978 | } | 1080 | } |
979 | 1081 | ||
@@ -1006,9 +1108,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
1006 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 1108 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
1007 | char evt_path[MAXPATHLEN]; | 1109 | char evt_path[MAXPATHLEN]; |
1008 | char dir_path[MAXPATHLEN]; | 1110 | char dir_path[MAXPATHLEN]; |
1111 | char sbuf[STRERR_BUFSIZE]; | ||
1009 | 1112 | ||
1010 | if (debugfs_valid_mountpoint(tracing_events_path)) { | 1113 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
1011 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | 1114 | printf(" [ Tracepoints not available: %s ]\n", |
1115 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
1012 | return; | 1116 | return; |
1013 | } | 1117 | } |
1014 | 1118 | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index df094b4ed5ed..db2cf78ff0f3 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
35 | 35 | ||
36 | #define EVENTS_HELP_MAX (128*1024) | 36 | #define EVENTS_HELP_MAX (128*1024) |
37 | 37 | ||
38 | enum perf_pmu_event_symbol_type { | ||
39 | PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */ | ||
40 | PMU_EVENT_SYMBOL, /* normal style PMU event */ | ||
41 | PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ | ||
42 | PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ | ||
43 | }; | ||
44 | |||
45 | struct perf_pmu_event_symbol { | ||
46 | char *symbol; | ||
47 | enum perf_pmu_event_symbol_type type; | ||
48 | }; | ||
49 | |||
38 | enum { | 50 | enum { |
39 | PARSE_EVENTS__TERM_TYPE_NUM, | 51 | PARSE_EVENTS__TERM_TYPE_NUM, |
40 | PARSE_EVENTS__TERM_TYPE_STR, | 52 | PARSE_EVENTS__TERM_TYPE_STR, |
@@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, | |||
95 | void *ptr, char *type); | 107 | void *ptr, char *type); |
96 | int parse_events_add_pmu(struct list_head *list, int *idx, | 108 | int parse_events_add_pmu(struct list_head *list, int *idx, |
97 | char *pmu , struct list_head *head_config); | 109 | char *pmu , struct list_head *head_config); |
110 | enum perf_pmu_event_symbol_type | ||
111 | perf_pmu__parse_check(const char *name); | ||
98 | void parse_events__set_leader(char *name, struct list_head *list); | 112 | void parse_events__set_leader(char *name, struct list_head *list); |
99 | void parse_events_update_lists(struct list_head *list_event, | 113 | void parse_events_update_lists(struct list_head *list_event, |
100 | struct list_head *list_all); | 114 | struct list_head *list_all); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 343299575b30..906630bbf8eb 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token) | |||
51 | return token; | 51 | return token; |
52 | } | 52 | } |
53 | 53 | ||
54 | static int pmu_str_check(yyscan_t scanner) | ||
55 | { | ||
56 | YYSTYPE *yylval = parse_events_get_lval(scanner); | ||
57 | char *text = parse_events_get_text(scanner); | ||
58 | |||
59 | yylval->str = strdup(text); | ||
60 | switch (perf_pmu__parse_check(text)) { | ||
61 | case PMU_EVENT_SYMBOL_PREFIX: | ||
62 | return PE_PMU_EVENT_PRE; | ||
63 | case PMU_EVENT_SYMBOL_SUFFIX: | ||
64 | return PE_PMU_EVENT_SUF; | ||
65 | case PMU_EVENT_SYMBOL: | ||
66 | return PE_KERNEL_PMU_EVENT; | ||
67 | default: | ||
68 | return PE_NAME; | ||
69 | } | ||
70 | } | ||
71 | |||
54 | static int sym(yyscan_t scanner, int type, int config) | 72 | static int sym(yyscan_t scanner, int type, int config) |
55 | { | 73 | { |
56 | YYSTYPE *yylval = parse_events_get_lval(scanner); | 74 | YYSTYPE *yylval = parse_events_get_lval(scanner); |
@@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL | |||
178 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | 196 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } |
179 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } | 197 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } |
180 | 198 | ||
199 | /* | ||
200 | * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. | ||
201 | * Because the prefix cycles is mixed up with cpu-cycles. | ||
202 | * loads and stores are mixed up with cache event | ||
203 | */ | ||
204 | cycles-ct { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
205 | cycles-t { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
206 | mem-loads { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
207 | mem-stores { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
208 | |||
181 | L1-dcache|l1-d|l1d|L1-data | | 209 | L1-dcache|l1-d|l1d|L1-data | |
182 | L1-icache|l1-i|l1i|L1-instruction | | 210 | L1-icache|l1-i|l1i|L1-instruction | |
183 | LLC|L2 | | 211 | LLC|L2 | |
@@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
199 | {num_hex} { return value(yyscanner, 16); } | 227 | {num_hex} { return value(yyscanner, 16); } |
200 | 228 | ||
201 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } | 229 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } |
202 | {name} { return str(yyscanner, PE_NAME); } | 230 | {name} { return pmu_str_check(yyscanner); } |
203 | "/" { BEGIN(config); return '/'; } | 231 | "/" { BEGIN(config); return '/'; } |
204 | - { return '-'; } | 232 | - { return '-'; } |
205 | , { BEGIN(event); return ','; } | 233 | , { BEGIN(event); return ','; } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0bc87ba46bf3..93c4c9fbc922 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list, | |||
47 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | 47 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT |
48 | %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP | 48 | %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP |
49 | %token PE_ERROR | 49 | %token PE_ERROR |
50 | %token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT | ||
50 | %type <num> PE_VALUE | 51 | %type <num> PE_VALUE |
51 | %type <num> PE_VALUE_SYM_HW | 52 | %type <num> PE_VALUE_SYM_HW |
52 | %type <num> PE_VALUE_SYM_SW | 53 | %type <num> PE_VALUE_SYM_SW |
@@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list, | |||
58 | %type <str> PE_MODIFIER_EVENT | 59 | %type <str> PE_MODIFIER_EVENT |
59 | %type <str> PE_MODIFIER_BP | 60 | %type <str> PE_MODIFIER_BP |
60 | %type <str> PE_EVENT_NAME | 61 | %type <str> PE_EVENT_NAME |
62 | %type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT | ||
61 | %type <num> value_sym | 63 | %type <num> value_sym |
62 | %type <head> event_config | 64 | %type <head> event_config |
63 | %type <term> event_term | 65 | %type <term> event_term |
@@ -210,6 +212,54 @@ PE_NAME '/' event_config '/' | |||
210 | parse_events__free_terms($3); | 212 | parse_events__free_terms($3); |
211 | $$ = list; | 213 | $$ = list; |
212 | } | 214 | } |
215 | | | ||
216 | PE_NAME '/' '/' | ||
217 | { | ||
218 | struct parse_events_evlist *data = _data; | ||
219 | struct list_head *list; | ||
220 | |||
221 | ALLOC_LIST(list); | ||
222 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); | ||
223 | $$ = list; | ||
224 | } | ||
225 | | | ||
226 | PE_KERNEL_PMU_EVENT sep_dc | ||
227 | { | ||
228 | struct parse_events_evlist *data = _data; | ||
229 | struct list_head *head; | ||
230 | struct parse_events_term *term; | ||
231 | struct list_head *list; | ||
232 | |||
233 | ALLOC_LIST(head); | ||
234 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
235 | $1, 1)); | ||
236 | list_add_tail(&term->list, head); | ||
237 | |||
238 | ALLOC_LIST(list); | ||
239 | ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); | ||
240 | parse_events__free_terms(head); | ||
241 | $$ = list; | ||
242 | } | ||
243 | | | ||
244 | PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc | ||
245 | { | ||
246 | struct parse_events_evlist *data = _data; | ||
247 | struct list_head *head; | ||
248 | struct parse_events_term *term; | ||
249 | struct list_head *list; | ||
250 | char pmu_name[128]; | ||
251 | snprintf(&pmu_name, 128, "%s-%s", $1, $3); | ||
252 | |||
253 | ALLOC_LIST(head); | ||
254 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
255 | &pmu_name, 1)); | ||
256 | list_add_tail(&term->list, head); | ||
257 | |||
258 | ALLOC_LIST(list); | ||
259 | ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); | ||
260 | parse_events__free_terms(head); | ||
261 | $$ = list; | ||
262 | } | ||
213 | 263 | ||
214 | value_sym: | 264 | value_sym: |
215 | PE_VALUE_SYM_HW | 265 | PE_VALUE_SYM_HW |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index bf48092983c6..f62dee7bd924 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -42,7 +42,26 @@ static int get_value(struct parse_opt_ctx_t *p, | |||
42 | return opterror(opt, "takes no value", flags); | 42 | return opterror(opt, "takes no value", flags); |
43 | if (unset && (opt->flags & PARSE_OPT_NONEG)) | 43 | if (unset && (opt->flags & PARSE_OPT_NONEG)) |
44 | return opterror(opt, "isn't available", flags); | 44 | return opterror(opt, "isn't available", flags); |
45 | 45 | if (opt->flags & PARSE_OPT_DISABLED) | |
46 | return opterror(opt, "is not usable", flags); | ||
47 | |||
48 | if (opt->flags & PARSE_OPT_EXCLUSIVE) { | ||
49 | if (p->excl_opt) { | ||
50 | char msg[128]; | ||
51 | |||
52 | if (((flags & OPT_SHORT) && p->excl_opt->short_name) || | ||
53 | p->excl_opt->long_name == NULL) { | ||
54 | scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'", | ||
55 | p->excl_opt->short_name); | ||
56 | } else { | ||
57 | scnprintf(msg, sizeof(msg), "cannot be used with %s", | ||
58 | p->excl_opt->long_name); | ||
59 | } | ||
60 | opterror(opt, msg, flags); | ||
61 | return -3; | ||
62 | } | ||
63 | p->excl_opt = opt; | ||
64 | } | ||
46 | if (!(flags & OPT_SHORT) && p->opt) { | 65 | if (!(flags & OPT_SHORT) && p->opt) { |
47 | switch (opt->type) { | 66 | switch (opt->type) { |
48 | case OPTION_CALLBACK: | 67 | case OPTION_CALLBACK: |
@@ -343,13 +362,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
343 | const char * const usagestr[]) | 362 | const char * const usagestr[]) |
344 | { | 363 | { |
345 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); | 364 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); |
365 | int excl_short_opt = 1; | ||
366 | const char *arg; | ||
346 | 367 | ||
347 | /* we must reset ->opt, unknown short option leave it dangling */ | 368 | /* we must reset ->opt, unknown short option leave it dangling */ |
348 | ctx->opt = NULL; | 369 | ctx->opt = NULL; |
349 | 370 | ||
350 | for (; ctx->argc; ctx->argc--, ctx->argv++) { | 371 | for (; ctx->argc; ctx->argc--, ctx->argv++) { |
351 | const char *arg = ctx->argv[0]; | 372 | arg = ctx->argv[0]; |
352 | |||
353 | if (*arg != '-' || !arg[1]) { | 373 | if (*arg != '-' || !arg[1]) { |
354 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) | 374 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) |
355 | break; | 375 | break; |
@@ -358,19 +378,21 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
358 | } | 378 | } |
359 | 379 | ||
360 | if (arg[1] != '-') { | 380 | if (arg[1] != '-') { |
361 | ctx->opt = arg + 1; | 381 | ctx->opt = ++arg; |
362 | if (internal_help && *ctx->opt == 'h') | 382 | if (internal_help && *ctx->opt == 'h') |
363 | return usage_with_options_internal(usagestr, options, 0); | 383 | return usage_with_options_internal(usagestr, options, 0); |
364 | switch (parse_short_opt(ctx, options)) { | 384 | switch (parse_short_opt(ctx, options)) { |
365 | case -1: | 385 | case -1: |
366 | return parse_options_usage(usagestr, options, arg + 1, 1); | 386 | return parse_options_usage(usagestr, options, arg, 1); |
367 | case -2: | 387 | case -2: |
368 | goto unknown; | 388 | goto unknown; |
389 | case -3: | ||
390 | goto exclusive; | ||
369 | default: | 391 | default: |
370 | break; | 392 | break; |
371 | } | 393 | } |
372 | if (ctx->opt) | 394 | if (ctx->opt) |
373 | check_typos(arg + 1, options); | 395 | check_typos(arg, options); |
374 | while (ctx->opt) { | 396 | while (ctx->opt) { |
375 | if (internal_help && *ctx->opt == 'h') | 397 | if (internal_help && *ctx->opt == 'h') |
376 | return usage_with_options_internal(usagestr, options, 0); | 398 | return usage_with_options_internal(usagestr, options, 0); |
@@ -387,6 +409,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
387 | ctx->argv[0] = strdup(ctx->opt - 1); | 409 | ctx->argv[0] = strdup(ctx->opt - 1); |
388 | *(char *)ctx->argv[0] = '-'; | 410 | *(char *)ctx->argv[0] = '-'; |
389 | goto unknown; | 411 | goto unknown; |
412 | case -3: | ||
413 | goto exclusive; | ||
390 | default: | 414 | default: |
391 | break; | 415 | break; |
392 | } | 416 | } |
@@ -402,19 +426,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
402 | break; | 426 | break; |
403 | } | 427 | } |
404 | 428 | ||
405 | if (internal_help && !strcmp(arg + 2, "help-all")) | 429 | arg += 2; |
430 | if (internal_help && !strcmp(arg, "help-all")) | ||
406 | return usage_with_options_internal(usagestr, options, 1); | 431 | return usage_with_options_internal(usagestr, options, 1); |
407 | if (internal_help && !strcmp(arg + 2, "help")) | 432 | if (internal_help && !strcmp(arg, "help")) |
408 | return usage_with_options_internal(usagestr, options, 0); | 433 | return usage_with_options_internal(usagestr, options, 0); |
409 | if (!strcmp(arg + 2, "list-opts")) | 434 | if (!strcmp(arg, "list-opts")) |
410 | return PARSE_OPT_LIST_OPTS; | 435 | return PARSE_OPT_LIST_OPTS; |
411 | if (!strcmp(arg + 2, "list-cmds")) | 436 | if (!strcmp(arg, "list-cmds")) |
412 | return PARSE_OPT_LIST_SUBCMDS; | 437 | return PARSE_OPT_LIST_SUBCMDS; |
413 | switch (parse_long_opt(ctx, arg + 2, options)) { | 438 | switch (parse_long_opt(ctx, arg, options)) { |
414 | case -1: | 439 | case -1: |
415 | return parse_options_usage(usagestr, options, arg + 2, 0); | 440 | return parse_options_usage(usagestr, options, arg, 0); |
416 | case -2: | 441 | case -2: |
417 | goto unknown; | 442 | goto unknown; |
443 | case -3: | ||
444 | excl_short_opt = 0; | ||
445 | goto exclusive; | ||
418 | default: | 446 | default: |
419 | break; | 447 | break; |
420 | } | 448 | } |
@@ -426,6 +454,17 @@ unknown: | |||
426 | ctx->opt = NULL; | 454 | ctx->opt = NULL; |
427 | } | 455 | } |
428 | return PARSE_OPT_DONE; | 456 | return PARSE_OPT_DONE; |
457 | |||
458 | exclusive: | ||
459 | parse_options_usage(usagestr, options, arg, excl_short_opt); | ||
460 | if ((excl_short_opt && ctx->excl_opt->short_name) || | ||
461 | ctx->excl_opt->long_name == NULL) { | ||
462 | char opt = ctx->excl_opt->short_name; | ||
463 | parse_options_usage(NULL, options, &opt, 1); | ||
464 | } else { | ||
465 | parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); | ||
466 | } | ||
467 | return PARSE_OPT_HELP; | ||
429 | } | 468 | } |
430 | 469 | ||
431 | int parse_options_end(struct parse_opt_ctx_t *ctx) | 470 | int parse_options_end(struct parse_opt_ctx_t *ctx) |
@@ -509,6 +548,8 @@ static void print_option_help(const struct option *opts, int full) | |||
509 | } | 548 | } |
510 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | 549 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) |
511 | return; | 550 | return; |
551 | if (opts->flags & PARSE_OPT_DISABLED) | ||
552 | return; | ||
512 | 553 | ||
513 | pos = fprintf(stderr, " "); | 554 | pos = fprintf(stderr, " "); |
514 | if (opts->short_name) | 555 | if (opts->short_name) |
@@ -679,3 +720,16 @@ int parse_opt_verbosity_cb(const struct option *opt, | |||
679 | } | 720 | } |
680 | return 0; | 721 | return 0; |
681 | } | 722 | } |
723 | |||
724 | void set_option_flag(struct option *opts, int shortopt, const char *longopt, | ||
725 | int flag) | ||
726 | { | ||
727 | for (; opts->type != OPTION_END; opts++) { | ||
728 | if ((shortopt && opts->short_name == shortopt) || | ||
729 | (opts->long_name && longopt && | ||
730 | !strcmp(opts->long_name, longopt))) { | ||
731 | opts->flags |= flag; | ||
732 | break; | ||
733 | } | ||
734 | } | ||
735 | } | ||
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b59ba858e73d..97b153fb4999 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -38,6 +38,8 @@ enum parse_opt_option_flags { | |||
38 | PARSE_OPT_NONEG = 4, | 38 | PARSE_OPT_NONEG = 4, |
39 | PARSE_OPT_HIDDEN = 8, | 39 | PARSE_OPT_HIDDEN = 8, |
40 | PARSE_OPT_LASTARG_DEFAULT = 16, | 40 | PARSE_OPT_LASTARG_DEFAULT = 16, |
41 | PARSE_OPT_DISABLED = 32, | ||
42 | PARSE_OPT_EXCLUSIVE = 64, | ||
41 | }; | 43 | }; |
42 | 44 | ||
43 | struct option; | 45 | struct option; |
@@ -173,6 +175,7 @@ struct parse_opt_ctx_t { | |||
173 | const char **out; | 175 | const char **out; |
174 | int argc, cpidx; | 176 | int argc, cpidx; |
175 | const char *opt; | 177 | const char *opt; |
178 | const struct option *excl_opt; | ||
176 | int flags; | 179 | int flags; |
177 | }; | 180 | }; |
178 | 181 | ||
@@ -211,4 +214,5 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | |||
211 | 214 | ||
212 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | 215 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); |
213 | 216 | ||
217 | void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); | ||
214 | #endif /* __PERF_PARSE_OPTIONS_H */ | 218 | #endif /* __PERF_PARSE_OPTIONS_H */ |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7a811eb61f75..5c9c4947cfb4 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -2,6 +2,8 @@ | |||
2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
3 | #include <unistd.h> | 3 | #include <unistd.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdbool.h> | ||
6 | #include <stdarg.h> | ||
5 | #include <dirent.h> | 7 | #include <dirent.h> |
6 | #include <api/fs/fs.h> | 8 | #include <api/fs/fs.h> |
7 | #include <locale.h> | 9 | #include <locale.h> |
@@ -10,16 +12,6 @@ | |||
10 | #include "parse-events.h" | 12 | #include "parse-events.h" |
11 | #include "cpumap.h" | 13 | #include "cpumap.h" |
12 | 14 | ||
13 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ | ||
14 | |||
15 | struct perf_pmu_alias { | ||
16 | char *name; | ||
17 | struct list_head terms; | ||
18 | struct list_head list; | ||
19 | char unit[UNIT_MAX_LEN+1]; | ||
20 | double scale; | ||
21 | }; | ||
22 | |||
23 | struct perf_pmu_format { | 15 | struct perf_pmu_format { |
24 | char *name; | 16 | char *name; |
25 | int value; | 17 | int value; |
@@ -171,6 +163,41 @@ error: | |||
171 | return -1; | 163 | return -1; |
172 | } | 164 | } |
173 | 165 | ||
166 | static int | ||
167 | perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name) | ||
168 | { | ||
169 | char path[PATH_MAX]; | ||
170 | int fd; | ||
171 | |||
172 | snprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name); | ||
173 | |||
174 | fd = open(path, O_RDONLY); | ||
175 | if (fd == -1) | ||
176 | return -1; | ||
177 | |||
178 | close(fd); | ||
179 | |||
180 | alias->per_pkg = true; | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, | ||
185 | char *dir, char *name) | ||
186 | { | ||
187 | char path[PATH_MAX]; | ||
188 | int fd; | ||
189 | |||
190 | snprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name); | ||
191 | |||
192 | fd = open(path, O_RDONLY); | ||
193 | if (fd == -1) | ||
194 | return -1; | ||
195 | |||
196 | alias->snapshot = true; | ||
197 | close(fd); | ||
198 | return 0; | ||
199 | } | ||
200 | |||
174 | static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) | 201 | static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) |
175 | { | 202 | { |
176 | struct perf_pmu_alias *alias; | 203 | struct perf_pmu_alias *alias; |
@@ -189,6 +216,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
189 | INIT_LIST_HEAD(&alias->terms); | 216 | INIT_LIST_HEAD(&alias->terms); |
190 | alias->scale = 1.0; | 217 | alias->scale = 1.0; |
191 | alias->unit[0] = '\0'; | 218 | alias->unit[0] = '\0'; |
219 | alias->per_pkg = false; | ||
192 | 220 | ||
193 | ret = parse_events_terms(&alias->terms, buf); | 221 | ret = parse_events_terms(&alias->terms, buf); |
194 | if (ret) { | 222 | if (ret) { |
@@ -202,12 +230,31 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
202 | */ | 230 | */ |
203 | perf_pmu__parse_unit(alias, dir, name); | 231 | perf_pmu__parse_unit(alias, dir, name); |
204 | perf_pmu__parse_scale(alias, dir, name); | 232 | perf_pmu__parse_scale(alias, dir, name); |
233 | perf_pmu__parse_per_pkg(alias, dir, name); | ||
234 | perf_pmu__parse_snapshot(alias, dir, name); | ||
205 | 235 | ||
206 | list_add_tail(&alias->list, list); | 236 | list_add_tail(&alias->list, list); |
207 | 237 | ||
208 | return 0; | 238 | return 0; |
209 | } | 239 | } |
210 | 240 | ||
241 | static inline bool pmu_alias_info_file(char *name) | ||
242 | { | ||
243 | size_t len; | ||
244 | |||
245 | len = strlen(name); | ||
246 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
247 | return true; | ||
248 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
249 | return true; | ||
250 | if (len > 8 && !strcmp(name + len - 8, ".per-pkg")) | ||
251 | return true; | ||
252 | if (len > 9 && !strcmp(name + len - 9, ".snapshot")) | ||
253 | return true; | ||
254 | |||
255 | return false; | ||
256 | } | ||
257 | |||
211 | /* | 258 | /* |
212 | * Process all the sysfs attributes located under the directory | 259 | * Process all the sysfs attributes located under the directory |
213 | * specified in 'dir' parameter. | 260 | * specified in 'dir' parameter. |
@@ -216,7 +263,6 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
216 | { | 263 | { |
217 | struct dirent *evt_ent; | 264 | struct dirent *evt_ent; |
218 | DIR *event_dir; | 265 | DIR *event_dir; |
219 | size_t len; | ||
220 | int ret = 0; | 266 | int ret = 0; |
221 | 267 | ||
222 | event_dir = opendir(dir); | 268 | event_dir = opendir(dir); |
@@ -232,13 +278,9 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
232 | continue; | 278 | continue; |
233 | 279 | ||
234 | /* | 280 | /* |
235 | * skip .unit and .scale info files | 281 | * skip info files parsed in perf_pmu__new_alias() |
236 | * parsed in perf_pmu__new_alias() | ||
237 | */ | 282 | */ |
238 | len = strlen(name); | 283 | if (pmu_alias_info_file(name)) |
239 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
240 | continue; | ||
241 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
242 | continue; | 284 | continue; |
243 | 285 | ||
244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 286 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
@@ -387,6 +429,12 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
387 | return cpus; | 429 | return cpus; |
388 | } | 430 | } |
389 | 431 | ||
432 | struct perf_event_attr *__attribute__((weak)) | ||
433 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | ||
434 | { | ||
435 | return NULL; | ||
436 | } | ||
437 | |||
390 | static struct perf_pmu *pmu_lookup(const char *name) | 438 | static struct perf_pmu *pmu_lookup(const char *name) |
391 | { | 439 | { |
392 | struct perf_pmu *pmu; | 440 | struct perf_pmu *pmu; |
@@ -421,6 +469,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
421 | pmu->name = strdup(name); | 469 | pmu->name = strdup(name); |
422 | pmu->type = type; | 470 | pmu->type = type; |
423 | list_add_tail(&pmu->list, &pmus); | 471 | list_add_tail(&pmu->list, &pmus); |
472 | |||
473 | pmu->default_config = perf_pmu__get_default_config(pmu); | ||
474 | |||
424 | return pmu; | 475 | return pmu; |
425 | } | 476 | } |
426 | 477 | ||
@@ -479,28 +530,24 @@ pmu_find_format(struct list_head *formats, char *name) | |||
479 | } | 530 | } |
480 | 531 | ||
481 | /* | 532 | /* |
482 | * Returns value based on the format definition (format parameter) | 533 | * Sets value based on the format definition (format parameter) |
483 | * and unformated value (value parameter). | 534 | * and unformated value (value parameter). |
484 | * | ||
485 | * TODO maybe optimize a little ;) | ||
486 | */ | 535 | */ |
487 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | 536 | static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, |
537 | bool zero) | ||
488 | { | 538 | { |
489 | unsigned long fbit, vbit; | 539 | unsigned long fbit, vbit; |
490 | __u64 v = 0; | ||
491 | 540 | ||
492 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | 541 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { |
493 | 542 | ||
494 | if (!test_bit(fbit, format)) | 543 | if (!test_bit(fbit, format)) |
495 | continue; | 544 | continue; |
496 | 545 | ||
497 | if (!(value & (1llu << vbit++))) | 546 | if (value & (1llu << vbit++)) |
498 | continue; | 547 | *v |= (1llu << fbit); |
499 | 548 | else if (zero) | |
500 | v |= (1llu << fbit); | 549 | *v &= ~(1llu << fbit); |
501 | } | 550 | } |
502 | |||
503 | return v; | ||
504 | } | 551 | } |
505 | 552 | ||
506 | /* | 553 | /* |
@@ -509,7 +556,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
509 | */ | 556 | */ |
510 | static int pmu_config_term(struct list_head *formats, | 557 | static int pmu_config_term(struct list_head *formats, |
511 | struct perf_event_attr *attr, | 558 | struct perf_event_attr *attr, |
512 | struct parse_events_term *term) | 559 | struct parse_events_term *term, |
560 | bool zero) | ||
513 | { | 561 | { |
514 | struct perf_pmu_format *format; | 562 | struct perf_pmu_format *format; |
515 | __u64 *vp; | 563 | __u64 *vp; |
@@ -548,18 +596,19 @@ static int pmu_config_term(struct list_head *formats, | |||
548 | * non-hardcoded terms, here's the place to translate | 596 | * non-hardcoded terms, here's the place to translate |
549 | * them into value. | 597 | * them into value. |
550 | */ | 598 | */ |
551 | *vp |= pmu_format_value(format->bits, term->val.num); | 599 | pmu_format_value(format->bits, term->val.num, vp, zero); |
552 | return 0; | 600 | return 0; |
553 | } | 601 | } |
554 | 602 | ||
555 | int perf_pmu__config_terms(struct list_head *formats, | 603 | int perf_pmu__config_terms(struct list_head *formats, |
556 | struct perf_event_attr *attr, | 604 | struct perf_event_attr *attr, |
557 | struct list_head *head_terms) | 605 | struct list_head *head_terms, |
606 | bool zero) | ||
558 | { | 607 | { |
559 | struct parse_events_term *term; | 608 | struct parse_events_term *term; |
560 | 609 | ||
561 | list_for_each_entry(term, head_terms, list) | 610 | list_for_each_entry(term, head_terms, list) |
562 | if (pmu_config_term(formats, attr, term)) | 611 | if (pmu_config_term(formats, attr, term, zero)) |
563 | return -EINVAL; | 612 | return -EINVAL; |
564 | 613 | ||
565 | return 0; | 614 | return 0; |
@@ -573,8 +622,10 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
573 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 622 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
574 | struct list_head *head_terms) | 623 | struct list_head *head_terms) |
575 | { | 624 | { |
625 | bool zero = !!pmu->default_config; | ||
626 | |||
576 | attr->type = pmu->type; | 627 | attr->type = pmu->type; |
577 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); | 628 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero); |
578 | } | 629 | } |
579 | 630 | ||
580 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | 631 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
@@ -608,23 +659,27 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | |||
608 | } | 659 | } |
609 | 660 | ||
610 | 661 | ||
611 | static int check_unit_scale(struct perf_pmu_alias *alias, | 662 | static int check_info_data(struct perf_pmu_alias *alias, |
612 | const char **unit, double *scale) | 663 | struct perf_pmu_info *info) |
613 | { | 664 | { |
614 | /* | 665 | /* |
615 | * Only one term in event definition can | 666 | * Only one term in event definition can |
616 | * define unit and scale, fail if there's | 667 | * define unit, scale and snapshot, fail |
617 | * more than one. | 668 | * if there's more than one. |
618 | */ | 669 | */ |
619 | if ((*unit && alias->unit) || | 670 | if ((info->unit && alias->unit) || |
620 | (*scale && alias->scale)) | 671 | (info->scale && alias->scale) || |
672 | (info->snapshot && alias->snapshot)) | ||
621 | return -EINVAL; | 673 | return -EINVAL; |
622 | 674 | ||
623 | if (alias->unit) | 675 | if (alias->unit) |
624 | *unit = alias->unit; | 676 | info->unit = alias->unit; |
625 | 677 | ||
626 | if (alias->scale) | 678 | if (alias->scale) |
627 | *scale = alias->scale; | 679 | info->scale = alias->scale; |
680 | |||
681 | if (alias->snapshot) | ||
682 | info->snapshot = alias->snapshot; | ||
628 | 683 | ||
629 | return 0; | 684 | return 0; |
630 | } | 685 | } |
@@ -634,18 +689,21 @@ static int check_unit_scale(struct perf_pmu_alias *alias, | |||
634 | * defined for the alias | 689 | * defined for the alias |
635 | */ | 690 | */ |
636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 691 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
637 | const char **unit, double *scale) | 692 | struct perf_pmu_info *info) |
638 | { | 693 | { |
639 | struct parse_events_term *term, *h; | 694 | struct parse_events_term *term, *h; |
640 | struct perf_pmu_alias *alias; | 695 | struct perf_pmu_alias *alias; |
641 | int ret; | 696 | int ret; |
642 | 697 | ||
698 | info->per_pkg = false; | ||
699 | |||
643 | /* | 700 | /* |
644 | * Mark unit and scale as not set | 701 | * Mark unit and scale as not set |
645 | * (different from default values, see below) | 702 | * (different from default values, see below) |
646 | */ | 703 | */ |
647 | *unit = NULL; | 704 | info->unit = NULL; |
648 | *scale = 0.0; | 705 | info->scale = 0.0; |
706 | info->snapshot = false; | ||
649 | 707 | ||
650 | list_for_each_entry_safe(term, h, head_terms, list) { | 708 | list_for_each_entry_safe(term, h, head_terms, list) { |
651 | alias = pmu_find_alias(pmu, term); | 709 | alias = pmu_find_alias(pmu, term); |
@@ -655,10 +713,13 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
655 | if (ret) | 713 | if (ret) |
656 | return ret; | 714 | return ret; |
657 | 715 | ||
658 | ret = check_unit_scale(alias, unit, scale); | 716 | ret = check_info_data(alias, info); |
659 | if (ret) | 717 | if (ret) |
660 | return ret; | 718 | return ret; |
661 | 719 | ||
720 | if (alias->per_pkg) | ||
721 | info->per_pkg = true; | ||
722 | |||
662 | list_del(&term->list); | 723 | list_del(&term->list); |
663 | free(term); | 724 | free(term); |
664 | } | 725 | } |
@@ -668,11 +729,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
668 | * set defaults as for evsel | 729 | * set defaults as for evsel |
669 | * unit cannot left to NULL | 730 | * unit cannot left to NULL |
670 | */ | 731 | */ |
671 | if (*unit == NULL) | 732 | if (info->unit == NULL) |
672 | *unit = ""; | 733 | info->unit = ""; |
673 | 734 | ||
674 | if (*scale == 0.0) | 735 | if (info->scale == 0.0) |
675 | *scale = 1.0; | 736 | info->scale = 1.0; |
676 | 737 | ||
677 | return 0; | 738 | return 0; |
678 | } | 739 | } |
@@ -738,15 +799,18 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
738 | 799 | ||
739 | pmu = NULL; | 800 | pmu = NULL; |
740 | len = 0; | 801 | len = 0; |
741 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | 802 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
742 | list_for_each_entry(alias, &pmu->aliases, list) | 803 | list_for_each_entry(alias, &pmu->aliases, list) |
743 | len++; | 804 | len++; |
744 | aliases = malloc(sizeof(char *) * len); | 805 | if (pmu->selectable) |
806 | len++; | ||
807 | } | ||
808 | aliases = zalloc(sizeof(char *) * len); | ||
745 | if (!aliases) | 809 | if (!aliases) |
746 | return; | 810 | goto out_enomem; |
747 | pmu = NULL; | 811 | pmu = NULL; |
748 | j = 0; | 812 | j = 0; |
749 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | 813 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
750 | list_for_each_entry(alias, &pmu->aliases, list) { | 814 | list_for_each_entry(alias, &pmu->aliases, list) { |
751 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | 815 | char *name = format_alias(buf, sizeof(buf), pmu, alias); |
752 | bool is_cpu = !strcmp(pmu->name, "cpu"); | 816 | bool is_cpu = !strcmp(pmu->name, "cpu"); |
@@ -756,13 +820,23 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
756 | (!is_cpu && strglobmatch(alias->name, | 820 | (!is_cpu && strglobmatch(alias->name, |
757 | event_glob)))) | 821 | event_glob)))) |
758 | continue; | 822 | continue; |
759 | aliases[j] = name; | 823 | |
760 | if (is_cpu && !name_only) | 824 | if (is_cpu && !name_only) |
761 | aliases[j] = format_alias_or(buf, sizeof(buf), | 825 | name = format_alias_or(buf, sizeof(buf), pmu, alias); |
762 | pmu, alias); | 826 | |
763 | aliases[j] = strdup(aliases[j]); | 827 | aliases[j] = strdup(name); |
828 | if (aliases[j] == NULL) | ||
829 | goto out_enomem; | ||
764 | j++; | 830 | j++; |
765 | } | 831 | } |
832 | if (pmu->selectable) { | ||
833 | char *s; | ||
834 | if (asprintf(&s, "%s//", pmu->name) < 0) | ||
835 | goto out_enomem; | ||
836 | aliases[j] = s; | ||
837 | j++; | ||
838 | } | ||
839 | } | ||
766 | len = j; | 840 | len = j; |
767 | qsort(aliases, len, sizeof(char *), cmp_string); | 841 | qsort(aliases, len, sizeof(char *), cmp_string); |
768 | for (j = 0; j < len; j++) { | 842 | for (j = 0; j < len; j++) { |
@@ -771,12 +845,20 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
771 | continue; | 845 | continue; |
772 | } | 846 | } |
773 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | 847 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); |
774 | zfree(&aliases[j]); | ||
775 | printed++; | 848 | printed++; |
776 | } | 849 | } |
777 | if (printed) | 850 | if (printed) |
778 | printf("\n"); | 851 | printf("\n"); |
779 | free(aliases); | 852 | out_free: |
853 | for (j = 0; j < len; j++) | ||
854 | zfree(&aliases[j]); | ||
855 | zfree(&aliases); | ||
856 | return; | ||
857 | |||
858 | out_enomem: | ||
859 | printf("FATAL: not enough memory to print PMU events\n"); | ||
860 | if (aliases) | ||
861 | goto out_free; | ||
780 | } | 862 | } |
781 | 863 | ||
782 | bool pmu_have_event(const char *pname, const char *name) | 864 | bool pmu_have_event(const char *pname, const char *name) |
@@ -794,3 +876,39 @@ bool pmu_have_event(const char *pname, const char *name) | |||
794 | } | 876 | } |
795 | return false; | 877 | return false; |
796 | } | 878 | } |
879 | |||
880 | static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) | ||
881 | { | ||
882 | struct stat st; | ||
883 | char path[PATH_MAX]; | ||
884 | const char *sysfs; | ||
885 | |||
886 | sysfs = sysfs__mountpoint(); | ||
887 | if (!sysfs) | ||
888 | return NULL; | ||
889 | |||
890 | snprintf(path, PATH_MAX, | ||
891 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); | ||
892 | |||
893 | if (stat(path, &st) < 0) | ||
894 | return NULL; | ||
895 | |||
896 | return fopen(path, "r"); | ||
897 | } | ||
898 | |||
899 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
900 | ...) | ||
901 | { | ||
902 | va_list args; | ||
903 | FILE *file; | ||
904 | int ret = EOF; | ||
905 | |||
906 | va_start(args, fmt); | ||
907 | file = perf_pmu__open_file(pmu, name); | ||
908 | if (file) { | ||
909 | ret = vfscanf(file, fmt, args); | ||
910 | fclose(file); | ||
911 | } | ||
912 | va_end(args); | ||
913 | return ret; | ||
914 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index c14a543ce1f3..6b1249fbdb5f 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -13,13 +13,36 @@ enum { | |||
13 | 13 | ||
14 | #define PERF_PMU_FORMAT_BITS 64 | 14 | #define PERF_PMU_FORMAT_BITS 64 |
15 | 15 | ||
16 | struct perf_event_attr; | ||
17 | |||
16 | struct perf_pmu { | 18 | struct perf_pmu { |
17 | char *name; | 19 | char *name; |
18 | __u32 type; | 20 | __u32 type; |
21 | bool selectable; | ||
22 | struct perf_event_attr *default_config; | ||
19 | struct cpu_map *cpus; | 23 | struct cpu_map *cpus; |
20 | struct list_head format; | 24 | struct list_head format; /* HEAD struct perf_pmu_format -> list */ |
21 | struct list_head aliases; | 25 | struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */ |
22 | struct list_head list; | 26 | struct list_head list; /* ELEM */ |
27 | }; | ||
28 | |||
29 | struct perf_pmu_info { | ||
30 | const char *unit; | ||
31 | double scale; | ||
32 | bool per_pkg; | ||
33 | bool snapshot; | ||
34 | }; | ||
35 | |||
36 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ | ||
37 | |||
38 | struct perf_pmu_alias { | ||
39 | char *name; | ||
40 | struct list_head terms; /* HEAD struct parse_events_term -> list */ | ||
41 | struct list_head list; /* ELEM */ | ||
42 | char unit[UNIT_MAX_LEN+1]; | ||
43 | double scale; | ||
44 | bool per_pkg; | ||
45 | bool snapshot; | ||
23 | }; | 46 | }; |
24 | 47 | ||
25 | struct perf_pmu *perf_pmu__find(const char *name); | 48 | struct perf_pmu *perf_pmu__find(const char *name); |
@@ -27,9 +50,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
27 | struct list_head *head_terms); | 50 | struct list_head *head_terms); |
28 | int perf_pmu__config_terms(struct list_head *formats, | 51 | int perf_pmu__config_terms(struct list_head *formats, |
29 | struct perf_event_attr *attr, | 52 | struct perf_event_attr *attr, |
30 | struct list_head *head_terms); | 53 | struct list_head *head_terms, |
54 | bool zero); | ||
31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 55 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
32 | const char **unit, double *scale); | 56 | struct perf_pmu_info *info); |
33 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 57 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
34 | struct list_head *head_terms); | 58 | struct list_head *head_terms); |
35 | int perf_pmu_wrap(void); | 59 | int perf_pmu_wrap(void); |
@@ -45,5 +69,11 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | |||
45 | void print_pmu_events(const char *event_glob, bool name_only); | 69 | void print_pmu_events(const char *event_glob, bool name_only); |
46 | bool pmu_have_event(const char *pname, const char *name); | 70 | bool pmu_have_event(const char *pname, const char *name); |
47 | 71 | ||
72 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
73 | ...) __attribute__((format(scanf, 3, 4))); | ||
74 | |||
48 | int perf_pmu__test(void); | 75 | int perf_pmu__test(void); |
76 | |||
77 | struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); | ||
78 | |||
49 | #endif /* __PMU_H */ | 79 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9a0a1839a377..7f9b8632e433 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -79,7 +79,7 @@ static int init_symbol_maps(bool user_only) | |||
79 | int ret; | 79 | int ret; |
80 | 80 | ||
81 | symbol_conf.sort_by_name = true; | 81 | symbol_conf.sort_by_name = true; |
82 | ret = symbol__init(); | 82 | ret = symbol__init(NULL); |
83 | if (ret < 0) { | 83 | if (ret < 0) { |
84 | pr_debug("Failed to init symbol map.\n"); | 84 | pr_debug("Failed to init symbol map.\n"); |
85 | goto out; | 85 | goto out; |
@@ -184,7 +184,8 @@ static struct dso *kernel_get_module_dso(const char *module) | |||
184 | const char *vmlinux_name; | 184 | const char *vmlinux_name; |
185 | 185 | ||
186 | if (module) { | 186 | if (module) { |
187 | list_for_each_entry(dso, &host_machine->kernel_dsos, node) { | 187 | list_for_each_entry(dso, &host_machine->kernel_dsos.head, |
188 | node) { | ||
188 | if (strncmp(dso->short_name + 1, module, | 189 | if (strncmp(dso->short_name + 1, module, |
189 | dso->short_name_len - 2) == 0) | 190 | dso->short_name_len - 2) == 0) |
190 | goto found; | 191 | goto found; |
@@ -258,21 +259,33 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | |||
258 | #ifdef HAVE_DWARF_SUPPORT | 259 | #ifdef HAVE_DWARF_SUPPORT |
259 | 260 | ||
260 | /* Open new debuginfo of given module */ | 261 | /* Open new debuginfo of given module */ |
261 | static struct debuginfo *open_debuginfo(const char *module) | 262 | static struct debuginfo *open_debuginfo(const char *module, bool silent) |
262 | { | 263 | { |
263 | const char *path = module; | 264 | const char *path = module; |
265 | struct debuginfo *ret; | ||
264 | 266 | ||
265 | if (!module || !strchr(module, '/')) { | 267 | if (!module || !strchr(module, '/')) { |
266 | path = kernel_get_module_path(module); | 268 | path = kernel_get_module_path(module); |
267 | if (!path) { | 269 | if (!path) { |
268 | pr_err("Failed to find path of %s module.\n", | 270 | if (!silent) |
269 | module ?: "kernel"); | 271 | pr_err("Failed to find path of %s module.\n", |
272 | module ?: "kernel"); | ||
270 | return NULL; | 273 | return NULL; |
271 | } | 274 | } |
272 | } | 275 | } |
273 | return debuginfo__new(path); | 276 | ret = debuginfo__new(path); |
277 | if (!ret && !silent) { | ||
278 | pr_warning("The %s file has no debug information.\n", path); | ||
279 | if (!module || !strtailcmp(path, ".ko")) | ||
280 | pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); | ||
281 | else | ||
282 | pr_warning("Rebuild with -g, "); | ||
283 | pr_warning("or install an appropriate debuginfo package.\n"); | ||
284 | } | ||
285 | return ret; | ||
274 | } | 286 | } |
275 | 287 | ||
288 | |||
276 | static int get_text_start_address(const char *exec, unsigned long *address) | 289 | static int get_text_start_address(const char *exec, unsigned long *address) |
277 | { | 290 | { |
278 | Elf *elf; | 291 | Elf *elf; |
@@ -333,15 +346,13 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | |||
333 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, | 346 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, |
334 | tp->module ? : "kernel"); | 347 | tp->module ? : "kernel"); |
335 | 348 | ||
336 | dinfo = open_debuginfo(tp->module); | 349 | dinfo = open_debuginfo(tp->module, verbose == 0); |
337 | if (dinfo) { | 350 | if (dinfo) { |
338 | ret = debuginfo__find_probe_point(dinfo, | 351 | ret = debuginfo__find_probe_point(dinfo, |
339 | (unsigned long)addr, pp); | 352 | (unsigned long)addr, pp); |
340 | debuginfo__delete(dinfo); | 353 | debuginfo__delete(dinfo); |
341 | } else { | 354 | } else |
342 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); | ||
343 | ret = -ENOENT; | 355 | ret = -ENOENT; |
344 | } | ||
345 | 356 | ||
346 | if (ret > 0) { | 357 | if (ret > 0) { |
347 | pp->retprobe = tp->retprobe; | 358 | pp->retprobe = tp->retprobe; |
@@ -457,13 +468,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
457 | struct debuginfo *dinfo; | 468 | struct debuginfo *dinfo; |
458 | int ntevs, ret = 0; | 469 | int ntevs, ret = 0; |
459 | 470 | ||
460 | dinfo = open_debuginfo(target); | 471 | dinfo = open_debuginfo(target, !need_dwarf); |
461 | 472 | ||
462 | if (!dinfo) { | 473 | if (!dinfo) { |
463 | if (need_dwarf) { | 474 | if (need_dwarf) |
464 | pr_warning("Failed to open debuginfo file.\n"); | ||
465 | return -ENOENT; | 475 | return -ENOENT; |
466 | } | ||
467 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); | 476 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
468 | return 0; | 477 | return 0; |
469 | } | 478 | } |
@@ -486,9 +495,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
486 | } | 495 | } |
487 | 496 | ||
488 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 497 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
489 | pr_warning("Probe point '%s' not found.\n", | 498 | pr_warning("Probe point '%s' not found in debuginfo.\n", |
490 | synthesize_perf_probe_point(&pev->point)); | 499 | synthesize_perf_probe_point(&pev->point)); |
491 | return -ENOENT; | 500 | if (need_dwarf) |
501 | return -ENOENT; | ||
502 | return 0; | ||
492 | } | 503 | } |
493 | /* Error path : ntevs < 0 */ | 504 | /* Error path : ntevs < 0 */ |
494 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); | 505 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); |
@@ -565,7 +576,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir, | |||
565 | 576 | ||
566 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | 577 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
567 | { | 578 | { |
568 | char buf[LINEBUF_SIZE]; | 579 | char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE]; |
569 | const char *color = show_num ? "" : PERF_COLOR_BLUE; | 580 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
570 | const char *prefix = NULL; | 581 | const char *prefix = NULL; |
571 | 582 | ||
@@ -585,7 +596,8 @@ static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | |||
585 | return 1; | 596 | return 1; |
586 | error: | 597 | error: |
587 | if (ferror(fp)) { | 598 | if (ferror(fp)) { |
588 | pr_warning("File read error: %s\n", strerror(errno)); | 599 | pr_warning("File read error: %s\n", |
600 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
589 | return -1; | 601 | return -1; |
590 | } | 602 | } |
591 | return 0; | 603 | return 0; |
@@ -618,13 +630,12 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
618 | FILE *fp; | 630 | FILE *fp; |
619 | int ret; | 631 | int ret; |
620 | char *tmp; | 632 | char *tmp; |
633 | char sbuf[STRERR_BUFSIZE]; | ||
621 | 634 | ||
622 | /* Search a line range */ | 635 | /* Search a line range */ |
623 | dinfo = open_debuginfo(module); | 636 | dinfo = open_debuginfo(module, false); |
624 | if (!dinfo) { | 637 | if (!dinfo) |
625 | pr_warning("Failed to open debuginfo file.\n"); | ||
626 | return -ENOENT; | 638 | return -ENOENT; |
627 | } | ||
628 | 639 | ||
629 | ret = debuginfo__find_line_range(dinfo, lr); | 640 | ret = debuginfo__find_line_range(dinfo, lr); |
630 | debuginfo__delete(dinfo); | 641 | debuginfo__delete(dinfo); |
@@ -656,7 +667,7 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
656 | fp = fopen(lr->path, "r"); | 667 | fp = fopen(lr->path, "r"); |
657 | if (fp == NULL) { | 668 | if (fp == NULL) { |
658 | pr_warning("Failed to open %s: %s\n", lr->path, | 669 | pr_warning("Failed to open %s: %s\n", lr->path, |
659 | strerror(errno)); | 670 | strerror_r(errno, sbuf, sizeof(sbuf))); |
660 | return -errno; | 671 | return -errno; |
661 | } | 672 | } |
662 | /* Skip to starting line number */ | 673 | /* Skip to starting line number */ |
@@ -689,11 +700,11 @@ end: | |||
689 | return ret; | 700 | return ret; |
690 | } | 701 | } |
691 | 702 | ||
692 | int show_line_range(struct line_range *lr, const char *module) | 703 | int show_line_range(struct line_range *lr, const char *module, bool user) |
693 | { | 704 | { |
694 | int ret; | 705 | int ret; |
695 | 706 | ||
696 | ret = init_symbol_maps(false); | 707 | ret = init_symbol_maps(user); |
697 | if (ret < 0) | 708 | if (ret < 0) |
698 | return ret; | 709 | return ret; |
699 | ret = __show_line_range(lr, module); | 710 | ret = __show_line_range(lr, module); |
@@ -768,13 +779,12 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
768 | int i, ret = 0; | 779 | int i, ret = 0; |
769 | struct debuginfo *dinfo; | 780 | struct debuginfo *dinfo; |
770 | 781 | ||
771 | ret = init_symbol_maps(false); | 782 | ret = init_symbol_maps(pevs->uprobes); |
772 | if (ret < 0) | 783 | if (ret < 0) |
773 | return ret; | 784 | return ret; |
774 | 785 | ||
775 | dinfo = open_debuginfo(module); | 786 | dinfo = open_debuginfo(module, false); |
776 | if (!dinfo) { | 787 | if (!dinfo) { |
777 | pr_warning("Failed to open debuginfo file.\n"); | ||
778 | ret = -ENOENT; | 788 | ret = -ENOENT; |
779 | goto out; | 789 | goto out; |
780 | } | 790 | } |
@@ -815,7 +825,8 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
815 | } | 825 | } |
816 | 826 | ||
817 | int show_line_range(struct line_range *lr __maybe_unused, | 827 | int show_line_range(struct line_range *lr __maybe_unused, |
818 | const char *module __maybe_unused) | 828 | const char *module __maybe_unused, |
829 | bool user __maybe_unused) | ||
819 | { | 830 | { |
820 | pr_warning("Debuginfo-analysis is not supported.\n"); | 831 | pr_warning("Debuginfo-analysis is not supported.\n"); |
821 | return -ENOSYS; | 832 | return -ENOSYS; |
@@ -1405,8 +1416,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
1405 | 1416 | ||
1406 | return tmp - buf; | 1417 | return tmp - buf; |
1407 | error: | 1418 | error: |
1408 | pr_debug("Failed to synthesize perf probe argument: %s\n", | 1419 | pr_debug("Failed to synthesize perf probe argument: %d\n", ret); |
1409 | strerror(-ret)); | ||
1410 | return ret; | 1420 | return ret; |
1411 | } | 1421 | } |
1412 | 1422 | ||
@@ -1455,8 +1465,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
1455 | 1465 | ||
1456 | return buf; | 1466 | return buf; |
1457 | error: | 1467 | error: |
1458 | pr_debug("Failed to synthesize perf probe point: %s\n", | 1468 | pr_debug("Failed to synthesize perf probe point: %d\n", ret); |
1459 | strerror(-ret)); | ||
1460 | free(buf); | 1469 | free(buf); |
1461 | return NULL; | 1470 | return NULL; |
1462 | } | 1471 | } |
@@ -1780,10 +1789,11 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
1780 | memset(tev, 0, sizeof(*tev)); | 1789 | memset(tev, 0, sizeof(*tev)); |
1781 | } | 1790 | } |
1782 | 1791 | ||
1783 | static void print_warn_msg(const char *file, bool is_kprobe) | 1792 | static void print_open_warning(int err, bool is_kprobe) |
1784 | { | 1793 | { |
1794 | char sbuf[STRERR_BUFSIZE]; | ||
1785 | 1795 | ||
1786 | if (errno == ENOENT) { | 1796 | if (err == -ENOENT) { |
1787 | const char *config; | 1797 | const char *config; |
1788 | 1798 | ||
1789 | if (!is_kprobe) | 1799 | if (!is_kprobe) |
@@ -1791,25 +1801,43 @@ static void print_warn_msg(const char *file, bool is_kprobe) | |||
1791 | else | 1801 | else |
1792 | config = "CONFIG_KPROBE_EVENTS"; | 1802 | config = "CONFIG_KPROBE_EVENTS"; |
1793 | 1803 | ||
1794 | pr_warning("%s file does not exist - please rebuild kernel" | 1804 | pr_warning("%cprobe_events file does not exist" |
1795 | " with %s.\n", file, config); | 1805 | " - please rebuild kernel with %s.\n", |
1796 | } else | 1806 | is_kprobe ? 'k' : 'u', config); |
1797 | pr_warning("Failed to open %s file: %s\n", file, | 1807 | } else if (err == -ENOTSUP) |
1798 | strerror(errno)); | 1808 | pr_warning("Debugfs is not mounted.\n"); |
1809 | else | ||
1810 | pr_warning("Failed to open %cprobe_events: %s\n", | ||
1811 | is_kprobe ? 'k' : 'u', | ||
1812 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
1813 | } | ||
1814 | |||
1815 | static void print_both_open_warning(int kerr, int uerr) | ||
1816 | { | ||
1817 | /* Both kprobes and uprobes are disabled, warn it. */ | ||
1818 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) | ||
1819 | pr_warning("Debugfs is not mounted.\n"); | ||
1820 | else if (kerr == -ENOENT && uerr == -ENOENT) | ||
1821 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " | ||
1822 | "or/and CONFIG_UPROBE_EVENTS.\n"); | ||
1823 | else { | ||
1824 | char sbuf[STRERR_BUFSIZE]; | ||
1825 | pr_warning("Failed to open kprobe events: %s.\n", | ||
1826 | strerror_r(-kerr, sbuf, sizeof(sbuf))); | ||
1827 | pr_warning("Failed to open uprobe events: %s.\n", | ||
1828 | strerror_r(-uerr, sbuf, sizeof(sbuf))); | ||
1829 | } | ||
1799 | } | 1830 | } |
1800 | 1831 | ||
1801 | static int open_probe_events(const char *trace_file, bool readwrite, | 1832 | static int open_probe_events(const char *trace_file, bool readwrite) |
1802 | bool is_kprobe) | ||
1803 | { | 1833 | { |
1804 | char buf[PATH_MAX]; | 1834 | char buf[PATH_MAX]; |
1805 | const char *__debugfs; | 1835 | const char *__debugfs; |
1806 | int ret; | 1836 | int ret; |
1807 | 1837 | ||
1808 | __debugfs = debugfs_find_mountpoint(); | 1838 | __debugfs = debugfs_find_mountpoint(); |
1809 | if (__debugfs == NULL) { | 1839 | if (__debugfs == NULL) |
1810 | pr_warning("Debugfs is not mounted.\n"); | 1840 | return -ENOTSUP; |
1811 | return -ENOENT; | ||
1812 | } | ||
1813 | 1841 | ||
1814 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); | 1842 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); |
1815 | if (ret >= 0) { | 1843 | if (ret >= 0) { |
@@ -1820,19 +1848,19 @@ static int open_probe_events(const char *trace_file, bool readwrite, | |||
1820 | ret = open(buf, O_RDONLY, 0); | 1848 | ret = open(buf, O_RDONLY, 0); |
1821 | 1849 | ||
1822 | if (ret < 0) | 1850 | if (ret < 0) |
1823 | print_warn_msg(buf, is_kprobe); | 1851 | ret = -errno; |
1824 | } | 1852 | } |
1825 | return ret; | 1853 | return ret; |
1826 | } | 1854 | } |
1827 | 1855 | ||
1828 | static int open_kprobe_events(bool readwrite) | 1856 | static int open_kprobe_events(bool readwrite) |
1829 | { | 1857 | { |
1830 | return open_probe_events("tracing/kprobe_events", readwrite, true); | 1858 | return open_probe_events("tracing/kprobe_events", readwrite); |
1831 | } | 1859 | } |
1832 | 1860 | ||
1833 | static int open_uprobe_events(bool readwrite) | 1861 | static int open_uprobe_events(bool readwrite) |
1834 | { | 1862 | { |
1835 | return open_probe_events("tracing/uprobe_events", readwrite, false); | 1863 | return open_probe_events("tracing/uprobe_events", readwrite); |
1836 | } | 1864 | } |
1837 | 1865 | ||
1838 | /* Get raw string list of current kprobe_events or uprobe_events */ | 1866 | /* Get raw string list of current kprobe_events or uprobe_events */ |
@@ -1857,7 +1885,7 @@ static struct strlist *get_probe_trace_command_rawlist(int fd) | |||
1857 | p[idx] = '\0'; | 1885 | p[idx] = '\0'; |
1858 | ret = strlist__add(sl, buf); | 1886 | ret = strlist__add(sl, buf); |
1859 | if (ret < 0) { | 1887 | if (ret < 0) { |
1860 | pr_debug("strlist__add failed: %s\n", strerror(-ret)); | 1888 | pr_debug("strlist__add failed (%d)\n", ret); |
1861 | strlist__delete(sl); | 1889 | strlist__delete(sl); |
1862 | return NULL; | 1890 | return NULL; |
1863 | } | 1891 | } |
@@ -1884,21 +1912,21 @@ static int show_perf_probe_event(struct perf_probe_event *pev, | |||
1884 | if (ret < 0) | 1912 | if (ret < 0) |
1885 | return ret; | 1913 | return ret; |
1886 | 1914 | ||
1887 | printf(" %-20s (on %s", buf, place); | 1915 | pr_info(" %-20s (on %s", buf, place); |
1888 | if (module) | 1916 | if (module) |
1889 | printf(" in %s", module); | 1917 | pr_info(" in %s", module); |
1890 | 1918 | ||
1891 | if (pev->nargs > 0) { | 1919 | if (pev->nargs > 0) { |
1892 | printf(" with"); | 1920 | pr_info(" with"); |
1893 | for (i = 0; i < pev->nargs; i++) { | 1921 | for (i = 0; i < pev->nargs; i++) { |
1894 | ret = synthesize_perf_probe_arg(&pev->args[i], | 1922 | ret = synthesize_perf_probe_arg(&pev->args[i], |
1895 | buf, 128); | 1923 | buf, 128); |
1896 | if (ret < 0) | 1924 | if (ret < 0) |
1897 | break; | 1925 | break; |
1898 | printf(" %s", buf); | 1926 | pr_info(" %s", buf); |
1899 | } | 1927 | } |
1900 | } | 1928 | } |
1901 | printf(")\n"); | 1929 | pr_info(")\n"); |
1902 | free(place); | 1930 | free(place); |
1903 | return ret; | 1931 | return ret; |
1904 | } | 1932 | } |
@@ -1916,7 +1944,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
1916 | 1944 | ||
1917 | rawlist = get_probe_trace_command_rawlist(fd); | 1945 | rawlist = get_probe_trace_command_rawlist(fd); |
1918 | if (!rawlist) | 1946 | if (!rawlist) |
1919 | return -ENOENT; | 1947 | return -ENOMEM; |
1920 | 1948 | ||
1921 | strlist__for_each(ent, rawlist) { | 1949 | strlist__for_each(ent, rawlist) { |
1922 | ret = parse_probe_trace_command(ent->s, &tev); | 1950 | ret = parse_probe_trace_command(ent->s, &tev); |
@@ -1940,27 +1968,34 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
1940 | /* List up current perf-probe events */ | 1968 | /* List up current perf-probe events */ |
1941 | int show_perf_probe_events(void) | 1969 | int show_perf_probe_events(void) |
1942 | { | 1970 | { |
1943 | int fd, ret; | 1971 | int kp_fd, up_fd, ret; |
1944 | 1972 | ||
1945 | setup_pager(); | 1973 | setup_pager(); |
1946 | fd = open_kprobe_events(false); | ||
1947 | |||
1948 | if (fd < 0) | ||
1949 | return fd; | ||
1950 | 1974 | ||
1951 | ret = init_symbol_maps(false); | 1975 | ret = init_symbol_maps(false); |
1952 | if (ret < 0) | 1976 | if (ret < 0) |
1953 | return ret; | 1977 | return ret; |
1954 | 1978 | ||
1955 | ret = __show_perf_probe_events(fd, true); | 1979 | kp_fd = open_kprobe_events(false); |
1956 | close(fd); | 1980 | if (kp_fd >= 0) { |
1981 | ret = __show_perf_probe_events(kp_fd, true); | ||
1982 | close(kp_fd); | ||
1983 | if (ret < 0) | ||
1984 | goto out; | ||
1985 | } | ||
1957 | 1986 | ||
1958 | fd = open_uprobe_events(false); | 1987 | up_fd = open_uprobe_events(false); |
1959 | if (fd >= 0) { | 1988 | if (kp_fd < 0 && up_fd < 0) { |
1960 | ret = __show_perf_probe_events(fd, false); | 1989 | print_both_open_warning(kp_fd, up_fd); |
1961 | close(fd); | 1990 | ret = kp_fd; |
1991 | goto out; | ||
1962 | } | 1992 | } |
1963 | 1993 | ||
1994 | if (up_fd >= 0) { | ||
1995 | ret = __show_perf_probe_events(up_fd, false); | ||
1996 | close(up_fd); | ||
1997 | } | ||
1998 | out: | ||
1964 | exit_symbol_maps(); | 1999 | exit_symbol_maps(); |
1965 | return ret; | 2000 | return ret; |
1966 | } | 2001 | } |
@@ -1976,6 +2011,8 @@ static struct strlist *get_probe_trace_event_names(int fd, bool include_group) | |||
1976 | 2011 | ||
1977 | memset(&tev, 0, sizeof(tev)); | 2012 | memset(&tev, 0, sizeof(tev)); |
1978 | rawlist = get_probe_trace_command_rawlist(fd); | 2013 | rawlist = get_probe_trace_command_rawlist(fd); |
2014 | if (!rawlist) | ||
2015 | return NULL; | ||
1979 | sl = strlist__new(true, NULL); | 2016 | sl = strlist__new(true, NULL); |
1980 | strlist__for_each(ent, rawlist) { | 2017 | strlist__for_each(ent, rawlist) { |
1981 | ret = parse_probe_trace_command(ent->s, &tev); | 2018 | ret = parse_probe_trace_command(ent->s, &tev); |
@@ -2005,6 +2042,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
2005 | { | 2042 | { |
2006 | int ret = 0; | 2043 | int ret = 0; |
2007 | char *buf = synthesize_probe_trace_command(tev); | 2044 | char *buf = synthesize_probe_trace_command(tev); |
2045 | char sbuf[STRERR_BUFSIZE]; | ||
2008 | 2046 | ||
2009 | if (!buf) { | 2047 | if (!buf) { |
2010 | pr_debug("Failed to synthesize probe trace event.\n"); | 2048 | pr_debug("Failed to synthesize probe trace event.\n"); |
@@ -2016,7 +2054,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
2016 | ret = write(fd, buf, strlen(buf)); | 2054 | ret = write(fd, buf, strlen(buf)); |
2017 | if (ret <= 0) | 2055 | if (ret <= 0) |
2018 | pr_warning("Failed to write event: %s\n", | 2056 | pr_warning("Failed to write event: %s\n", |
2019 | strerror(errno)); | 2057 | strerror_r(errno, sbuf, sizeof(sbuf))); |
2020 | } | 2058 | } |
2021 | free(buf); | 2059 | free(buf); |
2022 | return ret; | 2060 | return ret; |
@@ -2030,7 +2068,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
2030 | /* Try no suffix */ | 2068 | /* Try no suffix */ |
2031 | ret = e_snprintf(buf, len, "%s", base); | 2069 | ret = e_snprintf(buf, len, "%s", base); |
2032 | if (ret < 0) { | 2070 | if (ret < 0) { |
2033 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2071 | pr_debug("snprintf() failed: %d\n", ret); |
2034 | return ret; | 2072 | return ret; |
2035 | } | 2073 | } |
2036 | if (!strlist__has_entry(namelist, buf)) | 2074 | if (!strlist__has_entry(namelist, buf)) |
@@ -2046,7 +2084,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
2046 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 2084 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
2047 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 2085 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
2048 | if (ret < 0) { | 2086 | if (ret < 0) { |
2049 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2087 | pr_debug("snprintf() failed: %d\n", ret); |
2050 | return ret; | 2088 | return ret; |
2051 | } | 2089 | } |
2052 | if (!strlist__has_entry(namelist, buf)) | 2090 | if (!strlist__has_entry(namelist, buf)) |
@@ -2075,8 +2113,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
2075 | else | 2113 | else |
2076 | fd = open_kprobe_events(true); | 2114 | fd = open_kprobe_events(true); |
2077 | 2115 | ||
2078 | if (fd < 0) | 2116 | if (fd < 0) { |
2117 | print_open_warning(fd, !pev->uprobes); | ||
2079 | return fd; | 2118 | return fd; |
2119 | } | ||
2120 | |||
2080 | /* Get current event names */ | 2121 | /* Get current event names */ |
2081 | namelist = get_probe_trace_event_names(fd, false); | 2122 | namelist = get_probe_trace_event_names(fd, false); |
2082 | if (!namelist) { | 2123 | if (!namelist) { |
@@ -2085,7 +2126,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
2085 | } | 2126 | } |
2086 | 2127 | ||
2087 | ret = 0; | 2128 | ret = 0; |
2088 | printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); | 2129 | pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); |
2089 | for (i = 0; i < ntevs; i++) { | 2130 | for (i = 0; i < ntevs; i++) { |
2090 | tev = &tevs[i]; | 2131 | tev = &tevs[i]; |
2091 | if (pev->event) | 2132 | if (pev->event) |
@@ -2140,8 +2181,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
2140 | 2181 | ||
2141 | if (ret >= 0) { | 2182 | if (ret >= 0) { |
2142 | /* Show how to use the event. */ | 2183 | /* Show how to use the event. */ |
2143 | printf("\nYou can now use it in all perf tools, such as:\n\n"); | 2184 | pr_info("\nYou can now use it in all perf tools, such as:\n\n"); |
2144 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 2185 | pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
2145 | tev->event); | 2186 | tev->event); |
2146 | } | 2187 | } |
2147 | 2188 | ||
@@ -2405,10 +2446,11 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
2405 | goto error; | 2446 | goto error; |
2406 | } | 2447 | } |
2407 | 2448 | ||
2408 | printf("Removed event: %s\n", ent->s); | 2449 | pr_info("Removed event: %s\n", ent->s); |
2409 | return 0; | 2450 | return 0; |
2410 | error: | 2451 | error: |
2411 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 2452 | pr_warning("Failed to delete event: %s\n", |
2453 | strerror_r(-ret, buf, sizeof(buf))); | ||
2412 | return ret; | 2454 | return ret; |
2413 | } | 2455 | } |
2414 | 2456 | ||
@@ -2449,15 +2491,18 @@ int del_perf_probe_events(struct strlist *dellist) | |||
2449 | 2491 | ||
2450 | /* Get current event names */ | 2492 | /* Get current event names */ |
2451 | kfd = open_kprobe_events(true); | 2493 | kfd = open_kprobe_events(true); |
2452 | if (kfd < 0) | 2494 | if (kfd >= 0) |
2453 | return kfd; | 2495 | namelist = get_probe_trace_event_names(kfd, true); |
2454 | 2496 | ||
2455 | namelist = get_probe_trace_event_names(kfd, true); | ||
2456 | ufd = open_uprobe_events(true); | 2497 | ufd = open_uprobe_events(true); |
2457 | |||
2458 | if (ufd >= 0) | 2498 | if (ufd >= 0) |
2459 | unamelist = get_probe_trace_event_names(ufd, true); | 2499 | unamelist = get_probe_trace_event_names(ufd, true); |
2460 | 2500 | ||
2501 | if (kfd < 0 && ufd < 0) { | ||
2502 | print_both_open_warning(kfd, ufd); | ||
2503 | goto error; | ||
2504 | } | ||
2505 | |||
2461 | if (namelist == NULL && unamelist == NULL) | 2506 | if (namelist == NULL && unamelist == NULL) |
2462 | goto error; | 2507 | goto error; |
2463 | 2508 | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 776c9347a3b6..e01e9943139f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -128,7 +128,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
128 | bool force_add); | 128 | bool force_add); |
129 | extern int del_perf_probe_events(struct strlist *dellist); | 129 | extern int del_perf_probe_events(struct strlist *dellist); |
130 | extern int show_perf_probe_events(void); | 130 | extern int show_perf_probe_events(void); |
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 | 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, |
133 | int max_probe_points, const char *module, | 134 | int max_probe_points, const char *module, |
134 | struct strfilter *filter, bool externs); | 135 | struct strfilter *filter, bool externs); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index dca9145d704c..b5247d777f0e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -281,6 +281,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
282 | Dwarf_Die type; | 282 | Dwarf_Die type; |
283 | char buf[16]; | 283 | char buf[16]; |
284 | char sbuf[STRERR_BUFSIZE]; | ||
284 | int bsize, boffs, total; | 285 | int bsize, boffs, total; |
285 | int ret; | 286 | int ret; |
286 | 287 | ||
@@ -367,7 +368,7 @@ formatted: | |||
367 | if (ret >= 16) | 368 | if (ret >= 16) |
368 | ret = -E2BIG; | 369 | ret = -E2BIG; |
369 | pr_warning("Failed to convert variable type: %s\n", | 370 | pr_warning("Failed to convert variable type: %s\n", |
370 | strerror(-ret)); | 371 | strerror_r(-ret, sbuf, sizeof(sbuf))); |
371 | return ret; | 372 | return ret; |
372 | } | 373 | } |
373 | tvar->type = strdup(buf); | 374 | tvar->type = strdup(buf); |
@@ -608,14 +609,18 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, | |||
608 | return -EINVAL; | 609 | return -EINVAL; |
609 | } | 610 | } |
610 | 611 | ||
611 | /* Get an appropriate symbol from symtab */ | 612 | symbol = dwarf_diename(sp_die); |
612 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); | ||
613 | if (!symbol) { | 613 | if (!symbol) { |
614 | pr_warning("Failed to find symbol at 0x%lx\n", | 614 | /* Try to get the symbol name from symtab */ |
615 | (unsigned long)paddr); | 615 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
616 | return -ENOENT; | 616 | if (!symbol) { |
617 | pr_warning("Failed to find symbol at 0x%lx\n", | ||
618 | (unsigned long)paddr); | ||
619 | return -ENOENT; | ||
620 | } | ||
621 | eaddr = sym.st_value; | ||
617 | } | 622 | } |
618 | tp->offset = (unsigned long)(paddr - sym.st_value); | 623 | tp->offset = (unsigned long)(paddr - eaddr); |
619 | tp->address = (unsigned long)paddr; | 624 | tp->address = (unsigned long)paddr; |
620 | tp->symbol = strdup(symbol); | 625 | tp->symbol = strdup(symbol); |
621 | if (!tp->symbol) | 626 | if (!tp->symbol) |
@@ -779,10 +784,12 @@ static int find_lazy_match_lines(struct intlist *list, | |||
779 | size_t line_len; | 784 | size_t line_len; |
780 | ssize_t len; | 785 | ssize_t len; |
781 | int count = 0, linenum = 1; | 786 | int count = 0, linenum = 1; |
787 | char sbuf[STRERR_BUFSIZE]; | ||
782 | 788 | ||
783 | fp = fopen(fname, "r"); | 789 | fp = fopen(fname, "r"); |
784 | if (!fp) { | 790 | if (!fp) { |
785 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | 791 | pr_warning("Failed to open %s: %s\n", fname, |
792 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
786 | return -errno; | 793 | return -errno; |
787 | } | 794 | } |
788 | 795 | ||
@@ -982,8 +989,24 @@ static int debuginfo__find_probes(struct debuginfo *dbg, | |||
982 | int ret = 0; | 989 | int ret = 0; |
983 | 990 | ||
984 | #if _ELFUTILS_PREREQ(0, 142) | 991 | #if _ELFUTILS_PREREQ(0, 142) |
992 | Elf *elf; | ||
993 | GElf_Ehdr ehdr; | ||
994 | GElf_Shdr shdr; | ||
995 | |||
985 | /* Get the call frame information from this dwarf */ | 996 | /* Get the call frame information from this dwarf */ |
986 | pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg)); | 997 | elf = dwarf_getelf(dbg->dbg); |
998 | if (elf == NULL) | ||
999 | return -EINVAL; | ||
1000 | |||
1001 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
1002 | return -EINVAL; | ||
1003 | |||
1004 | if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && | ||
1005 | shdr.sh_type == SHT_PROGBITS) { | ||
1006 | pf->cfi = dwarf_getcfi_elf(elf); | ||
1007 | } else { | ||
1008 | pf->cfi = dwarf_getcfi(dbg->dbg); | ||
1009 | } | ||
987 | #endif | 1010 | #endif |
988 | 1011 | ||
989 | off = 0; | 1012 | off = 0; |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 12aa9b0d0ba1..3dda85ca50c1 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -736,7 +736,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
737 | return NULL; | 737 | return NULL; |
738 | 738 | ||
739 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | 739 | n = perf_evlist__poll(evlist, timeout); |
740 | if (n < 0) { | 740 | if (n < 0) { |
741 | PyErr_SetFromErrno(PyExc_OSError); | 741 | PyErr_SetFromErrno(PyExc_OSError); |
742 | return NULL; | 742 | return NULL; |
@@ -753,9 +753,9 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | |||
753 | PyObject *list = PyList_New(0); | 753 | PyObject *list = PyList_New(0); |
754 | int i; | 754 | int i; |
755 | 755 | ||
756 | for (i = 0; i < evlist->nr_fds; ++i) { | 756 | for (i = 0; i < evlist->pollfd.nr; ++i) { |
757 | PyObject *file; | 757 | PyObject *file; |
758 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | 758 | FILE *fp = fdopen(evlist->pollfd.entries[i].fd, "r"); |
759 | 759 | ||
760 | if (fp == NULL) | 760 | if (fp == NULL) |
761 | goto free_list; | 761 | goto free_list; |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index fe8079edbdc1..8acd0df88b5c 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c | |||
@@ -14,6 +14,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
14 | struct perf_evsel *evsel; | 14 | struct perf_evsel *evsel; |
15 | unsigned long flags = perf_event_open_cloexec_flag(); | 15 | unsigned long flags = perf_event_open_cloexec_flag(); |
16 | int err = -EAGAIN, fd; | 16 | int err = -EAGAIN, fd; |
17 | static pid_t pid = -1; | ||
17 | 18 | ||
18 | evlist = perf_evlist__new(); | 19 | evlist = perf_evlist__new(); |
19 | if (!evlist) | 20 | if (!evlist) |
@@ -24,14 +25,22 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
24 | 25 | ||
25 | evsel = perf_evlist__first(evlist); | 26 | evsel = perf_evlist__first(evlist); |
26 | 27 | ||
27 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 28 | while (1) { |
28 | if (fd < 0) | 29 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
29 | goto out_delete; | 30 | if (fd < 0) { |
31 | if (pid == -1 && errno == EACCES) { | ||
32 | pid = 0; | ||
33 | continue; | ||
34 | } | ||
35 | goto out_delete; | ||
36 | } | ||
37 | break; | ||
38 | } | ||
30 | close(fd); | 39 | close(fd); |
31 | 40 | ||
32 | fn(evsel); | 41 | fn(evsel); |
33 | 42 | ||
34 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 43 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
35 | if (fd < 0) { | 44 | if (fd < 0) { |
36 | if (errno == EINVAL) | 45 | if (errno == EINVAL) |
37 | err = -EINVAL; | 46 | err = -EINVAL; |
@@ -47,7 +56,7 @@ out_delete: | |||
47 | 56 | ||
48 | static bool perf_probe_api(setup_probe_fn_t fn) | 57 | static bool perf_probe_api(setup_probe_fn_t fn) |
49 | { | 58 | { |
50 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; | 59 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL}; |
51 | struct cpu_map *cpus; | 60 | struct cpu_map *cpus; |
52 | int cpu, ret, i = 0; | 61 | int cpu, ret, i = 0; |
53 | 62 | ||
@@ -106,7 +115,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) | |||
106 | 115 | ||
107 | evlist__for_each(evlist, evsel) { | 116 | evlist__for_each(evlist, evsel) { |
108 | perf_evsel__config(evsel, opts); | 117 | perf_evsel__config(evsel, opts); |
109 | if (!evsel->idx && use_comm_exec) | 118 | if (evsel->tracking && use_comm_exec) |
110 | evsel->attr.comm_exec = 1; | 119 | evsel->attr.comm_exec = 1; |
111 | } | 120 | } |
112 | 121 | ||
@@ -128,16 +137,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) | |||
128 | 137 | ||
129 | static int get_max_rate(unsigned int *rate) | 138 | static int get_max_rate(unsigned int *rate) |
130 | { | 139 | { |
131 | char path[PATH_MAX]; | 140 | return sysctl__read_int("kernel/perf_event_max_sample_rate", (int *)rate); |
132 | const char *procfs = procfs__mountpoint(); | ||
133 | |||
134 | if (!procfs) | ||
135 | return -1; | ||
136 | |||
137 | snprintf(path, PATH_MAX, | ||
138 | "%s/sys/kernel/perf_event_max_sample_rate", procfs); | ||
139 | |||
140 | return filename__read_int(path, (int *) rate); | ||
141 | } | 141 | } |
142 | 142 | ||
143 | static int record_opts__config_freq(struct record_opts *opts) | 143 | static int record_opts__config_freq(struct record_opts *opts) |
@@ -201,6 +201,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
201 | struct perf_evsel *evsel; | 201 | struct perf_evsel *evsel; |
202 | int err, fd, cpu; | 202 | int err, fd, cpu; |
203 | bool ret = false; | 203 | bool ret = false; |
204 | pid_t pid = -1; | ||
204 | 205 | ||
205 | temp_evlist = perf_evlist__new(); | 206 | temp_evlist = perf_evlist__new(); |
206 | if (!temp_evlist) | 207 | if (!temp_evlist) |
@@ -221,12 +222,20 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
221 | cpu = evlist->cpus->map[0]; | 222 | cpu = evlist->cpus->map[0]; |
222 | } | 223 | } |
223 | 224 | ||
224 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, | 225 | while (1) { |
225 | perf_event_open_cloexec_flag()); | 226 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, |
226 | if (fd >= 0) { | 227 | perf_event_open_cloexec_flag()); |
227 | close(fd); | 228 | if (fd < 0) { |
228 | ret = true; | 229 | if (pid == -1 && errno == EACCES) { |
230 | pid = 0; | ||
231 | continue; | ||
232 | } | ||
233 | goto out_delete; | ||
234 | } | ||
235 | break; | ||
229 | } | 236 | } |
237 | close(fd); | ||
238 | ret = true; | ||
230 | 239 | ||
231 | out_delete: | 240 | out_delete: |
232 | perf_evlist__delete(temp_evlist); | 241 | perf_evlist__delete(temp_evlist); |
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index da8e9b285f51..34622b53e733 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "cache.h" | 1 | #include "cache.h" |
2 | #include "run-command.h" | 2 | #include "run-command.h" |
3 | #include "exec_cmd.h" | 3 | #include "exec_cmd.h" |
4 | #include "debug.h" | ||
4 | 5 | ||
5 | static inline void close_pair(int fd[2]) | 6 | static inline void close_pair(int fd[2]) |
6 | { | 7 | { |
@@ -19,6 +20,7 @@ int start_command(struct child_process *cmd) | |||
19 | { | 20 | { |
20 | int need_in, need_out, need_err; | 21 | int need_in, need_out, need_err; |
21 | int fdin[2], fdout[2], fderr[2]; | 22 | int fdin[2], fdout[2], fderr[2]; |
23 | char sbuf[STRERR_BUFSIZE]; | ||
22 | 24 | ||
23 | /* | 25 | /* |
24 | * In case of errors we must keep the promise to close FDs | 26 | * In case of errors we must keep the promise to close FDs |
@@ -99,7 +101,7 @@ int start_command(struct child_process *cmd) | |||
99 | 101 | ||
100 | if (cmd->dir && chdir(cmd->dir)) | 102 | if (cmd->dir && chdir(cmd->dir)) |
101 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | 103 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], |
102 | cmd->dir, strerror(errno)); | 104 | cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); |
103 | if (cmd->env) { | 105 | if (cmd->env) { |
104 | for (; *cmd->env; cmd->env++) { | 106 | for (; *cmd->env; cmd->env++) { |
105 | if (strchr(*cmd->env, '=')) | 107 | if (strchr(*cmd->env, '=')) |
@@ -153,6 +155,8 @@ int start_command(struct child_process *cmd) | |||
153 | 155 | ||
154 | static int wait_or_whine(pid_t pid) | 156 | static int wait_or_whine(pid_t pid) |
155 | { | 157 | { |
158 | char sbuf[STRERR_BUFSIZE]; | ||
159 | |||
156 | for (;;) { | 160 | for (;;) { |
157 | int status, code; | 161 | int status, code; |
158 | pid_t waiting = waitpid(pid, &status, 0); | 162 | pid_t waiting = waitpid(pid, &status, 0); |
@@ -160,7 +164,8 @@ static int wait_or_whine(pid_t pid) | |||
160 | if (waiting < 0) { | 164 | if (waiting < 0) { |
161 | if (errno == EINTR) | 165 | if (errno == EINTR) |
162 | continue; | 166 | continue; |
163 | error("waitpid failed (%s)", strerror(errno)); | 167 | error("waitpid failed (%s)", |
168 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
164 | return -ERR_RUN_COMMAND_WAITPID; | 169 | return -ERR_RUN_COMMAND_WAITPID; |
165 | } | 170 | } |
166 | if (waiting != pid) | 171 | if (waiting != pid) |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b2dba9c0a3a1..22ebc46226e7 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | #include <linux/bitmap.h> | ||
27 | 28 | ||
28 | #include "../util.h" | 29 | #include "../util.h" |
29 | #include <EXTERN.h> | 30 | #include <EXTERN.h> |
@@ -57,7 +58,7 @@ INTERP my_perl; | |||
57 | #define FTRACE_MAX_EVENT \ | 58 | #define FTRACE_MAX_EVENT \ |
58 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 59 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
59 | 60 | ||
60 | struct event_format *events[FTRACE_MAX_EVENT]; | 61 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
61 | 62 | ||
62 | extern struct scripting_context *scripting_context; | 63 | extern struct scripting_context *scripting_context; |
63 | 64 | ||
@@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event, | |||
238 | define_event_symbols(event, ev_name, args->next); | 239 | define_event_symbols(event, ev_name, args->next); |
239 | } | 240 | } |
240 | 241 | ||
241 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
242 | { | ||
243 | static char ev_name[256]; | ||
244 | struct event_format *event; | ||
245 | int type = evsel->attr.config; | ||
246 | |||
247 | if (events[type]) | ||
248 | return events[type]; | ||
249 | |||
250 | events[type] = event = evsel->tp_format; | ||
251 | if (!event) | ||
252 | return NULL; | ||
253 | |||
254 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
255 | |||
256 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
257 | |||
258 | return event; | ||
259 | } | ||
260 | |||
261 | static void perl_process_tracepoint(struct perf_sample *sample, | 242 | static void perl_process_tracepoint(struct perf_sample *sample, |
262 | struct perf_evsel *evsel, | 243 | struct perf_evsel *evsel, |
263 | struct thread *thread) | 244 | struct thread *thread) |
264 | { | 245 | { |
246 | struct event_format *event = evsel->tp_format; | ||
265 | struct format_field *field; | 247 | struct format_field *field; |
266 | static char handler[256]; | 248 | static char handler[256]; |
267 | unsigned long long val; | 249 | unsigned long long val; |
268 | unsigned long s, ns; | 250 | unsigned long s, ns; |
269 | struct event_format *event; | ||
270 | int pid; | 251 | int pid; |
271 | int cpu = sample->cpu; | 252 | int cpu = sample->cpu; |
272 | void *data = sample->raw_data; | 253 | void *data = sample->raw_data; |
@@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
278 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 259 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
279 | return; | 260 | return; |
280 | 261 | ||
281 | event = find_cache_event(evsel); | ||
282 | if (!event) | 262 | if (!event) |
283 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); | 263 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
284 | 264 | ||
@@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
286 | 266 | ||
287 | sprintf(handler, "%s::%s", event->system, event->name); | 267 | sprintf(handler, "%s::%s", event->system, event->name); |
288 | 268 | ||
269 | if (!test_and_set_bit(event->id, events_defined)) | ||
270 | define_event_symbols(event, handler, event->print_fmt.args); | ||
271 | |||
289 | s = nsecs / NSECS_PER_SEC; | 272 | s = nsecs / NSECS_PER_SEC; |
290 | ns = nsecs - s * NSECS_PER_SEC; | 273 | ns = nsecs - s * NSECS_PER_SEC; |
291 | 274 | ||
@@ -432,6 +415,11 @@ error: | |||
432 | return err; | 415 | return err; |
433 | } | 416 | } |
434 | 417 | ||
418 | static int perl_flush_script(void) | ||
419 | { | ||
420 | return 0; | ||
421 | } | ||
422 | |||
435 | /* | 423 | /* |
436 | * Stop trace script | 424 | * Stop trace script |
437 | */ | 425 | */ |
@@ -633,6 +621,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
633 | struct scripting_ops perl_scripting_ops = { | 621 | struct scripting_ops perl_scripting_ops = { |
634 | .name = "Perl", | 622 | .name = "Perl", |
635 | .start_script = perl_start_script, | 623 | .start_script = perl_start_script, |
624 | .flush_script = perl_flush_script, | ||
636 | .stop_script = perl_stop_script, | 625 | .stop_script = perl_stop_script, |
637 | .process_event = perl_process_event, | 626 | .process_event = perl_process_event, |
638 | .generate_script = perl_generate_script, | 627 | .generate_script = perl_generate_script, |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cbce2545da45..d808a328f4dc 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -24,14 +24,21 @@ | |||
24 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include <stdbool.h> | ||
27 | #include <errno.h> | 28 | #include <errno.h> |
29 | #include <linux/bitmap.h> | ||
28 | 30 | ||
29 | #include "../../perf.h" | 31 | #include "../../perf.h" |
30 | #include "../debug.h" | 32 | #include "../debug.h" |
33 | #include "../callchain.h" | ||
31 | #include "../evsel.h" | 34 | #include "../evsel.h" |
32 | #include "../util.h" | 35 | #include "../util.h" |
33 | #include "../event.h" | 36 | #include "../event.h" |
34 | #include "../thread.h" | 37 | #include "../thread.h" |
38 | #include "../comm.h" | ||
39 | #include "../machine.h" | ||
40 | #include "../db-export.h" | ||
41 | #include "../thread-stack.h" | ||
35 | #include "../trace-event.h" | 42 | #include "../trace-event.h" |
36 | #include "../machine.h" | 43 | #include "../machine.h" |
37 | 44 | ||
@@ -40,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void); | |||
40 | #define FTRACE_MAX_EVENT \ | 47 | #define FTRACE_MAX_EVENT \ |
41 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 48 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
42 | 49 | ||
43 | struct event_format *events[FTRACE_MAX_EVENT]; | 50 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
44 | 51 | ||
45 | #define MAX_FIELDS 64 | 52 | #define MAX_FIELDS 64 |
46 | #define N_COMMON_FIELDS 7 | 53 | #define N_COMMON_FIELDS 7 |
@@ -52,6 +59,24 @@ static int zero_flag_atom; | |||
52 | 59 | ||
53 | static PyObject *main_module, *main_dict; | 60 | static PyObject *main_module, *main_dict; |
54 | 61 | ||
62 | struct tables { | ||
63 | struct db_export dbe; | ||
64 | PyObject *evsel_handler; | ||
65 | PyObject *machine_handler; | ||
66 | PyObject *thread_handler; | ||
67 | PyObject *comm_handler; | ||
68 | PyObject *comm_thread_handler; | ||
69 | PyObject *dso_handler; | ||
70 | PyObject *symbol_handler; | ||
71 | PyObject *branch_type_handler; | ||
72 | PyObject *sample_handler; | ||
73 | PyObject *call_path_handler; | ||
74 | PyObject *call_return_handler; | ||
75 | bool db_export_mode; | ||
76 | }; | ||
77 | |||
78 | static struct tables tables_global; | ||
79 | |||
55 | static void handler_call_die(const char *handler_name) NORETURN; | 80 | static void handler_call_die(const char *handler_name) NORETURN; |
56 | static void handler_call_die(const char *handler_name) | 81 | static void handler_call_die(const char *handler_name) |
57 | { | 82 | { |
@@ -73,6 +98,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj | |||
73 | Py_DECREF(val); | 98 | Py_DECREF(val); |
74 | } | 99 | } |
75 | 100 | ||
101 | static PyObject *get_handler(const char *handler_name) | ||
102 | { | ||
103 | PyObject *handler; | ||
104 | |||
105 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
106 | if (handler && !PyCallable_Check(handler)) | ||
107 | return NULL; | ||
108 | return handler; | ||
109 | } | ||
110 | |||
111 | static void call_object(PyObject *handler, PyObject *args, const char *die_msg) | ||
112 | { | ||
113 | PyObject *retval; | ||
114 | |||
115 | retval = PyObject_CallObject(handler, args); | ||
116 | if (retval == NULL) | ||
117 | handler_call_die(die_msg); | ||
118 | Py_DECREF(retval); | ||
119 | } | ||
120 | |||
121 | static void try_call_object(const char *handler_name, PyObject *args) | ||
122 | { | ||
123 | PyObject *handler; | ||
124 | |||
125 | handler = get_handler(handler_name); | ||
126 | if (handler) | ||
127 | call_object(handler, args, handler_name); | ||
128 | } | ||
129 | |||
76 | static void define_value(enum print_arg_type field_type, | 130 | static void define_value(enum print_arg_type field_type, |
77 | const char *ev_name, | 131 | const char *ev_name, |
78 | const char *field_name, | 132 | const char *field_name, |
@@ -80,7 +134,7 @@ static void define_value(enum print_arg_type field_type, | |||
80 | const char *field_str) | 134 | const char *field_str) |
81 | { | 135 | { |
82 | const char *handler_name = "define_flag_value"; | 136 | const char *handler_name = "define_flag_value"; |
83 | PyObject *handler, *t, *retval; | 137 | PyObject *t; |
84 | unsigned long long value; | 138 | unsigned long long value; |
85 | unsigned n = 0; | 139 | unsigned n = 0; |
86 | 140 | ||
@@ -98,13 +152,7 @@ static void define_value(enum print_arg_type field_type, | |||
98 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | 152 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); |
99 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | 153 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); |
100 | 154 | ||
101 | handler = PyDict_GetItemString(main_dict, handler_name); | 155 | try_call_object(handler_name, t); |
102 | if (handler && PyCallable_Check(handler)) { | ||
103 | retval = PyObject_CallObject(handler, t); | ||
104 | if (retval == NULL) | ||
105 | handler_call_die(handler_name); | ||
106 | Py_DECREF(retval); | ||
107 | } | ||
108 | 156 | ||
109 | Py_DECREF(t); | 157 | Py_DECREF(t); |
110 | } | 158 | } |
@@ -127,7 +175,7 @@ static void define_field(enum print_arg_type field_type, | |||
127 | const char *delim) | 175 | const char *delim) |
128 | { | 176 | { |
129 | const char *handler_name = "define_flag_field"; | 177 | const char *handler_name = "define_flag_field"; |
130 | PyObject *handler, *t, *retval; | 178 | PyObject *t; |
131 | unsigned n = 0; | 179 | unsigned n = 0; |
132 | 180 | ||
133 | if (field_type == PRINT_SYMBOL) | 181 | if (field_type == PRINT_SYMBOL) |
@@ -145,13 +193,7 @@ static void define_field(enum print_arg_type field_type, | |||
145 | if (field_type == PRINT_FLAGS) | 193 | if (field_type == PRINT_FLAGS) |
146 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | 194 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); |
147 | 195 | ||
148 | handler = PyDict_GetItemString(main_dict, handler_name); | 196 | try_call_object(handler_name, t); |
149 | if (handler && PyCallable_Check(handler)) { | ||
150 | retval = PyObject_CallObject(handler, t); | ||
151 | if (retval == NULL) | ||
152 | handler_call_die(handler_name); | ||
153 | Py_DECREF(retval); | ||
154 | } | ||
155 | 197 | ||
156 | Py_DECREF(t); | 198 | Py_DECREF(t); |
157 | } | 199 | } |
@@ -214,31 +256,6 @@ static void define_event_symbols(struct event_format *event, | |||
214 | define_event_symbols(event, ev_name, args->next); | 256 | define_event_symbols(event, ev_name, args->next); |
215 | } | 257 | } |
216 | 258 | ||
217 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
218 | { | ||
219 | static char ev_name[256]; | ||
220 | struct event_format *event; | ||
221 | int type = evsel->attr.config; | ||
222 | |||
223 | /* | ||
224 | * XXX: Do we really need to cache this since now we have evsel->tp_format | ||
225 | * cached already? Need to re-read this "cache" routine that as well calls | ||
226 | * define_event_symbols() :-\ | ||
227 | */ | ||
228 | if (events[type]) | ||
229 | return events[type]; | ||
230 | |||
231 | events[type] = event = evsel->tp_format; | ||
232 | if (!event) | ||
233 | return NULL; | ||
234 | |||
235 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
236 | |||
237 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
238 | |||
239 | return event; | ||
240 | } | ||
241 | |||
242 | static PyObject *get_field_numeric_entry(struct event_format *event, | 259 | static PyObject *get_field_numeric_entry(struct event_format *event, |
243 | struct format_field *field, void *data) | 260 | struct format_field *field, void *data) |
244 | { | 261 | { |
@@ -294,9 +311,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample, | |||
294 | if (!symbol_conf.use_callchain || !sample->callchain) | 311 | if (!symbol_conf.use_callchain || !sample->callchain) |
295 | goto exit; | 312 | goto exit; |
296 | 313 | ||
297 | if (machine__resolve_callchain(al->machine, evsel, al->thread, | 314 | if (thread__resolve_callchain(al->thread, evsel, |
298 | sample, NULL, NULL, | 315 | sample, NULL, NULL, |
299 | PERF_MAX_STACK_DEPTH) != 0) { | 316 | PERF_MAX_STACK_DEPTH) != 0) { |
300 | pr_err("Failed to resolve callchain. Skipping\n"); | 317 | pr_err("Failed to resolve callchain. Skipping\n"); |
301 | goto exit; | 318 | goto exit; |
302 | } | 319 | } |
@@ -362,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
362 | struct thread *thread, | 379 | struct thread *thread, |
363 | struct addr_location *al) | 380 | struct addr_location *al) |
364 | { | 381 | { |
365 | PyObject *handler, *retval, *context, *t, *obj, *callchain; | 382 | struct event_format *event = evsel->tp_format; |
383 | PyObject *handler, *context, *t, *obj, *callchain; | ||
366 | PyObject *dict = NULL; | 384 | PyObject *dict = NULL; |
367 | static char handler_name[256]; | 385 | static char handler_name[256]; |
368 | struct format_field *field; | 386 | struct format_field *field; |
369 | unsigned long s, ns; | 387 | unsigned long s, ns; |
370 | struct event_format *event; | ||
371 | unsigned n = 0; | 388 | unsigned n = 0; |
372 | int pid; | 389 | int pid; |
373 | int cpu = sample->cpu; | 390 | int cpu = sample->cpu; |
@@ -379,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
379 | if (!t) | 396 | if (!t) |
380 | Py_FatalError("couldn't create Python tuple"); | 397 | Py_FatalError("couldn't create Python tuple"); |
381 | 398 | ||
382 | event = find_cache_event(evsel); | ||
383 | if (!event) | 399 | if (!event) |
384 | die("ug! no event found for type %d", (int)evsel->attr.config); | 400 | die("ug! no event found for type %d", (int)evsel->attr.config); |
385 | 401 | ||
@@ -387,9 +403,10 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
387 | 403 | ||
388 | sprintf(handler_name, "%s__%s", event->system, event->name); | 404 | sprintf(handler_name, "%s__%s", event->system, event->name); |
389 | 405 | ||
390 | handler = PyDict_GetItemString(main_dict, handler_name); | 406 | if (!test_and_set_bit(event->id, events_defined)) |
391 | if (handler && !PyCallable_Check(handler)) | 407 | define_event_symbols(event, handler_name, event->print_fmt.args); |
392 | handler = NULL; | 408 | |
409 | handler = get_handler(handler_name); | ||
393 | if (!handler) { | 410 | if (!handler) { |
394 | dict = PyDict_New(); | 411 | dict = PyDict_New(); |
395 | if (!dict) | 412 | if (!dict) |
@@ -450,31 +467,304 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
450 | Py_FatalError("error resizing Python tuple"); | 467 | Py_FatalError("error resizing Python tuple"); |
451 | 468 | ||
452 | if (handler) { | 469 | if (handler) { |
453 | retval = PyObject_CallObject(handler, t); | 470 | call_object(handler, t, handler_name); |
454 | if (retval == NULL) | ||
455 | handler_call_die(handler_name); | ||
456 | Py_DECREF(retval); | ||
457 | } else { | 471 | } else { |
458 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | 472 | try_call_object("trace_unhandled", t); |
459 | if (handler && PyCallable_Check(handler)) { | ||
460 | |||
461 | retval = PyObject_CallObject(handler, t); | ||
462 | if (retval == NULL) | ||
463 | handler_call_die("trace_unhandled"); | ||
464 | Py_DECREF(retval); | ||
465 | } | ||
466 | Py_DECREF(dict); | 473 | Py_DECREF(dict); |
467 | } | 474 | } |
468 | 475 | ||
469 | Py_DECREF(t); | 476 | Py_DECREF(t); |
470 | } | 477 | } |
471 | 478 | ||
479 | static PyObject *tuple_new(unsigned int sz) | ||
480 | { | ||
481 | PyObject *t; | ||
482 | |||
483 | t = PyTuple_New(sz); | ||
484 | if (!t) | ||
485 | Py_FatalError("couldn't create Python tuple"); | ||
486 | return t; | ||
487 | } | ||
488 | |||
489 | static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val) | ||
490 | { | ||
491 | #if BITS_PER_LONG == 64 | ||
492 | return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); | ||
493 | #endif | ||
494 | #if BITS_PER_LONG == 32 | ||
495 | return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val)); | ||
496 | #endif | ||
497 | } | ||
498 | |||
499 | static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val) | ||
500 | { | ||
501 | return PyTuple_SetItem(t, pos, PyInt_FromLong(val)); | ||
502 | } | ||
503 | |||
504 | static int tuple_set_string(PyObject *t, unsigned int pos, const char *s) | ||
505 | { | ||
506 | return PyTuple_SetItem(t, pos, PyString_FromString(s)); | ||
507 | } | ||
508 | |||
509 | static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel) | ||
510 | { | ||
511 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
512 | PyObject *t; | ||
513 | |||
514 | t = tuple_new(2); | ||
515 | |||
516 | tuple_set_u64(t, 0, evsel->db_id); | ||
517 | tuple_set_string(t, 1, perf_evsel__name(evsel)); | ||
518 | |||
519 | call_object(tables->evsel_handler, t, "evsel_table"); | ||
520 | |||
521 | Py_DECREF(t); | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int python_export_machine(struct db_export *dbe, | ||
527 | struct machine *machine) | ||
528 | { | ||
529 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
530 | PyObject *t; | ||
531 | |||
532 | t = tuple_new(3); | ||
533 | |||
534 | tuple_set_u64(t, 0, machine->db_id); | ||
535 | tuple_set_s32(t, 1, machine->pid); | ||
536 | tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : ""); | ||
537 | |||
538 | call_object(tables->machine_handler, t, "machine_table"); | ||
539 | |||
540 | Py_DECREF(t); | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static int python_export_thread(struct db_export *dbe, struct thread *thread, | ||
546 | u64 main_thread_db_id, struct machine *machine) | ||
547 | { | ||
548 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
549 | PyObject *t; | ||
550 | |||
551 | t = tuple_new(5); | ||
552 | |||
553 | tuple_set_u64(t, 0, thread->db_id); | ||
554 | tuple_set_u64(t, 1, machine->db_id); | ||
555 | tuple_set_u64(t, 2, main_thread_db_id); | ||
556 | tuple_set_s32(t, 3, thread->pid_); | ||
557 | tuple_set_s32(t, 4, thread->tid); | ||
558 | |||
559 | call_object(tables->thread_handler, t, "thread_table"); | ||
560 | |||
561 | Py_DECREF(t); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static int python_export_comm(struct db_export *dbe, struct comm *comm) | ||
567 | { | ||
568 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
569 | PyObject *t; | ||
570 | |||
571 | t = tuple_new(2); | ||
572 | |||
573 | tuple_set_u64(t, 0, comm->db_id); | ||
574 | tuple_set_string(t, 1, comm__str(comm)); | ||
575 | |||
576 | call_object(tables->comm_handler, t, "comm_table"); | ||
577 | |||
578 | Py_DECREF(t); | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int python_export_comm_thread(struct db_export *dbe, u64 db_id, | ||
584 | struct comm *comm, struct thread *thread) | ||
585 | { | ||
586 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
587 | PyObject *t; | ||
588 | |||
589 | t = tuple_new(3); | ||
590 | |||
591 | tuple_set_u64(t, 0, db_id); | ||
592 | tuple_set_u64(t, 1, comm->db_id); | ||
593 | tuple_set_u64(t, 2, thread->db_id); | ||
594 | |||
595 | call_object(tables->comm_thread_handler, t, "comm_thread_table"); | ||
596 | |||
597 | Py_DECREF(t); | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | static int python_export_dso(struct db_export *dbe, struct dso *dso, | ||
603 | struct machine *machine) | ||
604 | { | ||
605 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
606 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
607 | PyObject *t; | ||
608 | |||
609 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
610 | |||
611 | t = tuple_new(5); | ||
612 | |||
613 | tuple_set_u64(t, 0, dso->db_id); | ||
614 | tuple_set_u64(t, 1, machine->db_id); | ||
615 | tuple_set_string(t, 2, dso->short_name); | ||
616 | tuple_set_string(t, 3, dso->long_name); | ||
617 | tuple_set_string(t, 4, sbuild_id); | ||
618 | |||
619 | call_object(tables->dso_handler, t, "dso_table"); | ||
620 | |||
621 | Py_DECREF(t); | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int python_export_symbol(struct db_export *dbe, struct symbol *sym, | ||
627 | struct dso *dso) | ||
628 | { | ||
629 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
630 | u64 *sym_db_id = symbol__priv(sym); | ||
631 | PyObject *t; | ||
632 | |||
633 | t = tuple_new(6); | ||
634 | |||
635 | tuple_set_u64(t, 0, *sym_db_id); | ||
636 | tuple_set_u64(t, 1, dso->db_id); | ||
637 | tuple_set_u64(t, 2, sym->start); | ||
638 | tuple_set_u64(t, 3, sym->end); | ||
639 | tuple_set_s32(t, 4, sym->binding); | ||
640 | tuple_set_string(t, 5, sym->name); | ||
641 | |||
642 | call_object(tables->symbol_handler, t, "symbol_table"); | ||
643 | |||
644 | Py_DECREF(t); | ||
645 | |||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | static int python_export_branch_type(struct db_export *dbe, u32 branch_type, | ||
650 | const char *name) | ||
651 | { | ||
652 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
653 | PyObject *t; | ||
654 | |||
655 | t = tuple_new(2); | ||
656 | |||
657 | tuple_set_s32(t, 0, branch_type); | ||
658 | tuple_set_string(t, 1, name); | ||
659 | |||
660 | call_object(tables->branch_type_handler, t, "branch_type_table"); | ||
661 | |||
662 | Py_DECREF(t); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int python_export_sample(struct db_export *dbe, | ||
668 | struct export_sample *es) | ||
669 | { | ||
670 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
671 | PyObject *t; | ||
672 | |||
673 | t = tuple_new(21); | ||
674 | |||
675 | tuple_set_u64(t, 0, es->db_id); | ||
676 | tuple_set_u64(t, 1, es->evsel->db_id); | ||
677 | tuple_set_u64(t, 2, es->al->machine->db_id); | ||
678 | tuple_set_u64(t, 3, es->thread->db_id); | ||
679 | tuple_set_u64(t, 4, es->comm_db_id); | ||
680 | tuple_set_u64(t, 5, es->dso_db_id); | ||
681 | tuple_set_u64(t, 6, es->sym_db_id); | ||
682 | tuple_set_u64(t, 7, es->offset); | ||
683 | tuple_set_u64(t, 8, es->sample->ip); | ||
684 | tuple_set_u64(t, 9, es->sample->time); | ||
685 | tuple_set_s32(t, 10, es->sample->cpu); | ||
686 | tuple_set_u64(t, 11, es->addr_dso_db_id); | ||
687 | tuple_set_u64(t, 12, es->addr_sym_db_id); | ||
688 | tuple_set_u64(t, 13, es->addr_offset); | ||
689 | tuple_set_u64(t, 14, es->sample->addr); | ||
690 | tuple_set_u64(t, 15, es->sample->period); | ||
691 | tuple_set_u64(t, 16, es->sample->weight); | ||
692 | tuple_set_u64(t, 17, es->sample->transaction); | ||
693 | tuple_set_u64(t, 18, es->sample->data_src); | ||
694 | tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK); | ||
695 | tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); | ||
696 | |||
697 | call_object(tables->sample_handler, t, "sample_table"); | ||
698 | |||
699 | Py_DECREF(t); | ||
700 | |||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static int python_export_call_path(struct db_export *dbe, struct call_path *cp) | ||
705 | { | ||
706 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
707 | PyObject *t; | ||
708 | u64 parent_db_id, sym_db_id; | ||
709 | |||
710 | parent_db_id = cp->parent ? cp->parent->db_id : 0; | ||
711 | sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; | ||
712 | |||
713 | t = tuple_new(4); | ||
714 | |||
715 | tuple_set_u64(t, 0, cp->db_id); | ||
716 | tuple_set_u64(t, 1, parent_db_id); | ||
717 | tuple_set_u64(t, 2, sym_db_id); | ||
718 | tuple_set_u64(t, 3, cp->ip); | ||
719 | |||
720 | call_object(tables->call_path_handler, t, "call_path_table"); | ||
721 | |||
722 | Py_DECREF(t); | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static int python_export_call_return(struct db_export *dbe, | ||
728 | struct call_return *cr) | ||
729 | { | ||
730 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
731 | u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; | ||
732 | PyObject *t; | ||
733 | |||
734 | t = tuple_new(11); | ||
735 | |||
736 | tuple_set_u64(t, 0, cr->db_id); | ||
737 | tuple_set_u64(t, 1, cr->thread->db_id); | ||
738 | tuple_set_u64(t, 2, comm_db_id); | ||
739 | tuple_set_u64(t, 3, cr->cp->db_id); | ||
740 | tuple_set_u64(t, 4, cr->call_time); | ||
741 | tuple_set_u64(t, 5, cr->return_time); | ||
742 | tuple_set_u64(t, 6, cr->branch_count); | ||
743 | tuple_set_u64(t, 7, cr->call_ref); | ||
744 | tuple_set_u64(t, 8, cr->return_ref); | ||
745 | tuple_set_u64(t, 9, cr->cp->parent->db_id); | ||
746 | tuple_set_s32(t, 10, cr->flags); | ||
747 | |||
748 | call_object(tables->call_return_handler, t, "call_return_table"); | ||
749 | |||
750 | Py_DECREF(t); | ||
751 | |||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int python_process_call_return(struct call_return *cr, void *data) | ||
756 | { | ||
757 | struct db_export *dbe = data; | ||
758 | |||
759 | return db_export__call_return(dbe, cr); | ||
760 | } | ||
761 | |||
472 | static void python_process_general_event(struct perf_sample *sample, | 762 | static void python_process_general_event(struct perf_sample *sample, |
473 | struct perf_evsel *evsel, | 763 | struct perf_evsel *evsel, |
474 | struct thread *thread, | 764 | struct thread *thread, |
475 | struct addr_location *al) | 765 | struct addr_location *al) |
476 | { | 766 | { |
477 | PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; | 767 | PyObject *handler, *t, *dict, *callchain, *dict_sample; |
478 | static char handler_name[64]; | 768 | static char handler_name[64]; |
479 | unsigned n = 0; | 769 | unsigned n = 0; |
480 | 770 | ||
@@ -496,8 +786,8 @@ static void python_process_general_event(struct perf_sample *sample, | |||
496 | 786 | ||
497 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); | 787 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); |
498 | 788 | ||
499 | handler = PyDict_GetItemString(main_dict, handler_name); | 789 | handler = get_handler(handler_name); |
500 | if (!handler || !PyCallable_Check(handler)) | 790 | if (!handler) |
501 | goto exit; | 791 | goto exit; |
502 | 792 | ||
503 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 793 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
@@ -539,63 +829,121 @@ static void python_process_general_event(struct perf_sample *sample, | |||
539 | if (_PyTuple_Resize(&t, n) == -1) | 829 | if (_PyTuple_Resize(&t, n) == -1) |
540 | Py_FatalError("error resizing Python tuple"); | 830 | Py_FatalError("error resizing Python tuple"); |
541 | 831 | ||
542 | retval = PyObject_CallObject(handler, t); | 832 | call_object(handler, t, handler_name); |
543 | if (retval == NULL) | ||
544 | handler_call_die(handler_name); | ||
545 | Py_DECREF(retval); | ||
546 | exit: | 833 | exit: |
547 | Py_DECREF(dict); | 834 | Py_DECREF(dict); |
548 | Py_DECREF(t); | 835 | Py_DECREF(t); |
549 | } | 836 | } |
550 | 837 | ||
551 | static void python_process_event(union perf_event *event __maybe_unused, | 838 | static void python_process_event(union perf_event *event, |
552 | struct perf_sample *sample, | 839 | struct perf_sample *sample, |
553 | struct perf_evsel *evsel, | 840 | struct perf_evsel *evsel, |
554 | struct thread *thread, | 841 | struct thread *thread, |
555 | struct addr_location *al) | 842 | struct addr_location *al) |
556 | { | 843 | { |
844 | struct tables *tables = &tables_global; | ||
845 | |||
557 | switch (evsel->attr.type) { | 846 | switch (evsel->attr.type) { |
558 | case PERF_TYPE_TRACEPOINT: | 847 | case PERF_TYPE_TRACEPOINT: |
559 | python_process_tracepoint(sample, evsel, thread, al); | 848 | python_process_tracepoint(sample, evsel, thread, al); |
560 | break; | 849 | break; |
561 | /* Reserve for future process_hw/sw/raw APIs */ | 850 | /* Reserve for future process_hw/sw/raw APIs */ |
562 | default: | 851 | default: |
563 | python_process_general_event(sample, evsel, thread, al); | 852 | if (tables->db_export_mode) |
853 | db_export__sample(&tables->dbe, event, sample, evsel, | ||
854 | thread, al); | ||
855 | else | ||
856 | python_process_general_event(sample, evsel, thread, al); | ||
564 | } | 857 | } |
565 | } | 858 | } |
566 | 859 | ||
567 | static int run_start_sub(void) | 860 | static int run_start_sub(void) |
568 | { | 861 | { |
569 | PyObject *handler, *retval; | ||
570 | int err = 0; | ||
571 | |||
572 | main_module = PyImport_AddModule("__main__"); | 862 | main_module = PyImport_AddModule("__main__"); |
573 | if (main_module == NULL) | 863 | if (main_module == NULL) |
574 | return -1; | 864 | return -1; |
575 | Py_INCREF(main_module); | 865 | Py_INCREF(main_module); |
576 | 866 | ||
577 | main_dict = PyModule_GetDict(main_module); | 867 | main_dict = PyModule_GetDict(main_module); |
578 | if (main_dict == NULL) { | 868 | if (main_dict == NULL) |
579 | err = -1; | ||
580 | goto error; | 869 | goto error; |
581 | } | ||
582 | Py_INCREF(main_dict); | 870 | Py_INCREF(main_dict); |
583 | 871 | ||
584 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | 872 | try_call_object("trace_begin", NULL); |
585 | if (handler == NULL || !PyCallable_Check(handler)) | ||
586 | goto out; | ||
587 | 873 | ||
588 | retval = PyObject_CallObject(handler, NULL); | 874 | return 0; |
589 | if (retval == NULL) | ||
590 | handler_call_die("trace_begin"); | ||
591 | 875 | ||
592 | Py_DECREF(retval); | ||
593 | return err; | ||
594 | error: | 876 | error: |
595 | Py_XDECREF(main_dict); | 877 | Py_XDECREF(main_dict); |
596 | Py_XDECREF(main_module); | 878 | Py_XDECREF(main_module); |
597 | out: | 879 | return -1; |
598 | return err; | 880 | } |
881 | |||
882 | #define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \ | ||
883 | tables->handler_name = get_handler(#table_name); \ | ||
884 | if (tables->handler_name) \ | ||
885 | tables->dbe.export_ ## name = python_export_ ## name; \ | ||
886 | } while (0) | ||
887 | |||
888 | #define SET_TABLE_HANDLER(name) \ | ||
889 | SET_TABLE_HANDLER_(name, name ## _handler, name ## _table) | ||
890 | |||
891 | static void set_table_handlers(struct tables *tables) | ||
892 | { | ||
893 | const char *perf_db_export_mode = "perf_db_export_mode"; | ||
894 | const char *perf_db_export_calls = "perf_db_export_calls"; | ||
895 | PyObject *db_export_mode, *db_export_calls; | ||
896 | bool export_calls = false; | ||
897 | int ret; | ||
898 | |||
899 | memset(tables, 0, sizeof(struct tables)); | ||
900 | if (db_export__init(&tables->dbe)) | ||
901 | Py_FatalError("failed to initialize export"); | ||
902 | |||
903 | db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode); | ||
904 | if (!db_export_mode) | ||
905 | return; | ||
906 | |||
907 | ret = PyObject_IsTrue(db_export_mode); | ||
908 | if (ret == -1) | ||
909 | handler_call_die(perf_db_export_mode); | ||
910 | if (!ret) | ||
911 | return; | ||
912 | |||
913 | tables->dbe.crp = NULL; | ||
914 | db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); | ||
915 | if (db_export_calls) { | ||
916 | ret = PyObject_IsTrue(db_export_calls); | ||
917 | if (ret == -1) | ||
918 | handler_call_die(perf_db_export_calls); | ||
919 | export_calls = !!ret; | ||
920 | } | ||
921 | |||
922 | if (export_calls) { | ||
923 | tables->dbe.crp = | ||
924 | call_return_processor__new(python_process_call_return, | ||
925 | &tables->dbe); | ||
926 | if (!tables->dbe.crp) | ||
927 | Py_FatalError("failed to create calls processor"); | ||
928 | } | ||
929 | |||
930 | tables->db_export_mode = true; | ||
931 | /* | ||
932 | * Reserve per symbol space for symbol->db_id via symbol__priv() | ||
933 | */ | ||
934 | symbol_conf.priv_size = sizeof(u64); | ||
935 | |||
936 | SET_TABLE_HANDLER(evsel); | ||
937 | SET_TABLE_HANDLER(machine); | ||
938 | SET_TABLE_HANDLER(thread); | ||
939 | SET_TABLE_HANDLER(comm); | ||
940 | SET_TABLE_HANDLER(comm_thread); | ||
941 | SET_TABLE_HANDLER(dso); | ||
942 | SET_TABLE_HANDLER(symbol); | ||
943 | SET_TABLE_HANDLER(branch_type); | ||
944 | SET_TABLE_HANDLER(sample); | ||
945 | SET_TABLE_HANDLER(call_path); | ||
946 | SET_TABLE_HANDLER(call_return); | ||
599 | } | 947 | } |
600 | 948 | ||
601 | /* | 949 | /* |
@@ -603,6 +951,7 @@ out: | |||
603 | */ | 951 | */ |
604 | static int python_start_script(const char *script, int argc, const char **argv) | 952 | static int python_start_script(const char *script, int argc, const char **argv) |
605 | { | 953 | { |
954 | struct tables *tables = &tables_global; | ||
606 | const char **command_line; | 955 | const char **command_line; |
607 | char buf[PATH_MAX]; | 956 | char buf[PATH_MAX]; |
608 | int i, err = 0; | 957 | int i, err = 0; |
@@ -641,6 +990,14 @@ static int python_start_script(const char *script, int argc, const char **argv) | |||
641 | 990 | ||
642 | free(command_line); | 991 | free(command_line); |
643 | 992 | ||
993 | set_table_handlers(tables); | ||
994 | |||
995 | if (tables->db_export_mode) { | ||
996 | err = db_export__branch_types(&tables->dbe); | ||
997 | if (err) | ||
998 | goto error; | ||
999 | } | ||
1000 | |||
644 | return err; | 1001 | return err; |
645 | error: | 1002 | error: |
646 | Py_Finalize(); | 1003 | Py_Finalize(); |
@@ -649,28 +1006,29 @@ error: | |||
649 | return err; | 1006 | return err; |
650 | } | 1007 | } |
651 | 1008 | ||
1009 | static int python_flush_script(void) | ||
1010 | { | ||
1011 | struct tables *tables = &tables_global; | ||
1012 | |||
1013 | return db_export__flush(&tables->dbe); | ||
1014 | } | ||
1015 | |||
652 | /* | 1016 | /* |
653 | * Stop trace script | 1017 | * Stop trace script |
654 | */ | 1018 | */ |
655 | static int python_stop_script(void) | 1019 | static int python_stop_script(void) |
656 | { | 1020 | { |
657 | PyObject *handler, *retval; | 1021 | struct tables *tables = &tables_global; |
658 | int err = 0; | ||
659 | 1022 | ||
660 | handler = PyDict_GetItemString(main_dict, "trace_end"); | 1023 | try_call_object("trace_end", NULL); |
661 | if (handler == NULL || !PyCallable_Check(handler)) | 1024 | |
662 | goto out; | 1025 | db_export__exit(&tables->dbe); |
663 | 1026 | ||
664 | retval = PyObject_CallObject(handler, NULL); | ||
665 | if (retval == NULL) | ||
666 | handler_call_die("trace_end"); | ||
667 | Py_DECREF(retval); | ||
668 | out: | ||
669 | Py_XDECREF(main_dict); | 1027 | Py_XDECREF(main_dict); |
670 | Py_XDECREF(main_module); | 1028 | Py_XDECREF(main_module); |
671 | Py_Finalize(); | 1029 | Py_Finalize(); |
672 | 1030 | ||
673 | return err; | 1031 | return 0; |
674 | } | 1032 | } |
675 | 1033 | ||
676 | static int python_generate_script(struct pevent *pevent, const char *outfile) | 1034 | static int python_generate_script(struct pevent *pevent, const char *outfile) |
@@ -843,6 +1201,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) | |||
843 | struct scripting_ops python_scripting_ops = { | 1201 | struct scripting_ops python_scripting_ops = { |
844 | .name = "Python", | 1202 | .name = "Python", |
845 | .start_script = python_start_script, | 1203 | .start_script = python_start_script, |
1204 | .flush_script = python_flush_script, | ||
846 | .stop_script = python_stop_script, | 1205 | .stop_script = python_stop_script, |
847 | .process_event = python_process_event, | 1206 | .process_event = python_process_event, |
848 | .generate_script = python_generate_script, | 1207 | .generate_script = python_generate_script, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88dfef70c13d..5f0e05a76c05 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util.h" | 14 | #include "util.h" |
15 | #include "cpumap.h" | 15 | #include "cpumap.h" |
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "asm/bug.h" | ||
17 | 18 | ||
18 | static int perf_session__open(struct perf_session *session) | 19 | static int perf_session__open(struct perf_session *session) |
19 | { | 20 | { |
@@ -66,6 +67,25 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session) | |||
66 | machines__destroy_kernel_maps(&session->machines); | 67 | machines__destroy_kernel_maps(&session->machines); |
67 | } | 68 | } |
68 | 69 | ||
70 | static bool perf_session__has_comm_exec(struct perf_session *session) | ||
71 | { | ||
72 | struct perf_evsel *evsel; | ||
73 | |||
74 | evlist__for_each(session->evlist, evsel) { | ||
75 | if (evsel->attr.comm_exec) | ||
76 | return true; | ||
77 | } | ||
78 | |||
79 | return false; | ||
80 | } | ||
81 | |||
82 | static void perf_session__set_comm_exec(struct perf_session *session) | ||
83 | { | ||
84 | bool comm_exec = perf_session__has_comm_exec(session); | ||
85 | |||
86 | machines__set_comm_exec(&session->machines, comm_exec); | ||
87 | } | ||
88 | |||
69 | struct perf_session *perf_session__new(struct perf_data_file *file, | 89 | struct perf_session *perf_session__new(struct perf_data_file *file, |
70 | bool repipe, struct perf_tool *tool) | 90 | bool repipe, struct perf_tool *tool) |
71 | { | 91 | { |
@@ -75,9 +95,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
75 | goto out; | 95 | goto out; |
76 | 96 | ||
77 | session->repipe = repipe; | 97 | session->repipe = repipe; |
78 | INIT_LIST_HEAD(&session->ordered_samples.samples); | 98 | ordered_events__init(&session->ordered_events); |
79 | INIT_LIST_HEAD(&session->ordered_samples.sample_cache); | ||
80 | INIT_LIST_HEAD(&session->ordered_samples.to_free); | ||
81 | machines__init(&session->machines); | 99 | machines__init(&session->machines); |
82 | 100 | ||
83 | if (file) { | 101 | if (file) { |
@@ -91,6 +109,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
91 | goto out_close; | 109 | goto out_close; |
92 | 110 | ||
93 | perf_session__set_id_hdr_size(session); | 111 | perf_session__set_id_hdr_size(session); |
112 | perf_session__set_comm_exec(session); | ||
94 | } | 113 | } |
95 | } | 114 | } |
96 | 115 | ||
@@ -100,13 +119,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
100 | * kernel MMAP event, in perf_event__process_mmap(). | 119 | * kernel MMAP event, in perf_event__process_mmap(). |
101 | */ | 120 | */ |
102 | if (perf_session__create_kernel_maps(session) < 0) | 121 | if (perf_session__create_kernel_maps(session) < 0) |
103 | goto out_delete; | 122 | pr_warning("Cannot read kernel map\n"); |
104 | } | 123 | } |
105 | 124 | ||
106 | if (tool && tool->ordering_requires_timestamps && | 125 | if (tool && tool->ordering_requires_timestamps && |
107 | tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { | 126 | tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) { |
108 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 127 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
109 | tool->ordered_samples = false; | 128 | tool->ordered_events = false; |
110 | } | 129 | } |
111 | 130 | ||
112 | return session; | 131 | return session; |
@@ -209,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool, | |||
209 | union perf_event *event, | 228 | union perf_event *event, |
210 | struct perf_session *session); | 229 | struct perf_session *session); |
211 | 230 | ||
231 | static int process_id_index_stub(struct perf_tool *tool __maybe_unused, | ||
232 | union perf_event *event __maybe_unused, | ||
233 | struct perf_session *perf_session | ||
234 | __maybe_unused) | ||
235 | { | ||
236 | dump_printf(": unhandled!\n"); | ||
237 | return 0; | ||
238 | } | ||
239 | |||
212 | void perf_tool__fill_defaults(struct perf_tool *tool) | 240 | void perf_tool__fill_defaults(struct perf_tool *tool) |
213 | { | 241 | { |
214 | if (tool->sample == NULL) | 242 | if (tool->sample == NULL) |
@@ -238,11 +266,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
238 | if (tool->build_id == NULL) | 266 | if (tool->build_id == NULL) |
239 | tool->build_id = process_finished_round_stub; | 267 | tool->build_id = process_finished_round_stub; |
240 | if (tool->finished_round == NULL) { | 268 | if (tool->finished_round == NULL) { |
241 | if (tool->ordered_samples) | 269 | if (tool->ordered_events) |
242 | tool->finished_round = process_finished_round; | 270 | tool->finished_round = process_finished_round; |
243 | else | 271 | else |
244 | tool->finished_round = process_finished_round_stub; | 272 | tool->finished_round = process_finished_round_stub; |
245 | } | 273 | } |
274 | if (tool->id_index == NULL) | ||
275 | tool->id_index = process_id_index_stub; | ||
246 | } | 276 | } |
247 | 277 | ||
248 | static void swap_sample_id_all(union perf_event *event, void *data) | 278 | static void swap_sample_id_all(union perf_event *event, void *data) |
@@ -441,90 +471,10 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
441 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 471 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
442 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 472 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
443 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 473 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
474 | [PERF_RECORD_ID_INDEX] = perf_event__all64_swap, | ||
444 | [PERF_RECORD_HEADER_MAX] = NULL, | 475 | [PERF_RECORD_HEADER_MAX] = NULL, |
445 | }; | 476 | }; |
446 | 477 | ||
447 | struct sample_queue { | ||
448 | u64 timestamp; | ||
449 | u64 file_offset; | ||
450 | union perf_event *event; | ||
451 | struct list_head list; | ||
452 | }; | ||
453 | |||
454 | static void perf_session_free_sample_buffers(struct perf_session *session) | ||
455 | { | ||
456 | struct ordered_samples *os = &session->ordered_samples; | ||
457 | |||
458 | while (!list_empty(&os->to_free)) { | ||
459 | struct sample_queue *sq; | ||
460 | |||
461 | sq = list_entry(os->to_free.next, struct sample_queue, list); | ||
462 | list_del(&sq->list); | ||
463 | free(sq); | ||
464 | } | ||
465 | } | ||
466 | |||
467 | static int perf_session_deliver_event(struct perf_session *session, | ||
468 | union perf_event *event, | ||
469 | struct perf_sample *sample, | ||
470 | struct perf_tool *tool, | ||
471 | u64 file_offset); | ||
472 | |||
473 | static int flush_sample_queue(struct perf_session *s, | ||
474 | struct perf_tool *tool) | ||
475 | { | ||
476 | struct ordered_samples *os = &s->ordered_samples; | ||
477 | struct list_head *head = &os->samples; | ||
478 | struct sample_queue *tmp, *iter; | ||
479 | struct perf_sample sample; | ||
480 | u64 limit = os->next_flush; | ||
481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | ||
482 | bool show_progress = limit == ULLONG_MAX; | ||
483 | struct ui_progress prog; | ||
484 | int ret; | ||
485 | |||
486 | if (!tool->ordered_samples || !limit) | ||
487 | return 0; | ||
488 | |||
489 | if (show_progress) | ||
490 | ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); | ||
491 | |||
492 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
493 | if (session_done()) | ||
494 | return 0; | ||
495 | |||
496 | if (iter->timestamp > limit) | ||
497 | break; | ||
498 | |||
499 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
500 | if (ret) | ||
501 | pr_err("Can't parse sample, err = %d\n", ret); | ||
502 | else { | ||
503 | ret = perf_session_deliver_event(s, iter->event, &sample, tool, | ||
504 | iter->file_offset); | ||
505 | if (ret) | ||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | os->last_flush = iter->timestamp; | ||
510 | list_del(&iter->list); | ||
511 | list_add(&iter->list, &os->sample_cache); | ||
512 | os->nr_samples--; | ||
513 | |||
514 | if (show_progress) | ||
515 | ui_progress__update(&prog, 1); | ||
516 | } | ||
517 | |||
518 | if (list_empty(head)) { | ||
519 | os->last_sample = NULL; | ||
520 | } else if (last_ts <= limit) { | ||
521 | os->last_sample = | ||
522 | list_entry(head->prev, struct sample_queue, list); | ||
523 | } | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* | 478 | /* |
529 | * When perf record finishes a pass on every buffers, it records this pseudo | 479 | * When perf record finishes a pass on every buffers, it records this pseudo |
530 | * event. | 480 | * event. |
@@ -568,99 +518,38 @@ static int process_finished_round(struct perf_tool *tool, | |||
568 | union perf_event *event __maybe_unused, | 518 | union perf_event *event __maybe_unused, |
569 | struct perf_session *session) | 519 | struct perf_session *session) |
570 | { | 520 | { |
571 | int ret = flush_sample_queue(session, tool); | 521 | return ordered_events__flush(session, tool, OE_FLUSH__ROUND); |
572 | if (!ret) | ||
573 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
574 | |||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | /* The queue is ordered by time */ | ||
579 | static void __queue_event(struct sample_queue *new, struct perf_session *s) | ||
580 | { | ||
581 | struct ordered_samples *os = &s->ordered_samples; | ||
582 | struct sample_queue *sample = os->last_sample; | ||
583 | u64 timestamp = new->timestamp; | ||
584 | struct list_head *p; | ||
585 | |||
586 | ++os->nr_samples; | ||
587 | os->last_sample = new; | ||
588 | |||
589 | if (!sample) { | ||
590 | list_add(&new->list, &os->samples); | ||
591 | os->max_timestamp = timestamp; | ||
592 | return; | ||
593 | } | ||
594 | |||
595 | /* | ||
596 | * last_sample might point to some random place in the list as it's | ||
597 | * the last queued event. We expect that the new event is close to | ||
598 | * this. | ||
599 | */ | ||
600 | if (sample->timestamp <= timestamp) { | ||
601 | while (sample->timestamp <= timestamp) { | ||
602 | p = sample->list.next; | ||
603 | if (p == &os->samples) { | ||
604 | list_add_tail(&new->list, &os->samples); | ||
605 | os->max_timestamp = timestamp; | ||
606 | return; | ||
607 | } | ||
608 | sample = list_entry(p, struct sample_queue, list); | ||
609 | } | ||
610 | list_add_tail(&new->list, &sample->list); | ||
611 | } else { | ||
612 | while (sample->timestamp > timestamp) { | ||
613 | p = sample->list.prev; | ||
614 | if (p == &os->samples) { | ||
615 | list_add(&new->list, &os->samples); | ||
616 | return; | ||
617 | } | ||
618 | sample = list_entry(p, struct sample_queue, list); | ||
619 | } | ||
620 | list_add(&new->list, &sample->list); | ||
621 | } | ||
622 | } | 522 | } |
623 | 523 | ||
624 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | ||
625 | |||
626 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 524 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
627 | struct perf_sample *sample, u64 file_offset) | 525 | struct perf_tool *tool, struct perf_sample *sample, |
526 | u64 file_offset) | ||
628 | { | 527 | { |
629 | struct ordered_samples *os = &s->ordered_samples; | 528 | struct ordered_events *oe = &s->ordered_events; |
630 | struct list_head *sc = &os->sample_cache; | ||
631 | u64 timestamp = sample->time; | 529 | u64 timestamp = sample->time; |
632 | struct sample_queue *new; | 530 | struct ordered_event *new; |
633 | 531 | ||
634 | if (!timestamp || timestamp == ~0ULL) | 532 | if (!timestamp || timestamp == ~0ULL) |
635 | return -ETIME; | 533 | return -ETIME; |
636 | 534 | ||
637 | if (timestamp < s->ordered_samples.last_flush) { | 535 | if (timestamp < oe->last_flush) { |
638 | printf("Warning: Timestamp below last timeslice flush\n"); | 536 | pr_oe_time(timestamp, "out of order event\n"); |
639 | return -EINVAL; | 537 | pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", |
640 | } | 538 | oe->last_flush_type); |
641 | 539 | ||
642 | if (!list_empty(sc)) { | 540 | s->stats.nr_unordered_events++; |
643 | new = list_entry(sc->next, struct sample_queue, list); | ||
644 | list_del(&new->list); | ||
645 | } else if (os->sample_buffer) { | ||
646 | new = os->sample_buffer + os->sample_buffer_idx; | ||
647 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | ||
648 | os->sample_buffer = NULL; | ||
649 | } else { | ||
650 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | ||
651 | if (!os->sample_buffer) | ||
652 | return -ENOMEM; | ||
653 | list_add(&os->sample_buffer->list, &os->to_free); | ||
654 | os->sample_buffer_idx = 2; | ||
655 | new = os->sample_buffer + 1; | ||
656 | } | 541 | } |
657 | 542 | ||
658 | new->timestamp = timestamp; | 543 | new = ordered_events__new(oe, timestamp, event); |
659 | new->file_offset = file_offset; | 544 | if (!new) { |
660 | new->event = event; | 545 | ordered_events__flush(s, tool, OE_FLUSH__HALF); |
546 | new = ordered_events__new(oe, timestamp, event); | ||
547 | } | ||
661 | 548 | ||
662 | __queue_event(new, s); | 549 | if (!new) |
550 | return -ENOMEM; | ||
663 | 551 | ||
552 | new->file_offset = file_offset; | ||
664 | return 0; | 553 | return 0; |
665 | } | 554 | } |
666 | 555 | ||
@@ -699,15 +588,46 @@ static void regs_dump__printf(u64 mask, u64 *regs) | |||
699 | } | 588 | } |
700 | } | 589 | } |
701 | 590 | ||
591 | static const char *regs_abi[] = { | ||
592 | [PERF_SAMPLE_REGS_ABI_NONE] = "none", | ||
593 | [PERF_SAMPLE_REGS_ABI_32] = "32-bit", | ||
594 | [PERF_SAMPLE_REGS_ABI_64] = "64-bit", | ||
595 | }; | ||
596 | |||
597 | static inline const char *regs_dump_abi(struct regs_dump *d) | ||
598 | { | ||
599 | if (d->abi > PERF_SAMPLE_REGS_ABI_64) | ||
600 | return "unknown"; | ||
601 | |||
602 | return regs_abi[d->abi]; | ||
603 | } | ||
604 | |||
605 | static void regs__printf(const char *type, struct regs_dump *regs) | ||
606 | { | ||
607 | u64 mask = regs->mask; | ||
608 | |||
609 | printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n", | ||
610 | type, | ||
611 | mask, | ||
612 | regs_dump_abi(regs)); | ||
613 | |||
614 | regs_dump__printf(mask, regs->regs); | ||
615 | } | ||
616 | |||
702 | static void regs_user__printf(struct perf_sample *sample) | 617 | static void regs_user__printf(struct perf_sample *sample) |
703 | { | 618 | { |
704 | struct regs_dump *user_regs = &sample->user_regs; | 619 | struct regs_dump *user_regs = &sample->user_regs; |
705 | 620 | ||
706 | if (user_regs->regs) { | 621 | if (user_regs->regs) |
707 | u64 mask = user_regs->mask; | 622 | regs__printf("user", user_regs); |
708 | printf("... user regs: mask 0x%" PRIx64 "\n", mask); | 623 | } |
709 | regs_dump__printf(mask, user_regs->regs); | 624 | |
710 | } | 625 | static void regs_intr__printf(struct perf_sample *sample) |
626 | { | ||
627 | struct regs_dump *intr_regs = &sample->intr_regs; | ||
628 | |||
629 | if (intr_regs->regs) | ||
630 | regs__printf("intr", intr_regs); | ||
711 | } | 631 | } |
712 | 632 | ||
713 | static void stack_user__printf(struct stack_dump *dump) | 633 | static void stack_user__printf(struct stack_dump *dump) |
@@ -806,6 +726,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
806 | if (sample_type & PERF_SAMPLE_REGS_USER) | 726 | if (sample_type & PERF_SAMPLE_REGS_USER) |
807 | regs_user__printf(sample); | 727 | regs_user__printf(sample); |
808 | 728 | ||
729 | if (sample_type & PERF_SAMPLE_REGS_INTR) | ||
730 | regs_intr__printf(sample); | ||
731 | |||
809 | if (sample_type & PERF_SAMPLE_STACK_USER) | 732 | if (sample_type & PERF_SAMPLE_STACK_USER) |
810 | stack_user__printf(&sample->user_stack); | 733 | stack_user__printf(&sample->user_stack); |
811 | 734 | ||
@@ -920,11 +843,10 @@ perf_session__deliver_sample(struct perf_session *session, | |||
920 | &sample->read.one, machine); | 843 | &sample->read.one, machine); |
921 | } | 844 | } |
922 | 845 | ||
923 | static int perf_session_deliver_event(struct perf_session *session, | 846 | int perf_session__deliver_event(struct perf_session *session, |
924 | union perf_event *event, | 847 | union perf_event *event, |
925 | struct perf_sample *sample, | 848 | struct perf_sample *sample, |
926 | struct perf_tool *tool, | 849 | struct perf_tool *tool, u64 file_offset) |
927 | u64 file_offset) | ||
928 | { | 850 | { |
929 | struct perf_evsel *evsel; | 851 | struct perf_evsel *evsel; |
930 | struct machine *machine; | 852 | struct machine *machine; |
@@ -932,22 +854,6 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
932 | dump_event(session, event, file_offset, sample); | 854 | dump_event(session, event, file_offset, sample); |
933 | 855 | ||
934 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | 856 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
935 | if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { | ||
936 | /* | ||
937 | * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here | ||
938 | * because the tools right now may apply filters, discarding | ||
939 | * some of the samples. For consistency, in the future we | ||
940 | * should have something like nr_filtered_samples and remove | ||
941 | * the sample->period from total_sample_period, etc, KISS for | ||
942 | * now tho. | ||
943 | * | ||
944 | * Also testing against NULL allows us to handle files without | ||
945 | * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the | ||
946 | * future probably it'll be a good idea to restrict event | ||
947 | * processing via perf_session to files with both set. | ||
948 | */ | ||
949 | hists__inc_nr_events(&evsel->hists, event->header.type); | ||
950 | } | ||
951 | 857 | ||
952 | machine = perf_session__find_machine_for_cpumode(session, event, | 858 | machine = perf_session__find_machine_for_cpumode(session, event, |
953 | sample); | 859 | sample); |
@@ -1005,8 +911,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1005 | switch (event->header.type) { | 911 | switch (event->header.type) { |
1006 | case PERF_RECORD_HEADER_ATTR: | 912 | case PERF_RECORD_HEADER_ATTR: |
1007 | err = tool->attr(tool, event, &session->evlist); | 913 | err = tool->attr(tool, event, &session->evlist); |
1008 | if (err == 0) | 914 | if (err == 0) { |
1009 | perf_session__set_id_hdr_size(session); | 915 | perf_session__set_id_hdr_size(session); |
916 | perf_session__set_comm_exec(session); | ||
917 | } | ||
1010 | return err; | 918 | return err; |
1011 | case PERF_RECORD_HEADER_EVENT_TYPE: | 919 | case PERF_RECORD_HEADER_EVENT_TYPE: |
1012 | /* | 920 | /* |
@@ -1022,11 +930,26 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1022 | return tool->build_id(tool, event, session); | 930 | return tool->build_id(tool, event, session); |
1023 | case PERF_RECORD_FINISHED_ROUND: | 931 | case PERF_RECORD_FINISHED_ROUND: |
1024 | return tool->finished_round(tool, event, session); | 932 | return tool->finished_round(tool, event, session); |
933 | case PERF_RECORD_ID_INDEX: | ||
934 | return tool->id_index(tool, event, session); | ||
1025 | default: | 935 | default: |
1026 | return -EINVAL; | 936 | return -EINVAL; |
1027 | } | 937 | } |
1028 | } | 938 | } |
1029 | 939 | ||
940 | int perf_session__deliver_synth_event(struct perf_session *session, | ||
941 | union perf_event *event, | ||
942 | struct perf_sample *sample, | ||
943 | struct perf_tool *tool) | ||
944 | { | ||
945 | events_stats__inc(&session->stats, event->header.type); | ||
946 | |||
947 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | ||
948 | return perf_session__process_user_event(session, event, tool, 0); | ||
949 | |||
950 | return perf_session__deliver_event(session, event, sample, tool, 0); | ||
951 | } | ||
952 | |||
1030 | static void event_swap(union perf_event *event, bool sample_id_all) | 953 | static void event_swap(union perf_event *event, bool sample_id_all) |
1031 | { | 954 | { |
1032 | perf_event__swap_op swap; | 955 | perf_event__swap_op swap; |
@@ -1036,6 +959,61 @@ static void event_swap(union perf_event *event, bool sample_id_all) | |||
1036 | swap(event, sample_id_all); | 959 | swap(event, sample_id_all); |
1037 | } | 960 | } |
1038 | 961 | ||
962 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
963 | void *buf, size_t buf_sz, | ||
964 | union perf_event **event_ptr, | ||
965 | struct perf_sample *sample) | ||
966 | { | ||
967 | union perf_event *event; | ||
968 | size_t hdr_sz, rest; | ||
969 | int fd; | ||
970 | |||
971 | if (session->one_mmap && !session->header.needs_swap) { | ||
972 | event = file_offset - session->one_mmap_offset + | ||
973 | session->one_mmap_addr; | ||
974 | goto out_parse_sample; | ||
975 | } | ||
976 | |||
977 | if (perf_data_file__is_pipe(session->file)) | ||
978 | return -1; | ||
979 | |||
980 | fd = perf_data_file__fd(session->file); | ||
981 | hdr_sz = sizeof(struct perf_event_header); | ||
982 | |||
983 | if (buf_sz < hdr_sz) | ||
984 | return -1; | ||
985 | |||
986 | if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || | ||
987 | readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) | ||
988 | return -1; | ||
989 | |||
990 | event = (union perf_event *)buf; | ||
991 | |||
992 | if (session->header.needs_swap) | ||
993 | perf_event_header__bswap(&event->header); | ||
994 | |||
995 | if (event->header.size < hdr_sz) | ||
996 | return -1; | ||
997 | |||
998 | rest = event->header.size - hdr_sz; | ||
999 | |||
1000 | if (readn(fd, &buf, rest) != (ssize_t)rest) | ||
1001 | return -1; | ||
1002 | |||
1003 | if (session->header.needs_swap) | ||
1004 | event_swap(event, perf_evlist__sample_id_all(session->evlist)); | ||
1005 | |||
1006 | out_parse_sample: | ||
1007 | |||
1008 | if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && | ||
1009 | perf_evlist__parse_sample(session->evlist, event, sample)) | ||
1010 | return -1; | ||
1011 | |||
1012 | *event_ptr = event; | ||
1013 | |||
1014 | return 0; | ||
1015 | } | ||
1016 | |||
1039 | static s64 perf_session__process_event(struct perf_session *session, | 1017 | static s64 perf_session__process_event(struct perf_session *session, |
1040 | union perf_event *event, | 1018 | union perf_event *event, |
1041 | struct perf_tool *tool, | 1019 | struct perf_tool *tool, |
@@ -1062,15 +1040,15 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
1062 | if (ret) | 1040 | if (ret) |
1063 | return ret; | 1041 | return ret; |
1064 | 1042 | ||
1065 | if (tool->ordered_samples) { | 1043 | if (tool->ordered_events) { |
1066 | ret = perf_session_queue_event(session, event, &sample, | 1044 | ret = perf_session_queue_event(session, event, tool, &sample, |
1067 | file_offset); | 1045 | file_offset); |
1068 | if (ret != -ETIME) | 1046 | if (ret != -ETIME) |
1069 | return ret; | 1047 | return ret; |
1070 | } | 1048 | } |
1071 | 1049 | ||
1072 | return perf_session_deliver_event(session, event, &sample, tool, | 1050 | return perf_session__deliver_event(session, event, &sample, tool, |
1073 | file_offset); | 1051 | file_offset); |
1074 | } | 1052 | } |
1075 | 1053 | ||
1076 | void perf_event_header__bswap(struct perf_event_header *hdr) | 1054 | void perf_event_header__bswap(struct perf_event_header *hdr) |
@@ -1136,6 +1114,9 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
1136 | "Do you have a KVM guest running and not using 'perf kvm'?\n", | 1114 | "Do you have a KVM guest running and not using 'perf kvm'?\n", |
1137 | session->stats.nr_unprocessable_samples); | 1115 | session->stats.nr_unprocessable_samples); |
1138 | } | 1116 | } |
1117 | |||
1118 | if (session->stats.nr_unordered_events != 0) | ||
1119 | ui__warning("%u out of order events recorded.\n", session->stats.nr_unordered_events); | ||
1139 | } | 1120 | } |
1140 | 1121 | ||
1141 | volatile int session_done; | 1122 | volatile int session_done; |
@@ -1222,12 +1203,11 @@ more: | |||
1222 | goto more; | 1203 | goto more; |
1223 | done: | 1204 | done: |
1224 | /* do the final flush for ordered samples */ | 1205 | /* do the final flush for ordered samples */ |
1225 | session->ordered_samples.next_flush = ULLONG_MAX; | 1206 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1226 | err = flush_sample_queue(session, tool); | ||
1227 | out_err: | 1207 | out_err: |
1228 | free(buf); | 1208 | free(buf); |
1229 | perf_session__warn_about_errors(session, tool); | 1209 | perf_session__warn_about_errors(session, tool); |
1230 | perf_session_free_sample_buffers(session); | 1210 | ordered_events__free(&session->ordered_events); |
1231 | return err; | 1211 | return err; |
1232 | } | 1212 | } |
1233 | 1213 | ||
@@ -1368,12 +1348,11 @@ more: | |||
1368 | 1348 | ||
1369 | out: | 1349 | out: |
1370 | /* do the final flush for ordered samples */ | 1350 | /* do the final flush for ordered samples */ |
1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1351 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
1372 | err = flush_sample_queue(session, tool); | ||
1373 | out_err: | 1352 | out_err: |
1374 | ui_progress__finish(); | 1353 | ui_progress__finish(); |
1375 | perf_session__warn_about_errors(session, tool); | 1354 | perf_session__warn_about_errors(session, tool); |
1376 | perf_session_free_sample_buffers(session); | 1355 | ordered_events__free(&session->ordered_events); |
1377 | session->one_mmap = false; | 1356 | session->one_mmap = false; |
1378 | return err; | 1357 | return err; |
1379 | } | 1358 | } |
@@ -1455,16 +1434,9 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp | |||
1455 | 1434 | ||
1456 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1435 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
1457 | { | 1436 | { |
1458 | struct perf_evsel *pos; | ||
1459 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | 1437 | size_t ret = fprintf(fp, "Aggregated stats:\n"); |
1460 | 1438 | ||
1461 | ret += events_stats__fprintf(&session->stats, fp); | 1439 | ret += events_stats__fprintf(&session->stats, fp); |
1462 | |||
1463 | evlist__for_each(session->evlist, pos) { | ||
1464 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | ||
1465 | ret += events_stats__fprintf(&pos->hists.stats, fp); | ||
1466 | } | ||
1467 | |||
1468 | return ret; | 1440 | return ret; |
1469 | } | 1441 | } |
1470 | 1442 | ||
@@ -1505,9 +1477,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, | |||
1505 | if (symbol_conf.use_callchain && sample->callchain) { | 1477 | if (symbol_conf.use_callchain && sample->callchain) { |
1506 | struct addr_location node_al; | 1478 | struct addr_location node_al; |
1507 | 1479 | ||
1508 | if (machine__resolve_callchain(al->machine, evsel, al->thread, | 1480 | if (thread__resolve_callchain(al->thread, evsel, |
1509 | sample, NULL, NULL, | 1481 | sample, NULL, NULL, |
1510 | PERF_MAX_STACK_DEPTH) != 0) { | 1482 | PERF_MAX_STACK_DEPTH) != 0) { |
1511 | if (verbose) | 1483 | if (verbose) |
1512 | error("Failed to resolve callchain. Skipping\n"); | 1484 | error("Failed to resolve callchain. Skipping\n"); |
1513 | return; | 1485 | return; |
@@ -1682,3 +1654,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
1682 | out: | 1654 | out: |
1683 | return err; | 1655 | return err; |
1684 | } | 1656 | } |
1657 | |||
1658 | int perf_event__process_id_index(struct perf_tool *tool __maybe_unused, | ||
1659 | union perf_event *event, | ||
1660 | struct perf_session *session) | ||
1661 | { | ||
1662 | struct perf_evlist *evlist = session->evlist; | ||
1663 | struct id_index_event *ie = &event->id_index; | ||
1664 | size_t i, nr, max_nr; | ||
1665 | |||
1666 | max_nr = (ie->header.size - sizeof(struct id_index_event)) / | ||
1667 | sizeof(struct id_index_entry); | ||
1668 | nr = ie->nr; | ||
1669 | if (nr > max_nr) | ||
1670 | return -EINVAL; | ||
1671 | |||
1672 | if (dump_trace) | ||
1673 | fprintf(stdout, " nr: %zu\n", nr); | ||
1674 | |||
1675 | for (i = 0; i < nr; i++) { | ||
1676 | struct id_index_entry *e = &ie->entries[i]; | ||
1677 | struct perf_sample_id *sid; | ||
1678 | |||
1679 | if (dump_trace) { | ||
1680 | fprintf(stdout, " ... id: %"PRIu64, e->id); | ||
1681 | fprintf(stdout, " idx: %"PRIu64, e->idx); | ||
1682 | fprintf(stdout, " cpu: %"PRId64, e->cpu); | ||
1683 | fprintf(stdout, " tid: %"PRId64"\n", e->tid); | ||
1684 | } | ||
1685 | |||
1686 | sid = perf_evlist__id2sid(evlist, e->id); | ||
1687 | if (!sid) | ||
1688 | return -ENOENT; | ||
1689 | sid->idx = e->idx; | ||
1690 | sid->cpu = e->cpu; | ||
1691 | sid->tid = e->tid; | ||
1692 | } | ||
1693 | return 0; | ||
1694 | } | ||
1695 | |||
1696 | int perf_event__synthesize_id_index(struct perf_tool *tool, | ||
1697 | perf_event__handler_t process, | ||
1698 | struct perf_evlist *evlist, | ||
1699 | struct machine *machine) | ||
1700 | { | ||
1701 | union perf_event *ev; | ||
1702 | struct perf_evsel *evsel; | ||
1703 | size_t nr = 0, i = 0, sz, max_nr, n; | ||
1704 | int err; | ||
1705 | |||
1706 | pr_debug2("Synthesizing id index\n"); | ||
1707 | |||
1708 | max_nr = (UINT16_MAX - sizeof(struct id_index_event)) / | ||
1709 | sizeof(struct id_index_entry); | ||
1710 | |||
1711 | evlist__for_each(evlist, evsel) | ||
1712 | nr += evsel->ids; | ||
1713 | |||
1714 | n = nr > max_nr ? max_nr : nr; | ||
1715 | sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry); | ||
1716 | ev = zalloc(sz); | ||
1717 | if (!ev) | ||
1718 | return -ENOMEM; | ||
1719 | |||
1720 | ev->id_index.header.type = PERF_RECORD_ID_INDEX; | ||
1721 | ev->id_index.header.size = sz; | ||
1722 | ev->id_index.nr = n; | ||
1723 | |||
1724 | evlist__for_each(evlist, evsel) { | ||
1725 | u32 j; | ||
1726 | |||
1727 | for (j = 0; j < evsel->ids; j++) { | ||
1728 | struct id_index_entry *e; | ||
1729 | struct perf_sample_id *sid; | ||
1730 | |||
1731 | if (i >= n) { | ||
1732 | err = process(tool, ev, NULL, machine); | ||
1733 | if (err) | ||
1734 | goto out_err; | ||
1735 | nr -= n; | ||
1736 | i = 0; | ||
1737 | } | ||
1738 | |||
1739 | e = &ev->id_index.entries[i++]; | ||
1740 | |||
1741 | e->id = evsel->id[j]; | ||
1742 | |||
1743 | sid = perf_evlist__id2sid(evlist, e->id); | ||
1744 | if (!sid) { | ||
1745 | free(ev); | ||
1746 | return -ENOENT; | ||
1747 | } | ||
1748 | |||
1749 | e->idx = sid->idx; | ||
1750 | e->cpu = sid->cpu; | ||
1751 | e->tid = sid->tid; | ||
1752 | } | ||
1753 | } | ||
1754 | |||
1755 | sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry); | ||
1756 | ev->id_index.header.size = sz; | ||
1757 | ev->id_index.nr = nr; | ||
1758 | |||
1759 | err = process(tool, ev, NULL, machine); | ||
1760 | out_err: | ||
1761 | free(ev); | ||
1762 | |||
1763 | return err; | ||
1764 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0321013bd9fd..dc26ebf60fe4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -2,33 +2,19 @@ | |||
2 | #define __PERF_SESSION_H | 2 | #define __PERF_SESSION_H |
3 | 3 | ||
4 | #include "trace-event.h" | 4 | #include "trace-event.h" |
5 | #include "hist.h" | ||
6 | #include "event.h" | 5 | #include "event.h" |
7 | #include "header.h" | 6 | #include "header.h" |
8 | #include "machine.h" | 7 | #include "machine.h" |
9 | #include "symbol.h" | 8 | #include "symbol.h" |
10 | #include "thread.h" | 9 | #include "thread.h" |
11 | #include "data.h" | 10 | #include "data.h" |
11 | #include "ordered-events.h" | ||
12 | #include <linux/rbtree.h> | 12 | #include <linux/rbtree.h> |
13 | #include <linux/perf_event.h> | 13 | #include <linux/perf_event.h> |
14 | 14 | ||
15 | struct sample_queue; | ||
16 | struct ip_callchain; | 15 | struct ip_callchain; |
17 | struct thread; | 16 | struct thread; |
18 | 17 | ||
19 | struct ordered_samples { | ||
20 | u64 last_flush; | ||
21 | u64 next_flush; | ||
22 | u64 max_timestamp; | ||
23 | struct list_head samples; | ||
24 | struct list_head sample_cache; | ||
25 | struct list_head to_free; | ||
26 | struct sample_queue *sample_buffer; | ||
27 | struct sample_queue *last_sample; | ||
28 | int sample_buffer_idx; | ||
29 | unsigned int nr_samples; | ||
30 | }; | ||
31 | |||
32 | struct perf_session { | 18 | struct perf_session { |
33 | struct perf_header header; | 19 | struct perf_header header; |
34 | struct machines machines; | 20 | struct machines machines; |
@@ -39,7 +25,7 @@ struct perf_session { | |||
39 | bool one_mmap; | 25 | bool one_mmap; |
40 | void *one_mmap_addr; | 26 | void *one_mmap_addr; |
41 | u64 one_mmap_offset; | 27 | u64 one_mmap_offset; |
42 | struct ordered_samples ordered_samples; | 28 | struct ordered_events ordered_events; |
43 | struct perf_data_file *file; | 29 | struct perf_data_file *file; |
44 | }; | 30 | }; |
45 | 31 | ||
@@ -58,6 +44,11 @@ void perf_session__delete(struct perf_session *session); | |||
58 | 44 | ||
59 | void perf_event_header__bswap(struct perf_event_header *hdr); | 45 | void perf_event_header__bswap(struct perf_event_header *hdr); |
60 | 46 | ||
47 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
48 | void *buf, size_t buf_sz, | ||
49 | union perf_event **event_ptr, | ||
50 | struct perf_sample *sample); | ||
51 | |||
61 | int __perf_session__process_events(struct perf_session *session, | 52 | int __perf_session__process_events(struct perf_session *session, |
62 | u64 data_offset, u64 data_size, u64 size, | 53 | u64 data_offset, u64 data_size, u64 size, |
63 | struct perf_tool *tool); | 54 | struct perf_tool *tool); |
@@ -65,10 +56,16 @@ int perf_session__process_events(struct perf_session *session, | |||
65 | struct perf_tool *tool); | 56 | struct perf_tool *tool); |
66 | 57 | ||
67 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 58 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
68 | struct perf_sample *sample, u64 file_offset); | 59 | struct perf_tool *tool, struct perf_sample *sample, |
60 | u64 file_offset); | ||
69 | 61 | ||
70 | void perf_tool__fill_defaults(struct perf_tool *tool); | 62 | void perf_tool__fill_defaults(struct perf_tool *tool); |
71 | 63 | ||
64 | int perf_session__deliver_event(struct perf_session *session, | ||
65 | union perf_event *event, | ||
66 | struct perf_sample *sample, | ||
67 | struct perf_tool *tool, u64 file_offset); | ||
68 | |||
72 | int perf_session__resolve_callchain(struct perf_session *session, | 69 | int perf_session__resolve_callchain(struct perf_session *session, |
73 | struct perf_evsel *evsel, | 70 | struct perf_evsel *evsel, |
74 | struct thread *thread, | 71 | struct thread *thread, |
@@ -128,5 +125,20 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
128 | 125 | ||
129 | extern volatile int session_done; | 126 | extern volatile int session_done; |
130 | 127 | ||
131 | #define session_done() (*(volatile int *)(&session_done)) | 128 | #define session_done() ACCESS_ONCE(session_done) |
129 | |||
130 | int perf_session__deliver_synth_event(struct perf_session *session, | ||
131 | union perf_event *event, | ||
132 | struct perf_sample *sample, | ||
133 | struct perf_tool *tool); | ||
134 | |||
135 | int perf_event__process_id_index(struct perf_tool *tool, | ||
136 | union perf_event *event, | ||
137 | struct perf_session *session); | ||
138 | |||
139 | int perf_event__synthesize_id_index(struct perf_tool *tool, | ||
140 | perf_event__handler_t process, | ||
141 | struct perf_evlist *evlist, | ||
142 | struct machine *machine); | ||
143 | |||
132 | #endif /* __PERF_SESSION_H */ | 144 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 14e5a039bc45..9139dda9f9a3 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -70,12 +70,14 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, | |||
70 | size_t size, unsigned int width) | 70 | size_t size, unsigned int width) |
71 | { | 71 | { |
72 | const char *comm = thread__comm_str(he->thread); | 72 | const char *comm = thread__comm_str(he->thread); |
73 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, | 73 | |
74 | comm ?: "", he->thread->tid); | 74 | width = max(7U, width) - 6; |
75 | return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid, | ||
76 | width, width, comm ?: ""); | ||
75 | } | 77 | } |
76 | 78 | ||
77 | struct sort_entry sort_thread = { | 79 | struct sort_entry sort_thread = { |
78 | .se_header = "Command: Pid", | 80 | .se_header = " Pid:Command", |
79 | .se_cmp = sort__thread_cmp, | 81 | .se_cmp = sort__thread_cmp, |
80 | .se_snprintf = hist_entry__thread_snprintf, | 82 | .se_snprintf = hist_entry__thread_snprintf, |
81 | .se_width_idx = HISTC_THREAD, | 83 | .se_width_idx = HISTC_THREAD, |
@@ -106,7 +108,7 @@ sort__comm_sort(struct hist_entry *left, struct hist_entry *right) | |||
106 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, | 108 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, |
107 | size_t size, unsigned int width) | 109 | size_t size, unsigned int width) |
108 | { | 110 | { |
109 | return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); | 111 | return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm)); |
110 | } | 112 | } |
111 | 113 | ||
112 | struct sort_entry sort_comm = { | 114 | struct sort_entry sort_comm = { |
@@ -152,10 +154,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, | |||
152 | if (map && map->dso) { | 154 | if (map && map->dso) { |
153 | const char *dso_name = !verbose ? map->dso->short_name : | 155 | const char *dso_name = !verbose ? map->dso->short_name : |
154 | map->dso->long_name; | 156 | map->dso->long_name; |
155 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 157 | return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); |
156 | } | 158 | } |
157 | 159 | ||
158 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 160 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]"); |
159 | } | 161 | } |
160 | 162 | ||
161 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, | 163 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, |
@@ -257,7 +259,10 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
257 | width - ret, ""); | 259 | width - ret, ""); |
258 | } | 260 | } |
259 | 261 | ||
260 | return ret; | 262 | if (ret > width) |
263 | bf[width] = '\0'; | ||
264 | |||
265 | return width; | ||
261 | } | 266 | } |
262 | 267 | ||
263 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | 268 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, |
@@ -286,7 +291,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
286 | else { | 291 | else { |
287 | struct map *map = left->ms.map; | 292 | struct map *map = left->ms.map; |
288 | left->srcline = get_srcline(map->dso, | 293 | left->srcline = get_srcline(map->dso, |
289 | map__rip_2objdump(map, left->ip)); | 294 | map__rip_2objdump(map, left->ip), |
295 | left->ms.sym, true); | ||
290 | } | 296 | } |
291 | } | 297 | } |
292 | if (!right->srcline) { | 298 | if (!right->srcline) { |
@@ -295,17 +301,17 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
295 | else { | 301 | else { |
296 | struct map *map = right->ms.map; | 302 | struct map *map = right->ms.map; |
297 | right->srcline = get_srcline(map->dso, | 303 | right->srcline = get_srcline(map->dso, |
298 | map__rip_2objdump(map, right->ip)); | 304 | map__rip_2objdump(map, right->ip), |
305 | right->ms.sym, true); | ||
299 | } | 306 | } |
300 | } | 307 | } |
301 | return strcmp(right->srcline, left->srcline); | 308 | return strcmp(right->srcline, left->srcline); |
302 | } | 309 | } |
303 | 310 | ||
304 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 311 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
305 | size_t size, | 312 | size_t size, unsigned int width) |
306 | unsigned int width __maybe_unused) | ||
307 | { | 313 | { |
308 | return repsep_snprintf(bf, size, "%s", he->srcline); | 314 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline); |
309 | } | 315 | } |
310 | 316 | ||
311 | struct sort_entry sort_srcline = { | 317 | struct sort_entry sort_srcline = { |
@@ -332,7 +338,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
332 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, | 338 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, |
333 | size_t size, unsigned int width) | 339 | size_t size, unsigned int width) |
334 | { | 340 | { |
335 | return repsep_snprintf(bf, size, "%-*s", width, | 341 | return repsep_snprintf(bf, size, "%-*.*s", width, width, |
336 | he->parent ? he->parent->name : "[other]"); | 342 | he->parent ? he->parent->name : "[other]"); |
337 | } | 343 | } |
338 | 344 | ||
@@ -354,7 +360,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
354 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, | 360 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, |
355 | size_t size, unsigned int width) | 361 | size_t size, unsigned int width) |
356 | { | 362 | { |
357 | return repsep_snprintf(bf, size, "%*d", width, he->cpu); | 363 | return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); |
358 | } | 364 | } |
359 | 365 | ||
360 | struct sort_entry sort_cpu = { | 366 | struct sort_entry sort_cpu = { |
@@ -369,6 +375,9 @@ struct sort_entry sort_cpu = { | |||
369 | static int64_t | 375 | static int64_t |
370 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | 376 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) |
371 | { | 377 | { |
378 | if (!left->branch_info || !right->branch_info) | ||
379 | return cmp_null(left->branch_info, right->branch_info); | ||
380 | |||
372 | return _sort__dso_cmp(left->branch_info->from.map, | 381 | return _sort__dso_cmp(left->branch_info->from.map, |
373 | right->branch_info->from.map); | 382 | right->branch_info->from.map); |
374 | } | 383 | } |
@@ -376,13 +385,19 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
376 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, | 385 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, |
377 | size_t size, unsigned int width) | 386 | size_t size, unsigned int width) |
378 | { | 387 | { |
379 | return _hist_entry__dso_snprintf(he->branch_info->from.map, | 388 | if (he->branch_info) |
380 | bf, size, width); | 389 | return _hist_entry__dso_snprintf(he->branch_info->from.map, |
390 | bf, size, width); | ||
391 | else | ||
392 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
381 | } | 393 | } |
382 | 394 | ||
383 | static int64_t | 395 | static int64_t |
384 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 396 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
385 | { | 397 | { |
398 | if (!left->branch_info || !right->branch_info) | ||
399 | return cmp_null(left->branch_info, right->branch_info); | ||
400 | |||
386 | return _sort__dso_cmp(left->branch_info->to.map, | 401 | return _sort__dso_cmp(left->branch_info->to.map, |
387 | right->branch_info->to.map); | 402 | right->branch_info->to.map); |
388 | } | 403 | } |
@@ -390,8 +405,11 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
390 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, | 405 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, |
391 | size_t size, unsigned int width) | 406 | size_t size, unsigned int width) |
392 | { | 407 | { |
393 | return _hist_entry__dso_snprintf(he->branch_info->to.map, | 408 | if (he->branch_info) |
394 | bf, size, width); | 409 | return _hist_entry__dso_snprintf(he->branch_info->to.map, |
410 | bf, size, width); | ||
411 | else | ||
412 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
395 | } | 413 | } |
396 | 414 | ||
397 | static int64_t | 415 | static int64_t |
@@ -400,6 +418,12 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
400 | struct addr_map_symbol *from_l = &left->branch_info->from; | 418 | struct addr_map_symbol *from_l = &left->branch_info->from; |
401 | struct addr_map_symbol *from_r = &right->branch_info->from; | 419 | struct addr_map_symbol *from_r = &right->branch_info->from; |
402 | 420 | ||
421 | if (!left->branch_info || !right->branch_info) | ||
422 | return cmp_null(left->branch_info, right->branch_info); | ||
423 | |||
424 | from_l = &left->branch_info->from; | ||
425 | from_r = &right->branch_info->from; | ||
426 | |||
403 | if (!from_l->sym && !from_r->sym) | 427 | if (!from_l->sym && !from_r->sym) |
404 | return _sort__addr_cmp(from_l->addr, from_r->addr); | 428 | return _sort__addr_cmp(from_l->addr, from_r->addr); |
405 | 429 | ||
@@ -409,8 +433,13 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
409 | static int64_t | 433 | static int64_t |
410 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | 434 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) |
411 | { | 435 | { |
412 | struct addr_map_symbol *to_l = &left->branch_info->to; | 436 | struct addr_map_symbol *to_l, *to_r; |
413 | struct addr_map_symbol *to_r = &right->branch_info->to; | 437 | |
438 | if (!left->branch_info || !right->branch_info) | ||
439 | return cmp_null(left->branch_info, right->branch_info); | ||
440 | |||
441 | to_l = &left->branch_info->to; | ||
442 | to_r = &right->branch_info->to; | ||
414 | 443 | ||
415 | if (!to_l->sym && !to_r->sym) | 444 | if (!to_l->sym && !to_r->sym) |
416 | return _sort__addr_cmp(to_l->addr, to_r->addr); | 445 | return _sort__addr_cmp(to_l->addr, to_r->addr); |
@@ -421,19 +450,27 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
421 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, | 450 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, |
422 | size_t size, unsigned int width) | 451 | size_t size, unsigned int width) |
423 | { | 452 | { |
424 | struct addr_map_symbol *from = &he->branch_info->from; | 453 | if (he->branch_info) { |
425 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 454 | struct addr_map_symbol *from = &he->branch_info->from; |
426 | he->level, bf, size, width); | ||
427 | 455 | ||
456 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | ||
457 | he->level, bf, size, width); | ||
458 | } | ||
459 | |||
460 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
428 | } | 461 | } |
429 | 462 | ||
430 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, | 463 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, |
431 | size_t size, unsigned int width) | 464 | size_t size, unsigned int width) |
432 | { | 465 | { |
433 | struct addr_map_symbol *to = &he->branch_info->to; | 466 | if (he->branch_info) { |
434 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 467 | struct addr_map_symbol *to = &he->branch_info->to; |
435 | he->level, bf, size, width); | ||
436 | 468 | ||
469 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | ||
470 | he->level, bf, size, width); | ||
471 | } | ||
472 | |||
473 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
437 | } | 474 | } |
438 | 475 | ||
439 | struct sort_entry sort_dso_from = { | 476 | struct sort_entry sort_dso_from = { |
@@ -467,11 +504,13 @@ struct sort_entry sort_sym_to = { | |||
467 | static int64_t | 504 | static int64_t |
468 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) | 505 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) |
469 | { | 506 | { |
470 | const unsigned char mp = left->branch_info->flags.mispred != | 507 | unsigned char mp, p; |
471 | right->branch_info->flags.mispred; | ||
472 | const unsigned char p = left->branch_info->flags.predicted != | ||
473 | right->branch_info->flags.predicted; | ||
474 | 508 | ||
509 | if (!left->branch_info || !right->branch_info) | ||
510 | return cmp_null(left->branch_info, right->branch_info); | ||
511 | |||
512 | mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred; | ||
513 | p = left->branch_info->flags.predicted != right->branch_info->flags.predicted; | ||
475 | return mp || p; | 514 | return mp || p; |
476 | } | 515 | } |
477 | 516 | ||
@@ -479,12 +518,14 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, | |||
479 | size_t size, unsigned int width){ | 518 | size_t size, unsigned int width){ |
480 | static const char *out = "N/A"; | 519 | static const char *out = "N/A"; |
481 | 520 | ||
482 | if (he->branch_info->flags.predicted) | 521 | if (he->branch_info) { |
483 | out = "N"; | 522 | if (he->branch_info->flags.predicted) |
484 | else if (he->branch_info->flags.mispred) | 523 | out = "N"; |
485 | out = "Y"; | 524 | else if (he->branch_info->flags.mispred) |
525 | out = "Y"; | ||
526 | } | ||
486 | 527 | ||
487 | return repsep_snprintf(bf, size, "%-*s", width, out); | 528 | return repsep_snprintf(bf, size, "%-*.*s", width, width, out); |
488 | } | 529 | } |
489 | 530 | ||
490 | /* --sort daddr_sym */ | 531 | /* --sort daddr_sym */ |
@@ -985,6 +1026,9 @@ struct sort_entry sort_mem_dcacheline = { | |||
985 | static int64_t | 1026 | static int64_t |
986 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | 1027 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) |
987 | { | 1028 | { |
1029 | if (!left->branch_info || !right->branch_info) | ||
1030 | return cmp_null(left->branch_info, right->branch_info); | ||
1031 | |||
988 | return left->branch_info->flags.abort != | 1032 | return left->branch_info->flags.abort != |
989 | right->branch_info->flags.abort; | 1033 | right->branch_info->flags.abort; |
990 | } | 1034 | } |
@@ -992,10 +1036,15 @@ sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | |||
992 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, | 1036 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, |
993 | size_t size, unsigned int width) | 1037 | size_t size, unsigned int width) |
994 | { | 1038 | { |
995 | static const char *out = "."; | 1039 | static const char *out = "N/A"; |
1040 | |||
1041 | if (he->branch_info) { | ||
1042 | if (he->branch_info->flags.abort) | ||
1043 | out = "A"; | ||
1044 | else | ||
1045 | out = "."; | ||
1046 | } | ||
996 | 1047 | ||
997 | if (he->branch_info->flags.abort) | ||
998 | out = "A"; | ||
999 | return repsep_snprintf(bf, size, "%-*s", width, out); | 1048 | return repsep_snprintf(bf, size, "%-*s", width, out); |
1000 | } | 1049 | } |
1001 | 1050 | ||
@@ -1009,6 +1058,9 @@ struct sort_entry sort_abort = { | |||
1009 | static int64_t | 1058 | static int64_t |
1010 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) | 1059 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) |
1011 | { | 1060 | { |
1061 | if (!left->branch_info || !right->branch_info) | ||
1062 | return cmp_null(left->branch_info, right->branch_info); | ||
1063 | |||
1012 | return left->branch_info->flags.in_tx != | 1064 | return left->branch_info->flags.in_tx != |
1013 | right->branch_info->flags.in_tx; | 1065 | right->branch_info->flags.in_tx; |
1014 | } | 1066 | } |
@@ -1016,10 +1068,14 @@ sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) | |||
1016 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, | 1068 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, |
1017 | size_t size, unsigned int width) | 1069 | size_t size, unsigned int width) |
1018 | { | 1070 | { |
1019 | static const char *out = "."; | 1071 | static const char *out = "N/A"; |
1020 | 1072 | ||
1021 | if (he->branch_info->flags.in_tx) | 1073 | if (he->branch_info) { |
1022 | out = "T"; | 1074 | if (he->branch_info->flags.in_tx) |
1075 | out = "T"; | ||
1076 | else | ||
1077 | out = "."; | ||
1078 | } | ||
1023 | 1079 | ||
1024 | return repsep_snprintf(bf, size, "%-*s", width, out); | 1080 | return repsep_snprintf(bf, size, "%-*s", width, out); |
1025 | } | 1081 | } |
@@ -1194,7 +1250,7 @@ bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | |||
1194 | return hse_a->se == hse_b->se; | 1250 | return hse_a->se == hse_b->se; |
1195 | } | 1251 | } |
1196 | 1252 | ||
1197 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 1253 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) |
1198 | { | 1254 | { |
1199 | struct hpp_sort_entry *hse; | 1255 | struct hpp_sort_entry *hse; |
1200 | 1256 | ||
@@ -1202,20 +1258,21 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | |||
1202 | return; | 1258 | return; |
1203 | 1259 | ||
1204 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1260 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1205 | hists__new_col_len(hists, hse->se->se_width_idx, | 1261 | hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); |
1206 | strlen(hse->se->se_header)); | ||
1207 | } | 1262 | } |
1208 | 1263 | ||
1209 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1264 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
1210 | struct perf_evsel *evsel) | 1265 | struct perf_evsel *evsel) |
1211 | { | 1266 | { |
1212 | struct hpp_sort_entry *hse; | 1267 | struct hpp_sort_entry *hse; |
1213 | size_t len; | 1268 | size_t len = fmt->user_len; |
1214 | 1269 | ||
1215 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1270 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1216 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1217 | 1271 | ||
1218 | return scnprintf(hpp->buf, hpp->size, "%-*s", len, hse->se->se_header); | 1272 | if (!len) |
1273 | len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); | ||
1274 | |||
1275 | return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); | ||
1219 | } | 1276 | } |
1220 | 1277 | ||
1221 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | 1278 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, |
@@ -1223,20 +1280,26 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | |||
1223 | struct perf_evsel *evsel) | 1280 | struct perf_evsel *evsel) |
1224 | { | 1281 | { |
1225 | struct hpp_sort_entry *hse; | 1282 | struct hpp_sort_entry *hse; |
1283 | size_t len = fmt->user_len; | ||
1226 | 1284 | ||
1227 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1285 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1228 | 1286 | ||
1229 | return hists__col_len(&evsel->hists, hse->se->se_width_idx); | 1287 | if (!len) |
1288 | len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); | ||
1289 | |||
1290 | return len; | ||
1230 | } | 1291 | } |
1231 | 1292 | ||
1232 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1293 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
1233 | struct hist_entry *he) | 1294 | struct hist_entry *he) |
1234 | { | 1295 | { |
1235 | struct hpp_sort_entry *hse; | 1296 | struct hpp_sort_entry *hse; |
1236 | size_t len; | 1297 | size_t len = fmt->user_len; |
1237 | 1298 | ||
1238 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1299 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
1239 | len = hists__col_len(he->hists, hse->se->se_width_idx); | 1300 | |
1301 | if (!len) | ||
1302 | len = hists__col_len(he->hists, hse->se->se_width_idx); | ||
1240 | 1303 | ||
1241 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | 1304 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); |
1242 | } | 1305 | } |
@@ -1253,6 +1316,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
1253 | } | 1316 | } |
1254 | 1317 | ||
1255 | hse->se = sd->entry; | 1318 | hse->se = sd->entry; |
1319 | hse->hpp.name = sd->entry->se_header; | ||
1256 | hse->hpp.header = __sort__hpp_header; | 1320 | hse->hpp.header = __sort__hpp_header; |
1257 | hse->hpp.width = __sort__hpp_width; | 1321 | hse->hpp.width = __sort__hpp_width; |
1258 | hse->hpp.entry = __sort__hpp_entry; | 1322 | hse->hpp.entry = __sort__hpp_entry; |
@@ -1265,6 +1329,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
1265 | INIT_LIST_HEAD(&hse->hpp.list); | 1329 | INIT_LIST_HEAD(&hse->hpp.list); |
1266 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1330 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
1267 | hse->hpp.elide = false; | 1331 | hse->hpp.elide = false; |
1332 | hse->hpp.len = 0; | ||
1333 | hse->hpp.user_len = 0; | ||
1268 | 1334 | ||
1269 | return hse; | 1335 | return hse; |
1270 | } | 1336 | } |
@@ -1432,14 +1498,49 @@ static const char *get_default_sort_order(void) | |||
1432 | return default_sort_orders[sort__mode]; | 1498 | return default_sort_orders[sort__mode]; |
1433 | } | 1499 | } |
1434 | 1500 | ||
1501 | static int setup_sort_order(void) | ||
1502 | { | ||
1503 | char *new_sort_order; | ||
1504 | |||
1505 | /* | ||
1506 | * Append '+'-prefixed sort order to the default sort | ||
1507 | * order string. | ||
1508 | */ | ||
1509 | if (!sort_order || is_strict_order(sort_order)) | ||
1510 | return 0; | ||
1511 | |||
1512 | if (sort_order[1] == '\0') { | ||
1513 | error("Invalid --sort key: `+'"); | ||
1514 | return -EINVAL; | ||
1515 | } | ||
1516 | |||
1517 | /* | ||
1518 | * We allocate new sort_order string, but we never free it, | ||
1519 | * because it's checked over the rest of the code. | ||
1520 | */ | ||
1521 | if (asprintf(&new_sort_order, "%s,%s", | ||
1522 | get_default_sort_order(), sort_order + 1) < 0) { | ||
1523 | error("Not enough memory to set up --sort"); | ||
1524 | return -ENOMEM; | ||
1525 | } | ||
1526 | |||
1527 | sort_order = new_sort_order; | ||
1528 | return 0; | ||
1529 | } | ||
1530 | |||
1435 | static int __setup_sorting(void) | 1531 | static int __setup_sorting(void) |
1436 | { | 1532 | { |
1437 | char *tmp, *tok, *str; | 1533 | char *tmp, *tok, *str; |
1438 | const char *sort_keys = sort_order; | 1534 | const char *sort_keys; |
1439 | int ret = 0; | 1535 | int ret = 0; |
1440 | 1536 | ||
1537 | ret = setup_sort_order(); | ||
1538 | if (ret) | ||
1539 | return ret; | ||
1540 | |||
1541 | sort_keys = sort_order; | ||
1441 | if (sort_keys == NULL) { | 1542 | if (sort_keys == NULL) { |
1442 | if (field_order) { | 1543 | if (is_strict_order(field_order)) { |
1443 | /* | 1544 | /* |
1444 | * If user specified field order but no sort order, | 1545 | * If user specified field order but no sort order, |
1445 | * we'll honor it and not add default sort orders. | 1546 | * we'll honor it and not add default sort orders. |
@@ -1625,23 +1726,36 @@ static void reset_dimensions(void) | |||
1625 | memory_sort_dimensions[i].taken = 0; | 1726 | memory_sort_dimensions[i].taken = 0; |
1626 | } | 1727 | } |
1627 | 1728 | ||
1729 | bool is_strict_order(const char *order) | ||
1730 | { | ||
1731 | return order && (*order != '+'); | ||
1732 | } | ||
1733 | |||
1628 | static int __setup_output_field(void) | 1734 | static int __setup_output_field(void) |
1629 | { | 1735 | { |
1630 | char *tmp, *tok, *str; | 1736 | char *tmp, *tok, *str, *strp; |
1631 | int ret = 0; | 1737 | int ret = -EINVAL; |
1632 | 1738 | ||
1633 | if (field_order == NULL) | 1739 | if (field_order == NULL) |
1634 | return 0; | 1740 | return 0; |
1635 | 1741 | ||
1636 | reset_dimensions(); | 1742 | reset_dimensions(); |
1637 | 1743 | ||
1638 | str = strdup(field_order); | 1744 | strp = str = strdup(field_order); |
1639 | if (str == NULL) { | 1745 | if (str == NULL) { |
1640 | error("Not enough memory to setup output fields"); | 1746 | error("Not enough memory to setup output fields"); |
1641 | return -ENOMEM; | 1747 | return -ENOMEM; |
1642 | } | 1748 | } |
1643 | 1749 | ||
1644 | for (tok = strtok_r(str, ", ", &tmp); | 1750 | if (!is_strict_order(field_order)) |
1751 | strp++; | ||
1752 | |||
1753 | if (!strlen(strp)) { | ||
1754 | error("Invalid --fields key: `+'"); | ||
1755 | goto out; | ||
1756 | } | ||
1757 | |||
1758 | for (tok = strtok_r(strp, ", ", &tmp); | ||
1645 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1759 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
1646 | ret = output_field_add(tok); | 1760 | ret = output_field_add(tok); |
1647 | if (ret == -EINVAL) { | 1761 | if (ret == -EINVAL) { |
@@ -1653,6 +1767,7 @@ static int __setup_output_field(void) | |||
1653 | } | 1767 | } |
1654 | } | 1768 | } |
1655 | 1769 | ||
1770 | out: | ||
1656 | free(str); | 1771 | free(str); |
1657 | return ret; | 1772 | return ret; |
1658 | } | 1773 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 041f0c9cea2b..c03e4ff8beff 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide); | |||
218 | 218 | ||
219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); | 219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); |
220 | 220 | ||
221 | bool is_strict_order(const char *order); | ||
221 | #endif /* __PERF_SORT_H */ | 222 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index f3e4bc5fe5d2..c93fb0c5bd0b 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c | |||
@@ -8,6 +8,8 @@ | |||
8 | #include "util/util.h" | 8 | #include "util/util.h" |
9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
10 | 10 | ||
11 | #include "symbol.h" | ||
12 | |||
11 | #ifdef HAVE_LIBBFD_SUPPORT | 13 | #ifdef HAVE_LIBBFD_SUPPORT |
12 | 14 | ||
13 | /* | 15 | /* |
@@ -18,7 +20,7 @@ | |||
18 | 20 | ||
19 | struct a2l_data { | 21 | struct a2l_data { |
20 | const char *input; | 22 | const char *input; |
21 | unsigned long addr; | 23 | u64 addr; |
22 | 24 | ||
23 | bool found; | 25 | bool found; |
24 | const char *filename; | 26 | const char *filename; |
@@ -145,7 +147,7 @@ static void addr2line_cleanup(struct a2l_data *a2l) | |||
145 | free(a2l); | 147 | free(a2l); |
146 | } | 148 | } |
147 | 149 | ||
148 | static int addr2line(const char *dso_name, unsigned long addr, | 150 | static int addr2line(const char *dso_name, u64 addr, |
149 | char **file, unsigned int *line, struct dso *dso) | 151 | char **file, unsigned int *line, struct dso *dso) |
150 | { | 152 | { |
151 | int ret = 0; | 153 | int ret = 0; |
@@ -191,7 +193,7 @@ void dso__free_a2l(struct dso *dso) | |||
191 | 193 | ||
192 | #else /* HAVE_LIBBFD_SUPPORT */ | 194 | #else /* HAVE_LIBBFD_SUPPORT */ |
193 | 195 | ||
194 | static int addr2line(const char *dso_name, unsigned long addr, | 196 | static int addr2line(const char *dso_name, u64 addr, |
195 | char **file, unsigned int *line_nr, | 197 | char **file, unsigned int *line_nr, |
196 | struct dso *dso __maybe_unused) | 198 | struct dso *dso __maybe_unused) |
197 | { | 199 | { |
@@ -250,7 +252,8 @@ void dso__free_a2l(struct dso *dso __maybe_unused) | |||
250 | */ | 252 | */ |
251 | #define A2L_FAIL_LIMIT 123 | 253 | #define A2L_FAIL_LIMIT 123 |
252 | 254 | ||
253 | char *get_srcline(struct dso *dso, unsigned long addr) | 255 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
256 | bool show_sym) | ||
254 | { | 257 | { |
255 | char *file = NULL; | 258 | char *file = NULL; |
256 | unsigned line = 0; | 259 | unsigned line = 0; |
@@ -258,7 +261,7 @@ char *get_srcline(struct dso *dso, unsigned long addr) | |||
258 | const char *dso_name; | 261 | const char *dso_name; |
259 | 262 | ||
260 | if (!dso->has_srcline) | 263 | if (!dso->has_srcline) |
261 | return SRCLINE_UNKNOWN; | 264 | goto out; |
262 | 265 | ||
263 | if (dso->symsrc_filename) | 266 | if (dso->symsrc_filename) |
264 | dso_name = dso->symsrc_filename; | 267 | dso_name = dso->symsrc_filename; |
@@ -274,7 +277,7 @@ char *get_srcline(struct dso *dso, unsigned long addr) | |||
274 | if (!addr2line(dso_name, addr, &file, &line, dso)) | 277 | if (!addr2line(dso_name, addr, &file, &line, dso)) |
275 | goto out; | 278 | goto out; |
276 | 279 | ||
277 | if (asprintf(&srcline, "%s:%u", file, line) < 0) { | 280 | if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) { |
278 | free(file); | 281 | free(file); |
279 | goto out; | 282 | goto out; |
280 | } | 283 | } |
@@ -289,7 +292,13 @@ out: | |||
289 | dso->has_srcline = 0; | 292 | dso->has_srcline = 0; |
290 | dso__free_a2l(dso); | 293 | dso__free_a2l(dso); |
291 | } | 294 | } |
292 | return SRCLINE_UNKNOWN; | 295 | if (sym) { |
296 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", | ||
297 | addr - sym->start) < 0) | ||
298 | return SRCLINE_UNKNOWN; | ||
299 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0) | ||
300 | return SRCLINE_UNKNOWN; | ||
301 | return srcline; | ||
293 | } | 302 | } |
294 | 303 | ||
295 | void free_srcline(char *srcline) | 304 | void free_srcline(char *srcline) |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 2553e5b55b89..6afd6106ceb5 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -9,78 +9,48 @@ | |||
9 | */ | 9 | */ |
10 | s64 perf_atoll(const char *str) | 10 | s64 perf_atoll(const char *str) |
11 | { | 11 | { |
12 | unsigned int i; | 12 | s64 length; |
13 | s64 length = -1, unit = 1; | 13 | char *p; |
14 | char c; | ||
14 | 15 | ||
15 | if (!isdigit(str[0])) | 16 | if (!isdigit(str[0])) |
16 | goto out_err; | 17 | goto out_err; |
17 | 18 | ||
18 | for (i = 1; i < strlen(str); i++) { | 19 | length = strtoll(str, &p, 10); |
19 | switch (str[i]) { | 20 | switch (c = *p++) { |
20 | case 'B': | 21 | case 'b': case 'B': |
21 | case 'b': | 22 | if (*p) |
22 | break; | ||
23 | case 'K': | ||
24 | if (str[i + 1] != 'B') | ||
25 | goto out_err; | ||
26 | else | ||
27 | goto kilo; | ||
28 | case 'k': | ||
29 | if (str[i + 1] != 'b') | ||
30 | goto out_err; | ||
31 | kilo: | ||
32 | unit = K; | ||
33 | break; | ||
34 | case 'M': | ||
35 | if (str[i + 1] != 'B') | ||
36 | goto out_err; | ||
37 | else | ||
38 | goto mega; | ||
39 | case 'm': | ||
40 | if (str[i + 1] != 'b') | ||
41 | goto out_err; | ||
42 | mega: | ||
43 | unit = K * K; | ||
44 | break; | ||
45 | case 'G': | ||
46 | if (str[i + 1] != 'B') | ||
47 | goto out_err; | 23 | goto out_err; |
48 | else | 24 | case '\0': |
49 | goto giga; | 25 | return length; |
50 | case 'g': | 26 | default: |
51 | if (str[i + 1] != 'b') | 27 | goto out_err; |
52 | goto out_err; | 28 | /* two-letter suffices */ |
53 | giga: | 29 | case 'k': case 'K': |
54 | unit = K * K * K; | 30 | length <<= 10; |
55 | break; | 31 | break; |
56 | case 'T': | 32 | case 'm': case 'M': |
57 | if (str[i + 1] != 'B') | 33 | length <<= 20; |
58 | goto out_err; | ||
59 | else | ||
60 | goto tera; | ||
61 | case 't': | ||
62 | if (str[i + 1] != 'b') | ||
63 | goto out_err; | ||
64 | tera: | ||
65 | unit = K * K * K * K; | ||
66 | break; | 34 | break; |
67 | case '\0': /* only specified figures */ | 35 | case 'g': case 'G': |
68 | unit = 1; | 36 | length <<= 30; |
69 | break; | 37 | break; |
70 | default: | 38 | case 't': case 'T': |
71 | if (!isdigit(str[i])) | 39 | length <<= 40; |
72 | goto out_err; | ||
73 | break; | 40 | break; |
74 | } | ||
75 | } | 41 | } |
76 | 42 | /* we want the cases to match */ | |
77 | length = atoll(str) * unit; | 43 | if (islower(c)) { |
78 | goto out; | 44 | if (strcmp(p, "b") != 0) |
45 | goto out_err; | ||
46 | } else { | ||
47 | if (strcmp(p, "B") != 0) | ||
48 | goto out_err; | ||
49 | } | ||
50 | return length; | ||
79 | 51 | ||
80 | out_err: | 52 | out_err: |
81 | length = -1; | 53 | return -1; |
82 | out: | ||
83 | return length; | ||
84 | } | 54 | } |
85 | 55 | ||
86 | /* | 56 | /* |
@@ -387,27 +357,3 @@ void *memdup(const void *src, size_t len) | |||
387 | 357 | ||
388 | return p; | 358 | return p; |
389 | } | 359 | } |
390 | |||
391 | /** | ||
392 | * str_append - reallocate string and append another | ||
393 | * @s: pointer to string pointer | ||
394 | * @len: pointer to len (initialized) | ||
395 | * @a: string to append. | ||
396 | */ | ||
397 | int str_append(char **s, int *len, const char *a) | ||
398 | { | ||
399 | int olen = *s ? strlen(*s) : 0; | ||
400 | int nlen = olen + strlen(a) + 1; | ||
401 | if (*len < nlen) { | ||
402 | *len = *len * 2; | ||
403 | if (*len < nlen) | ||
404 | *len = nlen; | ||
405 | *s = realloc(*s, *len); | ||
406 | if (!*s) | ||
407 | return -ENOMEM; | ||
408 | if (olen == 0) | ||
409 | **s = 0; | ||
410 | } | ||
411 | strcat(*s, a); | ||
412 | return 0; | ||
413 | } | ||
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d75349979e65..06fcd1bf98b6 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -6,10 +6,32 @@ | |||
6 | #include <inttypes.h> | 6 | #include <inttypes.h> |
7 | 7 | ||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "machine.h" | ||
9 | #include "vdso.h" | 10 | #include "vdso.h" |
10 | #include <symbol/kallsyms.h> | 11 | #include <symbol/kallsyms.h> |
11 | #include "debug.h" | 12 | #include "debug.h" |
12 | 13 | ||
14 | #ifdef HAVE_CPLUS_DEMANGLE_SUPPORT | ||
15 | extern char *cplus_demangle(const char *, int); | ||
16 | |||
17 | static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) | ||
18 | { | ||
19 | return cplus_demangle(c, i); | ||
20 | } | ||
21 | #else | ||
22 | #ifdef NO_DEMANGLE | ||
23 | static inline char *bfd_demangle(void __maybe_unused *v, | ||
24 | const char __maybe_unused *c, | ||
25 | int __maybe_unused i) | ||
26 | { | ||
27 | return NULL; | ||
28 | } | ||
29 | #else | ||
30 | #define PACKAGE 'perf' | ||
31 | #include <bfd.h> | ||
32 | #endif | ||
33 | #endif | ||
34 | |||
13 | #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT | 35 | #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT |
14 | static int elf_getphdrnum(Elf *elf, size_t *dst) | 36 | static int elf_getphdrnum(Elf *elf, size_t *dst) |
15 | { | 37 | { |
@@ -545,6 +567,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata) | |||
545 | return 0; | 567 | return 0; |
546 | } | 568 | } |
547 | 569 | ||
570 | static int decompress_kmodule(struct dso *dso, const char *name, | ||
571 | enum dso_binary_type type) | ||
572 | { | ||
573 | int fd; | ||
574 | const char *ext = strrchr(name, '.'); | ||
575 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; | ||
576 | |||
577 | if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && | ||
578 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) || | ||
579 | type != dso->symtab_type) | ||
580 | return -1; | ||
581 | |||
582 | if (!ext || !is_supported_compression(ext + 1)) | ||
583 | return -1; | ||
584 | |||
585 | fd = mkstemp(tmpbuf); | ||
586 | if (fd < 0) | ||
587 | return -1; | ||
588 | |||
589 | if (!decompress_to_file(ext + 1, name, fd)) { | ||
590 | close(fd); | ||
591 | fd = -1; | ||
592 | } | ||
593 | |||
594 | unlink(tmpbuf); | ||
595 | |||
596 | return fd; | ||
597 | } | ||
598 | |||
548 | bool symsrc__possibly_runtime(struct symsrc *ss) | 599 | bool symsrc__possibly_runtime(struct symsrc *ss) |
549 | { | 600 | { |
550 | return ss->dynsym || ss->opdsec; | 601 | return ss->dynsym || ss->opdsec; |
@@ -570,7 +621,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
570 | Elf *elf; | 621 | Elf *elf; |
571 | int fd; | 622 | int fd; |
572 | 623 | ||
573 | fd = open(name, O_RDONLY); | 624 | if (dso__needs_decompress(dso)) |
625 | fd = decompress_kmodule(dso, name, type); | ||
626 | else | ||
627 | fd = open(name, O_RDONLY); | ||
628 | |||
574 | if (fd < 0) | 629 | if (fd < 0) |
575 | return -1; | 630 | return -1; |
576 | 631 | ||
@@ -680,6 +735,11 @@ static u64 ref_reloc(struct kmap *kmap) | |||
680 | return 0; | 735 | return 0; |
681 | } | 736 | } |
682 | 737 | ||
738 | static bool want_demangle(bool is_kernel_sym) | ||
739 | { | ||
740 | return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; | ||
741 | } | ||
742 | |||
683 | int dso__load_sym(struct dso *dso, struct map *map, | 743 | int dso__load_sym(struct dso *dso, struct map *map, |
684 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 744 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
685 | symbol_filter_t filter, int kmodule) | 745 | symbol_filter_t filter, int kmodule) |
@@ -712,6 +772,14 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
712 | symbols__delete(&dso->symbols[map->type]); | 772 | symbols__delete(&dso->symbols[map->type]); |
713 | 773 | ||
714 | if (!syms_ss->symtab) { | 774 | if (!syms_ss->symtab) { |
775 | /* | ||
776 | * If the vmlinux is stripped, fail so we will fall back | ||
777 | * to using kallsyms. The vmlinux runtime symbols aren't | ||
778 | * of much use. | ||
779 | */ | ||
780 | if (dso->kernel) | ||
781 | goto out_elf_end; | ||
782 | |||
715 | syms_ss->symtab = syms_ss->dynsym; | 783 | syms_ss->symtab = syms_ss->dynsym; |
716 | syms_ss->symshdr = syms_ss->dynshdr; | 784 | syms_ss->symshdr = syms_ss->dynshdr; |
717 | } | 785 | } |
@@ -736,7 +804,7 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
736 | if (symstrs == NULL) | 804 | if (symstrs == NULL) |
737 | goto out_elf_end; | 805 | goto out_elf_end; |
738 | 806 | ||
739 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | 807 | sec_strndx = elf_getscn(runtime_ss->elf, runtime_ss->ehdr.e_shstrndx); |
740 | if (sec_strndx == NULL) | 808 | if (sec_strndx == NULL) |
741 | goto out_elf_end; | 809 | goto out_elf_end; |
742 | 810 | ||
@@ -916,7 +984,11 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
916 | } | 984 | } |
917 | curr_dso->symtab_type = dso->symtab_type; | 985 | curr_dso->symtab_type = dso->symtab_type; |
918 | map_groups__insert(kmap->kmaps, curr_map); | 986 | map_groups__insert(kmap->kmaps, curr_map); |
919 | dsos__add(&dso->node, curr_dso); | 987 | /* |
988 | * The new DSO should go to the kernel DSOS | ||
989 | */ | ||
990 | dsos__add(&map->groups->machine->kernel_dsos, | ||
991 | curr_dso); | ||
920 | dso__set_loaded(curr_dso, map->type); | 992 | dso__set_loaded(curr_dso, map->type); |
921 | } else | 993 | } else |
922 | curr_dso = curr_map->dso; | 994 | curr_dso = curr_map->dso; |
@@ -938,9 +1010,12 @@ new_symbol: | |||
938 | * DWARF DW_compile_unit has this, but we don't always have access | 1010 | * DWARF DW_compile_unit has this, but we don't always have access |
939 | * to it... | 1011 | * to it... |
940 | */ | 1012 | */ |
941 | if (symbol_conf.demangle) { | 1013 | if (want_demangle(dso->kernel || kmodule)) { |
942 | demangled = bfd_demangle(NULL, elf_name, | 1014 | int demangle_flags = DMGL_NO_OPTS; |
943 | DMGL_PARAMS | DMGL_ANSI); | 1015 | if (verbose) |
1016 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; | ||
1017 | |||
1018 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); | ||
944 | if (demangled != NULL) | 1019 | if (demangled != NULL) |
945 | elf_name = demangled; | 1020 | elf_name = demangled; |
946 | } | 1021 | } |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index c9541fea9514..d7efb03b3f9a 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -129,6 +129,7 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
129 | 129 | ||
130 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { | 130 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { |
131 | void *tmp; | 131 | void *tmp; |
132 | long offset; | ||
132 | 133 | ||
133 | if (need_swap) { | 134 | if (need_swap) { |
134 | phdr->p_type = bswap_32(phdr->p_type); | 135 | phdr->p_type = bswap_32(phdr->p_type); |
@@ -140,12 +141,13 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
140 | continue; | 141 | continue; |
141 | 142 | ||
142 | buf_size = phdr->p_filesz; | 143 | buf_size = phdr->p_filesz; |
144 | offset = phdr->p_offset; | ||
143 | tmp = realloc(buf, buf_size); | 145 | tmp = realloc(buf, buf_size); |
144 | if (tmp == NULL) | 146 | if (tmp == NULL) |
145 | goto out_free; | 147 | goto out_free; |
146 | 148 | ||
147 | buf = tmp; | 149 | buf = tmp; |
148 | fseek(fp, phdr->p_offset, SEEK_SET); | 150 | fseek(fp, offset, SEEK_SET); |
149 | if (fread(buf, buf_size, 1, fp) != 1) | 151 | if (fread(buf, buf_size, 1, fp) != 1) |
150 | goto out_free; | 152 | goto out_free; |
151 | 153 | ||
@@ -178,6 +180,7 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
178 | 180 | ||
179 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { | 181 | for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) { |
180 | void *tmp; | 182 | void *tmp; |
183 | long offset; | ||
181 | 184 | ||
182 | if (need_swap) { | 185 | if (need_swap) { |
183 | phdr->p_type = bswap_32(phdr->p_type); | 186 | phdr->p_type = bswap_32(phdr->p_type); |
@@ -189,12 +192,13 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
189 | continue; | 192 | continue; |
190 | 193 | ||
191 | buf_size = phdr->p_filesz; | 194 | buf_size = phdr->p_filesz; |
195 | offset = phdr->p_offset; | ||
192 | tmp = realloc(buf, buf_size); | 196 | tmp = realloc(buf, buf_size); |
193 | if (tmp == NULL) | 197 | if (tmp == NULL) |
194 | goto out_free; | 198 | goto out_free; |
195 | 199 | ||
196 | buf = tmp; | 200 | buf = tmp; |
197 | fseek(fp, phdr->p_offset, SEEK_SET); | 201 | fseek(fp, offset, SEEK_SET); |
198 | if (fread(buf, buf_size, 1, fp) != 1) | 202 | if (fread(buf, buf_size, 1, fp) != 1) |
199 | goto out_free; | 203 | goto out_free; |
200 | 204 | ||
@@ -341,7 +345,6 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, | |||
341 | 345 | ||
342 | if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { | 346 | if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { |
343 | dso__set_build_id(dso, build_id); | 347 | dso__set_build_id(dso, build_id); |
344 | return 1; | ||
345 | } | 348 | } |
346 | return 0; | 349 | return 0; |
347 | } | 350 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index eb06746b06b2..c24c5b83156c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "machine.h" | 15 | #include "machine.h" |
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "strlist.h" | 17 | #include "strlist.h" |
18 | #include "header.h" | ||
18 | 19 | ||
19 | #include <elf.h> | 20 | #include <elf.h> |
20 | #include <limits.h> | 21 | #include <limits.h> |
@@ -33,6 +34,7 @@ struct symbol_conf symbol_conf = { | |||
33 | .try_vmlinux_path = true, | 34 | .try_vmlinux_path = true, |
34 | .annotate_src = true, | 35 | .annotate_src = true, |
35 | .demangle = true, | 36 | .demangle = true, |
37 | .demangle_kernel = false, | ||
36 | .cumulate_callchain = true, | 38 | .cumulate_callchain = true, |
37 | .show_hist_headers = true, | 39 | .show_hist_headers = true, |
38 | .symfs = "", | 40 | .symfs = "", |
@@ -49,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
49 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
50 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
51 | DSO_BINARY_TYPE__GUEST_KMODULE, | 53 | DSO_BINARY_TYPE__GUEST_KMODULE, |
54 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
52 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 55 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
56 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
53 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 57 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
54 | DSO_BINARY_TYPE__NOT_FOUND, | 58 | DSO_BINARY_TYPE__NOT_FOUND, |
55 | }; | 59 | }; |
@@ -184,7 +188,7 @@ void symbols__fixup_end(struct rb_root *symbols) | |||
184 | curr = rb_entry(nd, struct symbol, rb_node); | 188 | curr = rb_entry(nd, struct symbol, rb_node); |
185 | 189 | ||
186 | if (prev->end == prev->start && prev->end != curr->start) | 190 | if (prev->end == prev->start && prev->end != curr->start) |
187 | prev->end = curr->start - 1; | 191 | prev->end = curr->start; |
188 | } | 192 | } |
189 | 193 | ||
190 | /* Last entry */ | 194 | /* Last entry */ |
@@ -205,7 +209,7 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | |||
205 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 209 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
206 | prev = curr; | 210 | prev = curr; |
207 | curr = rb_entry(nd, struct map, rb_node); | 211 | curr = rb_entry(nd, struct map, rb_node); |
208 | prev->end = curr->start - 1; | 212 | prev->end = curr->start; |
209 | } | 213 | } |
210 | 214 | ||
211 | /* | 215 | /* |
@@ -227,7 +231,7 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | |||
227 | sym = ((void *)sym) + symbol_conf.priv_size; | 231 | sym = ((void *)sym) + symbol_conf.priv_size; |
228 | 232 | ||
229 | sym->start = start; | 233 | sym->start = start; |
230 | sym->end = len ? start + len - 1 : start; | 234 | sym->end = len ? start + len : start; |
231 | sym->binding = binding; | 235 | sym->binding = binding; |
232 | sym->namelen = namelen - 1; | 236 | sym->namelen = namelen - 1; |
233 | 237 | ||
@@ -323,7 +327,7 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | |||
323 | 327 | ||
324 | if (ip < s->start) | 328 | if (ip < s->start) |
325 | n = n->rb_left; | 329 | n = n->rb_left; |
326 | else if (ip > s->end) | 330 | else if (ip >= s->end) |
327 | n = n->rb_right; | 331 | n = n->rb_right; |
328 | else | 332 | else |
329 | return s; | 333 | return s; |
@@ -523,10 +527,15 @@ struct process_kallsyms_args { | |||
523 | struct dso *dso; | 527 | struct dso *dso; |
524 | }; | 528 | }; |
525 | 529 | ||
530 | /* | ||
531 | * These are symbols in the kernel image, so make sure that | ||
532 | * sym is from a kernel DSO. | ||
533 | */ | ||
526 | bool symbol__is_idle(struct symbol *sym) | 534 | bool symbol__is_idle(struct symbol *sym) |
527 | { | 535 | { |
528 | const char * const idle_symbols[] = { | 536 | const char * const idle_symbols[] = { |
529 | "cpu_idle", | 537 | "cpu_idle", |
538 | "cpu_startup_entry", | ||
530 | "intel_idle", | 539 | "intel_idle", |
531 | "default_idle", | 540 | "default_idle", |
532 | "native_safe_halt", | 541 | "native_safe_halt", |
@@ -1293,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | |||
1293 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; | 1302 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; |
1294 | 1303 | ||
1295 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 1304 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
1305 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | ||
1296 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 1306 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
1307 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | ||
1297 | /* | 1308 | /* |
1298 | * kernel modules know their symtab type - it's set when | 1309 | * kernel modules know their symtab type - it's set when |
1299 | * creating a module dso in machine__new_module(). | 1310 | * creating a module dso in machine__new_module(). |
@@ -1361,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1361 | return -1; | 1372 | return -1; |
1362 | 1373 | ||
1363 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | 1374 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || |
1364 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | 1375 | dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || |
1376 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE || | ||
1377 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
1365 | 1378 | ||
1366 | /* | 1379 | /* |
1367 | * Iterate over candidate debug images. | 1380 | * Iterate over candidate debug images. |
@@ -1468,8 +1481,7 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
1468 | if (vmlinux[0] == '/') | 1481 | if (vmlinux[0] == '/') |
1469 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); | 1482 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
1470 | else | 1483 | else |
1471 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1484 | symbol__join_symfs(symfs_vmlinux, vmlinux); |
1472 | symbol_conf.symfs, vmlinux); | ||
1473 | 1485 | ||
1474 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1486 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1475 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1487 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
@@ -1499,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1499 | symbol_filter_t filter) | 1511 | symbol_filter_t filter) |
1500 | { | 1512 | { |
1501 | int i, err = 0; | 1513 | int i, err = 0; |
1502 | char *filename; | 1514 | char *filename = NULL; |
1503 | |||
1504 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1505 | vmlinux_path__nr_entries + 1); | ||
1506 | 1515 | ||
1507 | filename = dso__build_id_filename(dso, NULL, 0); | 1516 | if (!symbol_conf.ignore_vmlinux_buildid) |
1517 | filename = dso__build_id_filename(dso, NULL, 0); | ||
1508 | if (filename != NULL) { | 1518 | if (filename != NULL) { |
1509 | err = dso__load_vmlinux(dso, map, filename, true, filter); | 1519 | err = dso__load_vmlinux(dso, map, filename, true, filter); |
1510 | if (err > 0) | 1520 | if (err > 0) |
@@ -1512,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1512 | free(filename); | 1522 | free(filename); |
1513 | } | 1523 | } |
1514 | 1524 | ||
1525 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1526 | vmlinux_path__nr_entries + 1); | ||
1527 | |||
1515 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1528 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
1516 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); | 1529 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); |
1517 | if (err > 0) | 1530 | if (err > 0) |
@@ -1745,12 +1758,13 @@ static void vmlinux_path__exit(void) | |||
1745 | zfree(&vmlinux_path); | 1758 | zfree(&vmlinux_path); |
1746 | } | 1759 | } |
1747 | 1760 | ||
1748 | static int vmlinux_path__init(void) | 1761 | static int vmlinux_path__init(struct perf_session_env *env) |
1749 | { | 1762 | { |
1750 | struct utsname uts; | 1763 | struct utsname uts; |
1751 | char bf[PATH_MAX]; | 1764 | char bf[PATH_MAX]; |
1765 | char *kernel_version; | ||
1752 | 1766 | ||
1753 | vmlinux_path = malloc(sizeof(char *) * 5); | 1767 | vmlinux_path = malloc(sizeof(char *) * 6); |
1754 | if (vmlinux_path == NULL) | 1768 | if (vmlinux_path == NULL) |
1755 | return -1; | 1769 | return -1; |
1756 | 1770 | ||
@@ -1763,25 +1777,37 @@ static int vmlinux_path__init(void) | |||
1763 | goto out_fail; | 1777 | goto out_fail; |
1764 | ++vmlinux_path__nr_entries; | 1778 | ++vmlinux_path__nr_entries; |
1765 | 1779 | ||
1766 | /* only try running kernel version if no symfs was given */ | 1780 | /* only try kernel version if no symfs was given */ |
1767 | if (symbol_conf.symfs[0] != 0) | 1781 | if (symbol_conf.symfs[0] != 0) |
1768 | return 0; | 1782 | return 0; |
1769 | 1783 | ||
1770 | if (uname(&uts) < 0) | 1784 | if (env) { |
1771 | return -1; | 1785 | kernel_version = env->os_release; |
1786 | } else { | ||
1787 | if (uname(&uts) < 0) | ||
1788 | goto out_fail; | ||
1789 | |||
1790 | kernel_version = uts.release; | ||
1791 | } | ||
1772 | 1792 | ||
1773 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | 1793 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); |
1774 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1794 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1775 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1795 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1776 | goto out_fail; | 1796 | goto out_fail; |
1777 | ++vmlinux_path__nr_entries; | 1797 | ++vmlinux_path__nr_entries; |
1778 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | 1798 | snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", |
1799 | kernel_version); | ||
1800 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1801 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1802 | goto out_fail; | ||
1803 | ++vmlinux_path__nr_entries; | ||
1804 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); | ||
1779 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1805 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1780 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1806 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1781 | goto out_fail; | 1807 | goto out_fail; |
1782 | ++vmlinux_path__nr_entries; | 1808 | ++vmlinux_path__nr_entries; |
1783 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | 1809 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", |
1784 | uts.release); | 1810 | kernel_version); |
1785 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1811 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1786 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1812 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1787 | goto out_fail; | 1813 | goto out_fail; |
@@ -1827,7 +1853,7 @@ static bool symbol__read_kptr_restrict(void) | |||
1827 | return value; | 1853 | return value; |
1828 | } | 1854 | } |
1829 | 1855 | ||
1830 | int symbol__init(void) | 1856 | int symbol__init(struct perf_session_env *env) |
1831 | { | 1857 | { |
1832 | const char *symfs; | 1858 | const char *symfs; |
1833 | 1859 | ||
@@ -1842,7 +1868,7 @@ int symbol__init(void) | |||
1842 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 1868 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
1843 | sizeof(struct symbol)); | 1869 | sizeof(struct symbol)); |
1844 | 1870 | ||
1845 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | 1871 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) |
1846 | return -1; | 1872 | return -1; |
1847 | 1873 | ||
1848 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | 1874 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index e7295e93cff9..9d602e9c6f59 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | #include "event.h" | 15 | #include "event.h" |
16 | #include "util.h" | ||
16 | 17 | ||
17 | #ifdef HAVE_LIBELF_SUPPORT | 18 | #ifdef HAVE_LIBELF_SUPPORT |
18 | #include <libelf.h> | 19 | #include <libelf.h> |
@@ -22,27 +23,6 @@ | |||
22 | 23 | ||
23 | #include "dso.h" | 24 | #include "dso.h" |
24 | 25 | ||
25 | #ifdef HAVE_CPLUS_DEMANGLE_SUPPORT | ||
26 | extern char *cplus_demangle(const char *, int); | ||
27 | |||
28 | static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i) | ||
29 | { | ||
30 | return cplus_demangle(c, i); | ||
31 | } | ||
32 | #else | ||
33 | #ifdef NO_DEMANGLE | ||
34 | static inline char *bfd_demangle(void __maybe_unused *v, | ||
35 | const char __maybe_unused *c, | ||
36 | int __maybe_unused i) | ||
37 | { | ||
38 | return NULL; | ||
39 | } | ||
40 | #else | ||
41 | #define PACKAGE 'perf' | ||
42 | #include <bfd.h> | ||
43 | #endif | ||
44 | #endif | ||
45 | |||
46 | /* | 26 | /* |
47 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 27 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
48 | * for newer versions we can use mmap to reduce memory usage: | 28 | * for newer versions we can use mmap to reduce memory usage: |
@@ -59,6 +39,7 @@ extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
59 | #endif | 39 | #endif |
60 | 40 | ||
61 | #ifndef DMGL_PARAMS | 41 | #ifndef DMGL_PARAMS |
42 | #define DMGL_NO_OPTS 0 /* For readability... */ | ||
62 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 43 | #define DMGL_PARAMS (1 << 0) /* Include function args */ |
63 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 44 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
64 | #endif | 45 | #endif |
@@ -93,7 +74,7 @@ void symbols__delete(struct rb_root *symbols); | |||
93 | 74 | ||
94 | static inline size_t symbol__size(const struct symbol *sym) | 75 | static inline size_t symbol__size(const struct symbol *sym) |
95 | { | 76 | { |
96 | return sym->end - sym->start + 1; | 77 | return sym->end - sym->start; |
97 | } | 78 | } |
98 | 79 | ||
99 | struct strlist; | 80 | struct strlist; |
@@ -103,6 +84,7 @@ struct symbol_conf { | |||
103 | unsigned short nr_events; | 84 | unsigned short nr_events; |
104 | bool try_vmlinux_path, | 85 | bool try_vmlinux_path, |
105 | ignore_vmlinux, | 86 | ignore_vmlinux, |
87 | ignore_vmlinux_buildid, | ||
106 | show_kernel_path, | 88 | show_kernel_path, |
107 | use_modules, | 89 | use_modules, |
108 | sort_by_name, | 90 | sort_by_name, |
@@ -118,8 +100,10 @@ struct symbol_conf { | |||
118 | annotate_src, | 100 | annotate_src, |
119 | event_group, | 101 | event_group, |
120 | demangle, | 102 | demangle, |
103 | demangle_kernel, | ||
121 | filter_relative, | 104 | filter_relative, |
122 | show_hist_headers; | 105 | show_hist_headers, |
106 | branch_callstack; | ||
123 | const char *vmlinux_name, | 107 | const char *vmlinux_name, |
124 | *kallsyms_name, | 108 | *kallsyms_name, |
125 | *source_prefix, | 109 | *source_prefix, |
@@ -143,6 +127,14 @@ struct symbol_conf { | |||
143 | }; | 127 | }; |
144 | 128 | ||
145 | extern struct symbol_conf symbol_conf; | 129 | extern struct symbol_conf symbol_conf; |
130 | |||
131 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) | ||
132 | { | ||
133 | return path__join(bf, size, symbol_conf.symfs, path); | ||
134 | } | ||
135 | |||
136 | #define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path) | ||
137 | |||
146 | extern int vmlinux_path__nr_entries; | 138 | extern int vmlinux_path__nr_entries; |
147 | extern char **vmlinux_path; | 139 | extern char **vmlinux_path; |
148 | 140 | ||
@@ -253,7 +245,8 @@ int modules__parse(const char *filename, void *arg, | |||
253 | int filename__read_debuglink(const char *filename, char *debuglink, | 245 | int filename__read_debuglink(const char *filename, char *debuglink, |
254 | size_t size); | 246 | size_t size); |
255 | 247 | ||
256 | int symbol__init(void); | 248 | struct perf_session_env; |
249 | int symbol__init(struct perf_session_env *env); | ||
257 | void symbol__exit(void); | 250 | void symbol__exit(void); |
258 | void symbol__elf_init(void); | 251 | void symbol__elf_init(void); |
259 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | 252 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); |
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c new file mode 100644 index 000000000000..9ed59a452d1f --- /dev/null +++ b/tools/perf/util/thread-stack.c | |||
@@ -0,0 +1,747 @@ | |||
1 | /* | ||
2 | * thread-stack.c: Synthesize a thread's stack using call / return events | ||
3 | * Copyright (c) 2014, 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 <linux/rbtree.h> | ||
17 | #include <linux/list.h> | ||
18 | #include "thread.h" | ||
19 | #include "event.h" | ||
20 | #include "machine.h" | ||
21 | #include "util.h" | ||
22 | #include "debug.h" | ||
23 | #include "symbol.h" | ||
24 | #include "comm.h" | ||
25 | #include "thread-stack.h" | ||
26 | |||
27 | #define CALL_PATH_BLOCK_SHIFT 8 | ||
28 | #define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) | ||
29 | #define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) | ||
30 | |||
31 | struct call_path_block { | ||
32 | struct call_path cp[CALL_PATH_BLOCK_SIZE]; | ||
33 | struct list_head node; | ||
34 | }; | ||
35 | |||
36 | /** | ||
37 | * struct call_path_root - root of all call paths. | ||
38 | * @call_path: root call path | ||
39 | * @blocks: list of blocks to store call paths | ||
40 | * @next: next free space | ||
41 | * @sz: number of spaces | ||
42 | */ | ||
43 | struct call_path_root { | ||
44 | struct call_path call_path; | ||
45 | struct list_head blocks; | ||
46 | size_t next; | ||
47 | size_t sz; | ||
48 | }; | ||
49 | |||
50 | /** | ||
51 | * struct call_return_processor - provides a call-back to consume call-return | ||
52 | * information. | ||
53 | * @cpr: call path root | ||
54 | * @process: call-back that accepts call/return information | ||
55 | * @data: anonymous data for call-back | ||
56 | */ | ||
57 | struct call_return_processor { | ||
58 | struct call_path_root *cpr; | ||
59 | int (*process)(struct call_return *cr, void *data); | ||
60 | void *data; | ||
61 | }; | ||
62 | |||
63 | #define STACK_GROWTH 2048 | ||
64 | |||
65 | /** | ||
66 | * struct thread_stack_entry - thread stack entry. | ||
67 | * @ret_addr: return address | ||
68 | * @timestamp: timestamp (if known) | ||
69 | * @ref: external reference (e.g. db_id of sample) | ||
70 | * @branch_count: the branch count when the entry was created | ||
71 | * @cp: call path | ||
72 | * @no_call: a 'call' was not seen | ||
73 | */ | ||
74 | struct thread_stack_entry { | ||
75 | u64 ret_addr; | ||
76 | u64 timestamp; | ||
77 | u64 ref; | ||
78 | u64 branch_count; | ||
79 | struct call_path *cp; | ||
80 | bool no_call; | ||
81 | }; | ||
82 | |||
83 | /** | ||
84 | * struct thread_stack - thread stack constructed from 'call' and 'return' | ||
85 | * branch samples. | ||
86 | * @stack: array that holds the stack | ||
87 | * @cnt: number of entries in the stack | ||
88 | * @sz: current maximum stack size | ||
89 | * @trace_nr: current trace number | ||
90 | * @branch_count: running branch count | ||
91 | * @kernel_start: kernel start address | ||
92 | * @last_time: last timestamp | ||
93 | * @crp: call/return processor | ||
94 | * @comm: current comm | ||
95 | */ | ||
96 | struct thread_stack { | ||
97 | struct thread_stack_entry *stack; | ||
98 | size_t cnt; | ||
99 | size_t sz; | ||
100 | u64 trace_nr; | ||
101 | u64 branch_count; | ||
102 | u64 kernel_start; | ||
103 | u64 last_time; | ||
104 | struct call_return_processor *crp; | ||
105 | struct comm *comm; | ||
106 | }; | ||
107 | |||
108 | static int thread_stack__grow(struct thread_stack *ts) | ||
109 | { | ||
110 | struct thread_stack_entry *new_stack; | ||
111 | size_t sz, new_sz; | ||
112 | |||
113 | new_sz = ts->sz + STACK_GROWTH; | ||
114 | sz = new_sz * sizeof(struct thread_stack_entry); | ||
115 | |||
116 | new_stack = realloc(ts->stack, sz); | ||
117 | if (!new_stack) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | ts->stack = new_stack; | ||
121 | ts->sz = new_sz; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static struct thread_stack *thread_stack__new(struct thread *thread, | ||
127 | struct call_return_processor *crp) | ||
128 | { | ||
129 | struct thread_stack *ts; | ||
130 | |||
131 | ts = zalloc(sizeof(struct thread_stack)); | ||
132 | if (!ts) | ||
133 | return NULL; | ||
134 | |||
135 | if (thread_stack__grow(ts)) { | ||
136 | free(ts); | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | if (thread->mg && thread->mg->machine) | ||
141 | ts->kernel_start = machine__kernel_start(thread->mg->machine); | ||
142 | else | ||
143 | ts->kernel_start = 1ULL << 63; | ||
144 | ts->crp = crp; | ||
145 | |||
146 | return ts; | ||
147 | } | ||
148 | |||
149 | static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | ||
150 | { | ||
151 | int err = 0; | ||
152 | |||
153 | if (ts->cnt == ts->sz) { | ||
154 | err = thread_stack__grow(ts); | ||
155 | if (err) { | ||
156 | pr_warning("Out of memory: discarding thread stack\n"); | ||
157 | ts->cnt = 0; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | ts->stack[ts->cnt++].ret_addr = ret_addr; | ||
162 | |||
163 | return err; | ||
164 | } | ||
165 | |||
166 | static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) | ||
167 | { | ||
168 | size_t i; | ||
169 | |||
170 | /* | ||
171 | * In some cases there may be functions which are not seen to return. | ||
172 | * For example when setjmp / longjmp has been used. Or the perf context | ||
173 | * switch in the kernel which doesn't stop and start tracing in exactly | ||
174 | * the same code path. When that happens the return address will be | ||
175 | * further down the stack. If the return address is not found at all, | ||
176 | * we assume the opposite (i.e. this is a return for a call that wasn't | ||
177 | * seen for some reason) and leave the stack alone. | ||
178 | */ | ||
179 | for (i = ts->cnt; i; ) { | ||
180 | if (ts->stack[--i].ret_addr == ret_addr) { | ||
181 | ts->cnt = i; | ||
182 | return; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static bool thread_stack__in_kernel(struct thread_stack *ts) | ||
188 | { | ||
189 | if (!ts->cnt) | ||
190 | return false; | ||
191 | |||
192 | return ts->stack[ts->cnt - 1].cp->in_kernel; | ||
193 | } | ||
194 | |||
195 | static int thread_stack__call_return(struct thread *thread, | ||
196 | struct thread_stack *ts, size_t idx, | ||
197 | u64 timestamp, u64 ref, bool no_return) | ||
198 | { | ||
199 | struct call_return_processor *crp = ts->crp; | ||
200 | struct thread_stack_entry *tse; | ||
201 | struct call_return cr = { | ||
202 | .thread = thread, | ||
203 | .comm = ts->comm, | ||
204 | .db_id = 0, | ||
205 | }; | ||
206 | |||
207 | tse = &ts->stack[idx]; | ||
208 | cr.cp = tse->cp; | ||
209 | cr.call_time = tse->timestamp; | ||
210 | cr.return_time = timestamp; | ||
211 | cr.branch_count = ts->branch_count - tse->branch_count; | ||
212 | cr.call_ref = tse->ref; | ||
213 | cr.return_ref = ref; | ||
214 | if (tse->no_call) | ||
215 | cr.flags |= CALL_RETURN_NO_CALL; | ||
216 | if (no_return) | ||
217 | cr.flags |= CALL_RETURN_NO_RETURN; | ||
218 | |||
219 | return crp->process(&cr, crp->data); | ||
220 | } | ||
221 | |||
222 | static int thread_stack__flush(struct thread *thread, struct thread_stack *ts) | ||
223 | { | ||
224 | struct call_return_processor *crp = ts->crp; | ||
225 | int err; | ||
226 | |||
227 | if (!crp) { | ||
228 | ts->cnt = 0; | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | while (ts->cnt) { | ||
233 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
234 | ts->last_time, 0, true); | ||
235 | if (err) { | ||
236 | pr_err("Error flushing thread stack!\n"); | ||
237 | ts->cnt = 0; | ||
238 | return err; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
246 | u64 to_ip, u16 insn_len, u64 trace_nr) | ||
247 | { | ||
248 | if (!thread) | ||
249 | return -EINVAL; | ||
250 | |||
251 | if (!thread->ts) { | ||
252 | thread->ts = thread_stack__new(thread, NULL); | ||
253 | if (!thread->ts) { | ||
254 | pr_warning("Out of memory: no thread stack\n"); | ||
255 | return -ENOMEM; | ||
256 | } | ||
257 | thread->ts->trace_nr = trace_nr; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * When the trace is discontinuous, the trace_nr changes. In that case | ||
262 | * the stack might be completely invalid. Better to report nothing than | ||
263 | * to report something misleading, so flush the stack. | ||
264 | */ | ||
265 | if (trace_nr != thread->ts->trace_nr) { | ||
266 | if (thread->ts->trace_nr) | ||
267 | thread_stack__flush(thread, thread->ts); | ||
268 | thread->ts->trace_nr = trace_nr; | ||
269 | } | ||
270 | |||
271 | /* Stop here if thread_stack__process() is in use */ | ||
272 | if (thread->ts->crp) | ||
273 | return 0; | ||
274 | |||
275 | if (flags & PERF_IP_FLAG_CALL) { | ||
276 | u64 ret_addr; | ||
277 | |||
278 | if (!to_ip) | ||
279 | return 0; | ||
280 | ret_addr = from_ip + insn_len; | ||
281 | if (ret_addr == to_ip) | ||
282 | return 0; /* Zero-length calls are excluded */ | ||
283 | return thread_stack__push(thread->ts, ret_addr); | ||
284 | } else if (flags & PERF_IP_FLAG_RETURN) { | ||
285 | if (!from_ip) | ||
286 | return 0; | ||
287 | thread_stack__pop(thread->ts, to_ip); | ||
288 | } | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr) | ||
294 | { | ||
295 | if (!thread || !thread->ts) | ||
296 | return; | ||
297 | |||
298 | if (trace_nr != thread->ts->trace_nr) { | ||
299 | if (thread->ts->trace_nr) | ||
300 | thread_stack__flush(thread, thread->ts); | ||
301 | thread->ts->trace_nr = trace_nr; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | void thread_stack__free(struct thread *thread) | ||
306 | { | ||
307 | if (thread->ts) { | ||
308 | thread_stack__flush(thread, thread->ts); | ||
309 | zfree(&thread->ts->stack); | ||
310 | zfree(&thread->ts); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
315 | size_t sz, u64 ip) | ||
316 | { | ||
317 | size_t i; | ||
318 | |||
319 | if (!thread || !thread->ts) | ||
320 | chain->nr = 1; | ||
321 | else | ||
322 | chain->nr = min(sz, thread->ts->cnt + 1); | ||
323 | |||
324 | chain->ips[0] = ip; | ||
325 | |||
326 | for (i = 1; i < chain->nr; i++) | ||
327 | chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; | ||
328 | } | ||
329 | |||
330 | static void call_path__init(struct call_path *cp, struct call_path *parent, | ||
331 | struct symbol *sym, u64 ip, bool in_kernel) | ||
332 | { | ||
333 | cp->parent = parent; | ||
334 | cp->sym = sym; | ||
335 | cp->ip = sym ? 0 : ip; | ||
336 | cp->db_id = 0; | ||
337 | cp->in_kernel = in_kernel; | ||
338 | RB_CLEAR_NODE(&cp->rb_node); | ||
339 | cp->children = RB_ROOT; | ||
340 | } | ||
341 | |||
342 | static struct call_path_root *call_path_root__new(void) | ||
343 | { | ||
344 | struct call_path_root *cpr; | ||
345 | |||
346 | cpr = zalloc(sizeof(struct call_path_root)); | ||
347 | if (!cpr) | ||
348 | return NULL; | ||
349 | call_path__init(&cpr->call_path, NULL, NULL, 0, false); | ||
350 | INIT_LIST_HEAD(&cpr->blocks); | ||
351 | return cpr; | ||
352 | } | ||
353 | |||
354 | static void call_path_root__free(struct call_path_root *cpr) | ||
355 | { | ||
356 | struct call_path_block *pos, *n; | ||
357 | |||
358 | list_for_each_entry_safe(pos, n, &cpr->blocks, node) { | ||
359 | list_del(&pos->node); | ||
360 | free(pos); | ||
361 | } | ||
362 | free(cpr); | ||
363 | } | ||
364 | |||
365 | static struct call_path *call_path__new(struct call_path_root *cpr, | ||
366 | struct call_path *parent, | ||
367 | struct symbol *sym, u64 ip, | ||
368 | bool in_kernel) | ||
369 | { | ||
370 | struct call_path_block *cpb; | ||
371 | struct call_path *cp; | ||
372 | size_t n; | ||
373 | |||
374 | if (cpr->next < cpr->sz) { | ||
375 | cpb = list_last_entry(&cpr->blocks, struct call_path_block, | ||
376 | node); | ||
377 | } else { | ||
378 | cpb = zalloc(sizeof(struct call_path_block)); | ||
379 | if (!cpb) | ||
380 | return NULL; | ||
381 | list_add_tail(&cpb->node, &cpr->blocks); | ||
382 | cpr->sz += CALL_PATH_BLOCK_SIZE; | ||
383 | } | ||
384 | |||
385 | n = cpr->next++ & CALL_PATH_BLOCK_MASK; | ||
386 | cp = &cpb->cp[n]; | ||
387 | |||
388 | call_path__init(cp, parent, sym, ip, in_kernel); | ||
389 | |||
390 | return cp; | ||
391 | } | ||
392 | |||
393 | static struct call_path *call_path__findnew(struct call_path_root *cpr, | ||
394 | struct call_path *parent, | ||
395 | struct symbol *sym, u64 ip, u64 ks) | ||
396 | { | ||
397 | struct rb_node **p; | ||
398 | struct rb_node *node_parent = NULL; | ||
399 | struct call_path *cp; | ||
400 | bool in_kernel = ip >= ks; | ||
401 | |||
402 | if (sym) | ||
403 | ip = 0; | ||
404 | |||
405 | if (!parent) | ||
406 | return call_path__new(cpr, parent, sym, ip, in_kernel); | ||
407 | |||
408 | p = &parent->children.rb_node; | ||
409 | while (*p != NULL) { | ||
410 | node_parent = *p; | ||
411 | cp = rb_entry(node_parent, struct call_path, rb_node); | ||
412 | |||
413 | if (cp->sym == sym && cp->ip == ip) | ||
414 | return cp; | ||
415 | |||
416 | if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) | ||
417 | p = &(*p)->rb_left; | ||
418 | else | ||
419 | p = &(*p)->rb_right; | ||
420 | } | ||
421 | |||
422 | cp = call_path__new(cpr, parent, sym, ip, in_kernel); | ||
423 | if (!cp) | ||
424 | return NULL; | ||
425 | |||
426 | rb_link_node(&cp->rb_node, node_parent, p); | ||
427 | rb_insert_color(&cp->rb_node, &parent->children); | ||
428 | |||
429 | return cp; | ||
430 | } | ||
431 | |||
432 | struct call_return_processor * | ||
433 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
434 | void *data) | ||
435 | { | ||
436 | struct call_return_processor *crp; | ||
437 | |||
438 | crp = zalloc(sizeof(struct call_return_processor)); | ||
439 | if (!crp) | ||
440 | return NULL; | ||
441 | crp->cpr = call_path_root__new(); | ||
442 | if (!crp->cpr) | ||
443 | goto out_free; | ||
444 | crp->process = process; | ||
445 | crp->data = data; | ||
446 | return crp; | ||
447 | |||
448 | out_free: | ||
449 | free(crp); | ||
450 | return NULL; | ||
451 | } | ||
452 | |||
453 | void call_return_processor__free(struct call_return_processor *crp) | ||
454 | { | ||
455 | if (crp) { | ||
456 | call_path_root__free(crp->cpr); | ||
457 | free(crp); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, | ||
462 | u64 timestamp, u64 ref, struct call_path *cp, | ||
463 | bool no_call) | ||
464 | { | ||
465 | struct thread_stack_entry *tse; | ||
466 | int err; | ||
467 | |||
468 | if (ts->cnt == ts->sz) { | ||
469 | err = thread_stack__grow(ts); | ||
470 | if (err) | ||
471 | return err; | ||
472 | } | ||
473 | |||
474 | tse = &ts->stack[ts->cnt++]; | ||
475 | tse->ret_addr = ret_addr; | ||
476 | tse->timestamp = timestamp; | ||
477 | tse->ref = ref; | ||
478 | tse->branch_count = ts->branch_count; | ||
479 | tse->cp = cp; | ||
480 | tse->no_call = no_call; | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts, | ||
486 | u64 ret_addr, u64 timestamp, u64 ref, | ||
487 | struct symbol *sym) | ||
488 | { | ||
489 | int err; | ||
490 | |||
491 | if (!ts->cnt) | ||
492 | return 1; | ||
493 | |||
494 | if (ts->cnt == 1) { | ||
495 | struct thread_stack_entry *tse = &ts->stack[0]; | ||
496 | |||
497 | if (tse->cp->sym == sym) | ||
498 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
499 | timestamp, ref, false); | ||
500 | } | ||
501 | |||
502 | if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) { | ||
503 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
504 | timestamp, ref, false); | ||
505 | } else { | ||
506 | size_t i = ts->cnt - 1; | ||
507 | |||
508 | while (i--) { | ||
509 | if (ts->stack[i].ret_addr != ret_addr) | ||
510 | continue; | ||
511 | i += 1; | ||
512 | while (ts->cnt > i) { | ||
513 | err = thread_stack__call_return(thread, ts, | ||
514 | --ts->cnt, | ||
515 | timestamp, ref, | ||
516 | true); | ||
517 | if (err) | ||
518 | return err; | ||
519 | } | ||
520 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
521 | timestamp, ref, false); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | return 1; | ||
526 | } | ||
527 | |||
528 | static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts, | ||
529 | struct perf_sample *sample, | ||
530 | struct addr_location *from_al, | ||
531 | struct addr_location *to_al, u64 ref) | ||
532 | { | ||
533 | struct call_path_root *cpr = ts->crp->cpr; | ||
534 | struct call_path *cp; | ||
535 | struct symbol *sym; | ||
536 | u64 ip; | ||
537 | |||
538 | if (sample->ip) { | ||
539 | ip = sample->ip; | ||
540 | sym = from_al->sym; | ||
541 | } else if (sample->addr) { | ||
542 | ip = sample->addr; | ||
543 | sym = to_al->sym; | ||
544 | } else { | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | cp = call_path__findnew(cpr, &cpr->call_path, sym, ip, | ||
549 | ts->kernel_start); | ||
550 | if (!cp) | ||
551 | return -ENOMEM; | ||
552 | |||
553 | return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp, | ||
554 | true); | ||
555 | } | ||
556 | |||
557 | static int thread_stack__no_call_return(struct thread *thread, | ||
558 | struct thread_stack *ts, | ||
559 | struct perf_sample *sample, | ||
560 | struct addr_location *from_al, | ||
561 | struct addr_location *to_al, u64 ref) | ||
562 | { | ||
563 | struct call_path_root *cpr = ts->crp->cpr; | ||
564 | struct call_path *cp, *parent; | ||
565 | u64 ks = ts->kernel_start; | ||
566 | int err; | ||
567 | |||
568 | if (sample->ip >= ks && sample->addr < ks) { | ||
569 | /* Return to userspace, so pop all kernel addresses */ | ||
570 | while (thread_stack__in_kernel(ts)) { | ||
571 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
572 | sample->time, ref, | ||
573 | true); | ||
574 | if (err) | ||
575 | return err; | ||
576 | } | ||
577 | |||
578 | /* If the stack is empty, push the userspace address */ | ||
579 | if (!ts->cnt) { | ||
580 | cp = call_path__findnew(cpr, &cpr->call_path, | ||
581 | to_al->sym, sample->addr, | ||
582 | ts->kernel_start); | ||
583 | if (!cp) | ||
584 | return -ENOMEM; | ||
585 | return thread_stack__push_cp(ts, 0, sample->time, ref, | ||
586 | cp, true); | ||
587 | } | ||
588 | } else if (thread_stack__in_kernel(ts) && sample->ip < ks) { | ||
589 | /* Return to userspace, so pop all kernel addresses */ | ||
590 | while (thread_stack__in_kernel(ts)) { | ||
591 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
592 | sample->time, ref, | ||
593 | true); | ||
594 | if (err) | ||
595 | return err; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | if (ts->cnt) | ||
600 | parent = ts->stack[ts->cnt - 1].cp; | ||
601 | else | ||
602 | parent = &cpr->call_path; | ||
603 | |||
604 | /* This 'return' had no 'call', so push and pop top of stack */ | ||
605 | cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip, | ||
606 | ts->kernel_start); | ||
607 | if (!cp) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp, | ||
611 | true); | ||
612 | if (err) | ||
613 | return err; | ||
614 | |||
615 | return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref, | ||
616 | to_al->sym); | ||
617 | } | ||
618 | |||
619 | static int thread_stack__trace_begin(struct thread *thread, | ||
620 | struct thread_stack *ts, u64 timestamp, | ||
621 | u64 ref) | ||
622 | { | ||
623 | struct thread_stack_entry *tse; | ||
624 | int err; | ||
625 | |||
626 | if (!ts->cnt) | ||
627 | return 0; | ||
628 | |||
629 | /* Pop trace end */ | ||
630 | tse = &ts->stack[ts->cnt - 1]; | ||
631 | if (tse->cp->sym == NULL && tse->cp->ip == 0) { | ||
632 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
633 | timestamp, ref, false); | ||
634 | if (err) | ||
635 | return err; | ||
636 | } | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static int thread_stack__trace_end(struct thread_stack *ts, | ||
642 | struct perf_sample *sample, u64 ref) | ||
643 | { | ||
644 | struct call_path_root *cpr = ts->crp->cpr; | ||
645 | struct call_path *cp; | ||
646 | u64 ret_addr; | ||
647 | |||
648 | /* No point having 'trace end' on the bottom of the stack */ | ||
649 | if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref)) | ||
650 | return 0; | ||
651 | |||
652 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0, | ||
653 | ts->kernel_start); | ||
654 | if (!cp) | ||
655 | return -ENOMEM; | ||
656 | |||
657 | ret_addr = sample->ip + sample->insn_len; | ||
658 | |||
659 | return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, | ||
660 | false); | ||
661 | } | ||
662 | |||
663 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
664 | struct perf_sample *sample, | ||
665 | struct addr_location *from_al, | ||
666 | struct addr_location *to_al, u64 ref, | ||
667 | struct call_return_processor *crp) | ||
668 | { | ||
669 | struct thread_stack *ts = thread->ts; | ||
670 | int err = 0; | ||
671 | |||
672 | if (ts) { | ||
673 | if (!ts->crp) { | ||
674 | /* Supersede thread_stack__event() */ | ||
675 | thread_stack__free(thread); | ||
676 | thread->ts = thread_stack__new(thread, crp); | ||
677 | if (!thread->ts) | ||
678 | return -ENOMEM; | ||
679 | ts = thread->ts; | ||
680 | ts->comm = comm; | ||
681 | } | ||
682 | } else { | ||
683 | thread->ts = thread_stack__new(thread, crp); | ||
684 | if (!thread->ts) | ||
685 | return -ENOMEM; | ||
686 | ts = thread->ts; | ||
687 | ts->comm = comm; | ||
688 | } | ||
689 | |||
690 | /* Flush stack on exec */ | ||
691 | if (ts->comm != comm && thread->pid_ == thread->tid) { | ||
692 | err = thread_stack__flush(thread, ts); | ||
693 | if (err) | ||
694 | return err; | ||
695 | ts->comm = comm; | ||
696 | } | ||
697 | |||
698 | /* If the stack is empty, put the current symbol on the stack */ | ||
699 | if (!ts->cnt) { | ||
700 | err = thread_stack__bottom(thread, ts, sample, from_al, to_al, | ||
701 | ref); | ||
702 | if (err) | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | ts->branch_count += 1; | ||
707 | ts->last_time = sample->time; | ||
708 | |||
709 | if (sample->flags & PERF_IP_FLAG_CALL) { | ||
710 | struct call_path_root *cpr = ts->crp->cpr; | ||
711 | struct call_path *cp; | ||
712 | u64 ret_addr; | ||
713 | |||
714 | if (!sample->ip || !sample->addr) | ||
715 | return 0; | ||
716 | |||
717 | ret_addr = sample->ip + sample->insn_len; | ||
718 | if (ret_addr == sample->addr) | ||
719 | return 0; /* Zero-length calls are excluded */ | ||
720 | |||
721 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, | ||
722 | to_al->sym, sample->addr, | ||
723 | ts->kernel_start); | ||
724 | if (!cp) | ||
725 | return -ENOMEM; | ||
726 | err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, | ||
727 | cp, false); | ||
728 | } else if (sample->flags & PERF_IP_FLAG_RETURN) { | ||
729 | if (!sample->ip || !sample->addr) | ||
730 | return 0; | ||
731 | |||
732 | err = thread_stack__pop_cp(thread, ts, sample->addr, | ||
733 | sample->time, ref, from_al->sym); | ||
734 | if (err) { | ||
735 | if (err < 0) | ||
736 | return err; | ||
737 | err = thread_stack__no_call_return(thread, ts, sample, | ||
738 | from_al, to_al, ref); | ||
739 | } | ||
740 | } else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) { | ||
741 | err = thread_stack__trace_begin(thread, ts, sample->time, ref); | ||
742 | } else if (sample->flags & PERF_IP_FLAG_TRACE_END) { | ||
743 | err = thread_stack__trace_end(ts, sample, ref); | ||
744 | } | ||
745 | |||
746 | return err; | ||
747 | } | ||
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h new file mode 100644 index 000000000000..b843bbef8ba2 --- /dev/null +++ b/tools/perf/util/thread-stack.h | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * thread-stack.h: Synthesize a thread's stack using call / return events | ||
3 | * Copyright (c) 2014, 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_THREAD_STACK_H | ||
17 | #define __PERF_THREAD_STACK_H | ||
18 | |||
19 | #include <sys/types.h> | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/rbtree.h> | ||
23 | |||
24 | struct thread; | ||
25 | struct comm; | ||
26 | struct ip_callchain; | ||
27 | struct symbol; | ||
28 | struct dso; | ||
29 | struct call_return_processor; | ||
30 | struct comm; | ||
31 | struct perf_sample; | ||
32 | struct addr_location; | ||
33 | |||
34 | /* | ||
35 | * Call/Return flags. | ||
36 | * | ||
37 | * CALL_RETURN_NO_CALL: 'return' but no matching 'call' | ||
38 | * CALL_RETURN_NO_RETURN: 'call' but no matching 'return' | ||
39 | */ | ||
40 | enum { | ||
41 | CALL_RETURN_NO_CALL = 1 << 0, | ||
42 | CALL_RETURN_NO_RETURN = 1 << 1, | ||
43 | }; | ||
44 | |||
45 | /** | ||
46 | * struct call_return - paired call/return information. | ||
47 | * @thread: thread in which call/return occurred | ||
48 | * @comm: comm in which call/return occurred | ||
49 | * @cp: call path | ||
50 | * @call_time: timestamp of call (if known) | ||
51 | * @return_time: timestamp of return (if known) | ||
52 | * @branch_count: number of branches seen between call and return | ||
53 | * @call_ref: external reference to 'call' sample (e.g. db_id) | ||
54 | * @return_ref: external reference to 'return' sample (e.g. db_id) | ||
55 | * @db_id: id used for db-export | ||
56 | * @flags: Call/Return flags | ||
57 | */ | ||
58 | struct call_return { | ||
59 | struct thread *thread; | ||
60 | struct comm *comm; | ||
61 | struct call_path *cp; | ||
62 | u64 call_time; | ||
63 | u64 return_time; | ||
64 | u64 branch_count; | ||
65 | u64 call_ref; | ||
66 | u64 return_ref; | ||
67 | u64 db_id; | ||
68 | u32 flags; | ||
69 | }; | ||
70 | |||
71 | /** | ||
72 | * struct call_path - node in list of calls leading to a function call. | ||
73 | * @parent: call path to the parent function call | ||
74 | * @sym: symbol of function called | ||
75 | * @ip: only if sym is null, the ip of the function | ||
76 | * @db_id: id used for db-export | ||
77 | * @in_kernel: whether function is a in the kernel | ||
78 | * @rb_node: node in parent's tree of called functions | ||
79 | * @children: tree of call paths of functions called | ||
80 | * | ||
81 | * In combination with the call_return structure, the call_path structure | ||
82 | * defines a context-sensitve call-graph. | ||
83 | */ | ||
84 | struct call_path { | ||
85 | struct call_path *parent; | ||
86 | struct symbol *sym; | ||
87 | u64 ip; | ||
88 | u64 db_id; | ||
89 | bool in_kernel; | ||
90 | struct rb_node rb_node; | ||
91 | struct rb_root children; | ||
92 | }; | ||
93 | |||
94 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
95 | u64 to_ip, u16 insn_len, u64 trace_nr); | ||
96 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr); | ||
97 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
98 | size_t sz, u64 ip); | ||
99 | void thread_stack__free(struct thread *thread); | ||
100 | |||
101 | struct call_return_processor * | ||
102 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
103 | void *data); | ||
104 | void call_return_processor__free(struct call_return_processor *crp); | ||
105 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
106 | struct perf_sample *sample, | ||
107 | struct addr_location *from_al, | ||
108 | struct addr_location *to_al, u64 ref, | ||
109 | struct call_return_processor *crp); | ||
110 | |||
111 | #endif | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 12c7a253a63c..9ebc8b1f9be5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -4,9 +4,11 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include "session.h" | 5 | #include "session.h" |
6 | #include "thread.h" | 6 | #include "thread.h" |
7 | #include "thread-stack.h" | ||
7 | #include "util.h" | 8 | #include "util.h" |
8 | #include "debug.h" | 9 | #include "debug.h" |
9 | #include "comm.h" | 10 | #include "comm.h" |
11 | #include "unwind.h" | ||
10 | 12 | ||
11 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | 13 | int thread__init_map_groups(struct thread *thread, struct machine *machine) |
12 | { | 14 | { |
@@ -14,7 +16,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine) | |||
14 | pid_t pid = thread->pid_; | 16 | pid_t pid = thread->pid_; |
15 | 17 | ||
16 | if (pid == thread->tid || pid == -1) { | 18 | if (pid == thread->tid || pid == -1) { |
17 | thread->mg = map_groups__new(); | 19 | thread->mg = map_groups__new(machine); |
18 | } else { | 20 | } else { |
19 | leader = machine__findnew_thread(machine, pid, pid); | 21 | leader = machine__findnew_thread(machine, pid, pid); |
20 | if (leader) | 22 | if (leader) |
@@ -37,17 +39,21 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
37 | thread->cpu = -1; | 39 | thread->cpu = -1; |
38 | INIT_LIST_HEAD(&thread->comm_list); | 40 | INIT_LIST_HEAD(&thread->comm_list); |
39 | 41 | ||
42 | if (unwind__prepare_access(thread) < 0) | ||
43 | goto err_thread; | ||
44 | |||
40 | comm_str = malloc(32); | 45 | comm_str = malloc(32); |
41 | if (!comm_str) | 46 | if (!comm_str) |
42 | goto err_thread; | 47 | goto err_thread; |
43 | 48 | ||
44 | snprintf(comm_str, 32, ":%d", tid); | 49 | snprintf(comm_str, 32, ":%d", tid); |
45 | comm = comm__new(comm_str, 0); | 50 | comm = comm__new(comm_str, 0, false); |
46 | free(comm_str); | 51 | free(comm_str); |
47 | if (!comm) | 52 | if (!comm) |
48 | goto err_thread; | 53 | goto err_thread; |
49 | 54 | ||
50 | list_add(&comm->list, &thread->comm_list); | 55 | list_add(&comm->list, &thread->comm_list); |
56 | |||
51 | } | 57 | } |
52 | 58 | ||
53 | return thread; | 59 | return thread; |
@@ -61,6 +67,8 @@ void thread__delete(struct thread *thread) | |||
61 | { | 67 | { |
62 | struct comm *comm, *tmp; | 68 | struct comm *comm, *tmp; |
63 | 69 | ||
70 | thread_stack__free(thread); | ||
71 | |||
64 | if (thread->mg) { | 72 | if (thread->mg) { |
65 | map_groups__put(thread->mg); | 73 | map_groups__put(thread->mg); |
66 | thread->mg = NULL; | 74 | thread->mg = NULL; |
@@ -69,6 +77,7 @@ void thread__delete(struct thread *thread) | |||
69 | list_del(&comm->list); | 77 | list_del(&comm->list); |
70 | comm__free(comm); | 78 | comm__free(comm); |
71 | } | 79 | } |
80 | unwind__finish_access(thread); | ||
72 | 81 | ||
73 | free(thread); | 82 | free(thread); |
74 | } | 83 | } |
@@ -81,22 +90,38 @@ struct comm *thread__comm(const struct thread *thread) | |||
81 | return list_first_entry(&thread->comm_list, struct comm, list); | 90 | return list_first_entry(&thread->comm_list, struct comm, list); |
82 | } | 91 | } |
83 | 92 | ||
84 | /* CHECKME: time should always be 0 if event aren't ordered */ | 93 | struct comm *thread__exec_comm(const struct thread *thread) |
85 | int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) | 94 | { |
95 | struct comm *comm, *last = NULL; | ||
96 | |||
97 | list_for_each_entry(comm, &thread->comm_list, list) { | ||
98 | if (comm->exec) | ||
99 | return comm; | ||
100 | last = comm; | ||
101 | } | ||
102 | |||
103 | return last; | ||
104 | } | ||
105 | |||
106 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, | ||
107 | bool exec) | ||
86 | { | 108 | { |
87 | struct comm *new, *curr = thread__comm(thread); | 109 | struct comm *new, *curr = thread__comm(thread); |
88 | int err; | 110 | int err; |
89 | 111 | ||
90 | /* Override latest entry if it had no specific time coverage */ | 112 | /* Override the default :tid entry */ |
91 | if (!curr->start) { | 113 | if (!thread->comm_set) { |
92 | err = comm__override(curr, str, timestamp); | 114 | err = comm__override(curr, str, timestamp, exec); |
93 | if (err) | 115 | if (err) |
94 | return err; | 116 | return err; |
95 | } else { | 117 | } else { |
96 | new = comm__new(str, timestamp); | 118 | new = comm__new(str, timestamp, exec); |
97 | if (!new) | 119 | if (!new) |
98 | return -ENOMEM; | 120 | return -ENOMEM; |
99 | list_add(&new->list, &thread->comm_list); | 121 | list_add(&new->list, &thread->comm_list); |
122 | |||
123 | if (exec) | ||
124 | unwind__flush_access(thread); | ||
100 | } | 125 | } |
101 | 126 | ||
102 | thread->comm_set = true; | 127 | thread->comm_set = true; |
@@ -175,7 +200,6 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
175 | } | 200 | } |
176 | 201 | ||
177 | void thread__find_cpumode_addr_location(struct thread *thread, | 202 | void thread__find_cpumode_addr_location(struct thread *thread, |
178 | struct machine *machine, | ||
179 | enum map_type type, u64 addr, | 203 | enum map_type type, u64 addr, |
180 | struct addr_location *al) | 204 | struct addr_location *al) |
181 | { | 205 | { |
@@ -188,8 +212,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, | |||
188 | }; | 212 | }; |
189 | 213 | ||
190 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { | 214 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { |
191 | thread__find_addr_location(thread, machine, cpumodes[i], type, | 215 | thread__find_addr_location(thread, cpumodes[i], type, addr, al); |
192 | addr, al); | ||
193 | if (al->map) | 216 | if (al->map) |
194 | break; | 217 | break; |
195 | } | 218 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 716b7723cce2..160fd066a7d1 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -8,6 +8,8 @@ | |||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include <strlist.h> | 9 | #include <strlist.h> |
10 | 10 | ||
11 | struct thread_stack; | ||
12 | |||
11 | struct thread { | 13 | struct thread { |
12 | union { | 14 | union { |
13 | struct rb_node rb_node; | 15 | struct rb_node rb_node; |
@@ -23,8 +25,10 @@ struct thread { | |||
23 | bool dead; /* if set thread has exited */ | 25 | bool dead; /* if set thread has exited */ |
24 | struct list_head comm_list; | 26 | struct list_head comm_list; |
25 | int comm_len; | 27 | int comm_len; |
28 | u64 db_id; | ||
26 | 29 | ||
27 | void *priv; | 30 | void *priv; |
31 | struct thread_stack *ts; | ||
28 | }; | 32 | }; |
29 | 33 | ||
30 | struct machine; | 34 | struct machine; |
@@ -38,24 +42,31 @@ static inline void thread__exited(struct thread *thread) | |||
38 | thread->dead = true; | 42 | thread->dead = true; |
39 | } | 43 | } |
40 | 44 | ||
41 | int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); | 45 | int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, |
46 | bool exec); | ||
47 | static inline int thread__set_comm(struct thread *thread, const char *comm, | ||
48 | u64 timestamp) | ||
49 | { | ||
50 | return __thread__set_comm(thread, comm, timestamp, false); | ||
51 | } | ||
52 | |||
42 | int thread__comm_len(struct thread *thread); | 53 | int thread__comm_len(struct thread *thread); |
43 | struct comm *thread__comm(const struct thread *thread); | 54 | struct comm *thread__comm(const struct thread *thread); |
55 | struct comm *thread__exec_comm(const struct thread *thread); | ||
44 | const char *thread__comm_str(const struct thread *thread); | 56 | const char *thread__comm_str(const struct thread *thread); |
45 | void thread__insert_map(struct thread *thread, struct map *map); | 57 | void thread__insert_map(struct thread *thread, struct map *map); |
46 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 58 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); |
47 | size_t thread__fprintf(struct thread *thread, FILE *fp); | 59 | size_t thread__fprintf(struct thread *thread, FILE *fp); |
48 | 60 | ||
49 | void thread__find_addr_map(struct thread *thread, struct machine *machine, | 61 | void thread__find_addr_map(struct thread *thread, |
50 | u8 cpumode, enum map_type type, u64 addr, | 62 | u8 cpumode, enum map_type type, u64 addr, |
51 | struct addr_location *al); | 63 | struct addr_location *al); |
52 | 64 | ||
53 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 65 | void thread__find_addr_location(struct thread *thread, |
54 | u8 cpumode, enum map_type type, u64 addr, | 66 | u8 cpumode, enum map_type type, u64 addr, |
55 | struct addr_location *al); | 67 | struct addr_location *al); |
56 | 68 | ||
57 | void thread__find_cpumode_addr_location(struct thread *thread, | 69 | void thread__find_cpumode_addr_location(struct thread *thread, |
58 | struct machine *machine, | ||
59 | enum map_type type, u64 addr, | 70 | enum map_type type, u64 addr, |
60 | struct addr_location *al); | 71 | struct addr_location *al); |
61 | 72 | ||
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 5d3215912105..f93b9734735b 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
@@ -214,6 +214,17 @@ out_free_threads: | |||
214 | goto out; | 214 | goto out; |
215 | } | 215 | } |
216 | 216 | ||
217 | struct thread_map *thread_map__new_dummy(void) | ||
218 | { | ||
219 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
220 | |||
221 | if (threads != NULL) { | ||
222 | threads->map[0] = -1; | ||
223 | threads->nr = 1; | ||
224 | } | ||
225 | return threads; | ||
226 | } | ||
227 | |||
217 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | 228 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) |
218 | { | 229 | { |
219 | struct thread_map *threads = NULL, *nt; | 230 | struct thread_map *threads = NULL, *nt; |
@@ -224,14 +235,8 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | |||
224 | struct strlist *slist; | 235 | struct strlist *slist; |
225 | 236 | ||
226 | /* perf-stat expects threads to be generated even if tid not given */ | 237 | /* perf-stat expects threads to be generated even if tid not given */ |
227 | if (!tid_str) { | 238 | if (!tid_str) |
228 | threads = malloc(sizeof(*threads) + sizeof(pid_t)); | 239 | return thread_map__new_dummy(); |
229 | if (threads != NULL) { | ||
230 | threads->map[0] = -1; | ||
231 | threads->nr = 1; | ||
232 | } | ||
233 | return threads; | ||
234 | } | ||
235 | 240 | ||
236 | slist = strlist__new(false, tid_str); | 241 | slist = strlist__new(false, tid_str); |
237 | if (!slist) | 242 | if (!slist) |
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 0cd8b3108084..95313f43cc0f 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -9,6 +9,7 @@ struct thread_map { | |||
9 | pid_t map[]; | 9 | pid_t map[]; |
10 | }; | 10 | }; |
11 | 11 | ||
12 | struct thread_map *thread_map__new_dummy(void); | ||
12 | struct thread_map *thread_map__new_by_pid(pid_t pid); | 13 | struct thread_map *thread_map__new_by_pid(pid_t pid); |
13 | struct thread_map *thread_map__new_by_tid(pid_t tid); | 14 | struct thread_map *thread_map__new_by_tid(pid_t tid); |
14 | struct thread_map *thread_map__new_by_uid(uid_t uid); | 15 | struct thread_map *thread_map__new_by_uid(uid_t uid); |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 4385816d3d49..bb2708bbfaca 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -39,8 +39,9 @@ struct perf_tool { | |||
39 | event_attr_op attr; | 39 | event_attr_op attr; |
40 | event_op2 tracing_data; | 40 | event_op2 tracing_data; |
41 | event_op2 finished_round, | 41 | event_op2 finished_round, |
42 | build_id; | 42 | build_id, |
43 | bool ordered_samples; | 43 | id_index; |
44 | bool ordered_events; | ||
44 | bool ordering_requires_timestamps; | 45 | bool ordering_requires_timestamps; |
45 | }; | 46 | }; |
46 | 47 | ||
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 57aaccc1692e..5c9bdd1591a9 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -30,6 +30,11 @@ | |||
30 | 30 | ||
31 | struct scripting_context *scripting_context; | 31 | struct scripting_context *scripting_context; |
32 | 32 | ||
33 | static int flush_script_unsupported(void) | ||
34 | { | ||
35 | return 0; | ||
36 | } | ||
37 | |||
33 | static int stop_script_unsupported(void) | 38 | static int stop_script_unsupported(void) |
34 | { | 39 | { |
35 | return 0; | 40 | return 0; |
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent | |||
74 | struct scripting_ops python_scripting_unsupported_ops = { | 79 | struct scripting_ops python_scripting_unsupported_ops = { |
75 | .name = "Python", | 80 | .name = "Python", |
76 | .start_script = python_start_script_unsupported, | 81 | .start_script = python_start_script_unsupported, |
82 | .flush_script = flush_script_unsupported, | ||
77 | .stop_script = stop_script_unsupported, | 83 | .stop_script = stop_script_unsupported, |
78 | .process_event = process_event_unsupported, | 84 | .process_event = process_event_unsupported, |
79 | .generate_script = python_generate_script_unsupported, | 85 | .generate_script = python_generate_script_unsupported, |
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent | |||
137 | struct scripting_ops perl_scripting_unsupported_ops = { | 143 | struct scripting_ops perl_scripting_unsupported_ops = { |
138 | .name = "Perl", | 144 | .name = "Perl", |
139 | .start_script = perl_start_script_unsupported, | 145 | .start_script = perl_start_script_unsupported, |
146 | .flush_script = flush_script_unsupported, | ||
140 | .stop_script = stop_script_unsupported, | 147 | .stop_script = stop_script_unsupported, |
141 | .process_event = process_event_unsupported, | 148 | .process_event = process_event_unsupported, |
142 | .generate_script = perl_generate_script_unsupported, | 149 | .generate_script = perl_generate_script_unsupported, |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 7b6d68688327..52aaa19e1eb1 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -64,6 +64,7 @@ struct perf_session; | |||
64 | struct scripting_ops { | 64 | struct scripting_ops { |
65 | const char *name; | 65 | const char *name; |
66 | int (*start_script) (const char *script, int argc, const char **argv); | 66 | int (*start_script) (const char *script, int argc, const char **argv); |
67 | int (*flush_script) (void); | ||
67 | int (*stop_script) (void); | 68 | int (*stop_script) (void); |
68 | void (*process_event) (union perf_event *event, | 69 | void (*process_event) (union perf_event *event, |
69 | struct perf_sample *sample, | 70 | struct perf_sample *sample, |
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 7419768c38b1..2dcfe9a7c8d0 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
@@ -26,7 +26,7 @@ static int __report_module(struct addr_location *al, u64 ip, | |||
26 | Dwfl_Module *mod; | 26 | Dwfl_Module *mod; |
27 | struct dso *dso = NULL; | 27 | struct dso *dso = NULL; |
28 | 28 | ||
29 | thread__find_addr_location(ui->thread, ui->machine, | 29 | thread__find_addr_location(ui->thread, |
30 | PERF_RECORD_MISC_USER, | 30 | PERF_RECORD_MISC_USER, |
31 | MAP__FUNCTION, ip, al); | 31 | MAP__FUNCTION, ip, al); |
32 | 32 | ||
@@ -89,7 +89,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, | |||
89 | struct addr_location al; | 89 | struct addr_location al; |
90 | ssize_t size; | 90 | ssize_t size; |
91 | 91 | ||
92 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 92 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
93 | MAP__FUNCTION, addr, &al); | 93 | MAP__FUNCTION, addr, &al); |
94 | if (!al.map) { | 94 | if (!al.map) { |
95 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | 95 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); |
@@ -164,14 +164,14 @@ frame_callback(Dwfl_Frame *state, void *arg) | |||
164 | } | 164 | } |
165 | 165 | ||
166 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 166 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
167 | struct machine *machine, struct thread *thread, | 167 | struct thread *thread, |
168 | struct perf_sample *data, | 168 | struct perf_sample *data, |
169 | int max_stack) | 169 | int max_stack) |
170 | { | 170 | { |
171 | struct unwind_info ui = { | 171 | struct unwind_info ui = { |
172 | .sample = data, | 172 | .sample = data, |
173 | .thread = thread, | 173 | .thread = thread, |
174 | .machine = machine, | 174 | .machine = thread->mg->machine, |
175 | .cb = cb, | 175 | .cb = cb, |
176 | .arg = arg, | 176 | .arg = arg, |
177 | .max_stack = max_stack, | 177 | .max_stack = max_stack, |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 92b56db52471..371219a6daf1 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | #include <libunwind.h> | 25 | #include <libunwind.h> |
26 | #include <libunwind-ptrace.h> | 26 | #include <libunwind-ptrace.h> |
27 | #include "callchain.h" | ||
27 | #include "thread.h" | 28 | #include "thread.h" |
28 | #include "session.h" | 29 | #include "session.h" |
29 | #include "perf_regs.h" | 30 | #include "perf_regs.h" |
@@ -283,7 +284,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | |||
283 | { | 284 | { |
284 | struct addr_location al; | 285 | struct addr_location al; |
285 | 286 | ||
286 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 287 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
287 | MAP__FUNCTION, ip, &al); | 288 | MAP__FUNCTION, ip, &al); |
288 | return al.map; | 289 | return al.map; |
289 | } | 290 | } |
@@ -373,7 +374,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, | |||
373 | struct addr_location al; | 374 | struct addr_location al; |
374 | ssize_t size; | 375 | ssize_t size; |
375 | 376 | ||
376 | thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, | 377 | thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, |
377 | MAP__FUNCTION, addr, &al); | 378 | MAP__FUNCTION, addr, &al); |
378 | if (!al.map) { | 379 | if (!al.map) { |
379 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); | 380 | pr_debug("unwind: no map for %lx\n", (unsigned long)addr); |
@@ -475,14 +476,13 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as, | |||
475 | pr_debug("unwind: put_unwind_info called\n"); | 476 | pr_debug("unwind: put_unwind_info called\n"); |
476 | } | 477 | } |
477 | 478 | ||
478 | static int entry(u64 ip, struct thread *thread, struct machine *machine, | 479 | static int entry(u64 ip, struct thread *thread, |
479 | unwind_entry_cb_t cb, void *arg) | 480 | unwind_entry_cb_t cb, void *arg) |
480 | { | 481 | { |
481 | struct unwind_entry e; | 482 | struct unwind_entry e; |
482 | struct addr_location al; | 483 | struct addr_location al; |
483 | 484 | ||
484 | thread__find_addr_location(thread, machine, | 485 | thread__find_addr_location(thread, PERF_RECORD_MISC_USER, |
485 | PERF_RECORD_MISC_USER, | ||
486 | MAP__FUNCTION, ip, &al); | 486 | MAP__FUNCTION, ip, &al); |
487 | 487 | ||
488 | e.ip = ip; | 488 | e.ip = ip; |
@@ -525,12 +525,12 @@ static unw_accessors_t accessors = { | |||
525 | .get_proc_name = get_proc_name, | 525 | .get_proc_name = get_proc_name, |
526 | }; | 526 | }; |
527 | 527 | ||
528 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 528 | int unwind__prepare_access(struct thread *thread) |
529 | void *arg, int max_stack) | ||
530 | { | 529 | { |
531 | unw_addr_space_t addr_space; | 530 | unw_addr_space_t addr_space; |
532 | unw_cursor_t c; | 531 | |
533 | int ret; | 532 | if (callchain_param.record_mode != CALLCHAIN_DWARF) |
533 | return 0; | ||
534 | 534 | ||
535 | addr_space = unw_create_addr_space(&accessors, 0); | 535 | addr_space = unw_create_addr_space(&accessors, 0); |
536 | if (!addr_space) { | 536 | if (!addr_space) { |
@@ -538,6 +538,45 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
538 | return -ENOMEM; | 538 | return -ENOMEM; |
539 | } | 539 | } |
540 | 540 | ||
541 | unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); | ||
542 | thread__set_priv(thread, addr_space); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | void unwind__flush_access(struct thread *thread) | ||
548 | { | ||
549 | unw_addr_space_t addr_space; | ||
550 | |||
551 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
552 | return; | ||
553 | |||
554 | addr_space = thread__priv(thread); | ||
555 | unw_flush_cache(addr_space, 0, 0); | ||
556 | } | ||
557 | |||
558 | void unwind__finish_access(struct thread *thread) | ||
559 | { | ||
560 | unw_addr_space_t addr_space; | ||
561 | |||
562 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
563 | return; | ||
564 | |||
565 | addr_space = thread__priv(thread); | ||
566 | unw_destroy_addr_space(addr_space); | ||
567 | } | ||
568 | |||
569 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | ||
570 | void *arg, int max_stack) | ||
571 | { | ||
572 | unw_addr_space_t addr_space; | ||
573 | unw_cursor_t c; | ||
574 | int ret; | ||
575 | |||
576 | addr_space = thread__priv(ui->thread); | ||
577 | if (addr_space == NULL) | ||
578 | return -1; | ||
579 | |||
541 | ret = unw_init_remote(&c, addr_space, ui); | 580 | ret = unw_init_remote(&c, addr_space, ui); |
542 | if (ret) | 581 | if (ret) |
543 | display_error(ret); | 582 | display_error(ret); |
@@ -546,22 +585,21 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
546 | unw_word_t ip; | 585 | unw_word_t ip; |
547 | 586 | ||
548 | unw_get_reg(&c, UNW_REG_IP, &ip); | 587 | unw_get_reg(&c, UNW_REG_IP, &ip); |
549 | ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; | 588 | ret = ip ? entry(ip, ui->thread, cb, arg) : 0; |
550 | } | 589 | } |
551 | 590 | ||
552 | unw_destroy_addr_space(addr_space); | ||
553 | return ret; | 591 | return ret; |
554 | } | 592 | } |
555 | 593 | ||
556 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 594 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
557 | struct machine *machine, struct thread *thread, | 595 | struct thread *thread, |
558 | struct perf_sample *data, int max_stack) | 596 | struct perf_sample *data, int max_stack) |
559 | { | 597 | { |
560 | u64 ip; | 598 | u64 ip; |
561 | struct unwind_info ui = { | 599 | struct unwind_info ui = { |
562 | .sample = data, | 600 | .sample = data, |
563 | .thread = thread, | 601 | .thread = thread, |
564 | .machine = machine, | 602 | .machine = thread->mg->machine, |
565 | }; | 603 | }; |
566 | int ret; | 604 | int ret; |
567 | 605 | ||
@@ -572,7 +610,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
572 | if (ret) | 610 | if (ret) |
573 | return ret; | 611 | return ret; |
574 | 612 | ||
575 | ret = entry(ip, thread, machine, cb, arg); | 613 | ret = entry(ip, thread, cb, arg); |
576 | if (ret) | 614 | if (ret) |
577 | return -ENOMEM; | 615 | return -ENOMEM; |
578 | 616 | ||
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index f03061260b4e..12790cf94618 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include "event.h" | 5 | #include "event.h" |
6 | #include "symbol.h" | 6 | #include "symbol.h" |
7 | #include "thread.h" | ||
7 | 8 | ||
8 | struct unwind_entry { | 9 | struct unwind_entry { |
9 | struct map *map; | 10 | struct map *map; |
@@ -15,23 +16,40 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); | |||
15 | 16 | ||
16 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 17 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
17 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 18 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
18 | struct machine *machine, | ||
19 | struct thread *thread, | 19 | struct thread *thread, |
20 | struct perf_sample *data, int max_stack); | 20 | struct perf_sample *data, int max_stack); |
21 | /* libunwind specific */ | 21 | /* libunwind specific */ |
22 | #ifdef HAVE_LIBUNWIND_SUPPORT | 22 | #ifdef HAVE_LIBUNWIND_SUPPORT |
23 | int libunwind__arch_reg_id(int regnum); | 23 | int libunwind__arch_reg_id(int regnum); |
24 | int unwind__prepare_access(struct thread *thread); | ||
25 | void unwind__flush_access(struct thread *thread); | ||
26 | void unwind__finish_access(struct thread *thread); | ||
27 | #else | ||
28 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | ||
29 | { | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static inline void unwind__flush_access(struct thread *thread __maybe_unused) {} | ||
34 | static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} | ||
24 | #endif | 35 | #endif |
25 | #else | 36 | #else |
26 | static inline int | 37 | static inline int |
27 | unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | 38 | unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, |
28 | void *arg __maybe_unused, | 39 | void *arg __maybe_unused, |
29 | struct machine *machine __maybe_unused, | ||
30 | struct thread *thread __maybe_unused, | 40 | struct thread *thread __maybe_unused, |
31 | struct perf_sample *data __maybe_unused, | 41 | struct perf_sample *data __maybe_unused, |
32 | int max_stack __maybe_unused) | 42 | int max_stack __maybe_unused) |
33 | { | 43 | { |
34 | return 0; | 44 | return 0; |
35 | } | 45 | } |
46 | |||
47 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | ||
48 | { | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static inline void unwind__flush_access(struct thread *thread __maybe_unused) {} | ||
53 | static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} | ||
36 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | 54 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ |
37 | #endif /* __UNWIND_H */ | 55 | #endif /* __UNWIND_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index e52e7461911b..b86744f29eef 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -13,6 +13,15 @@ | |||
13 | #include <limits.h> | 13 | #include <limits.h> |
14 | #include <byteswap.h> | 14 | #include <byteswap.h> |
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <unistd.h> | ||
17 | #include "callchain.h" | ||
18 | |||
19 | struct callchain_param callchain_param = { | ||
20 | .mode = CHAIN_GRAPH_REL, | ||
21 | .min_percent = 0.5, | ||
22 | .order = ORDER_CALLEE, | ||
23 | .key = CCKEY_FUNCTION | ||
24 | }; | ||
16 | 25 | ||
17 | /* | 26 | /* |
18 | * XXX We need to find a better place for these things... | 27 | * XXX We need to find a better place for these things... |
@@ -282,6 +291,18 @@ void get_term_dimensions(struct winsize *ws) | |||
282 | ws->ws_col = 80; | 291 | ws->ws_col = 80; |
283 | } | 292 | } |
284 | 293 | ||
294 | void set_term_quiet_input(struct termios *old) | ||
295 | { | ||
296 | struct termios tc; | ||
297 | |||
298 | tcgetattr(0, old); | ||
299 | tc = *old; | ||
300 | tc.c_lflag &= ~(ICANON | ECHO); | ||
301 | tc.c_cc[VMIN] = 0; | ||
302 | tc.c_cc[VTIME] = 0; | ||
303 | tcsetattr(0, TCSANOW, &tc); | ||
304 | } | ||
305 | |||
285 | static void set_tracing_events_path(const char *mountpoint) | 306 | static void set_tracing_events_path(const char *mountpoint) |
286 | { | 307 | { |
287 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 308 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
@@ -421,28 +442,12 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) | |||
421 | return (unsigned long) -1; | 442 | return (unsigned long) -1; |
422 | } | 443 | } |
423 | 444 | ||
424 | int filename__read_int(const char *filename, int *value) | ||
425 | { | ||
426 | char line[64]; | ||
427 | int fd = open(filename, O_RDONLY), err = -1; | ||
428 | |||
429 | if (fd < 0) | ||
430 | return -1; | ||
431 | |||
432 | if (read(fd, line, sizeof(line)) > 0) { | ||
433 | *value = atoi(line); | ||
434 | err = 0; | ||
435 | } | ||
436 | |||
437 | close(fd); | ||
438 | return err; | ||
439 | } | ||
440 | |||
441 | int filename__read_str(const char *filename, char **buf, size_t *sizep) | 445 | int filename__read_str(const char *filename, char **buf, size_t *sizep) |
442 | { | 446 | { |
443 | size_t size = 0, alloc_size = 0; | 447 | size_t size = 0, alloc_size = 0; |
444 | void *bf = NULL, *nbf; | 448 | void *bf = NULL, *nbf; |
445 | int fd, n, err = 0; | 449 | int fd, n, err = 0; |
450 | char sbuf[STRERR_BUFSIZE]; | ||
446 | 451 | ||
447 | fd = open(filename, O_RDONLY); | 452 | fd = open(filename, O_RDONLY); |
448 | if (fd < 0) | 453 | if (fd < 0) |
@@ -463,8 +468,8 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
463 | n = read(fd, bf + size, alloc_size - size); | 468 | n = read(fd, bf + size, alloc_size - size); |
464 | if (n < 0) { | 469 | if (n < 0) { |
465 | if (size) { | 470 | if (size) { |
466 | pr_warning("read failed %d: %s\n", | 471 | pr_warning("read failed %d: %s\n", errno, |
467 | errno, strerror(errno)); | 472 | strerror_r(errno, sbuf, sizeof(sbuf))); |
468 | err = 0; | 473 | err = 0; |
469 | } else | 474 | } else |
470 | err = -errno; | 475 | err = -errno; |
@@ -501,16 +506,9 @@ const char *get_filename_for_perf_kvm(void) | |||
501 | 506 | ||
502 | int perf_event_paranoid(void) | 507 | int perf_event_paranoid(void) |
503 | { | 508 | { |
504 | char path[PATH_MAX]; | ||
505 | const char *procfs = procfs__mountpoint(); | ||
506 | int value; | 509 | int value; |
507 | 510 | ||
508 | if (!procfs) | 511 | if (sysctl__read_int("kernel/perf_event_paranoid", &value)) |
509 | return INT_MAX; | ||
510 | |||
511 | scnprintf(path, PATH_MAX, "%s/sys/kernel/perf_event_paranoid", procfs); | ||
512 | |||
513 | if (filename__read_int(path, &value)) | ||
514 | return INT_MAX; | 512 | return INT_MAX; |
515 | 513 | ||
516 | return value; | 514 | return value; |
@@ -536,3 +534,39 @@ void mem_bswap_64(void *src, int byte_size) | |||
536 | ++m; | 534 | ++m; |
537 | } | 535 | } |
538 | } | 536 | } |
537 | |||
538 | bool find_process(const char *name) | ||
539 | { | ||
540 | size_t len = strlen(name); | ||
541 | DIR *dir; | ||
542 | struct dirent *d; | ||
543 | int ret = -1; | ||
544 | |||
545 | dir = opendir(procfs__mountpoint()); | ||
546 | if (!dir) | ||
547 | return -1; | ||
548 | |||
549 | /* Walk through the directory. */ | ||
550 | while (ret && (d = readdir(dir)) != NULL) { | ||
551 | char path[PATH_MAX]; | ||
552 | char *data; | ||
553 | size_t size; | ||
554 | |||
555 | if ((d->d_type != DT_DIR) || | ||
556 | !strcmp(".", d->d_name) || | ||
557 | !strcmp("..", d->d_name)) | ||
558 | continue; | ||
559 | |||
560 | scnprintf(path, sizeof(path), "%s/%s/comm", | ||
561 | procfs__mountpoint(), d->d_name); | ||
562 | |||
563 | if (filename__read_str(path, &data, &size)) | ||
564 | continue; | ||
565 | |||
566 | ret = strncmp(name, data, len); | ||
567 | free(data); | ||
568 | } | ||
569 | |||
570 | closedir(dir); | ||
571 | return ret ? false : true; | ||
572 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 66864364ccb4..027a5153495c 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -39,6 +39,8 @@ | |||
39 | 39 | ||
40 | #define _ALL_SOURCE 1 | 40 | #define _ALL_SOURCE 1 |
41 | #define _BSD_SOURCE 1 | 41 | #define _BSD_SOURCE 1 |
42 | /* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */ | ||
43 | #define _DEFAULT_SOURCE 1 | ||
42 | #define HAS_BOOL | 44 | #define HAS_BOOL |
43 | 45 | ||
44 | #include <unistd.h> | 46 | #include <unistd.h> |
@@ -64,16 +66,18 @@ | |||
64 | #include <regex.h> | 66 | #include <regex.h> |
65 | #include <utime.h> | 67 | #include <utime.h> |
66 | #include <sys/wait.h> | 68 | #include <sys/wait.h> |
67 | #include <sys/poll.h> | 69 | #include <poll.h> |
68 | #include <sys/socket.h> | 70 | #include <sys/socket.h> |
69 | #include <sys/ioctl.h> | 71 | #include <sys/ioctl.h> |
70 | #include <inttypes.h> | 72 | #include <inttypes.h> |
73 | #include <linux/kernel.h> | ||
71 | #include <linux/magic.h> | 74 | #include <linux/magic.h> |
72 | #include <linux/types.h> | 75 | #include <linux/types.h> |
73 | #include <sys/ttydefaults.h> | 76 | #include <sys/ttydefaults.h> |
74 | #include <api/fs/debugfs.h> | 77 | #include <api/fs/debugfs.h> |
75 | #include <termios.h> | 78 | #include <termios.h> |
76 | #include <linux/bitops.h> | 79 | #include <linux/bitops.h> |
80 | #include <termios.h> | ||
77 | 81 | ||
78 | extern const char *graph_line; | 82 | extern const char *graph_line; |
79 | extern const char *graph_dotted_line; | 83 | extern const char *graph_dotted_line; |
@@ -149,8 +153,7 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))) | |||
149 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 153 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
150 | 154 | ||
151 | extern int prefixcmp(const char *str, const char *prefix); | 155 | extern int prefixcmp(const char *str, const char *prefix); |
152 | extern void set_buildid_dir(void); | 156 | extern void set_buildid_dir(const char *dir); |
153 | extern void disable_buildid_cache(void); | ||
154 | 157 | ||
155 | static inline const char *skip_prefix(const char *str, const char *prefix) | 158 | static inline const char *skip_prefix(const char *str, const char *prefix) |
156 | { | 159 | { |
@@ -266,35 +269,6 @@ void event_attr_init(struct perf_event_attr *attr); | |||
266 | #define _STR(x) #x | 269 | #define _STR(x) #x |
267 | #define STR(x) _STR(x) | 270 | #define STR(x) _STR(x) |
268 | 271 | ||
269 | /* | ||
270 | * Determine whether some value is a power of two, where zero is | ||
271 | * *not* considered a power of two. | ||
272 | */ | ||
273 | |||
274 | static inline __attribute__((const)) | ||
275 | bool is_power_of_2(unsigned long n) | ||
276 | { | ||
277 | return (n != 0 && ((n & (n - 1)) == 0)); | ||
278 | } | ||
279 | |||
280 | static inline unsigned next_pow2(unsigned x) | ||
281 | { | ||
282 | if (!x) | ||
283 | return 1; | ||
284 | return 1ULL << (32 - __builtin_clz(x - 1)); | ||
285 | } | ||
286 | |||
287 | static inline unsigned long next_pow2_l(unsigned long x) | ||
288 | { | ||
289 | #if BITS_PER_LONG == 64 | ||
290 | if (x <= (1UL << 31)) | ||
291 | return next_pow2(x); | ||
292 | return (unsigned long)next_pow2(x >> 32) << 32; | ||
293 | #else | ||
294 | return next_pow2(x); | ||
295 | #endif | ||
296 | } | ||
297 | |||
298 | size_t hex_width(u64 v); | 272 | size_t hex_width(u64 v); |
299 | int hex2u64(const char *ptr, u64 *val); | 273 | int hex2u64(const char *ptr, u64 *val); |
300 | 274 | ||
@@ -307,6 +281,7 @@ extern unsigned int page_size; | |||
307 | extern int cacheline_size; | 281 | extern int cacheline_size; |
308 | 282 | ||
309 | void get_term_dimensions(struct winsize *ws); | 283 | void get_term_dimensions(struct winsize *ws); |
284 | void set_term_quiet_input(struct termios *old); | ||
310 | 285 | ||
311 | struct parse_tag { | 286 | struct parse_tag { |
312 | char tag; | 287 | char tag; |
@@ -317,12 +292,28 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags); | |||
317 | 292 | ||
318 | #define SRCLINE_UNKNOWN ((char *) "??:0") | 293 | #define SRCLINE_UNKNOWN ((char *) "??:0") |
319 | 294 | ||
295 | static inline int path__join(char *bf, size_t size, | ||
296 | const char *path1, const char *path2) | ||
297 | { | ||
298 | return scnprintf(bf, size, "%s%s%s", path1, path1[0] ? "/" : "", path2); | ||
299 | } | ||
300 | |||
301 | static inline int path__join3(char *bf, size_t size, | ||
302 | const char *path1, const char *path2, | ||
303 | const char *path3) | ||
304 | { | ||
305 | return scnprintf(bf, size, "%s%s%s%s%s", | ||
306 | path1, path1[0] ? "/" : "", | ||
307 | path2, path2[0] ? "/" : "", path3); | ||
308 | } | ||
309 | |||
320 | struct dso; | 310 | struct dso; |
311 | struct symbol; | ||
321 | 312 | ||
322 | char *get_srcline(struct dso *dso, unsigned long addr); | 313 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
314 | bool show_sym); | ||
323 | void free_srcline(char *srcline); | 315 | void free_srcline(char *srcline); |
324 | 316 | ||
325 | int filename__read_int(const char *filename, int *value); | ||
326 | int filename__read_str(const char *filename, char **buf, size_t *sizep); | 317 | int filename__read_str(const char *filename, char **buf, size_t *sizep); |
327 | int perf_event_paranoid(void); | 318 | int perf_event_paranoid(void); |
328 | 319 | ||
@@ -330,4 +321,10 @@ void mem_bswap_64(void *src, int byte_size); | |||
330 | void mem_bswap_32(void *src, int byte_size); | 321 | void mem_bswap_32(void *src, int byte_size); |
331 | 322 | ||
332 | const char *get_filename_for_perf_kvm(void); | 323 | const char *get_filename_for_perf_kvm(void); |
324 | bool find_process(const char *name); | ||
325 | |||
326 | #ifdef HAVE_ZLIB_SUPPORT | ||
327 | int gzip_decompress_to_file(const char *input, int output_fd); | ||
328 | #endif | ||
329 | |||
333 | #endif /* GIT_COMPAT_UTIL_H */ | 330 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index adca69384fcc..5c7dd796979d 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c | |||
@@ -12,9 +12,16 @@ | |||
12 | #include "util.h" | 12 | #include "util.h" |
13 | #include "symbol.h" | 13 | #include "symbol.h" |
14 | #include "machine.h" | 14 | #include "machine.h" |
15 | #include "thread.h" | ||
15 | #include "linux/string.h" | 16 | #include "linux/string.h" |
16 | #include "debug.h" | 17 | #include "debug.h" |
17 | 18 | ||
19 | /* | ||
20 | * Include definition of find_vdso_map() also used in perf-read-vdso.c for | ||
21 | * building perf-read-vdso32 and perf-read-vdsox32. | ||
22 | */ | ||
23 | #include "find-vdso-map.c" | ||
24 | |||
18 | #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" | 25 | #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" |
19 | 26 | ||
20 | struct vdso_file { | 27 | struct vdso_file { |
@@ -22,10 +29,15 @@ struct vdso_file { | |||
22 | bool error; | 29 | bool error; |
23 | char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; | 30 | char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; |
24 | const char *dso_name; | 31 | const char *dso_name; |
32 | const char *read_prog; | ||
25 | }; | 33 | }; |
26 | 34 | ||
27 | struct vdso_info { | 35 | struct vdso_info { |
28 | struct vdso_file vdso; | 36 | struct vdso_file vdso; |
37 | #if BITS_PER_LONG == 64 | ||
38 | struct vdso_file vdso32; | ||
39 | struct vdso_file vdsox32; | ||
40 | #endif | ||
29 | }; | 41 | }; |
30 | 42 | ||
31 | static struct vdso_info *vdso_info__new(void) | 43 | static struct vdso_info *vdso_info__new(void) |
@@ -35,42 +47,23 @@ static struct vdso_info *vdso_info__new(void) | |||
35 | .temp_file_name = VDSO__TEMP_FILE_NAME, | 47 | .temp_file_name = VDSO__TEMP_FILE_NAME, |
36 | .dso_name = DSO__NAME_VDSO, | 48 | .dso_name = DSO__NAME_VDSO, |
37 | }, | 49 | }, |
50 | #if BITS_PER_LONG == 64 | ||
51 | .vdso32 = { | ||
52 | .temp_file_name = VDSO__TEMP_FILE_NAME, | ||
53 | .dso_name = DSO__NAME_VDSO32, | ||
54 | .read_prog = "perf-read-vdso32", | ||
55 | }, | ||
56 | .vdsox32 = { | ||
57 | .temp_file_name = VDSO__TEMP_FILE_NAME, | ||
58 | .dso_name = DSO__NAME_VDSOX32, | ||
59 | .read_prog = "perf-read-vdsox32", | ||
60 | }, | ||
61 | #endif | ||
38 | }; | 62 | }; |
39 | 63 | ||
40 | return memdup(&vdso_info_init, sizeof(vdso_info_init)); | 64 | return memdup(&vdso_info_init, sizeof(vdso_info_init)); |
41 | } | 65 | } |
42 | 66 | ||
43 | static int find_vdso_map(void **start, void **end) | ||
44 | { | ||
45 | FILE *maps; | ||
46 | char line[128]; | ||
47 | int found = 0; | ||
48 | |||
49 | maps = fopen("/proc/self/maps", "r"); | ||
50 | if (!maps) { | ||
51 | pr_err("vdso: cannot open maps\n"); | ||
52 | return -1; | ||
53 | } | ||
54 | |||
55 | while (!found && fgets(line, sizeof(line), maps)) { | ||
56 | int m = -1; | ||
57 | |||
58 | /* We care only about private r-x mappings. */ | ||
59 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | ||
60 | start, end, &m)) | ||
61 | continue; | ||
62 | if (m < 0) | ||
63 | continue; | ||
64 | |||
65 | if (!strncmp(&line[m], VDSO__MAP_NAME, | ||
66 | sizeof(VDSO__MAP_NAME) - 1)) | ||
67 | found = 1; | ||
68 | } | ||
69 | |||
70 | fclose(maps); | ||
71 | return !found; | ||
72 | } | ||
73 | |||
74 | static char *get_file(struct vdso_file *vdso_file) | 67 | static char *get_file(struct vdso_file *vdso_file) |
75 | { | 68 | { |
76 | char *vdso = NULL; | 69 | char *vdso = NULL; |
@@ -117,6 +110,12 @@ void vdso__exit(struct machine *machine) | |||
117 | 110 | ||
118 | if (vdso_info->vdso.found) | 111 | if (vdso_info->vdso.found) |
119 | unlink(vdso_info->vdso.temp_file_name); | 112 | unlink(vdso_info->vdso.temp_file_name); |
113 | #if BITS_PER_LONG == 64 | ||
114 | if (vdso_info->vdso32.found) | ||
115 | unlink(vdso_info->vdso32.temp_file_name); | ||
116 | if (vdso_info->vdsox32.found) | ||
117 | unlink(vdso_info->vdsox32.temp_file_name); | ||
118 | #endif | ||
120 | 119 | ||
121 | zfree(&machine->vdso_info); | 120 | zfree(&machine->vdso_info); |
122 | } | 121 | } |
@@ -135,6 +134,153 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name, | |||
135 | return dso; | 134 | return dso; |
136 | } | 135 | } |
137 | 136 | ||
137 | #if BITS_PER_LONG == 64 | ||
138 | |||
139 | static enum dso_type machine__thread_dso_type(struct machine *machine, | ||
140 | struct thread *thread) | ||
141 | { | ||
142 | enum dso_type dso_type = DSO__TYPE_UNKNOWN; | ||
143 | struct map *map; | ||
144 | struct dso *dso; | ||
145 | |||
146 | map = map_groups__first(thread->mg, MAP__FUNCTION); | ||
147 | for (; map ; map = map_groups__next(map)) { | ||
148 | dso = map->dso; | ||
149 | if (!dso || dso->long_name[0] != '/') | ||
150 | continue; | ||
151 | dso_type = dso__type(dso, machine); | ||
152 | if (dso_type != DSO__TYPE_UNKNOWN) | ||
153 | break; | ||
154 | } | ||
155 | |||
156 | return dso_type; | ||
157 | } | ||
158 | |||
159 | static int vdso__do_copy_compat(FILE *f, int fd) | ||
160 | { | ||
161 | char buf[4096]; | ||
162 | size_t count; | ||
163 | |||
164 | while (1) { | ||
165 | count = fread(buf, 1, sizeof(buf), f); | ||
166 | if (ferror(f)) | ||
167 | return -errno; | ||
168 | if (feof(f)) | ||
169 | break; | ||
170 | if (count && writen(fd, buf, count) != (ssize_t)count) | ||
171 | return -errno; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int vdso__copy_compat(const char *prog, int fd) | ||
178 | { | ||
179 | FILE *f; | ||
180 | int err; | ||
181 | |||
182 | f = popen(prog, "r"); | ||
183 | if (!f) | ||
184 | return -errno; | ||
185 | |||
186 | err = vdso__do_copy_compat(f, fd); | ||
187 | |||
188 | if (pclose(f) == -1) | ||
189 | return -errno; | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | static int vdso__create_compat_file(const char *prog, char *temp_name) | ||
195 | { | ||
196 | int fd, err; | ||
197 | |||
198 | fd = mkstemp(temp_name); | ||
199 | if (fd < 0) | ||
200 | return -errno; | ||
201 | |||
202 | err = vdso__copy_compat(prog, fd); | ||
203 | |||
204 | if (close(fd) == -1) | ||
205 | return -errno; | ||
206 | |||
207 | return err; | ||
208 | } | ||
209 | |||
210 | static const char *vdso__get_compat_file(struct vdso_file *vdso_file) | ||
211 | { | ||
212 | int err; | ||
213 | |||
214 | if (vdso_file->found) | ||
215 | return vdso_file->temp_file_name; | ||
216 | |||
217 | if (vdso_file->error) | ||
218 | return NULL; | ||
219 | |||
220 | err = vdso__create_compat_file(vdso_file->read_prog, | ||
221 | vdso_file->temp_file_name); | ||
222 | if (err) { | ||
223 | pr_err("%s failed, error %d\n", vdso_file->read_prog, err); | ||
224 | vdso_file->error = true; | ||
225 | return NULL; | ||
226 | } | ||
227 | |||
228 | vdso_file->found = true; | ||
229 | |||
230 | return vdso_file->temp_file_name; | ||
231 | } | ||
232 | |||
233 | static struct dso *vdso__findnew_compat(struct machine *machine, | ||
234 | struct vdso_file *vdso_file) | ||
235 | { | ||
236 | const char *file_name; | ||
237 | struct dso *dso; | ||
238 | |||
239 | dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true); | ||
240 | if (dso) | ||
241 | return dso; | ||
242 | |||
243 | file_name = vdso__get_compat_file(vdso_file); | ||
244 | if (!file_name) | ||
245 | return NULL; | ||
246 | |||
247 | return vdso__new(machine, vdso_file->dso_name, file_name); | ||
248 | } | ||
249 | |||
250 | static int vdso__dso_findnew_compat(struct machine *machine, | ||
251 | struct thread *thread, | ||
252 | struct vdso_info *vdso_info, | ||
253 | struct dso **dso) | ||
254 | { | ||
255 | enum dso_type dso_type; | ||
256 | |||
257 | dso_type = machine__thread_dso_type(machine, thread); | ||
258 | |||
259 | #ifndef HAVE_PERF_READ_VDSO32 | ||
260 | if (dso_type == DSO__TYPE_32BIT) | ||
261 | return 0; | ||
262 | #endif | ||
263 | #ifndef HAVE_PERF_READ_VDSOX32 | ||
264 | if (dso_type == DSO__TYPE_X32BIT) | ||
265 | return 0; | ||
266 | #endif | ||
267 | |||
268 | switch (dso_type) { | ||
269 | case DSO__TYPE_32BIT: | ||
270 | *dso = vdso__findnew_compat(machine, &vdso_info->vdso32); | ||
271 | return 1; | ||
272 | case DSO__TYPE_X32BIT: | ||
273 | *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32); | ||
274 | return 1; | ||
275 | case DSO__TYPE_UNKNOWN: | ||
276 | case DSO__TYPE_64BIT: | ||
277 | default: | ||
278 | return 0; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | #endif | ||
283 | |||
138 | struct dso *vdso__dso_findnew(struct machine *machine, | 284 | struct dso *vdso__dso_findnew(struct machine *machine, |
139 | struct thread *thread __maybe_unused) | 285 | struct thread *thread __maybe_unused) |
140 | { | 286 | { |
@@ -148,6 +294,11 @@ struct dso *vdso__dso_findnew(struct machine *machine, | |||
148 | if (!vdso_info) | 294 | if (!vdso_info) |
149 | return NULL; | 295 | return NULL; |
150 | 296 | ||
297 | #if BITS_PER_LONG == 64 | ||
298 | if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso)) | ||
299 | return dso; | ||
300 | #endif | ||
301 | |||
151 | dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); | 302 | dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); |
152 | if (!dso) { | 303 | if (!dso) { |
153 | char *file; | 304 | char *file; |
@@ -164,5 +315,7 @@ struct dso *vdso__dso_findnew(struct machine *machine, | |||
164 | 315 | ||
165 | bool dso__is_vdso(struct dso *dso) | 316 | bool dso__is_vdso(struct dso *dso) |
166 | { | 317 | { |
167 | return !strcmp(dso->short_name, DSO__NAME_VDSO); | 318 | return !strcmp(dso->short_name, DSO__NAME_VDSO) || |
319 | !strcmp(dso->short_name, DSO__NAME_VDSO32) || | ||
320 | !strcmp(dso->short_name, DSO__NAME_VDSOX32); | ||
168 | } | 321 | } |
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h index af9d6929a215..d97da1616f0c 100644 --- a/tools/perf/util/vdso.h +++ b/tools/perf/util/vdso.h | |||
@@ -7,7 +7,9 @@ | |||
7 | 7 | ||
8 | #define VDSO__MAP_NAME "[vdso]" | 8 | #define VDSO__MAP_NAME "[vdso]" |
9 | 9 | ||
10 | #define DSO__NAME_VDSO "[vdso]" | 10 | #define DSO__NAME_VDSO "[vdso]" |
11 | #define DSO__NAME_VDSO32 "[vdso32]" | ||
12 | #define DSO__NAME_VDSOX32 "[vdsox32]" | ||
11 | 13 | ||
12 | static inline bool is_vdso_map(const char *filename) | 14 | static inline bool is_vdso_map(const char *filename) |
13 | { | 15 | { |
diff --git a/tools/perf/util/zlib.c b/tools/perf/util/zlib.c new file mode 100644 index 000000000000..495a449fc25c --- /dev/null +++ b/tools/perf/util/zlib.c | |||
@@ -0,0 +1,78 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <sys/mman.h> | ||
5 | #include <zlib.h> | ||
6 | |||
7 | #include "util/util.h" | ||
8 | #include "util/debug.h" | ||
9 | |||
10 | |||
11 | #define CHUNK_SIZE 16384 | ||
12 | |||
13 | int gzip_decompress_to_file(const char *input, int output_fd) | ||
14 | { | ||
15 | int ret = Z_STREAM_ERROR; | ||
16 | int input_fd; | ||
17 | void *ptr; | ||
18 | int len; | ||
19 | struct stat stbuf; | ||
20 | unsigned char buf[CHUNK_SIZE]; | ||
21 | z_stream zs = { | ||
22 | .zalloc = Z_NULL, | ||
23 | .zfree = Z_NULL, | ||
24 | .opaque = Z_NULL, | ||
25 | .avail_in = 0, | ||
26 | .next_in = Z_NULL, | ||
27 | }; | ||
28 | |||
29 | input_fd = open(input, O_RDONLY); | ||
30 | if (input_fd < 0) | ||
31 | return -1; | ||
32 | |||
33 | if (fstat(input_fd, &stbuf) < 0) | ||
34 | goto out_close; | ||
35 | |||
36 | ptr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0); | ||
37 | if (ptr == MAP_FAILED) | ||
38 | goto out_close; | ||
39 | |||
40 | if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) | ||
41 | goto out_unmap; | ||
42 | |||
43 | zs.next_in = ptr; | ||
44 | zs.avail_in = stbuf.st_size; | ||
45 | |||
46 | do { | ||
47 | zs.next_out = buf; | ||
48 | zs.avail_out = CHUNK_SIZE; | ||
49 | |||
50 | ret = inflate(&zs, Z_NO_FLUSH); | ||
51 | switch (ret) { | ||
52 | case Z_NEED_DICT: | ||
53 | ret = Z_DATA_ERROR; | ||
54 | /* fall through */ | ||
55 | case Z_DATA_ERROR: | ||
56 | case Z_MEM_ERROR: | ||
57 | goto out; | ||
58 | default: | ||
59 | break; | ||
60 | } | ||
61 | |||
62 | len = CHUNK_SIZE - zs.avail_out; | ||
63 | if (writen(output_fd, buf, len) != len) { | ||
64 | ret = Z_DATA_ERROR; | ||
65 | goto out; | ||
66 | } | ||
67 | |||
68 | } while (ret != Z_STREAM_END); | ||
69 | |||
70 | out: | ||
71 | inflateEnd(&zs); | ||
72 | out_unmap: | ||
73 | munmap(ptr, stbuf.st_size); | ||
74 | out_close: | ||
75 | close(input_fd); | ||
76 | |||
77 | return ret == Z_STREAM_END ? 0 : -1; | ||
78 | } | ||