diff options
Diffstat (limited to 'tools/perf')
173 files changed, 11206 insertions, 2308 deletions
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 3ba1c0b09908..098cfb9ca8f0 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | include ../../scripts/Makefile.include | 1 | include ../../scripts/Makefile.include |
2 | include ../config/utilities.mak | 2 | include ../../scripts/utilities.mak |
3 | 3 | ||
4 | MAN1_TXT= \ | 4 | MAN1_TXT= \ |
5 | $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ | 5 | $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ |
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index b9ca1e304158..15949e2a7805 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt | |||
@@ -8,7 +8,7 @@ perf-config - Get and set variables in a configuration file. | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf config' -l | --list | 11 | 'perf config' [<file-option>] -l | --list |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
@@ -21,6 +21,14 @@ OPTIONS | |||
21 | --list:: | 21 | --list:: |
22 | Show current config variables, name and value, for all sections. | 22 | Show current config variables, name and value, for all sections. |
23 | 23 | ||
24 | --user:: | ||
25 | For writing and reading options: write to user | ||
26 | '$HOME/.perfconfig' file or read it. | ||
27 | |||
28 | --system:: | ||
29 | For writing and reading options: write to system-wide | ||
30 | '$(sysconfdir)/perfconfig' or read it. | ||
31 | |||
24 | CONFIGURATION FILE | 32 | CONFIGURATION FILE |
25 | ------------------ | 33 | ------------------ |
26 | 34 | ||
@@ -30,6 +38,10 @@ The '$HOME/.perfconfig' file is used to store a per-user configuration. | |||
30 | The file '$(sysconfdir)/perfconfig' can be used to | 38 | The file '$(sysconfdir)/perfconfig' can be used to |
31 | store a system-wide default configuration. | 39 | store a system-wide default configuration. |
32 | 40 | ||
41 | When reading or writing, the values are read from the system and user | ||
42 | configuration files by default, and options '--system' and '--user' | ||
43 | can be used to tell the command to read from or write to only that location. | ||
44 | |||
33 | Syntax | 45 | Syntax |
34 | ~~~~~~ | 46 | ~~~~~~ |
35 | 47 | ||
@@ -62,7 +74,7 @@ Given a $HOME/.perfconfig like this: | |||
62 | medium = green, default | 74 | medium = green, default |
63 | normal = lightgray, default | 75 | normal = lightgray, default |
64 | selected = white, lightgray | 76 | selected = white, lightgray |
65 | code = blue, default | 77 | jump_arrows = blue, default |
66 | addr = magenta, default | 78 | addr = magenta, default |
67 | root = white, blue | 79 | root = white, blue |
68 | 80 | ||
@@ -98,6 +110,347 @@ Given a $HOME/.perfconfig like this: | |||
98 | order = caller | 110 | order = caller |
99 | sort-key = function | 111 | sort-key = function |
100 | 112 | ||
113 | Variables | ||
114 | ~~~~~~~~~ | ||
115 | |||
116 | colors.*:: | ||
117 | The variables for customizing the colors used in the output for the | ||
118 | 'report', 'top' and 'annotate' in the TUI. They should specify the | ||
119 | foreground and background colors, separated by a comma, for example: | ||
120 | |||
121 | medium = green, lightgray | ||
122 | |||
123 | If you want to use the color configured for you terminal, just leave it | ||
124 | as 'default', for example: | ||
125 | |||
126 | medium = default, lightgray | ||
127 | |||
128 | Available colors: | ||
129 | red, yellow, green, cyan, gray, black, blue, | ||
130 | white, default, magenta, lightgray | ||
131 | |||
132 | colors.top:: | ||
133 | 'top' means a overhead percentage which is more than 5%. | ||
134 | And values of this variable specify percentage colors. | ||
135 | Basic key values are foreground-color 'red' and | ||
136 | background-color 'default'. | ||
137 | colors.medium:: | ||
138 | 'medium' means a overhead percentage which has more than 0.5%. | ||
139 | Default values are 'green' and 'default'. | ||
140 | colors.normal:: | ||
141 | 'normal' means the rest of overhead percentages | ||
142 | except 'top', 'medium', 'selected'. | ||
143 | Default values are 'lightgray' and 'default'. | ||
144 | colors.selected:: | ||
145 | This selects the colors for the current entry in a list of entries | ||
146 | from sub-commands (top, report, annotate). | ||
147 | Default values are 'black' and 'lightgray'. | ||
148 | colors.jump_arrows:: | ||
149 | Colors for jump arrows on assembly code listings | ||
150 | such as 'jns', 'jmp', 'jane', etc. | ||
151 | Default values are 'blue', 'default'. | ||
152 | colors.addr:: | ||
153 | This selects colors for addresses from 'annotate'. | ||
154 | Default values are 'magenta', 'default'. | ||
155 | colors.root:: | ||
156 | Colors for headers in the output of a sub-commands (top, report). | ||
157 | Default values are 'white', 'blue'. | ||
158 | |||
159 | tui.*, gtk.*:: | ||
160 | Subcommands that can be configured here are 'top', 'report' and 'annotate'. | ||
161 | These values are booleans, for example: | ||
162 | |||
163 | [tui] | ||
164 | top = true | ||
165 | |||
166 | will make the TUI be the default for the 'top' subcommand. Those will be | ||
167 | available if the required libs were detected at tool build time. | ||
168 | |||
169 | buildid.*:: | ||
170 | buildid.dir:: | ||
171 | Each executable and shared library in modern distributions comes with a | ||
172 | content based identifier that, if available, will be inserted in a | ||
173 | 'perf.data' file header to, at analysis time find what is needed to do | ||
174 | symbol resolution, code annotation, etc. | ||
175 | |||
176 | The recording tools also stores a hard link or copy in a per-user | ||
177 | directory, $HOME/.debug/, of binaries, shared libraries, /proc/kallsyms | ||
178 | and /proc/kcore files to be used at analysis time. | ||
179 | |||
180 | The buildid.dir variable can be used to either change this directory | ||
181 | cache location, or to disable it altogether. If you want to disable it, | ||
182 | set buildid.dir to /dev/null. The default is $HOME/.debug | ||
183 | |||
184 | annotate.*:: | ||
185 | These options work only for TUI. | ||
186 | These are in control of addresses, jump function, source code | ||
187 | in lines of assembly code from a specific program. | ||
188 | |||
189 | annotate.hide_src_code:: | ||
190 | If a program which is analyzed has source code, | ||
191 | this option lets 'annotate' print a list of assembly code with the source code. | ||
192 | For example, let's see a part of a program. There're four lines. | ||
193 | If this option is 'true', they can be printed | ||
194 | without source code from a program as below. | ||
195 | |||
196 | │ push %rbp | ||
197 | │ mov %rsp,%rbp | ||
198 | │ sub $0x10,%rsp | ||
199 | │ mov (%rdi),%rdx | ||
200 | |||
201 | But if this option is 'false', source code of the part | ||
202 | can be also printed as below. Default is 'false'. | ||
203 | |||
204 | │ struct rb_node *rb_next(const struct rb_node *node) | ||
205 | │ { | ||
206 | │ push %rbp | ||
207 | │ mov %rsp,%rbp | ||
208 | │ sub $0x10,%rsp | ||
209 | │ struct rb_node *parent; | ||
210 | │ | ||
211 | │ if (RB_EMPTY_NODE(node)) | ||
212 | │ mov (%rdi),%rdx | ||
213 | │ return n; | ||
214 | |||
215 | annotate.use_offset:: | ||
216 | Basing on a first address of a loaded function, offset can be used. | ||
217 | Instead of using original addresses of assembly code, | ||
218 | addresses subtracted from a base address can be printed. | ||
219 | Let's illustrate an example. | ||
220 | If a base address is 0XFFFFFFFF81624d50 as below, | ||
221 | |||
222 | ffffffff81624d50 <load0> | ||
223 | |||
224 | an address on assembly code has a specific absolute address as below | ||
225 | |||
226 | ffffffff816250b8:│ mov 0x8(%r14),%rdi | ||
227 | |||
228 | but if use_offset is 'true', an address subtracted from a base address is printed. | ||
229 | Default is true. This option is only applied to TUI. | ||
230 | |||
231 | 368:│ mov 0x8(%r14),%rdi | ||
232 | |||
233 | annotate.jump_arrows:: | ||
234 | There can be jump instruction among assembly code. | ||
235 | Depending on a boolean value of jump_arrows, | ||
236 | arrows can be printed or not which represent | ||
237 | where do the instruction jump into as below. | ||
238 | |||
239 | │ ┌──jmp 1333 | ||
240 | │ │ xchg %ax,%ax | ||
241 | │1330:│ mov %r15,%r10 | ||
242 | │1333:└─→cmp %r15,%r14 | ||
243 | |||
244 | If jump_arrow is 'false', the arrows isn't printed as below. | ||
245 | Default is 'false'. | ||
246 | |||
247 | │ ↓ jmp 1333 | ||
248 | │ xchg %ax,%ax | ||
249 | │1330: mov %r15,%r10 | ||
250 | │1333: cmp %r15,%r14 | ||
251 | |||
252 | annotate.show_linenr:: | ||
253 | When showing source code if this option is 'true', | ||
254 | line numbers are printed as below. | ||
255 | |||
256 | │1628 if (type & PERF_SAMPLE_IDENTIFIER) { | ||
257 | │ ↓ jne 508 | ||
258 | │1628 data->id = *array; | ||
259 | │1629 array++; | ||
260 | │1630 } | ||
261 | |||
262 | However if this option is 'false', they aren't printed as below. | ||
263 | Default is 'false'. | ||
264 | |||
265 | │ if (type & PERF_SAMPLE_IDENTIFIER) { | ||
266 | │ ↓ jne 508 | ||
267 | │ data->id = *array; | ||
268 | │ array++; | ||
269 | │ } | ||
270 | |||
271 | annotate.show_nr_jumps:: | ||
272 | Let's see a part of assembly code. | ||
273 | |||
274 | │1382: movb $0x1,-0x270(%rbp) | ||
275 | |||
276 | If use this, the number of branches jumping to that address can be printed as below. | ||
277 | Default is 'false'. | ||
278 | |||
279 | │1 1382: movb $0x1,-0x270(%rbp) | ||
280 | |||
281 | annotate.show_total_period:: | ||
282 | To compare two records on an instruction base, with this option | ||
283 | provided, display total number of samples that belong to a line | ||
284 | in assembly code. If this option is 'true', total periods are printed | ||
285 | instead of percent values as below. | ||
286 | |||
287 | 302 │ mov %eax,%eax | ||
288 | |||
289 | But if this option is 'false', percent values for overhead are printed i.e. | ||
290 | Default is 'false'. | ||
291 | |||
292 | 99.93 │ mov %eax,%eax | ||
293 | |||
294 | hist.*:: | ||
295 | hist.percentage:: | ||
296 | This option control the way to calculate overhead of filtered entries - | ||
297 | that means the value of this option is effective only if there's a | ||
298 | filter (by comm, dso or symbol name). Suppose a following example: | ||
299 | |||
300 | Overhead Symbols | ||
301 | ........ ....... | ||
302 | 33.33% foo | ||
303 | 33.33% bar | ||
304 | 33.33% baz | ||
305 | |||
306 | This is an original overhead and we'll filter out the first 'foo' | ||
307 | entry. The value of 'relative' would increase the overhead of 'bar' | ||
308 | and 'baz' to 50.00% for each, while 'absolute' would show their | ||
309 | current overhead (33.33%). | ||
310 | |||
311 | ui.*:: | ||
312 | ui.show-headers:: | ||
313 | This option controls display of column headers (like 'Overhead' and 'Symbol') | ||
314 | in 'report' and 'top'. If this option is false, they are hidden. | ||
315 | This option is only applied to TUI. | ||
316 | |||
317 | call-graph.*:: | ||
318 | When sub-commands 'top' and 'report' work with -g/—-children | ||
319 | there're options in control of call-graph. | ||
320 | |||
321 | call-graph.record-mode:: | ||
322 | The record-mode can be 'fp' (frame pointer), 'dwarf' and 'lbr'. | ||
323 | The value of 'dwarf' is effective only if perf detect needed library | ||
324 | (libunwind or a recent version of libdw). | ||
325 | 'lbr' only work for cpus that support it. | ||
326 | |||
327 | call-graph.dump-size:: | ||
328 | The size of stack to dump in order to do post-unwinding. Default is 8192 (byte). | ||
329 | When using dwarf into record-mode, the default size will be used if omitted. | ||
330 | |||
331 | call-graph.print-type:: | ||
332 | The print-types can be graph (graph absolute), fractal (graph relative), | ||
333 | flat and folded. This option controls a way to show overhead for each callchain | ||
334 | entry. Suppose a following example. | ||
335 | |||
336 | Overhead Symbols | ||
337 | ........ ....... | ||
338 | 40.00% foo | ||
339 | | | ||
340 | ---foo | ||
341 | | | ||
342 | |--50.00%--bar | ||
343 | | main | ||
344 | | | ||
345 | --50.00%--baz | ||
346 | main | ||
347 | |||
348 | This output is a 'fractal' format. The 'foo' came from 'bar' and 'baz' exactly | ||
349 | half and half so 'fractal' shows 50.00% for each | ||
350 | (meaning that it assumes 100% total overhead of 'foo'). | ||
351 | |||
352 | The 'graph' uses absolute overhead value of 'foo' as total so each of | ||
353 | 'bar' and 'baz' callchain will have 20.00% of overhead. | ||
354 | If 'flat' is used, single column and linear exposure of call chains. | ||
355 | 'folded' mean call chains are displayed in a line, separated by semicolons. | ||
356 | |||
357 | call-graph.order:: | ||
358 | This option controls print order of callchains. The default is | ||
359 | 'callee' which means callee is printed at top and then followed by its | ||
360 | caller and so on. The 'caller' prints it in reverse order. | ||
361 | |||
362 | If this option is not set and report.children or top.children is | ||
363 | set to true (or the equivalent command line option is given), | ||
364 | the default value of this option is changed to 'caller' for the | ||
365 | execution of 'perf report' or 'perf top'. Other commands will | ||
366 | still default to 'callee'. | ||
367 | |||
368 | call-graph.sort-key:: | ||
369 | The callchains are merged if they contain same information. | ||
370 | The sort-key option determines a way to compare the callchains. | ||
371 | A value of 'sort-key' can be 'function' or 'address'. | ||
372 | The default is 'function'. | ||
373 | |||
374 | call-graph.threshold:: | ||
375 | When there're many callchains it'd print tons of lines. So perf omits | ||
376 | small callchains under a certain overhead (threshold) and this option | ||
377 | control the threshold. Default is 0.5 (%). The overhead is calculated | ||
378 | by value depends on call-graph.print-type. | ||
379 | |||
380 | call-graph.print-limit:: | ||
381 | This is a maximum number of lines of callchain printed for a single | ||
382 | histogram entry. Default is 0 which means no limitation. | ||
383 | |||
384 | report.*:: | ||
385 | report.percent-limit:: | ||
386 | This one is mostly the same as call-graph.threshold but works for | ||
387 | histogram entries. Entries having an overhead lower than this | ||
388 | percentage will not be printed. Default is '0'. If percent-limit | ||
389 | is '10', only entries which have more than 10% of overhead will be | ||
390 | printed. | ||
391 | |||
392 | report.queue-size:: | ||
393 | This option sets up the maximum allocation size of the internal | ||
394 | event queue for ordering events. Default is 0, meaning no limit. | ||
395 | |||
396 | report.children:: | ||
397 | 'Children' means functions called from another function. | ||
398 | If this option is true, 'perf report' cumulates callchains of children | ||
399 | and show (accumulated) total overhead as well as 'Self' overhead. | ||
400 | Please refer to the 'perf report' manual. The default is 'true'. | ||
401 | |||
402 | report.group:: | ||
403 | This option is to show event group information together. | ||
404 | Example output with this turned on, notice that there is one column | ||
405 | per event in the group, ref-cycles and cycles: | ||
406 | |||
407 | # group: {ref-cycles,cycles} | ||
408 | # ======== | ||
409 | # | ||
410 | # Samples: 7K of event 'anon group { ref-cycles, cycles }' | ||
411 | # Event count (approx.): 6876107743 | ||
412 | # | ||
413 | # Overhead Command Shared Object Symbol | ||
414 | # ................ ....... ................. ................... | ||
415 | # | ||
416 | 99.84% 99.76% noploop noploop [.] main | ||
417 | 0.07% 0.00% noploop ld-2.15.so [.] strcmp | ||
418 | 0.03% 0.00% noploop [kernel.kallsyms] [k] timerqueue_del | ||
419 | |||
420 | top.*:: | ||
421 | top.children:: | ||
422 | Same as 'report.children'. So if it is enabled, the output of 'top' | ||
423 | command will have 'Children' overhead column as well as 'Self' overhead | ||
424 | column by default. | ||
425 | The default is 'true'. | ||
426 | |||
427 | man.*:: | ||
428 | man.viewer:: | ||
429 | This option can assign a tool to view manual pages when 'help' | ||
430 | subcommand was invoked. Supported tools are 'man', 'woman' | ||
431 | (with emacs client) and 'konqueror'. Default is 'man'. | ||
432 | |||
433 | New man viewer tool can be also added using 'man.<tool>.cmd' | ||
434 | or use different path using 'man.<tool>.path' config option. | ||
435 | |||
436 | pager.*:: | ||
437 | pager.<subcommand>:: | ||
438 | When the subcommand is run on stdio, determine whether it uses | ||
439 | pager or not based on this value. Default is 'unspecified'. | ||
440 | |||
441 | kmem.*:: | ||
442 | kmem.default:: | ||
443 | This option decides which allocator is to be analyzed if neither | ||
444 | '--slab' nor '--page' option is used. Default is 'slab'. | ||
445 | |||
446 | record.*:: | ||
447 | record.build-id:: | ||
448 | This option can be 'cache', 'no-cache' or 'skip'. | ||
449 | 'cache' is to post-process data and save/update the binaries into | ||
450 | the build-id cache (in ~/.debug). This is the default. | ||
451 | But if this option is 'no-cache', it will not update the build-id cache. | ||
452 | 'skip' skips post-processing and does not update the cache. | ||
453 | |||
101 | SEE ALSO | 454 | SEE ALSO |
102 | -------- | 455 | -------- |
103 | linkperf:perf[1] | 456 | linkperf:perf[1] |
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index 0b1cedeef895..87b2588d1cbd 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt | |||
@@ -53,6 +53,13 @@ include::itrace.txt[] | |||
53 | --strip:: | 53 | --strip:: |
54 | Use with --itrace to strip out non-synthesized events. | 54 | Use with --itrace to strip out non-synthesized events. |
55 | 55 | ||
56 | -j:: | ||
57 | --jit:: | ||
58 | Process jitdump files by injecting the mmap records corresponding to jitted | ||
59 | functions. This option also generates the ELF images for each jitted function | ||
60 | found in the jitdumps files captured in the input perf.data file. Use this option | ||
61 | if you are monitoring environment using JIT runtimes, such as Java, DART or V8. | ||
62 | |||
56 | SEE ALSO | 63 | SEE ALSO |
57 | -------- | 64 | -------- |
58 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] | 65 | linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1] |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 79483f40e991..ec723d0a5bb3 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -40,10 +40,12 @@ address should be. The 'p' modifier can be specified multiple times: | |||
40 | 0 - SAMPLE_IP can have arbitrary skid | 40 | 0 - SAMPLE_IP can have arbitrary skid |
41 | 1 - SAMPLE_IP must have constant skid | 41 | 1 - SAMPLE_IP must have constant skid |
42 | 2 - SAMPLE_IP requested to have 0 skid | 42 | 2 - SAMPLE_IP requested to have 0 skid |
43 | 3 - SAMPLE_IP must have 0 skid | 43 | 3 - SAMPLE_IP must have 0 skid, or uses randomization to avoid |
44 | sample shadowing effects. | ||
44 | 45 | ||
45 | For Intel systems precise event sampling is implemented with PEBS | 46 | For Intel systems precise event sampling is implemented with PEBS |
46 | which supports up to precise-level 2. | 47 | which supports up to precise-level 2, and precise level 3 for |
48 | some special cases | ||
47 | 49 | ||
48 | On AMD systems it is implemented using IBS (up to precise-level 2). | 50 | On AMD systems it is implemented using IBS (up to precise-level 2). |
49 | The precise modifier works with event types 0x76 (cpu-cycles, CPU | 51 | The precise modifier works with event types 0x76 (cpu-cycles, CPU |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index fbceb631387c..19aa17532a16 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -341,6 +341,12 @@ Specify vmlinux path which has debuginfo. | |||
341 | --buildid-all:: | 341 | --buildid-all:: |
342 | Record build-id of all DSOs regardless whether it's actually hit or not. | 342 | Record build-id of all DSOs regardless whether it's actually hit or not. |
343 | 343 | ||
344 | --all-kernel:: | ||
345 | Configure all used events to run in kernel space. | ||
346 | |||
347 | --all-user:: | ||
348 | Configure all used events to run in user space. | ||
349 | |||
344 | SEE ALSO | 350 | SEE ALSO |
345 | -------- | 351 | -------- |
346 | linkperf:perf-stat[1], linkperf:perf-list[1] | 352 | 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 8a301f6afb37..12113992ac9d 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -117,6 +117,22 @@ OPTIONS | |||
117 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to | 117 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to |
118 | and symbol_to, see '--branch-stack'. | 118 | and symbol_to, see '--branch-stack'. |
119 | 119 | ||
120 | If the --mem-mode option is used, the following sort keys are also available | ||
121 | (incompatible with --branch-stack): | ||
122 | symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline. | ||
123 | |||
124 | - symbol_daddr: name of data symbol being executed on at the time of sample | ||
125 | - dso_daddr: name of library or module containing the data being executed | ||
126 | on at the time of the sample | ||
127 | - locked: whether the bus was locked at the time of the sample | ||
128 | - tlb: type of tlb access for the data at the time of the sample | ||
129 | - mem: type of memory access for the data at the time of the sample | ||
130 | - snoop: type of snoop (if any) for the data at the time of the sample | ||
131 | - dcacheline: the cacheline the data address is on at the time of the sample | ||
132 | |||
133 | And the default sort keys are changed to local_weight, mem, sym, dso, | ||
134 | symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'. | ||
135 | |||
120 | If the data file has tracepoint event(s), following (dynamic) sort keys | 136 | If the data file has tracepoint event(s), following (dynamic) sort keys |
121 | are also available: | 137 | are also available: |
122 | trace, trace_fields, [<event>.]<field>[/raw] | 138 | trace, trace_fields, [<event>.]<field>[/raw] |
@@ -151,22 +167,6 @@ OPTIONS | |||
151 | By default, every sort keys not specified in -F will be appended | 167 | By default, every sort keys not specified in -F will be appended |
152 | automatically. | 168 | automatically. |
153 | 169 | ||
154 | If --mem-mode option is used, following sort keys are also available | ||
155 | (incompatible with --branch-stack): | ||
156 | symbol_daddr, dso_daddr, locked, tlb, mem, snoop, dcacheline. | ||
157 | |||
158 | - symbol_daddr: name of data symbol being executed on at the time of sample | ||
159 | - dso_daddr: name of library or module containing the data being executed | ||
160 | on at the time of sample | ||
161 | - locked: whether the bus was locked at the time of sample | ||
162 | - tlb: type of tlb access for the data at the time of sample | ||
163 | - mem: type of memory access for the data at the time of sample | ||
164 | - snoop: type of snoop (if any) for the data at the time of sample | ||
165 | - dcacheline: the cacheline the data address is on at the time of sample | ||
166 | |||
167 | And default sort keys are changed to local_weight, mem, sym, dso, | ||
168 | symbol_daddr, dso_daddr, snoop, tlb, locked, see '--mem-mode'. | ||
169 | |||
170 | -p:: | 170 | -p:: |
171 | --parent=<regex>:: | 171 | --parent=<regex>:: |
172 | A regex filter to identify parent. The parent is a caller of this | 172 | A regex filter to identify parent. The parent is a caller of this |
@@ -351,7 +351,10 @@ OPTIONS | |||
351 | 351 | ||
352 | --percent-limit:: | 352 | --percent-limit:: |
353 | Do not show entries which have an overhead under that percent. | 353 | Do not show entries which have an overhead under that percent. |
354 | (Default: 0). | 354 | (Default: 0). Note that this option also sets the percent limit (threshold) |
355 | of callchains. However the default value of callchain threshold is | ||
356 | different than the default value of hist entries. Please see the | ||
357 | --call-graph option for details. | ||
355 | 358 | ||
356 | --percentage:: | 359 | --percentage:: |
357 | Determine how to display the overhead percentage of filtered entries. | 360 | Determine how to display the overhead percentage of filtered entries. |
@@ -398,6 +401,9 @@ include::itrace.txt[] | |||
398 | --raw-trace:: | 401 | --raw-trace:: |
399 | When displaying traceevent output, do not use print fmt or plugins. | 402 | When displaying traceevent output, do not use print fmt or plugins. |
400 | 403 | ||
404 | --hierarchy:: | ||
405 | Enable hierarchical output. | ||
406 | |||
401 | include::callchain-overhead-calculation.txt[] | 407 | include::callchain-overhead-calculation.txt[] |
402 | 408 | ||
403 | SEE ALSO | 409 | SEE ALSO |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 52ef7a9d50aa..04f23b404bbc 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -69,6 +69,14 @@ report:: | |||
69 | --scale:: | 69 | --scale:: |
70 | scale/normalize counter values | 70 | scale/normalize counter values |
71 | 71 | ||
72 | -d:: | ||
73 | --detailed:: | ||
74 | print more detailed statistics, can be specified up to 3 times | ||
75 | |||
76 | -d: detailed events, L1 and LLC data cache | ||
77 | -d -d: more detailed events, dTLB and iTLB events | ||
78 | -d -d -d: very detailed events, adding prefetch events | ||
79 | |||
72 | -r:: | 80 | -r:: |
73 | --repeat=<n>:: | 81 | --repeat=<n>:: |
74 | repeat command and print average + stddev (max: 100). 0 means forever. | 82 | repeat command and print average + stddev (max: 100). 0 means forever. |
@@ -139,6 +147,10 @@ Print count deltas every N milliseconds (minimum: 10ms) | |||
139 | The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution. | 147 | The overhead percentage could be high in some cases, for instance with small, sub 100ms intervals. Use with caution. |
140 | example: 'perf stat -I 1000 -e cycles -a sleep 5' | 148 | example: 'perf stat -I 1000 -e cycles -a sleep 5' |
141 | 149 | ||
150 | --metric-only:: | ||
151 | Only print computed metrics. Print them in a single line. | ||
152 | Don't show any raw values. Not supported with --per-thread. | ||
153 | |||
142 | --per-socket:: | 154 | --per-socket:: |
143 | Aggregate counts per processor socket for system-wide mode measurements. This | 155 | Aggregate counts per processor socket for system-wide mode measurements. This |
144 | is a useful mode to detect imbalance between sockets. To enable this mode, | 156 | is a useful mode to detect imbalance between sockets. To enable this mode, |
@@ -211,6 +223,29 @@ $ perf stat -- make -j | |||
211 | 223 | ||
212 | Wall-clock time elapsed: 719.554352 msecs | 224 | Wall-clock time elapsed: 719.554352 msecs |
213 | 225 | ||
226 | CSV FORMAT | ||
227 | ---------- | ||
228 | |||
229 | With -x, perf stat is able to output a not-quite-CSV format output | ||
230 | Commas in the output are not put into "". To make it easy to parse | ||
231 | it is recommended to use a different character like -x \; | ||
232 | |||
233 | The fields are in this order: | ||
234 | |||
235 | - optional usec time stamp in fractions of second (with -I xxx) | ||
236 | - optional CPU, core, or socket identifier | ||
237 | - optional number of logical CPUs aggregated | ||
238 | - counter value | ||
239 | - unit of the counter value or empty | ||
240 | - event name | ||
241 | - run time of counter | ||
242 | - percentage of measurement time the counter was running | ||
243 | - optional variance if multiple values are collected with -r | ||
244 | - optional metric value | ||
245 | - optional unit of metric | ||
246 | |||
247 | Additional metrics may be printed with all earlier fields being empty. | ||
248 | |||
214 | SEE ALSO | 249 | SEE ALSO |
215 | -------- | 250 | -------- |
216 | linkperf:perf-top[1], linkperf:perf-list[1] | 251 | linkperf:perf-top[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index b0e60e17db38..19f046f027cd 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -233,6 +233,9 @@ Default is to monitor all CPUS. | |||
233 | --raw-trace:: | 233 | --raw-trace:: |
234 | When displaying traceevent output, do not use print fmt or plugins. | 234 | When displaying traceevent output, do not use print fmt or plugins. |
235 | 235 | ||
236 | --hierarchy:: | ||
237 | Enable hierarchy output. | ||
238 | |||
236 | INTERACTIVE PROMPTING KEYS | 239 | INTERACTIVE PROMPTING KEYS |
237 | -------------------------- | 240 | -------------------------- |
238 | 241 | ||
diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example index 767ea2436e1c..1d8d5bc4cd2d 100644 --- a/tools/perf/Documentation/perfconfig.example +++ b/tools/perf/Documentation/perfconfig.example | |||
@@ -5,7 +5,7 @@ | |||
5 | medium = green, lightgray | 5 | medium = green, lightgray |
6 | normal = black, lightgray | 6 | normal = black, lightgray |
7 | selected = lightgray, magenta | 7 | selected = lightgray, magenta |
8 | code = blue, lightgray | 8 | jump_arrows = blue, lightgray |
9 | addr = magenta, lightgray | 9 | addr = magenta, lightgray |
10 | 10 | ||
11 | [tui] | 11 | [tui] |
diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt index e0ce9573b79b..5950b5a24efd 100644 --- a/tools/perf/Documentation/tips.txt +++ b/tools/perf/Documentation/tips.txt | |||
@@ -27,3 +27,4 @@ Skip collecing build-id when recording: perf record -B | |||
27 | To change sampling frequency to 100 Hz: perf record -F 100 | 27 | To change sampling frequency to 100 Hz: perf record -F 100 |
28 | See assembly instructions with percentage: perf annotate <symbol> | 28 | See assembly instructions with percentage: perf annotate <symbol> |
29 | If you prefer Intel style assembly, try: perf annotate -M intel | 29 | If you prefer Intel style assembly, try: perf annotate -M intel |
30 | For hierarchical output, try: perf report --hierarchy | ||
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 2e1fa2357528..8c8c6b9ce915 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -74,6 +74,7 @@ arch/*/include/uapi/asm/unistd*.h | |||
74 | arch/*/include/uapi/asm/perf_regs.h | 74 | arch/*/include/uapi/asm/perf_regs.h |
75 | arch/*/lib/memcpy*.S | 75 | arch/*/lib/memcpy*.S |
76 | arch/*/lib/memset*.S | 76 | arch/*/lib/memset*.S |
77 | arch/*/include/asm/*features.h | ||
77 | include/linux/poison.h | 78 | include/linux/poison.h |
78 | include/linux/hw_breakpoint.h | 79 | include/linux/hw_breakpoint.h |
79 | include/uapi/linux/perf_event.h | 80 | include/uapi/linux/perf_event.h |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index dcd9a70c7193..32a64e619028 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -68,6 +68,20 @@ all tags TAGS: | |||
68 | $(print_msg) | 68 | $(print_msg) |
69 | $(make) | 69 | $(make) |
70 | 70 | ||
71 | ifdef MAKECMDGOALS | ||
72 | has_clean := 0 | ||
73 | ifneq ($(filter clean,$(MAKECMDGOALS)),) | ||
74 | has_clean := 1 | ||
75 | endif # clean | ||
76 | |||
77 | ifeq ($(has_clean),1) | ||
78 | rest := $(filter-out clean,$(MAKECMDGOALS)) | ||
79 | ifneq ($(rest),) | ||
80 | $(rest): clean | ||
81 | endif # rest | ||
82 | endif # has_clean | ||
83 | endif # MAKECMDGOALS | ||
84 | |||
71 | # | 85 | # |
72 | # The clean target is not really parallel, don't print the jobs info: | 86 | # The clean target is not really parallel, don't print the jobs info: |
73 | # | 87 | # |
@@ -75,10 +89,17 @@ clean: | |||
75 | $(make) | 89 | $(make) |
76 | 90 | ||
77 | # | 91 | # |
78 | # The build-test target is not really parallel, don't print the jobs info: | 92 | # The build-test target is not really parallel, don't print the jobs info, |
93 | # it also uses only the tests/make targets that don't pollute the source | ||
94 | # repository, i.e. that uses O= or builds the tarpkg outside the source | ||
95 | # repo directories. | ||
96 | # | ||
97 | # For a full test, use: | ||
98 | # | ||
99 | # make -C tools/perf -f tests/make | ||
79 | # | 100 | # |
80 | build-test: | 101 | build-test: |
81 | @$(MAKE) SHUF=1 -f tests/make --no-print-directory | 102 | @$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg out |
82 | 103 | ||
83 | # | 104 | # |
84 | # All other targets get passed through: | 105 | # All other targets get passed through: |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 5d34815c7ccb..000ea210389d 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -3,7 +3,7 @@ include ../scripts/Makefile.include | |||
3 | # The default target of this Makefile is... | 3 | # The default target of this Makefile is... |
4 | all: | 4 | all: |
5 | 5 | ||
6 | include config/utilities.mak | 6 | include ../scripts/utilities.mak |
7 | 7 | ||
8 | # Define V to have a more verbose compile. | 8 | # Define V to have a more verbose compile. |
9 | # | 9 | # |
@@ -58,6 +58,9 @@ include config/utilities.mak | |||
58 | # | 58 | # |
59 | # Define NO_LIBBIONIC if you do not want bionic support | 59 | # Define NO_LIBBIONIC if you do not want bionic support |
60 | # | 60 | # |
61 | # Define NO_LIBCRYPTO if you do not want libcrypto (openssl) support | ||
62 | # used for generating build-ids for ELFs generated by jitdump. | ||
63 | # | ||
61 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support | 64 | # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support |
62 | # for dwarf backtrace post unwind. | 65 | # for dwarf backtrace post unwind. |
63 | # | 66 | # |
@@ -136,6 +139,8 @@ $(call allow-override,CC,$(CROSS_COMPILE)gcc) | |||
136 | $(call allow-override,AR,$(CROSS_COMPILE)ar) | 139 | $(call allow-override,AR,$(CROSS_COMPILE)ar) |
137 | $(call allow-override,LD,$(CROSS_COMPILE)ld) | 140 | $(call allow-override,LD,$(CROSS_COMPILE)ld) |
138 | 141 | ||
142 | LD += $(EXTRA_LDFLAGS) | ||
143 | |||
139 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config | 144 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config |
140 | 145 | ||
141 | RM = rm -f | 146 | RM = rm -f |
@@ -165,7 +170,16 @@ ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) | |||
165 | endif | 170 | endif |
166 | endif | 171 | endif |
167 | 172 | ||
173 | # Set FEATURE_TESTS to 'all' so all possible feature checkers are executed. | ||
174 | # Without this setting the output feature dump file misses some features, for | ||
175 | # example, liberty. Select all checkers so we won't get an incomplete feature | ||
176 | # dump file. | ||
168 | ifeq ($(config),1) | 177 | ifeq ($(config),1) |
178 | ifdef MAKECMDGOALS | ||
179 | ifeq ($(filter feature-dump,$(MAKECMDGOALS)),feature-dump) | ||
180 | FEATURE_TESTS := all | ||
181 | endif | ||
182 | endif | ||
169 | include config/Makefile | 183 | include config/Makefile |
170 | endif | 184 | endif |
171 | 185 | ||
@@ -618,7 +632,7 @@ clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean | |||
618 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 | 632 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 |
619 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ | 633 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ |
620 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ | 634 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ |
621 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue}.c | 635 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c |
622 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 636 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
623 | $(python-clean) | 637 | $(python-clean) |
624 | 638 | ||
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 7fbca175099e..18b13518d8d8 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | endif | 3 | endif |
4 | PERF_HAVE_JITDUMP := 1 | ||
diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile index 7fbca175099e..18b13518d8d8 100644 --- a/tools/perf/arch/arm64/Makefile +++ b/tools/perf/arch/arm64/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | endif | 3 | endif |
4 | PERF_HAVE_JITDUMP := 1 | ||
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index 7fbca175099e..56e05f126ad8 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile | |||
@@ -1,3 +1,6 @@ | |||
1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
3 | endif | 3 | endif |
4 | |||
5 | HAVE_KVM_STAT_SUPPORT := 1 | ||
6 | PERF_HAVE_JITDUMP := 1 | ||
diff --git a/tools/perf/arch/powerpc/util/Build b/tools/perf/arch/powerpc/util/Build index 7b8b0d1a1b62..c8fe2074d217 100644 --- a/tools/perf/arch/powerpc/util/Build +++ b/tools/perf/arch/powerpc/util/Build | |||
@@ -1,5 +1,6 @@ | |||
1 | libperf-y += header.o | 1 | libperf-y += header.o |
2 | libperf-y += sym-handling.o | 2 | libperf-y += sym-handling.o |
3 | libperf-y += kvm-stat.o | ||
3 | 4 | ||
4 | libperf-$(CONFIG_DWARF) += dwarf-regs.o | 5 | libperf-$(CONFIG_DWARF) += dwarf-regs.o |
5 | libperf-$(CONFIG_DWARF) += skip-callchain-idx.o | 6 | libperf-$(CONFIG_DWARF) += skip-callchain-idx.o |
diff --git a/tools/perf/arch/powerpc/util/book3s_hcalls.h b/tools/perf/arch/powerpc/util/book3s_hcalls.h new file mode 100644 index 000000000000..0dd6b7f2d44f --- /dev/null +++ b/tools/perf/arch/powerpc/util/book3s_hcalls.h | |||
@@ -0,0 +1,123 @@ | |||
1 | #ifndef ARCH_PERF_BOOK3S_HV_HCALLS_H | ||
2 | #define ARCH_PERF_BOOK3S_HV_HCALLS_H | ||
3 | |||
4 | /* | ||
5 | * PowerPC HCALL codes : hcall code to name mapping | ||
6 | */ | ||
7 | #define kvm_trace_symbol_hcall \ | ||
8 | {0x4, "H_REMOVE"}, \ | ||
9 | {0x8, "H_ENTER"}, \ | ||
10 | {0xc, "H_READ"}, \ | ||
11 | {0x10, "H_CLEAR_MOD"}, \ | ||
12 | {0x14, "H_CLEAR_REF"}, \ | ||
13 | {0x18, "H_PROTECT"}, \ | ||
14 | {0x1c, "H_GET_TCE"}, \ | ||
15 | {0x20, "H_PUT_TCE"}, \ | ||
16 | {0x24, "H_SET_SPRG0"}, \ | ||
17 | {0x28, "H_SET_DABR"}, \ | ||
18 | {0x2c, "H_PAGE_INIT"}, \ | ||
19 | {0x30, "H_SET_ASR"}, \ | ||
20 | {0x34, "H_ASR_ON"}, \ | ||
21 | {0x38, "H_ASR_OFF"}, \ | ||
22 | {0x3c, "H_LOGICAL_CI_LOAD"}, \ | ||
23 | {0x40, "H_LOGICAL_CI_STORE"}, \ | ||
24 | {0x44, "H_LOGICAL_CACHE_LOAD"}, \ | ||
25 | {0x48, "H_LOGICAL_CACHE_STORE"}, \ | ||
26 | {0x4c, "H_LOGICAL_ICBI"}, \ | ||
27 | {0x50, "H_LOGICAL_DCBF"}, \ | ||
28 | {0x54, "H_GET_TERM_CHAR"}, \ | ||
29 | {0x58, "H_PUT_TERM_CHAR"}, \ | ||
30 | {0x5c, "H_REAL_TO_LOGICAL"}, \ | ||
31 | {0x60, "H_HYPERVISOR_DATA"}, \ | ||
32 | {0x64, "H_EOI"}, \ | ||
33 | {0x68, "H_CPPR"}, \ | ||
34 | {0x6c, "H_IPI"}, \ | ||
35 | {0x70, "H_IPOLL"}, \ | ||
36 | {0x74, "H_XIRR"}, \ | ||
37 | {0x78, "H_MIGRATE_DMA"}, \ | ||
38 | {0x7c, "H_PERFMON"}, \ | ||
39 | {0xdc, "H_REGISTER_VPA"}, \ | ||
40 | {0xe0, "H_CEDE"}, \ | ||
41 | {0xe4, "H_CONFER"}, \ | ||
42 | {0xe8, "H_PROD"}, \ | ||
43 | {0xec, "H_GET_PPP"}, \ | ||
44 | {0xf0, "H_SET_PPP"}, \ | ||
45 | {0xf4, "H_PURR"}, \ | ||
46 | {0xf8, "H_PIC"}, \ | ||
47 | {0xfc, "H_REG_CRQ"}, \ | ||
48 | {0x100, "H_FREE_CRQ"}, \ | ||
49 | {0x104, "H_VIO_SIGNAL"}, \ | ||
50 | {0x108, "H_SEND_CRQ"}, \ | ||
51 | {0x110, "H_COPY_RDMA"}, \ | ||
52 | {0x114, "H_REGISTER_LOGICAL_LAN"}, \ | ||
53 | {0x118, "H_FREE_LOGICAL_LAN"}, \ | ||
54 | {0x11c, "H_ADD_LOGICAL_LAN_BUFFER"}, \ | ||
55 | {0x120, "H_SEND_LOGICAL_LAN"}, \ | ||
56 | {0x124, "H_BULK_REMOVE"}, \ | ||
57 | {0x130, "H_MULTICAST_CTRL"}, \ | ||
58 | {0x134, "H_SET_XDABR"}, \ | ||
59 | {0x138, "H_STUFF_TCE"}, \ | ||
60 | {0x13c, "H_PUT_TCE_INDIRECT"}, \ | ||
61 | {0x14c, "H_CHANGE_LOGICAL_LAN_MAC"}, \ | ||
62 | {0x150, "H_VTERM_PARTNER_INFO"}, \ | ||
63 | {0x154, "H_REGISTER_VTERM"}, \ | ||
64 | {0x158, "H_FREE_VTERM"}, \ | ||
65 | {0x15c, "H_RESET_EVENTS"}, \ | ||
66 | {0x160, "H_ALLOC_RESOURCE"}, \ | ||
67 | {0x164, "H_FREE_RESOURCE"}, \ | ||
68 | {0x168, "H_MODIFY_QP"}, \ | ||
69 | {0x16c, "H_QUERY_QP"}, \ | ||
70 | {0x170, "H_REREGISTER_PMR"}, \ | ||
71 | {0x174, "H_REGISTER_SMR"}, \ | ||
72 | {0x178, "H_QUERY_MR"}, \ | ||
73 | {0x17c, "H_QUERY_MW"}, \ | ||
74 | {0x180, "H_QUERY_HCA"}, \ | ||
75 | {0x184, "H_QUERY_PORT"}, \ | ||
76 | {0x188, "H_MODIFY_PORT"}, \ | ||
77 | {0x18c, "H_DEFINE_AQP1"}, \ | ||
78 | {0x190, "H_GET_TRACE_BUFFER"}, \ | ||
79 | {0x194, "H_DEFINE_AQP0"}, \ | ||
80 | {0x198, "H_RESIZE_MR"}, \ | ||
81 | {0x19c, "H_ATTACH_MCQP"}, \ | ||
82 | {0x1a0, "H_DETACH_MCQP"}, \ | ||
83 | {0x1a4, "H_CREATE_RPT"}, \ | ||
84 | {0x1a8, "H_REMOVE_RPT"}, \ | ||
85 | {0x1ac, "H_REGISTER_RPAGES"}, \ | ||
86 | {0x1b0, "H_DISABLE_AND_GETC"}, \ | ||
87 | {0x1b4, "H_ERROR_DATA"}, \ | ||
88 | {0x1b8, "H_GET_HCA_INFO"}, \ | ||
89 | {0x1bc, "H_GET_PERF_COUNT"}, \ | ||
90 | {0x1c0, "H_MANAGE_TRACE"}, \ | ||
91 | {0x1d4, "H_FREE_LOGICAL_LAN_BUFFER"}, \ | ||
92 | {0x1d8, "H_POLL_PENDING"}, \ | ||
93 | {0x1e4, "H_QUERY_INT_STATE"}, \ | ||
94 | {0x244, "H_ILLAN_ATTRIBUTES"}, \ | ||
95 | {0x250, "H_MODIFY_HEA_QP"}, \ | ||
96 | {0x254, "H_QUERY_HEA_QP"}, \ | ||
97 | {0x258, "H_QUERY_HEA"}, \ | ||
98 | {0x25c, "H_QUERY_HEA_PORT"}, \ | ||
99 | {0x260, "H_MODIFY_HEA_PORT"}, \ | ||
100 | {0x264, "H_REG_BCMC"}, \ | ||
101 | {0x268, "H_DEREG_BCMC"}, \ | ||
102 | {0x26c, "H_REGISTER_HEA_RPAGES"}, \ | ||
103 | {0x270, "H_DISABLE_AND_GET_HEA"}, \ | ||
104 | {0x274, "H_GET_HEA_INFO"}, \ | ||
105 | {0x278, "H_ALLOC_HEA_RESOURCE"}, \ | ||
106 | {0x284, "H_ADD_CONN"}, \ | ||
107 | {0x288, "H_DEL_CONN"}, \ | ||
108 | {0x298, "H_JOIN"}, \ | ||
109 | {0x2a4, "H_VASI_STATE"}, \ | ||
110 | {0x2b0, "H_ENABLE_CRQ"}, \ | ||
111 | {0x2b8, "H_GET_EM_PARMS"}, \ | ||
112 | {0x2d0, "H_SET_MPP"}, \ | ||
113 | {0x2d4, "H_GET_MPP"}, \ | ||
114 | {0x2ec, "H_HOME_NODE_ASSOCIATIVITY"}, \ | ||
115 | {0x2f4, "H_BEST_ENERGY"}, \ | ||
116 | {0x2fc, "H_XIRR_X"}, \ | ||
117 | {0x300, "H_RANDOM"}, \ | ||
118 | {0x304, "H_COP"}, \ | ||
119 | {0x314, "H_GET_MPP_X"}, \ | ||
120 | {0x31c, "H_SET_MODE"}, \ | ||
121 | {0xf000, "H_RTAS"} \ | ||
122 | |||
123 | #endif | ||
diff --git a/tools/perf/arch/powerpc/util/book3s_hv_exits.h b/tools/perf/arch/powerpc/util/book3s_hv_exits.h new file mode 100644 index 000000000000..e68ba2da8970 --- /dev/null +++ b/tools/perf/arch/powerpc/util/book3s_hv_exits.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #ifndef ARCH_PERF_BOOK3S_HV_EXITS_H | ||
2 | #define ARCH_PERF_BOOK3S_HV_EXITS_H | ||
3 | |||
4 | /* | ||
5 | * PowerPC Interrupt vectors : exit code to name mapping | ||
6 | */ | ||
7 | |||
8 | #define kvm_trace_symbol_exit \ | ||
9 | {0x0, "RETURN_TO_HOST"}, \ | ||
10 | {0x100, "SYSTEM_RESET"}, \ | ||
11 | {0x200, "MACHINE_CHECK"}, \ | ||
12 | {0x300, "DATA_STORAGE"}, \ | ||
13 | {0x380, "DATA_SEGMENT"}, \ | ||
14 | {0x400, "INST_STORAGE"}, \ | ||
15 | {0x480, "INST_SEGMENT"}, \ | ||
16 | {0x500, "EXTERNAL"}, \ | ||
17 | {0x501, "EXTERNAL_LEVEL"}, \ | ||
18 | {0x502, "EXTERNAL_HV"}, \ | ||
19 | {0x600, "ALIGNMENT"}, \ | ||
20 | {0x700, "PROGRAM"}, \ | ||
21 | {0x800, "FP_UNAVAIL"}, \ | ||
22 | {0x900, "DECREMENTER"}, \ | ||
23 | {0x980, "HV_DECREMENTER"}, \ | ||
24 | {0xc00, "SYSCALL"}, \ | ||
25 | {0xd00, "TRACE"}, \ | ||
26 | {0xe00, "H_DATA_STORAGE"}, \ | ||
27 | {0xe20, "H_INST_STORAGE"}, \ | ||
28 | {0xe40, "H_EMUL_ASSIST"}, \ | ||
29 | {0xf00, "PERFMON"}, \ | ||
30 | {0xf20, "ALTIVEC"}, \ | ||
31 | {0xf40, "VSX"} | ||
32 | |||
33 | #endif | ||
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index 6c1b8a75db09..f8ccee132867 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -3,9 +3,9 @@ | |||
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <string.h> | 5 | #include <string.h> |
6 | 6 | #include <linux/stringify.h> | |
7 | #include "../../util/header.h" | 7 | #include "header.h" |
8 | #include "../../util/util.h" | 8 | #include "util.h" |
9 | 9 | ||
10 | #define mfspr(rn) ({unsigned long rval; \ | 10 | #define mfspr(rn) ({unsigned long rval; \ |
11 | asm volatile("mfspr %0," __stringify(rn) \ | 11 | asm volatile("mfspr %0," __stringify(rn) \ |
diff --git a/tools/perf/arch/powerpc/util/kvm-stat.c b/tools/perf/arch/powerpc/util/kvm-stat.c new file mode 100644 index 000000000000..74eee30398f8 --- /dev/null +++ b/tools/perf/arch/powerpc/util/kvm-stat.c | |||
@@ -0,0 +1,170 @@ | |||
1 | #include "util/kvm-stat.h" | ||
2 | #include "util/parse-events.h" | ||
3 | #include "util/debug.h" | ||
4 | |||
5 | #include "book3s_hv_exits.h" | ||
6 | #include "book3s_hcalls.h" | ||
7 | |||
8 | #define NR_TPS 4 | ||
9 | |||
10 | const char *vcpu_id_str = "vcpu_id"; | ||
11 | const int decode_str_len = 40; | ||
12 | const char *kvm_entry_trace = "kvm_hv:kvm_guest_enter"; | ||
13 | const char *kvm_exit_trace = "kvm_hv:kvm_guest_exit"; | ||
14 | |||
15 | define_exit_reasons_table(hv_exit_reasons, kvm_trace_symbol_exit); | ||
16 | define_exit_reasons_table(hcall_reasons, kvm_trace_symbol_hcall); | ||
17 | |||
18 | /* Tracepoints specific to ppc_book3s_hv */ | ||
19 | const char *ppc_book3s_hv_kvm_tp[] = { | ||
20 | "kvm_hv:kvm_guest_enter", | ||
21 | "kvm_hv:kvm_guest_exit", | ||
22 | "kvm_hv:kvm_hcall_enter", | ||
23 | "kvm_hv:kvm_hcall_exit", | ||
24 | NULL, | ||
25 | }; | ||
26 | |||
27 | /* 1 extra placeholder for NULL */ | ||
28 | const char *kvm_events_tp[NR_TPS + 1]; | ||
29 | const char *kvm_exit_reason; | ||
30 | |||
31 | static void hcall_event_get_key(struct perf_evsel *evsel, | ||
32 | struct perf_sample *sample, | ||
33 | struct event_key *key) | ||
34 | { | ||
35 | key->info = 0; | ||
36 | key->key = perf_evsel__intval(evsel, sample, "req"); | ||
37 | } | ||
38 | |||
39 | static const char *get_hcall_exit_reason(u64 exit_code) | ||
40 | { | ||
41 | struct exit_reasons_table *tbl = hcall_reasons; | ||
42 | |||
43 | while (tbl->reason != NULL) { | ||
44 | if (tbl->exit_code == exit_code) | ||
45 | return tbl->reason; | ||
46 | tbl++; | ||
47 | } | ||
48 | |||
49 | pr_debug("Unknown hcall code: %lld\n", | ||
50 | (unsigned long long)exit_code); | ||
51 | return "UNKNOWN"; | ||
52 | } | ||
53 | |||
54 | static bool hcall_event_end(struct perf_evsel *evsel, | ||
55 | struct perf_sample *sample __maybe_unused, | ||
56 | struct event_key *key __maybe_unused) | ||
57 | { | ||
58 | return (!strcmp(evsel->name, kvm_events_tp[3])); | ||
59 | } | ||
60 | |||
61 | static bool hcall_event_begin(struct perf_evsel *evsel, | ||
62 | struct perf_sample *sample, struct event_key *key) | ||
63 | { | ||
64 | if (!strcmp(evsel->name, kvm_events_tp[2])) { | ||
65 | hcall_event_get_key(evsel, sample, key); | ||
66 | return true; | ||
67 | } | ||
68 | |||
69 | return false; | ||
70 | } | ||
71 | static void hcall_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | ||
72 | struct event_key *key, | ||
73 | char *decode) | ||
74 | { | ||
75 | const char *hcall_reason = get_hcall_exit_reason(key->key); | ||
76 | |||
77 | scnprintf(decode, decode_str_len, "%s", hcall_reason); | ||
78 | } | ||
79 | |||
80 | static struct kvm_events_ops hcall_events = { | ||
81 | .is_begin_event = hcall_event_begin, | ||
82 | .is_end_event = hcall_event_end, | ||
83 | .decode_key = hcall_event_decode_key, | ||
84 | .name = "HCALL-EVENT", | ||
85 | }; | ||
86 | |||
87 | static struct kvm_events_ops exit_events = { | ||
88 | .is_begin_event = exit_event_begin, | ||
89 | .is_end_event = exit_event_end, | ||
90 | .decode_key = exit_event_decode_key, | ||
91 | .name = "VM-EXIT" | ||
92 | }; | ||
93 | |||
94 | struct kvm_reg_events_ops kvm_reg_events_ops[] = { | ||
95 | { .name = "vmexit", .ops = &exit_events }, | ||
96 | { .name = "hcall", .ops = &hcall_events }, | ||
97 | { NULL, NULL }, | ||
98 | }; | ||
99 | |||
100 | const char * const kvm_skip_events[] = { | ||
101 | NULL, | ||
102 | }; | ||
103 | |||
104 | |||
105 | static int is_tracepoint_available(const char *str, struct perf_evlist *evlist) | ||
106 | { | ||
107 | struct parse_events_error err; | ||
108 | int ret; | ||
109 | |||
110 | err.str = NULL; | ||
111 | ret = parse_events(evlist, str, &err); | ||
112 | if (err.str) | ||
113 | pr_err("%s : %s\n", str, err.str); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static int ppc__setup_book3s_hv(struct perf_kvm_stat *kvm, | ||
118 | struct perf_evlist *evlist) | ||
119 | { | ||
120 | const char **events_ptr; | ||
121 | int i, nr_tp = 0, err = -1; | ||
122 | |||
123 | /* Check for book3s_hv tracepoints */ | ||
124 | for (events_ptr = ppc_book3s_hv_kvm_tp; *events_ptr; events_ptr++) { | ||
125 | err = is_tracepoint_available(*events_ptr, evlist); | ||
126 | if (err) | ||
127 | return -1; | ||
128 | nr_tp++; | ||
129 | } | ||
130 | |||
131 | for (i = 0; i < nr_tp; i++) | ||
132 | kvm_events_tp[i] = ppc_book3s_hv_kvm_tp[i]; | ||
133 | |||
134 | kvm_events_tp[i] = NULL; | ||
135 | kvm_exit_reason = "trap"; | ||
136 | kvm->exit_reasons = hv_exit_reasons; | ||
137 | kvm->exit_reasons_isa = "HV"; | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /* Wrapper to setup kvm tracepoints */ | ||
143 | static int ppc__setup_kvm_tp(struct perf_kvm_stat *kvm) | ||
144 | { | ||
145 | struct perf_evlist *evlist = perf_evlist__new(); | ||
146 | |||
147 | if (evlist == NULL) | ||
148 | return -ENOMEM; | ||
149 | |||
150 | /* Right now, only supported on book3s_hv */ | ||
151 | return ppc__setup_book3s_hv(kvm, evlist); | ||
152 | } | ||
153 | |||
154 | int setup_kvm_events_tp(struct perf_kvm_stat *kvm) | ||
155 | { | ||
156 | return ppc__setup_kvm_tp(kvm); | ||
157 | } | ||
158 | |||
159 | int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) | ||
160 | { | ||
161 | int ret; | ||
162 | |||
163 | ret = ppc__setup_kvm_tp(kvm); | ||
164 | if (ret) { | ||
165 | kvm->exit_reasons = NULL; | ||
166 | kvm->exit_reasons_isa = NULL; | ||
167 | } | ||
168 | |||
169 | return ret; | ||
170 | } | ||
diff --git a/tools/perf/arch/s390/util/kvm-stat.c b/tools/perf/arch/s390/util/kvm-stat.c index a5dbc07ec9dc..ed57df2e6d68 100644 --- a/tools/perf/arch/s390/util/kvm-stat.c +++ b/tools/perf/arch/s390/util/kvm-stat.c | |||
@@ -10,7 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include "../../util/kvm-stat.h" | 12 | #include "../../util/kvm-stat.h" |
13 | #include <asm/kvm_perf.h> | 13 | #include <asm/sie.h> |
14 | 14 | ||
15 | define_exit_reasons_table(sie_exit_reasons, sie_intercept_code); | 15 | define_exit_reasons_table(sie_exit_reasons, sie_intercept_code); |
16 | define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes); | 16 | define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes); |
@@ -18,6 +18,12 @@ define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes); | |||
18 | define_exit_reasons_table(sie_diagnose_codes, diagnose_codes); | 18 | define_exit_reasons_table(sie_diagnose_codes, diagnose_codes); |
19 | define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes); | 19 | define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes); |
20 | 20 | ||
21 | const char *vcpu_id_str = "id"; | ||
22 | const int decode_str_len = 40; | ||
23 | const char *kvm_exit_reason = "icptcode"; | ||
24 | const char *kvm_entry_trace = "kvm:kvm_s390_sie_enter"; | ||
25 | const char *kvm_exit_trace = "kvm:kvm_s390_sie_exit"; | ||
26 | |||
21 | static void event_icpt_insn_get_key(struct perf_evsel *evsel, | 27 | static void event_icpt_insn_get_key(struct perf_evsel *evsel, |
22 | struct perf_sample *sample, | 28 | struct perf_sample *sample, |
23 | struct event_key *key) | 29 | struct event_key *key) |
@@ -73,7 +79,7 @@ static struct kvm_events_ops exit_events = { | |||
73 | .name = "VM-EXIT" | 79 | .name = "VM-EXIT" |
74 | }; | 80 | }; |
75 | 81 | ||
76 | const char * const kvm_events_tp[] = { | 82 | const char *kvm_events_tp[] = { |
77 | "kvm:kvm_s390_sie_enter", | 83 | "kvm:kvm_s390_sie_enter", |
78 | "kvm:kvm_s390_sie_exit", | 84 | "kvm:kvm_s390_sie_exit", |
79 | "kvm:kvm_s390_intercept_instruction", | 85 | "kvm:kvm_s390_intercept_instruction", |
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 09ba923debe8..269af2143735 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile | |||
@@ -3,3 +3,4 @@ PERF_HAVE_DWARF_REGS := 1 | |||
3 | endif | 3 | endif |
4 | HAVE_KVM_STAT_SUPPORT := 1 | 4 | HAVE_KVM_STAT_SUPPORT := 1 |
5 | PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 | 5 | PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 |
6 | PERF_HAVE_JITDUMP := 1 | ||
diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c index 7bb0d13c235f..72193f19d6d7 100644 --- a/tools/perf/arch/x86/tests/rdpmc.c +++ b/tools/perf/arch/x86/tests/rdpmc.c | |||
@@ -59,7 +59,7 @@ static u64 mmap_read_self(void *addr) | |||
59 | u64 quot, rem; | 59 | u64 quot, rem; |
60 | 60 | ||
61 | quot = (cyc >> time_shift); | 61 | quot = (cyc >> time_shift); |
62 | rem = cyc & ((1 << time_shift) - 1); | 62 | rem = cyc & (((u64)1 << time_shift) - 1); |
63 | delta = time_offset + quot * time_mult + | 63 | delta = time_offset + quot * time_mult + |
64 | ((rem * time_mult) >> time_shift); | 64 | ((rem * time_mult) >> time_shift); |
65 | 65 | ||
@@ -103,6 +103,7 @@ static int __test__rdpmc(void) | |||
103 | 103 | ||
104 | sigfillset(&sa.sa_mask); | 104 | sigfillset(&sa.sa_mask); |
105 | sa.sa_sigaction = segfault_handler; | 105 | sa.sa_sigaction = segfault_handler; |
106 | sa.sa_flags = 0; | ||
106 | sigaction(SIGSEGV, &sa, NULL); | 107 | sigaction(SIGSEGV, &sa, NULL); |
107 | 108 | ||
108 | fd = sys_perf_event_open(&attr, 0, -1, -1, | 109 | fd = sys_perf_event_open(&attr, 0, -1, -1, |
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 8d8150f1cf9b..d66f9ad4df2e 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c | |||
@@ -60,7 +60,9 @@ struct branch { | |||
60 | u64 misc; | 60 | u64 misc; |
61 | }; | 61 | }; |
62 | 62 | ||
63 | static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused) | 63 | static size_t |
64 | intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused, | ||
65 | struct perf_evlist *evlist __maybe_unused) | ||
64 | { | 66 | { |
65 | return INTEL_BTS_AUXTRACE_PRIV_SIZE; | 67 | return INTEL_BTS_AUXTRACE_PRIV_SIZE; |
66 | } | 68 | } |
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index f05daacc9e78..a3395179c9ee 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c | |||
@@ -89,7 +89,7 @@ static int intel_pt_parse_terms_with_default(struct list_head *formats, | |||
89 | 89 | ||
90 | *config = attr.config; | 90 | *config = attr.config; |
91 | out_free: | 91 | out_free: |
92 | parse_events__free_terms(terms); | 92 | parse_events_terms__delete(terms); |
93 | return err; | 93 | return err; |
94 | } | 94 | } |
95 | 95 | ||
@@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) | |||
273 | return attr; | 273 | return attr; |
274 | } | 274 | } |
275 | 275 | ||
276 | static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused) | 276 | static size_t |
277 | intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused, | ||
278 | struct perf_evlist *evlist __maybe_unused) | ||
277 | { | 279 | { |
278 | return INTEL_PT_AUXTRACE_PRIV_SIZE; | 280 | return INTEL_PT_AUXTRACE_PRIV_SIZE; |
279 | } | 281 | } |
diff --git a/tools/perf/arch/x86/util/kvm-stat.c b/tools/perf/arch/x86/util/kvm-stat.c index 14e4e668fad7..b63d4be655a2 100644 --- a/tools/perf/arch/x86/util/kvm-stat.c +++ b/tools/perf/arch/x86/util/kvm-stat.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #include "../../util/kvm-stat.h" | 1 | #include "../../util/kvm-stat.h" |
2 | #include <asm/kvm_perf.h> | 2 | #include <asm/svm.h> |
3 | #include <asm/vmx.h> | ||
4 | #include <asm/kvm.h> | ||
3 | 5 | ||
4 | define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); | 6 | define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); |
5 | define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); | 7 | define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); |
@@ -11,6 +13,12 @@ static struct kvm_events_ops exit_events = { | |||
11 | .name = "VM-EXIT" | 13 | .name = "VM-EXIT" |
12 | }; | 14 | }; |
13 | 15 | ||
16 | const char *vcpu_id_str = "vcpu_id"; | ||
17 | const int decode_str_len = 20; | ||
18 | const char *kvm_exit_reason = "exit_reason"; | ||
19 | const char *kvm_entry_trace = "kvm:kvm_entry"; | ||
20 | const char *kvm_exit_trace = "kvm:kvm_exit"; | ||
21 | |||
14 | /* | 22 | /* |
15 | * For the mmio events, we treat: | 23 | * For the mmio events, we treat: |
16 | * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry | 24 | * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry |
@@ -65,7 +73,7 @@ static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | |||
65 | struct event_key *key, | 73 | struct event_key *key, |
66 | char *decode) | 74 | char *decode) |
67 | { | 75 | { |
68 | scnprintf(decode, DECODE_STR_LEN, "%#lx:%s", | 76 | scnprintf(decode, decode_str_len, "%#lx:%s", |
69 | (unsigned long)key->key, | 77 | (unsigned long)key->key, |
70 | key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); | 78 | key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); |
71 | } | 79 | } |
@@ -109,7 +117,7 @@ static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, | |||
109 | struct event_key *key, | 117 | struct event_key *key, |
110 | char *decode) | 118 | char *decode) |
111 | { | 119 | { |
112 | scnprintf(decode, DECODE_STR_LEN, "%#llx:%s", | 120 | scnprintf(decode, decode_str_len, "%#llx:%s", |
113 | (unsigned long long)key->key, | 121 | (unsigned long long)key->key, |
114 | key->info ? "POUT" : "PIN"); | 122 | key->info ? "POUT" : "PIN"); |
115 | } | 123 | } |
@@ -121,7 +129,7 @@ static struct kvm_events_ops ioport_events = { | |||
121 | .name = "IO Port Access" | 129 | .name = "IO Port Access" |
122 | }; | 130 | }; |
123 | 131 | ||
124 | const char * const kvm_events_tp[] = { | 132 | const char *kvm_events_tp[] = { |
125 | "kvm:kvm_entry", | 133 | "kvm:kvm_entry", |
126 | "kvm:kvm_exit", | 134 | "kvm:kvm_exit", |
127 | "kvm:kvm_mmio", | 135 | "kvm:kvm_mmio", |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index a50df86f2b9b..579a592990dd 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h | |||
@@ -25,19 +25,17 @@ | |||
25 | # endif | 25 | # endif |
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | extern int bench_numa(int argc, const char **argv, const char *prefix); | 28 | int bench_numa(int argc, const char **argv, const char *prefix); |
29 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); | 29 | int bench_sched_messaging(int argc, const char **argv, const char *prefix); |
30 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); | 30 | int bench_sched_pipe(int argc, const char **argv, const char *prefix); |
31 | extern int bench_mem_memcpy(int argc, const char **argv, | 31 | int bench_mem_memcpy(int argc, const char **argv, const char *prefix); |
32 | const char *prefix __maybe_unused); | 32 | int bench_mem_memset(int argc, const char **argv, const char *prefix); |
33 | extern int bench_mem_memset(int argc, const char **argv, const char *prefix); | 33 | int bench_futex_hash(int argc, const char **argv, const char *prefix); |
34 | extern int bench_futex_hash(int argc, const char **argv, const char *prefix); | 34 | int bench_futex_wake(int argc, const char **argv, const char *prefix); |
35 | extern int bench_futex_wake(int argc, const char **argv, const char *prefix); | 35 | int bench_futex_wake_parallel(int argc, const char **argv, const char *prefix); |
36 | extern int bench_futex_wake_parallel(int argc, const char **argv, | 36 | int bench_futex_requeue(int argc, const char **argv, const char *prefix); |
37 | const char *prefix); | ||
38 | extern int bench_futex_requeue(int argc, const char **argv, const char *prefix); | ||
39 | /* pi futexes */ | 37 | /* pi futexes */ |
40 | extern int bench_futex_lock_pi(int argc, const char **argv, const char *prefix); | 38 | int bench_futex_lock_pi(int argc, const char **argv, const char *prefix); |
41 | 39 | ||
42 | #define BENCH_FORMAT_DEFAULT_STR "default" | 40 | #define BENCH_FORMAT_DEFAULT_STR "default" |
43 | #define BENCH_FORMAT_DEFAULT 0 | 41 | #define BENCH_FORMAT_DEFAULT 0 |
diff --git a/tools/perf/bench/mem-memcpy-arch.h b/tools/perf/bench/mem-memcpy-arch.h index 57b4ed871459..5aad2a9408b0 100644 --- a/tools/perf/bench/mem-memcpy-arch.h +++ b/tools/perf/bench/mem-memcpy-arch.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #ifdef HAVE_ARCH_X86_64_SUPPORT | 2 | #ifdef HAVE_ARCH_X86_64_SUPPORT |
3 | 3 | ||
4 | #define MEMCPY_FN(fn, name, desc) \ | 4 | #define MEMCPY_FN(fn, name, desc) \ |
5 | extern void *fn(void *, const void *, size_t); | 5 | void *fn(void *, const void *, size_t); |
6 | 6 | ||
7 | #include "mem-memcpy-x86-64-asm-def.h" | 7 | #include "mem-memcpy-x86-64-asm-def.h" |
8 | 8 | ||
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S index e4c2c30143b9..5c3cce082cb8 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S | |||
@@ -1,6 +1,11 @@ | |||
1 | |||
2 | /* Various wrappers to make the kernel .S file build in user-space: */ | ||
3 | |||
1 | #define memcpy MEMCPY /* don't hide glibc's memcpy() */ | 4 | #define memcpy MEMCPY /* don't hide glibc's memcpy() */ |
2 | #define altinstr_replacement text | 5 | #define altinstr_replacement text |
3 | #define globl p2align 4; .globl | 6 | #define globl p2align 4; .globl |
7 | #define _ASM_EXTABLE_FAULT(x, y) | ||
8 | |||
4 | #include "../../../arch/x86/lib/memcpy_64.S" | 9 | #include "../../../arch/x86/lib/memcpy_64.S" |
5 | /* | 10 | /* |
6 | * We need to provide note.GNU-stack section, saying that we want | 11 | * We need to provide note.GNU-stack section, saying that we want |
diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h index 633800cb0dcb..0d15786d9ae3 100644 --- a/tools/perf/bench/mem-memset-arch.h +++ b/tools/perf/bench/mem-memset-arch.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #ifdef HAVE_ARCH_X86_64_SUPPORT | 2 | #ifdef HAVE_ARCH_X86_64_SUPPORT |
3 | 3 | ||
4 | #define MEMSET_FN(fn, name, desc) \ | 4 | #define MEMSET_FN(fn, name, desc) \ |
5 | extern void *fn(void *, int, size_t); | 5 | void *fn(void *, int, size_t); |
6 | 6 | ||
7 | #include "mem-memset-x86-64-asm-def.h" | 7 | #include "mem-memset-x86-64-asm-def.h" |
8 | 8 | ||
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 5049d6357a46..7500d959d7eb 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c | |||
@@ -293,7 +293,7 @@ static void bind_to_memnode(int node) | |||
293 | if (node == -1) | 293 | if (node == -1) |
294 | return; | 294 | return; |
295 | 295 | ||
296 | BUG_ON(g->p.nr_nodes > (int)sizeof(nodemask)); | 296 | BUG_ON(g->p.nr_nodes > (int)sizeof(nodemask)*8); |
297 | nodemask = 1L << node; | 297 | nodemask = 1L << node; |
298 | 298 | ||
299 | ret = set_mempolicy(MPOL_BIND, &nodemask, sizeof(nodemask)*8); | 299 | ret = set_mempolicy(MPOL_BIND, &nodemask, sizeof(nodemask)*8); |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index cc5c1267c738..814158393656 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -94,7 +94,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
94 | struct addr_location al; | 94 | struct addr_location al; |
95 | int ret = 0; | 95 | int ret = 0; |
96 | 96 | ||
97 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 97 | if (machine__resolve(machine, &al, sample) < 0) { |
98 | pr_warning("problem processing %d event, skipping it.\n", | 98 | pr_warning("problem processing %d event, skipping it.\n", |
99 | event->header.type); | 99 | event->header.type); |
100 | return -1; | 100 | return -1; |
@@ -245,7 +245,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
245 | hists__collapse_resort(hists, NULL); | 245 | hists__collapse_resort(hists, NULL); |
246 | /* Don't sort callchain */ | 246 | /* Don't sort callchain */ |
247 | perf_evsel__reset_sample_bit(pos, CALLCHAIN); | 247 | perf_evsel__reset_sample_bit(pos, CALLCHAIN); |
248 | hists__output_resort(hists, NULL); | 248 | perf_evsel__output_resort(pos, NULL); |
249 | 249 | ||
250 | if (symbol_conf.event_group && | 250 | if (symbol_conf.event_group && |
251 | !perf_evsel__is_group_leader(pos)) | 251 | !perf_evsel__is_group_leader(pos)) |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index d93bff7fc0e4..632efc6b79a0 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -38,19 +38,7 @@ static int build_id_cache__kcore_buildid(const char *proc_dir, char *sbuildid) | |||
38 | 38 | ||
39 | static int build_id_cache__kcore_dir(char *dir, size_t sz) | 39 | static int build_id_cache__kcore_dir(char *dir, size_t sz) |
40 | { | 40 | { |
41 | struct timeval tv; | 41 | return fetch_current_timestamp(dir, sz); |
42 | struct tm tm; | ||
43 | char dt[32]; | ||
44 | |||
45 | if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm)) | ||
46 | return -1; | ||
47 | |||
48 | if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm)) | ||
49 | return -1; | ||
50 | |||
51 | scnprintf(dir, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000); | ||
52 | |||
53 | return 0; | ||
54 | } | 42 | } |
55 | 43 | ||
56 | static bool same_kallsyms_reloc(const char *from_dir, char *to_dir) | 44 | static bool same_kallsyms_reloc(const char *from_dir, char *to_dir) |
diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c index f04e804a9fad..c42448ed5dfe 100644 --- a/tools/perf/builtin-config.c +++ b/tools/perf/builtin-config.c | |||
@@ -13,8 +13,10 @@ | |||
13 | #include "util/util.h" | 13 | #include "util/util.h" |
14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
15 | 15 | ||
16 | static bool use_system_config, use_user_config; | ||
17 | |||
16 | static const char * const config_usage[] = { | 18 | static const char * const config_usage[] = { |
17 | "perf config [options]", | 19 | "perf config [<file-option>] [options]", |
18 | NULL | 20 | NULL |
19 | }; | 21 | }; |
20 | 22 | ||
@@ -25,6 +27,8 @@ enum actions { | |||
25 | static struct option config_options[] = { | 27 | static struct option config_options[] = { |
26 | OPT_SET_UINT('l', "list", &actions, | 28 | OPT_SET_UINT('l', "list", &actions, |
27 | "show current config variables", ACTION_LIST), | 29 | "show current config variables", ACTION_LIST), |
30 | OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), | ||
31 | OPT_BOOLEAN(0, "user", &use_user_config, "use user config file"), | ||
28 | OPT_END() | 32 | OPT_END() |
29 | }; | 33 | }; |
30 | 34 | ||
@@ -42,10 +46,23 @@ static int show_config(const char *key, const char *value, | |||
42 | int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) | 46 | int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) |
43 | { | 47 | { |
44 | int ret = 0; | 48 | int ret = 0; |
49 | char *user_config = mkpath("%s/.perfconfig", getenv("HOME")); | ||
45 | 50 | ||
46 | argc = parse_options(argc, argv, config_options, config_usage, | 51 | argc = parse_options(argc, argv, config_options, config_usage, |
47 | PARSE_OPT_STOP_AT_NON_OPTION); | 52 | PARSE_OPT_STOP_AT_NON_OPTION); |
48 | 53 | ||
54 | if (use_system_config && use_user_config) { | ||
55 | pr_err("Error: only one config file at a time\n"); | ||
56 | parse_options_usage(config_usage, config_options, "user", 0); | ||
57 | parse_options_usage(NULL, config_options, "system", 0); | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | if (use_system_config) | ||
62 | config_exclusive_filename = perf_etc_perfconfig(); | ||
63 | else if (use_user_config) | ||
64 | config_exclusive_filename = user_config; | ||
65 | |||
49 | switch (actions) { | 66 | switch (actions) { |
50 | case ACTION_LIST: | 67 | case ACTION_LIST: |
51 | if (argc) { | 68 | if (argc) { |
@@ -53,9 +70,13 @@ int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) | |||
53 | parse_options_usage(config_usage, config_options, "l", 1); | 70 | parse_options_usage(config_usage, config_options, "l", 1); |
54 | } else { | 71 | } else { |
55 | ret = perf_config(show_config, NULL); | 72 | ret = perf_config(show_config, NULL); |
56 | if (ret < 0) | 73 | if (ret < 0) { |
74 | const char * config_filename = config_exclusive_filename; | ||
75 | if (!config_exclusive_filename) | ||
76 | config_filename = user_config; | ||
57 | pr_err("Nothing configured, " | 77 | pr_err("Nothing configured, " |
58 | "please check your ~/.perfconfig file\n"); | 78 | "please check your %s \n", config_filename); |
79 | } | ||
59 | } | 80 | } |
60 | break; | 81 | break; |
61 | default: | 82 | default: |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 36ccc2b8827f..8053a8ceefda 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -330,7 +330,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
330 | struct hists *hists = evsel__hists(evsel); | 330 | struct hists *hists = evsel__hists(evsel); |
331 | int ret = -1; | 331 | int ret = -1; |
332 | 332 | ||
333 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 333 | if (machine__resolve(machine, &al, sample) < 0) { |
334 | pr_warning("problem processing %d event, skipping it.\n", | 334 | pr_warning("problem processing %d event, skipping it.\n", |
335 | event->header.type); | 335 | event->header.type); |
336 | return -1; | 336 | return -1; |
@@ -1264,8 +1264,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1264 | if (ret < 0) | 1264 | if (ret < 0) |
1265 | return ret; | 1265 | return ret; |
1266 | 1266 | ||
1267 | perf_config(perf_default_config, NULL); | ||
1268 | |||
1269 | argc = parse_options(argc, argv, options, diff_usage, 0); | 1267 | argc = parse_options(argc, argv, options, diff_usage, 0); |
1270 | 1268 | ||
1271 | if (symbol__init(NULL) < 0) | 1269 | if (symbol__init(NULL) < 0) |
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 96c1a4cfbbbf..bc1de9b8fd67 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c | |||
@@ -86,8 +86,7 @@ static int check_emacsclient_version(void) | |||
86 | return -1; | 86 | return -1; |
87 | } | 87 | } |
88 | 88 | ||
89 | strbuf_remove(&buffer, 0, strlen("emacsclient")); | 89 | version = atoi(buffer.buf + strlen("emacsclient")); |
90 | version = atoi(buffer.buf); | ||
91 | 90 | ||
92 | if (version < 22) { | 91 | if (version < 22) { |
93 | fprintf(stderr, | 92 | fprintf(stderr, |
@@ -107,12 +106,14 @@ static void exec_woman_emacs(const char *path, const char *page) | |||
107 | 106 | ||
108 | if (!check_emacsclient_version()) { | 107 | if (!check_emacsclient_version()) { |
109 | /* This works only with emacsclient version >= 22. */ | 108 | /* This works only with emacsclient version >= 22. */ |
110 | struct strbuf man_page = STRBUF_INIT; | 109 | char *man_page; |
111 | 110 | ||
112 | if (!path) | 111 | if (!path) |
113 | path = "emacsclient"; | 112 | path = "emacsclient"; |
114 | strbuf_addf(&man_page, "(woman \"%s\")", page); | 113 | if (asprintf(&man_page, "(woman \"%s\")", page) > 0) { |
115 | execlp(path, "emacsclient", "-e", man_page.buf, NULL); | 114 | execlp(path, "emacsclient", "-e", man_page, NULL); |
115 | free(man_page); | ||
116 | } | ||
116 | warning("failed to exec '%s': %s", path, | 117 | warning("failed to exec '%s': %s", path, |
117 | strerror_r(errno, sbuf, sizeof(sbuf))); | 118 | strerror_r(errno, sbuf, sizeof(sbuf))); |
118 | } | 119 | } |
@@ -123,7 +124,7 @@ static void exec_man_konqueror(const char *path, const char *page) | |||
123 | const char *display = getenv("DISPLAY"); | 124 | const char *display = getenv("DISPLAY"); |
124 | 125 | ||
125 | if (display && *display) { | 126 | if (display && *display) { |
126 | struct strbuf man_page = STRBUF_INIT; | 127 | char *man_page; |
127 | const char *filename = "kfmclient"; | 128 | const char *filename = "kfmclient"; |
128 | char sbuf[STRERR_BUFSIZE]; | 129 | char sbuf[STRERR_BUFSIZE]; |
129 | 130 | ||
@@ -142,8 +143,10 @@ static void exec_man_konqueror(const char *path, const char *page) | |||
142 | filename = file; | 143 | filename = file; |
143 | } else | 144 | } else |
144 | path = "kfmclient"; | 145 | path = "kfmclient"; |
145 | strbuf_addf(&man_page, "man:%s(1)", page); | 146 | if (asprintf(&man_page, "man:%s(1)", page) > 0) { |
146 | execlp(path, filename, "newTab", man_page.buf, NULL); | 147 | execlp(path, filename, "newTab", man_page, NULL); |
148 | free(man_page); | ||
149 | } | ||
147 | warning("failed to exec '%s': %s", path, | 150 | warning("failed to exec '%s': %s", path, |
148 | strerror_r(errno, sbuf, sizeof(sbuf))); | 151 | strerror_r(errno, sbuf, sizeof(sbuf))); |
149 | } | 152 | } |
@@ -162,11 +165,13 @@ static void exec_man_man(const char *path, const char *page) | |||
162 | 165 | ||
163 | static void exec_man_cmd(const char *cmd, const char *page) | 166 | static void exec_man_cmd(const char *cmd, const char *page) |
164 | { | 167 | { |
165 | struct strbuf shell_cmd = STRBUF_INIT; | ||
166 | char sbuf[STRERR_BUFSIZE]; | 168 | char sbuf[STRERR_BUFSIZE]; |
169 | char *shell_cmd; | ||
167 | 170 | ||
168 | strbuf_addf(&shell_cmd, "%s %s", cmd, page); | 171 | if (asprintf(&shell_cmd, "%s %s", cmd, page) > 0) { |
169 | execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); | 172 | execl("/bin/sh", "sh", "-c", shell_cmd, NULL); |
173 | free(shell_cmd); | ||
174 | } | ||
170 | warning("failed to exec '%s': %s", cmd, | 175 | warning("failed to exec '%s': %s", cmd, |
171 | strerror_r(errno, sbuf, sizeof(sbuf))); | 176 | strerror_r(errno, sbuf, sizeof(sbuf))); |
172 | } | 177 | } |
@@ -273,7 +278,7 @@ static int perf_help_config(const char *var, const char *value, void *cb) | |||
273 | if (!prefixcmp(var, "man.")) | 278 | if (!prefixcmp(var, "man.")) |
274 | return add_man_viewer_info(var, value); | 279 | return add_man_viewer_info(var, value); |
275 | 280 | ||
276 | return perf_default_config(var, value, cb); | 281 | return 0; |
277 | } | 282 | } |
278 | 283 | ||
279 | static struct cmdnames main_cmds, other_cmds; | 284 | static struct cmdnames main_cmds, other_cmds; |
@@ -300,43 +305,33 @@ static int is_perf_command(const char *s) | |||
300 | is_in_cmdlist(&other_cmds, s); | 305 | is_in_cmdlist(&other_cmds, s); |
301 | } | 306 | } |
302 | 307 | ||
303 | static const char *prepend(const char *prefix, const char *cmd) | ||
304 | { | ||
305 | size_t pre_len = strlen(prefix); | ||
306 | size_t cmd_len = strlen(cmd); | ||
307 | char *p = malloc(pre_len + cmd_len + 1); | ||
308 | memcpy(p, prefix, pre_len); | ||
309 | strcpy(p + pre_len, cmd); | ||
310 | return p; | ||
311 | } | ||
312 | |||
313 | static const char *cmd_to_page(const char *perf_cmd) | 308 | static const char *cmd_to_page(const char *perf_cmd) |
314 | { | 309 | { |
310 | char *s; | ||
311 | |||
315 | if (!perf_cmd) | 312 | if (!perf_cmd) |
316 | return "perf"; | 313 | return "perf"; |
317 | else if (!prefixcmp(perf_cmd, "perf")) | 314 | else if (!prefixcmp(perf_cmd, "perf")) |
318 | return perf_cmd; | 315 | return perf_cmd; |
319 | else | 316 | |
320 | return prepend("perf-", perf_cmd); | 317 | return asprintf(&s, "perf-%s", perf_cmd) < 0 ? NULL : s; |
321 | } | 318 | } |
322 | 319 | ||
323 | static void setup_man_path(void) | 320 | static void setup_man_path(void) |
324 | { | 321 | { |
325 | struct strbuf new_path = STRBUF_INIT; | 322 | char *new_path; |
326 | const char *old_path = getenv("MANPATH"); | 323 | const char *old_path = getenv("MANPATH"); |
327 | 324 | ||
328 | /* We should always put ':' after our path. If there is no | 325 | /* We should always put ':' after our path. If there is no |
329 | * old_path, the ':' at the end will let 'man' to try | 326 | * old_path, the ':' at the end will let 'man' to try |
330 | * system-wide paths after ours to find the manual page. If | 327 | * system-wide paths after ours to find the manual page. If |
331 | * there is old_path, we need ':' as delimiter. */ | 328 | * there is old_path, we need ':' as delimiter. */ |
332 | strbuf_addstr(&new_path, system_path(PERF_MAN_PATH)); | 329 | if (asprintf(&new_path, "%s:%s", system_path(PERF_MAN_PATH), old_path ?: "") > 0) { |
333 | strbuf_addch(&new_path, ':'); | 330 | setenv("MANPATH", new_path, 1); |
334 | if (old_path) | 331 | free(new_path); |
335 | strbuf_addstr(&new_path, old_path); | 332 | } else { |
336 | 333 | error("Unable to setup man path"); | |
337 | setenv("MANPATH", new_path.buf, 1); | 334 | } |
338 | |||
339 | strbuf_release(&new_path); | ||
340 | } | 335 | } |
341 | 336 | ||
342 | static void exec_viewer(const char *name, const char *page) | 337 | static void exec_viewer(const char *name, const char *page) |
@@ -381,7 +376,7 @@ static int show_info_page(const char *perf_cmd) | |||
381 | return -1; | 376 | return -1; |
382 | } | 377 | } |
383 | 378 | ||
384 | static int get_html_page_path(struct strbuf *page_path, const char *page) | 379 | static int get_html_page_path(char **page_path, const char *page) |
385 | { | 380 | { |
386 | struct stat st; | 381 | struct stat st; |
387 | const char *html_path = system_path(PERF_HTML_PATH); | 382 | const char *html_path = system_path(PERF_HTML_PATH); |
@@ -393,10 +388,7 @@ static int get_html_page_path(struct strbuf *page_path, const char *page) | |||
393 | return -1; | 388 | return -1; |
394 | } | 389 | } |
395 | 390 | ||
396 | strbuf_init(page_path, 0); | 391 | return asprintf(page_path, "%s/%s.html", html_path, page); |
397 | strbuf_addf(page_path, "%s/%s.html", html_path, page); | ||
398 | |||
399 | return 0; | ||
400 | } | 392 | } |
401 | 393 | ||
402 | /* | 394 | /* |
@@ -414,12 +406,12 @@ static void open_html(const char *path) | |||
414 | static int show_html_page(const char *perf_cmd) | 406 | static int show_html_page(const char *perf_cmd) |
415 | { | 407 | { |
416 | const char *page = cmd_to_page(perf_cmd); | 408 | const char *page = cmd_to_page(perf_cmd); |
417 | struct strbuf page_path; /* it leaks but we exec bellow */ | 409 | char *page_path; /* it leaks but we exec bellow */ |
418 | 410 | ||
419 | if (get_html_page_path(&page_path, page) != 0) | 411 | if (get_html_page_path(&page_path, page) < 0) |
420 | return -1; | 412 | return -1; |
421 | 413 | ||
422 | open_html(page_path.buf); | 414 | open_html(page_path); |
423 | 415 | ||
424 | return 0; | 416 | return 0; |
425 | } | 417 | } |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 0022e02ed31a..d1a2d104f2bc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/build-id.h" | 17 | #include "util/build-id.h" |
18 | #include "util/data.h" | 18 | #include "util/data.h" |
19 | #include "util/auxtrace.h" | 19 | #include "util/auxtrace.h" |
20 | #include "util/jit.h" | ||
20 | 21 | ||
21 | #include <subcmd/parse-options.h> | 22 | #include <subcmd/parse-options.h> |
22 | 23 | ||
@@ -29,6 +30,7 @@ struct perf_inject { | |||
29 | bool sched_stat; | 30 | bool sched_stat; |
30 | bool have_auxtrace; | 31 | bool have_auxtrace; |
31 | bool strip; | 32 | bool strip; |
33 | bool jit_mode; | ||
32 | const char *input_name; | 34 | const char *input_name; |
33 | struct perf_data_file output; | 35 | struct perf_data_file output; |
34 | u64 bytes_written; | 36 | u64 bytes_written; |
@@ -71,6 +73,15 @@ static int perf_event__repipe_oe_synth(struct perf_tool *tool, | |||
71 | return perf_event__repipe_synth(tool, event); | 73 | return perf_event__repipe_synth(tool, event); |
72 | } | 74 | } |
73 | 75 | ||
76 | #ifdef HAVE_JITDUMP | ||
77 | static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, | ||
78 | union perf_event *event __maybe_unused, | ||
79 | struct ordered_events *oe __maybe_unused) | ||
80 | { | ||
81 | return 0; | ||
82 | } | ||
83 | #endif | ||
84 | |||
74 | static int perf_event__repipe_op2_synth(struct perf_tool *tool, | 85 | static int perf_event__repipe_op2_synth(struct perf_tool *tool, |
75 | union perf_event *event, | 86 | union perf_event *event, |
76 | struct perf_session *session | 87 | struct perf_session *session |
@@ -120,8 +131,7 @@ static int copy_bytes(struct perf_inject *inject, int fd, off_t size) | |||
120 | 131 | ||
121 | static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, | 132 | static s64 perf_event__repipe_auxtrace(struct perf_tool *tool, |
122 | union perf_event *event, | 133 | union perf_event *event, |
123 | struct perf_session *session | 134 | struct perf_session *session) |
124 | __maybe_unused) | ||
125 | { | 135 | { |
126 | struct perf_inject *inject = container_of(tool, struct perf_inject, | 136 | struct perf_inject *inject = container_of(tool, struct perf_inject, |
127 | tool); | 137 | tool); |
@@ -234,6 +244,31 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, | |||
234 | return err; | 244 | return err; |
235 | } | 245 | } |
236 | 246 | ||
247 | #ifdef HAVE_JITDUMP | ||
248 | static int perf_event__jit_repipe_mmap(struct perf_tool *tool, | ||
249 | union perf_event *event, | ||
250 | struct perf_sample *sample, | ||
251 | struct machine *machine) | ||
252 | { | ||
253 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
254 | u64 n = 0; | ||
255 | int ret; | ||
256 | |||
257 | /* | ||
258 | * if jit marker, then inject jit mmaps and generate ELF images | ||
259 | */ | ||
260 | ret = jit_process(inject->session, &inject->output, machine, | ||
261 | event->mmap.filename, sample->pid, &n); | ||
262 | if (ret < 0) | ||
263 | return ret; | ||
264 | if (ret) { | ||
265 | inject->bytes_written += n; | ||
266 | return 0; | ||
267 | } | ||
268 | return perf_event__repipe_mmap(tool, event, sample, machine); | ||
269 | } | ||
270 | #endif | ||
271 | |||
237 | static int perf_event__repipe_mmap2(struct perf_tool *tool, | 272 | static int perf_event__repipe_mmap2(struct perf_tool *tool, |
238 | union perf_event *event, | 273 | union perf_event *event, |
239 | struct perf_sample *sample, | 274 | struct perf_sample *sample, |
@@ -247,6 +282,31 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool, | |||
247 | return err; | 282 | return err; |
248 | } | 283 | } |
249 | 284 | ||
285 | #ifdef HAVE_JITDUMP | ||
286 | static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, | ||
287 | union perf_event *event, | ||
288 | struct perf_sample *sample, | ||
289 | struct machine *machine) | ||
290 | { | ||
291 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
292 | u64 n = 0; | ||
293 | int ret; | ||
294 | |||
295 | /* | ||
296 | * if jit marker, then inject jit mmaps and generate ELF images | ||
297 | */ | ||
298 | ret = jit_process(inject->session, &inject->output, machine, | ||
299 | event->mmap2.filename, sample->pid, &n); | ||
300 | if (ret < 0) | ||
301 | return ret; | ||
302 | if (ret) { | ||
303 | inject->bytes_written += n; | ||
304 | return 0; | ||
305 | } | ||
306 | return perf_event__repipe_mmap2(tool, event, sample, machine); | ||
307 | } | ||
308 | #endif | ||
309 | |||
250 | static int perf_event__repipe_fork(struct perf_tool *tool, | 310 | static int perf_event__repipe_fork(struct perf_tool *tool, |
251 | union perf_event *event, | 311 | union perf_event *event, |
252 | struct perf_sample *sample, | 312 | struct perf_sample *sample, |
@@ -356,9 +416,6 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
356 | { | 416 | { |
357 | struct addr_location al; | 417 | struct addr_location al; |
358 | struct thread *thread; | 418 | struct thread *thread; |
359 | u8 cpumode; | ||
360 | |||
361 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
362 | 419 | ||
363 | thread = machine__findnew_thread(machine, sample->pid, sample->tid); | 420 | thread = machine__findnew_thread(machine, sample->pid, sample->tid); |
364 | if (thread == NULL) { | 421 | if (thread == NULL) { |
@@ -367,7 +424,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
367 | goto repipe; | 424 | goto repipe; |
368 | } | 425 | } |
369 | 426 | ||
370 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); | 427 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, &al); |
371 | 428 | ||
372 | if (al.map != NULL) { | 429 | if (al.map != NULL) { |
373 | if (!al.map->dso->hit) { | 430 | if (!al.map->dso->hit) { |
@@ -626,12 +683,16 @@ static int __cmd_inject(struct perf_inject *inject) | |||
626 | ret = perf_session__process_events(session); | 683 | ret = perf_session__process_events(session); |
627 | 684 | ||
628 | if (!file_out->is_pipe) { | 685 | if (!file_out->is_pipe) { |
629 | if (inject->build_ids) { | 686 | if (inject->build_ids) |
630 | perf_header__set_feat(&session->header, | 687 | perf_header__set_feat(&session->header, |
631 | HEADER_BUILD_ID); | 688 | HEADER_BUILD_ID); |
632 | if (inject->have_auxtrace) | 689 | /* |
633 | dsos__hit_all(session); | 690 | * Keep all buildids when there is unprocessed AUX data because |
634 | } | 691 | * it is not known which ones the AUX trace hits. |
692 | */ | ||
693 | if (perf_header__has_feat(&session->header, HEADER_BUILD_ID) && | ||
694 | inject->have_auxtrace && !inject->itrace_synth_opts.set) | ||
695 | dsos__hit_all(session); | ||
635 | /* | 696 | /* |
636 | * The AUX areas have been removed and replaced with | 697 | * The AUX areas have been removed and replaced with |
637 | * synthesized hardware events, so clear the feature flag and | 698 | * synthesized hardware events, so clear the feature flag and |
@@ -703,7 +764,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
703 | }; | 764 | }; |
704 | int ret; | 765 | int ret; |
705 | 766 | ||
706 | const struct option options[] = { | 767 | struct option options[] = { |
707 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 768 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
708 | "Inject build-ids into the output stream"), | 769 | "Inject build-ids into the output stream"), |
709 | OPT_STRING('i', "input", &inject.input_name, "file", | 770 | OPT_STRING('i', "input", &inject.input_name, "file", |
@@ -713,6 +774,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
713 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, | 774 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, |
714 | "Merge sched-stat and sched-switch for getting events " | 775 | "Merge sched-stat and sched-switch for getting events " |
715 | "where and how long tasks slept"), | 776 | "where and how long tasks slept"), |
777 | #ifdef HAVE_JITDUMP | ||
778 | OPT_BOOLEAN('j', "jit", &inject.jit_mode, "merge jitdump files into perf.data file"), | ||
779 | #endif | ||
716 | OPT_INCR('v', "verbose", &verbose, | 780 | OPT_INCR('v', "verbose", &verbose, |
717 | "be more verbose (show build ids, etc)"), | 781 | "be more verbose (show build ids, etc)"), |
718 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", | 782 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", |
@@ -729,7 +793,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
729 | "perf inject [<options>]", | 793 | "perf inject [<options>]", |
730 | NULL | 794 | NULL |
731 | }; | 795 | }; |
732 | 796 | #ifndef HAVE_JITDUMP | |
797 | set_option_nobuild(options, 'j', "jit", "NO_LIBELF=1", true); | ||
798 | #endif | ||
733 | argc = parse_options(argc, argv, options, inject_usage, 0); | 799 | argc = parse_options(argc, argv, options, inject_usage, 0); |
734 | 800 | ||
735 | /* | 801 | /* |
@@ -755,6 +821,29 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
755 | if (inject.session == NULL) | 821 | if (inject.session == NULL) |
756 | return -1; | 822 | return -1; |
757 | 823 | ||
824 | if (inject.build_ids) { | ||
825 | /* | ||
826 | * to make sure the mmap records are ordered correctly | ||
827 | * and so that the correct especially due to jitted code | ||
828 | * mmaps. We cannot generate the buildid hit list and | ||
829 | * inject the jit mmaps at the same time for now. | ||
830 | */ | ||
831 | inject.tool.ordered_events = true; | ||
832 | inject.tool.ordering_requires_timestamps = true; | ||
833 | } | ||
834 | #ifdef HAVE_JITDUMP | ||
835 | if (inject.jit_mode) { | ||
836 | inject.tool.mmap2 = perf_event__jit_repipe_mmap2; | ||
837 | inject.tool.mmap = perf_event__jit_repipe_mmap; | ||
838 | inject.tool.ordered_events = true; | ||
839 | inject.tool.ordering_requires_timestamps = true; | ||
840 | /* | ||
841 | * JIT MMAP injection injects all MMAP events in one go, so it | ||
842 | * does not obey finished_round semantics. | ||
843 | */ | ||
844 | inject.tool.finished_round = perf_event__drop_oe; | ||
845 | } | ||
846 | #endif | ||
758 | ret = symbol__init(&inject.session->header.env); | 847 | ret = symbol__init(&inject.session->header.env); |
759 | if (ret < 0) | 848 | if (ret < 0) |
760 | goto out_delete; | 849 | goto out_delete; |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 118010553d0c..c9cb3be47cff 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -602,7 +602,7 @@ static int gfpcmp(const void *a, const void *b) | |||
602 | return fa->flags - fb->flags; | 602 | return fa->flags - fb->flags; |
603 | } | 603 | } |
604 | 604 | ||
605 | /* see include/trace/events/gfpflags.h */ | 605 | /* see include/trace/events/mmflags.h */ |
606 | static const struct { | 606 | static const struct { |
607 | const char *original; | 607 | const char *original; |
608 | const char *compact; | 608 | const char *compact; |
@@ -612,30 +612,39 @@ static const struct { | |||
612 | { "GFP_HIGHUSER", "HU" }, | 612 | { "GFP_HIGHUSER", "HU" }, |
613 | { "GFP_USER", "U" }, | 613 | { "GFP_USER", "U" }, |
614 | { "GFP_TEMPORARY", "TMP" }, | 614 | { "GFP_TEMPORARY", "TMP" }, |
615 | { "GFP_KERNEL_ACCOUNT", "KAC" }, | ||
615 | { "GFP_KERNEL", "K" }, | 616 | { "GFP_KERNEL", "K" }, |
616 | { "GFP_NOFS", "NF" }, | 617 | { "GFP_NOFS", "NF" }, |
617 | { "GFP_ATOMIC", "A" }, | 618 | { "GFP_ATOMIC", "A" }, |
618 | { "GFP_NOIO", "NI" }, | 619 | { "GFP_NOIO", "NI" }, |
619 | { "GFP_HIGH", "H" }, | ||
620 | { "GFP_WAIT", "W" }, | ||
621 | { "GFP_IO", "I" }, | ||
622 | { "GFP_COLD", "CO" }, | ||
623 | { "GFP_NOWARN", "NWR" }, | ||
624 | { "GFP_REPEAT", "R" }, | ||
625 | { "GFP_NOFAIL", "NF" }, | ||
626 | { "GFP_NORETRY", "NR" }, | ||
627 | { "GFP_COMP", "C" }, | ||
628 | { "GFP_ZERO", "Z" }, | ||
629 | { "GFP_NOMEMALLOC", "NMA" }, | ||
630 | { "GFP_MEMALLOC", "MA" }, | ||
631 | { "GFP_HARDWALL", "HW" }, | ||
632 | { "GFP_THISNODE", "TN" }, | ||
633 | { "GFP_RECLAIMABLE", "RC" }, | ||
634 | { "GFP_MOVABLE", "M" }, | ||
635 | { "GFP_NOTRACK", "NT" }, | ||
636 | { "GFP_NO_KSWAPD", "NK" }, | ||
637 | { "GFP_OTHER_NODE", "ON" }, | ||
638 | { "GFP_NOWAIT", "NW" }, | 620 | { "GFP_NOWAIT", "NW" }, |
621 | { "GFP_DMA", "D" }, | ||
622 | { "__GFP_HIGHMEM", "HM" }, | ||
623 | { "GFP_DMA32", "D32" }, | ||
624 | { "__GFP_HIGH", "H" }, | ||
625 | { "__GFP_ATOMIC", "_A" }, | ||
626 | { "__GFP_IO", "I" }, | ||
627 | { "__GFP_FS", "F" }, | ||
628 | { "__GFP_COLD", "CO" }, | ||
629 | { "__GFP_NOWARN", "NWR" }, | ||
630 | { "__GFP_REPEAT", "R" }, | ||
631 | { "__GFP_NOFAIL", "NF" }, | ||
632 | { "__GFP_NORETRY", "NR" }, | ||
633 | { "__GFP_COMP", "C" }, | ||
634 | { "__GFP_ZERO", "Z" }, | ||
635 | { "__GFP_NOMEMALLOC", "NMA" }, | ||
636 | { "__GFP_MEMALLOC", "MA" }, | ||
637 | { "__GFP_HARDWALL", "HW" }, | ||
638 | { "__GFP_THISNODE", "TN" }, | ||
639 | { "__GFP_RECLAIMABLE", "RC" }, | ||
640 | { "__GFP_MOVABLE", "M" }, | ||
641 | { "__GFP_ACCOUNT", "AC" }, | ||
642 | { "__GFP_NOTRACK", "NT" }, | ||
643 | { "__GFP_WRITE", "WR" }, | ||
644 | { "__GFP_RECLAIM", "R" }, | ||
645 | { "__GFP_DIRECT_RECLAIM", "DR" }, | ||
646 | { "__GFP_KSWAPD_RECLAIM", "KR" }, | ||
647 | { "__GFP_OTHER_NODE", "ON" }, | ||
639 | }; | 648 | }; |
640 | 649 | ||
641 | static size_t max_gfp_len; | 650 | static size_t max_gfp_len; |
@@ -1834,7 +1843,7 @@ static int __cmd_record(int argc, const char **argv) | |||
1834 | return cmd_record(i, rec_argv, NULL); | 1843 | return cmd_record(i, rec_argv, NULL); |
1835 | } | 1844 | } |
1836 | 1845 | ||
1837 | static int kmem_config(const char *var, const char *value, void *cb) | 1846 | static int kmem_config(const char *var, const char *value, void *cb __maybe_unused) |
1838 | { | 1847 | { |
1839 | if (!strcmp(var, "kmem.default")) { | 1848 | if (!strcmp(var, "kmem.default")) { |
1840 | if (!strcmp(value, "slab")) | 1849 | if (!strcmp(value, "slab")) |
@@ -1847,7 +1856,7 @@ static int kmem_config(const char *var, const char *value, void *cb) | |||
1847 | return 0; | 1856 | return 0; |
1848 | } | 1857 | } |
1849 | 1858 | ||
1850 | return perf_default_config(var, value, cb); | 1859 | return 0; |
1851 | } | 1860 | } |
1852 | 1861 | ||
1853 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | 1862 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 4418d9214872..bff666458b28 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <math.h> | 30 | #include <math.h> |
31 | 31 | ||
32 | #ifdef HAVE_KVM_STAT_SUPPORT | 32 | #ifdef HAVE_KVM_STAT_SUPPORT |
33 | #include <asm/kvm_perf.h> | ||
34 | #include "util/kvm-stat.h" | 33 | #include "util/kvm-stat.h" |
35 | 34 | ||
36 | void exit_event_get_key(struct perf_evsel *evsel, | 35 | void exit_event_get_key(struct perf_evsel *evsel, |
@@ -38,12 +37,12 @@ void exit_event_get_key(struct perf_evsel *evsel, | |||
38 | struct event_key *key) | 37 | struct event_key *key) |
39 | { | 38 | { |
40 | key->info = 0; | 39 | key->info = 0; |
41 | key->key = perf_evsel__intval(evsel, sample, KVM_EXIT_REASON); | 40 | key->key = perf_evsel__intval(evsel, sample, kvm_exit_reason); |
42 | } | 41 | } |
43 | 42 | ||
44 | bool kvm_exit_event(struct perf_evsel *evsel) | 43 | bool kvm_exit_event(struct perf_evsel *evsel) |
45 | { | 44 | { |
46 | return !strcmp(evsel->name, KVM_EXIT_TRACE); | 45 | return !strcmp(evsel->name, kvm_exit_trace); |
47 | } | 46 | } |
48 | 47 | ||
49 | bool exit_event_begin(struct perf_evsel *evsel, | 48 | bool exit_event_begin(struct perf_evsel *evsel, |
@@ -59,7 +58,7 @@ bool exit_event_begin(struct perf_evsel *evsel, | |||
59 | 58 | ||
60 | bool kvm_entry_event(struct perf_evsel *evsel) | 59 | bool kvm_entry_event(struct perf_evsel *evsel) |
61 | { | 60 | { |
62 | return !strcmp(evsel->name, KVM_ENTRY_TRACE); | 61 | return !strcmp(evsel->name, kvm_entry_trace); |
63 | } | 62 | } |
64 | 63 | ||
65 | bool exit_event_end(struct perf_evsel *evsel, | 64 | bool exit_event_end(struct perf_evsel *evsel, |
@@ -91,7 +90,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm, | |||
91 | const char *exit_reason = get_exit_reason(kvm, key->exit_reasons, | 90 | const char *exit_reason = get_exit_reason(kvm, key->exit_reasons, |
92 | key->key); | 91 | key->key); |
93 | 92 | ||
94 | scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason); | 93 | scnprintf(decode, decode_str_len, "%s", exit_reason); |
95 | } | 94 | } |
96 | 95 | ||
97 | static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) | 96 | static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) |
@@ -357,7 +356,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, | |||
357 | time_diff = sample->time - time_begin; | 356 | time_diff = sample->time - time_begin; |
358 | 357 | ||
359 | if (kvm->duration && time_diff > kvm->duration) { | 358 | if (kvm->duration && time_diff > kvm->duration) { |
360 | char decode[DECODE_STR_LEN]; | 359 | char decode[decode_str_len]; |
361 | 360 | ||
362 | kvm->events_ops->decode_key(kvm, &event->key, decode); | 361 | kvm->events_ops->decode_key(kvm, &event->key, decode); |
363 | if (!skip_event(decode)) { | 362 | if (!skip_event(decode)) { |
@@ -385,7 +384,8 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
385 | return NULL; | 384 | return NULL; |
386 | } | 385 | } |
387 | 386 | ||
388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); | 387 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, |
388 | vcpu_id_str); | ||
389 | thread__set_priv(thread, vcpu_record); | 389 | thread__set_priv(thread, vcpu_record); |
390 | } | 390 | } |
391 | 391 | ||
@@ -574,7 +574,7 @@ static void show_timeofday(void) | |||
574 | 574 | ||
575 | static void print_result(struct perf_kvm_stat *kvm) | 575 | static void print_result(struct perf_kvm_stat *kvm) |
576 | { | 576 | { |
577 | char decode[DECODE_STR_LEN]; | 577 | char decode[decode_str_len]; |
578 | struct kvm_event *event; | 578 | struct kvm_event *event; |
579 | int vcpu = kvm->trace_vcpu; | 579 | int vcpu = kvm->trace_vcpu; |
580 | 580 | ||
@@ -585,7 +585,7 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
585 | 585 | ||
586 | pr_info("\n\n"); | 586 | pr_info("\n\n"); |
587 | print_vcpu_info(kvm); | 587 | print_vcpu_info(kvm); |
588 | pr_info("%*s ", DECODE_STR_LEN, kvm->events_ops->name); | 588 | pr_info("%*s ", decode_str_len, kvm->events_ops->name); |
589 | pr_info("%10s ", "Samples"); | 589 | pr_info("%10s ", "Samples"); |
590 | pr_info("%9s ", "Samples%"); | 590 | pr_info("%9s ", "Samples%"); |
591 | 591 | ||
@@ -604,7 +604,7 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
604 | min = get_event_min(event, vcpu); | 604 | min = get_event_min(event, vcpu); |
605 | 605 | ||
606 | kvm->events_ops->decode_key(kvm, &event->key, decode); | 606 | kvm->events_ops->decode_key(kvm, &event->key, decode); |
607 | pr_info("%*s ", DECODE_STR_LEN, decode); | 607 | pr_info("%*s ", decode_str_len, decode); |
608 | pr_info("%10llu ", (unsigned long long)ecount); | 608 | pr_info("%10llu ", (unsigned long long)ecount); |
609 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); | 609 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); |
610 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); | 610 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); |
@@ -1132,6 +1132,11 @@ exit: | |||
1132 | _p; \ | 1132 | _p; \ |
1133 | }) | 1133 | }) |
1134 | 1134 | ||
1135 | int __weak setup_kvm_events_tp(struct perf_kvm_stat *kvm __maybe_unused) | ||
1136 | { | ||
1137 | return 0; | ||
1138 | } | ||
1139 | |||
1135 | static int | 1140 | static int |
1136 | kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | 1141 | kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) |
1137 | { | 1142 | { |
@@ -1148,7 +1153,14 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
1148 | NULL | 1153 | NULL |
1149 | }; | 1154 | }; |
1150 | const char * const *events_tp; | 1155 | const char * const *events_tp; |
1156 | int ret; | ||
1157 | |||
1151 | events_tp_size = 0; | 1158 | events_tp_size = 0; |
1159 | ret = setup_kvm_events_tp(kvm); | ||
1160 | if (ret < 0) { | ||
1161 | pr_err("Unable to setup the kvm tracepoints\n"); | ||
1162 | return ret; | ||
1163 | } | ||
1152 | 1164 | ||
1153 | for (events_tp = kvm_events_tp; *events_tp; events_tp++) | 1165 | for (events_tp = kvm_events_tp; *events_tp; events_tp++) |
1154 | events_tp_size++; | 1166 | events_tp_size++; |
@@ -1377,6 +1389,12 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
1377 | /* | 1389 | /* |
1378 | * generate the event list | 1390 | * generate the event list |
1379 | */ | 1391 | */ |
1392 | err = setup_kvm_events_tp(kvm); | ||
1393 | if (err < 0) { | ||
1394 | pr_err("Unable to setup the kvm tracepoints\n"); | ||
1395 | return err; | ||
1396 | } | ||
1397 | |||
1380 | kvm->evlist = kvm_live_event_list(); | 1398 | kvm->evlist = kvm_live_event_list(); |
1381 | if (kvm->evlist == NULL) { | 1399 | if (kvm->evlist == NULL) { |
1382 | err = -1; | 1400 | err = -1; |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 390170041696..85db3be4b3cb 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -6,6 +6,8 @@ | |||
6 | #include "util/tool.h" | 6 | #include "util/tool.h" |
7 | #include "util/session.h" | 7 | #include "util/session.h" |
8 | #include "util/data.h" | 8 | #include "util/data.h" |
9 | #include "util/mem-events.h" | ||
10 | #include "util/debug.h" | ||
9 | 11 | ||
10 | #define MEM_OPERATION_LOAD 0x1 | 12 | #define MEM_OPERATION_LOAD 0x1 |
11 | #define MEM_OPERATION_STORE 0x2 | 13 | #define MEM_OPERATION_STORE 0x2 |
@@ -21,11 +23,56 @@ struct perf_mem { | |||
21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 23 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
22 | }; | 24 | }; |
23 | 25 | ||
26 | static int parse_record_events(const struct option *opt, | ||
27 | const char *str, int unset __maybe_unused) | ||
28 | { | ||
29 | struct perf_mem *mem = *(struct perf_mem **)opt->value; | ||
30 | int j; | ||
31 | |||
32 | if (strcmp(str, "list")) { | ||
33 | if (!perf_mem_events__parse(str)) { | ||
34 | mem->operation = 0; | ||
35 | return 0; | ||
36 | } | ||
37 | exit(-1); | ||
38 | } | ||
39 | |||
40 | for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { | ||
41 | struct perf_mem_event *e = &perf_mem_events[j]; | ||
42 | |||
43 | fprintf(stderr, "%-13s%-*s%s\n", | ||
44 | e->tag, | ||
45 | verbose ? 25 : 0, | ||
46 | verbose ? perf_mem_events__name(j) : "", | ||
47 | e->supported ? ": available" : ""); | ||
48 | } | ||
49 | exit(0); | ||
50 | } | ||
51 | |||
52 | static const char * const __usage[] = { | ||
53 | "perf mem record [<options>] [<command>]", | ||
54 | "perf mem record [<options>] -- <command> [<options>]", | ||
55 | NULL | ||
56 | }; | ||
57 | |||
58 | static const char * const *record_mem_usage = __usage; | ||
59 | |||
24 | static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) | 60 | static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) |
25 | { | 61 | { |
26 | int rec_argc, i = 0, j; | 62 | int rec_argc, i = 0, j; |
27 | const char **rec_argv; | 63 | const char **rec_argv; |
28 | int ret; | 64 | int ret; |
65 | struct option options[] = { | ||
66 | OPT_CALLBACK('e', "event", &mem, "event", | ||
67 | "event selector. use 'perf mem record -e list' to list available events", | ||
68 | parse_record_events), | ||
69 | OPT_INCR('v', "verbose", &verbose, | ||
70 | "be more verbose (show counter open errors, etc)"), | ||
71 | OPT_END() | ||
72 | }; | ||
73 | |||
74 | argc = parse_options(argc, argv, options, record_mem_usage, | ||
75 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
29 | 76 | ||
30 | rec_argc = argc + 7; /* max number of arguments */ | 77 | rec_argc = argc + 7; /* max number of arguments */ |
31 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 78 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
@@ -35,23 +82,40 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) | |||
35 | rec_argv[i++] = "record"; | 82 | rec_argv[i++] = "record"; |
36 | 83 | ||
37 | if (mem->operation & MEM_OPERATION_LOAD) | 84 | if (mem->operation & MEM_OPERATION_LOAD) |
85 | perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true; | ||
86 | |||
87 | if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record) | ||
38 | rec_argv[i++] = "-W"; | 88 | rec_argv[i++] = "-W"; |
39 | 89 | ||
40 | rec_argv[i++] = "-d"; | 90 | rec_argv[i++] = "-d"; |
41 | 91 | ||
42 | if (mem->operation & MEM_OPERATION_LOAD) { | 92 | for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { |
43 | rec_argv[i++] = "-e"; | 93 | if (!perf_mem_events[j].record) |
44 | rec_argv[i++] = "cpu/mem-loads/pp"; | 94 | continue; |
45 | } | 95 | |
96 | if (!perf_mem_events[j].supported) { | ||
97 | pr_err("failed: event '%s' not supported\n", | ||
98 | perf_mem_events__name(j)); | ||
99 | return -1; | ||
100 | } | ||
46 | 101 | ||
47 | if (mem->operation & MEM_OPERATION_STORE) { | ||
48 | rec_argv[i++] = "-e"; | 102 | rec_argv[i++] = "-e"; |
49 | rec_argv[i++] = "cpu/mem-stores/pp"; | 103 | rec_argv[i++] = perf_mem_events__name(j); |
50 | } | 104 | }; |
51 | 105 | ||
52 | for (j = 1; j < argc; j++, i++) | 106 | for (j = 0; j < argc; j++, i++) |
53 | rec_argv[i] = argv[j]; | 107 | rec_argv[i] = argv[j]; |
54 | 108 | ||
109 | if (verbose > 0) { | ||
110 | pr_debug("calling: record "); | ||
111 | |||
112 | while (rec_argv[j]) { | ||
113 | pr_debug("%s ", rec_argv[j]); | ||
114 | j++; | ||
115 | } | ||
116 | pr_debug("\n"); | ||
117 | } | ||
118 | |||
55 | ret = cmd_record(i, rec_argv, NULL); | 119 | ret = cmd_record(i, rec_argv, NULL); |
56 | free(rec_argv); | 120 | free(rec_argv); |
57 | return ret; | 121 | return ret; |
@@ -67,7 +131,7 @@ dump_raw_samples(struct perf_tool *tool, | |||
67 | struct addr_location al; | 131 | struct addr_location al; |
68 | const char *fmt; | 132 | const char *fmt; |
69 | 133 | ||
70 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 134 | if (machine__resolve(machine, &al, sample) < 0) { |
71 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 135 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
72 | event->header.type); | 136 | event->header.type); |
73 | return -1; | 137 | return -1; |
@@ -298,6 +362,10 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
298 | NULL | 362 | NULL |
299 | }; | 363 | }; |
300 | 364 | ||
365 | if (perf_mem_events__init()) { | ||
366 | pr_err("failed: memory events not supported\n"); | ||
367 | return -1; | ||
368 | } | ||
301 | 369 | ||
302 | argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, | 370 | argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, |
303 | mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); | 371 | mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 319712a4e02b..515510ecc76a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include "util/parse-branch-options.h" | 32 | #include "util/parse-branch-options.h" |
33 | #include "util/parse-regs-options.h" | 33 | #include "util/parse-regs-options.h" |
34 | #include "util/llvm-utils.h" | 34 | #include "util/llvm-utils.h" |
35 | #include "util/bpf-loader.h" | ||
36 | #include "asm/bug.h" | ||
35 | 37 | ||
36 | #include <unistd.h> | 38 | #include <unistd.h> |
37 | #include <sched.h> | 39 | #include <sched.h> |
@@ -49,7 +51,9 @@ struct record { | |||
49 | const char *progname; | 51 | const char *progname; |
50 | int realtime_prio; | 52 | int realtime_prio; |
51 | bool no_buildid; | 53 | bool no_buildid; |
54 | bool no_buildid_set; | ||
52 | bool no_buildid_cache; | 55 | bool no_buildid_cache; |
56 | bool no_buildid_cache_set; | ||
53 | bool buildid_all; | 57 | bool buildid_all; |
54 | unsigned long long samples; | 58 | unsigned long long samples; |
55 | }; | 59 | }; |
@@ -320,7 +324,10 @@ try_again: | |||
320 | } else { | 324 | } else { |
321 | pr_err("failed to mmap with %d (%s)\n", errno, | 325 | pr_err("failed to mmap with %d (%s)\n", errno, |
322 | strerror_r(errno, msg, sizeof(msg))); | 326 | strerror_r(errno, msg, sizeof(msg))); |
323 | rc = -errno; | 327 | if (errno) |
328 | rc = -errno; | ||
329 | else | ||
330 | rc = -EINVAL; | ||
324 | } | 331 | } |
325 | goto out; | 332 | goto out; |
326 | } | 333 | } |
@@ -464,6 +471,29 @@ static void record__init_features(struct record *rec) | |||
464 | perf_header__clear_feat(&session->header, HEADER_STAT); | 471 | perf_header__clear_feat(&session->header, HEADER_STAT); |
465 | } | 472 | } |
466 | 473 | ||
474 | static void | ||
475 | record__finish_output(struct record *rec) | ||
476 | { | ||
477 | struct perf_data_file *file = &rec->file; | ||
478 | int fd = perf_data_file__fd(file); | ||
479 | |||
480 | if (file->is_pipe) | ||
481 | return; | ||
482 | |||
483 | rec->session->header.data_size += rec->bytes_written; | ||
484 | file->size = lseek(perf_data_file__fd(file), 0, SEEK_CUR); | ||
485 | |||
486 | if (!rec->no_buildid) { | ||
487 | process_buildids(rec); | ||
488 | |||
489 | if (rec->buildid_all) | ||
490 | dsos__hit_all(rec->session); | ||
491 | } | ||
492 | perf_session__write_header(rec->session, rec->evlist, fd, true); | ||
493 | |||
494 | return; | ||
495 | } | ||
496 | |||
467 | static volatile int workload_exec_errno; | 497 | static volatile int workload_exec_errno; |
468 | 498 | ||
469 | /* | 499 | /* |
@@ -482,6 +512,74 @@ static void workload_exec_failed_signal(int signo __maybe_unused, | |||
482 | 512 | ||
483 | static void snapshot_sig_handler(int sig); | 513 | static void snapshot_sig_handler(int sig); |
484 | 514 | ||
515 | static int record__synthesize(struct record *rec) | ||
516 | { | ||
517 | struct perf_session *session = rec->session; | ||
518 | struct machine *machine = &session->machines.host; | ||
519 | struct perf_data_file *file = &rec->file; | ||
520 | struct record_opts *opts = &rec->opts; | ||
521 | struct perf_tool *tool = &rec->tool; | ||
522 | int fd = perf_data_file__fd(file); | ||
523 | int err = 0; | ||
524 | |||
525 | if (file->is_pipe) { | ||
526 | err = perf_event__synthesize_attrs(tool, session, | ||
527 | process_synthesized_event); | ||
528 | if (err < 0) { | ||
529 | pr_err("Couldn't synthesize attrs.\n"); | ||
530 | goto out; | ||
531 | } | ||
532 | |||
533 | if (have_tracepoints(&rec->evlist->entries)) { | ||
534 | /* | ||
535 | * FIXME err <= 0 here actually means that | ||
536 | * there were no tracepoints so its not really | ||
537 | * an error, just that we don't need to | ||
538 | * synthesize anything. We really have to | ||
539 | * return this more properly and also | ||
540 | * propagate errors that now are calling die() | ||
541 | */ | ||
542 | err = perf_event__synthesize_tracing_data(tool, fd, rec->evlist, | ||
543 | process_synthesized_event); | ||
544 | if (err <= 0) { | ||
545 | pr_err("Couldn't record tracing data.\n"); | ||
546 | goto out; | ||
547 | } | ||
548 | rec->bytes_written += err; | ||
549 | } | ||
550 | } | ||
551 | |||
552 | if (rec->opts.full_auxtrace) { | ||
553 | err = perf_event__synthesize_auxtrace_info(rec->itr, tool, | ||
554 | session, process_synthesized_event); | ||
555 | if (err) | ||
556 | goto out; | ||
557 | } | ||
558 | |||
559 | err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, | ||
560 | machine); | ||
561 | WARN_ONCE(err < 0, "Couldn't record kernel reference relocation symbol\n" | ||
562 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | ||
563 | "Check /proc/kallsyms permission or run as root.\n"); | ||
564 | |||
565 | err = perf_event__synthesize_modules(tool, process_synthesized_event, | ||
566 | machine); | ||
567 | WARN_ONCE(err < 0, "Couldn't record kernel module information.\n" | ||
568 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | ||
569 | "Check /proc/modules permission or run as root.\n"); | ||
570 | |||
571 | if (perf_guest) { | ||
572 | machines__process_guests(&session->machines, | ||
573 | perf_event__synthesize_guest_os, tool); | ||
574 | } | ||
575 | |||
576 | err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, | ||
577 | process_synthesized_event, opts->sample_address, | ||
578 | opts->proc_map_timeout); | ||
579 | out: | ||
580 | return err; | ||
581 | } | ||
582 | |||
485 | static int __cmd_record(struct record *rec, int argc, const char **argv) | 583 | static int __cmd_record(struct record *rec, int argc, const char **argv) |
486 | { | 584 | { |
487 | int err; | 585 | int err; |
@@ -534,6 +632,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
534 | goto out_child; | 632 | goto out_child; |
535 | } | 633 | } |
536 | 634 | ||
635 | err = bpf__apply_obj_config(); | ||
636 | if (err) { | ||
637 | char errbuf[BUFSIZ]; | ||
638 | |||
639 | bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); | ||
640 | pr_err("ERROR: Apply config to BPF failed: %s\n", | ||
641 | errbuf); | ||
642 | goto out_child; | ||
643 | } | ||
644 | |||
537 | /* | 645 | /* |
538 | * Normally perf_session__new would do this, but it doesn't have the | 646 | * Normally perf_session__new would do this, but it doesn't have the |
539 | * evlist. | 647 | * evlist. |
@@ -566,63 +674,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
566 | 674 | ||
567 | machine = &session->machines.host; | 675 | machine = &session->machines.host; |
568 | 676 | ||
569 | if (file->is_pipe) { | 677 | err = record__synthesize(rec); |
570 | err = perf_event__synthesize_attrs(tool, session, | ||
571 | process_synthesized_event); | ||
572 | if (err < 0) { | ||
573 | pr_err("Couldn't synthesize attrs.\n"); | ||
574 | goto out_child; | ||
575 | } | ||
576 | |||
577 | if (have_tracepoints(&rec->evlist->entries)) { | ||
578 | /* | ||
579 | * FIXME err <= 0 here actually means that | ||
580 | * there were no tracepoints so its not really | ||
581 | * an error, just that we don't need to | ||
582 | * synthesize anything. We really have to | ||
583 | * return this more properly and also | ||
584 | * propagate errors that now are calling die() | ||
585 | */ | ||
586 | err = perf_event__synthesize_tracing_data(tool, fd, rec->evlist, | ||
587 | process_synthesized_event); | ||
588 | if (err <= 0) { | ||
589 | pr_err("Couldn't record tracing data.\n"); | ||
590 | goto out_child; | ||
591 | } | ||
592 | rec->bytes_written += err; | ||
593 | } | ||
594 | } | ||
595 | |||
596 | if (rec->opts.full_auxtrace) { | ||
597 | err = perf_event__synthesize_auxtrace_info(rec->itr, tool, | ||
598 | session, process_synthesized_event); | ||
599 | if (err) | ||
600 | goto out_delete_session; | ||
601 | } | ||
602 | |||
603 | err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event, | ||
604 | machine); | ||
605 | if (err < 0) | ||
606 | pr_err("Couldn't record kernel reference relocation symbol\n" | ||
607 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | ||
608 | "Check /proc/kallsyms permission or run as root.\n"); | ||
609 | |||
610 | err = perf_event__synthesize_modules(tool, process_synthesized_event, | ||
611 | machine); | ||
612 | if (err < 0) | 678 | if (err < 0) |
613 | pr_err("Couldn't record kernel module information.\n" | ||
614 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | ||
615 | "Check /proc/modules permission or run as root.\n"); | ||
616 | |||
617 | if (perf_guest) { | ||
618 | machines__process_guests(&session->machines, | ||
619 | perf_event__synthesize_guest_os, tool); | ||
620 | } | ||
621 | |||
622 | err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, | ||
623 | process_synthesized_event, opts->sample_address, | ||
624 | opts->proc_map_timeout); | ||
625 | if (err != 0) | ||
626 | goto out_child; | 679 | goto out_child; |
627 | 680 | ||
628 | if (rec->realtime_prio) { | 681 | if (rec->realtime_prio) { |
@@ -758,18 +811,8 @@ out_child: | |||
758 | /* this will be recalculated during process_buildids() */ | 811 | /* this will be recalculated during process_buildids() */ |
759 | rec->samples = 0; | 812 | rec->samples = 0; |
760 | 813 | ||
761 | if (!err && !file->is_pipe) { | 814 | if (!err) |
762 | rec->session->header.data_size += rec->bytes_written; | 815 | record__finish_output(rec); |
763 | file->size = lseek(perf_data_file__fd(file), 0, SEEK_CUR); | ||
764 | |||
765 | if (!rec->no_buildid) { | ||
766 | process_buildids(rec); | ||
767 | |||
768 | if (rec->buildid_all) | ||
769 | dsos__hit_all(rec->session); | ||
770 | } | ||
771 | perf_session__write_header(rec->session, rec->evlist, fd, true); | ||
772 | } | ||
773 | 816 | ||
774 | if (!err && !quiet) { | 817 | if (!err && !quiet) { |
775 | char samples[128]; | 818 | char samples[128]; |
@@ -1097,10 +1140,12 @@ struct option __record_options[] = { | |||
1097 | OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"), | 1140 | OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"), |
1098 | OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples, | 1141 | OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples, |
1099 | "don't sample"), | 1142 | "don't sample"), |
1100 | OPT_BOOLEAN('N', "no-buildid-cache", &record.no_buildid_cache, | 1143 | OPT_BOOLEAN_SET('N', "no-buildid-cache", &record.no_buildid_cache, |
1101 | "do not update the buildid cache"), | 1144 | &record.no_buildid_cache_set, |
1102 | OPT_BOOLEAN('B', "no-buildid", &record.no_buildid, | 1145 | "do not update the buildid cache"), |
1103 | "do not collect buildids in perf.data"), | 1146 | OPT_BOOLEAN_SET('B', "no-buildid", &record.no_buildid, |
1147 | &record.no_buildid_set, | ||
1148 | "do not collect buildids in perf.data"), | ||
1104 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", | 1149 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", |
1105 | "monitor event in cgroup name only", | 1150 | "monitor event in cgroup name only", |
1106 | parse_cgroups), | 1151 | parse_cgroups), |
@@ -1136,6 +1181,12 @@ struct option __record_options[] = { | |||
1136 | "per thread proc mmap processing timeout in ms"), | 1181 | "per thread proc mmap processing timeout in ms"), |
1137 | OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events, | 1182 | OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events, |
1138 | "Record context switch events"), | 1183 | "Record context switch events"), |
1184 | OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel, | ||
1185 | "Configure all used events to run in kernel space.", | ||
1186 | PARSE_OPT_EXCLUSIVE), | ||
1187 | OPT_BOOLEAN_FLAG(0, "all-user", &record.opts.all_user, | ||
1188 | "Configure all used events to run in user space.", | ||
1189 | PARSE_OPT_EXCLUSIVE), | ||
1139 | OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path", | 1190 | OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path", |
1140 | "clang binary to use for compiling BPF scriptlets"), | 1191 | "clang binary to use for compiling BPF scriptlets"), |
1141 | OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options", | 1192 | OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options", |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2bf537f190a0..160ea23b45aa 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -41,6 +41,7 @@ | |||
41 | 41 | ||
42 | #include <dlfcn.h> | 42 | #include <dlfcn.h> |
43 | #include <linux/bitmap.h> | 43 | #include <linux/bitmap.h> |
44 | #include <linux/stringify.h> | ||
44 | 45 | ||
45 | struct report { | 46 | struct report { |
46 | struct perf_tool tool; | 47 | struct perf_tool tool; |
@@ -75,7 +76,10 @@ static int report__config(const char *var, const char *value, void *cb) | |||
75 | return 0; | 76 | return 0; |
76 | } | 77 | } |
77 | if (!strcmp(var, "report.percent-limit")) { | 78 | if (!strcmp(var, "report.percent-limit")) { |
78 | rep->min_percent = strtof(value, NULL); | 79 | double pcnt = strtof(value, NULL); |
80 | |||
81 | rep->min_percent = pcnt; | ||
82 | callchain_param.min_percent = pcnt; | ||
79 | return 0; | 83 | return 0; |
80 | } | 84 | } |
81 | if (!strcmp(var, "report.children")) { | 85 | if (!strcmp(var, "report.children")) { |
@@ -87,7 +91,7 @@ static int report__config(const char *var, const char *value, void *cb) | |||
87 | return 0; | 91 | return 0; |
88 | } | 92 | } |
89 | 93 | ||
90 | return perf_default_config(var, value, cb); | 94 | return 0; |
91 | } | 95 | } |
92 | 96 | ||
93 | static int hist_iter__report_callback(struct hist_entry_iter *iter, | 97 | static int hist_iter__report_callback(struct hist_entry_iter *iter, |
@@ -151,7 +155,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
151 | }; | 155 | }; |
152 | int ret = 0; | 156 | int ret = 0; |
153 | 157 | ||
154 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 158 | if (machine__resolve(machine, &al, sample) < 0) { |
155 | pr_debug("problem processing %d event, skipping it.\n", | 159 | pr_debug("problem processing %d event, skipping it.\n", |
156 | event->header.type); | 160 | event->header.type); |
157 | return -1; | 161 | return -1; |
@@ -466,10 +470,11 @@ static int report__browse_hists(struct report *rep) | |||
466 | return ret; | 470 | return ret; |
467 | } | 471 | } |
468 | 472 | ||
469 | static void report__collapse_hists(struct report *rep) | 473 | static int report__collapse_hists(struct report *rep) |
470 | { | 474 | { |
471 | struct ui_progress prog; | 475 | struct ui_progress prog; |
472 | struct perf_evsel *pos; | 476 | struct perf_evsel *pos; |
477 | int ret = 0; | ||
473 | 478 | ||
474 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); | 479 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); |
475 | 480 | ||
@@ -481,7 +486,9 @@ static void report__collapse_hists(struct report *rep) | |||
481 | 486 | ||
482 | hists->socket_filter = rep->socket_filter; | 487 | hists->socket_filter = rep->socket_filter; |
483 | 488 | ||
484 | hists__collapse_resort(hists, &prog); | 489 | ret = hists__collapse_resort(hists, &prog); |
490 | if (ret < 0) | ||
491 | break; | ||
485 | 492 | ||
486 | /* Non-group events are considered as leader */ | 493 | /* Non-group events are considered as leader */ |
487 | if (symbol_conf.event_group && | 494 | if (symbol_conf.event_group && |
@@ -494,6 +501,7 @@ static void report__collapse_hists(struct report *rep) | |||
494 | } | 501 | } |
495 | 502 | ||
496 | ui_progress__finish(); | 503 | ui_progress__finish(); |
504 | return ret; | ||
497 | } | 505 | } |
498 | 506 | ||
499 | static void report__output_resort(struct report *rep) | 507 | static void report__output_resort(struct report *rep) |
@@ -504,7 +512,7 @@ static void report__output_resort(struct report *rep) | |||
504 | ui_progress__init(&prog, rep->nr_entries, "Sorting events for output..."); | 512 | ui_progress__init(&prog, rep->nr_entries, "Sorting events for output..."); |
505 | 513 | ||
506 | evlist__for_each(rep->session->evlist, pos) | 514 | evlist__for_each(rep->session->evlist, pos) |
507 | hists__output_resort(evsel__hists(pos), &prog); | 515 | perf_evsel__output_resort(pos, &prog); |
508 | 516 | ||
509 | ui_progress__finish(); | 517 | ui_progress__finish(); |
510 | } | 518 | } |
@@ -561,7 +569,11 @@ static int __cmd_report(struct report *rep) | |||
561 | } | 569 | } |
562 | } | 570 | } |
563 | 571 | ||
564 | report__collapse_hists(rep); | 572 | ret = report__collapse_hists(rep); |
573 | if (ret) { | ||
574 | ui__error("failed to process hist entry\n"); | ||
575 | return ret; | ||
576 | } | ||
565 | 577 | ||
566 | if (session_done()) | 578 | if (session_done()) |
567 | return 0; | 579 | return 0; |
@@ -633,8 +645,10 @@ parse_percent_limit(const struct option *opt, const char *str, | |||
633 | int unset __maybe_unused) | 645 | int unset __maybe_unused) |
634 | { | 646 | { |
635 | struct report *rep = opt->value; | 647 | struct report *rep = opt->value; |
648 | double pcnt = strtof(str, NULL); | ||
636 | 649 | ||
637 | rep->min_percent = strtof(str, NULL); | 650 | rep->min_percent = pcnt; |
651 | callchain_param.min_percent = pcnt; | ||
638 | return 0; | 652 | return 0; |
639 | } | 653 | } |
640 | 654 | ||
@@ -798,6 +812,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
798 | "only show processor socket that match with this filter"), | 812 | "only show processor socket that match with this filter"), |
799 | OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, | 813 | OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, |
800 | "Show raw trace event output (do not use print fmt or plugins)"), | 814 | "Show raw trace event output (do not use print fmt or plugins)"), |
815 | OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy, | ||
816 | "Show entries in a hierarchy"), | ||
801 | OPT_END() | 817 | OPT_END() |
802 | }; | 818 | }; |
803 | struct perf_data_file file = { | 819 | struct perf_data_file file = { |
@@ -907,13 +923,19 @@ repeat: | |||
907 | symbol_conf.cumulate_callchain = false; | 923 | symbol_conf.cumulate_callchain = false; |
908 | } | 924 | } |
909 | 925 | ||
910 | if (setup_sorting(session->evlist) < 0) { | 926 | if (symbol_conf.report_hierarchy) { |
911 | if (sort_order) | 927 | /* disable incompatible options */ |
912 | parse_options_usage(report_usage, options, "s", 1); | 928 | symbol_conf.event_group = false; |
913 | if (field_order) | 929 | symbol_conf.cumulate_callchain = false; |
914 | parse_options_usage(sort_order ? NULL : report_usage, | 930 | |
915 | options, "F", 1); | 931 | if (field_order) { |
916 | goto error; | 932 | pr_err("Error: --hierarchy and --fields options cannot be used together\n"); |
933 | parse_options_usage(report_usage, options, "F", 1); | ||
934 | parse_options_usage(NULL, options, "hierarchy", 0); | ||
935 | goto error; | ||
936 | } | ||
937 | |||
938 | sort__need_collapse = true; | ||
917 | } | 939 | } |
918 | 940 | ||
919 | /* Force tty output for header output and per-thread stat. */ | 941 | /* Force tty output for header output and per-thread stat. */ |
@@ -925,6 +947,15 @@ repeat: | |||
925 | else | 947 | else |
926 | use_browser = 0; | 948 | use_browser = 0; |
927 | 949 | ||
950 | if (setup_sorting(session->evlist) < 0) { | ||
951 | if (sort_order) | ||
952 | parse_options_usage(report_usage, options, "s", 1); | ||
953 | if (field_order) | ||
954 | parse_options_usage(sort_order ? NULL : report_usage, | ||
955 | options, "F", 1); | ||
956 | goto error; | ||
957 | } | ||
958 | |||
928 | if (report.header || report.header_only) { | 959 | if (report.header || report.header_only) { |
929 | perf_session__fprintf_info(session, stdout, | 960 | perf_session__fprintf_info(session, stdout, |
930 | report.show_full_info); | 961 | report.show_full_info); |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c691214d820f..3770c3dffe5e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include "util/stat.h" | 23 | #include "util/stat.h" |
24 | #include <linux/bitmap.h> | 24 | #include <linux/bitmap.h> |
25 | #include "asm/bug.h" | 25 | #include "asm/bug.h" |
26 | #include "util/mem-events.h" | ||
26 | 27 | ||
27 | static char const *script_name; | 28 | static char const *script_name; |
28 | static char const *generate_script_lang; | 29 | static char const *generate_script_lang; |
@@ -58,6 +59,9 @@ enum perf_output_field { | |||
58 | PERF_OUTPUT_IREGS = 1U << 14, | 59 | PERF_OUTPUT_IREGS = 1U << 14, |
59 | PERF_OUTPUT_BRSTACK = 1U << 15, | 60 | PERF_OUTPUT_BRSTACK = 1U << 15, |
60 | PERF_OUTPUT_BRSTACKSYM = 1U << 16, | 61 | PERF_OUTPUT_BRSTACKSYM = 1U << 16, |
62 | PERF_OUTPUT_DATA_SRC = 1U << 17, | ||
63 | PERF_OUTPUT_WEIGHT = 1U << 18, | ||
64 | PERF_OUTPUT_BPF_OUTPUT = 1U << 19, | ||
61 | }; | 65 | }; |
62 | 66 | ||
63 | struct output_option { | 67 | struct output_option { |
@@ -81,6 +85,9 @@ struct output_option { | |||
81 | {.str = "iregs", .field = PERF_OUTPUT_IREGS}, | 85 | {.str = "iregs", .field = PERF_OUTPUT_IREGS}, |
82 | {.str = "brstack", .field = PERF_OUTPUT_BRSTACK}, | 86 | {.str = "brstack", .field = PERF_OUTPUT_BRSTACK}, |
83 | {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM}, | 87 | {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM}, |
88 | {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC}, | ||
89 | {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, | ||
90 | {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT}, | ||
84 | }; | 91 | }; |
85 | 92 | ||
86 | /* default set to maintain compatibility with current format */ | 93 | /* default set to maintain compatibility with current format */ |
@@ -101,7 +108,7 @@ static struct { | |||
101 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 108 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
102 | PERF_OUTPUT_PERIOD, | 109 | PERF_OUTPUT_PERIOD, |
103 | 110 | ||
104 | .invalid_fields = PERF_OUTPUT_TRACE, | 111 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
105 | }, | 112 | }, |
106 | 113 | ||
107 | [PERF_TYPE_SOFTWARE] = { | 114 | [PERF_TYPE_SOFTWARE] = { |
@@ -111,7 +118,7 @@ static struct { | |||
111 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 118 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
112 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 119 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
113 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 120 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
114 | PERF_OUTPUT_PERIOD, | 121 | PERF_OUTPUT_PERIOD | PERF_OUTPUT_BPF_OUTPUT, |
115 | 122 | ||
116 | .invalid_fields = PERF_OUTPUT_TRACE, | 123 | .invalid_fields = PERF_OUTPUT_TRACE, |
117 | }, | 124 | }, |
@@ -121,7 +128,7 @@ static struct { | |||
121 | 128 | ||
122 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 129 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
123 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 130 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
124 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | 131 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE |
125 | }, | 132 | }, |
126 | 133 | ||
127 | [PERF_TYPE_RAW] = { | 134 | [PERF_TYPE_RAW] = { |
@@ -131,9 +138,10 @@ static struct { | |||
131 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 138 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
132 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 139 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
133 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 140 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
134 | PERF_OUTPUT_PERIOD, | 141 | PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR | |
142 | PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT, | ||
135 | 143 | ||
136 | .invalid_fields = PERF_OUTPUT_TRACE, | 144 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
137 | }, | 145 | }, |
138 | 146 | ||
139 | [PERF_TYPE_BREAKPOINT] = { | 147 | [PERF_TYPE_BREAKPOINT] = { |
@@ -145,7 +153,7 @@ static struct { | |||
145 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 153 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
146 | PERF_OUTPUT_PERIOD, | 154 | PERF_OUTPUT_PERIOD, |
147 | 155 | ||
148 | .invalid_fields = PERF_OUTPUT_TRACE, | 156 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
149 | }, | 157 | }, |
150 | }; | 158 | }; |
151 | 159 | ||
@@ -242,6 +250,16 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
242 | PERF_OUTPUT_ADDR, allow_user_set)) | 250 | PERF_OUTPUT_ADDR, allow_user_set)) |
243 | return -EINVAL; | 251 | return -EINVAL; |
244 | 252 | ||
253 | if (PRINT_FIELD(DATA_SRC) && | ||
254 | perf_evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC", | ||
255 | PERF_OUTPUT_DATA_SRC)) | ||
256 | return -EINVAL; | ||
257 | |||
258 | if (PRINT_FIELD(WEIGHT) && | ||
259 | perf_evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT", | ||
260 | PERF_OUTPUT_WEIGHT)) | ||
261 | return -EINVAL; | ||
262 | |||
245 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | 263 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { |
246 | pr_err("Display of symbols requested but neither sample IP nor " | 264 | pr_err("Display of symbols requested but neither sample IP nor " |
247 | "sample address\nis selected. Hence, no addresses to convert " | 265 | "sample address\nis selected. Hence, no addresses to convert " |
@@ -387,9 +405,7 @@ out: | |||
387 | return 0; | 405 | return 0; |
388 | } | 406 | } |
389 | 407 | ||
390 | static void print_sample_iregs(union perf_event *event __maybe_unused, | 408 | static void print_sample_iregs(struct perf_sample *sample, |
391 | struct perf_sample *sample, | ||
392 | struct thread *thread __maybe_unused, | ||
393 | struct perf_event_attr *attr) | 409 | struct perf_event_attr *attr) |
394 | { | 410 | { |
395 | struct regs_dump *regs = &sample->intr_regs; | 411 | struct regs_dump *regs = &sample->intr_regs; |
@@ -458,10 +474,7 @@ mispred_str(struct branch_entry *br) | |||
458 | return br->flags.predicted ? 'P' : 'M'; | 474 | return br->flags.predicted ? 'P' : 'M'; |
459 | } | 475 | } |
460 | 476 | ||
461 | static void print_sample_brstack(union perf_event *event __maybe_unused, | 477 | static void print_sample_brstack(struct perf_sample *sample) |
462 | struct perf_sample *sample, | ||
463 | struct thread *thread __maybe_unused, | ||
464 | struct perf_event_attr *attr __maybe_unused) | ||
465 | { | 478 | { |
466 | struct branch_stack *br = sample->branch_stack; | 479 | struct branch_stack *br = sample->branch_stack; |
467 | u64 i; | 480 | u64 i; |
@@ -480,14 +493,11 @@ static void print_sample_brstack(union perf_event *event __maybe_unused, | |||
480 | } | 493 | } |
481 | } | 494 | } |
482 | 495 | ||
483 | static void print_sample_brstacksym(union perf_event *event __maybe_unused, | 496 | static void print_sample_brstacksym(struct perf_sample *sample, |
484 | struct perf_sample *sample, | 497 | struct thread *thread) |
485 | struct thread *thread __maybe_unused, | ||
486 | struct perf_event_attr *attr __maybe_unused) | ||
487 | { | 498 | { |
488 | struct branch_stack *br = sample->branch_stack; | 499 | struct branch_stack *br = sample->branch_stack; |
489 | struct addr_location alf, alt; | 500 | struct addr_location alf, alt; |
490 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
491 | u64 i, from, to; | 501 | u64 i, from, to; |
492 | 502 | ||
493 | if (!(br && br->nr)) | 503 | if (!(br && br->nr)) |
@@ -500,11 +510,11 @@ static void print_sample_brstacksym(union perf_event *event __maybe_unused, | |||
500 | from = br->entries[i].from; | 510 | from = br->entries[i].from; |
501 | to = br->entries[i].to; | 511 | to = br->entries[i].to; |
502 | 512 | ||
503 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, from, &alf); | 513 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); |
504 | if (alf.map) | 514 | if (alf.map) |
505 | alf.sym = map__find_symbol(alf.map, alf.addr, NULL); | 515 | alf.sym = map__find_symbol(alf.map, alf.addr, NULL); |
506 | 516 | ||
507 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, to, &alt); | 517 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); |
508 | if (alt.map) | 518 | if (alt.map) |
509 | alt.sym = map__find_symbol(alt.map, alt.addr, NULL); | 519 | alt.sym = map__find_symbol(alt.map, alt.addr, NULL); |
510 | 520 | ||
@@ -520,8 +530,7 @@ static void print_sample_brstacksym(union perf_event *event __maybe_unused, | |||
520 | } | 530 | } |
521 | 531 | ||
522 | 532 | ||
523 | static void print_sample_addr(union perf_event *event, | 533 | static void print_sample_addr(struct perf_sample *sample, |
524 | struct perf_sample *sample, | ||
525 | struct thread *thread, | 534 | struct thread *thread, |
526 | struct perf_event_attr *attr) | 535 | struct perf_event_attr *attr) |
527 | { | 536 | { |
@@ -532,7 +541,7 @@ static void print_sample_addr(union perf_event *event, | |||
532 | if (!sample_addr_correlates_sym(attr)) | 541 | if (!sample_addr_correlates_sym(attr)) |
533 | return; | 542 | return; |
534 | 543 | ||
535 | perf_event__preprocess_sample_addr(event, sample, thread, &al); | 544 | thread__resolve(thread, &al, sample); |
536 | 545 | ||
537 | if (PRINT_FIELD(SYM)) { | 546 | if (PRINT_FIELD(SYM)) { |
538 | printf(" "); | 547 | printf(" "); |
@@ -549,8 +558,7 @@ static void print_sample_addr(union perf_event *event, | |||
549 | } | 558 | } |
550 | } | 559 | } |
551 | 560 | ||
552 | static void print_sample_bts(union perf_event *event, | 561 | static void print_sample_bts(struct perf_sample *sample, |
553 | struct perf_sample *sample, | ||
554 | struct perf_evsel *evsel, | 562 | struct perf_evsel *evsel, |
555 | struct thread *thread, | 563 | struct thread *thread, |
556 | struct addr_location *al) | 564 | struct addr_location *al) |
@@ -580,7 +588,7 @@ static void print_sample_bts(union perf_event *event, | |||
580 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | 588 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && |
581 | !output[attr->type].user_set)) { | 589 | !output[attr->type].user_set)) { |
582 | printf(" => "); | 590 | printf(" => "); |
583 | print_sample_addr(event, sample, thread, attr); | 591 | print_sample_addr(sample, thread, attr); |
584 | } | 592 | } |
585 | 593 | ||
586 | if (print_srcline_last) | 594 | if (print_srcline_last) |
@@ -608,6 +616,84 @@ static void print_sample_flags(u32 flags) | |||
608 | printf(" %-4s ", str); | 616 | printf(" %-4s ", str); |
609 | } | 617 | } |
610 | 618 | ||
619 | struct printer_data { | ||
620 | int line_no; | ||
621 | bool hit_nul; | ||
622 | bool is_printable; | ||
623 | }; | ||
624 | |||
625 | static void | ||
626 | print_sample_bpf_output_printer(enum binary_printer_ops op, | ||
627 | unsigned int val, | ||
628 | void *extra) | ||
629 | { | ||
630 | unsigned char ch = (unsigned char)val; | ||
631 | struct printer_data *printer_data = extra; | ||
632 | |||
633 | switch (op) { | ||
634 | case BINARY_PRINT_DATA_BEGIN: | ||
635 | printf("\n"); | ||
636 | break; | ||
637 | case BINARY_PRINT_LINE_BEGIN: | ||
638 | printf("%17s", !printer_data->line_no ? "BPF output:" : | ||
639 | " "); | ||
640 | break; | ||
641 | case BINARY_PRINT_ADDR: | ||
642 | printf(" %04x:", val); | ||
643 | break; | ||
644 | case BINARY_PRINT_NUM_DATA: | ||
645 | printf(" %02x", val); | ||
646 | break; | ||
647 | case BINARY_PRINT_NUM_PAD: | ||
648 | printf(" "); | ||
649 | break; | ||
650 | case BINARY_PRINT_SEP: | ||
651 | printf(" "); | ||
652 | break; | ||
653 | case BINARY_PRINT_CHAR_DATA: | ||
654 | if (printer_data->hit_nul && ch) | ||
655 | printer_data->is_printable = false; | ||
656 | |||
657 | if (!isprint(ch)) { | ||
658 | printf("%c", '.'); | ||
659 | |||
660 | if (!printer_data->is_printable) | ||
661 | break; | ||
662 | |||
663 | if (ch == '\0') | ||
664 | printer_data->hit_nul = true; | ||
665 | else | ||
666 | printer_data->is_printable = false; | ||
667 | } else { | ||
668 | printf("%c", ch); | ||
669 | } | ||
670 | break; | ||
671 | case BINARY_PRINT_CHAR_PAD: | ||
672 | printf(" "); | ||
673 | break; | ||
674 | case BINARY_PRINT_LINE_END: | ||
675 | printf("\n"); | ||
676 | printer_data->line_no++; | ||
677 | break; | ||
678 | case BINARY_PRINT_DATA_END: | ||
679 | default: | ||
680 | break; | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static void print_sample_bpf_output(struct perf_sample *sample) | ||
685 | { | ||
686 | unsigned int nr_bytes = sample->raw_size; | ||
687 | struct printer_data printer_data = {0, false, true}; | ||
688 | |||
689 | print_binary(sample->raw_data, nr_bytes, 8, | ||
690 | print_sample_bpf_output_printer, &printer_data); | ||
691 | |||
692 | if (printer_data.is_printable && printer_data.hit_nul) | ||
693 | printf("%17s \"%s\"\n", "BPF string:", | ||
694 | (char *)(sample->raw_data)); | ||
695 | } | ||
696 | |||
611 | struct perf_script { | 697 | struct perf_script { |
612 | struct perf_tool tool; | 698 | struct perf_tool tool; |
613 | struct perf_session *session; | 699 | struct perf_session *session; |
@@ -634,7 +720,24 @@ static int perf_evlist__max_name_len(struct perf_evlist *evlist) | |||
634 | return max; | 720 | return max; |
635 | } | 721 | } |
636 | 722 | ||
637 | static void process_event(struct perf_script *script, union perf_event *event, | 723 | static size_t data_src__printf(u64 data_src) |
724 | { | ||
725 | struct mem_info mi = { .data_src.val = data_src }; | ||
726 | char decode[100]; | ||
727 | char out[100]; | ||
728 | static int maxlen; | ||
729 | int len; | ||
730 | |||
731 | perf_script__meminfo_scnprintf(decode, 100, &mi); | ||
732 | |||
733 | len = scnprintf(out, 100, "%16" PRIx64 " %s", data_src, decode); | ||
734 | if (maxlen < len) | ||
735 | maxlen = len; | ||
736 | |||
737 | return printf("%-*s", maxlen, out); | ||
738 | } | ||
739 | |||
740 | static void process_event(struct perf_script *script, | ||
638 | struct perf_sample *sample, struct perf_evsel *evsel, | 741 | struct perf_sample *sample, struct perf_evsel *evsel, |
639 | struct addr_location *al) | 742 | struct addr_location *al) |
640 | { | 743 | { |
@@ -663,7 +766,7 @@ static void process_event(struct perf_script *script, union perf_event *event, | |||
663 | print_sample_flags(sample->flags); | 766 | print_sample_flags(sample->flags); |
664 | 767 | ||
665 | if (is_bts_event(attr)) { | 768 | if (is_bts_event(attr)) { |
666 | print_sample_bts(event, sample, evsel, thread, al); | 769 | print_sample_bts(sample, evsel, thread, al); |
667 | return; | 770 | return; |
668 | } | 771 | } |
669 | 772 | ||
@@ -671,7 +774,13 @@ static void process_event(struct perf_script *script, union perf_event *event, | |||
671 | event_format__print(evsel->tp_format, sample->cpu, | 774 | event_format__print(evsel->tp_format, sample->cpu, |
672 | sample->raw_data, sample->raw_size); | 775 | sample->raw_data, sample->raw_size); |
673 | if (PRINT_FIELD(ADDR)) | 776 | if (PRINT_FIELD(ADDR)) |
674 | print_sample_addr(event, sample, thread, attr); | 777 | print_sample_addr(sample, thread, attr); |
778 | |||
779 | if (PRINT_FIELD(DATA_SRC)) | ||
780 | data_src__printf(sample->data_src); | ||
781 | |||
782 | if (PRINT_FIELD(WEIGHT)) | ||
783 | printf("%16" PRIu64, sample->weight); | ||
675 | 784 | ||
676 | if (PRINT_FIELD(IP)) { | 785 | if (PRINT_FIELD(IP)) { |
677 | if (!symbol_conf.use_callchain) | 786 | if (!symbol_conf.use_callchain) |
@@ -685,12 +794,15 @@ static void process_event(struct perf_script *script, union perf_event *event, | |||
685 | } | 794 | } |
686 | 795 | ||
687 | if (PRINT_FIELD(IREGS)) | 796 | if (PRINT_FIELD(IREGS)) |
688 | print_sample_iregs(event, sample, thread, attr); | 797 | print_sample_iregs(sample, attr); |
689 | 798 | ||
690 | if (PRINT_FIELD(BRSTACK)) | 799 | if (PRINT_FIELD(BRSTACK)) |
691 | print_sample_brstack(event, sample, thread, attr); | 800 | print_sample_brstack(sample); |
692 | else if (PRINT_FIELD(BRSTACKSYM)) | 801 | else if (PRINT_FIELD(BRSTACKSYM)) |
693 | print_sample_brstacksym(event, sample, thread, attr); | 802 | print_sample_brstacksym(sample, thread); |
803 | |||
804 | if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) | ||
805 | print_sample_bpf_output(sample); | ||
694 | 806 | ||
695 | printf("\n"); | 807 | printf("\n"); |
696 | } | 808 | } |
@@ -783,7 +895,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
783 | return 0; | 895 | return 0; |
784 | } | 896 | } |
785 | 897 | ||
786 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 898 | if (machine__resolve(machine, &al, sample) < 0) { |
787 | pr_err("problem processing %d event, skipping it.\n", | 899 | pr_err("problem processing %d event, skipping it.\n", |
788 | event->header.type); | 900 | event->header.type); |
789 | return -1; | 901 | return -1; |
@@ -798,7 +910,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
798 | if (scripting_ops) | 910 | if (scripting_ops) |
799 | scripting_ops->process_event(event, sample, evsel, &al); | 911 | scripting_ops->process_event(event, sample, evsel, &al); |
800 | else | 912 | else |
801 | process_event(scr, event, sample, evsel, &al); | 913 | process_event(scr, sample, evsel, &al); |
802 | 914 | ||
803 | out_put: | 915 | out_put: |
804 | addr_location__put(&al); | 916 | addr_location__put(&al); |
@@ -1090,23 +1202,6 @@ static struct script_spec *script_spec__find(const char *spec) | |||
1090 | return NULL; | 1202 | return NULL; |
1091 | } | 1203 | } |
1092 | 1204 | ||
1093 | static struct script_spec *script_spec__findnew(const char *spec, | ||
1094 | struct scripting_ops *ops) | ||
1095 | { | ||
1096 | struct script_spec *s = script_spec__find(spec); | ||
1097 | |||
1098 | if (s) | ||
1099 | return s; | ||
1100 | |||
1101 | s = script_spec__new(spec, ops); | ||
1102 | if (!s) | ||
1103 | return NULL; | ||
1104 | |||
1105 | script_spec__add(s); | ||
1106 | |||
1107 | return s; | ||
1108 | } | ||
1109 | |||
1110 | int script_spec_register(const char *spec, struct scripting_ops *ops) | 1205 | int script_spec_register(const char *spec, struct scripting_ops *ops) |
1111 | { | 1206 | { |
1112 | struct script_spec *s; | 1207 | struct script_spec *s; |
@@ -1115,9 +1210,11 @@ int script_spec_register(const char *spec, struct scripting_ops *ops) | |||
1115 | if (s) | 1210 | if (s) |
1116 | return -1; | 1211 | return -1; |
1117 | 1212 | ||
1118 | s = script_spec__findnew(spec, ops); | 1213 | s = script_spec__new(spec, ops); |
1119 | if (!s) | 1214 | if (!s) |
1120 | return -1; | 1215 | return -1; |
1216 | else | ||
1217 | script_spec__add(s); | ||
1121 | 1218 | ||
1122 | return 0; | 1219 | return 0; |
1123 | } | 1220 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 038e877081b6..1f19f2f999c8 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -122,6 +122,7 @@ static bool sync_run = false; | |||
122 | static unsigned int initial_delay = 0; | 122 | static unsigned int initial_delay = 0; |
123 | static unsigned int unit_width = 4; /* strlen("unit") */ | 123 | static unsigned int unit_width = 4; /* strlen("unit") */ |
124 | static bool forever = false; | 124 | static bool forever = false; |
125 | static bool metric_only = false; | ||
125 | static struct timespec ref_time; | 126 | static struct timespec ref_time; |
126 | static struct cpu_map *aggr_map; | 127 | static struct cpu_map *aggr_map; |
127 | static aggr_get_id_t aggr_get_id; | 128 | static aggr_get_id_t aggr_get_id; |
@@ -735,6 +736,191 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
735 | } | 736 | } |
736 | } | 737 | } |
737 | 738 | ||
739 | struct outstate { | ||
740 | FILE *fh; | ||
741 | bool newline; | ||
742 | const char *prefix; | ||
743 | int nfields; | ||
744 | int id, nr; | ||
745 | struct perf_evsel *evsel; | ||
746 | }; | ||
747 | |||
748 | #define METRIC_LEN 35 | ||
749 | |||
750 | static void new_line_std(void *ctx) | ||
751 | { | ||
752 | struct outstate *os = ctx; | ||
753 | |||
754 | os->newline = true; | ||
755 | } | ||
756 | |||
757 | static void do_new_line_std(struct outstate *os) | ||
758 | { | ||
759 | fputc('\n', os->fh); | ||
760 | fputs(os->prefix, os->fh); | ||
761 | aggr_printout(os->evsel, os->id, os->nr); | ||
762 | if (stat_config.aggr_mode == AGGR_NONE) | ||
763 | fprintf(os->fh, " "); | ||
764 | fprintf(os->fh, " "); | ||
765 | } | ||
766 | |||
767 | static void print_metric_std(void *ctx, const char *color, const char *fmt, | ||
768 | const char *unit, double val) | ||
769 | { | ||
770 | struct outstate *os = ctx; | ||
771 | FILE *out = os->fh; | ||
772 | int n; | ||
773 | bool newline = os->newline; | ||
774 | |||
775 | os->newline = false; | ||
776 | |||
777 | if (unit == NULL || fmt == NULL) { | ||
778 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
779 | return; | ||
780 | } | ||
781 | |||
782 | if (newline) | ||
783 | do_new_line_std(os); | ||
784 | |||
785 | n = fprintf(out, " # "); | ||
786 | if (color) | ||
787 | n += color_fprintf(out, color, fmt, val); | ||
788 | else | ||
789 | n += fprintf(out, fmt, val); | ||
790 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
791 | } | ||
792 | |||
793 | static void new_line_csv(void *ctx) | ||
794 | { | ||
795 | struct outstate *os = ctx; | ||
796 | int i; | ||
797 | |||
798 | fputc('\n', os->fh); | ||
799 | if (os->prefix) | ||
800 | fprintf(os->fh, "%s%s", os->prefix, csv_sep); | ||
801 | aggr_printout(os->evsel, os->id, os->nr); | ||
802 | for (i = 0; i < os->nfields; i++) | ||
803 | fputs(csv_sep, os->fh); | ||
804 | } | ||
805 | |||
806 | static void print_metric_csv(void *ctx, | ||
807 | const char *color __maybe_unused, | ||
808 | const char *fmt, const char *unit, double val) | ||
809 | { | ||
810 | struct outstate *os = ctx; | ||
811 | FILE *out = os->fh; | ||
812 | char buf[64], *vals, *ends; | ||
813 | |||
814 | if (unit == NULL || fmt == NULL) { | ||
815 | fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep); | ||
816 | return; | ||
817 | } | ||
818 | snprintf(buf, sizeof(buf), fmt, val); | ||
819 | vals = buf; | ||
820 | while (isspace(*vals)) | ||
821 | vals++; | ||
822 | ends = vals; | ||
823 | while (isdigit(*ends) || *ends == '.') | ||
824 | ends++; | ||
825 | *ends = 0; | ||
826 | while (isspace(*unit)) | ||
827 | unit++; | ||
828 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); | ||
829 | } | ||
830 | |||
831 | #define METRIC_ONLY_LEN 20 | ||
832 | |||
833 | /* Filter out some columns that don't work well in metrics only mode */ | ||
834 | |||
835 | static bool valid_only_metric(const char *unit) | ||
836 | { | ||
837 | if (!unit) | ||
838 | return false; | ||
839 | if (strstr(unit, "/sec") || | ||
840 | strstr(unit, "hz") || | ||
841 | strstr(unit, "Hz") || | ||
842 | strstr(unit, "CPUs utilized")) | ||
843 | return false; | ||
844 | return true; | ||
845 | } | ||
846 | |||
847 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
848 | const char *unit) | ||
849 | { | ||
850 | if (!strncmp(unit, "of all", 6)) { | ||
851 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
852 | unit); | ||
853 | return buf; | ||
854 | } | ||
855 | return unit; | ||
856 | } | ||
857 | |||
858 | static void print_metric_only(void *ctx, const char *color, const char *fmt, | ||
859 | const char *unit, double val) | ||
860 | { | ||
861 | struct outstate *os = ctx; | ||
862 | FILE *out = os->fh; | ||
863 | int n; | ||
864 | char buf[1024]; | ||
865 | unsigned mlen = METRIC_ONLY_LEN; | ||
866 | |||
867 | if (!valid_only_metric(unit)) | ||
868 | return; | ||
869 | unit = fixunit(buf, os->evsel, unit); | ||
870 | if (color) | ||
871 | n = color_fprintf(out, color, fmt, val); | ||
872 | else | ||
873 | n = fprintf(out, fmt, val); | ||
874 | if (n > METRIC_ONLY_LEN) | ||
875 | n = METRIC_ONLY_LEN; | ||
876 | if (mlen < strlen(unit)) | ||
877 | mlen = strlen(unit) + 1; | ||
878 | fprintf(out, "%*s", mlen - n, ""); | ||
879 | } | ||
880 | |||
881 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, | ||
882 | const char *fmt, | ||
883 | const char *unit, double val) | ||
884 | { | ||
885 | struct outstate *os = ctx; | ||
886 | FILE *out = os->fh; | ||
887 | char buf[64], *vals, *ends; | ||
888 | char tbuf[1024]; | ||
889 | |||
890 | if (!valid_only_metric(unit)) | ||
891 | return; | ||
892 | unit = fixunit(tbuf, os->evsel, unit); | ||
893 | snprintf(buf, sizeof buf, fmt, val); | ||
894 | vals = buf; | ||
895 | while (isspace(*vals)) | ||
896 | vals++; | ||
897 | ends = vals; | ||
898 | while (isdigit(*ends) || *ends == '.') | ||
899 | ends++; | ||
900 | *ends = 0; | ||
901 | fprintf(out, "%s%s", vals, csv_sep); | ||
902 | } | ||
903 | |||
904 | static void new_line_metric(void *ctx __maybe_unused) | ||
905 | { | ||
906 | } | ||
907 | |||
908 | static void print_metric_header(void *ctx, const char *color __maybe_unused, | ||
909 | const char *fmt __maybe_unused, | ||
910 | const char *unit, double val __maybe_unused) | ||
911 | { | ||
912 | struct outstate *os = ctx; | ||
913 | char tbuf[1024]; | ||
914 | |||
915 | if (!valid_only_metric(unit)) | ||
916 | return; | ||
917 | unit = fixunit(tbuf, os->evsel, unit); | ||
918 | if (csv_output) | ||
919 | fprintf(os->fh, "%s%s", unit, csv_sep); | ||
920 | else | ||
921 | fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit); | ||
922 | } | ||
923 | |||
738 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 924 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
739 | { | 925 | { |
740 | FILE *output = stat_config.output; | 926 | FILE *output = stat_config.output; |
@@ -763,6 +949,28 @@ static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
763 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 949 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
764 | } | 950 | } |
765 | 951 | ||
952 | static int first_shadow_cpu(struct perf_evsel *evsel, int id) | ||
953 | { | ||
954 | int i; | ||
955 | |||
956 | if (!aggr_get_id) | ||
957 | return 0; | ||
958 | |||
959 | if (stat_config.aggr_mode == AGGR_NONE) | ||
960 | return id; | ||
961 | |||
962 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
963 | return 0; | ||
964 | |||
965 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
966 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
967 | |||
968 | if (aggr_get_id(evsel_list->cpus, cpu2) == id) | ||
969 | return cpu2; | ||
970 | } | ||
971 | return 0; | ||
972 | } | ||
973 | |||
766 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 974 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
767 | { | 975 | { |
768 | FILE *output = stat_config.output; | 976 | FILE *output = stat_config.output; |
@@ -793,22 +1001,124 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
793 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 1001 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
794 | } | 1002 | } |
795 | 1003 | ||
796 | static void printout(int id, int nr, struct perf_evsel *counter, double uval) | 1004 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, |
1005 | char *prefix, u64 run, u64 ena, double noise) | ||
797 | { | 1006 | { |
798 | int cpu = cpu_map__id_to_cpu(id); | 1007 | struct perf_stat_output_ctx out; |
1008 | struct outstate os = { | ||
1009 | .fh = stat_config.output, | ||
1010 | .prefix = prefix ? prefix : "", | ||
1011 | .id = id, | ||
1012 | .nr = nr, | ||
1013 | .evsel = counter, | ||
1014 | }; | ||
1015 | print_metric_t pm = print_metric_std; | ||
1016 | void (*nl)(void *); | ||
799 | 1017 | ||
800 | if (stat_config.aggr_mode == AGGR_GLOBAL) | 1018 | if (metric_only) { |
801 | cpu = 0; | 1019 | nl = new_line_metric; |
1020 | if (csv_output) | ||
1021 | pm = print_metric_only_csv; | ||
1022 | else | ||
1023 | pm = print_metric_only; | ||
1024 | } else | ||
1025 | nl = new_line_std; | ||
1026 | |||
1027 | if (csv_output && !metric_only) { | ||
1028 | static int aggr_fields[] = { | ||
1029 | [AGGR_GLOBAL] = 0, | ||
1030 | [AGGR_THREAD] = 1, | ||
1031 | [AGGR_NONE] = 1, | ||
1032 | [AGGR_SOCKET] = 2, | ||
1033 | [AGGR_CORE] = 2, | ||
1034 | }; | ||
1035 | |||
1036 | pm = print_metric_csv; | ||
1037 | nl = new_line_csv; | ||
1038 | os.nfields = 3; | ||
1039 | os.nfields += aggr_fields[stat_config.aggr_mode]; | ||
1040 | if (counter->cgrp) | ||
1041 | os.nfields++; | ||
1042 | } | ||
1043 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
1044 | if (metric_only) { | ||
1045 | pm(&os, NULL, "", "", 0); | ||
1046 | return; | ||
1047 | } | ||
1048 | aggr_printout(counter, id, nr); | ||
1049 | |||
1050 | fprintf(stat_config.output, "%*s%s", | ||
1051 | csv_output ? 0 : 18, | ||
1052 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
1053 | csv_sep); | ||
1054 | |||
1055 | fprintf(stat_config.output, "%-*s%s", | ||
1056 | csv_output ? 0 : unit_width, | ||
1057 | counter->unit, csv_sep); | ||
1058 | |||
1059 | fprintf(stat_config.output, "%*s", | ||
1060 | csv_output ? 0 : -25, | ||
1061 | perf_evsel__name(counter)); | ||
1062 | |||
1063 | if (counter->cgrp) | ||
1064 | fprintf(stat_config.output, "%s%s", | ||
1065 | csv_sep, counter->cgrp->name); | ||
802 | 1066 | ||
803 | if (nsec_counter(counter)) | 1067 | if (!csv_output) |
1068 | pm(&os, NULL, NULL, "", 0); | ||
1069 | print_noise(counter, noise); | ||
1070 | print_running(run, ena); | ||
1071 | if (csv_output) | ||
1072 | pm(&os, NULL, NULL, "", 0); | ||
1073 | return; | ||
1074 | } | ||
1075 | |||
1076 | if (metric_only) | ||
1077 | /* nothing */; | ||
1078 | else if (nsec_counter(counter)) | ||
804 | nsec_printout(id, nr, counter, uval); | 1079 | nsec_printout(id, nr, counter, uval); |
805 | else | 1080 | else |
806 | abs_printout(id, nr, counter, uval); | 1081 | abs_printout(id, nr, counter, uval); |
807 | 1082 | ||
808 | if (!csv_output && !stat_config.interval) | 1083 | out.print_metric = pm; |
809 | perf_stat__print_shadow_stats(stat_config.output, counter, | 1084 | out.new_line = nl; |
810 | uval, cpu, | 1085 | out.ctx = &os; |
811 | stat_config.aggr_mode); | 1086 | |
1087 | if (csv_output && !metric_only) { | ||
1088 | print_noise(counter, noise); | ||
1089 | print_running(run, ena); | ||
1090 | } | ||
1091 | |||
1092 | perf_stat__print_shadow_stats(counter, uval, | ||
1093 | first_shadow_cpu(counter, id), | ||
1094 | &out); | ||
1095 | if (!csv_output && !metric_only) { | ||
1096 | print_noise(counter, noise); | ||
1097 | print_running(run, ena); | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | static void aggr_update_shadow(void) | ||
1102 | { | ||
1103 | int cpu, s2, id, s; | ||
1104 | u64 val; | ||
1105 | struct perf_evsel *counter; | ||
1106 | |||
1107 | for (s = 0; s < aggr_map->nr; s++) { | ||
1108 | id = aggr_map->map[s]; | ||
1109 | evlist__for_each(evsel_list, counter) { | ||
1110 | val = 0; | ||
1111 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
1112 | s2 = aggr_get_id(evsel_list->cpus, cpu); | ||
1113 | if (s2 != id) | ||
1114 | continue; | ||
1115 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
1116 | } | ||
1117 | val = val * counter->scale; | ||
1118 | perf_stat__update_shadow_stats(counter, &val, | ||
1119 | first_shadow_cpu(counter, id)); | ||
1120 | } | ||
1121 | } | ||
812 | } | 1122 | } |
813 | 1123 | ||
814 | static void print_aggr(char *prefix) | 1124 | static void print_aggr(char *prefix) |
@@ -818,12 +1128,23 @@ static void print_aggr(char *prefix) | |||
818 | int cpu, s, s2, id, nr; | 1128 | int cpu, s, s2, id, nr; |
819 | double uval; | 1129 | double uval; |
820 | u64 ena, run, val; | 1130 | u64 ena, run, val; |
1131 | bool first; | ||
821 | 1132 | ||
822 | if (!(aggr_map || aggr_get_id)) | 1133 | if (!(aggr_map || aggr_get_id)) |
823 | return; | 1134 | return; |
824 | 1135 | ||
1136 | aggr_update_shadow(); | ||
1137 | |||
1138 | /* | ||
1139 | * With metric_only everything is on a single line. | ||
1140 | * Without each counter has its own line. | ||
1141 | */ | ||
825 | for (s = 0; s < aggr_map->nr; s++) { | 1142 | for (s = 0; s < aggr_map->nr; s++) { |
1143 | if (prefix && metric_only) | ||
1144 | fprintf(output, "%s", prefix); | ||
1145 | |||
826 | id = aggr_map->map[s]; | 1146 | id = aggr_map->map[s]; |
1147 | first = true; | ||
827 | evlist__for_each(evsel_list, counter) { | 1148 | evlist__for_each(evsel_list, counter) { |
828 | val = ena = run = 0; | 1149 | val = ena = run = 0; |
829 | nr = 0; | 1150 | nr = 0; |
@@ -836,41 +1157,20 @@ static void print_aggr(char *prefix) | |||
836 | run += perf_counts(counter->counts, cpu, 0)->run; | 1157 | run += perf_counts(counter->counts, cpu, 0)->run; |
837 | nr++; | 1158 | nr++; |
838 | } | 1159 | } |
839 | if (prefix) | 1160 | if (first && metric_only) { |
840 | fprintf(output, "%s", prefix); | 1161 | first = false; |
841 | |||
842 | if (run == 0 || ena == 0) { | ||
843 | aggr_printout(counter, id, nr); | 1162 | aggr_printout(counter, id, nr); |
844 | |||
845 | fprintf(output, "%*s%s", | ||
846 | csv_output ? 0 : 18, | ||
847 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
848 | csv_sep); | ||
849 | |||
850 | fprintf(output, "%-*s%s", | ||
851 | csv_output ? 0 : unit_width, | ||
852 | counter->unit, csv_sep); | ||
853 | |||
854 | fprintf(output, "%*s", | ||
855 | csv_output ? 0 : -25, | ||
856 | perf_evsel__name(counter)); | ||
857 | |||
858 | if (counter->cgrp) | ||
859 | fprintf(output, "%s%s", | ||
860 | csv_sep, counter->cgrp->name); | ||
861 | |||
862 | print_running(run, ena); | ||
863 | fputc('\n', output); | ||
864 | continue; | ||
865 | } | 1163 | } |
866 | uval = val * counter->scale; | 1164 | if (prefix && !metric_only) |
867 | printout(id, nr, counter, uval); | 1165 | fprintf(output, "%s", prefix); |
868 | if (!csv_output) | ||
869 | print_noise(counter, 1.0); | ||
870 | 1166 | ||
871 | print_running(run, ena); | 1167 | uval = val * counter->scale; |
872 | fputc('\n', output); | 1168 | printout(id, nr, counter, uval, prefix, run, ena, 1.0); |
1169 | if (!metric_only) | ||
1170 | fputc('\n', output); | ||
873 | } | 1171 | } |
1172 | if (metric_only) | ||
1173 | fputc('\n', output); | ||
874 | } | 1174 | } |
875 | } | 1175 | } |
876 | 1176 | ||
@@ -895,12 +1195,7 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
895 | fprintf(output, "%s", prefix); | 1195 | fprintf(output, "%s", prefix); |
896 | 1196 | ||
897 | uval = val * counter->scale; | 1197 | uval = val * counter->scale; |
898 | printout(thread, 0, counter, uval); | 1198 | printout(thread, 0, counter, uval, prefix, run, ena, 1.0); |
899 | |||
900 | if (!csv_output) | ||
901 | print_noise(counter, 1.0); | ||
902 | |||
903 | print_running(run, ena); | ||
904 | fputc('\n', output); | 1199 | fputc('\n', output); |
905 | } | 1200 | } |
906 | } | 1201 | } |
@@ -914,43 +1209,19 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | |||
914 | FILE *output = stat_config.output; | 1209 | FILE *output = stat_config.output; |
915 | struct perf_stat_evsel *ps = counter->priv; | 1210 | struct perf_stat_evsel *ps = counter->priv; |
916 | double avg = avg_stats(&ps->res_stats[0]); | 1211 | double avg = avg_stats(&ps->res_stats[0]); |
917 | int scaled = counter->counts->scaled; | ||
918 | double uval; | 1212 | double uval; |
919 | double avg_enabled, avg_running; | 1213 | double avg_enabled, avg_running; |
920 | 1214 | ||
921 | avg_enabled = avg_stats(&ps->res_stats[1]); | 1215 | avg_enabled = avg_stats(&ps->res_stats[1]); |
922 | avg_running = avg_stats(&ps->res_stats[2]); | 1216 | avg_running = avg_stats(&ps->res_stats[2]); |
923 | 1217 | ||
924 | if (prefix) | 1218 | if (prefix && !metric_only) |
925 | fprintf(output, "%s", prefix); | 1219 | fprintf(output, "%s", prefix); |
926 | 1220 | ||
927 | if (scaled == -1 || !counter->supported) { | ||
928 | fprintf(output, "%*s%s", | ||
929 | csv_output ? 0 : 18, | ||
930 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
931 | csv_sep); | ||
932 | fprintf(output, "%-*s%s", | ||
933 | csv_output ? 0 : unit_width, | ||
934 | counter->unit, csv_sep); | ||
935 | fprintf(output, "%*s", | ||
936 | csv_output ? 0 : -25, | ||
937 | perf_evsel__name(counter)); | ||
938 | |||
939 | if (counter->cgrp) | ||
940 | fprintf(output, "%s%s", csv_sep, counter->cgrp->name); | ||
941 | |||
942 | print_running(avg_running, avg_enabled); | ||
943 | fputc('\n', output); | ||
944 | return; | ||
945 | } | ||
946 | |||
947 | uval = avg * counter->scale; | 1221 | uval = avg * counter->scale; |
948 | printout(-1, 0, counter, uval); | 1222 | printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg); |
949 | 1223 | if (!metric_only) | |
950 | print_noise(counter, avg); | 1224 | fprintf(output, "\n"); |
951 | |||
952 | print_running(avg_running, avg_enabled); | ||
953 | fprintf(output, "\n"); | ||
954 | } | 1225 | } |
955 | 1226 | ||
956 | /* | 1227 | /* |
@@ -972,39 +1243,78 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
972 | if (prefix) | 1243 | if (prefix) |
973 | fprintf(output, "%s", prefix); | 1244 | fprintf(output, "%s", prefix); |
974 | 1245 | ||
975 | if (run == 0 || ena == 0) { | 1246 | uval = val * counter->scale; |
976 | fprintf(output, "CPU%*d%s%*s%s", | 1247 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); |
977 | csv_output ? 0 : -4, | ||
978 | perf_evsel__cpus(counter)->map[cpu], csv_sep, | ||
979 | csv_output ? 0 : 18, | ||
980 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
981 | csv_sep); | ||
982 | 1248 | ||
983 | fprintf(output, "%-*s%s", | 1249 | fputc('\n', output); |
984 | csv_output ? 0 : unit_width, | 1250 | } |
985 | counter->unit, csv_sep); | 1251 | } |
986 | 1252 | ||
987 | fprintf(output, "%*s", | 1253 | static void print_no_aggr_metric(char *prefix) |
988 | csv_output ? 0 : -25, | 1254 | { |
989 | perf_evsel__name(counter)); | 1255 | int cpu; |
1256 | int nrcpus = 0; | ||
1257 | struct perf_evsel *counter; | ||
1258 | u64 ena, run, val; | ||
1259 | double uval; | ||
990 | 1260 | ||
991 | if (counter->cgrp) | 1261 | nrcpus = evsel_list->cpus->nr; |
992 | fprintf(output, "%s%s", | 1262 | for (cpu = 0; cpu < nrcpus; cpu++) { |
993 | csv_sep, counter->cgrp->name); | 1263 | bool first = true; |
994 | 1264 | ||
995 | print_running(run, ena); | 1265 | if (prefix) |
996 | fputc('\n', output); | 1266 | fputs(prefix, stat_config.output); |
997 | continue; | 1267 | evlist__for_each(evsel_list, counter) { |
1268 | if (first) { | ||
1269 | aggr_printout(counter, cpu, 0); | ||
1270 | first = false; | ||
1271 | } | ||
1272 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
1273 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
1274 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
1275 | |||
1276 | uval = val * counter->scale; | ||
1277 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); | ||
998 | } | 1278 | } |
1279 | fputc('\n', stat_config.output); | ||
1280 | } | ||
1281 | } | ||
999 | 1282 | ||
1000 | uval = val * counter->scale; | 1283 | static int aggr_header_lens[] = { |
1001 | printout(cpu, 0, counter, uval); | 1284 | [AGGR_CORE] = 18, |
1002 | if (!csv_output) | 1285 | [AGGR_SOCKET] = 12, |
1003 | print_noise(counter, 1.0); | 1286 | [AGGR_NONE] = 6, |
1004 | print_running(run, ena); | 1287 | [AGGR_THREAD] = 24, |
1288 | [AGGR_GLOBAL] = 0, | ||
1289 | }; | ||
1005 | 1290 | ||
1006 | fputc('\n', output); | 1291 | static void print_metric_headers(char *prefix) |
1292 | { | ||
1293 | struct perf_stat_output_ctx out; | ||
1294 | struct perf_evsel *counter; | ||
1295 | struct outstate os = { | ||
1296 | .fh = stat_config.output | ||
1297 | }; | ||
1298 | |||
1299 | if (prefix) | ||
1300 | fprintf(stat_config.output, "%s", prefix); | ||
1301 | |||
1302 | if (!csv_output) | ||
1303 | fprintf(stat_config.output, "%*s", | ||
1304 | aggr_header_lens[stat_config.aggr_mode], ""); | ||
1305 | |||
1306 | /* Print metrics headers only */ | ||
1307 | evlist__for_each(evsel_list, counter) { | ||
1308 | os.evsel = counter; | ||
1309 | out.ctx = &os; | ||
1310 | out.print_metric = print_metric_header; | ||
1311 | out.new_line = new_line_metric; | ||
1312 | os.evsel = counter; | ||
1313 | perf_stat__print_shadow_stats(counter, 0, | ||
1314 | 0, | ||
1315 | &out); | ||
1007 | } | 1316 | } |
1317 | fputc('\n', stat_config.output); | ||
1008 | } | 1318 | } |
1009 | 1319 | ||
1010 | static void print_interval(char *prefix, struct timespec *ts) | 1320 | static void print_interval(char *prefix, struct timespec *ts) |
@@ -1014,7 +1324,7 @@ static void print_interval(char *prefix, struct timespec *ts) | |||
1014 | 1324 | ||
1015 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | 1325 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); |
1016 | 1326 | ||
1017 | if (num_print_interval == 0 && !csv_output) { | 1327 | if (num_print_interval == 0 && !csv_output && !metric_only) { |
1018 | switch (stat_config.aggr_mode) { | 1328 | switch (stat_config.aggr_mode) { |
1019 | case AGGR_SOCKET: | 1329 | case AGGR_SOCKET: |
1020 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); | 1330 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); |
@@ -1101,6 +1411,17 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) | |||
1101 | else | 1411 | else |
1102 | print_header(argc, argv); | 1412 | print_header(argc, argv); |
1103 | 1413 | ||
1414 | if (metric_only) { | ||
1415 | static int num_print_iv; | ||
1416 | |||
1417 | if (num_print_iv == 0) | ||
1418 | print_metric_headers(prefix); | ||
1419 | if (num_print_iv++ == 25) | ||
1420 | num_print_iv = 0; | ||
1421 | if (stat_config.aggr_mode == AGGR_GLOBAL && prefix) | ||
1422 | fprintf(stat_config.output, "%s", prefix); | ||
1423 | } | ||
1424 | |||
1104 | switch (stat_config.aggr_mode) { | 1425 | switch (stat_config.aggr_mode) { |
1105 | case AGGR_CORE: | 1426 | case AGGR_CORE: |
1106 | case AGGR_SOCKET: | 1427 | case AGGR_SOCKET: |
@@ -1113,10 +1434,16 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) | |||
1113 | case AGGR_GLOBAL: | 1434 | case AGGR_GLOBAL: |
1114 | evlist__for_each(evsel_list, counter) | 1435 | evlist__for_each(evsel_list, counter) |
1115 | print_counter_aggr(counter, prefix); | 1436 | print_counter_aggr(counter, prefix); |
1437 | if (metric_only) | ||
1438 | fputc('\n', stat_config.output); | ||
1116 | break; | 1439 | break; |
1117 | case AGGR_NONE: | 1440 | case AGGR_NONE: |
1118 | evlist__for_each(evsel_list, counter) | 1441 | if (metric_only) |
1119 | print_counter(counter, prefix); | 1442 | print_no_aggr_metric(prefix); |
1443 | else { | ||
1444 | evlist__for_each(evsel_list, counter) | ||
1445 | print_counter(counter, prefix); | ||
1446 | } | ||
1120 | break; | 1447 | break; |
1121 | case AGGR_UNSET: | 1448 | case AGGR_UNSET: |
1122 | default: | 1449 | default: |
@@ -1237,6 +1564,8 @@ static const struct option stat_options[] = { | |||
1237 | "aggregate counts per thread", AGGR_THREAD), | 1564 | "aggregate counts per thread", AGGR_THREAD), |
1238 | OPT_UINTEGER('D', "delay", &initial_delay, | 1565 | OPT_UINTEGER('D', "delay", &initial_delay, |
1239 | "ms to wait before starting measurement after program start"), | 1566 | "ms to wait before starting measurement after program start"), |
1567 | OPT_BOOLEAN(0, "metric-only", &metric_only, | ||
1568 | "Only print computed metrics. No raw values"), | ||
1240 | OPT_END() | 1569 | OPT_END() |
1241 | }; | 1570 | }; |
1242 | 1571 | ||
@@ -1435,7 +1764,7 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st) | |||
1435 | */ | 1764 | */ |
1436 | static int add_default_attributes(void) | 1765 | static int add_default_attributes(void) |
1437 | { | 1766 | { |
1438 | struct perf_event_attr default_attrs[] = { | 1767 | struct perf_event_attr default_attrs0[] = { |
1439 | 1768 | ||
1440 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, | 1769 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, |
1441 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, | 1770 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, |
@@ -1443,8 +1772,14 @@ static int add_default_attributes(void) | |||
1443 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, | 1772 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, |
1444 | 1773 | ||
1445 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, | 1774 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, |
1775 | }; | ||
1776 | struct perf_event_attr frontend_attrs[] = { | ||
1446 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, | 1777 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, |
1778 | }; | ||
1779 | struct perf_event_attr backend_attrs[] = { | ||
1447 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, | 1780 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, |
1781 | }; | ||
1782 | struct perf_event_attr default_attrs1[] = { | ||
1448 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, | 1783 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, |
1449 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, | 1784 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, |
1450 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, | 1785 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, |
@@ -1561,7 +1896,19 @@ static int add_default_attributes(void) | |||
1561 | } | 1896 | } |
1562 | 1897 | ||
1563 | if (!evsel_list->nr_entries) { | 1898 | if (!evsel_list->nr_entries) { |
1564 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0) | 1899 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0) |
1900 | return -1; | ||
1901 | if (pmu_have_event("cpu", "stalled-cycles-frontend")) { | ||
1902 | if (perf_evlist__add_default_attrs(evsel_list, | ||
1903 | frontend_attrs) < 0) | ||
1904 | return -1; | ||
1905 | } | ||
1906 | if (pmu_have_event("cpu", "stalled-cycles-backend")) { | ||
1907 | if (perf_evlist__add_default_attrs(evsel_list, | ||
1908 | backend_attrs) < 0) | ||
1909 | return -1; | ||
1910 | } | ||
1911 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0) | ||
1565 | return -1; | 1912 | return -1; |
1566 | } | 1913 | } |
1567 | 1914 | ||
@@ -1825,9 +2172,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1825 | if (evsel_list == NULL) | 2172 | if (evsel_list == NULL) |
1826 | return -ENOMEM; | 2173 | return -ENOMEM; |
1827 | 2174 | ||
2175 | parse_events__shrink_config_terms(); | ||
1828 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, | 2176 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, |
1829 | (const char **) stat_usage, | 2177 | (const char **) stat_usage, |
1830 | PARSE_OPT_STOP_AT_NON_OPTION); | 2178 | PARSE_OPT_STOP_AT_NON_OPTION); |
2179 | perf_stat__init_shadow_stats(); | ||
1831 | 2180 | ||
1832 | if (csv_sep) { | 2181 | if (csv_sep) { |
1833 | csv_output = true; | 2182 | csv_output = true; |
@@ -1858,6 +2207,16 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1858 | goto out; | 2207 | goto out; |
1859 | } | 2208 | } |
1860 | 2209 | ||
2210 | if (metric_only && stat_config.aggr_mode == AGGR_THREAD) { | ||
2211 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); | ||
2212 | goto out; | ||
2213 | } | ||
2214 | |||
2215 | if (metric_only && run_count > 1) { | ||
2216 | fprintf(stderr, "--metric-only is not supported with -r\n"); | ||
2217 | goto out; | ||
2218 | } | ||
2219 | |||
1861 | if (output_fd < 0) { | 2220 | if (output_fd < 0) { |
1862 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); | 2221 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); |
1863 | parse_options_usage(stat_usage, stat_options, "log-fd", 0); | 2222 | parse_options_usage(stat_usage, stat_options, "log-fd", 0); |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index bd7a7757176f..40cc9bb3506c 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -489,7 +489,7 @@ static const char *cat_backtrace(union perf_event *event, | |||
489 | if (!chain) | 489 | if (!chain) |
490 | goto exit; | 490 | goto exit; |
491 | 491 | ||
492 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 492 | if (machine__resolve(machine, &al, sample) < 0) { |
493 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 493 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
494 | event->header.type); | 494 | event->header.type); |
495 | goto exit; | 495 | goto exit; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index bf01cbb0ef23..833214979c4f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -67,6 +67,7 @@ | |||
67 | #include <sys/utsname.h> | 67 | #include <sys/utsname.h> |
68 | #include <sys/mman.h> | 68 | #include <sys/mman.h> |
69 | 69 | ||
70 | #include <linux/stringify.h> | ||
70 | #include <linux/types.h> | 71 | #include <linux/types.h> |
71 | 72 | ||
72 | static volatile int done; | 73 | static volatile int done; |
@@ -252,7 +253,8 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
252 | char bf[160]; | 253 | char bf[160]; |
253 | int printed = 0; | 254 | int printed = 0; |
254 | const int win_width = top->winsize.ws_col - 1; | 255 | const int win_width = top->winsize.ws_col - 1; |
255 | struct hists *hists = evsel__hists(top->sym_evsel); | 256 | struct perf_evsel *evsel = top->sym_evsel; |
257 | struct hists *hists = evsel__hists(evsel); | ||
256 | 258 | ||
257 | puts(CONSOLE_CLEAR); | 259 | puts(CONSOLE_CLEAR); |
258 | 260 | ||
@@ -288,7 +290,7 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
288 | } | 290 | } |
289 | 291 | ||
290 | hists__collapse_resort(hists, NULL); | 292 | hists__collapse_resort(hists, NULL); |
291 | hists__output_resort(hists, NULL); | 293 | perf_evsel__output_resort(evsel, NULL); |
292 | 294 | ||
293 | hists__output_recalc_col_len(hists, top->print_entries - printed); | 295 | hists__output_recalc_col_len(hists, top->print_entries - printed); |
294 | putchar('\n'); | 296 | putchar('\n'); |
@@ -540,6 +542,7 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
540 | static void perf_top__sort_new_samples(void *arg) | 542 | static void perf_top__sort_new_samples(void *arg) |
541 | { | 543 | { |
542 | struct perf_top *t = arg; | 544 | struct perf_top *t = arg; |
545 | struct perf_evsel *evsel = t->sym_evsel; | ||
543 | struct hists *hists; | 546 | struct hists *hists; |
544 | 547 | ||
545 | perf_top__reset_sample_counters(t); | 548 | perf_top__reset_sample_counters(t); |
@@ -547,7 +550,7 @@ static void perf_top__sort_new_samples(void *arg) | |||
547 | if (t->evlist->selected != NULL) | 550 | if (t->evlist->selected != NULL) |
548 | t->sym_evsel = t->evlist->selected; | 551 | t->sym_evsel = t->evlist->selected; |
549 | 552 | ||
550 | hists = evsel__hists(t->sym_evsel); | 553 | hists = evsel__hists(evsel); |
551 | 554 | ||
552 | if (t->evlist->enabled) { | 555 | if (t->evlist->enabled) { |
553 | if (t->zero) { | 556 | if (t->zero) { |
@@ -559,7 +562,7 @@ static void perf_top__sort_new_samples(void *arg) | |||
559 | } | 562 | } |
560 | 563 | ||
561 | hists__collapse_resort(hists, NULL); | 564 | hists__collapse_resort(hists, NULL); |
562 | hists__output_resort(hists, NULL); | 565 | perf_evsel__output_resort(evsel, NULL); |
563 | } | 566 | } |
564 | 567 | ||
565 | static void *display_thread_tui(void *arg) | 568 | static void *display_thread_tui(void *arg) |
@@ -726,7 +729,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
726 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 729 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
727 | top->exact_samples++; | 730 | top->exact_samples++; |
728 | 731 | ||
729 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) | 732 | if (machine__resolve(machine, &al, sample) < 0) |
730 | return; | 733 | return; |
731 | 734 | ||
732 | if (!top->kptr_restrict_warned && | 735 | if (!top->kptr_restrict_warned && |
@@ -807,7 +810,6 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
807 | struct perf_session *session = top->session; | 810 | struct perf_session *session = top->session; |
808 | union perf_event *event; | 811 | union perf_event *event; |
809 | struct machine *machine; | 812 | struct machine *machine; |
810 | u8 origin; | ||
811 | int ret; | 813 | int ret; |
812 | 814 | ||
813 | while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) { | 815 | while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) { |
@@ -820,12 +822,10 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
820 | evsel = perf_evlist__id2evsel(session->evlist, sample.id); | 822 | evsel = perf_evlist__id2evsel(session->evlist, sample.id); |
821 | assert(evsel != NULL); | 823 | assert(evsel != NULL); |
822 | 824 | ||
823 | origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
824 | |||
825 | if (event->header.type == PERF_RECORD_SAMPLE) | 825 | if (event->header.type == PERF_RECORD_SAMPLE) |
826 | ++top->samples; | 826 | ++top->samples; |
827 | 827 | ||
828 | switch (origin) { | 828 | switch (sample.cpumode) { |
829 | case PERF_RECORD_MISC_USER: | 829 | case PERF_RECORD_MISC_USER: |
830 | ++top->us_samples; | 830 | ++top->us_samples; |
831 | if (top->hide_user_symbols) | 831 | if (top->hide_user_symbols) |
@@ -1063,7 +1063,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
1063 | return parse_callchain_top_opt(arg); | 1063 | return parse_callchain_top_opt(arg); |
1064 | } | 1064 | } |
1065 | 1065 | ||
1066 | static int perf_top_config(const char *var, const char *value, void *cb) | 1066 | static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused) |
1067 | { | 1067 | { |
1068 | if (!strcmp(var, "top.call-graph")) | 1068 | if (!strcmp(var, "top.call-graph")) |
1069 | var = "call-graph.record-mode"; /* fall-through */ | 1069 | var = "call-graph.record-mode"; /* fall-through */ |
@@ -1072,7 +1072,7 @@ static int perf_top_config(const char *var, const char *value, void *cb) | |||
1072 | return 0; | 1072 | return 0; |
1073 | } | 1073 | } |
1074 | 1074 | ||
1075 | return perf_default_config(var, value, cb); | 1075 | return 0; |
1076 | } | 1076 | } |
1077 | 1077 | ||
1078 | static int | 1078 | static int |
@@ -1212,6 +1212,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1212 | parse_branch_stack), | 1212 | parse_branch_stack), |
1213 | OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, | 1213 | OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, |
1214 | "Show raw trace event output (do not use print fmt or plugins)"), | 1214 | "Show raw trace event output (do not use print fmt or plugins)"), |
1215 | OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy, | ||
1216 | "Show entries in a hierarchy"), | ||
1215 | OPT_END() | 1217 | OPT_END() |
1216 | }; | 1218 | }; |
1217 | const char * const top_usage[] = { | 1219 | const char * const top_usage[] = { |
@@ -1239,10 +1241,30 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1239 | goto out_delete_evlist; | 1241 | goto out_delete_evlist; |
1240 | } | 1242 | } |
1241 | 1243 | ||
1244 | if (symbol_conf.report_hierarchy) { | ||
1245 | /* disable incompatible options */ | ||
1246 | symbol_conf.event_group = false; | ||
1247 | symbol_conf.cumulate_callchain = false; | ||
1248 | |||
1249 | if (field_order) { | ||
1250 | pr_err("Error: --hierarchy and --fields options cannot be used together\n"); | ||
1251 | parse_options_usage(top_usage, options, "fields", 0); | ||
1252 | parse_options_usage(NULL, options, "hierarchy", 0); | ||
1253 | goto out_delete_evlist; | ||
1254 | } | ||
1255 | } | ||
1256 | |||
1242 | sort__mode = SORT_MODE__TOP; | 1257 | sort__mode = SORT_MODE__TOP; |
1243 | /* display thread wants entries to be collapsed in a different tree */ | 1258 | /* display thread wants entries to be collapsed in a different tree */ |
1244 | sort__need_collapse = 1; | 1259 | sort__need_collapse = 1; |
1245 | 1260 | ||
1261 | if (top.use_stdio) | ||
1262 | use_browser = 0; | ||
1263 | else if (top.use_tui) | ||
1264 | use_browser = 1; | ||
1265 | |||
1266 | setup_browser(false); | ||
1267 | |||
1246 | if (setup_sorting(top.evlist) < 0) { | 1268 | if (setup_sorting(top.evlist) < 0) { |
1247 | if (sort_order) | 1269 | if (sort_order) |
1248 | parse_options_usage(top_usage, options, "s", 1); | 1270 | parse_options_usage(top_usage, options, "s", 1); |
@@ -1252,13 +1274,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1252 | goto out_delete_evlist; | 1274 | goto out_delete_evlist; |
1253 | } | 1275 | } |
1254 | 1276 | ||
1255 | if (top.use_stdio) | ||
1256 | use_browser = 0; | ||
1257 | else if (top.use_tui) | ||
1258 | use_browser = 1; | ||
1259 | |||
1260 | setup_browser(false); | ||
1261 | |||
1262 | status = target__validate(target); | 1277 | status = target__validate(target); |
1263 | if (status) { | 1278 | if (status) { |
1264 | target__strerror(target, status, errbuf, BUFSIZ); | 1279 | target__strerror(target, status, errbuf, BUFSIZ); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 20916dd77aac..93ac724fb635 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "util/stat.h" | 33 | #include "util/stat.h" |
34 | #include "trace-event.h" | 34 | #include "trace-event.h" |
35 | #include "util/parse-events.h" | 35 | #include "util/parse-events.h" |
36 | #include "util/bpf-loader.h" | ||
36 | 37 | ||
37 | #include <libaudit.h> | 38 | #include <libaudit.h> |
38 | #include <stdlib.h> | 39 | #include <stdlib.h> |
@@ -1724,8 +1725,12 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
1724 | 1725 | ||
1725 | sc->args = sc->tp_format->format.fields; | 1726 | sc->args = sc->tp_format->format.fields; |
1726 | sc->nr_args = sc->tp_format->format.nr_fields; | 1727 | sc->nr_args = sc->tp_format->format.nr_fields; |
1727 | /* drop nr field - not relevant here; does not exist on older kernels */ | 1728 | /* |
1728 | if (sc->args && strcmp(sc->args->name, "nr") == 0) { | 1729 | * We need to check and discard the first variable '__syscall_nr' |
1730 | * or 'nr' that mean the syscall number. It is needless here. | ||
1731 | * So drop '__syscall_nr' or 'nr' field but does not exist on older kernels. | ||
1732 | */ | ||
1733 | if (sc->args && (!strcmp(sc->args->name, "__syscall_nr") || !strcmp(sc->args->name, "nr"))) { | ||
1729 | sc->args = sc->args->next; | 1734 | sc->args = sc->args->next; |
1730 | --sc->nr_args; | 1735 | --sc->nr_args; |
1731 | } | 1736 | } |
@@ -2177,6 +2182,37 @@ out_dump: | |||
2177 | return 0; | 2182 | return 0; |
2178 | } | 2183 | } |
2179 | 2184 | ||
2185 | static void bpf_output__printer(enum binary_printer_ops op, | ||
2186 | unsigned int val, void *extra) | ||
2187 | { | ||
2188 | FILE *output = extra; | ||
2189 | unsigned char ch = (unsigned char)val; | ||
2190 | |||
2191 | switch (op) { | ||
2192 | case BINARY_PRINT_CHAR_DATA: | ||
2193 | fprintf(output, "%c", isprint(ch) ? ch : '.'); | ||
2194 | break; | ||
2195 | case BINARY_PRINT_DATA_BEGIN: | ||
2196 | case BINARY_PRINT_LINE_BEGIN: | ||
2197 | case BINARY_PRINT_ADDR: | ||
2198 | case BINARY_PRINT_NUM_DATA: | ||
2199 | case BINARY_PRINT_NUM_PAD: | ||
2200 | case BINARY_PRINT_SEP: | ||
2201 | case BINARY_PRINT_CHAR_PAD: | ||
2202 | case BINARY_PRINT_LINE_END: | ||
2203 | case BINARY_PRINT_DATA_END: | ||
2204 | default: | ||
2205 | break; | ||
2206 | } | ||
2207 | } | ||
2208 | |||
2209 | static void bpf_output__fprintf(struct trace *trace, | ||
2210 | struct perf_sample *sample) | ||
2211 | { | ||
2212 | print_binary(sample->raw_data, sample->raw_size, 8, | ||
2213 | bpf_output__printer, trace->output); | ||
2214 | } | ||
2215 | |||
2180 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | 2216 | static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, |
2181 | union perf_event *event __maybe_unused, | 2217 | union perf_event *event __maybe_unused, |
2182 | struct perf_sample *sample) | 2218 | struct perf_sample *sample) |
@@ -2189,7 +2225,9 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, | |||
2189 | 2225 | ||
2190 | fprintf(trace->output, "%s:", evsel->name); | 2226 | fprintf(trace->output, "%s:", evsel->name); |
2191 | 2227 | ||
2192 | if (evsel->tp_format) { | 2228 | if (perf_evsel__is_bpf_output(evsel)) { |
2229 | bpf_output__fprintf(trace, sample); | ||
2230 | } else if (evsel->tp_format) { | ||
2193 | event_format__fprintf(evsel->tp_format, sample->cpu, | 2231 | event_format__fprintf(evsel->tp_format, sample->cpu, |
2194 | sample->raw_data, sample->raw_size, | 2232 | sample->raw_data, sample->raw_size, |
2195 | trace->output); | 2233 | trace->output); |
@@ -2218,11 +2256,10 @@ static void print_location(FILE *f, struct perf_sample *sample, | |||
2218 | 2256 | ||
2219 | static int trace__pgfault(struct trace *trace, | 2257 | static int trace__pgfault(struct trace *trace, |
2220 | struct perf_evsel *evsel, | 2258 | struct perf_evsel *evsel, |
2221 | union perf_event *event, | 2259 | union perf_event *event __maybe_unused, |
2222 | struct perf_sample *sample) | 2260 | struct perf_sample *sample) |
2223 | { | 2261 | { |
2224 | struct thread *thread; | 2262 | struct thread *thread; |
2225 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
2226 | struct addr_location al; | 2263 | struct addr_location al; |
2227 | char map_type = 'd'; | 2264 | char map_type = 'd'; |
2228 | struct thread_trace *ttrace; | 2265 | struct thread_trace *ttrace; |
@@ -2241,7 +2278,7 @@ static int trace__pgfault(struct trace *trace, | |||
2241 | if (trace->summary_only) | 2278 | if (trace->summary_only) |
2242 | goto out; | 2279 | goto out; |
2243 | 2280 | ||
2244 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | 2281 | thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION, |
2245 | sample->ip, &al); | 2282 | sample->ip, &al); |
2246 | 2283 | ||
2247 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); | 2284 | trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); |
@@ -2254,11 +2291,11 @@ static int trace__pgfault(struct trace *trace, | |||
2254 | 2291 | ||
2255 | fprintf(trace->output, "] => "); | 2292 | fprintf(trace->output, "] => "); |
2256 | 2293 | ||
2257 | thread__find_addr_location(thread, cpumode, MAP__VARIABLE, | 2294 | thread__find_addr_location(thread, sample->cpumode, MAP__VARIABLE, |
2258 | sample->addr, &al); | 2295 | sample->addr, &al); |
2259 | 2296 | ||
2260 | if (!al.map) { | 2297 | if (!al.map) { |
2261 | thread__find_addr_location(thread, cpumode, | 2298 | thread__find_addr_location(thread, sample->cpumode, |
2262 | MAP__FUNCTION, sample->addr, &al); | 2299 | MAP__FUNCTION, sample->addr, &al); |
2263 | 2300 | ||
2264 | if (al.map) | 2301 | if (al.map) |
@@ -2586,6 +2623,16 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
2586 | if (err < 0) | 2623 | if (err < 0) |
2587 | goto out_error_open; | 2624 | goto out_error_open; |
2588 | 2625 | ||
2626 | err = bpf__apply_obj_config(); | ||
2627 | if (err) { | ||
2628 | char errbuf[BUFSIZ]; | ||
2629 | |||
2630 | bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); | ||
2631 | pr_err("ERROR: Apply config to BPF failed: %s\n", | ||
2632 | errbuf); | ||
2633 | goto out_error_open; | ||
2634 | } | ||
2635 | |||
2589 | /* | 2636 | /* |
2590 | * Better not use !target__has_task() here because we need to cover the | 2637 | * Better not use !target__has_task() here because we need to cover the |
2591 | * case where no threads were specified in the command line, but a | 2638 | * case where no threads were specified in the command line, but a |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 3f871b54e261..41c24010ab43 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -7,38 +7,38 @@ | |||
7 | extern const char perf_usage_string[]; | 7 | extern const char perf_usage_string[]; |
8 | extern const char perf_more_info_string[]; | 8 | extern const char perf_more_info_string[]; |
9 | 9 | ||
10 | extern void list_common_cmds_help(void); | 10 | void list_common_cmds_help(void); |
11 | extern const char *help_unknown_cmd(const char *cmd); | 11 | const char *help_unknown_cmd(const char *cmd); |
12 | extern void prune_packed_objects(int); | 12 | void prune_packed_objects(int); |
13 | extern int read_line_with_nul(char *buf, int size, FILE *file); | 13 | int read_line_with_nul(char *buf, int size, FILE *file); |
14 | extern int check_pager_config(const char *cmd); | 14 | int check_pager_config(const char *cmd); |
15 | 15 | ||
16 | extern int cmd_annotate(int argc, const char **argv, const char *prefix); | 16 | int cmd_annotate(int argc, const char **argv, const char *prefix); |
17 | extern int cmd_bench(int argc, const char **argv, const char *prefix); | 17 | int cmd_bench(int argc, const char **argv, const char *prefix); |
18 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); | 18 | int cmd_buildid_cache(int argc, const char **argv, const char *prefix); |
19 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); | 19 | int cmd_buildid_list(int argc, const char **argv, const char *prefix); |
20 | extern int cmd_config(int argc, const char **argv, const char *prefix); | 20 | int cmd_config(int argc, const char **argv, const char *prefix); |
21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); | 21 | int cmd_diff(int argc, const char **argv, const char *prefix); |
22 | extern int cmd_evlist(int argc, const char **argv, const char *prefix); | 22 | int cmd_evlist(int argc, const char **argv, const char *prefix); |
23 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 23 | int cmd_help(int argc, const char **argv, const char *prefix); |
24 | extern int cmd_sched(int argc, const char **argv, const char *prefix); | 24 | int cmd_sched(int argc, const char **argv, const char *prefix); |
25 | extern int cmd_list(int argc, const char **argv, const char *prefix); | 25 | int cmd_list(int argc, const char **argv, const char *prefix); |
26 | extern int cmd_record(int argc, const char **argv, const char *prefix); | 26 | int cmd_record(int argc, const char **argv, const char *prefix); |
27 | extern int cmd_report(int argc, const char **argv, const char *prefix); | 27 | int cmd_report(int argc, const char **argv, const char *prefix); |
28 | extern int cmd_stat(int argc, const char **argv, const char *prefix); | 28 | int cmd_stat(int argc, const char **argv, const char *prefix); |
29 | extern int cmd_timechart(int argc, const char **argv, const char *prefix); | 29 | int cmd_timechart(int argc, const char **argv, const char *prefix); |
30 | extern int cmd_top(int argc, const char **argv, const char *prefix); | 30 | int cmd_top(int argc, const char **argv, const char *prefix); |
31 | extern int cmd_script(int argc, const char **argv, const char *prefix); | 31 | int cmd_script(int argc, const char **argv, const char *prefix); |
32 | extern int cmd_version(int argc, const char **argv, const char *prefix); | 32 | int cmd_version(int argc, const char **argv, const char *prefix); |
33 | extern int cmd_probe(int argc, const char **argv, const char *prefix); | 33 | int cmd_probe(int argc, const char **argv, const char *prefix); |
34 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); | 34 | int cmd_kmem(int argc, const char **argv, const char *prefix); |
35 | extern int cmd_lock(int argc, const char **argv, const char *prefix); | 35 | int cmd_lock(int argc, const char **argv, const char *prefix); |
36 | extern int cmd_kvm(int argc, const char **argv, const char *prefix); | 36 | int cmd_kvm(int argc, const char **argv, const char *prefix); |
37 | extern int cmd_test(int argc, const char **argv, const char *prefix); | 37 | int cmd_test(int argc, const char **argv, const char *prefix); |
38 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | 38 | int cmd_trace(int argc, const char **argv, const char *prefix); |
39 | extern int cmd_inject(int argc, const char **argv, const char *prefix); | 39 | int cmd_inject(int argc, const char **argv, const char *prefix); |
40 | extern int cmd_mem(int argc, const char **argv, const char *prefix); | 40 | int cmd_mem(int argc, const char **argv, const char *prefix); |
41 | extern int cmd_data(int argc, const char **argv, const char *prefix); | 41 | int cmd_data(int argc, const char **argv, const char *prefix); |
42 | 42 | ||
43 | extern int find_scripts(char **scripts_array, char **scripts_path_array); | 43 | int find_scripts(char **scripts_array, char **scripts_path_array); |
44 | #endif | 44 | #endif |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 511141b102e8..f7d7f5a1cad5 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -61,50 +61,45 @@ endif | |||
61 | 61 | ||
62 | ifeq ($(LIBUNWIND_LIBS),) | 62 | ifeq ($(LIBUNWIND_LIBS),) |
63 | NO_LIBUNWIND := 1 | 63 | NO_LIBUNWIND := 1 |
64 | else | ||
65 | # | ||
66 | # For linking with debug library, run like: | ||
67 | # | ||
68 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | ||
69 | # | ||
70 | ifdef LIBUNWIND_DIR | ||
71 | LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include | ||
72 | LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib | ||
73 | endif | ||
74 | LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS) | ||
75 | |||
76 | # Set per-feature check compilation flags | ||
77 | FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) | ||
78 | FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) | ||
79 | FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) | ||
80 | FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) | ||
81 | endif | 64 | endif |
65 | # | ||
66 | # For linking with debug library, run like: | ||
67 | # | ||
68 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | ||
69 | # | ||
70 | ifdef LIBUNWIND_DIR | ||
71 | LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include | ||
72 | LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib | ||
73 | endif | ||
74 | LIBUNWIND_LDFLAGS += $(LIBUNWIND_LIBS) | ||
75 | |||
76 | # Set per-feature check compilation flags | ||
77 | FEATURE_CHECK_CFLAGS-libunwind = $(LIBUNWIND_CFLAGS) | ||
78 | FEATURE_CHECK_LDFLAGS-libunwind = $(LIBUNWIND_LDFLAGS) | ||
79 | FEATURE_CHECK_CFLAGS-libunwind-debug-frame = $(LIBUNWIND_CFLAGS) | ||
80 | FEATURE_CHECK_LDFLAGS-libunwind-debug-frame = $(LIBUNWIND_LDFLAGS) | ||
82 | 81 | ||
83 | ifeq ($(NO_PERF_REGS),0) | 82 | ifeq ($(NO_PERF_REGS),0) |
84 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT | 83 | CFLAGS += -DHAVE_PERF_REGS_SUPPORT |
85 | endif | 84 | endif |
86 | 85 | ||
87 | ifndef NO_LIBELF | 86 | # for linking with debug library, run like: |
88 | # for linking with debug library, run like: | 87 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ |
89 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ | 88 | ifdef LIBDW_DIR |
90 | ifdef LIBDW_DIR | 89 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include |
91 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include | 90 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib |
92 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib | ||
93 | endif | ||
94 | FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS) | ||
95 | FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw | ||
96 | endif | 91 | endif |
92 | FEATURE_CHECK_CFLAGS-libdw-dwarf-unwind := $(LIBDW_CFLAGS) | ||
93 | FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw | ||
97 | 94 | ||
98 | ifdef LIBBABELTRACE | 95 | # for linking with debug library, run like: |
99 | # for linking with debug library, run like: | 96 | # make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/ |
100 | # make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/ | 97 | ifdef LIBBABELTRACE_DIR |
101 | ifdef LIBBABELTRACE_DIR | 98 | LIBBABELTRACE_CFLAGS := -I$(LIBBABELTRACE_DIR)/include |
102 | LIBBABELTRACE_CFLAGS := -I$(LIBBABELTRACE_DIR)/include | 99 | LIBBABELTRACE_LDFLAGS := -L$(LIBBABELTRACE_DIR)/lib |
103 | LIBBABELTRACE_LDFLAGS := -L$(LIBBABELTRACE_DIR)/lib | ||
104 | endif | ||
105 | FEATURE_CHECK_CFLAGS-libbabeltrace := $(LIBBABELTRACE_CFLAGS) | ||
106 | FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf | ||
107 | endif | 100 | endif |
101 | FEATURE_CHECK_CFLAGS-libbabeltrace := $(LIBBABELTRACE_CFLAGS) | ||
102 | FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf | ||
108 | 103 | ||
109 | FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi | 104 | FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi |
110 | # include ARCH specific config | 105 | # include ARCH specific config |
@@ -114,7 +109,7 @@ ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET | |||
114 | CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET | 109 | CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET |
115 | endif | 110 | endif |
116 | 111 | ||
117 | include $(src-perf)/config/utilities.mak | 112 | include $(srctree)/tools/scripts/utilities.mak |
118 | 113 | ||
119 | ifeq ($(call get-executable,$(FLEX)),) | 114 | ifeq ($(call get-executable,$(FLEX)),) |
120 | dummy := $(error Error: $(FLEX) is missing on this system, please install it) | 115 | dummy := $(error Error: $(FLEX) is missing on this system, please install it) |
@@ -145,28 +140,26 @@ ifdef PARSER_DEBUG | |||
145 | $(call detected_var,PARSER_DEBUG_FLEX) | 140 | $(call detected_var,PARSER_DEBUG_FLEX) |
146 | endif | 141 | endif |
147 | 142 | ||
148 | ifndef NO_LIBPYTHON | 143 | # Try different combinations to accommodate systems that only have |
149 | # Try different combinations to accommodate systems that only have | 144 | # python[2][-config] in weird combinations but always preferring |
150 | # python[2][-config] in weird combinations but always preferring | 145 | # python2 and python2-config as per pep-0394. If we catch a |
151 | # python2 and python2-config as per pep-0394. If we catch a | 146 | # python[-config] in version 3, the version check will kill it. |
152 | # python[-config] in version 3, the version check will kill it. | 147 | PYTHON2 := $(if $(call get-executable,python2),python2,python) |
153 | PYTHON2 := $(if $(call get-executable,python2),python2,python) | 148 | override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2)) |
154 | override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2)) | 149 | PYTHON2_CONFIG := \ |
155 | PYTHON2_CONFIG := \ | 150 | $(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config) |
156 | $(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config) | 151 | override PYTHON_CONFIG := \ |
157 | override PYTHON_CONFIG := \ | 152 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG)) |
158 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG)) | ||
159 | 153 | ||
160 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | 154 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) |
161 | 155 | ||
162 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | 156 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) |
163 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | 157 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) |
164 | 158 | ||
165 | FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS) | 159 | FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS) |
166 | FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) | 160 | FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) |
167 | FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS) | 161 | FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS) |
168 | FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS) | 162 | FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS) |
169 | endif | ||
170 | 163 | ||
171 | CFLAGS += -fno-omit-frame-pointer | 164 | CFLAGS += -fno-omit-frame-pointer |
172 | CFLAGS += -ggdb3 | 165 | CFLAGS += -ggdb3 |
@@ -335,6 +328,13 @@ ifndef NO_LIBELF | |||
335 | endif # NO_LIBBPF | 328 | endif # NO_LIBBPF |
336 | endif # NO_LIBELF | 329 | endif # NO_LIBELF |
337 | 330 | ||
331 | ifdef PERF_HAVE_JITDUMP | ||
332 | ifndef NO_DWARF | ||
333 | $(call detected,CONFIG_JITDUMP) | ||
334 | CFLAGS += -DHAVE_JITDUMP | ||
335 | endif | ||
336 | endif | ||
337 | |||
338 | ifeq ($(ARCH),powerpc) | 338 | ifeq ($(ARCH),powerpc) |
339 | ifndef NO_DWARF | 339 | ifndef NO_DWARF |
340 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX | 340 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX |
@@ -411,6 +411,17 @@ ifndef NO_LIBAUDIT | |||
411 | endif | 411 | endif |
412 | endif | 412 | endif |
413 | 413 | ||
414 | ifndef NO_LIBCRYPTO | ||
415 | ifneq ($(feature-libcrypto), 1) | ||
416 | msg := $(warning No libcrypto.h found, disables jitted code injection, please install libssl-devel or libssl-dev); | ||
417 | NO_LIBCRYPTO := 1 | ||
418 | else | ||
419 | CFLAGS += -DHAVE_LIBCRYPTO_SUPPORT | ||
420 | EXTLIBS += -lcrypto | ||
421 | $(call detected,CONFIG_CRYPTO) | ||
422 | endif | ||
423 | endif | ||
424 | |||
414 | ifdef NO_NEWT | 425 | ifdef NO_NEWT |
415 | NO_SLANG=1 | 426 | NO_SLANG=1 |
416 | endif | 427 | endif |
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak deleted file mode 100644 index c16ce833079c..000000000000 --- a/tools/perf/config/utilities.mak +++ /dev/null | |||
@@ -1,179 +0,0 @@ | |||
1 | # This allows us to work with the newline character: | ||
2 | define newline | ||
3 | |||
4 | |||
5 | endef | ||
6 | newline := $(newline) | ||
7 | |||
8 | # nl-escape | ||
9 | # | ||
10 | # Usage: escape = $(call nl-escape[,escape]) | ||
11 | # | ||
12 | # This is used as the common way to specify | ||
13 | # what should replace a newline when escaping | ||
14 | # newlines; the default is a bizarre string. | ||
15 | # | ||
16 | nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n) | ||
17 | |||
18 | # escape-nl | ||
19 | # | ||
20 | # Usage: escaped-text = $(call escape-nl,text[,escape]) | ||
21 | # | ||
22 | # GNU make's $(shell ...) function converts to a | ||
23 | # single space each newline character in the output | ||
24 | # produced during the expansion; this may not be | ||
25 | # desirable. | ||
26 | # | ||
27 | # The only solution is to change each newline into | ||
28 | # something that won't be converted, so that the | ||
29 | # information can be recovered later with | ||
30 | # $(call unescape-nl...) | ||
31 | # | ||
32 | escape-nl = $(subst $(newline),$(call nl-escape,$(2)),$(1)) | ||
33 | |||
34 | # unescape-nl | ||
35 | # | ||
36 | # Usage: text = $(call unescape-nl,escaped-text[,escape]) | ||
37 | # | ||
38 | # See escape-nl. | ||
39 | # | ||
40 | unescape-nl = $(subst $(call nl-escape,$(2)),$(newline),$(1)) | ||
41 | |||
42 | # shell-escape-nl | ||
43 | # | ||
44 | # Usage: $(shell some-command | $(call shell-escape-nl[,escape])) | ||
45 | # | ||
46 | # Use this to escape newlines from within a shell call; | ||
47 | # the default escape is a bizarre string. | ||
48 | # | ||
49 | # NOTE: The escape is used directly as a string constant | ||
50 | # in an `awk' program that is delimited by shell | ||
51 | # single-quotes, so be wary of the characters | ||
52 | # that are chosen. | ||
53 | # | ||
54 | define shell-escape-nl | ||
55 | awk 'NR==1 {t=$$0} NR>1 {t=t "$(nl-escape)" $$0} END {printf t}' | ||
56 | endef | ||
57 | |||
58 | # shell-unescape-nl | ||
59 | # | ||
60 | # Usage: $(shell some-command | $(call shell-unescape-nl[,escape])) | ||
61 | # | ||
62 | # Use this to unescape newlines from within a shell call; | ||
63 | # the default escape is a bizarre string. | ||
64 | # | ||
65 | # NOTE: The escape is used directly as an extended regular | ||
66 | # expression constant in an `awk' program that is | ||
67 | # delimited by shell single-quotes, so be wary | ||
68 | # of the characters that are chosen. | ||
69 | # | ||
70 | # (The bash shell has a bug where `{gsub(...),...}' is | ||
71 | # misinterpreted as a brace expansion; this can be | ||
72 | # overcome by putting a space between `{' and `gsub'). | ||
73 | # | ||
74 | define shell-unescape-nl | ||
75 | awk 'NR==1 {t=$$0} NR>1 {t=t "\n" $$0} END { gsub(/$(nl-escape)/,"\n",t); printf t }' | ||
76 | endef | ||
77 | |||
78 | # escape-for-shell-sq | ||
79 | # | ||
80 | # Usage: embeddable-text = $(call escape-for-shell-sq,text) | ||
81 | # | ||
82 | # This function produces text that is suitable for | ||
83 | # embedding in a shell string that is delimited by | ||
84 | # single-quotes. | ||
85 | # | ||
86 | escape-for-shell-sq = $(subst ','\'',$(1)) | ||
87 | |||
88 | # shell-sq | ||
89 | # | ||
90 | # Usage: single-quoted-and-escaped-text = $(call shell-sq,text) | ||
91 | # | ||
92 | shell-sq = '$(escape-for-shell-sq)' | ||
93 | |||
94 | # shell-wordify | ||
95 | # | ||
96 | # Usage: wordified-text = $(call shell-wordify,text) | ||
97 | # | ||
98 | # For instance: | ||
99 | # | ||
100 | # |define text | ||
101 | # |hello | ||
102 | # |world | ||
103 | # |endef | ||
104 | # | | ||
105 | # |target: | ||
106 | # | echo $(call shell-wordify,$(text)) | ||
107 | # | ||
108 | # At least GNU make gets confused by expanding a newline | ||
109 | # within the context of a command line of a makefile rule | ||
110 | # (this is in constrast to a `$(shell ...)' function call, | ||
111 | # which can handle it just fine). | ||
112 | # | ||
113 | # This function avoids the problem by producing a string | ||
114 | # that works as a shell word, regardless of whether or | ||
115 | # not it contains a newline. | ||
116 | # | ||
117 | # If the text to be wordified contains a newline, then | ||
118 | # an intrictate shell command substitution is constructed | ||
119 | # to render the text as a single line; when the shell | ||
120 | # processes the resulting escaped text, it transforms | ||
121 | # it into the original unescaped text. | ||
122 | # | ||
123 | # If the text does not contain a newline, then this function | ||
124 | # produces the same results as the `$(shell-sq)' function. | ||
125 | # | ||
126 | shell-wordify = $(if $(findstring $(newline),$(1)),$(_sw-esc-nl),$(shell-sq)) | ||
127 | define _sw-esc-nl | ||
128 | "$$(echo $(call escape-nl,$(shell-sq),$(2)) | $(call shell-unescape-nl,$(2)))" | ||
129 | endef | ||
130 | |||
131 | # is-absolute | ||
132 | # | ||
133 | # Usage: bool-value = $(call is-absolute,path) | ||
134 | # | ||
135 | is-absolute = $(shell echo $(shell-sq) | grep -q ^/ && echo y) | ||
136 | |||
137 | # lookup | ||
138 | # | ||
139 | # Usage: absolute-executable-path-or-empty = $(call lookup,path) | ||
140 | # | ||
141 | # (It's necessary to use `sh -c' because GNU make messes up by | ||
142 | # trying too hard and getting things wrong). | ||
143 | # | ||
144 | lookup = $(call unescape-nl,$(shell sh -c $(_l-sh))) | ||
145 | _l-sh = $(call shell-sq,command -v $(shell-sq) | $(call shell-escape-nl,)) | ||
146 | |||
147 | # is-executable | ||
148 | # | ||
149 | # Usage: bool-value = $(call is-executable,path) | ||
150 | # | ||
151 | # (It's necessary to use `sh -c' because GNU make messes up by | ||
152 | # trying too hard and getting things wrong). | ||
153 | # | ||
154 | is-executable = $(call _is-executable-helper,$(shell-sq)) | ||
155 | _is-executable-helper = $(shell sh -c $(_is-executable-sh)) | ||
156 | _is-executable-sh = $(call shell-sq,test -f $(1) -a -x $(1) && echo y) | ||
157 | |||
158 | # get-executable | ||
159 | # | ||
160 | # Usage: absolute-executable-path-or-empty = $(call get-executable,path) | ||
161 | # | ||
162 | # The goal is to get an absolute path for an executable; | ||
163 | # the `command -v' is defined by POSIX, but it's not | ||
164 | # necessarily very portable, so it's only used if | ||
165 | # relative path resolution is requested, as determined | ||
166 | # by the presence of a leading `/'. | ||
167 | # | ||
168 | get-executable = $(if $(1),$(if $(is-absolute),$(_ge-abspath),$(lookup))) | ||
169 | _ge-abspath = $(if $(is-executable),$(1)) | ||
170 | |||
171 | # get-supplied-or-default-executable | ||
172 | # | ||
173 | # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) | ||
174 | # | ||
175 | define get-executable-or-default | ||
176 | $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) | ||
177 | endef | ||
178 | _ge_attempt = $(if $(get-executable),$(get-executable),$(call _gea_err,$(2))) | ||
179 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) | ||
diff --git a/tools/perf/jvmti/Makefile b/tools/perf/jvmti/Makefile new file mode 100644 index 000000000000..5ce61a1bda9c --- /dev/null +++ b/tools/perf/jvmti/Makefile | |||
@@ -0,0 +1,89 @@ | |||
1 | ARCH=$(shell uname -m) | ||
2 | |||
3 | ifeq ($(ARCH), x86_64) | ||
4 | JARCH=amd64 | ||
5 | endif | ||
6 | ifeq ($(ARCH), armv7l) | ||
7 | JARCH=armhf | ||
8 | endif | ||
9 | ifeq ($(ARCH), armv6l) | ||
10 | JARCH=armhf | ||
11 | endif | ||
12 | ifeq ($(ARCH), aarch64) | ||
13 | JARCH=aarch64 | ||
14 | endif | ||
15 | ifeq ($(ARCH), ppc64) | ||
16 | JARCH=powerpc | ||
17 | endif | ||
18 | ifeq ($(ARCH), ppc64le) | ||
19 | JARCH=powerpc | ||
20 | endif | ||
21 | |||
22 | DESTDIR=/usr/local | ||
23 | |||
24 | VERSION=1 | ||
25 | REVISION=0 | ||
26 | AGE=0 | ||
27 | |||
28 | LN=ln -sf | ||
29 | RM=rm | ||
30 | |||
31 | SLIBJVMTI=libjvmti.so.$(VERSION).$(REVISION).$(AGE) | ||
32 | VLIBJVMTI=libjvmti.so.$(VERSION) | ||
33 | SLDFLAGS=-shared -Wl,-soname -Wl,$(VLIBJVMTI) | ||
34 | SOLIBEXT=so | ||
35 | |||
36 | # The following works at least on fedora 23, you may need the next | ||
37 | # line for other distros. | ||
38 | ifneq (,$(wildcard /usr/sbin/update-java-alternatives)) | ||
39 | JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | cut -d ' ' -f 3) | ||
40 | else | ||
41 | ifneq (,$(wildcard /usr/sbin/alternatives)) | ||
42 | JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') | ||
43 | endif | ||
44 | endif | ||
45 | ifndef JDIR | ||
46 | $(error Could not find alternatives command, you need to set JDIR= to point to the root of your Java directory) | ||
47 | else | ||
48 | ifeq (,$(wildcard $(JDIR)/include/jvmti.h)) | ||
49 | $(error the openjdk development package appears to me missing, install and try again) | ||
50 | endif | ||
51 | endif | ||
52 | $(info Using Java from $(JDIR)) | ||
53 | # -lrt required in 32-bit mode for clock_gettime() | ||
54 | LIBS=-lelf -lrt | ||
55 | INCDIR=-I $(JDIR)/include -I $(JDIR)/include/linux | ||
56 | |||
57 | TARGETS=$(SLIBJVMTI) | ||
58 | |||
59 | SRCS=libjvmti.c jvmti_agent.c | ||
60 | OBJS=$(SRCS:.c=.o) | ||
61 | SOBJS=$(OBJS:.o=.lo) | ||
62 | OPT=-O2 -g -Werror -Wall | ||
63 | |||
64 | CFLAGS=$(INCDIR) $(OPT) | ||
65 | |||
66 | all: $(TARGETS) | ||
67 | |||
68 | .c.o: | ||
69 | $(CC) $(CFLAGS) -c $*.c | ||
70 | .c.lo: | ||
71 | $(CC) -fPIC -DPIC $(CFLAGS) -c $*.c -o $*.lo | ||
72 | |||
73 | $(OBJS) $(SOBJS): Makefile jvmti_agent.h ../util/jitdump.h | ||
74 | |||
75 | $(SLIBJVMTI): $(SOBJS) | ||
76 | $(CC) $(CFLAGS) $(SLDFLAGS) -o $@ $(SOBJS) $(LIBS) | ||
77 | $(LN) $@ libjvmti.$(SOLIBEXT) | ||
78 | |||
79 | clean: | ||
80 | $(RM) -f *.o *.so.* *.so *.lo | ||
81 | |||
82 | install: | ||
83 | -mkdir -p $(DESTDIR)/lib | ||
84 | install -m 755 $(SLIBJVMTI) $(DESTDIR)/lib/ | ||
85 | (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) $(VLIBJVMTI)) | ||
86 | (cd $(DESTDIR)/lib; $(LN) $(SLIBJVMTI) libjvmti.$(SOLIBEXT)) | ||
87 | ldconfig | ||
88 | |||
89 | .SUFFIXES: .c .S .o .lo | ||
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c new file mode 100644 index 000000000000..6461e02ab940 --- /dev/null +++ b/tools/perf/jvmti/jvmti_agent.c | |||
@@ -0,0 +1,465 @@ | |||
1 | /* | ||
2 | * jvmti_agent.c: JVMTI agent interface | ||
3 | * | ||
4 | * Adapted from the Oprofile code in opagent.c: | ||
5 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; either | ||
8 | * version 2.1 of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This library is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this library; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * Copyright 2007 OProfile authors | ||
20 | * Jens Wilke | ||
21 | * Daniel Hansel | ||
22 | * Copyright IBM Corporation 2007 | ||
23 | */ | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> /* for mkdir() */ | ||
26 | #include <stdio.h> | ||
27 | #include <errno.h> | ||
28 | #include <string.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <stdint.h> | ||
31 | #include <limits.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <unistd.h> | ||
34 | #include <time.h> | ||
35 | #include <sys/mman.h> | ||
36 | #include <syscall.h> /* for gettid() */ | ||
37 | #include <err.h> | ||
38 | |||
39 | #include "jvmti_agent.h" | ||
40 | #include "../util/jitdump.h" | ||
41 | |||
42 | #define JIT_LANG "java" | ||
43 | |||
44 | static char jit_path[PATH_MAX]; | ||
45 | static void *marker_addr; | ||
46 | |||
47 | /* | ||
48 | * padding buffer | ||
49 | */ | ||
50 | static const char pad_bytes[7]; | ||
51 | |||
52 | static inline pid_t gettid(void) | ||
53 | { | ||
54 | return (pid_t)syscall(__NR_gettid); | ||
55 | } | ||
56 | |||
57 | static int get_e_machine(struct jitheader *hdr) | ||
58 | { | ||
59 | ssize_t sret; | ||
60 | char id[16]; | ||
61 | int fd, ret = -1; | ||
62 | int m = -1; | ||
63 | struct { | ||
64 | uint16_t e_type; | ||
65 | uint16_t e_machine; | ||
66 | } info; | ||
67 | |||
68 | fd = open("/proc/self/exe", O_RDONLY); | ||
69 | if (fd == -1) | ||
70 | return -1; | ||
71 | |||
72 | sret = read(fd, id, sizeof(id)); | ||
73 | if (sret != sizeof(id)) | ||
74 | goto error; | ||
75 | |||
76 | /* check ELF signature */ | ||
77 | if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') | ||
78 | goto error; | ||
79 | |||
80 | sret = read(fd, &info, sizeof(info)); | ||
81 | if (sret != sizeof(info)) | ||
82 | goto error; | ||
83 | |||
84 | m = info.e_machine; | ||
85 | if (m < 0) | ||
86 | m = 0; /* ELF EM_NONE */ | ||
87 | |||
88 | hdr->elf_mach = m; | ||
89 | ret = 0; | ||
90 | error: | ||
91 | close(fd); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | #define NSEC_PER_SEC 1000000000 | ||
96 | static int perf_clk_id = CLOCK_MONOTONIC; | ||
97 | |||
98 | static inline uint64_t | ||
99 | timespec_to_ns(const struct timespec *ts) | ||
100 | { | ||
101 | return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; | ||
102 | } | ||
103 | |||
104 | static inline uint64_t | ||
105 | perf_get_timestamp(void) | ||
106 | { | ||
107 | struct timespec ts; | ||
108 | int ret; | ||
109 | |||
110 | ret = clock_gettime(perf_clk_id, &ts); | ||
111 | if (ret) | ||
112 | return 0; | ||
113 | |||
114 | return timespec_to_ns(&ts); | ||
115 | } | ||
116 | |||
117 | static int | ||
118 | debug_cache_init(void) | ||
119 | { | ||
120 | char str[32]; | ||
121 | char *base, *p; | ||
122 | struct tm tm; | ||
123 | time_t t; | ||
124 | int ret; | ||
125 | |||
126 | time(&t); | ||
127 | localtime_r(&t, &tm); | ||
128 | |||
129 | base = getenv("JITDUMPDIR"); | ||
130 | if (!base) | ||
131 | base = getenv("HOME"); | ||
132 | if (!base) | ||
133 | base = "."; | ||
134 | |||
135 | strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); | ||
136 | |||
137 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base); | ||
138 | |||
139 | ret = mkdir(jit_path, 0755); | ||
140 | if (ret == -1) { | ||
141 | if (errno != EEXIST) { | ||
142 | warn("jvmti: cannot create jit cache dir %s", jit_path); | ||
143 | return -1; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base); | ||
148 | ret = mkdir(jit_path, 0755); | ||
149 | if (ret == -1) { | ||
150 | if (errno != EEXIST) { | ||
151 | warn("cannot create jit cache dir %s", jit_path); | ||
152 | return -1; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str); | ||
157 | |||
158 | p = mkdtemp(jit_path); | ||
159 | if (p != jit_path) { | ||
160 | warn("cannot create jit cache dir %s", jit_path); | ||
161 | return -1; | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | perf_open_marker_file(int fd) | ||
169 | { | ||
170 | long pgsz; | ||
171 | |||
172 | pgsz = sysconf(_SC_PAGESIZE); | ||
173 | if (pgsz == -1) | ||
174 | return -1; | ||
175 | |||
176 | /* | ||
177 | * we mmap the jitdump to create an MMAP RECORD in perf.data file. | ||
178 | * The mmap is captured either live (perf record running when we mmap) | ||
179 | * or in deferred mode, via /proc/PID/maps | ||
180 | * the MMAP record is used as a marker of a jitdump file for more meta | ||
181 | * data info about the jitted code. Perf report/annotate detect this | ||
182 | * special filename and process the jitdump file. | ||
183 | * | ||
184 | * mapping must be PROT_EXEC to ensure it is captured by perf record | ||
185 | * even when not using -d option | ||
186 | */ | ||
187 | marker_addr = mmap(NULL, pgsz, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); | ||
188 | return (marker_addr == MAP_FAILED) ? -1 : 0; | ||
189 | } | ||
190 | |||
191 | static void | ||
192 | perf_close_marker_file(void) | ||
193 | { | ||
194 | long pgsz; | ||
195 | |||
196 | if (!marker_addr) | ||
197 | return; | ||
198 | |||
199 | pgsz = sysconf(_SC_PAGESIZE); | ||
200 | if (pgsz == -1) | ||
201 | return; | ||
202 | |||
203 | munmap(marker_addr, pgsz); | ||
204 | } | ||
205 | |||
206 | void *jvmti_open(void) | ||
207 | { | ||
208 | int pad_cnt; | ||
209 | char dump_path[PATH_MAX]; | ||
210 | struct jitheader header; | ||
211 | int fd; | ||
212 | FILE *fp; | ||
213 | |||
214 | /* | ||
215 | * check if clockid is supported | ||
216 | */ | ||
217 | if (!perf_get_timestamp()) | ||
218 | warnx("jvmti: kernel does not support %d clock id", perf_clk_id); | ||
219 | |||
220 | memset(&header, 0, sizeof(header)); | ||
221 | |||
222 | debug_cache_init(); | ||
223 | |||
224 | /* | ||
225 | * jitdump file name | ||
226 | */ | ||
227 | snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); | ||
228 | |||
229 | fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); | ||
230 | if (fd == -1) | ||
231 | return NULL; | ||
232 | |||
233 | /* | ||
234 | * create perf.data maker for the jitdump file | ||
235 | */ | ||
236 | if (perf_open_marker_file(fd)) { | ||
237 | warnx("jvmti: failed to create marker file"); | ||
238 | return NULL; | ||
239 | } | ||
240 | |||
241 | fp = fdopen(fd, "w+"); | ||
242 | if (!fp) { | ||
243 | warn("jvmti: cannot create %s", dump_path); | ||
244 | close(fd); | ||
245 | goto error; | ||
246 | } | ||
247 | |||
248 | warnx("jvmti: jitdump in %s", dump_path); | ||
249 | |||
250 | if (get_e_machine(&header)) { | ||
251 | warn("get_e_machine failed\n"); | ||
252 | goto error; | ||
253 | } | ||
254 | |||
255 | header.magic = JITHEADER_MAGIC; | ||
256 | header.version = JITHEADER_VERSION; | ||
257 | header.total_size = sizeof(header); | ||
258 | header.pid = getpid(); | ||
259 | |||
260 | /* calculate amount of padding '\0' */ | ||
261 | pad_cnt = PADDING_8ALIGNED(header.total_size); | ||
262 | header.total_size += pad_cnt; | ||
263 | |||
264 | header.timestamp = perf_get_timestamp(); | ||
265 | |||
266 | if (!fwrite(&header, sizeof(header), 1, fp)) { | ||
267 | warn("jvmti: cannot write dumpfile header"); | ||
268 | goto error; | ||
269 | } | ||
270 | |||
271 | /* write padding '\0' if necessary */ | ||
272 | if (pad_cnt && !fwrite(pad_bytes, pad_cnt, 1, fp)) { | ||
273 | warn("jvmti: cannot write dumpfile header padding"); | ||
274 | goto error; | ||
275 | } | ||
276 | |||
277 | return fp; | ||
278 | error: | ||
279 | fclose(fp); | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | int | ||
284 | jvmti_close(void *agent) | ||
285 | { | ||
286 | struct jr_code_close rec; | ||
287 | FILE *fp = agent; | ||
288 | |||
289 | if (!fp) { | ||
290 | warnx("jvmti: incalid fd in close_agent"); | ||
291 | return -1; | ||
292 | } | ||
293 | |||
294 | rec.p.id = JIT_CODE_CLOSE; | ||
295 | rec.p.total_size = sizeof(rec); | ||
296 | |||
297 | rec.p.timestamp = perf_get_timestamp(); | ||
298 | |||
299 | if (!fwrite(&rec, sizeof(rec), 1, fp)) | ||
300 | return -1; | ||
301 | |||
302 | fclose(fp); | ||
303 | |||
304 | fp = NULL; | ||
305 | |||
306 | perf_close_marker_file(); | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | int | ||
312 | jvmti_write_code(void *agent, char const *sym, | ||
313 | uint64_t vma, void const *code, unsigned int const size) | ||
314 | { | ||
315 | static int code_generation = 1; | ||
316 | struct jr_code_load rec; | ||
317 | size_t sym_len; | ||
318 | size_t padding_count; | ||
319 | FILE *fp = agent; | ||
320 | int ret = -1; | ||
321 | |||
322 | /* don't care about 0 length function, no samples */ | ||
323 | if (size == 0) | ||
324 | return 0; | ||
325 | |||
326 | if (!fp) { | ||
327 | warnx("jvmti: invalid fd in write_native_code"); | ||
328 | return -1; | ||
329 | } | ||
330 | |||
331 | sym_len = strlen(sym) + 1; | ||
332 | |||
333 | rec.p.id = JIT_CODE_LOAD; | ||
334 | rec.p.total_size = sizeof(rec) + sym_len; | ||
335 | padding_count = PADDING_8ALIGNED(rec.p.total_size); | ||
336 | rec.p. total_size += padding_count; | ||
337 | rec.p.timestamp = perf_get_timestamp(); | ||
338 | |||
339 | rec.code_size = size; | ||
340 | rec.vma = vma; | ||
341 | rec.code_addr = vma; | ||
342 | rec.pid = getpid(); | ||
343 | rec.tid = gettid(); | ||
344 | |||
345 | if (code) | ||
346 | rec.p.total_size += size; | ||
347 | |||
348 | /* | ||
349 | * If JVM is multi-threaded, nultiple concurrent calls to agent | ||
350 | * may be possible, so protect file writes | ||
351 | */ | ||
352 | flockfile(fp); | ||
353 | |||
354 | /* | ||
355 | * get code index inside lock to avoid race condition | ||
356 | */ | ||
357 | rec.code_index = code_generation++; | ||
358 | |||
359 | ret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); | ||
360 | fwrite_unlocked(sym, sym_len, 1, fp); | ||
361 | |||
362 | if (padding_count) | ||
363 | fwrite_unlocked(pad_bytes, padding_count, 1, fp); | ||
364 | |||
365 | if (code) | ||
366 | fwrite_unlocked(code, size, 1, fp); | ||
367 | |||
368 | funlockfile(fp); | ||
369 | |||
370 | ret = 0; | ||
371 | |||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | int | ||
376 | jvmti_write_debug_info(void *agent, uint64_t code, const char *file, | ||
377 | jvmti_line_info_t *li, int nr_lines) | ||
378 | { | ||
379 | struct jr_code_debug_info rec; | ||
380 | size_t sret, len, size, flen; | ||
381 | size_t padding_count; | ||
382 | uint64_t addr; | ||
383 | const char *fn = file; | ||
384 | FILE *fp = agent; | ||
385 | int i; | ||
386 | |||
387 | /* | ||
388 | * no entry to write | ||
389 | */ | ||
390 | if (!nr_lines) | ||
391 | return 0; | ||
392 | |||
393 | if (!fp) { | ||
394 | warnx("jvmti: invalid fd in write_debug_info"); | ||
395 | return -1; | ||
396 | } | ||
397 | |||
398 | flen = strlen(file) + 1; | ||
399 | |||
400 | rec.p.id = JIT_CODE_DEBUG_INFO; | ||
401 | size = sizeof(rec); | ||
402 | rec.p.timestamp = perf_get_timestamp(); | ||
403 | rec.code_addr = (uint64_t)(uintptr_t)code; | ||
404 | rec.nr_entry = nr_lines; | ||
405 | |||
406 | /* | ||
407 | * on disk source line info layout: | ||
408 | * uint64_t : addr | ||
409 | * int : line number | ||
410 | * int : column discriminator | ||
411 | * file[] : source file name | ||
412 | * padding : pad to multiple of 8 bytes | ||
413 | */ | ||
414 | size += nr_lines * sizeof(struct debug_entry); | ||
415 | size += flen * nr_lines; | ||
416 | /* | ||
417 | * pad to 8 bytes | ||
418 | */ | ||
419 | padding_count = PADDING_8ALIGNED(size); | ||
420 | |||
421 | rec.p.total_size = size + padding_count; | ||
422 | |||
423 | /* | ||
424 | * If JVM is multi-threaded, nultiple concurrent calls to agent | ||
425 | * may be possible, so protect file writes | ||
426 | */ | ||
427 | flockfile(fp); | ||
428 | |||
429 | sret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); | ||
430 | if (sret != 1) | ||
431 | goto error; | ||
432 | |||
433 | for (i = 0; i < nr_lines; i++) { | ||
434 | |||
435 | addr = (uint64_t)li[i].pc; | ||
436 | len = sizeof(addr); | ||
437 | sret = fwrite_unlocked(&addr, len, 1, fp); | ||
438 | if (sret != 1) | ||
439 | goto error; | ||
440 | |||
441 | len = sizeof(li[0].line_number); | ||
442 | sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); | ||
443 | if (sret != 1) | ||
444 | goto error; | ||
445 | |||
446 | len = sizeof(li[0].discrim); | ||
447 | sret = fwrite_unlocked(&li[i].discrim, len, 1, fp); | ||
448 | if (sret != 1) | ||
449 | goto error; | ||
450 | |||
451 | sret = fwrite_unlocked(fn, flen, 1, fp); | ||
452 | if (sret != 1) | ||
453 | goto error; | ||
454 | } | ||
455 | if (padding_count) | ||
456 | sret = fwrite_unlocked(pad_bytes, padding_count, 1, fp); | ||
457 | if (sret != 1) | ||
458 | goto error; | ||
459 | |||
460 | funlockfile(fp); | ||
461 | return 0; | ||
462 | error: | ||
463 | funlockfile(fp); | ||
464 | return -1; | ||
465 | } | ||
diff --git a/tools/perf/jvmti/jvmti_agent.h b/tools/perf/jvmti/jvmti_agent.h new file mode 100644 index 000000000000..bedf5d0ba9ff --- /dev/null +++ b/tools/perf/jvmti/jvmti_agent.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef __JVMTI_AGENT_H__ | ||
2 | #define __JVMTI_AGENT_H__ | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include <stdint.h> | ||
6 | #include <jvmti.h> | ||
7 | |||
8 | #define __unused __attribute__((unused)) | ||
9 | |||
10 | #if defined(__cplusplus) | ||
11 | extern "C" { | ||
12 | #endif | ||
13 | |||
14 | typedef struct { | ||
15 | unsigned long pc; | ||
16 | int line_number; | ||
17 | int discrim; /* discriminator -- 0 for now */ | ||
18 | } jvmti_line_info_t; | ||
19 | |||
20 | void *jvmti_open(void); | ||
21 | int jvmti_close(void *agent); | ||
22 | int jvmti_write_code(void *agent, char const *symbol_name, | ||
23 | uint64_t vma, void const *code, | ||
24 | const unsigned int code_size); | ||
25 | |||
26 | int jvmti_write_debug_info(void *agent, | ||
27 | uint64_t code, | ||
28 | const char *file, | ||
29 | jvmti_line_info_t *li, | ||
30 | int nr_lines); | ||
31 | |||
32 | #if defined(__cplusplus) | ||
33 | } | ||
34 | |||
35 | #endif | ||
36 | #endif /* __JVMTI_H__ */ | ||
diff --git a/tools/perf/jvmti/libjvmti.c b/tools/perf/jvmti/libjvmti.c new file mode 100644 index 000000000000..ac12e4b91a92 --- /dev/null +++ b/tools/perf/jvmti/libjvmti.c | |||
@@ -0,0 +1,304 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <err.h> | ||
6 | #include <jvmti.h> | ||
7 | #include <jvmticmlr.h> | ||
8 | #include <limits.h> | ||
9 | |||
10 | #include "jvmti_agent.h" | ||
11 | |||
12 | static int has_line_numbers; | ||
13 | void *jvmti_agent; | ||
14 | |||
15 | static jvmtiError | ||
16 | do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, | ||
17 | jvmti_line_info_t *tab, jint *nr) | ||
18 | { | ||
19 | jint i, lines = 0; | ||
20 | jint nr_lines = 0; | ||
21 | jvmtiLineNumberEntry *loc_tab = NULL; | ||
22 | jvmtiError ret; | ||
23 | |||
24 | ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab); | ||
25 | if (ret != JVMTI_ERROR_NONE) | ||
26 | return ret; | ||
27 | |||
28 | for (i = 0; i < nr_lines; i++) { | ||
29 | if (loc_tab[i].start_location < bci) { | ||
30 | tab[lines].pc = (unsigned long)pc; | ||
31 | tab[lines].line_number = loc_tab[i].line_number; | ||
32 | tab[lines].discrim = 0; /* not yet used */ | ||
33 | lines++; | ||
34 | } else { | ||
35 | break; | ||
36 | } | ||
37 | } | ||
38 | (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab); | ||
39 | *nr = lines; | ||
40 | return JVMTI_ERROR_NONE; | ||
41 | } | ||
42 | |||
43 | static jvmtiError | ||
44 | get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines) | ||
45 | { | ||
46 | const jvmtiCompiledMethodLoadRecordHeader *hdr; | ||
47 | jvmtiCompiledMethodLoadInlineRecord *rec; | ||
48 | jvmtiLineNumberEntry *lne = NULL; | ||
49 | PCStackInfo *c; | ||
50 | jint nr, ret; | ||
51 | int nr_total = 0; | ||
52 | int i, lines_total = 0; | ||
53 | |||
54 | if (!(tab && nr_lines)) | ||
55 | return JVMTI_ERROR_NULL_POINTER; | ||
56 | |||
57 | /* | ||
58 | * Phase 1 -- get the number of lines necessary | ||
59 | */ | ||
60 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
61 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
62 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
63 | for (i = 0; i < rec->numpcs; i++) { | ||
64 | c = rec->pcinfo + i; | ||
65 | nr = 0; | ||
66 | /* | ||
67 | * unfortunately, need a tab to get the number of lines! | ||
68 | */ | ||
69 | ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne); | ||
70 | if (ret == JVMTI_ERROR_NONE) { | ||
71 | /* free what was allocated for nothing */ | ||
72 | (*jvmti)->Deallocate(jvmti, (unsigned char *)lne); | ||
73 | nr_total += (int)nr; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | if (nr_total == 0) | ||
80 | return JVMTI_ERROR_NOT_FOUND; | ||
81 | |||
82 | /* | ||
83 | * Phase 2 -- allocate big enough line table | ||
84 | */ | ||
85 | *tab = malloc(nr_total * sizeof(**tab)); | ||
86 | if (!*tab) | ||
87 | return JVMTI_ERROR_OUT_OF_MEMORY; | ||
88 | |||
89 | for (hdr = compile_info; hdr != NULL; hdr = hdr->next) { | ||
90 | if (hdr->kind == JVMTI_CMLR_INLINE_INFO) { | ||
91 | rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr; | ||
92 | for (i = 0; i < rec->numpcs; i++) { | ||
93 | c = rec->pcinfo + i; | ||
94 | nr = 0; | ||
95 | ret = do_get_line_numbers(jvmti, c->pc, | ||
96 | c->methods[0], | ||
97 | c->bcis[0], | ||
98 | *tab + lines_total, | ||
99 | &nr); | ||
100 | if (ret == JVMTI_ERROR_NONE) | ||
101 | lines_total += nr; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | *nr_lines = lines_total; | ||
106 | return JVMTI_ERROR_NONE; | ||
107 | } | ||
108 | |||
109 | static void JNICALL | ||
110 | compiled_method_load_cb(jvmtiEnv *jvmti, | ||
111 | jmethodID method, | ||
112 | jint code_size, | ||
113 | void const *code_addr, | ||
114 | jint map_length, | ||
115 | jvmtiAddrLocationMap const *map, | ||
116 | const void *compile_info) | ||
117 | { | ||
118 | jvmti_line_info_t *line_tab = NULL; | ||
119 | jclass decl_class; | ||
120 | char *class_sign = NULL; | ||
121 | char *func_name = NULL; | ||
122 | char *func_sign = NULL; | ||
123 | char *file_name= NULL; | ||
124 | char fn[PATH_MAX]; | ||
125 | uint64_t addr = (uint64_t)(uintptr_t)code_addr; | ||
126 | jvmtiError ret; | ||
127 | int nr_lines = 0; /* in line_tab[] */ | ||
128 | size_t len; | ||
129 | |||
130 | ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, | ||
131 | &decl_class); | ||
132 | if (ret != JVMTI_ERROR_NONE) { | ||
133 | warnx("jvmti: cannot get declaring class"); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | if (has_line_numbers && map && map_length) { | ||
138 | ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines); | ||
139 | if (ret != JVMTI_ERROR_NONE) { | ||
140 | warnx("jvmti: cannot get line table for method"); | ||
141 | nr_lines = 0; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); | ||
146 | if (ret != JVMTI_ERROR_NONE) { | ||
147 | warnx("jvmti: cannot get source filename ret=%d", ret); | ||
148 | goto error; | ||
149 | } | ||
150 | |||
151 | ret = (*jvmti)->GetClassSignature(jvmti, decl_class, | ||
152 | &class_sign, NULL); | ||
153 | if (ret != JVMTI_ERROR_NONE) { | ||
154 | warnx("jvmti: getclassignature failed"); | ||
155 | goto error; | ||
156 | } | ||
157 | |||
158 | ret = (*jvmti)->GetMethodName(jvmti, method, &func_name, | ||
159 | &func_sign, NULL); | ||
160 | if (ret != JVMTI_ERROR_NONE) { | ||
161 | warnx("jvmti: failed getmethodname"); | ||
162 | goto error; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Assume path name is class hierarchy, this is a common practice with Java programs | ||
167 | */ | ||
168 | if (*class_sign == 'L') { | ||
169 | int j, i = 0; | ||
170 | char *p = strrchr(class_sign, '/'); | ||
171 | if (p) { | ||
172 | /* drop the 'L' prefix and copy up to the final '/' */ | ||
173 | for (i = 0; i < (p - class_sign); i++) | ||
174 | fn[i] = class_sign[i+1]; | ||
175 | } | ||
176 | /* | ||
177 | * append file name, we use loops and not string ops to avoid modifying | ||
178 | * class_sign which is used later for the symbol name | ||
179 | */ | ||
180 | for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++) | ||
181 | fn[i] = file_name[j]; | ||
182 | fn[i] = '\0'; | ||
183 | } else { | ||
184 | /* fallback case */ | ||
185 | strcpy(fn, file_name); | ||
186 | } | ||
187 | /* | ||
188 | * write source line info record if we have it | ||
189 | */ | ||
190 | if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines)) | ||
191 | warnx("jvmti: write_debug_info() failed"); | ||
192 | |||
193 | len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; | ||
194 | { | ||
195 | char str[len]; | ||
196 | snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); | ||
197 | |||
198 | if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) | ||
199 | warnx("jvmti: write_code() failed"); | ||
200 | } | ||
201 | error: | ||
202 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); | ||
203 | (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); | ||
204 | (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); | ||
205 | (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name); | ||
206 | free(line_tab); | ||
207 | } | ||
208 | |||
209 | static void JNICALL | ||
210 | code_generated_cb(jvmtiEnv *jvmti, | ||
211 | char const *name, | ||
212 | void const *code_addr, | ||
213 | jint code_size) | ||
214 | { | ||
215 | uint64_t addr = (uint64_t)(unsigned long)code_addr; | ||
216 | int ret; | ||
217 | |||
218 | ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size); | ||
219 | if (ret) | ||
220 | warnx("jvmti: write_code() failed for code_generated"); | ||
221 | } | ||
222 | |||
223 | JNIEXPORT jint JNICALL | ||
224 | Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused) | ||
225 | { | ||
226 | jvmtiEventCallbacks cb; | ||
227 | jvmtiCapabilities caps1; | ||
228 | jvmtiJlocationFormat format; | ||
229 | jvmtiEnv *jvmti = NULL; | ||
230 | jint ret; | ||
231 | |||
232 | jvmti_agent = jvmti_open(); | ||
233 | if (!jvmti_agent) { | ||
234 | warnx("jvmti: open_agent failed"); | ||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * Request a JVMTI interface version 1 environment | ||
240 | */ | ||
241 | ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); | ||
242 | if (ret != JNI_OK) { | ||
243 | warnx("jvmti: jvmti version 1 not supported"); | ||
244 | return -1; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * acquire method_load capability, we require it | ||
249 | * request line numbers (optional) | ||
250 | */ | ||
251 | memset(&caps1, 0, sizeof(caps1)); | ||
252 | caps1.can_generate_compiled_method_load_events = 1; | ||
253 | |||
254 | ret = (*jvmti)->AddCapabilities(jvmti, &caps1); | ||
255 | if (ret != JVMTI_ERROR_NONE) { | ||
256 | warnx("jvmti: acquire compiled_method capability failed"); | ||
257 | return -1; | ||
258 | } | ||
259 | ret = (*jvmti)->GetJLocationFormat(jvmti, &format); | ||
260 | if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) { | ||
261 | memset(&caps1, 0, sizeof(caps1)); | ||
262 | caps1.can_get_line_numbers = 1; | ||
263 | caps1.can_get_source_file_name = 1; | ||
264 | ret = (*jvmti)->AddCapabilities(jvmti, &caps1); | ||
265 | if (ret == JVMTI_ERROR_NONE) | ||
266 | has_line_numbers = 1; | ||
267 | } | ||
268 | |||
269 | memset(&cb, 0, sizeof(cb)); | ||
270 | |||
271 | cb.CompiledMethodLoad = compiled_method_load_cb; | ||
272 | cb.DynamicCodeGenerated = code_generated_cb; | ||
273 | |||
274 | ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb)); | ||
275 | if (ret != JVMTI_ERROR_NONE) { | ||
276 | warnx("jvmti: cannot set event callbacks"); | ||
277 | return -1; | ||
278 | } | ||
279 | |||
280 | ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, | ||
281 | JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); | ||
282 | if (ret != JVMTI_ERROR_NONE) { | ||
283 | warnx("jvmti: setnotification failed for method_load"); | ||
284 | return -1; | ||
285 | } | ||
286 | |||
287 | ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, | ||
288 | JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); | ||
289 | if (ret != JVMTI_ERROR_NONE) { | ||
290 | warnx("jvmti: setnotification failed on code_generated"); | ||
291 | return -1; | ||
292 | } | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | JNIEXPORT void JNICALL | ||
297 | Agent_OnUnload(JavaVM *jvm __unused) | ||
298 | { | ||
299 | int ret; | ||
300 | |||
301 | ret = jvmti_close(jvmti_agent); | ||
302 | if (ret) | ||
303 | errx(1, "Error: op_close_agent()"); | ||
304 | } | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index a929618b8eb6..aaee0a782747 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -454,11 +454,12 @@ static void handle_internal_command(int argc, const char **argv) | |||
454 | 454 | ||
455 | static void execv_dashed_external(const char **argv) | 455 | static void execv_dashed_external(const char **argv) |
456 | { | 456 | { |
457 | struct strbuf cmd = STRBUF_INIT; | 457 | char *cmd; |
458 | const char *tmp; | 458 | const char *tmp; |
459 | int status; | 459 | int status; |
460 | 460 | ||
461 | strbuf_addf(&cmd, "perf-%s", argv[0]); | 461 | if (asprintf(&cmd, "perf-%s", argv[0]) < 0) |
462 | goto do_die; | ||
462 | 463 | ||
463 | /* | 464 | /* |
464 | * argv[0] must be the perf command, but the argv array | 465 | * argv[0] must be the perf command, but the argv array |
@@ -467,7 +468,7 @@ static void execv_dashed_external(const char **argv) | |||
467 | * restore it on error. | 468 | * restore it on error. |
468 | */ | 469 | */ |
469 | tmp = argv[0]; | 470 | tmp = argv[0]; |
470 | argv[0] = cmd.buf; | 471 | argv[0] = cmd; |
471 | 472 | ||
472 | /* | 473 | /* |
473 | * if we fail because the command is not found, it is | 474 | * if we fail because the command is not found, it is |
@@ -475,15 +476,16 @@ static void execv_dashed_external(const char **argv) | |||
475 | */ | 476 | */ |
476 | status = run_command_v_opt(argv, 0); | 477 | status = run_command_v_opt(argv, 0); |
477 | if (status != -ERR_RUN_COMMAND_EXEC) { | 478 | if (status != -ERR_RUN_COMMAND_EXEC) { |
478 | if (IS_RUN_COMMAND_ERR(status)) | 479 | if (IS_RUN_COMMAND_ERR(status)) { |
480 | do_die: | ||
479 | die("unable to run '%s'", argv[0]); | 481 | die("unable to run '%s'", argv[0]); |
482 | } | ||
480 | exit(-status); | 483 | exit(-status); |
481 | } | 484 | } |
482 | errno = ENOENT; /* as if we called execvp */ | 485 | errno = ENOENT; /* as if we called execvp */ |
483 | 486 | ||
484 | argv[0] = tmp; | 487 | argv[0] = tmp; |
485 | 488 | zfree(&cmd); | |
486 | strbuf_release(&cmd); | ||
487 | } | 489 | } |
488 | 490 | ||
489 | static int run_argv(int *argcp, const char ***argv) | 491 | static int run_argv(int *argcp, const char ***argv) |
@@ -546,6 +548,8 @@ int main(int argc, const char **argv) | |||
546 | 548 | ||
547 | srandom(time(NULL)); | 549 | srandom(time(NULL)); |
548 | 550 | ||
551 | perf_config(perf_default_config, NULL); | ||
552 | |||
549 | /* get debugfs/tracefs mount point from /proc/mounts */ | 553 | /* get debugfs/tracefs mount point from /proc/mounts */ |
550 | tracing_path_mount(); | 554 | tracing_path_mount(); |
551 | 555 | ||
@@ -613,6 +617,8 @@ int main(int argc, const char **argv) | |||
613 | */ | 617 | */ |
614 | pthread__block_sigwinch(); | 618 | pthread__block_sigwinch(); |
615 | 619 | ||
620 | perf_debug_setup(); | ||
621 | |||
616 | while (1) { | 622 | while (1) { |
617 | static int done_help; | 623 | static int done_help; |
618 | int was_alias = run_argv(&argc, &argv); | 624 | int was_alias = run_argv(&argc, &argv); |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 90129accffbe..5381a01c0610 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -58,6 +58,8 @@ struct record_opts { | |||
58 | bool full_auxtrace; | 58 | bool full_auxtrace; |
59 | bool auxtrace_snapshot_mode; | 59 | bool auxtrace_snapshot_mode; |
60 | bool record_switch_events; | 60 | bool record_switch_events; |
61 | bool all_kernel; | ||
62 | bool all_user; | ||
61 | unsigned int freq; | 63 | unsigned int freq; |
62 | unsigned int mmap_pages; | 64 | unsigned int mmap_pages; |
63 | unsigned int auxtrace_mmap_pages; | 65 | unsigned int auxtrace_mmap_pages; |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 15c8400240fd..1d95009592eb 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py | |||
@@ -71,7 +71,10 @@ try: | |||
71 | except: | 71 | except: |
72 | if not audit_package_warned: | 72 | if not audit_package_warned: |
73 | audit_package_warned = True | 73 | audit_package_warned = True |
74 | print "Install the audit-libs-python package to get syscall names" | 74 | print "Install the audit-libs-python package to get syscall names.\n" \ |
75 | "For example:\n # apt-get install python-audit (Ubuntu)" \ | ||
76 | "\n # yum install audit-libs-python (Fedora)" \ | ||
77 | "\n etc.\n" | ||
75 | 78 | ||
76 | def syscall_name(id): | 79 | def syscall_name(id): |
77 | try: | 80 | try: |
diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore index bf016c439fbd..8cc30e731c73 100644 --- a/tools/perf/tests/.gitignore +++ b/tools/perf/tests/.gitignore | |||
@@ -1,3 +1,4 @@ | |||
1 | llvm-src-base.c | 1 | llvm-src-base.c |
2 | llvm-src-kbuild.c | 2 | llvm-src-kbuild.c |
3 | llvm-src-prologue.c | 3 | llvm-src-prologue.c |
4 | llvm-src-relocation.c | ||
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 614899b88b37..1ba628ed049a 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -31,7 +31,7 @@ perf-y += sample-parsing.o | |||
31 | perf-y += parse-no-sample-id-all.o | 31 | perf-y += parse-no-sample-id-all.o |
32 | perf-y += kmod-path.o | 32 | perf-y += kmod-path.o |
33 | perf-y += thread-map.o | 33 | perf-y += thread-map.o |
34 | perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o | 34 | perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o llvm-src-relocation.o |
35 | perf-y += bpf.o | 35 | perf-y += bpf.o |
36 | perf-y += topology.o | 36 | perf-y += topology.o |
37 | perf-y += cpumap.o | 37 | perf-y += cpumap.o |
@@ -59,6 +59,13 @@ $(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c tests/Build | |||
59 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ | 59 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ |
60 | $(Q)echo ';' >> $@ | 60 | $(Q)echo ';' >> $@ |
61 | 61 | ||
62 | $(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/Build | ||
63 | $(call rule_mkdir) | ||
64 | $(Q)echo '#include <tests/llvm.h>' > $@ | ||
65 | $(Q)echo 'const char test_llvm__bpf_test_relocation[] =' >> $@ | ||
66 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ | ||
67 | $(Q)echo ';' >> $@ | ||
68 | |||
62 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) | 69 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) |
63 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o | 70 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o |
64 | endif | 71 | endif |
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index fb80c9eb6a95..e7664fe3bd33 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c | |||
@@ -29,14 +29,59 @@ | |||
29 | 29 | ||
30 | static int fd1; | 30 | static int fd1; |
31 | static int fd2; | 31 | static int fd2; |
32 | static int fd3; | ||
32 | static int overflows; | 33 | static int overflows; |
34 | static int overflows_2; | ||
35 | |||
36 | volatile long the_var; | ||
37 | |||
38 | |||
39 | /* | ||
40 | * Use ASM to ensure watchpoint and breakpoint can be triggered | ||
41 | * at one instruction. | ||
42 | */ | ||
43 | #if defined (__x86_64__) | ||
44 | extern void __test_function(volatile long *ptr); | ||
45 | asm ( | ||
46 | ".globl __test_function\n" | ||
47 | "__test_function:\n" | ||
48 | "incq (%rdi)\n" | ||
49 | "ret\n"); | ||
50 | #elif defined (__aarch64__) | ||
51 | extern void __test_function(volatile long *ptr); | ||
52 | asm ( | ||
53 | ".globl __test_function\n" | ||
54 | "__test_function:\n" | ||
55 | "str x30, [x0]\n" | ||
56 | "ret\n"); | ||
57 | |||
58 | #else | ||
59 | static void __test_function(volatile long *ptr) | ||
60 | { | ||
61 | *ptr = 0x1234; | ||
62 | } | ||
63 | #endif | ||
33 | 64 | ||
34 | __attribute__ ((noinline)) | 65 | __attribute__ ((noinline)) |
35 | static int test_function(void) | 66 | static int test_function(void) |
36 | { | 67 | { |
68 | __test_function(&the_var); | ||
69 | the_var++; | ||
37 | return time(NULL); | 70 | return time(NULL); |
38 | } | 71 | } |
39 | 72 | ||
73 | static void sig_handler_2(int signum __maybe_unused, | ||
74 | siginfo_t *oh __maybe_unused, | ||
75 | void *uc __maybe_unused) | ||
76 | { | ||
77 | overflows_2++; | ||
78 | if (overflows_2 > 10) { | ||
79 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | ||
80 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | ||
81 | ioctl(fd3, PERF_EVENT_IOC_DISABLE, 0); | ||
82 | } | ||
83 | } | ||
84 | |||
40 | static void sig_handler(int signum __maybe_unused, | 85 | static void sig_handler(int signum __maybe_unused, |
41 | siginfo_t *oh __maybe_unused, | 86 | siginfo_t *oh __maybe_unused, |
42 | void *uc __maybe_unused) | 87 | void *uc __maybe_unused) |
@@ -54,10 +99,11 @@ static void sig_handler(int signum __maybe_unused, | |||
54 | */ | 99 | */ |
55 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | 100 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); |
56 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | 101 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); |
102 | ioctl(fd3, PERF_EVENT_IOC_DISABLE, 0); | ||
57 | } | 103 | } |
58 | } | 104 | } |
59 | 105 | ||
60 | static int bp_event(void *fn, int setup_signal) | 106 | static int __event(bool is_x, void *addr, int sig) |
61 | { | 107 | { |
62 | struct perf_event_attr pe; | 108 | struct perf_event_attr pe; |
63 | int fd; | 109 | int fd; |
@@ -67,8 +113,8 @@ static int bp_event(void *fn, int setup_signal) | |||
67 | pe.size = sizeof(struct perf_event_attr); | 113 | pe.size = sizeof(struct perf_event_attr); |
68 | 114 | ||
69 | pe.config = 0; | 115 | pe.config = 0; |
70 | pe.bp_type = HW_BREAKPOINT_X; | 116 | pe.bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; |
71 | pe.bp_addr = (unsigned long) fn; | 117 | pe.bp_addr = (unsigned long) addr; |
72 | pe.bp_len = sizeof(long); | 118 | pe.bp_len = sizeof(long); |
73 | 119 | ||
74 | pe.sample_period = 1; | 120 | pe.sample_period = 1; |
@@ -86,17 +132,25 @@ static int bp_event(void *fn, int setup_signal) | |||
86 | return TEST_FAIL; | 132 | return TEST_FAIL; |
87 | } | 133 | } |
88 | 134 | ||
89 | if (setup_signal) { | 135 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); |
90 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); | 136 | fcntl(fd, F_SETSIG, sig); |
91 | fcntl(fd, F_SETSIG, SIGIO); | 137 | fcntl(fd, F_SETOWN, getpid()); |
92 | fcntl(fd, F_SETOWN, getpid()); | ||
93 | } | ||
94 | 138 | ||
95 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); | 139 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); |
96 | 140 | ||
97 | return fd; | 141 | return fd; |
98 | } | 142 | } |
99 | 143 | ||
144 | static int bp_event(void *addr, int sig) | ||
145 | { | ||
146 | return __event(true, addr, sig); | ||
147 | } | ||
148 | |||
149 | static int wp_event(void *addr, int sig) | ||
150 | { | ||
151 | return __event(false, addr, sig); | ||
152 | } | ||
153 | |||
100 | static long long bp_count(int fd) | 154 | static long long bp_count(int fd) |
101 | { | 155 | { |
102 | long long count; | 156 | long long count; |
@@ -114,7 +168,7 @@ static long long bp_count(int fd) | |||
114 | int test__bp_signal(int subtest __maybe_unused) | 168 | int test__bp_signal(int subtest __maybe_unused) |
115 | { | 169 | { |
116 | struct sigaction sa; | 170 | struct sigaction sa; |
117 | long long count1, count2; | 171 | long long count1, count2, count3; |
118 | 172 | ||
119 | /* setup SIGIO signal handler */ | 173 | /* setup SIGIO signal handler */ |
120 | memset(&sa, 0, sizeof(struct sigaction)); | 174 | memset(&sa, 0, sizeof(struct sigaction)); |
@@ -126,21 +180,52 @@ int test__bp_signal(int subtest __maybe_unused) | |||
126 | return TEST_FAIL; | 180 | return TEST_FAIL; |
127 | } | 181 | } |
128 | 182 | ||
183 | sa.sa_sigaction = (void *) sig_handler_2; | ||
184 | if (sigaction(SIGUSR1, &sa, NULL) < 0) { | ||
185 | pr_debug("failed setting up signal handler 2\n"); | ||
186 | return TEST_FAIL; | ||
187 | } | ||
188 | |||
129 | /* | 189 | /* |
130 | * We create following events: | 190 | * We create following events: |
131 | * | 191 | * |
132 | * fd1 - breakpoint event on test_function with SIGIO | 192 | * fd1 - breakpoint event on __test_function with SIGIO |
133 | * signal configured. We should get signal | 193 | * signal configured. We should get signal |
134 | * notification each time the breakpoint is hit | 194 | * notification each time the breakpoint is hit |
135 | * | 195 | * |
136 | * fd2 - breakpoint event on sig_handler without SIGIO | 196 | * fd2 - breakpoint event on sig_handler with SIGUSR1 |
197 | * configured. We should get SIGUSR1 each time when | ||
198 | * breakpoint is hit | ||
199 | * | ||
200 | * fd3 - watchpoint event on __test_function with SIGIO | ||
137 | * configured. | 201 | * configured. |
138 | * | 202 | * |
139 | * Following processing should happen: | 203 | * Following processing should happen: |
140 | * - execute test_function | 204 | * Exec: Action: Result: |
141 | * - fd1 event breakpoint hit -> count1 == 1 | 205 | * incq (%rdi) - fd1 event breakpoint hit -> count1 == 1 |
142 | * - SIGIO is delivered -> overflows == 1 | 206 | * - SIGIO is delivered |
143 | * - fd2 event breakpoint hit -> count2 == 1 | 207 | * sig_handler - fd2 event breakpoint hit -> count2 == 1 |
208 | * - SIGUSR1 is delivered | ||
209 | * sig_handler_2 -> overflows_2 == 1 (nested signal) | ||
210 | * sys_rt_sigreturn - return from sig_handler_2 | ||
211 | * overflows++ -> overflows = 1 | ||
212 | * sys_rt_sigreturn - return from sig_handler | ||
213 | * incq (%rdi) - fd3 event watchpoint hit -> count3 == 1 (wp and bp in one insn) | ||
214 | * - SIGIO is delivered | ||
215 | * sig_handler - fd2 event breakpoint hit -> count2 == 2 | ||
216 | * - SIGUSR1 is delivered | ||
217 | * sig_handler_2 -> overflows_2 == 2 (nested signal) | ||
218 | * sys_rt_sigreturn - return from sig_handler_2 | ||
219 | * overflows++ -> overflows = 2 | ||
220 | * sys_rt_sigreturn - return from sig_handler | ||
221 | * the_var++ - fd3 event watchpoint hit -> count3 == 2 (standalone watchpoint) | ||
222 | * - SIGIO is delivered | ||
223 | * sig_handler - fd2 event breakpoint hit -> count2 == 3 | ||
224 | * - SIGUSR1 is delivered | ||
225 | * sig_handler_2 -> overflows_2 == 3 (nested signal) | ||
226 | * sys_rt_sigreturn - return from sig_handler_2 | ||
227 | * overflows++ -> overflows == 3 | ||
228 | * sys_rt_sigreturn - return from sig_handler | ||
144 | * | 229 | * |
145 | * The test case check following error conditions: | 230 | * The test case check following error conditions: |
146 | * - we get stuck in signal handler because of debug | 231 | * - we get stuck in signal handler because of debug |
@@ -152,11 +237,13 @@ int test__bp_signal(int subtest __maybe_unused) | |||
152 | * | 237 | * |
153 | */ | 238 | */ |
154 | 239 | ||
155 | fd1 = bp_event(test_function, 1); | 240 | fd1 = bp_event(__test_function, SIGIO); |
156 | fd2 = bp_event(sig_handler, 0); | 241 | fd2 = bp_event(sig_handler, SIGUSR1); |
242 | fd3 = wp_event((void *)&the_var, SIGIO); | ||
157 | 243 | ||
158 | ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0); | 244 | ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0); |
159 | ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0); | 245 | ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0); |
246 | ioctl(fd3, PERF_EVENT_IOC_ENABLE, 0); | ||
160 | 247 | ||
161 | /* | 248 | /* |
162 | * Kick off the test by trigering 'fd1' | 249 | * Kick off the test by trigering 'fd1' |
@@ -166,15 +253,18 @@ int test__bp_signal(int subtest __maybe_unused) | |||
166 | 253 | ||
167 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | 254 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); |
168 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | 255 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); |
256 | ioctl(fd3, PERF_EVENT_IOC_DISABLE, 0); | ||
169 | 257 | ||
170 | count1 = bp_count(fd1); | 258 | count1 = bp_count(fd1); |
171 | count2 = bp_count(fd2); | 259 | count2 = bp_count(fd2); |
260 | count3 = bp_count(fd3); | ||
172 | 261 | ||
173 | close(fd1); | 262 | close(fd1); |
174 | close(fd2); | 263 | close(fd2); |
264 | close(fd3); | ||
175 | 265 | ||
176 | pr_debug("count1 %lld, count2 %lld, overflow %d\n", | 266 | pr_debug("count1 %lld, count2 %lld, count3 %lld, overflow %d, overflows_2 %d\n", |
177 | count1, count2, overflows); | 267 | count1, count2, count3, overflows, overflows_2); |
178 | 268 | ||
179 | if (count1 != 1) { | 269 | if (count1 != 1) { |
180 | if (count1 == 11) | 270 | if (count1 == 11) |
@@ -183,12 +273,18 @@ int test__bp_signal(int subtest __maybe_unused) | |||
183 | pr_debug("failed: wrong count for bp1%lld\n", count1); | 273 | pr_debug("failed: wrong count for bp1%lld\n", count1); |
184 | } | 274 | } |
185 | 275 | ||
186 | if (overflows != 1) | 276 | if (overflows != 3) |
187 | pr_debug("failed: wrong overflow hit\n"); | 277 | pr_debug("failed: wrong overflow hit\n"); |
188 | 278 | ||
189 | if (count2 != 1) | 279 | if (overflows_2 != 3) |
280 | pr_debug("failed: wrong overflow_2 hit\n"); | ||
281 | |||
282 | if (count2 != 3) | ||
190 | pr_debug("failed: wrong count for bp2\n"); | 283 | pr_debug("failed: wrong count for bp2\n"); |
191 | 284 | ||
192 | return count1 == 1 && overflows == 1 && count2 == 1 ? | 285 | if (count3 != 2) |
286 | pr_debug("failed: wrong count for bp3\n"); | ||
287 | |||
288 | return count1 == 1 && overflows == 3 && count2 == 3 && overflows_2 == 3 && count3 == 2 ? | ||
193 | TEST_OK : TEST_FAIL; | 289 | TEST_OK : TEST_FAIL; |
194 | } | 290 | } |
diff --git a/tools/perf/tests/bpf-script-test-relocation.c b/tools/perf/tests/bpf-script-test-relocation.c new file mode 100644 index 000000000000..93af77421816 --- /dev/null +++ b/tools/perf/tests/bpf-script-test-relocation.c | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * bpf-script-test-relocation.c | ||
3 | * Test BPF loader checking relocation | ||
4 | */ | ||
5 | #ifndef LINUX_VERSION_CODE | ||
6 | # error Need LINUX_VERSION_CODE | ||
7 | # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' | ||
8 | #endif | ||
9 | #define BPF_ANY 0 | ||
10 | #define BPF_MAP_TYPE_ARRAY 2 | ||
11 | #define BPF_FUNC_map_lookup_elem 1 | ||
12 | #define BPF_FUNC_map_update_elem 2 | ||
13 | |||
14 | static void *(*bpf_map_lookup_elem)(void *map, void *key) = | ||
15 | (void *) BPF_FUNC_map_lookup_elem; | ||
16 | static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) = | ||
17 | (void *) BPF_FUNC_map_update_elem; | ||
18 | |||
19 | struct bpf_map_def { | ||
20 | unsigned int type; | ||
21 | unsigned int key_size; | ||
22 | unsigned int value_size; | ||
23 | unsigned int max_entries; | ||
24 | }; | ||
25 | |||
26 | #define SEC(NAME) __attribute__((section(NAME), used)) | ||
27 | struct bpf_map_def SEC("maps") my_table = { | ||
28 | .type = BPF_MAP_TYPE_ARRAY, | ||
29 | .key_size = sizeof(int), | ||
30 | .value_size = sizeof(int), | ||
31 | .max_entries = 1, | ||
32 | }; | ||
33 | |||
34 | int this_is_a_global_val; | ||
35 | |||
36 | SEC("func=sys_write") | ||
37 | int bpf_func__sys_write(void *ctx) | ||
38 | { | ||
39 | int key = 0; | ||
40 | int value = 0; | ||
41 | |||
42 | /* | ||
43 | * Incorrect relocation. Should not allow this program be | ||
44 | * loaded into kernel. | ||
45 | */ | ||
46 | bpf_map_update_elem(&this_is_a_global_val, &key, &value, 0); | ||
47 | return 0; | ||
48 | } | ||
49 | char _license[] SEC("license") = "GPL"; | ||
50 | int _version SEC("version") = LINUX_VERSION_CODE; | ||
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 33689a0cf821..199501c71e27 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c | |||
@@ -1,7 +1,11 @@ | |||
1 | #include <stdio.h> | 1 | #include <stdio.h> |
2 | #include <sys/epoll.h> | 2 | #include <sys/epoll.h> |
3 | #include <util/util.h> | ||
3 | #include <util/bpf-loader.h> | 4 | #include <util/bpf-loader.h> |
4 | #include <util/evlist.h> | 5 | #include <util/evlist.h> |
6 | #include <linux/bpf.h> | ||
7 | #include <linux/filter.h> | ||
8 | #include <bpf/bpf.h> | ||
5 | #include "tests.h" | 9 | #include "tests.h" |
6 | #include "llvm.h" | 10 | #include "llvm.h" |
7 | #include "debug.h" | 11 | #include "debug.h" |
@@ -71,6 +75,15 @@ static struct { | |||
71 | (NR_ITERS + 1) / 4, | 75 | (NR_ITERS + 1) / 4, |
72 | }, | 76 | }, |
73 | #endif | 77 | #endif |
78 | { | ||
79 | LLVM_TESTCASE_BPF_RELOCATION, | ||
80 | "Test BPF relocation checker", | ||
81 | "[bpf_relocation_test]", | ||
82 | "fix 'perf test LLVM' first", | ||
83 | "libbpf error when dealing with relocation", | ||
84 | NULL, | ||
85 | 0, | ||
86 | }, | ||
74 | }; | 87 | }; |
75 | 88 | ||
76 | static int do_test(struct bpf_object *obj, int (*func)(void), | 89 | static int do_test(struct bpf_object *obj, int (*func)(void), |
@@ -99,7 +112,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void), | |||
99 | parse_evlist.error = &parse_error; | 112 | parse_evlist.error = &parse_error; |
100 | INIT_LIST_HEAD(&parse_evlist.list); | 113 | INIT_LIST_HEAD(&parse_evlist.list); |
101 | 114 | ||
102 | err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj); | 115 | err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj, NULL); |
103 | if (err || list_empty(&parse_evlist.list)) { | 116 | if (err || list_empty(&parse_evlist.list)) { |
104 | pr_debug("Failed to add events selected by BPF\n"); | 117 | pr_debug("Failed to add events selected by BPF\n"); |
105 | return TEST_FAIL; | 118 | return TEST_FAIL; |
@@ -190,7 +203,7 @@ static int __test__bpf(int idx) | |||
190 | 203 | ||
191 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | 204 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, |
192 | bpf_testcase_table[idx].prog_id, | 205 | bpf_testcase_table[idx].prog_id, |
193 | true); | 206 | true, NULL); |
194 | if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { | 207 | if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { |
195 | pr_debug("Unable to get BPF object, %s\n", | 208 | pr_debug("Unable to get BPF object, %s\n", |
196 | bpf_testcase_table[idx].msg_compile_fail); | 209 | bpf_testcase_table[idx].msg_compile_fail); |
@@ -202,14 +215,21 @@ static int __test__bpf(int idx) | |||
202 | 215 | ||
203 | obj = prepare_bpf(obj_buf, obj_buf_sz, | 216 | obj = prepare_bpf(obj_buf, obj_buf_sz, |
204 | bpf_testcase_table[idx].name); | 217 | bpf_testcase_table[idx].name); |
205 | if (!obj) { | 218 | if ((!!bpf_testcase_table[idx].target_func) != (!!obj)) { |
219 | if (!obj) | ||
220 | pr_debug("Fail to load BPF object: %s\n", | ||
221 | bpf_testcase_table[idx].msg_load_fail); | ||
222 | else | ||
223 | pr_debug("Success unexpectedly: %s\n", | ||
224 | bpf_testcase_table[idx].msg_load_fail); | ||
206 | ret = TEST_FAIL; | 225 | ret = TEST_FAIL; |
207 | goto out; | 226 | goto out; |
208 | } | 227 | } |
209 | 228 | ||
210 | ret = do_test(obj, | 229 | if (obj) |
211 | bpf_testcase_table[idx].target_func, | 230 | ret = do_test(obj, |
212 | bpf_testcase_table[idx].expect_result); | 231 | bpf_testcase_table[idx].target_func, |
232 | bpf_testcase_table[idx].expect_result); | ||
213 | out: | 233 | out: |
214 | bpf__clear(); | 234 | bpf__clear(); |
215 | return ret; | 235 | return ret; |
@@ -227,6 +247,36 @@ const char *test__bpf_subtest_get_desc(int i) | |||
227 | return bpf_testcase_table[i].desc; | 247 | return bpf_testcase_table[i].desc; |
228 | } | 248 | } |
229 | 249 | ||
250 | static int check_env(void) | ||
251 | { | ||
252 | int err; | ||
253 | unsigned int kver_int; | ||
254 | char license[] = "GPL"; | ||
255 | |||
256 | struct bpf_insn insns[] = { | ||
257 | BPF_MOV64_IMM(BPF_REG_0, 1), | ||
258 | BPF_EXIT_INSN(), | ||
259 | }; | ||
260 | |||
261 | err = fetch_kernel_version(&kver_int, NULL, 0); | ||
262 | if (err) { | ||
263 | pr_debug("Unable to get kernel version\n"); | ||
264 | return err; | ||
265 | } | ||
266 | |||
267 | err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, | ||
268 | sizeof(insns) / sizeof(insns[0]), | ||
269 | license, kver_int, NULL, 0); | ||
270 | if (err < 0) { | ||
271 | pr_err("Missing basic BPF support, skip this test: %s\n", | ||
272 | strerror(errno)); | ||
273 | return err; | ||
274 | } | ||
275 | close(err); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
230 | int test__bpf(int i) | 280 | int test__bpf(int i) |
231 | { | 281 | { |
232 | int err; | 282 | int err; |
@@ -239,6 +289,9 @@ int test__bpf(int i) | |||
239 | return TEST_SKIP; | 289 | return TEST_SKIP; |
240 | } | 290 | } |
241 | 291 | ||
292 | if (check_env()) | ||
293 | return TEST_SKIP; | ||
294 | |||
242 | err = __test__bpf(i); | 295 | err = __test__bpf(i); |
243 | return err; | 296 | return err; |
244 | } | 297 | } |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 313a48c6b2bc..abd3f0ec0c0b 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -293,7 +293,6 @@ static int process_sample_event(struct machine *machine, | |||
293 | { | 293 | { |
294 | struct perf_sample sample; | 294 | struct perf_sample sample; |
295 | struct thread *thread; | 295 | struct thread *thread; |
296 | u8 cpumode; | ||
297 | int ret; | 296 | int ret; |
298 | 297 | ||
299 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | 298 | if (perf_evlist__parse_sample(evlist, event, &sample)) { |
@@ -307,9 +306,7 @@ static int process_sample_event(struct machine *machine, | |||
307 | return -1; | 306 | return -1; |
308 | } | 307 | } |
309 | 308 | ||
310 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 309 | ret = read_object_code(sample.ip, READLEN, sample.cpumode, thread, state); |
311 | |||
312 | ret = read_object_code(sample.ip, READLEN, cpumode, thread, state); | ||
313 | thread__put(thread); | 310 | thread__put(thread); |
314 | return ret; | 311 | return ret; |
315 | } | 312 | } |
@@ -439,7 +436,7 @@ static int do_test_code_reading(bool try_kcore) | |||
439 | .mmap_pages = UINT_MAX, | 436 | .mmap_pages = UINT_MAX, |
440 | .user_freq = UINT_MAX, | 437 | .user_freq = UINT_MAX, |
441 | .user_interval = ULLONG_MAX, | 438 | .user_interval = ULLONG_MAX, |
442 | .freq = 4000, | 439 | .freq = 500, |
443 | .target = { | 440 | .target = { |
444 | .uses_mmap = true, | 441 | .uses_mmap = true, |
445 | }, | 442 | }, |
@@ -559,7 +556,13 @@ static int do_test_code_reading(bool try_kcore) | |||
559 | evlist = NULL; | 556 | evlist = NULL; |
560 | continue; | 557 | continue; |
561 | } | 558 | } |
562 | pr_debug("perf_evlist__open failed\n"); | 559 | |
560 | if (verbose) { | ||
561 | char errbuf[512]; | ||
562 | perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); | ||
563 | pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); | ||
564 | } | ||
565 | |||
563 | goto out_put; | 566 | goto out_put; |
564 | } | 567 | } |
565 | break; | 568 | break; |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 1c5c0221cea2..8f6eb853aaf7 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -20,10 +20,10 @@ | |||
20 | 20 | ||
21 | static int mmap_handler(struct perf_tool *tool __maybe_unused, | 21 | static int mmap_handler(struct perf_tool *tool __maybe_unused, |
22 | union perf_event *event, | 22 | union perf_event *event, |
23 | struct perf_sample *sample __maybe_unused, | 23 | struct perf_sample *sample, |
24 | struct machine *machine) | 24 | struct machine *machine) |
25 | { | 25 | { |
26 | return machine__process_mmap2_event(machine, event, NULL); | 26 | return machine__process_mmap2_event(machine, event, sample); |
27 | } | 27 | } |
28 | 28 | ||
29 | static int init_live_machine(struct machine *machine) | 29 | static int init_live_machine(struct machine *machine) |
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index 071a8b5f5232..f55f4bd47932 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c | |||
@@ -100,9 +100,11 @@ struct machine *setup_fake_machine(struct machines *machines) | |||
100 | } | 100 | } |
101 | 101 | ||
102 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | 102 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { |
103 | struct perf_sample sample = { | ||
104 | .cpumode = PERF_RECORD_MISC_USER, | ||
105 | }; | ||
103 | union perf_event fake_mmap_event = { | 106 | union perf_event fake_mmap_event = { |
104 | .mmap = { | 107 | .mmap = { |
105 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
106 | .pid = fake_mmap_info[i].pid, | 108 | .pid = fake_mmap_info[i].pid, |
107 | .tid = fake_mmap_info[i].pid, | 109 | .tid = fake_mmap_info[i].pid, |
108 | .start = fake_mmap_info[i].start, | 110 | .start = fake_mmap_info[i].start, |
@@ -114,7 +116,7 @@ struct machine *setup_fake_machine(struct machines *machines) | |||
114 | strcpy(fake_mmap_event.mmap.filename, | 116 | strcpy(fake_mmap_event.mmap.filename, |
115 | fake_mmap_info[i].filename); | 117 | fake_mmap_info[i].filename); |
116 | 118 | ||
117 | machine__process_mmap_event(machine, &fake_mmap_event, NULL); | 119 | machine__process_mmap_event(machine, &fake_mmap_event, &sample); |
118 | } | 120 | } |
119 | 121 | ||
120 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | 122 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { |
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 5e6a86e50fb9..ed5aa9eaeb6c 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
@@ -81,11 +81,6 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
81 | size_t i; | 81 | size_t i; |
82 | 82 | ||
83 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | 83 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { |
84 | const union perf_event event = { | ||
85 | .header = { | ||
86 | .misc = PERF_RECORD_MISC_USER, | ||
87 | }, | ||
88 | }; | ||
89 | struct hist_entry_iter iter = { | 84 | struct hist_entry_iter iter = { |
90 | .evsel = evsel, | 85 | .evsel = evsel, |
91 | .sample = &sample, | 86 | .sample = &sample, |
@@ -97,13 +92,13 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
97 | else | 92 | else |
98 | iter.ops = &hist_iter_normal; | 93 | iter.ops = &hist_iter_normal; |
99 | 94 | ||
95 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
100 | sample.pid = fake_samples[i].pid; | 96 | sample.pid = fake_samples[i].pid; |
101 | sample.tid = fake_samples[i].pid; | 97 | sample.tid = fake_samples[i].pid; |
102 | sample.ip = fake_samples[i].ip; | 98 | sample.ip = fake_samples[i].ip; |
103 | sample.callchain = (struct ip_callchain *)fake_callchains[i]; | 99 | sample.callchain = (struct ip_callchain *)fake_callchains[i]; |
104 | 100 | ||
105 | if (perf_event__preprocess_sample(&event, machine, &al, | 101 | if (machine__resolve(machine, &al, &sample) < 0) |
106 | &sample) < 0) | ||
107 | goto out; | 102 | goto out; |
108 | 103 | ||
109 | if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, | 104 | if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, |
@@ -191,7 +186,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec | |||
191 | * function since TEST_ASSERT_VAL() returns in case of failure. | 186 | * function since TEST_ASSERT_VAL() returns in case of failure. |
192 | */ | 187 | */ |
193 | hists__collapse_resort(hists, NULL); | 188 | hists__collapse_resort(hists, NULL); |
194 | hists__output_resort(hists, NULL); | 189 | perf_evsel__output_resort(hists_to_evsel(hists), NULL); |
195 | 190 | ||
196 | if (verbose > 2) { | 191 | if (verbose > 2) { |
197 | pr_info("use callchain: %d, cumulate callchain: %d\n", | 192 | pr_info("use callchain: %d, cumulate callchain: %d\n", |
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 351a42463444..b825d24f8186 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
@@ -58,11 +58,6 @@ static int add_hist_entries(struct perf_evlist *evlist, | |||
58 | */ | 58 | */ |
59 | evlist__for_each(evlist, evsel) { | 59 | evlist__for_each(evlist, evsel) { |
60 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | 60 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { |
61 | const union perf_event event = { | ||
62 | .header = { | ||
63 | .misc = PERF_RECORD_MISC_USER, | ||
64 | }, | ||
65 | }; | ||
66 | struct hist_entry_iter iter = { | 61 | struct hist_entry_iter iter = { |
67 | .evsel = evsel, | 62 | .evsel = evsel, |
68 | .sample = &sample, | 63 | .sample = &sample, |
@@ -76,12 +71,12 @@ static int add_hist_entries(struct perf_evlist *evlist, | |||
76 | hists->dso_filter = NULL; | 71 | hists->dso_filter = NULL; |
77 | hists->symbol_filter_str = NULL; | 72 | hists->symbol_filter_str = NULL; |
78 | 73 | ||
74 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
79 | sample.pid = fake_samples[i].pid; | 75 | sample.pid = fake_samples[i].pid; |
80 | sample.tid = fake_samples[i].pid; | 76 | sample.tid = fake_samples[i].pid; |
81 | sample.ip = fake_samples[i].ip; | 77 | sample.ip = fake_samples[i].ip; |
82 | 78 | ||
83 | if (perf_event__preprocess_sample(&event, machine, &al, | 79 | if (machine__resolve(machine, &al, &sample) < 0) |
84 | &sample) < 0) | ||
85 | goto out; | 80 | goto out; |
86 | 81 | ||
87 | al.socket = fake_samples[i].socket; | 82 | al.socket = fake_samples[i].socket; |
@@ -145,7 +140,7 @@ int test__hists_filter(int subtest __maybe_unused) | |||
145 | struct hists *hists = evsel__hists(evsel); | 140 | struct hists *hists = evsel__hists(evsel); |
146 | 141 | ||
147 | hists__collapse_resort(hists, NULL); | 142 | hists__collapse_resort(hists, NULL); |
148 | hists__output_resort(hists, NULL); | 143 | perf_evsel__output_resort(evsel, NULL); |
149 | 144 | ||
150 | if (verbose > 2) { | 145 | if (verbose > 2) { |
151 | pr_info("Normal histogram\n"); | 146 | pr_info("Normal histogram\n"); |
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 64b257d8d557..358324e47805 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -76,17 +76,12 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
76 | struct hists *hists = evsel__hists(evsel); | 76 | struct hists *hists = evsel__hists(evsel); |
77 | 77 | ||
78 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | 78 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { |
79 | const union perf_event event = { | 79 | sample.cpumode = PERF_RECORD_MISC_USER; |
80 | .header = { | ||
81 | .misc = PERF_RECORD_MISC_USER, | ||
82 | }, | ||
83 | }; | ||
84 | |||
85 | sample.pid = fake_common_samples[k].pid; | 80 | sample.pid = fake_common_samples[k].pid; |
86 | sample.tid = fake_common_samples[k].pid; | 81 | sample.tid = fake_common_samples[k].pid; |
87 | sample.ip = fake_common_samples[k].ip; | 82 | sample.ip = fake_common_samples[k].ip; |
88 | if (perf_event__preprocess_sample(&event, machine, &al, | 83 | |
89 | &sample) < 0) | 84 | if (machine__resolve(machine, &al, &sample) < 0) |
90 | goto out; | 85 | goto out; |
91 | 86 | ||
92 | he = __hists__add_entry(hists, &al, NULL, | 87 | he = __hists__add_entry(hists, &al, NULL, |
@@ -102,17 +97,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
102 | } | 97 | } |
103 | 98 | ||
104 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { | 99 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { |
105 | const union perf_event event = { | ||
106 | .header = { | ||
107 | .misc = PERF_RECORD_MISC_USER, | ||
108 | }, | ||
109 | }; | ||
110 | |||
111 | sample.pid = fake_samples[i][k].pid; | 100 | sample.pid = fake_samples[i][k].pid; |
112 | sample.tid = fake_samples[i][k].pid; | 101 | sample.tid = fake_samples[i][k].pid; |
113 | sample.ip = fake_samples[i][k].ip; | 102 | sample.ip = fake_samples[i][k].ip; |
114 | if (perf_event__preprocess_sample(&event, machine, &al, | 103 | if (machine__resolve(machine, &al, &sample) < 0) |
115 | &sample) < 0) | ||
116 | goto out; | 104 | goto out; |
117 | 105 | ||
118 | he = __hists__add_entry(hists, &al, NULL, | 106 | he = __hists__add_entry(hists, &al, NULL, |
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index b231265148d8..d3556fbe8c5c 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
@@ -51,11 +51,6 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
51 | size_t i; | 51 | size_t i; |
52 | 52 | ||
53 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | 53 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { |
54 | const union perf_event event = { | ||
55 | .header = { | ||
56 | .misc = PERF_RECORD_MISC_USER, | ||
57 | }, | ||
58 | }; | ||
59 | struct hist_entry_iter iter = { | 54 | struct hist_entry_iter iter = { |
60 | .evsel = evsel, | 55 | .evsel = evsel, |
61 | .sample = &sample, | 56 | .sample = &sample, |
@@ -63,13 +58,13 @@ static int add_hist_entries(struct hists *hists, struct machine *machine) | |||
63 | .hide_unresolved = false, | 58 | .hide_unresolved = false, |
64 | }; | 59 | }; |
65 | 60 | ||
61 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
66 | sample.cpu = fake_samples[i].cpu; | 62 | sample.cpu = fake_samples[i].cpu; |
67 | sample.pid = fake_samples[i].pid; | 63 | sample.pid = fake_samples[i].pid; |
68 | sample.tid = fake_samples[i].pid; | 64 | sample.tid = fake_samples[i].pid; |
69 | sample.ip = fake_samples[i].ip; | 65 | sample.ip = fake_samples[i].ip; |
70 | 66 | ||
71 | if (perf_event__preprocess_sample(&event, machine, &al, | 67 | if (machine__resolve(machine, &al, &sample) < 0) |
72 | &sample) < 0) | ||
73 | goto out; | 68 | goto out; |
74 | 69 | ||
75 | if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, | 70 | if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, |
@@ -156,7 +151,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) | |||
156 | goto out; | 151 | goto out; |
157 | 152 | ||
158 | hists__collapse_resort(hists, NULL); | 153 | hists__collapse_resort(hists, NULL); |
159 | hists__output_resort(hists, NULL); | 154 | perf_evsel__output_resort(evsel, NULL); |
160 | 155 | ||
161 | if (verbose > 2) { | 156 | if (verbose > 2) { |
162 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 157 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -256,7 +251,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) | |||
256 | goto out; | 251 | goto out; |
257 | 252 | ||
258 | hists__collapse_resort(hists, NULL); | 253 | hists__collapse_resort(hists, NULL); |
259 | hists__output_resort(hists, NULL); | 254 | perf_evsel__output_resort(evsel, NULL); |
260 | 255 | ||
261 | if (verbose > 2) { | 256 | if (verbose > 2) { |
262 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 257 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -310,7 +305,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
310 | goto out; | 305 | goto out; |
311 | 306 | ||
312 | hists__collapse_resort(hists, NULL); | 307 | hists__collapse_resort(hists, NULL); |
313 | hists__output_resort(hists, NULL); | 308 | perf_evsel__output_resort(evsel, NULL); |
314 | 309 | ||
315 | if (verbose > 2) { | 310 | if (verbose > 2) { |
316 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 311 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -388,7 +383,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
388 | goto out; | 383 | goto out; |
389 | 384 | ||
390 | hists__collapse_resort(hists, NULL); | 385 | hists__collapse_resort(hists, NULL); |
391 | hists__output_resort(hists, NULL); | 386 | perf_evsel__output_resort(evsel, NULL); |
392 | 387 | ||
393 | if (verbose > 2) { | 388 | if (verbose > 2) { |
394 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 389 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -491,7 +486,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine) | |||
491 | goto out; | 486 | goto out; |
492 | 487 | ||
493 | hists__collapse_resort(hists, NULL); | 488 | hists__collapse_resort(hists, NULL); |
494 | hists__output_resort(hists, NULL); | 489 | perf_evsel__output_resort(evsel, NULL); |
495 | 490 | ||
496 | if (verbose > 2) { | 491 | if (verbose > 2) { |
497 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 492 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 06f45c1d4256..cff564fb4b66 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c | |||
@@ -6,12 +6,6 @@ | |||
6 | #include "tests.h" | 6 | #include "tests.h" |
7 | #include "debug.h" | 7 | #include "debug.h" |
8 | 8 | ||
9 | static int perf_config_cb(const char *var, const char *val, | ||
10 | void *arg __maybe_unused) | ||
11 | { | ||
12 | return perf_default_config(var, val, arg); | ||
13 | } | ||
14 | |||
15 | #ifdef HAVE_LIBBPF_SUPPORT | 9 | #ifdef HAVE_LIBBPF_SUPPORT |
16 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) | 10 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) |
17 | { | 11 | { |
@@ -35,6 +29,7 @@ static int test__bpf_parsing(void *obj_buf __maybe_unused, | |||
35 | static struct { | 29 | static struct { |
36 | const char *source; | 30 | const char *source; |
37 | const char *desc; | 31 | const char *desc; |
32 | bool should_load_fail; | ||
38 | } bpf_source_table[__LLVM_TESTCASE_MAX] = { | 33 | } bpf_source_table[__LLVM_TESTCASE_MAX] = { |
39 | [LLVM_TESTCASE_BASE] = { | 34 | [LLVM_TESTCASE_BASE] = { |
40 | .source = test_llvm__bpf_base_prog, | 35 | .source = test_llvm__bpf_base_prog, |
@@ -48,14 +43,19 @@ static struct { | |||
48 | .source = test_llvm__bpf_test_prologue_prog, | 43 | .source = test_llvm__bpf_test_prologue_prog, |
49 | .desc = "Compile source for BPF prologue generation test", | 44 | .desc = "Compile source for BPF prologue generation test", |
50 | }, | 45 | }, |
46 | [LLVM_TESTCASE_BPF_RELOCATION] = { | ||
47 | .source = test_llvm__bpf_test_relocation, | ||
48 | .desc = "Compile source for BPF relocation test", | ||
49 | .should_load_fail = true, | ||
50 | }, | ||
51 | }; | 51 | }; |
52 | 52 | ||
53 | |||
54 | int | 53 | int |
55 | test_llvm__fetch_bpf_obj(void **p_obj_buf, | 54 | test_llvm__fetch_bpf_obj(void **p_obj_buf, |
56 | size_t *p_obj_buf_sz, | 55 | size_t *p_obj_buf_sz, |
57 | enum test_llvm__testcase idx, | 56 | enum test_llvm__testcase idx, |
58 | bool force) | 57 | bool force, |
58 | bool *should_load_fail) | ||
59 | { | 59 | { |
60 | const char *source; | 60 | const char *source; |
61 | const char *desc; | 61 | const char *desc; |
@@ -68,8 +68,8 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf, | |||
68 | 68 | ||
69 | source = bpf_source_table[idx].source; | 69 | source = bpf_source_table[idx].source; |
70 | desc = bpf_source_table[idx].desc; | 70 | desc = bpf_source_table[idx].desc; |
71 | 71 | if (should_load_fail) | |
72 | perf_config(perf_config_cb, NULL); | 72 | *should_load_fail = bpf_source_table[idx].should_load_fail; |
73 | 73 | ||
74 | /* | 74 | /* |
75 | * Skip this test if user's .perfconfig doesn't set [llvm] section | 75 | * Skip this test if user's .perfconfig doesn't set [llvm] section |
@@ -136,14 +136,15 @@ int test__llvm(int subtest) | |||
136 | int ret; | 136 | int ret; |
137 | void *obj_buf = NULL; | 137 | void *obj_buf = NULL; |
138 | size_t obj_buf_sz = 0; | 138 | size_t obj_buf_sz = 0; |
139 | bool should_load_fail = false; | ||
139 | 140 | ||
140 | if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) | 141 | if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) |
141 | return TEST_FAIL; | 142 | return TEST_FAIL; |
142 | 143 | ||
143 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | 144 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, |
144 | subtest, false); | 145 | subtest, false, &should_load_fail); |
145 | 146 | ||
146 | if (ret == TEST_OK) { | 147 | if (ret == TEST_OK && !should_load_fail) { |
147 | ret = test__bpf_parsing(obj_buf, obj_buf_sz); | 148 | ret = test__bpf_parsing(obj_buf, obj_buf_sz); |
148 | if (ret != TEST_OK) { | 149 | if (ret != TEST_OK) { |
149 | pr_debug("Failed to parse test case '%s'\n", | 150 | pr_debug("Failed to parse test case '%s'\n", |
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h index 5150b4d6ef50..0eaa604be99d 100644 --- a/tools/perf/tests/llvm.h +++ b/tools/perf/tests/llvm.h | |||
@@ -7,14 +7,17 @@ | |||
7 | extern const char test_llvm__bpf_base_prog[]; | 7 | extern const char test_llvm__bpf_base_prog[]; |
8 | extern const char test_llvm__bpf_test_kbuild_prog[]; | 8 | extern const char test_llvm__bpf_test_kbuild_prog[]; |
9 | extern const char test_llvm__bpf_test_prologue_prog[]; | 9 | extern const char test_llvm__bpf_test_prologue_prog[]; |
10 | extern const char test_llvm__bpf_test_relocation[]; | ||
10 | 11 | ||
11 | enum test_llvm__testcase { | 12 | enum test_llvm__testcase { |
12 | LLVM_TESTCASE_BASE, | 13 | LLVM_TESTCASE_BASE, |
13 | LLVM_TESTCASE_KBUILD, | 14 | LLVM_TESTCASE_KBUILD, |
14 | LLVM_TESTCASE_BPF_PROLOGUE, | 15 | LLVM_TESTCASE_BPF_PROLOGUE, |
16 | LLVM_TESTCASE_BPF_RELOCATION, | ||
15 | __LLVM_TESTCASE_MAX, | 17 | __LLVM_TESTCASE_MAX, |
16 | }; | 18 | }; |
17 | 19 | ||
18 | int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, | 20 | int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, |
19 | enum test_llvm__testcase index, bool force); | 21 | enum test_llvm__testcase index, bool force, |
22 | bool *should_load_fail); | ||
20 | #endif | 23 | #endif |
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index f918015512af..cac15d93aea6 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
@@ -15,6 +15,7 @@ else | |||
15 | PERF := . | 15 | PERF := . |
16 | PERF_O := $(PERF) | 16 | PERF_O := $(PERF) |
17 | O_OPT := | 17 | O_OPT := |
18 | FULL_O := $(shell readlink -f $(PERF_O) || echo $(PERF_O)) | ||
18 | 19 | ||
19 | ifneq ($(O),) | 20 | ifneq ($(O),) |
20 | FULL_O := $(shell readlink -f $(O) || echo $(O)) | 21 | FULL_O := $(shell readlink -f $(O) || echo $(O)) |
@@ -79,6 +80,7 @@ make_no_libaudit := NO_LIBAUDIT=1 | |||
79 | make_no_libbionic := NO_LIBBIONIC=1 | 80 | make_no_libbionic := NO_LIBBIONIC=1 |
80 | make_no_auxtrace := NO_AUXTRACE=1 | 81 | make_no_auxtrace := NO_AUXTRACE=1 |
81 | make_no_libbpf := NO_LIBBPF=1 | 82 | make_no_libbpf := NO_LIBBPF=1 |
83 | make_no_libcrypto := NO_LIBCRYPTO=1 | ||
82 | make_tags := tags | 84 | make_tags := tags |
83 | make_cscope := cscope | 85 | make_cscope := cscope |
84 | make_help := help | 86 | make_help := help |
@@ -102,6 +104,7 @@ make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 | |||
102 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 | 104 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 |
103 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 | 105 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 |
104 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 | 106 | make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 |
107 | make_minimal += NO_LIBCRYPTO=1 | ||
105 | 108 | ||
106 | # $(run) contains all available tests | 109 | # $(run) contains all available tests |
107 | run := make_pure | 110 | run := make_pure |
@@ -110,6 +113,9 @@ run := make_pure | |||
110 | # disable features detection | 113 | # disable features detection |
111 | ifeq ($(MK),Makefile) | 114 | ifeq ($(MK),Makefile) |
112 | run += make_clean_all | 115 | run += make_clean_all |
116 | MAKE_F := $(MAKE) | ||
117 | else | ||
118 | MAKE_F := $(MAKE) -f $(MK) | ||
113 | endif | 119 | endif |
114 | run += make_python_perf_so | 120 | run += make_python_perf_so |
115 | run += make_debug | 121 | run += make_debug |
@@ -260,6 +266,8 @@ run := $(shell shuf -e $(run)) | |||
260 | run_O := $(shell shuf -e $(run_O)) | 266 | run_O := $(shell shuf -e $(run_O)) |
261 | endif | 267 | endif |
262 | 268 | ||
269 | max_width := $(shell echo $(run_O) | sed 's/ /\n/g' | wc -L) | ||
270 | |||
263 | ifdef DEBUG | 271 | ifdef DEBUG |
264 | d := $(info run $(run)) | 272 | d := $(info run $(run)) |
265 | d := $(info run_O $(run_O)) | 273 | d := $(info run_O $(run_O)) |
@@ -267,13 +275,13 @@ endif | |||
267 | 275 | ||
268 | MAKEFLAGS := --no-print-directory | 276 | MAKEFLAGS := --no-print-directory |
269 | 277 | ||
270 | clean := @(cd $(PERF); make -s -f $(MK) $(O_OPT) clean >/dev/null) | 278 | clean := @(cd $(PERF); $(MAKE_F) -s $(O_OPT) clean >/dev/null) |
271 | 279 | ||
272 | $(run): | 280 | $(run): |
273 | $(call clean) | 281 | $(call clean) |
274 | @TMP_DEST=$$(mktemp -d); \ | 282 | @TMP_DEST=$$(mktemp -d); \ |
275 | cmd="cd $(PERF) && make -f $(MK) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST $($@)"; \ | 283 | cmd="cd $(PERF) && $(MAKE_F) $($@) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST"; \ |
276 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 284 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ |
277 | ( eval $$cmd ) >> $@ 2>&1; \ | 285 | ( eval $$cmd ) >> $@ 2>&1; \ |
278 | echo " test: $(call test,$@)" >> $@ 2>&1; \ | 286 | echo " test: $(call test,$@)" >> $@ 2>&1; \ |
279 | $(call test,$@) && \ | 287 | $(call test,$@) && \ |
@@ -283,8 +291,8 @@ $(run_O): | |||
283 | $(call clean) | 291 | $(call clean) |
284 | @TMP_O=$$(mktemp -d); \ | 292 | @TMP_O=$$(mktemp -d); \ |
285 | TMP_DEST=$$(mktemp -d); \ | 293 | TMP_DEST=$$(mktemp -d); \ |
286 | cmd="cd $(PERF) && make -f $(MK) $(PARALLEL_OPT) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \ | 294 | cmd="cd $(PERF) && $(MAKE_F) $($(patsubst %_O,%,$@)) $(PARALLEL_OPT) O=$$TMP_O DESTDIR=$$TMP_DEST"; \ |
287 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 295 | printf "%*.*s: %s\n" $(max_width) $(max_width) "$@" "$$cmd" && echo $$cmd > $@ && \ |
288 | ( eval $$cmd ) >> $@ 2>&1 && \ | 296 | ( eval $$cmd ) >> $@ 2>&1 && \ |
289 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ | 297 | echo " test: $(call test_O,$@)" >> $@ 2>&1; \ |
290 | $(call test_O,$@) && \ | 298 | $(call test_O,$@) && \ |
@@ -313,11 +321,43 @@ make_kernelsrc_tools: | |||
313 | (make -C ../../tools $(PARALLEL_OPT) $(K_O_OPT) perf) > $@ 2>&1 && \ | 321 | (make -C ../../tools $(PARALLEL_OPT) $(K_O_OPT) perf) > $@ 2>&1 && \ |
314 | test -x $(KERNEL_O)/tools/perf/perf && rm -f $@ || (cat $@ ; false) | 322 | test -x $(KERNEL_O)/tools/perf/perf && rm -f $@ || (cat $@ ; false) |
315 | 323 | ||
324 | FEATURES_DUMP_FILE := $(FULL_O)/BUILD_TEST_FEATURE_DUMP | ||
325 | FEATURES_DUMP_FILE_STATIC := $(FULL_O)/BUILD_TEST_FEATURE_DUMP_STATIC | ||
326 | |||
316 | all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools | 327 | all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools |
317 | @echo OK | 328 | @echo OK |
329 | @rm -f $(FEATURES_DUMP_FILE) $(FEATURES_DUMP_FILE_STATIC) | ||
318 | 330 | ||
319 | out: $(run_O) | 331 | out: $(run_O) |
320 | @echo OK | 332 | @echo OK |
333 | @rm -f $(FEATURES_DUMP_FILE) $(FEATURES_DUMP_FILE_STATIC) | ||
334 | |||
335 | ifeq ($(REUSE_FEATURES_DUMP),1) | ||
336 | $(FEATURES_DUMP_FILE): | ||
337 | $(call clean) | ||
338 | @cmd="cd $(PERF) && make FEATURE_DUMP_COPY=$@ $(O_OPT) feature-dump"; \ | ||
339 | echo "- $@: $$cmd" && echo $$cmd && \ | ||
340 | ( eval $$cmd ) > /dev/null 2>&1 | ||
341 | |||
342 | $(FEATURES_DUMP_FILE_STATIC): | ||
343 | $(call clean) | ||
344 | @cmd="cd $(PERF) && make FEATURE_DUMP_COPY=$@ $(O_OPT) LDFLAGS='-static' feature-dump"; \ | ||
345 | echo "- $@: $$cmd" && echo $$cmd && \ | ||
346 | ( eval $$cmd ) > /dev/null 2>&1 | ||
347 | |||
348 | # Add feature dump dependency for run/run_O targets | ||
349 | $(foreach t,$(run) $(run_O),$(eval \ | ||
350 | $(t): $(if $(findstring make_static,$(t)),\ | ||
351 | $(FEATURES_DUMP_FILE_STATIC),\ | ||
352 | $(FEATURES_DUMP_FILE)))) | ||
353 | |||
354 | # Append 'FEATURES_DUMP=' option to all test cases. For example: | ||
355 | # make_no_libbpf: NO_LIBBPF=1 --> NO_LIBBPF=1 FEATURES_DUMP=/a/b/BUILD_TEST_FEATURE_DUMP | ||
356 | # make_static: LDFLAGS=-static --> LDFLAGS=-static FEATURES_DUMP=/a/b/BUILD_TEST_FEATURE_DUMP_STATIC | ||
357 | $(foreach t,$(run),$(if $(findstring make_static,$(t)),\ | ||
358 | $(eval $(t) := $($(t)) FEATURES_DUMP=$(FEATURES_DUMP_FILE_STATIC)),\ | ||
359 | $(eval $(t) := $($(t)) FEATURES_DUMP=$(FEATURES_DUMP_FILE)))) | ||
360 | endif | ||
321 | 361 | ||
322 | .PHONY: all $(run) $(run_O) tarpkg clean make_kernelsrc make_kernelsrc_tools | 362 | .PHONY: all $(run) $(run_O) tarpkg clean make_kernelsrc make_kernelsrc_tools |
323 | endif # ifndef MK | 363 | endif # ifndef MK |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index abe8849d1d70..7865f68dc0d8 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -1271,6 +1271,38 @@ static int test__checkevent_precise_max_modifier(struct perf_evlist *evlist) | |||
1271 | return 0; | 1271 | return 0; |
1272 | } | 1272 | } |
1273 | 1273 | ||
1274 | static int test__checkevent_config_symbol(struct perf_evlist *evlist) | ||
1275 | { | ||
1276 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
1277 | |||
1278 | TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "insn") == 0); | ||
1279 | return 0; | ||
1280 | } | ||
1281 | |||
1282 | static int test__checkevent_config_raw(struct perf_evlist *evlist) | ||
1283 | { | ||
1284 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
1285 | |||
1286 | TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "rawpmu") == 0); | ||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
1290 | static int test__checkevent_config_num(struct perf_evlist *evlist) | ||
1291 | { | ||
1292 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
1293 | |||
1294 | TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "numpmu") == 0); | ||
1295 | return 0; | ||
1296 | } | ||
1297 | |||
1298 | static int test__checkevent_config_cache(struct perf_evlist *evlist) | ||
1299 | { | ||
1300 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
1301 | |||
1302 | TEST_ASSERT_VAL("wrong name setting", strcmp(evsel->name, "cachepmu") == 0); | ||
1303 | return 0; | ||
1304 | } | ||
1305 | |||
1274 | static int count_tracepoints(void) | 1306 | static int count_tracepoints(void) |
1275 | { | 1307 | { |
1276 | struct dirent *events_ent; | 1308 | struct dirent *events_ent; |
@@ -1579,6 +1611,26 @@ static struct evlist_test test__events[] = { | |||
1579 | .check = test__checkevent_precise_max_modifier, | 1611 | .check = test__checkevent_precise_max_modifier, |
1580 | .id = 47, | 1612 | .id = 47, |
1581 | }, | 1613 | }, |
1614 | { | ||
1615 | .name = "instructions/name=insn/", | ||
1616 | .check = test__checkevent_config_symbol, | ||
1617 | .id = 48, | ||
1618 | }, | ||
1619 | { | ||
1620 | .name = "r1234/name=rawpmu/", | ||
1621 | .check = test__checkevent_config_raw, | ||
1622 | .id = 49, | ||
1623 | }, | ||
1624 | { | ||
1625 | .name = "4:0x6530160/name=numpmu/", | ||
1626 | .check = test__checkevent_config_num, | ||
1627 | .id = 50, | ||
1628 | }, | ||
1629 | { | ||
1630 | .name = "L1-dcache-misses/name=cachepmu/", | ||
1631 | .check = test__checkevent_config_cache, | ||
1632 | .id = 51, | ||
1633 | }, | ||
1582 | }; | 1634 | }; |
1583 | 1635 | ||
1584 | static struct evlist_test test__events_pmu[] = { | 1636 | static struct evlist_test test__events_pmu[] = { |
@@ -1666,7 +1718,7 @@ static int test_term(struct terms_test *t) | |||
1666 | } | 1718 | } |
1667 | 1719 | ||
1668 | ret = t->check(&terms); | 1720 | ret = t->check(&terms); |
1669 | parse_events__free_terms(&terms); | 1721 | parse_events_terms__purge(&terms); |
1670 | 1722 | ||
1671 | return ret; | 1723 | return ret; |
1672 | } | 1724 | } |
diff --git a/tools/perf/tests/perf-targz-src-pkg b/tools/perf/tests/perf-targz-src-pkg index 238aa3927c71..f2d9c5fe58e0 100755 --- a/tools/perf/tests/perf-targz-src-pkg +++ b/tools/perf/tests/perf-targz-src-pkg | |||
@@ -15,7 +15,7 @@ TMP_DEST=$(mktemp -d) | |||
15 | tar xf ${TARBALL} -C $TMP_DEST | 15 | tar xf ${TARBALL} -C $TMP_DEST |
16 | rm -f ${TARBALL} | 16 | rm -f ${TARBALL} |
17 | cd - > /dev/null | 17 | cd - > /dev/null |
18 | make -C $TMP_DEST/perf*/tools/perf > /dev/null 2>&1 | 18 | make -C $TMP_DEST/perf*/tools/perf > /dev/null |
19 | RC=$? | 19 | RC=$? |
20 | rm -rf ${TMP_DEST} | 20 | rm -rf ${TMP_DEST} |
21 | exit $RC | 21 | exit $RC |
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index f0bfc9e8fd9f..630b0b409b97 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -110,7 +110,6 @@ int test__vmlinux_matches_kallsyms(int subtest __maybe_unused) | |||
110 | */ | 110 | */ |
111 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | 111 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { |
112 | struct symbol *pair, *first_pair; | 112 | struct symbol *pair, *first_pair; |
113 | bool backwards = true; | ||
114 | 113 | ||
115 | sym = rb_entry(nd, struct symbol, rb_node); | 114 | sym = rb_entry(nd, struct symbol, rb_node); |
116 | 115 | ||
@@ -151,27 +150,14 @@ next_pair: | |||
151 | continue; | 150 | continue; |
152 | 151 | ||
153 | } else { | 152 | } else { |
154 | struct rb_node *nnd; | 153 | pair = machine__find_kernel_symbol_by_name(&kallsyms, type, sym->name, NULL, NULL); |
155 | detour: | 154 | if (pair) { |
156 | nnd = backwards ? rb_prev(&pair->rb_node) : | 155 | if (UM(pair->start) == mem_start) |
157 | rb_next(&pair->rb_node); | ||
158 | if (nnd) { | ||
159 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
160 | |||
161 | if (UM(next->start) == mem_start) { | ||
162 | pair = next; | ||
163 | goto next_pair; | 156 | goto next_pair; |
164 | } | ||
165 | } | ||
166 | 157 | ||
167 | if (backwards) { | 158 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", |
168 | backwards = false; | 159 | mem_start, sym->name, pair->name); |
169 | pair = first_pair; | ||
170 | goto detour; | ||
171 | } | 160 | } |
172 | |||
173 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
174 | mem_start, sym->name, pair->name); | ||
175 | } | 161 | } |
176 | } else | 162 | } else |
177 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", | 163 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", |
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index d37202121689..af68a9d488bf 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c | |||
@@ -531,8 +531,8 @@ static struct ui_browser_colorset { | |||
531 | .bg = "yellow", | 531 | .bg = "yellow", |
532 | }, | 532 | }, |
533 | { | 533 | { |
534 | .colorset = HE_COLORSET_CODE, | 534 | .colorset = HE_COLORSET_JUMP_ARROWS, |
535 | .name = "code", | 535 | .name = "jump_arrows", |
536 | .fg = "blue", | 536 | .fg = "blue", |
537 | .bg = "default", | 537 | .bg = "default", |
538 | }, | 538 | }, |
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 01781de59532..be3b70eb5fca 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #define HE_COLORSET_MEDIUM 51 | 7 | #define HE_COLORSET_MEDIUM 51 |
8 | #define HE_COLORSET_NORMAL 52 | 8 | #define HE_COLORSET_NORMAL 52 |
9 | #define HE_COLORSET_SELECTED 53 | 9 | #define HE_COLORSET_SELECTED 53 |
10 | #define HE_COLORSET_CODE 54 | 10 | #define HE_COLORSET_JUMP_ARROWS 54 |
11 | #define HE_COLORSET_ADDR 55 | 11 | #define HE_COLORSET_ADDR 55 |
12 | #define HE_COLORSET_ROOT 56 | 12 | #define HE_COLORSET_ROOT 56 |
13 | 13 | ||
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 718bd46d47fa..4fc208e82c6f 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -284,7 +284,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
284 | to = (u64)btarget->idx; | 284 | to = (u64)btarget->idx; |
285 | } | 285 | } |
286 | 286 | ||
287 | ui_browser__set_color(browser, HE_COLORSET_CODE); | 287 | ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); |
288 | __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, | 288 | __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, |
289 | from, to); | 289 | from, to); |
290 | } | 290 | } |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 08c09ad755d2..2a83414159a6 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -32,6 +32,7 @@ struct hist_browser { | |||
32 | bool show_headers; | 32 | bool show_headers; |
33 | float min_pcnt; | 33 | float min_pcnt; |
34 | u64 nr_non_filtered_entries; | 34 | u64 nr_non_filtered_entries; |
35 | u64 nr_hierarchy_entries; | ||
35 | u64 nr_callchain_rows; | 36 | u64 nr_callchain_rows; |
36 | }; | 37 | }; |
37 | 38 | ||
@@ -58,11 +59,11 @@ static int hist_browser__get_folding(struct hist_browser *browser) | |||
58 | 59 | ||
59 | for (nd = rb_first(&hists->entries); | 60 | for (nd = rb_first(&hists->entries); |
60 | (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; | 61 | (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; |
61 | nd = rb_next(nd)) { | 62 | nd = rb_hierarchy_next(nd)) { |
62 | struct hist_entry *he = | 63 | struct hist_entry *he = |
63 | rb_entry(nd, struct hist_entry, rb_node); | 64 | rb_entry(nd, struct hist_entry, rb_node); |
64 | 65 | ||
65 | if (he->unfolded) | 66 | if (he->leaf && he->unfolded) |
66 | unfolded_rows += he->nr_rows; | 67 | unfolded_rows += he->nr_rows; |
67 | } | 68 | } |
68 | return unfolded_rows; | 69 | return unfolded_rows; |
@@ -72,7 +73,9 @@ static u32 hist_browser__nr_entries(struct hist_browser *hb) | |||
72 | { | 73 | { |
73 | u32 nr_entries; | 74 | u32 nr_entries; |
74 | 75 | ||
75 | if (hist_browser__has_filter(hb)) | 76 | if (symbol_conf.report_hierarchy) |
77 | nr_entries = hb->nr_hierarchy_entries; | ||
78 | else if (hist_browser__has_filter(hb)) | ||
76 | nr_entries = hb->nr_non_filtered_entries; | 79 | nr_entries = hb->nr_non_filtered_entries; |
77 | else | 80 | else |
78 | nr_entries = hb->hists->nr_entries; | 81 | nr_entries = hb->hists->nr_entries; |
@@ -247,6 +250,38 @@ static int callchain__count_rows(struct rb_root *chain) | |||
247 | return n; | 250 | return n; |
248 | } | 251 | } |
249 | 252 | ||
253 | static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he, | ||
254 | bool include_children) | ||
255 | { | ||
256 | int count = 0; | ||
257 | struct rb_node *node; | ||
258 | struct hist_entry *child; | ||
259 | |||
260 | if (he->leaf) | ||
261 | return callchain__count_rows(&he->sorted_chain); | ||
262 | |||
263 | if (he->has_no_entry) | ||
264 | return 1; | ||
265 | |||
266 | node = rb_first(&he->hroot_out); | ||
267 | while (node) { | ||
268 | float percent; | ||
269 | |||
270 | child = rb_entry(node, struct hist_entry, rb_node); | ||
271 | percent = hist_entry__get_percent_limit(child); | ||
272 | |||
273 | if (!child->filtered && percent >= hb->min_pcnt) { | ||
274 | count++; | ||
275 | |||
276 | if (include_children && child->unfolded) | ||
277 | count += hierarchy_count_rows(hb, child, true); | ||
278 | } | ||
279 | |||
280 | node = rb_next(node); | ||
281 | } | ||
282 | return count; | ||
283 | } | ||
284 | |||
250 | static bool hist_entry__toggle_fold(struct hist_entry *he) | 285 | static bool hist_entry__toggle_fold(struct hist_entry *he) |
251 | { | 286 | { |
252 | if (!he) | 287 | if (!he) |
@@ -302,7 +337,7 @@ static void callchain_node__init_have_children(struct callchain_node *node, | |||
302 | chain = list_entry(node->val.next, struct callchain_list, list); | 337 | chain = list_entry(node->val.next, struct callchain_list, list); |
303 | chain->has_children = has_sibling; | 338 | chain->has_children = has_sibling; |
304 | 339 | ||
305 | if (node->val.next != node->val.prev) { | 340 | if (!list_empty(&node->val)) { |
306 | chain = list_entry(node->val.prev, struct callchain_list, list); | 341 | chain = list_entry(node->val.prev, struct callchain_list, list); |
307 | chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); | 342 | chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); |
308 | } | 343 | } |
@@ -326,11 +361,17 @@ static void callchain__init_have_children(struct rb_root *root) | |||
326 | 361 | ||
327 | static void hist_entry__init_have_children(struct hist_entry *he) | 362 | static void hist_entry__init_have_children(struct hist_entry *he) |
328 | { | 363 | { |
329 | if (!he->init_have_children) { | 364 | if (he->init_have_children) |
365 | return; | ||
366 | |||
367 | if (he->leaf) { | ||
330 | he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); | 368 | he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); |
331 | callchain__init_have_children(&he->sorted_chain); | 369 | callchain__init_have_children(&he->sorted_chain); |
332 | he->init_have_children = true; | 370 | } else { |
371 | he->has_children = !RB_EMPTY_ROOT(&he->hroot_out); | ||
333 | } | 372 | } |
373 | |||
374 | he->init_have_children = true; | ||
334 | } | 375 | } |
335 | 376 | ||
336 | static bool hist_browser__toggle_fold(struct hist_browser *browser) | 377 | static bool hist_browser__toggle_fold(struct hist_browser *browser) |
@@ -349,17 +390,49 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser) | |||
349 | has_children = callchain_list__toggle_fold(cl); | 390 | has_children = callchain_list__toggle_fold(cl); |
350 | 391 | ||
351 | if (has_children) { | 392 | if (has_children) { |
393 | int child_rows = 0; | ||
394 | |||
352 | hist_entry__init_have_children(he); | 395 | hist_entry__init_have_children(he); |
353 | browser->b.nr_entries -= he->nr_rows; | 396 | browser->b.nr_entries -= he->nr_rows; |
354 | browser->nr_callchain_rows -= he->nr_rows; | ||
355 | 397 | ||
356 | if (he->unfolded) | 398 | if (he->leaf) |
357 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | 399 | browser->nr_callchain_rows -= he->nr_rows; |
358 | else | 400 | else |
401 | browser->nr_hierarchy_entries -= he->nr_rows; | ||
402 | |||
403 | if (symbol_conf.report_hierarchy) | ||
404 | child_rows = hierarchy_count_rows(browser, he, true); | ||
405 | |||
406 | if (he->unfolded) { | ||
407 | if (he->leaf) | ||
408 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | ||
409 | else | ||
410 | he->nr_rows = hierarchy_count_rows(browser, he, false); | ||
411 | |||
412 | /* account grand children */ | ||
413 | if (symbol_conf.report_hierarchy) | ||
414 | browser->b.nr_entries += child_rows - he->nr_rows; | ||
415 | |||
416 | if (!he->leaf && he->nr_rows == 0) { | ||
417 | he->has_no_entry = true; | ||
418 | he->nr_rows = 1; | ||
419 | } | ||
420 | } else { | ||
421 | if (symbol_conf.report_hierarchy) | ||
422 | browser->b.nr_entries -= child_rows - he->nr_rows; | ||
423 | |||
424 | if (he->has_no_entry) | ||
425 | he->has_no_entry = false; | ||
426 | |||
359 | he->nr_rows = 0; | 427 | he->nr_rows = 0; |
428 | } | ||
360 | 429 | ||
361 | browser->b.nr_entries += he->nr_rows; | 430 | browser->b.nr_entries += he->nr_rows; |
362 | browser->nr_callchain_rows += he->nr_rows; | 431 | |
432 | if (he->leaf) | ||
433 | browser->nr_callchain_rows += he->nr_rows; | ||
434 | else | ||
435 | browser->nr_hierarchy_entries += he->nr_rows; | ||
363 | 436 | ||
364 | return true; | 437 | return true; |
365 | } | 438 | } |
@@ -422,13 +495,38 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold) | |||
422 | return n; | 495 | return n; |
423 | } | 496 | } |
424 | 497 | ||
425 | static void hist_entry__set_folding(struct hist_entry *he, bool unfold) | 498 | static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, |
499 | bool unfold __maybe_unused) | ||
500 | { | ||
501 | float percent; | ||
502 | struct rb_node *nd; | ||
503 | struct hist_entry *child; | ||
504 | int n = 0; | ||
505 | |||
506 | for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) { | ||
507 | child = rb_entry(nd, struct hist_entry, rb_node); | ||
508 | percent = hist_entry__get_percent_limit(child); | ||
509 | if (!child->filtered && percent >= hb->min_pcnt) | ||
510 | n++; | ||
511 | } | ||
512 | |||
513 | return n; | ||
514 | } | ||
515 | |||
516 | static void hist_entry__set_folding(struct hist_entry *he, | ||
517 | struct hist_browser *hb, bool unfold) | ||
426 | { | 518 | { |
427 | hist_entry__init_have_children(he); | 519 | hist_entry__init_have_children(he); |
428 | he->unfolded = unfold ? he->has_children : false; | 520 | he->unfolded = unfold ? he->has_children : false; |
429 | 521 | ||
430 | if (he->has_children) { | 522 | if (he->has_children) { |
431 | int n = callchain__set_folding(&he->sorted_chain, unfold); | 523 | int n; |
524 | |||
525 | if (he->leaf) | ||
526 | n = callchain__set_folding(&he->sorted_chain, unfold); | ||
527 | else | ||
528 | n = hierarchy_set_folding(hb, he, unfold); | ||
529 | |||
432 | he->nr_rows = unfold ? n : 0; | 530 | he->nr_rows = unfold ? n : 0; |
433 | } else | 531 | } else |
434 | he->nr_rows = 0; | 532 | he->nr_rows = 0; |
@@ -438,19 +536,38 @@ static void | |||
438 | __hist_browser__set_folding(struct hist_browser *browser, bool unfold) | 536 | __hist_browser__set_folding(struct hist_browser *browser, bool unfold) |
439 | { | 537 | { |
440 | struct rb_node *nd; | 538 | struct rb_node *nd; |
441 | struct hists *hists = browser->hists; | 539 | struct hist_entry *he; |
540 | double percent; | ||
442 | 541 | ||
443 | for (nd = rb_first(&hists->entries); | 542 | nd = rb_first(&browser->hists->entries); |
444 | (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; | 543 | while (nd) { |
445 | nd = rb_next(nd)) { | 544 | he = rb_entry(nd, struct hist_entry, rb_node); |
446 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 545 | |
447 | hist_entry__set_folding(he, unfold); | 546 | /* set folding state even if it's currently folded */ |
448 | browser->nr_callchain_rows += he->nr_rows; | 547 | nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); |
548 | |||
549 | hist_entry__set_folding(he, browser, unfold); | ||
550 | |||
551 | percent = hist_entry__get_percent_limit(he); | ||
552 | if (he->filtered || percent < browser->min_pcnt) | ||
553 | continue; | ||
554 | |||
555 | if (!he->depth || unfold) | ||
556 | browser->nr_hierarchy_entries++; | ||
557 | if (he->leaf) | ||
558 | browser->nr_callchain_rows += he->nr_rows; | ||
559 | else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { | ||
560 | browser->nr_hierarchy_entries++; | ||
561 | he->has_no_entry = true; | ||
562 | he->nr_rows = 1; | ||
563 | } else | ||
564 | he->has_no_entry = false; | ||
449 | } | 565 | } |
450 | } | 566 | } |
451 | 567 | ||
452 | static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) | 568 | static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) |
453 | { | 569 | { |
570 | browser->nr_hierarchy_entries = 0; | ||
454 | browser->nr_callchain_rows = 0; | 571 | browser->nr_callchain_rows = 0; |
455 | __hist_browser__set_folding(browser, unfold); | 572 | __hist_browser__set_folding(browser, unfold); |
456 | 573 | ||
@@ -657,9 +774,24 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser, | |||
657 | return 1; | 774 | return 1; |
658 | } | 775 | } |
659 | 776 | ||
777 | static bool check_percent_display(struct rb_node *node, u64 parent_total) | ||
778 | { | ||
779 | struct callchain_node *child; | ||
780 | |||
781 | if (node == NULL) | ||
782 | return false; | ||
783 | |||
784 | if (rb_next(node)) | ||
785 | return true; | ||
786 | |||
787 | child = rb_entry(node, struct callchain_node, rb_node); | ||
788 | return callchain_cumul_hits(child) != parent_total; | ||
789 | } | ||
790 | |||
660 | static int hist_browser__show_callchain_flat(struct hist_browser *browser, | 791 | static int hist_browser__show_callchain_flat(struct hist_browser *browser, |
661 | struct rb_root *root, | 792 | struct rb_root *root, |
662 | unsigned short row, u64 total, | 793 | unsigned short row, u64 total, |
794 | u64 parent_total, | ||
663 | print_callchain_entry_fn print, | 795 | print_callchain_entry_fn print, |
664 | struct callchain_print_arg *arg, | 796 | struct callchain_print_arg *arg, |
665 | check_output_full_fn is_output_full) | 797 | check_output_full_fn is_output_full) |
@@ -669,7 +801,7 @@ static int hist_browser__show_callchain_flat(struct hist_browser *browser, | |||
669 | bool need_percent; | 801 | bool need_percent; |
670 | 802 | ||
671 | node = rb_first(root); | 803 | node = rb_first(root); |
672 | need_percent = node && rb_next(node); | 804 | need_percent = check_percent_display(node, parent_total); |
673 | 805 | ||
674 | while (node) { | 806 | while (node) { |
675 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 807 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
@@ -763,6 +895,7 @@ static char *hist_browser__folded_callchain_str(struct hist_browser *browser, | |||
763 | static int hist_browser__show_callchain_folded(struct hist_browser *browser, | 895 | static int hist_browser__show_callchain_folded(struct hist_browser *browser, |
764 | struct rb_root *root, | 896 | struct rb_root *root, |
765 | unsigned short row, u64 total, | 897 | unsigned short row, u64 total, |
898 | u64 parent_total, | ||
766 | print_callchain_entry_fn print, | 899 | print_callchain_entry_fn print, |
767 | struct callchain_print_arg *arg, | 900 | struct callchain_print_arg *arg, |
768 | check_output_full_fn is_output_full) | 901 | check_output_full_fn is_output_full) |
@@ -772,7 +905,7 @@ static int hist_browser__show_callchain_folded(struct hist_browser *browser, | |||
772 | bool need_percent; | 905 | bool need_percent; |
773 | 906 | ||
774 | node = rb_first(root); | 907 | node = rb_first(root); |
775 | need_percent = node && rb_next(node); | 908 | need_percent = check_percent_display(node, parent_total); |
776 | 909 | ||
777 | while (node) { | 910 | while (node) { |
778 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 911 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
@@ -844,20 +977,24 @@ next: | |||
844 | return row - first_row; | 977 | return row - first_row; |
845 | } | 978 | } |
846 | 979 | ||
847 | static int hist_browser__show_callchain(struct hist_browser *browser, | 980 | static int hist_browser__show_callchain_graph(struct hist_browser *browser, |
848 | struct rb_root *root, int level, | 981 | struct rb_root *root, int level, |
849 | unsigned short row, u64 total, | 982 | unsigned short row, u64 total, |
983 | u64 parent_total, | ||
850 | print_callchain_entry_fn print, | 984 | print_callchain_entry_fn print, |
851 | struct callchain_print_arg *arg, | 985 | struct callchain_print_arg *arg, |
852 | check_output_full_fn is_output_full) | 986 | check_output_full_fn is_output_full) |
853 | { | 987 | { |
854 | struct rb_node *node; | 988 | struct rb_node *node; |
855 | int first_row = row, offset = level * LEVEL_OFFSET_STEP; | 989 | int first_row = row, offset = level * LEVEL_OFFSET_STEP; |
856 | u64 new_total; | ||
857 | bool need_percent; | 990 | bool need_percent; |
991 | u64 percent_total = total; | ||
992 | |||
993 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
994 | percent_total = parent_total; | ||
858 | 995 | ||
859 | node = rb_first(root); | 996 | node = rb_first(root); |
860 | need_percent = node && rb_next(node); | 997 | need_percent = check_percent_display(node, parent_total); |
861 | 998 | ||
862 | while (node) { | 999 | while (node) { |
863 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 1000 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
@@ -878,7 +1015,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
878 | folded_sign = callchain_list__folded(chain); | 1015 | folded_sign = callchain_list__folded(chain); |
879 | 1016 | ||
880 | row += hist_browser__show_callchain_list(browser, child, | 1017 | row += hist_browser__show_callchain_list(browser, child, |
881 | chain, row, total, | 1018 | chain, row, percent_total, |
882 | was_first && need_percent, | 1019 | was_first && need_percent, |
883 | offset + extra_offset, | 1020 | offset + extra_offset, |
884 | print, arg); | 1021 | print, arg); |
@@ -893,13 +1030,9 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
893 | if (folded_sign == '-') { | 1030 | if (folded_sign == '-') { |
894 | const int new_level = level + (extra_offset ? 2 : 1); | 1031 | const int new_level = level + (extra_offset ? 2 : 1); |
895 | 1032 | ||
896 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 1033 | row += hist_browser__show_callchain_graph(browser, &child->rb_root, |
897 | new_total = child->children_hit; | 1034 | new_level, row, total, |
898 | else | 1035 | child->children_hit, |
899 | new_total = total; | ||
900 | |||
901 | row += hist_browser__show_callchain(browser, &child->rb_root, | ||
902 | new_level, row, new_total, | ||
903 | print, arg, is_output_full); | 1036 | print, arg, is_output_full); |
904 | } | 1037 | } |
905 | if (is_output_full(browser, row)) | 1038 | if (is_output_full(browser, row)) |
@@ -910,6 +1043,45 @@ out: | |||
910 | return row - first_row; | 1043 | return row - first_row; |
911 | } | 1044 | } |
912 | 1045 | ||
1046 | static int hist_browser__show_callchain(struct hist_browser *browser, | ||
1047 | struct hist_entry *entry, int level, | ||
1048 | unsigned short row, | ||
1049 | print_callchain_entry_fn print, | ||
1050 | struct callchain_print_arg *arg, | ||
1051 | check_output_full_fn is_output_full) | ||
1052 | { | ||
1053 | u64 total = hists__total_period(entry->hists); | ||
1054 | u64 parent_total; | ||
1055 | int printed; | ||
1056 | |||
1057 | if (symbol_conf.cumulate_callchain) | ||
1058 | parent_total = entry->stat_acc->period; | ||
1059 | else | ||
1060 | parent_total = entry->stat.period; | ||
1061 | |||
1062 | if (callchain_param.mode == CHAIN_FLAT) { | ||
1063 | printed = hist_browser__show_callchain_flat(browser, | ||
1064 | &entry->sorted_chain, row, | ||
1065 | total, parent_total, print, arg, | ||
1066 | is_output_full); | ||
1067 | } else if (callchain_param.mode == CHAIN_FOLDED) { | ||
1068 | printed = hist_browser__show_callchain_folded(browser, | ||
1069 | &entry->sorted_chain, row, | ||
1070 | total, parent_total, print, arg, | ||
1071 | is_output_full); | ||
1072 | } else { | ||
1073 | printed = hist_browser__show_callchain_graph(browser, | ||
1074 | &entry->sorted_chain, level, row, | ||
1075 | total, parent_total, print, arg, | ||
1076 | is_output_full); | ||
1077 | } | ||
1078 | |||
1079 | if (arg->is_current_entry) | ||
1080 | browser->he_selection = entry; | ||
1081 | |||
1082 | return printed; | ||
1083 | } | ||
1084 | |||
913 | struct hpp_arg { | 1085 | struct hpp_arg { |
914 | struct ui_browser *b; | 1086 | struct ui_browser *b; |
915 | char folded_sign; | 1087 | char folded_sign; |
@@ -1006,7 +1178,6 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
1006 | struct hist_entry *entry, | 1178 | struct hist_entry *entry, |
1007 | unsigned short row) | 1179 | unsigned short row) |
1008 | { | 1180 | { |
1009 | char s[256]; | ||
1010 | int printed = 0; | 1181 | int printed = 0; |
1011 | int width = browser->b.width; | 1182 | int width = browser->b.width; |
1012 | char folded_sign = ' '; | 1183 | char folded_sign = ' '; |
@@ -1031,16 +1202,18 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
1031 | .folded_sign = folded_sign, | 1202 | .folded_sign = folded_sign, |
1032 | .current_entry = current_entry, | 1203 | .current_entry = current_entry, |
1033 | }; | 1204 | }; |
1034 | struct perf_hpp hpp = { | ||
1035 | .buf = s, | ||
1036 | .size = sizeof(s), | ||
1037 | .ptr = &arg, | ||
1038 | }; | ||
1039 | int column = 0; | 1205 | int column = 0; |
1040 | 1206 | ||
1041 | hist_browser__gotorc(browser, row, 0); | 1207 | hist_browser__gotorc(browser, row, 0); |
1042 | 1208 | ||
1043 | perf_hpp__for_each_format(fmt) { | 1209 | hists__for_each_format(browser->hists, fmt) { |
1210 | char s[2048]; | ||
1211 | struct perf_hpp hpp = { | ||
1212 | .buf = s, | ||
1213 | .size = sizeof(s), | ||
1214 | .ptr = &arg, | ||
1215 | }; | ||
1216 | |||
1044 | if (perf_hpp__should_skip(fmt, entry->hists) || | 1217 | if (perf_hpp__should_skip(fmt, entry->hists) || |
1045 | column++ < browser->b.horiz_scroll) | 1218 | column++ < browser->b.horiz_scroll) |
1046 | continue; | 1219 | continue; |
@@ -1065,11 +1238,18 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
1065 | } | 1238 | } |
1066 | 1239 | ||
1067 | if (fmt->color) { | 1240 | if (fmt->color) { |
1068 | width -= fmt->color(fmt, &hpp, entry); | 1241 | int ret = fmt->color(fmt, &hpp, entry); |
1242 | hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); | ||
1243 | /* | ||
1244 | * fmt->color() already used ui_browser to | ||
1245 | * print the non alignment bits, skip it (+ret): | ||
1246 | */ | ||
1247 | ui_browser__printf(&browser->b, "%s", s + ret); | ||
1069 | } else { | 1248 | } else { |
1070 | width -= fmt->entry(fmt, &hpp, entry); | 1249 | hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry)); |
1071 | ui_browser__printf(&browser->b, "%s", s); | 1250 | ui_browser__printf(&browser->b, "%s", s); |
1072 | } | 1251 | } |
1252 | width -= hpp.buf - s; | ||
1073 | } | 1253 | } |
1074 | 1254 | ||
1075 | /* The scroll bar isn't being used */ | 1255 | /* The scroll bar isn't being used */ |
@@ -1084,43 +1264,246 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
1084 | --row_offset; | 1264 | --row_offset; |
1085 | 1265 | ||
1086 | if (folded_sign == '-' && row != browser->b.rows) { | 1266 | if (folded_sign == '-' && row != browser->b.rows) { |
1087 | u64 total = hists__total_period(entry->hists); | ||
1088 | struct callchain_print_arg arg = { | 1267 | struct callchain_print_arg arg = { |
1089 | .row_offset = row_offset, | 1268 | .row_offset = row_offset, |
1090 | .is_current_entry = current_entry, | 1269 | .is_current_entry = current_entry, |
1091 | }; | 1270 | }; |
1092 | 1271 | ||
1093 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | 1272 | printed += hist_browser__show_callchain(browser, entry, 1, row, |
1094 | if (symbol_conf.cumulate_callchain) | ||
1095 | total = entry->stat_acc->period; | ||
1096 | else | ||
1097 | total = entry->stat.period; | ||
1098 | } | ||
1099 | |||
1100 | if (callchain_param.mode == CHAIN_FLAT) { | ||
1101 | printed += hist_browser__show_callchain_flat(browser, | ||
1102 | &entry->sorted_chain, row, total, | ||
1103 | hist_browser__show_callchain_entry, &arg, | ||
1104 | hist_browser__check_output_full); | ||
1105 | } else if (callchain_param.mode == CHAIN_FOLDED) { | ||
1106 | printed += hist_browser__show_callchain_folded(browser, | ||
1107 | &entry->sorted_chain, row, total, | ||
1108 | hist_browser__show_callchain_entry, &arg, | 1273 | hist_browser__show_callchain_entry, &arg, |
1109 | hist_browser__check_output_full); | 1274 | hist_browser__check_output_full); |
1275 | } | ||
1276 | |||
1277 | return printed; | ||
1278 | } | ||
1279 | |||
1280 | static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, | ||
1281 | struct hist_entry *entry, | ||
1282 | unsigned short row, | ||
1283 | int level) | ||
1284 | { | ||
1285 | int printed = 0; | ||
1286 | int width = browser->b.width; | ||
1287 | char folded_sign = ' '; | ||
1288 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); | ||
1289 | off_t row_offset = entry->row_offset; | ||
1290 | bool first = true; | ||
1291 | struct perf_hpp_fmt *fmt; | ||
1292 | struct perf_hpp_list_node *fmt_node; | ||
1293 | struct hpp_arg arg = { | ||
1294 | .b = &browser->b, | ||
1295 | .current_entry = current_entry, | ||
1296 | }; | ||
1297 | int column = 0; | ||
1298 | int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; | ||
1299 | |||
1300 | if (current_entry) { | ||
1301 | browser->he_selection = entry; | ||
1302 | browser->selection = &entry->ms; | ||
1303 | } | ||
1304 | |||
1305 | hist_entry__init_have_children(entry); | ||
1306 | folded_sign = hist_entry__folded(entry); | ||
1307 | arg.folded_sign = folded_sign; | ||
1308 | |||
1309 | if (entry->leaf && row_offset) { | ||
1310 | row_offset--; | ||
1311 | goto show_callchain; | ||
1312 | } | ||
1313 | |||
1314 | hist_browser__gotorc(browser, row, 0); | ||
1315 | |||
1316 | if (current_entry && browser->b.navkeypressed) | ||
1317 | ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); | ||
1318 | else | ||
1319 | ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); | ||
1320 | |||
1321 | ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); | ||
1322 | width -= level * HIERARCHY_INDENT; | ||
1323 | |||
1324 | /* the first hpp_list_node is for overhead columns */ | ||
1325 | fmt_node = list_first_entry(&entry->hists->hpp_formats, | ||
1326 | struct perf_hpp_list_node, list); | ||
1327 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
1328 | char s[2048]; | ||
1329 | struct perf_hpp hpp = { | ||
1330 | .buf = s, | ||
1331 | .size = sizeof(s), | ||
1332 | .ptr = &arg, | ||
1333 | }; | ||
1334 | |||
1335 | if (perf_hpp__should_skip(fmt, entry->hists) || | ||
1336 | column++ < browser->b.horiz_scroll) | ||
1337 | continue; | ||
1338 | |||
1339 | if (current_entry && browser->b.navkeypressed) { | ||
1340 | ui_browser__set_color(&browser->b, | ||
1341 | HE_COLORSET_SELECTED); | ||
1110 | } else { | 1342 | } else { |
1111 | printed += hist_browser__show_callchain(browser, | 1343 | ui_browser__set_color(&browser->b, |
1112 | &entry->sorted_chain, 1, row, total, | 1344 | HE_COLORSET_NORMAL); |
1113 | hist_browser__show_callchain_entry, &arg, | 1345 | } |
1114 | hist_browser__check_output_full); | 1346 | |
1347 | if (first) { | ||
1348 | ui_browser__printf(&browser->b, "%c", folded_sign); | ||
1349 | width--; | ||
1350 | first = false; | ||
1351 | } else { | ||
1352 | ui_browser__printf(&browser->b, " "); | ||
1353 | width -= 2; | ||
1354 | } | ||
1355 | |||
1356 | if (fmt->color) { | ||
1357 | int ret = fmt->color(fmt, &hpp, entry); | ||
1358 | hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); | ||
1359 | /* | ||
1360 | * fmt->color() already used ui_browser to | ||
1361 | * print the non alignment bits, skip it (+ret): | ||
1362 | */ | ||
1363 | ui_browser__printf(&browser->b, "%s", s + ret); | ||
1364 | } else { | ||
1365 | int ret = fmt->entry(fmt, &hpp, entry); | ||
1366 | hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); | ||
1367 | ui_browser__printf(&browser->b, "%s", s); | ||
1368 | } | ||
1369 | width -= hpp.buf - s; | ||
1370 | } | ||
1371 | |||
1372 | ui_browser__write_nstring(&browser->b, "", hierarchy_indent); | ||
1373 | width -= hierarchy_indent; | ||
1374 | |||
1375 | if (column >= browser->b.horiz_scroll) { | ||
1376 | char s[2048]; | ||
1377 | struct perf_hpp hpp = { | ||
1378 | .buf = s, | ||
1379 | .size = sizeof(s), | ||
1380 | .ptr = &arg, | ||
1381 | }; | ||
1382 | |||
1383 | if (current_entry && browser->b.navkeypressed) { | ||
1384 | ui_browser__set_color(&browser->b, | ||
1385 | HE_COLORSET_SELECTED); | ||
1386 | } else { | ||
1387 | ui_browser__set_color(&browser->b, | ||
1388 | HE_COLORSET_NORMAL); | ||
1115 | } | 1389 | } |
1116 | 1390 | ||
1117 | if (arg.is_current_entry) | 1391 | perf_hpp_list__for_each_format(entry->hpp_list, fmt) { |
1118 | browser->he_selection = entry; | 1392 | ui_browser__write_nstring(&browser->b, "", 2); |
1393 | width -= 2; | ||
1394 | |||
1395 | /* | ||
1396 | * No need to call hist_entry__snprintf_alignment() | ||
1397 | * since this fmt is always the last column in the | ||
1398 | * hierarchy mode. | ||
1399 | */ | ||
1400 | if (fmt->color) { | ||
1401 | width -= fmt->color(fmt, &hpp, entry); | ||
1402 | } else { | ||
1403 | int i = 0; | ||
1404 | |||
1405 | width -= fmt->entry(fmt, &hpp, entry); | ||
1406 | ui_browser__printf(&browser->b, "%s", ltrim(s)); | ||
1407 | |||
1408 | while (isspace(s[i++])) | ||
1409 | width++; | ||
1410 | } | ||
1411 | } | ||
1412 | } | ||
1413 | |||
1414 | /* The scroll bar isn't being used */ | ||
1415 | if (!browser->b.navkeypressed) | ||
1416 | width += 1; | ||
1417 | |||
1418 | ui_browser__write_nstring(&browser->b, "", width); | ||
1419 | |||
1420 | ++row; | ||
1421 | ++printed; | ||
1422 | |||
1423 | show_callchain: | ||
1424 | if (entry->leaf && folded_sign == '-' && row != browser->b.rows) { | ||
1425 | struct callchain_print_arg carg = { | ||
1426 | .row_offset = row_offset, | ||
1427 | }; | ||
1428 | |||
1429 | printed += hist_browser__show_callchain(browser, entry, | ||
1430 | level + 1, row, | ||
1431 | hist_browser__show_callchain_entry, &carg, | ||
1432 | hist_browser__check_output_full); | ||
1119 | } | 1433 | } |
1120 | 1434 | ||
1121 | return printed; | 1435 | return printed; |
1122 | } | 1436 | } |
1123 | 1437 | ||
1438 | static int hist_browser__show_no_entry(struct hist_browser *browser, | ||
1439 | unsigned short row, int level) | ||
1440 | { | ||
1441 | int width = browser->b.width; | ||
1442 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); | ||
1443 | bool first = true; | ||
1444 | int column = 0; | ||
1445 | int ret; | ||
1446 | struct perf_hpp_fmt *fmt; | ||
1447 | struct perf_hpp_list_node *fmt_node; | ||
1448 | int indent = browser->hists->nr_hpp_node - 2; | ||
1449 | |||
1450 | if (current_entry) { | ||
1451 | browser->he_selection = NULL; | ||
1452 | browser->selection = NULL; | ||
1453 | } | ||
1454 | |||
1455 | hist_browser__gotorc(browser, row, 0); | ||
1456 | |||
1457 | if (current_entry && browser->b.navkeypressed) | ||
1458 | ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); | ||
1459 | else | ||
1460 | ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); | ||
1461 | |||
1462 | ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); | ||
1463 | width -= level * HIERARCHY_INDENT; | ||
1464 | |||
1465 | /* the first hpp_list_node is for overhead columns */ | ||
1466 | fmt_node = list_first_entry(&browser->hists->hpp_formats, | ||
1467 | struct perf_hpp_list_node, list); | ||
1468 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
1469 | if (perf_hpp__should_skip(fmt, browser->hists) || | ||
1470 | column++ < browser->b.horiz_scroll) | ||
1471 | continue; | ||
1472 | |||
1473 | ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists)); | ||
1474 | |||
1475 | if (first) { | ||
1476 | /* for folded sign */ | ||
1477 | first = false; | ||
1478 | ret++; | ||
1479 | } else { | ||
1480 | /* space between columns */ | ||
1481 | ret += 2; | ||
1482 | } | ||
1483 | |||
1484 | ui_browser__write_nstring(&browser->b, "", ret); | ||
1485 | width -= ret; | ||
1486 | } | ||
1487 | |||
1488 | ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT); | ||
1489 | width -= indent * HIERARCHY_INDENT; | ||
1490 | |||
1491 | if (column >= browser->b.horiz_scroll) { | ||
1492 | char buf[32]; | ||
1493 | |||
1494 | ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt); | ||
1495 | ui_browser__printf(&browser->b, " %s", buf); | ||
1496 | width -= ret + 2; | ||
1497 | } | ||
1498 | |||
1499 | /* The scroll bar isn't being used */ | ||
1500 | if (!browser->b.navkeypressed) | ||
1501 | width += 1; | ||
1502 | |||
1503 | ui_browser__write_nstring(&browser->b, "", width); | ||
1504 | return 1; | ||
1505 | } | ||
1506 | |||
1124 | static int advance_hpp_check(struct perf_hpp *hpp, int inc) | 1507 | static int advance_hpp_check(struct perf_hpp *hpp, int inc) |
1125 | { | 1508 | { |
1126 | advance_hpp(hpp, inc); | 1509 | advance_hpp(hpp, inc); |
@@ -1144,7 +1527,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char * | |||
1144 | return ret; | 1527 | return ret; |
1145 | } | 1528 | } |
1146 | 1529 | ||
1147 | perf_hpp__for_each_format(fmt) { | 1530 | hists__for_each_format(browser->hists, fmt) { |
1148 | if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) | 1531 | if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) |
1149 | continue; | 1532 | continue; |
1150 | 1533 | ||
@@ -1160,11 +1543,96 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char * | |||
1160 | return ret; | 1543 | return ret; |
1161 | } | 1544 | } |
1162 | 1545 | ||
1546 | static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) | ||
1547 | { | ||
1548 | struct hists *hists = browser->hists; | ||
1549 | struct perf_hpp dummy_hpp = { | ||
1550 | .buf = buf, | ||
1551 | .size = size, | ||
1552 | }; | ||
1553 | struct perf_hpp_fmt *fmt; | ||
1554 | struct perf_hpp_list_node *fmt_node; | ||
1555 | size_t ret = 0; | ||
1556 | int column = 0; | ||
1557 | int indent = hists->nr_hpp_node - 2; | ||
1558 | bool first_node, first_col; | ||
1559 | |||
1560 | ret = scnprintf(buf, size, " "); | ||
1561 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1562 | return ret; | ||
1563 | |||
1564 | /* the first hpp_list_node is for overhead columns */ | ||
1565 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
1566 | struct perf_hpp_list_node, list); | ||
1567 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
1568 | if (column++ < browser->b.horiz_scroll) | ||
1569 | continue; | ||
1570 | |||
1571 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); | ||
1572 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1573 | break; | ||
1574 | |||
1575 | ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); | ||
1576 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1577 | break; | ||
1578 | } | ||
1579 | |||
1580 | ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", | ||
1581 | indent * HIERARCHY_INDENT, ""); | ||
1582 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1583 | return ret; | ||
1584 | |||
1585 | first_node = true; | ||
1586 | list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { | ||
1587 | if (!first_node) { | ||
1588 | ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / "); | ||
1589 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1590 | break; | ||
1591 | } | ||
1592 | first_node = false; | ||
1593 | |||
1594 | first_col = true; | ||
1595 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
1596 | char *start; | ||
1597 | |||
1598 | if (perf_hpp__should_skip(fmt, hists)) | ||
1599 | continue; | ||
1600 | |||
1601 | if (!first_col) { | ||
1602 | ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+"); | ||
1603 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1604 | break; | ||
1605 | } | ||
1606 | first_col = false; | ||
1607 | |||
1608 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); | ||
1609 | dummy_hpp.buf[ret] = '\0'; | ||
1610 | rtrim(dummy_hpp.buf); | ||
1611 | |||
1612 | start = ltrim(dummy_hpp.buf); | ||
1613 | ret = strlen(start); | ||
1614 | |||
1615 | if (start != dummy_hpp.buf) | ||
1616 | memmove(dummy_hpp.buf, start, ret + 1); | ||
1617 | |||
1618 | if (advance_hpp_check(&dummy_hpp, ret)) | ||
1619 | break; | ||
1620 | } | ||
1621 | } | ||
1622 | |||
1623 | return ret; | ||
1624 | } | ||
1625 | |||
1163 | static void hist_browser__show_headers(struct hist_browser *browser) | 1626 | static void hist_browser__show_headers(struct hist_browser *browser) |
1164 | { | 1627 | { |
1165 | char headers[1024]; | 1628 | char headers[1024]; |
1166 | 1629 | ||
1167 | hists_browser__scnprintf_headers(browser, headers, sizeof(headers)); | 1630 | if (symbol_conf.report_hierarchy) |
1631 | hists_browser__scnprintf_hierarchy_headers(browser, headers, | ||
1632 | sizeof(headers)); | ||
1633 | else | ||
1634 | hists_browser__scnprintf_headers(browser, headers, | ||
1635 | sizeof(headers)); | ||
1168 | ui_browser__gotorc(&browser->b, 0, 0); | 1636 | ui_browser__gotorc(&browser->b, 0, 0); |
1169 | ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); | 1637 | ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); |
1170 | ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); | 1638 | ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); |
@@ -1196,18 +1664,34 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) | |||
1196 | hb->he_selection = NULL; | 1664 | hb->he_selection = NULL; |
1197 | hb->selection = NULL; | 1665 | hb->selection = NULL; |
1198 | 1666 | ||
1199 | for (nd = browser->top; nd; nd = rb_next(nd)) { | 1667 | for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) { |
1200 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1668 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1201 | float percent; | 1669 | float percent; |
1202 | 1670 | ||
1203 | if (h->filtered) | 1671 | if (h->filtered) { |
1672 | /* let it move to sibling */ | ||
1673 | h->unfolded = false; | ||
1204 | continue; | 1674 | continue; |
1675 | } | ||
1205 | 1676 | ||
1206 | percent = hist_entry__get_percent_limit(h); | 1677 | percent = hist_entry__get_percent_limit(h); |
1207 | if (percent < hb->min_pcnt) | 1678 | if (percent < hb->min_pcnt) |
1208 | continue; | 1679 | continue; |
1209 | 1680 | ||
1210 | row += hist_browser__show_entry(hb, h, row); | 1681 | if (symbol_conf.report_hierarchy) { |
1682 | row += hist_browser__show_hierarchy_entry(hb, h, row, | ||
1683 | h->depth); | ||
1684 | if (row == browser->rows) | ||
1685 | break; | ||
1686 | |||
1687 | if (h->has_no_entry) { | ||
1688 | hist_browser__show_no_entry(hb, row, h->depth + 1); | ||
1689 | row++; | ||
1690 | } | ||
1691 | } else { | ||
1692 | row += hist_browser__show_entry(hb, h, row); | ||
1693 | } | ||
1694 | |||
1211 | if (row == browser->rows) | 1695 | if (row == browser->rows) |
1212 | break; | 1696 | break; |
1213 | } | 1697 | } |
@@ -1225,7 +1709,14 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd, | |||
1225 | if (!h->filtered && percent >= min_pcnt) | 1709 | if (!h->filtered && percent >= min_pcnt) |
1226 | return nd; | 1710 | return nd; |
1227 | 1711 | ||
1228 | nd = rb_next(nd); | 1712 | /* |
1713 | * If it's filtered, its all children also were filtered. | ||
1714 | * So move to sibling node. | ||
1715 | */ | ||
1716 | if (rb_next(nd)) | ||
1717 | nd = rb_next(nd); | ||
1718 | else | ||
1719 | nd = rb_hierarchy_next(nd); | ||
1229 | } | 1720 | } |
1230 | 1721 | ||
1231 | return NULL; | 1722 | return NULL; |
@@ -1241,7 +1732,7 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, | |||
1241 | if (!h->filtered && percent >= min_pcnt) | 1732 | if (!h->filtered && percent >= min_pcnt) |
1242 | return nd; | 1733 | return nd; |
1243 | 1734 | ||
1244 | nd = rb_prev(nd); | 1735 | nd = rb_hierarchy_prev(nd); |
1245 | } | 1736 | } |
1246 | 1737 | ||
1247 | return NULL; | 1738 | return NULL; |
@@ -1271,8 +1762,8 @@ static void ui_browser__hists_seek(struct ui_browser *browser, | |||
1271 | nd = browser->top; | 1762 | nd = browser->top; |
1272 | goto do_offset; | 1763 | goto do_offset; |
1273 | case SEEK_END: | 1764 | case SEEK_END: |
1274 | nd = hists__filter_prev_entries(rb_last(browser->entries), | 1765 | nd = rb_hierarchy_last(rb_last(browser->entries)); |
1275 | hb->min_pcnt); | 1766 | nd = hists__filter_prev_entries(nd, hb->min_pcnt); |
1276 | first = false; | 1767 | first = false; |
1277 | break; | 1768 | break; |
1278 | default: | 1769 | default: |
@@ -1306,7 +1797,7 @@ do_offset: | |||
1306 | if (offset > 0) { | 1797 | if (offset > 0) { |
1307 | do { | 1798 | do { |
1308 | h = rb_entry(nd, struct hist_entry, rb_node); | 1799 | h = rb_entry(nd, struct hist_entry, rb_node); |
1309 | if (h->unfolded) { | 1800 | if (h->unfolded && h->leaf) { |
1310 | u16 remaining = h->nr_rows - h->row_offset; | 1801 | u16 remaining = h->nr_rows - h->row_offset; |
1311 | if (offset > remaining) { | 1802 | if (offset > remaining) { |
1312 | offset -= remaining; | 1803 | offset -= remaining; |
@@ -1318,7 +1809,8 @@ do_offset: | |||
1318 | break; | 1809 | break; |
1319 | } | 1810 | } |
1320 | } | 1811 | } |
1321 | nd = hists__filter_entries(rb_next(nd), hb->min_pcnt); | 1812 | nd = hists__filter_entries(rb_hierarchy_next(nd), |
1813 | hb->min_pcnt); | ||
1322 | if (nd == NULL) | 1814 | if (nd == NULL) |
1323 | break; | 1815 | break; |
1324 | --offset; | 1816 | --offset; |
@@ -1327,7 +1819,7 @@ do_offset: | |||
1327 | } else if (offset < 0) { | 1819 | } else if (offset < 0) { |
1328 | while (1) { | 1820 | while (1) { |
1329 | h = rb_entry(nd, struct hist_entry, rb_node); | 1821 | h = rb_entry(nd, struct hist_entry, rb_node); |
1330 | if (h->unfolded) { | 1822 | if (h->unfolded && h->leaf) { |
1331 | if (first) { | 1823 | if (first) { |
1332 | if (-offset > h->row_offset) { | 1824 | if (-offset > h->row_offset) { |
1333 | offset += h->row_offset; | 1825 | offset += h->row_offset; |
@@ -1351,7 +1843,7 @@ do_offset: | |||
1351 | } | 1843 | } |
1352 | } | 1844 | } |
1353 | 1845 | ||
1354 | nd = hists__filter_prev_entries(rb_prev(nd), | 1846 | nd = hists__filter_prev_entries(rb_hierarchy_prev(nd), |
1355 | hb->min_pcnt); | 1847 | hb->min_pcnt); |
1356 | if (nd == NULL) | 1848 | if (nd == NULL) |
1357 | break; | 1849 | break; |
@@ -1364,7 +1856,7 @@ do_offset: | |||
1364 | * row_offset at its last entry. | 1856 | * row_offset at its last entry. |
1365 | */ | 1857 | */ |
1366 | h = rb_entry(nd, struct hist_entry, rb_node); | 1858 | h = rb_entry(nd, struct hist_entry, rb_node); |
1367 | if (h->unfolded) | 1859 | if (h->unfolded && h->leaf) |
1368 | h->row_offset = h->nr_rows; | 1860 | h->row_offset = h->nr_rows; |
1369 | break; | 1861 | break; |
1370 | } | 1862 | } |
@@ -1378,17 +1870,14 @@ do_offset: | |||
1378 | } | 1870 | } |
1379 | 1871 | ||
1380 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, | 1872 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, |
1381 | struct hist_entry *he, FILE *fp) | 1873 | struct hist_entry *he, FILE *fp, |
1874 | int level) | ||
1382 | { | 1875 | { |
1383 | u64 total = hists__total_period(he->hists); | ||
1384 | struct callchain_print_arg arg = { | 1876 | struct callchain_print_arg arg = { |
1385 | .fp = fp, | 1877 | .fp = fp, |
1386 | }; | 1878 | }; |
1387 | 1879 | ||
1388 | if (symbol_conf.cumulate_callchain) | 1880 | hist_browser__show_callchain(browser, he, level, 0, |
1389 | total = he->stat_acc->period; | ||
1390 | |||
1391 | hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, | ||
1392 | hist_browser__fprintf_callchain_entry, &arg, | 1881 | hist_browser__fprintf_callchain_entry, &arg, |
1393 | hist_browser__check_dump_full); | 1882 | hist_browser__check_dump_full); |
1394 | return arg.printed; | 1883 | return arg.printed; |
@@ -1414,7 +1903,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
1414 | if (symbol_conf.use_callchain) | 1903 | if (symbol_conf.use_callchain) |
1415 | printed += fprintf(fp, "%c ", folded_sign); | 1904 | printed += fprintf(fp, "%c ", folded_sign); |
1416 | 1905 | ||
1417 | perf_hpp__for_each_format(fmt) { | 1906 | hists__for_each_format(browser->hists, fmt) { |
1418 | if (perf_hpp__should_skip(fmt, he->hists)) | 1907 | if (perf_hpp__should_skip(fmt, he->hists)) |
1419 | continue; | 1908 | continue; |
1420 | 1909 | ||
@@ -1425,12 +1914,71 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
1425 | first = false; | 1914 | first = false; |
1426 | 1915 | ||
1427 | ret = fmt->entry(fmt, &hpp, he); | 1916 | ret = fmt->entry(fmt, &hpp, he); |
1917 | ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret); | ||
1428 | advance_hpp(&hpp, ret); | 1918 | advance_hpp(&hpp, ret); |
1429 | } | 1919 | } |
1430 | printed += fprintf(fp, "%s\n", rtrim(s)); | 1920 | printed += fprintf(fp, "%s\n", s); |
1431 | 1921 | ||
1432 | if (folded_sign == '-') | 1922 | if (folded_sign == '-') |
1433 | printed += hist_browser__fprintf_callchain(browser, he, fp); | 1923 | printed += hist_browser__fprintf_callchain(browser, he, fp, 1); |
1924 | |||
1925 | return printed; | ||
1926 | } | ||
1927 | |||
1928 | |||
1929 | static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, | ||
1930 | struct hist_entry *he, | ||
1931 | FILE *fp, int level) | ||
1932 | { | ||
1933 | char s[8192]; | ||
1934 | int printed = 0; | ||
1935 | char folded_sign = ' '; | ||
1936 | struct perf_hpp hpp = { | ||
1937 | .buf = s, | ||
1938 | .size = sizeof(s), | ||
1939 | }; | ||
1940 | struct perf_hpp_fmt *fmt; | ||
1941 | struct perf_hpp_list_node *fmt_node; | ||
1942 | bool first = true; | ||
1943 | int ret; | ||
1944 | int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; | ||
1945 | |||
1946 | printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, ""); | ||
1947 | |||
1948 | folded_sign = hist_entry__folded(he); | ||
1949 | printed += fprintf(fp, "%c", folded_sign); | ||
1950 | |||
1951 | /* the first hpp_list_node is for overhead columns */ | ||
1952 | fmt_node = list_first_entry(&he->hists->hpp_formats, | ||
1953 | struct perf_hpp_list_node, list); | ||
1954 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
1955 | if (!first) { | ||
1956 | ret = scnprintf(hpp.buf, hpp.size, " "); | ||
1957 | advance_hpp(&hpp, ret); | ||
1958 | } else | ||
1959 | first = false; | ||
1960 | |||
1961 | ret = fmt->entry(fmt, &hpp, he); | ||
1962 | advance_hpp(&hpp, ret); | ||
1963 | } | ||
1964 | |||
1965 | ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, ""); | ||
1966 | advance_hpp(&hpp, ret); | ||
1967 | |||
1968 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
1969 | ret = scnprintf(hpp.buf, hpp.size, " "); | ||
1970 | advance_hpp(&hpp, ret); | ||
1971 | |||
1972 | ret = fmt->entry(fmt, &hpp, he); | ||
1973 | advance_hpp(&hpp, ret); | ||
1974 | } | ||
1975 | |||
1976 | printed += fprintf(fp, "%s\n", rtrim(s)); | ||
1977 | |||
1978 | if (he->leaf && folded_sign == '-') { | ||
1979 | printed += hist_browser__fprintf_callchain(browser, he, fp, | ||
1980 | he->depth + 1); | ||
1981 | } | ||
1434 | 1982 | ||
1435 | return printed; | 1983 | return printed; |
1436 | } | 1984 | } |
@@ -1444,8 +1992,16 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) | |||
1444 | while (nd) { | 1992 | while (nd) { |
1445 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1993 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1446 | 1994 | ||
1447 | printed += hist_browser__fprintf_entry(browser, h, fp); | 1995 | if (symbol_conf.report_hierarchy) { |
1448 | nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); | 1996 | printed += hist_browser__fprintf_hierarchy_entry(browser, |
1997 | h, fp, | ||
1998 | h->depth); | ||
1999 | } else { | ||
2000 | printed += hist_browser__fprintf_entry(browser, h, fp); | ||
2001 | } | ||
2002 | |||
2003 | nd = hists__filter_entries(rb_hierarchy_next(nd), | ||
2004 | browser->min_pcnt); | ||
1449 | } | 2005 | } |
1450 | 2006 | ||
1451 | return printed; | 2007 | return printed; |
@@ -1580,11 +2136,18 @@ static int hists__browser_title(struct hists *hists, | |||
1580 | if (hists->uid_filter_str) | 2136 | if (hists->uid_filter_str) |
1581 | printed += snprintf(bf + printed, size - printed, | 2137 | printed += snprintf(bf + printed, size - printed, |
1582 | ", UID: %s", hists->uid_filter_str); | 2138 | ", UID: %s", hists->uid_filter_str); |
1583 | if (thread) | 2139 | if (thread) { |
1584 | printed += scnprintf(bf + printed, size - printed, | 2140 | if (sort__has_thread) { |
2141 | printed += scnprintf(bf + printed, size - printed, | ||
1585 | ", Thread: %s(%d)", | 2142 | ", Thread: %s(%d)", |
1586 | (thread->comm_set ? thread__comm_str(thread) : ""), | 2143 | (thread->comm_set ? thread__comm_str(thread) : ""), |
1587 | thread->tid); | 2144 | thread->tid); |
2145 | } else { | ||
2146 | printed += scnprintf(bf + printed, size - printed, | ||
2147 | ", Thread: %s", | ||
2148 | (thread->comm_set ? thread__comm_str(thread) : "")); | ||
2149 | } | ||
2150 | } | ||
1588 | if (dso) | 2151 | if (dso) |
1589 | printed += scnprintf(bf + printed, size - printed, | 2152 | printed += scnprintf(bf + printed, size - printed, |
1590 | ", DSO: %s", dso->short_name); | 2153 | ", DSO: %s", dso->short_name); |
@@ -1759,15 +2322,24 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act) | |||
1759 | { | 2322 | { |
1760 | struct thread *thread = act->thread; | 2323 | struct thread *thread = act->thread; |
1761 | 2324 | ||
2325 | if ((!sort__has_thread && !sort__has_comm) || thread == NULL) | ||
2326 | return 0; | ||
2327 | |||
1762 | if (browser->hists->thread_filter) { | 2328 | if (browser->hists->thread_filter) { |
1763 | pstack__remove(browser->pstack, &browser->hists->thread_filter); | 2329 | pstack__remove(browser->pstack, &browser->hists->thread_filter); |
1764 | perf_hpp__set_elide(HISTC_THREAD, false); | 2330 | perf_hpp__set_elide(HISTC_THREAD, false); |
1765 | thread__zput(browser->hists->thread_filter); | 2331 | thread__zput(browser->hists->thread_filter); |
1766 | ui_helpline__pop(); | 2332 | ui_helpline__pop(); |
1767 | } else { | 2333 | } else { |
1768 | ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", | 2334 | if (sort__has_thread) { |
1769 | thread->comm_set ? thread__comm_str(thread) : "", | 2335 | ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", |
1770 | thread->tid); | 2336 | thread->comm_set ? thread__comm_str(thread) : "", |
2337 | thread->tid); | ||
2338 | } else { | ||
2339 | ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"", | ||
2340 | thread->comm_set ? thread__comm_str(thread) : ""); | ||
2341 | } | ||
2342 | |||
1771 | browser->hists->thread_filter = thread__get(thread); | 2343 | browser->hists->thread_filter = thread__get(thread); |
1772 | perf_hpp__set_elide(HISTC_THREAD, false); | 2344 | perf_hpp__set_elide(HISTC_THREAD, false); |
1773 | pstack__push(browser->pstack, &browser->hists->thread_filter); | 2345 | pstack__push(browser->pstack, &browser->hists->thread_filter); |
@@ -1782,13 +2354,22 @@ static int | |||
1782 | add_thread_opt(struct hist_browser *browser, struct popup_action *act, | 2354 | add_thread_opt(struct hist_browser *browser, struct popup_action *act, |
1783 | char **optstr, struct thread *thread) | 2355 | char **optstr, struct thread *thread) |
1784 | { | 2356 | { |
1785 | if (thread == NULL) | 2357 | int ret; |
2358 | |||
2359 | if ((!sort__has_thread && !sort__has_comm) || thread == NULL) | ||
1786 | return 0; | 2360 | return 0; |
1787 | 2361 | ||
1788 | if (asprintf(optstr, "Zoom %s %s(%d) thread", | 2362 | if (sort__has_thread) { |
1789 | browser->hists->thread_filter ? "out of" : "into", | 2363 | ret = asprintf(optstr, "Zoom %s %s(%d) thread", |
1790 | thread->comm_set ? thread__comm_str(thread) : "", | 2364 | browser->hists->thread_filter ? "out of" : "into", |
1791 | thread->tid) < 0) | 2365 | thread->comm_set ? thread__comm_str(thread) : "", |
2366 | thread->tid); | ||
2367 | } else { | ||
2368 | ret = asprintf(optstr, "Zoom %s %s thread", | ||
2369 | browser->hists->thread_filter ? "out of" : "into", | ||
2370 | thread->comm_set ? thread__comm_str(thread) : ""); | ||
2371 | } | ||
2372 | if (ret < 0) | ||
1792 | return 0; | 2373 | return 0; |
1793 | 2374 | ||
1794 | act->thread = thread; | 2375 | act->thread = thread; |
@@ -1801,6 +2382,9 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act) | |||
1801 | { | 2382 | { |
1802 | struct map *map = act->ms.map; | 2383 | struct map *map = act->ms.map; |
1803 | 2384 | ||
2385 | if (!sort__has_dso || map == NULL) | ||
2386 | return 0; | ||
2387 | |||
1804 | if (browser->hists->dso_filter) { | 2388 | if (browser->hists->dso_filter) { |
1805 | pstack__remove(browser->pstack, &browser->hists->dso_filter); | 2389 | pstack__remove(browser->pstack, &browser->hists->dso_filter); |
1806 | perf_hpp__set_elide(HISTC_DSO, false); | 2390 | perf_hpp__set_elide(HISTC_DSO, false); |
@@ -1825,7 +2409,7 @@ static int | |||
1825 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, | 2409 | add_dso_opt(struct hist_browser *browser, struct popup_action *act, |
1826 | char **optstr, struct map *map) | 2410 | char **optstr, struct map *map) |
1827 | { | 2411 | { |
1828 | if (map == NULL) | 2412 | if (!sort__has_dso || map == NULL) |
1829 | return 0; | 2413 | return 0; |
1830 | 2414 | ||
1831 | if (asprintf(optstr, "Zoom %s %s DSO", | 2415 | if (asprintf(optstr, "Zoom %s %s DSO", |
@@ -1850,7 +2434,7 @@ static int | |||
1850 | add_map_opt(struct hist_browser *browser __maybe_unused, | 2434 | add_map_opt(struct hist_browser *browser __maybe_unused, |
1851 | struct popup_action *act, char **optstr, struct map *map) | 2435 | struct popup_action *act, char **optstr, struct map *map) |
1852 | { | 2436 | { |
1853 | if (map == NULL) | 2437 | if (!sort__has_dso || map == NULL) |
1854 | return 0; | 2438 | return 0; |
1855 | 2439 | ||
1856 | if (asprintf(optstr, "Browse map details") < 0) | 2440 | if (asprintf(optstr, "Browse map details") < 0) |
@@ -1952,6 +2536,9 @@ add_exit_opt(struct hist_browser *browser __maybe_unused, | |||
1952 | static int | 2536 | static int |
1953 | do_zoom_socket(struct hist_browser *browser, struct popup_action *act) | 2537 | do_zoom_socket(struct hist_browser *browser, struct popup_action *act) |
1954 | { | 2538 | { |
2539 | if (!sort__has_socket || act->socket < 0) | ||
2540 | return 0; | ||
2541 | |||
1955 | if (browser->hists->socket_filter > -1) { | 2542 | if (browser->hists->socket_filter > -1) { |
1956 | pstack__remove(browser->pstack, &browser->hists->socket_filter); | 2543 | pstack__remove(browser->pstack, &browser->hists->socket_filter); |
1957 | browser->hists->socket_filter = -1; | 2544 | browser->hists->socket_filter = -1; |
@@ -1971,7 +2558,7 @@ static int | |||
1971 | add_socket_opt(struct hist_browser *browser, struct popup_action *act, | 2558 | add_socket_opt(struct hist_browser *browser, struct popup_action *act, |
1972 | char **optstr, int socket_id) | 2559 | char **optstr, int socket_id) |
1973 | { | 2560 | { |
1974 | if (socket_id < 0) | 2561 | if (!sort__has_socket || socket_id < 0) |
1975 | return 0; | 2562 | return 0; |
1976 | 2563 | ||
1977 | if (asprintf(optstr, "Zoom %s Processor Socket %d", | 2564 | if (asprintf(optstr, "Zoom %s Processor Socket %d", |
@@ -1989,17 +2576,60 @@ static void hist_browser__update_nr_entries(struct hist_browser *hb) | |||
1989 | u64 nr_entries = 0; | 2576 | u64 nr_entries = 0; |
1990 | struct rb_node *nd = rb_first(&hb->hists->entries); | 2577 | struct rb_node *nd = rb_first(&hb->hists->entries); |
1991 | 2578 | ||
1992 | if (hb->min_pcnt == 0) { | 2579 | if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { |
1993 | hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; | 2580 | hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; |
1994 | return; | 2581 | return; |
1995 | } | 2582 | } |
1996 | 2583 | ||
1997 | while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { | 2584 | while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { |
1998 | nr_entries++; | 2585 | nr_entries++; |
1999 | nd = rb_next(nd); | 2586 | nd = rb_hierarchy_next(nd); |
2000 | } | 2587 | } |
2001 | 2588 | ||
2002 | hb->nr_non_filtered_entries = nr_entries; | 2589 | hb->nr_non_filtered_entries = nr_entries; |
2590 | hb->nr_hierarchy_entries = nr_entries; | ||
2591 | } | ||
2592 | |||
2593 | static void hist_browser__update_percent_limit(struct hist_browser *hb, | ||
2594 | double percent) | ||
2595 | { | ||
2596 | struct hist_entry *he; | ||
2597 | struct rb_node *nd = rb_first(&hb->hists->entries); | ||
2598 | u64 total = hists__total_period(hb->hists); | ||
2599 | u64 min_callchain_hits = total * (percent / 100); | ||
2600 | |||
2601 | hb->min_pcnt = callchain_param.min_percent = percent; | ||
2602 | |||
2603 | while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { | ||
2604 | he = rb_entry(nd, struct hist_entry, rb_node); | ||
2605 | |||
2606 | if (he->has_no_entry) { | ||
2607 | he->has_no_entry = false; | ||
2608 | he->nr_rows = 0; | ||
2609 | } | ||
2610 | |||
2611 | if (!he->leaf || !symbol_conf.use_callchain) | ||
2612 | goto next; | ||
2613 | |||
2614 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
2615 | total = he->stat.period; | ||
2616 | |||
2617 | if (symbol_conf.cumulate_callchain) | ||
2618 | total = he->stat_acc->period; | ||
2619 | |||
2620 | min_callchain_hits = total * (percent / 100); | ||
2621 | } | ||
2622 | |||
2623 | callchain_param.sort(&he->sorted_chain, he->callchain, | ||
2624 | min_callchain_hits, &callchain_param); | ||
2625 | |||
2626 | next: | ||
2627 | nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); | ||
2628 | |||
2629 | /* force to re-evaluate folding state of callchains */ | ||
2630 | he->init_have_children = false; | ||
2631 | hist_entry__set_folding(he, hb, false); | ||
2632 | } | ||
2003 | } | 2633 | } |
2004 | 2634 | ||
2005 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 2635 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
@@ -2037,6 +2667,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
2037 | "E Expand all callchains\n" \ | 2667 | "E Expand all callchains\n" \ |
2038 | "F Toggle percentage of filtered entries\n" \ | 2668 | "F Toggle percentage of filtered entries\n" \ |
2039 | "H Display column headers\n" \ | 2669 | "H Display column headers\n" \ |
2670 | "L Change percent limit\n" \ | ||
2040 | "m Display context menu\n" \ | 2671 | "m Display context menu\n" \ |
2041 | "S Zoom into current Processor Socket\n" \ | 2672 | "S Zoom into current Processor Socket\n" \ |
2042 | 2673 | ||
@@ -2077,7 +2708,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
2077 | memset(options, 0, sizeof(options)); | 2708 | memset(options, 0, sizeof(options)); |
2078 | memset(actions, 0, sizeof(actions)); | 2709 | memset(actions, 0, sizeof(actions)); |
2079 | 2710 | ||
2080 | perf_hpp__for_each_format(fmt) { | 2711 | hists__for_each_format(browser->hists, fmt) { |
2081 | perf_hpp__reset_width(fmt, hists); | 2712 | perf_hpp__reset_width(fmt, hists); |
2082 | /* | 2713 | /* |
2083 | * This is done just once, and activates the horizontal scrolling | 2714 | * This is done just once, and activates the horizontal scrolling |
@@ -2192,6 +2823,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
2192 | top->zero = !top->zero; | 2823 | top->zero = !top->zero; |
2193 | } | 2824 | } |
2194 | continue; | 2825 | continue; |
2826 | case 'L': | ||
2827 | if (ui_browser__input_window("Percent Limit", | ||
2828 | "Please enter the value you want to hide entries under that percent.", | ||
2829 | buf, "ENTER: OK, ESC: Cancel", | ||
2830 | delay_secs * 2) == K_ENTER) { | ||
2831 | char *end; | ||
2832 | double new_percent = strtod(buf, &end); | ||
2833 | |||
2834 | if (new_percent < 0 || new_percent > 100) { | ||
2835 | ui_browser__warning(&browser->b, delay_secs * 2, | ||
2836 | "Invalid percent: %.2f", new_percent); | ||
2837 | continue; | ||
2838 | } | ||
2839 | |||
2840 | hist_browser__update_percent_limit(browser, new_percent); | ||
2841 | hist_browser__reset(browser); | ||
2842 | } | ||
2843 | continue; | ||
2195 | case K_F1: | 2844 | case K_F1: |
2196 | case 'h': | 2845 | case 'h': |
2197 | case '?': | 2846 | case '?': |
@@ -2263,10 +2912,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
2263 | continue; | 2912 | continue; |
2264 | } | 2913 | } |
2265 | 2914 | ||
2266 | if (!sort__has_sym) | 2915 | if (!sort__has_sym || browser->selection == NULL) |
2267 | goto add_exit_option; | ||
2268 | |||
2269 | if (browser->selection == NULL) | ||
2270 | goto skip_annotation; | 2916 | goto skip_annotation; |
2271 | 2917 | ||
2272 | if (sort__mode == SORT_MODE__BRANCH) { | 2918 | if (sort__mode == SORT_MODE__BRANCH) { |
@@ -2306,11 +2952,16 @@ skip_annotation: | |||
2306 | &options[nr_options], | 2952 | &options[nr_options], |
2307 | socked_id); | 2953 | socked_id); |
2308 | /* perf script support */ | 2954 | /* perf script support */ |
2955 | if (!is_report_browser(hbt)) | ||
2956 | goto skip_scripting; | ||
2957 | |||
2309 | if (browser->he_selection) { | 2958 | if (browser->he_selection) { |
2310 | nr_options += add_script_opt(browser, | 2959 | if (sort__has_thread && thread) { |
2311 | &actions[nr_options], | 2960 | nr_options += add_script_opt(browser, |
2312 | &options[nr_options], | 2961 | &actions[nr_options], |
2313 | thread, NULL); | 2962 | &options[nr_options], |
2963 | thread, NULL); | ||
2964 | } | ||
2314 | /* | 2965 | /* |
2315 | * Note that browser->selection != NULL | 2966 | * Note that browser->selection != NULL |
2316 | * when browser->he_selection is not NULL, | 2967 | * when browser->he_selection is not NULL, |
@@ -2320,16 +2971,18 @@ skip_annotation: | |||
2320 | * | 2971 | * |
2321 | * See hist_browser__show_entry. | 2972 | * See hist_browser__show_entry. |
2322 | */ | 2973 | */ |
2323 | nr_options += add_script_opt(browser, | 2974 | if (sort__has_sym && browser->selection->sym) { |
2324 | &actions[nr_options], | 2975 | nr_options += add_script_opt(browser, |
2325 | &options[nr_options], | 2976 | &actions[nr_options], |
2326 | NULL, browser->selection->sym); | 2977 | &options[nr_options], |
2978 | NULL, browser->selection->sym); | ||
2979 | } | ||
2327 | } | 2980 | } |
2328 | nr_options += add_script_opt(browser, &actions[nr_options], | 2981 | nr_options += add_script_opt(browser, &actions[nr_options], |
2329 | &options[nr_options], NULL, NULL); | 2982 | &options[nr_options], NULL, NULL); |
2330 | nr_options += add_switch_opt(browser, &actions[nr_options], | 2983 | nr_options += add_switch_opt(browser, &actions[nr_options], |
2331 | &options[nr_options]); | 2984 | &options[nr_options]); |
2332 | add_exit_option: | 2985 | skip_scripting: |
2333 | nr_options += add_exit_opt(browser, &actions[nr_options], | 2986 | nr_options += add_exit_opt(browser, &actions[nr_options], |
2334 | &options[nr_options]); | 2987 | &options[nr_options]); |
2335 | 2988 | ||
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 0f8dcfdfb10f..2aa45b606fa4 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -55,7 +55,7 @@ static u64 he_get_acc_##_field(struct hist_entry *he) \ | |||
55 | return he->stat_acc->_field; \ | 55 | return he->stat_acc->_field; \ |
56 | } \ | 56 | } \ |
57 | \ | 57 | \ |
58 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 58 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
59 | struct perf_hpp *hpp, \ | 59 | struct perf_hpp *hpp, \ |
60 | struct hist_entry *he) \ | 60 | struct hist_entry *he) \ |
61 | { \ | 61 | { \ |
@@ -306,7 +306,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
306 | 306 | ||
307 | nr_cols = 0; | 307 | nr_cols = 0; |
308 | 308 | ||
309 | perf_hpp__for_each_format(fmt) | 309 | hists__for_each_format(hists, fmt) |
310 | col_types[nr_cols++] = G_TYPE_STRING; | 310 | col_types[nr_cols++] = G_TYPE_STRING; |
311 | 311 | ||
312 | store = gtk_tree_store_newv(nr_cols, col_types); | 312 | store = gtk_tree_store_newv(nr_cols, col_types); |
@@ -317,7 +317,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
317 | 317 | ||
318 | col_idx = 0; | 318 | col_idx = 0; |
319 | 319 | ||
320 | perf_hpp__for_each_format(fmt) { | 320 | hists__for_each_format(hists, fmt) { |
321 | if (perf_hpp__should_skip(fmt, hists)) | 321 | if (perf_hpp__should_skip(fmt, hists)) |
322 | continue; | 322 | continue; |
323 | 323 | ||
@@ -367,7 +367,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
367 | 367 | ||
368 | col_idx = 0; | 368 | col_idx = 0; |
369 | 369 | ||
370 | perf_hpp__for_each_format(fmt) { | 370 | hists__for_each_format(hists, fmt) { |
371 | if (perf_hpp__should_skip(fmt, h->hists)) | 371 | if (perf_hpp__should_skip(fmt, h->hists)) |
372 | continue; | 372 | continue; |
373 | 373 | ||
@@ -396,6 +396,194 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
396 | gtk_container_add(GTK_CONTAINER(window), view); | 396 | gtk_container_add(GTK_CONTAINER(window), view); |
397 | } | 397 | } |
398 | 398 | ||
399 | static void perf_gtk__add_hierarchy_entries(struct hists *hists, | ||
400 | struct rb_root *root, | ||
401 | GtkTreeStore *store, | ||
402 | GtkTreeIter *parent, | ||
403 | struct perf_hpp *hpp, | ||
404 | float min_pcnt) | ||
405 | { | ||
406 | int col_idx = 0; | ||
407 | struct rb_node *node; | ||
408 | struct hist_entry *he; | ||
409 | struct perf_hpp_fmt *fmt; | ||
410 | struct perf_hpp_list_node *fmt_node; | ||
411 | u64 total = hists__total_period(hists); | ||
412 | int size; | ||
413 | |||
414 | for (node = rb_first(root); node; node = rb_next(node)) { | ||
415 | GtkTreeIter iter; | ||
416 | float percent; | ||
417 | char *bf; | ||
418 | |||
419 | he = rb_entry(node, struct hist_entry, rb_node); | ||
420 | if (he->filtered) | ||
421 | continue; | ||
422 | |||
423 | percent = hist_entry__get_percent_limit(he); | ||
424 | if (percent < min_pcnt) | ||
425 | continue; | ||
426 | |||
427 | gtk_tree_store_append(store, &iter, parent); | ||
428 | |||
429 | col_idx = 0; | ||
430 | |||
431 | /* the first hpp_list_node is for overhead columns */ | ||
432 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
433 | struct perf_hpp_list_node, list); | ||
434 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
435 | if (fmt->color) | ||
436 | fmt->color(fmt, hpp, he); | ||
437 | else | ||
438 | fmt->entry(fmt, hpp, he); | ||
439 | |||
440 | gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1); | ||
441 | } | ||
442 | |||
443 | bf = hpp->buf; | ||
444 | size = hpp->size; | ||
445 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
446 | int ret; | ||
447 | |||
448 | if (fmt->color) | ||
449 | ret = fmt->color(fmt, hpp, he); | ||
450 | else | ||
451 | ret = fmt->entry(fmt, hpp, he); | ||
452 | |||
453 | snprintf(hpp->buf + ret, hpp->size - ret, " "); | ||
454 | advance_hpp(hpp, ret + 2); | ||
455 | } | ||
456 | |||
457 | gtk_tree_store_set(store, &iter, col_idx, ltrim(rtrim(bf)), -1); | ||
458 | |||
459 | if (!he->leaf) { | ||
460 | hpp->buf = bf; | ||
461 | hpp->size = size; | ||
462 | |||
463 | perf_gtk__add_hierarchy_entries(hists, &he->hroot_out, | ||
464 | store, &iter, hpp, | ||
465 | min_pcnt); | ||
466 | |||
467 | if (!hist_entry__has_hierarchy_children(he, min_pcnt)) { | ||
468 | char buf[32]; | ||
469 | GtkTreeIter child; | ||
470 | |||
471 | snprintf(buf, sizeof(buf), "no entry >= %.2f%%", | ||
472 | min_pcnt); | ||
473 | |||
474 | gtk_tree_store_append(store, &child, &iter); | ||
475 | gtk_tree_store_set(store, &child, col_idx, buf, -1); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | if (symbol_conf.use_callchain && he->leaf) { | ||
480 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
481 | total = symbol_conf.cumulate_callchain ? | ||
482 | he->stat_acc->period : he->stat.period; | ||
483 | |||
484 | perf_gtk__add_callchain(&he->sorted_chain, store, &iter, | ||
485 | col_idx, total); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | } | ||
490 | |||
491 | static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists, | ||
492 | float min_pcnt) | ||
493 | { | ||
494 | struct perf_hpp_fmt *fmt; | ||
495 | struct perf_hpp_list_node *fmt_node; | ||
496 | GType col_types[MAX_COLUMNS]; | ||
497 | GtkCellRenderer *renderer; | ||
498 | GtkTreeStore *store; | ||
499 | GtkWidget *view; | ||
500 | int col_idx; | ||
501 | int nr_cols = 0; | ||
502 | char s[512]; | ||
503 | char buf[512]; | ||
504 | bool first_node, first_col; | ||
505 | struct perf_hpp hpp = { | ||
506 | .buf = s, | ||
507 | .size = sizeof(s), | ||
508 | }; | ||
509 | |||
510 | hists__for_each_format(hists, fmt) { | ||
511 | if (perf_hpp__is_sort_entry(fmt) || | ||
512 | perf_hpp__is_dynamic_entry(fmt)) | ||
513 | break; | ||
514 | |||
515 | col_types[nr_cols++] = G_TYPE_STRING; | ||
516 | } | ||
517 | col_types[nr_cols++] = G_TYPE_STRING; | ||
518 | |||
519 | store = gtk_tree_store_newv(nr_cols, col_types); | ||
520 | view = gtk_tree_view_new(); | ||
521 | renderer = gtk_cell_renderer_text_new(); | ||
522 | |||
523 | col_idx = 0; | ||
524 | |||
525 | /* the first hpp_list_node is for overhead columns */ | ||
526 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
527 | struct perf_hpp_list_node, list); | ||
528 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
529 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
530 | -1, fmt->name, | ||
531 | renderer, "markup", | ||
532 | col_idx++, NULL); | ||
533 | } | ||
534 | |||
535 | /* construct merged column header since sort keys share single column */ | ||
536 | buf[0] = '\0'; | ||
537 | first_node = true; | ||
538 | list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { | ||
539 | if (!first_node) | ||
540 | strcat(buf, " / "); | ||
541 | first_node = false; | ||
542 | |||
543 | first_col = true; | ||
544 | perf_hpp_list__for_each_format(&fmt_node->hpp ,fmt) { | ||
545 | if (perf_hpp__should_skip(fmt, hists)) | ||
546 | continue; | ||
547 | |||
548 | if (!first_col) | ||
549 | strcat(buf, "+"); | ||
550 | first_col = false; | ||
551 | |||
552 | fmt->header(fmt, &hpp, hists_to_evsel(hists)); | ||
553 | strcat(buf, ltrim(rtrim(hpp.buf))); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
558 | -1, buf, | ||
559 | renderer, "markup", | ||
560 | col_idx++, NULL); | ||
561 | |||
562 | for (col_idx = 0; col_idx < nr_cols; col_idx++) { | ||
563 | GtkTreeViewColumn *column; | ||
564 | |||
565 | column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx); | ||
566 | gtk_tree_view_column_set_resizable(column, TRUE); | ||
567 | |||
568 | if (col_idx == 0) { | ||
569 | gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), | ||
570 | column); | ||
571 | } | ||
572 | } | ||
573 | |||
574 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
575 | g_object_unref(GTK_TREE_MODEL(store)); | ||
576 | |||
577 | perf_gtk__add_hierarchy_entries(hists, &hists->entries, store, | ||
578 | NULL, &hpp, min_pcnt); | ||
579 | |||
580 | gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); | ||
581 | |||
582 | g_signal_connect(view, "row-activated", | ||
583 | G_CALLBACK(on_row_activated), NULL); | ||
584 | gtk_container_add(GTK_CONTAINER(window), view); | ||
585 | } | ||
586 | |||
399 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | 587 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, |
400 | const char *help, | 588 | const char *help, |
401 | struct hist_browser_timer *hbt __maybe_unused, | 589 | struct hist_browser_timer *hbt __maybe_unused, |
@@ -463,7 +651,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | |||
463 | GTK_POLICY_AUTOMATIC, | 651 | GTK_POLICY_AUTOMATIC, |
464 | GTK_POLICY_AUTOMATIC); | 652 | GTK_POLICY_AUTOMATIC); |
465 | 653 | ||
466 | perf_gtk__show_hists(scrolled_window, hists, min_pcnt); | 654 | if (symbol_conf.report_hierarchy) |
655 | perf_gtk__show_hierarchy(scrolled_window, hists, min_pcnt); | ||
656 | else | ||
657 | perf_gtk__show_hists(scrolled_window, hists, min_pcnt); | ||
467 | 658 | ||
468 | tab_label = gtk_label_new(evname); | 659 | tab_label = gtk_label_new(evname); |
469 | 660 | ||
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index bf2a66e254ea..3baeaa6e71b5 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "../util/util.h" | 5 | #include "../util/util.h" |
6 | #include "../util/sort.h" | 6 | #include "../util/sort.h" |
7 | #include "../util/evsel.h" | 7 | #include "../util/evsel.h" |
8 | #include "../util/evlist.h" | ||
8 | 9 | ||
9 | /* hist period print (hpp) functions */ | 10 | /* hist period print (hpp) functions */ |
10 | 11 | ||
@@ -371,7 +372,20 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
371 | return 0; | 372 | return 0; |
372 | } | 373 | } |
373 | 374 | ||
374 | #define HPP__COLOR_PRINT_FNS(_name, _fn) \ | 375 | static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) |
376 | { | ||
377 | return a->header == hpp__header_fn; | ||
378 | } | ||
379 | |||
380 | static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
381 | { | ||
382 | if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b)) | ||
383 | return false; | ||
384 | |||
385 | return a->idx == b->idx; | ||
386 | } | ||
387 | |||
388 | #define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ | ||
375 | { \ | 389 | { \ |
376 | .name = _name, \ | 390 | .name = _name, \ |
377 | .header = hpp__header_fn, \ | 391 | .header = hpp__header_fn, \ |
@@ -381,9 +395,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
381 | .cmp = hpp__nop_cmp, \ | 395 | .cmp = hpp__nop_cmp, \ |
382 | .collapse = hpp__nop_cmp, \ | 396 | .collapse = hpp__nop_cmp, \ |
383 | .sort = hpp__sort_ ## _fn, \ | 397 | .sort = hpp__sort_ ## _fn, \ |
398 | .idx = PERF_HPP__ ## _idx, \ | ||
399 | .equal = hpp__equal, \ | ||
384 | } | 400 | } |
385 | 401 | ||
386 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \ | 402 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \ |
387 | { \ | 403 | { \ |
388 | .name = _name, \ | 404 | .name = _name, \ |
389 | .header = hpp__header_fn, \ | 405 | .header = hpp__header_fn, \ |
@@ -393,9 +409,11 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
393 | .cmp = hpp__nop_cmp, \ | 409 | .cmp = hpp__nop_cmp, \ |
394 | .collapse = hpp__nop_cmp, \ | 410 | .collapse = hpp__nop_cmp, \ |
395 | .sort = hpp__sort_ ## _fn, \ | 411 | .sort = hpp__sort_ ## _fn, \ |
412 | .idx = PERF_HPP__ ## _idx, \ | ||
413 | .equal = hpp__equal, \ | ||
396 | } | 414 | } |
397 | 415 | ||
398 | #define HPP__PRINT_FNS(_name, _fn) \ | 416 | #define HPP__PRINT_FNS(_name, _fn, _idx) \ |
399 | { \ | 417 | { \ |
400 | .name = _name, \ | 418 | .name = _name, \ |
401 | .header = hpp__header_fn, \ | 419 | .header = hpp__header_fn, \ |
@@ -404,22 +422,25 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, | |||
404 | .cmp = hpp__nop_cmp, \ | 422 | .cmp = hpp__nop_cmp, \ |
405 | .collapse = hpp__nop_cmp, \ | 423 | .collapse = hpp__nop_cmp, \ |
406 | .sort = hpp__sort_ ## _fn, \ | 424 | .sort = hpp__sort_ ## _fn, \ |
425 | .idx = PERF_HPP__ ## _idx, \ | ||
426 | .equal = hpp__equal, \ | ||
407 | } | 427 | } |
408 | 428 | ||
409 | struct perf_hpp_fmt perf_hpp__format[] = { | 429 | struct perf_hpp_fmt perf_hpp__format[] = { |
410 | HPP__COLOR_PRINT_FNS("Overhead", overhead), | 430 | HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), |
411 | HPP__COLOR_PRINT_FNS("sys", overhead_sys), | 431 | HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS), |
412 | HPP__COLOR_PRINT_FNS("usr", overhead_us), | 432 | HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US), |
413 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys), | 433 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS), |
414 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us), | 434 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US), |
415 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc), | 435 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC), |
416 | HPP__PRINT_FNS("Samples", samples), | 436 | HPP__PRINT_FNS("Samples", samples, SAMPLES), |
417 | HPP__PRINT_FNS("Period", period) | 437 | HPP__PRINT_FNS("Period", period, PERIOD) |
418 | }; | 438 | }; |
419 | 439 | ||
420 | LIST_HEAD(perf_hpp__list); | 440 | struct perf_hpp_list perf_hpp_list = { |
421 | LIST_HEAD(perf_hpp__sort_list); | 441 | .fields = LIST_HEAD_INIT(perf_hpp_list.fields), |
422 | 442 | .sorts = LIST_HEAD_INIT(perf_hpp_list.sorts), | |
443 | }; | ||
423 | 444 | ||
424 | #undef HPP__COLOR_PRINT_FNS | 445 | #undef HPP__COLOR_PRINT_FNS |
425 | #undef HPP__COLOR_ACC_PRINT_FNS | 446 | #undef HPP__COLOR_ACC_PRINT_FNS |
@@ -485,63 +506,60 @@ void perf_hpp__init(void) | |||
485 | hpp_dimension__add_output(PERF_HPP__PERIOD); | 506 | hpp_dimension__add_output(PERF_HPP__PERIOD); |
486 | } | 507 | } |
487 | 508 | ||
488 | void perf_hpp__column_register(struct perf_hpp_fmt *format) | 509 | void perf_hpp_list__column_register(struct perf_hpp_list *list, |
510 | struct perf_hpp_fmt *format) | ||
489 | { | 511 | { |
490 | list_add_tail(&format->list, &perf_hpp__list); | 512 | list_add_tail(&format->list, &list->fields); |
491 | } | 513 | } |
492 | 514 | ||
493 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format) | 515 | void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, |
516 | struct perf_hpp_fmt *format) | ||
494 | { | 517 | { |
495 | list_del(&format->list); | 518 | list_add_tail(&format->sort_list, &list->sorts); |
496 | } | 519 | } |
497 | 520 | ||
498 | void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) | 521 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format) |
499 | { | ||
500 | list_add_tail(&format->sort_list, &perf_hpp__sort_list); | ||
501 | } | ||
502 | |||
503 | void perf_hpp__column_enable(unsigned col) | ||
504 | { | ||
505 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | ||
506 | perf_hpp__column_register(&perf_hpp__format[col]); | ||
507 | } | ||
508 | |||
509 | void perf_hpp__column_disable(unsigned col) | ||
510 | { | 522 | { |
511 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 523 | list_del(&format->list); |
512 | perf_hpp__column_unregister(&perf_hpp__format[col]); | ||
513 | } | 524 | } |
514 | 525 | ||
515 | void perf_hpp__cancel_cumulate(void) | 526 | void perf_hpp__cancel_cumulate(void) |
516 | { | 527 | { |
528 | struct perf_hpp_fmt *fmt, *acc, *ovh, *tmp; | ||
529 | |||
517 | if (is_strict_order(field_order)) | 530 | if (is_strict_order(field_order)) |
518 | return; | 531 | return; |
519 | 532 | ||
520 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); | 533 | ovh = &perf_hpp__format[PERF_HPP__OVERHEAD]; |
521 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead"; | 534 | acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC]; |
535 | |||
536 | perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { | ||
537 | if (acc->equal(acc, fmt)) { | ||
538 | perf_hpp__column_unregister(fmt); | ||
539 | continue; | ||
540 | } | ||
541 | |||
542 | if (ovh->equal(ovh, fmt)) | ||
543 | fmt->name = "Overhead"; | ||
544 | } | ||
522 | } | 545 | } |
523 | 546 | ||
524 | void perf_hpp__setup_output_field(void) | 547 | static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) |
548 | { | ||
549 | return a->equal && a->equal(a, b); | ||
550 | } | ||
551 | |||
552 | void perf_hpp__setup_output_field(struct perf_hpp_list *list) | ||
525 | { | 553 | { |
526 | struct perf_hpp_fmt *fmt; | 554 | struct perf_hpp_fmt *fmt; |
527 | 555 | ||
528 | /* append sort keys to output field */ | 556 | /* append sort keys to output field */ |
529 | perf_hpp__for_each_sort_list(fmt) { | 557 | perf_hpp_list__for_each_sort_list(list, fmt) { |
530 | if (!list_empty(&fmt->list)) | 558 | struct perf_hpp_fmt *pos; |
531 | continue; | ||
532 | |||
533 | /* | ||
534 | * sort entry fields are dynamically created, | ||
535 | * so they can share a same sort key even though | ||
536 | * the list is empty. | ||
537 | */ | ||
538 | if (perf_hpp__is_sort_entry(fmt)) { | ||
539 | struct perf_hpp_fmt *pos; | ||
540 | 559 | ||
541 | perf_hpp__for_each_format(pos) { | 560 | perf_hpp_list__for_each_format(list, pos) { |
542 | if (perf_hpp__same_sort_entry(pos, fmt)) | 561 | if (fmt_equal(fmt, pos)) |
543 | goto next; | 562 | goto next; |
544 | } | ||
545 | } | 563 | } |
546 | 564 | ||
547 | perf_hpp__column_register(fmt); | 565 | perf_hpp__column_register(fmt); |
@@ -550,27 +568,17 @@ next: | |||
550 | } | 568 | } |
551 | } | 569 | } |
552 | 570 | ||
553 | void perf_hpp__append_sort_keys(void) | 571 | void perf_hpp__append_sort_keys(struct perf_hpp_list *list) |
554 | { | 572 | { |
555 | struct perf_hpp_fmt *fmt; | 573 | struct perf_hpp_fmt *fmt; |
556 | 574 | ||
557 | /* append output fields to sort keys */ | 575 | /* append output fields to sort keys */ |
558 | perf_hpp__for_each_format(fmt) { | 576 | perf_hpp_list__for_each_format(list, fmt) { |
559 | if (!list_empty(&fmt->sort_list)) | 577 | struct perf_hpp_fmt *pos; |
560 | continue; | ||
561 | |||
562 | /* | ||
563 | * sort entry fields are dynamically created, | ||
564 | * so they can share a same sort key even though | ||
565 | * the list is empty. | ||
566 | */ | ||
567 | if (perf_hpp__is_sort_entry(fmt)) { | ||
568 | struct perf_hpp_fmt *pos; | ||
569 | 578 | ||
570 | perf_hpp__for_each_sort_list(pos) { | 579 | perf_hpp_list__for_each_sort_list(list, pos) { |
571 | if (perf_hpp__same_sort_entry(pos, fmt)) | 580 | if (fmt_equal(fmt, pos)) |
572 | goto next; | 581 | goto next; |
573 | } | ||
574 | } | 582 | } |
575 | 583 | ||
576 | perf_hpp__register_sort_field(fmt); | 584 | perf_hpp__register_sort_field(fmt); |
@@ -579,20 +587,29 @@ next: | |||
579 | } | 587 | } |
580 | } | 588 | } |
581 | 589 | ||
582 | void perf_hpp__reset_output_field(void) | 590 | |
591 | static void fmt_free(struct perf_hpp_fmt *fmt) | ||
592 | { | ||
593 | if (fmt->free) | ||
594 | fmt->free(fmt); | ||
595 | } | ||
596 | |||
597 | void perf_hpp__reset_output_field(struct perf_hpp_list *list) | ||
583 | { | 598 | { |
584 | struct perf_hpp_fmt *fmt, *tmp; | 599 | struct perf_hpp_fmt *fmt, *tmp; |
585 | 600 | ||
586 | /* reset output fields */ | 601 | /* reset output fields */ |
587 | perf_hpp__for_each_format_safe(fmt, tmp) { | 602 | perf_hpp_list__for_each_format_safe(list, fmt, tmp) { |
588 | list_del_init(&fmt->list); | 603 | list_del_init(&fmt->list); |
589 | list_del_init(&fmt->sort_list); | 604 | list_del_init(&fmt->sort_list); |
605 | fmt_free(fmt); | ||
590 | } | 606 | } |
591 | 607 | ||
592 | /* reset sort keys */ | 608 | /* reset sort keys */ |
593 | perf_hpp__for_each_sort_list_safe(fmt, tmp) { | 609 | perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) { |
594 | list_del_init(&fmt->list); | 610 | list_del_init(&fmt->list); |
595 | list_del_init(&fmt->sort_list); | 611 | list_del_init(&fmt->sort_list); |
612 | fmt_free(fmt); | ||
596 | } | 613 | } |
597 | } | 614 | } |
598 | 615 | ||
@@ -606,7 +623,7 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
606 | bool first = true; | 623 | bool first = true; |
607 | struct perf_hpp dummy_hpp; | 624 | struct perf_hpp dummy_hpp; |
608 | 625 | ||
609 | perf_hpp__for_each_format(fmt) { | 626 | hists__for_each_format(hists, fmt) { |
610 | if (perf_hpp__should_skip(fmt, hists)) | 627 | if (perf_hpp__should_skip(fmt, hists)) |
611 | continue; | 628 | continue; |
612 | 629 | ||
@@ -624,22 +641,39 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
624 | return ret; | 641 | return ret; |
625 | } | 642 | } |
626 | 643 | ||
627 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 644 | unsigned int hists__overhead_width(struct hists *hists) |
628 | { | 645 | { |
629 | int idx; | 646 | struct perf_hpp_fmt *fmt; |
630 | 647 | int ret = 0; | |
631 | if (perf_hpp__is_sort_entry(fmt)) | 648 | bool first = true; |
632 | return perf_hpp__reset_sort_width(fmt, hists); | 649 | struct perf_hpp dummy_hpp; |
633 | 650 | ||
634 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | 651 | hists__for_each_format(hists, fmt) { |
635 | if (fmt == &perf_hpp__format[idx]) | 652 | if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) |
636 | break; | 653 | break; |
654 | |||
655 | if (first) | ||
656 | first = false; | ||
657 | else | ||
658 | ret += 2; | ||
659 | |||
660 | ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); | ||
637 | } | 661 | } |
638 | 662 | ||
639 | if (idx == PERF_HPP__MAX_INDEX) | 663 | return ret; |
664 | } | ||
665 | |||
666 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
667 | { | ||
668 | if (perf_hpp__is_sort_entry(fmt)) | ||
669 | return perf_hpp__reset_sort_width(fmt, hists); | ||
670 | |||
671 | if (perf_hpp__is_dynamic_entry(fmt)) | ||
640 | return; | 672 | return; |
641 | 673 | ||
642 | switch (idx) { | 674 | BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX); |
675 | |||
676 | switch (fmt->idx) { | ||
643 | case PERF_HPP__OVERHEAD: | 677 | case PERF_HPP__OVERHEAD: |
644 | case PERF_HPP__OVERHEAD_SYS: | 678 | case PERF_HPP__OVERHEAD_SYS: |
645 | case PERF_HPP__OVERHEAD_US: | 679 | case PERF_HPP__OVERHEAD_US: |
@@ -667,7 +701,7 @@ void perf_hpp__set_user_width(const char *width_list_str) | |||
667 | struct perf_hpp_fmt *fmt; | 701 | struct perf_hpp_fmt *fmt; |
668 | const char *ptr = width_list_str; | 702 | const char *ptr = width_list_str; |
669 | 703 | ||
670 | perf_hpp__for_each_format(fmt) { | 704 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
671 | char *p; | 705 | char *p; |
672 | 706 | ||
673 | int len = strtol(ptr, &p, 10); | 707 | int len = strtol(ptr, &p, 10); |
@@ -679,3 +713,71 @@ void perf_hpp__set_user_width(const char *width_list_str) | |||
679 | break; | 713 | break; |
680 | } | 714 | } |
681 | } | 715 | } |
716 | |||
717 | static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt) | ||
718 | { | ||
719 | struct perf_hpp_list_node *node = NULL; | ||
720 | struct perf_hpp_fmt *fmt_copy; | ||
721 | bool found = false; | ||
722 | bool skip = perf_hpp__should_skip(fmt, hists); | ||
723 | |||
724 | list_for_each_entry(node, &hists->hpp_formats, list) { | ||
725 | if (node->level == fmt->level) { | ||
726 | found = true; | ||
727 | break; | ||
728 | } | ||
729 | } | ||
730 | |||
731 | if (!found) { | ||
732 | node = malloc(sizeof(*node)); | ||
733 | if (node == NULL) | ||
734 | return -1; | ||
735 | |||
736 | node->skip = skip; | ||
737 | node->level = fmt->level; | ||
738 | perf_hpp_list__init(&node->hpp); | ||
739 | |||
740 | hists->nr_hpp_node++; | ||
741 | list_add_tail(&node->list, &hists->hpp_formats); | ||
742 | } | ||
743 | |||
744 | fmt_copy = perf_hpp_fmt__dup(fmt); | ||
745 | if (fmt_copy == NULL) | ||
746 | return -1; | ||
747 | |||
748 | if (!skip) | ||
749 | node->skip = false; | ||
750 | |||
751 | list_add_tail(&fmt_copy->list, &node->hpp.fields); | ||
752 | list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts); | ||
753 | |||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, | ||
758 | struct perf_evlist *evlist) | ||
759 | { | ||
760 | struct perf_evsel *evsel; | ||
761 | struct perf_hpp_fmt *fmt; | ||
762 | struct hists *hists; | ||
763 | int ret; | ||
764 | |||
765 | if (!symbol_conf.report_hierarchy) | ||
766 | return 0; | ||
767 | |||
768 | evlist__for_each(evlist, evsel) { | ||
769 | hists = evsel__hists(evsel); | ||
770 | |||
771 | perf_hpp_list__for_each_sort_list(list, fmt) { | ||
772 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
773 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
774 | continue; | ||
775 | |||
776 | ret = add_hierarchy_fmt(hists, fmt); | ||
777 | if (ret < 0) | ||
778 | return ret; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | return 0; | ||
783 | } | ||
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 387110d50b00..7aff5acf3265 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -165,8 +165,28 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
165 | return ret; | 165 | return ret; |
166 | } | 166 | } |
167 | 167 | ||
168 | /* | ||
169 | * If have one single callchain root, don't bother printing | ||
170 | * its percentage (100 % in fractal mode and the same percentage | ||
171 | * than the hist in graph mode). This also avoid one level of column. | ||
172 | * | ||
173 | * However when percent-limit applied, it's possible that single callchain | ||
174 | * node have different (non-100% in fractal mode) percentage. | ||
175 | */ | ||
176 | static bool need_percent_display(struct rb_node *node, u64 parent_samples) | ||
177 | { | ||
178 | struct callchain_node *cnode; | ||
179 | |||
180 | if (rb_next(node)) | ||
181 | return true; | ||
182 | |||
183 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
184 | return callchain_cumul_hits(cnode) != parent_samples; | ||
185 | } | ||
186 | |||
168 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | 187 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
169 | u64 total_samples, int left_margin) | 188 | u64 total_samples, u64 parent_samples, |
189 | int left_margin) | ||
170 | { | 190 | { |
171 | struct callchain_node *cnode; | 191 | struct callchain_node *cnode; |
172 | struct callchain_list *chain; | 192 | struct callchain_list *chain; |
@@ -177,13 +197,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
177 | int ret = 0; | 197 | int ret = 0; |
178 | char bf[1024]; | 198 | char bf[1024]; |
179 | 199 | ||
180 | /* | ||
181 | * If have one single callchain root, don't bother printing | ||
182 | * its percentage (100 % in fractal mode and the same percentage | ||
183 | * than the hist in graph mode). This also avoid one level of column. | ||
184 | */ | ||
185 | node = rb_first(root); | 200 | node = rb_first(root); |
186 | if (node && !rb_next(node)) { | 201 | if (node && !need_percent_display(node, parent_samples)) { |
187 | cnode = rb_entry(node, struct callchain_node, rb_node); | 202 | cnode = rb_entry(node, struct callchain_node, rb_node); |
188 | list_for_each_entry(chain, &cnode->val, list) { | 203 | list_for_each_entry(chain, &cnode->val, list) { |
189 | /* | 204 | /* |
@@ -213,9 +228,15 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
213 | root = &cnode->rb_root; | 228 | root = &cnode->rb_root; |
214 | } | 229 | } |
215 | 230 | ||
231 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
232 | total_samples = parent_samples; | ||
233 | |||
216 | ret += __callchain__fprintf_graph(fp, root, total_samples, | 234 | ret += __callchain__fprintf_graph(fp, root, total_samples, |
217 | 1, 1, left_margin); | 235 | 1, 1, left_margin); |
218 | ret += fprintf(fp, "\n"); | 236 | if (ret) { |
237 | /* do not add a blank line if it printed nothing */ | ||
238 | ret += fprintf(fp, "\n"); | ||
239 | } | ||
219 | 240 | ||
220 | return ret; | 241 | return ret; |
221 | } | 242 | } |
@@ -323,16 +344,19 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | |||
323 | u64 total_samples, int left_margin, | 344 | u64 total_samples, int left_margin, |
324 | FILE *fp) | 345 | FILE *fp) |
325 | { | 346 | { |
347 | u64 parent_samples = he->stat.period; | ||
348 | |||
349 | if (symbol_conf.cumulate_callchain) | ||
350 | parent_samples = he->stat_acc->period; | ||
351 | |||
326 | switch (callchain_param.mode) { | 352 | switch (callchain_param.mode) { |
327 | case CHAIN_GRAPH_REL: | 353 | case CHAIN_GRAPH_REL: |
328 | return callchain__fprintf_graph(fp, &he->sorted_chain, | 354 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, |
329 | symbol_conf.cumulate_callchain ? | 355 | parent_samples, left_margin); |
330 | he->stat_acc->period : he->stat.period, | ||
331 | left_margin); | ||
332 | break; | 356 | break; |
333 | case CHAIN_GRAPH_ABS: | 357 | case CHAIN_GRAPH_ABS: |
334 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | 358 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, |
335 | left_margin); | 359 | parent_samples, left_margin); |
336 | break; | 360 | break; |
337 | case CHAIN_FLAT: | 361 | case CHAIN_FLAT: |
338 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | 362 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); |
@@ -349,45 +373,66 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | |||
349 | return 0; | 373 | return 0; |
350 | } | 374 | } |
351 | 375 | ||
352 | static size_t hist_entry__callchain_fprintf(struct hist_entry *he, | 376 | static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) |
353 | struct hists *hists, | ||
354 | FILE *fp) | ||
355 | { | 377 | { |
356 | int left_margin = 0; | 378 | const char *sep = symbol_conf.field_sep; |
357 | u64 total_period = hists->stats.total_period; | 379 | struct perf_hpp_fmt *fmt; |
380 | char *start = hpp->buf; | ||
381 | int ret; | ||
382 | bool first = true; | ||
358 | 383 | ||
359 | if (field_order == NULL && (sort_order == NULL || | 384 | if (symbol_conf.exclude_other && !he->parent) |
360 | !prefixcmp(sort_order, "comm"))) { | 385 | return 0; |
361 | struct perf_hpp_fmt *fmt; | ||
362 | 386 | ||
363 | perf_hpp__for_each_format(fmt) { | 387 | hists__for_each_format(he->hists, fmt) { |
364 | if (!perf_hpp__is_sort_entry(fmt)) | 388 | if (perf_hpp__should_skip(fmt, he->hists)) |
365 | continue; | 389 | continue; |
366 | 390 | ||
367 | /* must be 'comm' sort entry */ | 391 | /* |
368 | left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists)); | 392 | * If there's no field_sep, we still need |
369 | left_margin -= thread__comm_len(he->thread); | 393 | * to display initial ' '. |
370 | break; | 394 | */ |
371 | } | 395 | if (!sep || !first) { |
396 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); | ||
397 | advance_hpp(hpp, ret); | ||
398 | } else | ||
399 | first = false; | ||
400 | |||
401 | if (perf_hpp__use_color() && fmt->color) | ||
402 | ret = fmt->color(fmt, hpp, he); | ||
403 | else | ||
404 | ret = fmt->entry(fmt, hpp, he); | ||
405 | |||
406 | ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); | ||
407 | advance_hpp(hpp, ret); | ||
372 | } | 408 | } |
373 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); | 409 | |
410 | return hpp->buf - start; | ||
374 | } | 411 | } |
375 | 412 | ||
376 | static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) | 413 | static int hist_entry__hierarchy_fprintf(struct hist_entry *he, |
414 | struct perf_hpp *hpp, | ||
415 | struct hists *hists, | ||
416 | FILE *fp) | ||
377 | { | 417 | { |
378 | const char *sep = symbol_conf.field_sep; | 418 | const char *sep = symbol_conf.field_sep; |
379 | struct perf_hpp_fmt *fmt; | 419 | struct perf_hpp_fmt *fmt; |
380 | char *start = hpp->buf; | 420 | struct perf_hpp_list_node *fmt_node; |
381 | int ret; | 421 | char *buf = hpp->buf; |
422 | size_t size = hpp->size; | ||
423 | int ret, printed = 0; | ||
382 | bool first = true; | 424 | bool first = true; |
383 | 425 | ||
384 | if (symbol_conf.exclude_other && !he->parent) | 426 | if (symbol_conf.exclude_other && !he->parent) |
385 | return 0; | 427 | return 0; |
386 | 428 | ||
387 | perf_hpp__for_each_format(fmt) { | 429 | ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, ""); |
388 | if (perf_hpp__should_skip(fmt, he->hists)) | 430 | advance_hpp(hpp, ret); |
389 | continue; | ||
390 | 431 | ||
432 | /* the first hpp_list_node is for overhead columns */ | ||
433 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
434 | struct perf_hpp_list_node, list); | ||
435 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
391 | /* | 436 | /* |
392 | * If there's no field_sep, we still need | 437 | * If there's no field_sep, we still need |
393 | * to display initial ' '. | 438 | * to display initial ' '. |
@@ -403,10 +448,47 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) | |||
403 | else | 448 | else |
404 | ret = fmt->entry(fmt, hpp, he); | 449 | ret = fmt->entry(fmt, hpp, he); |
405 | 450 | ||
451 | ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); | ||
406 | advance_hpp(hpp, ret); | 452 | advance_hpp(hpp, ret); |
407 | } | 453 | } |
408 | 454 | ||
409 | return hpp->buf - start; | 455 | if (!sep) |
456 | ret = scnprintf(hpp->buf, hpp->size, "%*s", | ||
457 | (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, ""); | ||
458 | advance_hpp(hpp, ret); | ||
459 | |||
460 | printed += fprintf(fp, "%s", buf); | ||
461 | |||
462 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
463 | hpp->buf = buf; | ||
464 | hpp->size = size; | ||
465 | |||
466 | /* | ||
467 | * No need to call hist_entry__snprintf_alignment() since this | ||
468 | * fmt is always the last column in the hierarchy mode. | ||
469 | */ | ||
470 | if (perf_hpp__use_color() && fmt->color) | ||
471 | fmt->color(fmt, hpp, he); | ||
472 | else | ||
473 | fmt->entry(fmt, hpp, he); | ||
474 | |||
475 | /* | ||
476 | * dynamic entries are right-aligned but we want left-aligned | ||
477 | * in the hierarchy mode | ||
478 | */ | ||
479 | printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf)); | ||
480 | } | ||
481 | printed += putc('\n', fp); | ||
482 | |||
483 | if (symbol_conf.use_callchain && he->leaf) { | ||
484 | u64 total = hists__total_period(hists); | ||
485 | |||
486 | printed += hist_entry_callchain__fprintf(he, total, 0, fp); | ||
487 | goto out; | ||
488 | } | ||
489 | |||
490 | out: | ||
491 | return printed; | ||
410 | } | 492 | } |
411 | 493 | ||
412 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, | 494 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, |
@@ -418,24 +500,134 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, | |||
418 | .buf = bf, | 500 | .buf = bf, |
419 | .size = size, | 501 | .size = size, |
420 | }; | 502 | }; |
503 | u64 total_period = hists->stats.total_period; | ||
421 | 504 | ||
422 | if (size == 0 || size > bfsz) | 505 | if (size == 0 || size > bfsz) |
423 | size = hpp.size = bfsz; | 506 | size = hpp.size = bfsz; |
424 | 507 | ||
508 | if (symbol_conf.report_hierarchy) | ||
509 | return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp); | ||
510 | |||
425 | hist_entry__snprintf(he, &hpp); | 511 | hist_entry__snprintf(he, &hpp); |
426 | 512 | ||
427 | ret = fprintf(fp, "%s\n", bf); | 513 | ret = fprintf(fp, "%s\n", bf); |
428 | 514 | ||
429 | if (symbol_conf.use_callchain) | 515 | if (symbol_conf.use_callchain) |
430 | ret += hist_entry__callchain_fprintf(he, hists, fp); | 516 | ret += hist_entry_callchain__fprintf(he, total_period, 0, fp); |
431 | 517 | ||
432 | return ret; | 518 | return ret; |
433 | } | 519 | } |
434 | 520 | ||
521 | static int print_hierarchy_indent(const char *sep, int indent, | ||
522 | const char *line, FILE *fp) | ||
523 | { | ||
524 | if (sep != NULL || indent < 2) | ||
525 | return 0; | ||
526 | |||
527 | return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line); | ||
528 | } | ||
529 | |||
530 | static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, | ||
531 | const char *sep, FILE *fp) | ||
532 | { | ||
533 | bool first_node, first_col; | ||
534 | int indent; | ||
535 | int depth; | ||
536 | unsigned width = 0; | ||
537 | unsigned header_width = 0; | ||
538 | struct perf_hpp_fmt *fmt; | ||
539 | struct perf_hpp_list_node *fmt_node; | ||
540 | |||
541 | indent = hists->nr_hpp_node; | ||
542 | |||
543 | /* preserve max indent depth for column headers */ | ||
544 | print_hierarchy_indent(sep, indent, spaces, fp); | ||
545 | |||
546 | /* the first hpp_list_node is for overhead columns */ | ||
547 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
548 | struct perf_hpp_list_node, list); | ||
549 | |||
550 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
551 | fmt->header(fmt, hpp, hists_to_evsel(hists)); | ||
552 | fprintf(fp, "%s%s", hpp->buf, sep ?: " "); | ||
553 | } | ||
554 | |||
555 | /* combine sort headers with ' / ' */ | ||
556 | first_node = true; | ||
557 | list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { | ||
558 | if (!first_node) | ||
559 | header_width += fprintf(fp, " / "); | ||
560 | first_node = false; | ||
561 | |||
562 | first_col = true; | ||
563 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
564 | if (perf_hpp__should_skip(fmt, hists)) | ||
565 | continue; | ||
566 | |||
567 | if (!first_col) | ||
568 | header_width += fprintf(fp, "+"); | ||
569 | first_col = false; | ||
570 | |||
571 | fmt->header(fmt, hpp, hists_to_evsel(hists)); | ||
572 | rtrim(hpp->buf); | ||
573 | |||
574 | header_width += fprintf(fp, "%s", ltrim(hpp->buf)); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | fprintf(fp, "\n# "); | ||
579 | |||
580 | /* preserve max indent depth for initial dots */ | ||
581 | print_hierarchy_indent(sep, indent, dots, fp); | ||
582 | |||
583 | /* the first hpp_list_node is for overhead columns */ | ||
584 | fmt_node = list_first_entry(&hists->hpp_formats, | ||
585 | struct perf_hpp_list_node, list); | ||
586 | |||
587 | first_col = true; | ||
588 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
589 | if (!first_col) | ||
590 | fprintf(fp, "%s", sep ?: ".."); | ||
591 | first_col = false; | ||
592 | |||
593 | width = fmt->width(fmt, hpp, hists_to_evsel(hists)); | ||
594 | fprintf(fp, "%.*s", width, dots); | ||
595 | } | ||
596 | |||
597 | depth = 0; | ||
598 | list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { | ||
599 | first_col = true; | ||
600 | width = depth * HIERARCHY_INDENT; | ||
601 | |||
602 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { | ||
603 | if (perf_hpp__should_skip(fmt, hists)) | ||
604 | continue; | ||
605 | |||
606 | if (!first_col) | ||
607 | width++; /* for '+' sign between column header */ | ||
608 | first_col = false; | ||
609 | |||
610 | width += fmt->width(fmt, hpp, hists_to_evsel(hists)); | ||
611 | } | ||
612 | |||
613 | if (width > header_width) | ||
614 | header_width = width; | ||
615 | |||
616 | depth++; | ||
617 | } | ||
618 | |||
619 | fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots); | ||
620 | |||
621 | fprintf(fp, "\n#\n"); | ||
622 | |||
623 | return 2; | ||
624 | } | ||
625 | |||
435 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | 626 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
436 | int max_cols, float min_pcnt, FILE *fp) | 627 | int max_cols, float min_pcnt, FILE *fp) |
437 | { | 628 | { |
438 | struct perf_hpp_fmt *fmt; | 629 | struct perf_hpp_fmt *fmt; |
630 | struct perf_hpp_list_node *fmt_node; | ||
439 | struct rb_node *nd; | 631 | struct rb_node *nd; |
440 | size_t ret = 0; | 632 | size_t ret = 0; |
441 | unsigned int width; | 633 | unsigned int width; |
@@ -449,10 +641,11 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
449 | bool first = true; | 641 | bool first = true; |
450 | size_t linesz; | 642 | size_t linesz; |
451 | char *line = NULL; | 643 | char *line = NULL; |
644 | unsigned indent; | ||
452 | 645 | ||
453 | init_rem_hits(); | 646 | init_rem_hits(); |
454 | 647 | ||
455 | perf_hpp__for_each_format(fmt) | 648 | hists__for_each_format(hists, fmt) |
456 | perf_hpp__reset_width(fmt, hists); | 649 | perf_hpp__reset_width(fmt, hists); |
457 | 650 | ||
458 | if (symbol_conf.col_width_list_str) | 651 | if (symbol_conf.col_width_list_str) |
@@ -463,7 +656,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
463 | 656 | ||
464 | fprintf(fp, "# "); | 657 | fprintf(fp, "# "); |
465 | 658 | ||
466 | perf_hpp__for_each_format(fmt) { | 659 | if (symbol_conf.report_hierarchy) { |
660 | list_for_each_entry(fmt_node, &hists->hpp_formats, list) { | ||
661 | perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) | ||
662 | perf_hpp__reset_width(fmt, hists); | ||
663 | } | ||
664 | nr_rows += print_hierarchy_header(hists, &dummy_hpp, sep, fp); | ||
665 | goto print_entries; | ||
666 | } | ||
667 | |||
668 | hists__for_each_format(hists, fmt) { | ||
467 | if (perf_hpp__should_skip(fmt, hists)) | 669 | if (perf_hpp__should_skip(fmt, hists)) |
468 | continue; | 670 | continue; |
469 | 671 | ||
@@ -487,7 +689,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
487 | 689 | ||
488 | fprintf(fp, "# "); | 690 | fprintf(fp, "# "); |
489 | 691 | ||
490 | perf_hpp__for_each_format(fmt) { | 692 | hists__for_each_format(hists, fmt) { |
491 | unsigned int i; | 693 | unsigned int i; |
492 | 694 | ||
493 | if (perf_hpp__should_skip(fmt, hists)) | 695 | if (perf_hpp__should_skip(fmt, hists)) |
@@ -520,7 +722,9 @@ print_entries: | |||
520 | goto out; | 722 | goto out; |
521 | } | 723 | } |
522 | 724 | ||
523 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 725 | indent = hists__overhead_width(hists) + 4; |
726 | |||
727 | for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { | ||
524 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 728 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
525 | float percent; | 729 | float percent; |
526 | 730 | ||
@@ -536,6 +740,20 @@ print_entries: | |||
536 | if (max_rows && ++nr_rows >= max_rows) | 740 | if (max_rows && ++nr_rows >= max_rows) |
537 | break; | 741 | break; |
538 | 742 | ||
743 | /* | ||
744 | * If all children are filtered out or percent-limited, | ||
745 | * display "no entry >= x.xx%" message. | ||
746 | */ | ||
747 | if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) { | ||
748 | int depth = hists->nr_hpp_node + h->depth + 1; | ||
749 | |||
750 | print_hierarchy_indent(sep, depth, spaces, fp); | ||
751 | fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt); | ||
752 | |||
753 | if (max_rows && ++nr_rows >= max_rows) | ||
754 | break; | ||
755 | } | ||
756 | |||
539 | if (h->ms.map == NULL && verbose > 1) { | 757 | if (h->ms.map == NULL && verbose > 1) { |
540 | __map_groups__fprintf_maps(h->thread->mg, | 758 | __map_groups__fprintf_maps(h->thread->mg, |
541 | MAP__FUNCTION, fp); | 759 | MAP__FUNCTION, fp); |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 5eec53a3f4ac..da48fd843438 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -1,4 +1,3 @@ | |||
1 | libperf-y += abspath.o | ||
2 | libperf-y += alias.o | 1 | libperf-y += alias.o |
3 | libperf-y += annotate.o | 2 | libperf-y += annotate.o |
4 | libperf-y += build-id.o | 3 | libperf-y += build-id.o |
@@ -82,6 +81,7 @@ libperf-y += parse-branch-options.o | |||
82 | libperf-y += parse-regs-options.o | 81 | libperf-y += parse-regs-options.o |
83 | libperf-y += term.o | 82 | libperf-y += term.o |
84 | libperf-y += help-unknown-cmd.o | 83 | libperf-y += help-unknown-cmd.o |
84 | libperf-y += mem-events.o | ||
85 | 85 | ||
86 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o | 86 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o |
87 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o | 87 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o |
@@ -105,8 +105,17 @@ libperf-y += scripting-engines/ | |||
105 | 105 | ||
106 | libperf-$(CONFIG_ZLIB) += zlib.o | 106 | libperf-$(CONFIG_ZLIB) += zlib.o |
107 | libperf-$(CONFIG_LZMA) += lzma.o | 107 | libperf-$(CONFIG_LZMA) += lzma.o |
108 | libperf-y += demangle-java.o | ||
109 | |||
110 | ifdef CONFIG_JITDUMP | ||
111 | libperf-$(CONFIG_LIBELF) += jitdump.o | ||
112 | libperf-$(CONFIG_LIBELF) += genelf.o | ||
113 | libperf-$(CONFIG_LIBELF) += genelf_debug.o | ||
114 | endif | ||
108 | 115 | ||
109 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 116 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" |
117 | # avoid compiler warnings in 32-bit mode | ||
118 | CFLAGS_genelf_debug.o += -Wno-packed | ||
110 | 119 | ||
111 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c | 120 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c |
112 | $(call rule_mkdir) | 121 | $(call rule_mkdir) |
diff --git a/tools/perf/util/abspath.c b/tools/perf/util/abspath.c deleted file mode 100644 index 0e76affe9c36..000000000000 --- a/tools/perf/util/abspath.c +++ /dev/null | |||
@@ -1,37 +0,0 @@ | |||
1 | #include "cache.h" | ||
2 | |||
3 | static const char *get_pwd_cwd(void) | ||
4 | { | ||
5 | static char cwd[PATH_MAX + 1]; | ||
6 | char *pwd; | ||
7 | struct stat cwd_stat, pwd_stat; | ||
8 | if (getcwd(cwd, PATH_MAX) == NULL) | ||
9 | return NULL; | ||
10 | pwd = getenv("PWD"); | ||
11 | if (pwd && strcmp(pwd, cwd)) { | ||
12 | stat(cwd, &cwd_stat); | ||
13 | if (!stat(pwd, &pwd_stat) && | ||
14 | pwd_stat.st_dev == cwd_stat.st_dev && | ||
15 | pwd_stat.st_ino == cwd_stat.st_ino) { | ||
16 | strlcpy(cwd, pwd, PATH_MAX); | ||
17 | } | ||
18 | } | ||
19 | return cwd; | ||
20 | } | ||
21 | |||
22 | const char *make_nonrelative_path(const char *path) | ||
23 | { | ||
24 | static char buf[PATH_MAX + 1]; | ||
25 | |||
26 | if (is_absolute_path(path)) { | ||
27 | if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) | ||
28 | die("Too long path: %.*s", 60, path); | ||
29 | } else { | ||
30 | const char *cwd = get_pwd_cwd(); | ||
31 | if (!cwd) | ||
32 | die("Cannot determine the current working directory"); | ||
33 | if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) | ||
34 | die("Too long path: %.*s", 60, path); | ||
35 | } | ||
36 | return buf; | ||
37 | } | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index cea323d9ee7e..9241f8c2b7e1 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -158,7 +158,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | |||
158 | 158 | ||
159 | int hist_entry__annotate(struct hist_entry *he, size_t privsize); | 159 | int hist_entry__annotate(struct hist_entry *he, size_t privsize); |
160 | 160 | ||
161 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); | 161 | int symbol__annotate_init(struct map *map, struct symbol *sym); |
162 | int symbol__annotate_printf(struct symbol *sym, struct map *map, | 162 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
163 | struct perf_evsel *evsel, bool full_paths, | 163 | struct perf_evsel *evsel, bool full_paths, |
164 | int min_pcnt, int max_lines, int context); | 164 | int min_pcnt, int max_lines, int context); |
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 360fda01f3b0..ec164fe70718 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap) | |||
478 | heap_array[last].ordinal); | 478 | heap_array[last].ordinal); |
479 | } | 479 | } |
480 | 480 | ||
481 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) | 481 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, |
482 | struct perf_evlist *evlist) | ||
482 | { | 483 | { |
483 | if (itr) | 484 | if (itr) |
484 | return itr->info_priv_size(itr); | 485 | return itr->info_priv_size(itr, evlist); |
485 | return 0; | 486 | return 0; |
486 | } | 487 | } |
487 | 488 | ||
@@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, | |||
852 | int err; | 853 | int err; |
853 | 854 | ||
854 | pr_debug2("Synthesizing auxtrace information\n"); | 855 | pr_debug2("Synthesizing auxtrace information\n"); |
855 | priv_size = auxtrace_record__info_priv_size(itr); | 856 | priv_size = auxtrace_record__info_priv_size(itr, session->evlist); |
856 | ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); | 857 | ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); |
857 | if (!ev) | 858 | if (!ev) |
858 | return -ENOMEM; | 859 | return -ENOMEM; |
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index b86f90db1352..57ff31ecb8e4 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -293,7 +293,8 @@ struct auxtrace_record { | |||
293 | int (*recording_options)(struct auxtrace_record *itr, | 293 | int (*recording_options)(struct auxtrace_record *itr, |
294 | struct perf_evlist *evlist, | 294 | struct perf_evlist *evlist, |
295 | struct record_opts *opts); | 295 | struct record_opts *opts); |
296 | size_t (*info_priv_size)(struct auxtrace_record *itr); | 296 | size_t (*info_priv_size)(struct auxtrace_record *itr, |
297 | struct perf_evlist *evlist); | ||
297 | int (*info_fill)(struct auxtrace_record *itr, | 298 | int (*info_fill)(struct auxtrace_record *itr, |
298 | struct perf_session *session, | 299 | struct perf_session *session, |
299 | struct auxtrace_info_event *auxtrace_info, | 300 | struct auxtrace_info_event *auxtrace_info, |
@@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, | |||
429 | int auxtrace_record__options(struct auxtrace_record *itr, | 430 | int auxtrace_record__options(struct auxtrace_record *itr, |
430 | struct perf_evlist *evlist, | 431 | struct perf_evlist *evlist, |
431 | struct record_opts *opts); | 432 | struct record_opts *opts); |
432 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr); | 433 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, |
434 | struct perf_evlist *evlist); | ||
433 | int auxtrace_record__info_fill(struct auxtrace_record *itr, | 435 | int auxtrace_record__info_fill(struct auxtrace_record *itr, |
434 | struct perf_session *session, | 436 | struct perf_session *session, |
435 | struct auxtrace_info_event *auxtrace_info, | 437 | struct auxtrace_info_event *auxtrace_info, |
@@ -515,7 +517,7 @@ static inline void auxtrace__free(struct perf_session *session) | |||
515 | 517 | ||
516 | static inline struct auxtrace_record * | 518 | static inline struct auxtrace_record * |
517 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, | 519 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, |
518 | int *err __maybe_unused) | 520 | int *err) |
519 | { | 521 | { |
520 | *err = 0; | 522 | *err = 0; |
521 | return NULL; | 523 | return NULL; |
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 540a7efa657e..0967ce601931 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #include <linux/bpf.h> | 8 | #include <linux/bpf.h> |
9 | #include <bpf/libbpf.h> | 9 | #include <bpf/libbpf.h> |
10 | #include <bpf/bpf.h> | ||
10 | #include <linux/err.h> | 11 | #include <linux/err.h> |
11 | #include <linux/string.h> | 12 | #include <linux/string.h> |
12 | #include "perf.h" | 13 | #include "perf.h" |
@@ -16,6 +17,7 @@ | |||
16 | #include "llvm-utils.h" | 17 | #include "llvm-utils.h" |
17 | #include "probe-event.h" | 18 | #include "probe-event.h" |
18 | #include "probe-finder.h" // for MAX_PROBES | 19 | #include "probe-finder.h" // for MAX_PROBES |
20 | #include "parse-events.h" | ||
19 | #include "llvm-utils.h" | 21 | #include "llvm-utils.h" |
20 | 22 | ||
21 | #define DEFINE_PRINT_FN(name, level) \ | 23 | #define DEFINE_PRINT_FN(name, level) \ |
@@ -108,8 +110,8 @@ void bpf__clear(void) | |||
108 | } | 110 | } |
109 | 111 | ||
110 | static void | 112 | static void |
111 | bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, | 113 | clear_prog_priv(struct bpf_program *prog __maybe_unused, |
112 | void *_priv) | 114 | void *_priv) |
113 | { | 115 | { |
114 | struct bpf_prog_priv *priv = _priv; | 116 | struct bpf_prog_priv *priv = _priv; |
115 | 117 | ||
@@ -337,7 +339,7 @@ config_bpf_program(struct bpf_program *prog) | |||
337 | } | 339 | } |
338 | pr_debug("bpf: config '%s' is ok\n", config_str); | 340 | pr_debug("bpf: config '%s' is ok\n", config_str); |
339 | 341 | ||
340 | err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear); | 342 | err = bpf_program__set_private(prog, priv, clear_prog_priv); |
341 | if (err) { | 343 | if (err) { |
342 | pr_debug("Failed to set priv for program '%s'\n", config_str); | 344 | pr_debug("Failed to set priv for program '%s'\n", config_str); |
343 | goto errout; | 345 | goto errout; |
@@ -739,6 +741,682 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
739 | return 0; | 741 | return 0; |
740 | } | 742 | } |
741 | 743 | ||
744 | enum bpf_map_op_type { | ||
745 | BPF_MAP_OP_SET_VALUE, | ||
746 | BPF_MAP_OP_SET_EVSEL, | ||
747 | }; | ||
748 | |||
749 | enum bpf_map_key_type { | ||
750 | BPF_MAP_KEY_ALL, | ||
751 | BPF_MAP_KEY_RANGES, | ||
752 | }; | ||
753 | |||
754 | struct bpf_map_op { | ||
755 | struct list_head list; | ||
756 | enum bpf_map_op_type op_type; | ||
757 | enum bpf_map_key_type key_type; | ||
758 | union { | ||
759 | struct parse_events_array array; | ||
760 | } k; | ||
761 | union { | ||
762 | u64 value; | ||
763 | struct perf_evsel *evsel; | ||
764 | } v; | ||
765 | }; | ||
766 | |||
767 | struct bpf_map_priv { | ||
768 | struct list_head ops_list; | ||
769 | }; | ||
770 | |||
771 | static void | ||
772 | bpf_map_op__delete(struct bpf_map_op *op) | ||
773 | { | ||
774 | if (!list_empty(&op->list)) | ||
775 | list_del(&op->list); | ||
776 | if (op->key_type == BPF_MAP_KEY_RANGES) | ||
777 | parse_events__clear_array(&op->k.array); | ||
778 | free(op); | ||
779 | } | ||
780 | |||
781 | static void | ||
782 | bpf_map_priv__purge(struct bpf_map_priv *priv) | ||
783 | { | ||
784 | struct bpf_map_op *pos, *n; | ||
785 | |||
786 | list_for_each_entry_safe(pos, n, &priv->ops_list, list) { | ||
787 | list_del_init(&pos->list); | ||
788 | bpf_map_op__delete(pos); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | static void | ||
793 | bpf_map_priv__clear(struct bpf_map *map __maybe_unused, | ||
794 | void *_priv) | ||
795 | { | ||
796 | struct bpf_map_priv *priv = _priv; | ||
797 | |||
798 | bpf_map_priv__purge(priv); | ||
799 | free(priv); | ||
800 | } | ||
801 | |||
802 | static int | ||
803 | bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term) | ||
804 | { | ||
805 | op->key_type = BPF_MAP_KEY_ALL; | ||
806 | if (!term) | ||
807 | return 0; | ||
808 | |||
809 | if (term->array.nr_ranges) { | ||
810 | size_t memsz = term->array.nr_ranges * | ||
811 | sizeof(op->k.array.ranges[0]); | ||
812 | |||
813 | op->k.array.ranges = memdup(term->array.ranges, memsz); | ||
814 | if (!op->k.array.ranges) { | ||
815 | pr_debug("No enough memory to alloc indices for map\n"); | ||
816 | return -ENOMEM; | ||
817 | } | ||
818 | op->key_type = BPF_MAP_KEY_RANGES; | ||
819 | op->k.array.nr_ranges = term->array.nr_ranges; | ||
820 | } | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | static struct bpf_map_op * | ||
825 | bpf_map_op__new(struct parse_events_term *term) | ||
826 | { | ||
827 | struct bpf_map_op *op; | ||
828 | int err; | ||
829 | |||
830 | op = zalloc(sizeof(*op)); | ||
831 | if (!op) { | ||
832 | pr_debug("Failed to alloc bpf_map_op\n"); | ||
833 | return ERR_PTR(-ENOMEM); | ||
834 | } | ||
835 | INIT_LIST_HEAD(&op->list); | ||
836 | |||
837 | err = bpf_map_op_setkey(op, term); | ||
838 | if (err) { | ||
839 | free(op); | ||
840 | return ERR_PTR(err); | ||
841 | } | ||
842 | return op; | ||
843 | } | ||
844 | |||
845 | static int | ||
846 | bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) | ||
847 | { | ||
848 | struct bpf_map_priv *priv; | ||
849 | const char *map_name; | ||
850 | int err; | ||
851 | |||
852 | map_name = bpf_map__get_name(map); | ||
853 | err = bpf_map__get_private(map, (void **)&priv); | ||
854 | if (err) { | ||
855 | pr_debug("Failed to get private from map %s\n", map_name); | ||
856 | return err; | ||
857 | } | ||
858 | |||
859 | if (!priv) { | ||
860 | priv = zalloc(sizeof(*priv)); | ||
861 | if (!priv) { | ||
862 | pr_debug("No enough memory to alloc map private\n"); | ||
863 | return -ENOMEM; | ||
864 | } | ||
865 | INIT_LIST_HEAD(&priv->ops_list); | ||
866 | |||
867 | if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) { | ||
868 | free(priv); | ||
869 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | list_add_tail(&op->list, &priv->ops_list); | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static struct bpf_map_op * | ||
878 | bpf_map__add_newop(struct bpf_map *map, struct parse_events_term *term) | ||
879 | { | ||
880 | struct bpf_map_op *op; | ||
881 | int err; | ||
882 | |||
883 | op = bpf_map_op__new(term); | ||
884 | if (IS_ERR(op)) | ||
885 | return op; | ||
886 | |||
887 | err = bpf_map__add_op(map, op); | ||
888 | if (err) { | ||
889 | bpf_map_op__delete(op); | ||
890 | return ERR_PTR(err); | ||
891 | } | ||
892 | return op; | ||
893 | } | ||
894 | |||
895 | static int | ||
896 | __bpf_map__config_value(struct bpf_map *map, | ||
897 | struct parse_events_term *term) | ||
898 | { | ||
899 | struct bpf_map_def def; | ||
900 | struct bpf_map_op *op; | ||
901 | const char *map_name; | ||
902 | int err; | ||
903 | |||
904 | map_name = bpf_map__get_name(map); | ||
905 | |||
906 | err = bpf_map__get_def(map, &def); | ||
907 | if (err) { | ||
908 | pr_debug("Unable to get map definition from '%s'\n", | ||
909 | map_name); | ||
910 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
911 | } | ||
912 | |||
913 | if (def.type != BPF_MAP_TYPE_ARRAY) { | ||
914 | pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n", | ||
915 | map_name); | ||
916 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; | ||
917 | } | ||
918 | if (def.key_size < sizeof(unsigned int)) { | ||
919 | pr_debug("Map %s has incorrect key size\n", map_name); | ||
920 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE; | ||
921 | } | ||
922 | switch (def.value_size) { | ||
923 | case 1: | ||
924 | case 2: | ||
925 | case 4: | ||
926 | case 8: | ||
927 | break; | ||
928 | default: | ||
929 | pr_debug("Map %s has incorrect value size\n", map_name); | ||
930 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE; | ||
931 | } | ||
932 | |||
933 | op = bpf_map__add_newop(map, term); | ||
934 | if (IS_ERR(op)) | ||
935 | return PTR_ERR(op); | ||
936 | op->op_type = BPF_MAP_OP_SET_VALUE; | ||
937 | op->v.value = term->val.num; | ||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static int | ||
942 | bpf_map__config_value(struct bpf_map *map, | ||
943 | struct parse_events_term *term, | ||
944 | struct perf_evlist *evlist __maybe_unused) | ||
945 | { | ||
946 | if (!term->err_val) { | ||
947 | pr_debug("Config value not set\n"); | ||
948 | return -BPF_LOADER_ERRNO__OBJCONF_CONF; | ||
949 | } | ||
950 | |||
951 | if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) { | ||
952 | pr_debug("ERROR: wrong value type for 'value'\n"); | ||
953 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE; | ||
954 | } | ||
955 | |||
956 | return __bpf_map__config_value(map, term); | ||
957 | } | ||
958 | |||
959 | static int | ||
960 | __bpf_map__config_event(struct bpf_map *map, | ||
961 | struct parse_events_term *term, | ||
962 | struct perf_evlist *evlist) | ||
963 | { | ||
964 | struct perf_evsel *evsel; | ||
965 | struct bpf_map_def def; | ||
966 | struct bpf_map_op *op; | ||
967 | const char *map_name; | ||
968 | int err; | ||
969 | |||
970 | map_name = bpf_map__get_name(map); | ||
971 | evsel = perf_evlist__find_evsel_by_str(evlist, term->val.str); | ||
972 | if (!evsel) { | ||
973 | pr_debug("Event (for '%s') '%s' doesn't exist\n", | ||
974 | map_name, term->val.str); | ||
975 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT; | ||
976 | } | ||
977 | |||
978 | err = bpf_map__get_def(map, &def); | ||
979 | if (err) { | ||
980 | pr_debug("Unable to get map definition from '%s'\n", | ||
981 | map_name); | ||
982 | return err; | ||
983 | } | ||
984 | |||
985 | /* | ||
986 | * No need to check key_size and value_size: | ||
987 | * kernel has already checked them. | ||
988 | */ | ||
989 | if (def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) { | ||
990 | pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n", | ||
991 | map_name); | ||
992 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; | ||
993 | } | ||
994 | |||
995 | op = bpf_map__add_newop(map, term); | ||
996 | if (IS_ERR(op)) | ||
997 | return PTR_ERR(op); | ||
998 | op->op_type = BPF_MAP_OP_SET_EVSEL; | ||
999 | op->v.evsel = evsel; | ||
1000 | return 0; | ||
1001 | } | ||
1002 | |||
1003 | static int | ||
1004 | bpf_map__config_event(struct bpf_map *map, | ||
1005 | struct parse_events_term *term, | ||
1006 | struct perf_evlist *evlist) | ||
1007 | { | ||
1008 | if (!term->err_val) { | ||
1009 | pr_debug("Config value not set\n"); | ||
1010 | return -BPF_LOADER_ERRNO__OBJCONF_CONF; | ||
1011 | } | ||
1012 | |||
1013 | if (term->type_val != PARSE_EVENTS__TERM_TYPE_STR) { | ||
1014 | pr_debug("ERROR: wrong value type for 'event'\n"); | ||
1015 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE; | ||
1016 | } | ||
1017 | |||
1018 | return __bpf_map__config_event(map, term, evlist); | ||
1019 | } | ||
1020 | |||
1021 | struct bpf_obj_config__map_func { | ||
1022 | const char *config_opt; | ||
1023 | int (*config_func)(struct bpf_map *, struct parse_events_term *, | ||
1024 | struct perf_evlist *); | ||
1025 | }; | ||
1026 | |||
1027 | struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = { | ||
1028 | {"value", bpf_map__config_value}, | ||
1029 | {"event", bpf_map__config_event}, | ||
1030 | }; | ||
1031 | |||
1032 | static int | ||
1033 | config_map_indices_range_check(struct parse_events_term *term, | ||
1034 | struct bpf_map *map, | ||
1035 | const char *map_name) | ||
1036 | { | ||
1037 | struct parse_events_array *array = &term->array; | ||
1038 | struct bpf_map_def def; | ||
1039 | unsigned int i; | ||
1040 | int err; | ||
1041 | |||
1042 | if (!array->nr_ranges) | ||
1043 | return 0; | ||
1044 | if (!array->ranges) { | ||
1045 | pr_debug("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n", | ||
1046 | map_name, (int)array->nr_ranges); | ||
1047 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1048 | } | ||
1049 | |||
1050 | err = bpf_map__get_def(map, &def); | ||
1051 | if (err) { | ||
1052 | pr_debug("ERROR: Unable to get map definition from '%s'\n", | ||
1053 | map_name); | ||
1054 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1055 | } | ||
1056 | |||
1057 | for (i = 0; i < array->nr_ranges; i++) { | ||
1058 | unsigned int start = array->ranges[i].start; | ||
1059 | size_t length = array->ranges[i].length; | ||
1060 | unsigned int idx = start + length - 1; | ||
1061 | |||
1062 | if (idx >= def.max_entries) { | ||
1063 | pr_debug("ERROR: index %d too large\n", idx); | ||
1064 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG; | ||
1065 | } | ||
1066 | } | ||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | static int | ||
1071 | bpf__obj_config_map(struct bpf_object *obj, | ||
1072 | struct parse_events_term *term, | ||
1073 | struct perf_evlist *evlist, | ||
1074 | int *key_scan_pos) | ||
1075 | { | ||
1076 | /* key is "map:<mapname>.<config opt>" */ | ||
1077 | char *map_name = strdup(term->config + sizeof("map:") - 1); | ||
1078 | struct bpf_map *map; | ||
1079 | int err = -BPF_LOADER_ERRNO__OBJCONF_OPT; | ||
1080 | char *map_opt; | ||
1081 | size_t i; | ||
1082 | |||
1083 | if (!map_name) | ||
1084 | return -ENOMEM; | ||
1085 | |||
1086 | map_opt = strchr(map_name, '.'); | ||
1087 | if (!map_opt) { | ||
1088 | pr_debug("ERROR: Invalid map config: %s\n", map_name); | ||
1089 | goto out; | ||
1090 | } | ||
1091 | |||
1092 | *map_opt++ = '\0'; | ||
1093 | if (*map_opt == '\0') { | ||
1094 | pr_debug("ERROR: Invalid map option: %s\n", term->config); | ||
1095 | goto out; | ||
1096 | } | ||
1097 | |||
1098 | map = bpf_object__get_map_by_name(obj, map_name); | ||
1099 | if (!map) { | ||
1100 | pr_debug("ERROR: Map %s doesn't exist\n", map_name); | ||
1101 | err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST; | ||
1102 | goto out; | ||
1103 | } | ||
1104 | |||
1105 | *key_scan_pos += strlen(map_opt); | ||
1106 | err = config_map_indices_range_check(term, map, map_name); | ||
1107 | if (err) | ||
1108 | goto out; | ||
1109 | *key_scan_pos -= strlen(map_opt); | ||
1110 | |||
1111 | for (i = 0; i < ARRAY_SIZE(bpf_obj_config__map_funcs); i++) { | ||
1112 | struct bpf_obj_config__map_func *func = | ||
1113 | &bpf_obj_config__map_funcs[i]; | ||
1114 | |||
1115 | if (strcmp(map_opt, func->config_opt) == 0) { | ||
1116 | err = func->config_func(map, term, evlist); | ||
1117 | goto out; | ||
1118 | } | ||
1119 | } | ||
1120 | |||
1121 | pr_debug("ERROR: Invalid map config option '%s'\n", map_opt); | ||
1122 | err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT; | ||
1123 | out: | ||
1124 | free(map_name); | ||
1125 | if (!err) | ||
1126 | key_scan_pos += strlen(map_opt); | ||
1127 | return err; | ||
1128 | } | ||
1129 | |||
1130 | int bpf__config_obj(struct bpf_object *obj, | ||
1131 | struct parse_events_term *term, | ||
1132 | struct perf_evlist *evlist, | ||
1133 | int *error_pos) | ||
1134 | { | ||
1135 | int key_scan_pos = 0; | ||
1136 | int err; | ||
1137 | |||
1138 | if (!obj || !term || !term->config) | ||
1139 | return -EINVAL; | ||
1140 | |||
1141 | if (!prefixcmp(term->config, "map:")) { | ||
1142 | key_scan_pos = sizeof("map:") - 1; | ||
1143 | err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos); | ||
1144 | goto out; | ||
1145 | } | ||
1146 | err = -BPF_LOADER_ERRNO__OBJCONF_OPT; | ||
1147 | out: | ||
1148 | if (error_pos) | ||
1149 | *error_pos = key_scan_pos; | ||
1150 | return err; | ||
1151 | |||
1152 | } | ||
1153 | |||
1154 | typedef int (*map_config_func_t)(const char *name, int map_fd, | ||
1155 | struct bpf_map_def *pdef, | ||
1156 | struct bpf_map_op *op, | ||
1157 | void *pkey, void *arg); | ||
1158 | |||
1159 | static int | ||
1160 | foreach_key_array_all(map_config_func_t func, | ||
1161 | void *arg, const char *name, | ||
1162 | int map_fd, struct bpf_map_def *pdef, | ||
1163 | struct bpf_map_op *op) | ||
1164 | { | ||
1165 | unsigned int i; | ||
1166 | int err; | ||
1167 | |||
1168 | for (i = 0; i < pdef->max_entries; i++) { | ||
1169 | err = func(name, map_fd, pdef, op, &i, arg); | ||
1170 | if (err) { | ||
1171 | pr_debug("ERROR: failed to insert value to %s[%u]\n", | ||
1172 | name, i); | ||
1173 | return err; | ||
1174 | } | ||
1175 | } | ||
1176 | return 0; | ||
1177 | } | ||
1178 | |||
1179 | static int | ||
1180 | foreach_key_array_ranges(map_config_func_t func, void *arg, | ||
1181 | const char *name, int map_fd, | ||
1182 | struct bpf_map_def *pdef, | ||
1183 | struct bpf_map_op *op) | ||
1184 | { | ||
1185 | unsigned int i, j; | ||
1186 | int err; | ||
1187 | |||
1188 | for (i = 0; i < op->k.array.nr_ranges; i++) { | ||
1189 | unsigned int start = op->k.array.ranges[i].start; | ||
1190 | size_t length = op->k.array.ranges[i].length; | ||
1191 | |||
1192 | for (j = 0; j < length; j++) { | ||
1193 | unsigned int idx = start + j; | ||
1194 | |||
1195 | err = func(name, map_fd, pdef, op, &idx, arg); | ||
1196 | if (err) { | ||
1197 | pr_debug("ERROR: failed to insert value to %s[%u]\n", | ||
1198 | name, idx); | ||
1199 | return err; | ||
1200 | } | ||
1201 | } | ||
1202 | } | ||
1203 | return 0; | ||
1204 | } | ||
1205 | |||
1206 | static int | ||
1207 | bpf_map_config_foreach_key(struct bpf_map *map, | ||
1208 | map_config_func_t func, | ||
1209 | void *arg) | ||
1210 | { | ||
1211 | int err, map_fd; | ||
1212 | const char *name; | ||
1213 | struct bpf_map_op *op; | ||
1214 | struct bpf_map_def def; | ||
1215 | struct bpf_map_priv *priv; | ||
1216 | |||
1217 | name = bpf_map__get_name(map); | ||
1218 | |||
1219 | err = bpf_map__get_private(map, (void **)&priv); | ||
1220 | if (err) { | ||
1221 | pr_debug("ERROR: failed to get private from map %s\n", name); | ||
1222 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1223 | } | ||
1224 | if (!priv || list_empty(&priv->ops_list)) { | ||
1225 | pr_debug("INFO: nothing to config for map %s\n", name); | ||
1226 | return 0; | ||
1227 | } | ||
1228 | |||
1229 | err = bpf_map__get_def(map, &def); | ||
1230 | if (err) { | ||
1231 | pr_debug("ERROR: failed to get definition from map %s\n", name); | ||
1232 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1233 | } | ||
1234 | map_fd = bpf_map__get_fd(map); | ||
1235 | if (map_fd < 0) { | ||
1236 | pr_debug("ERROR: failed to get fd from map %s\n", name); | ||
1237 | return map_fd; | ||
1238 | } | ||
1239 | |||
1240 | list_for_each_entry(op, &priv->ops_list, list) { | ||
1241 | switch (def.type) { | ||
1242 | case BPF_MAP_TYPE_ARRAY: | ||
1243 | case BPF_MAP_TYPE_PERF_EVENT_ARRAY: | ||
1244 | switch (op->key_type) { | ||
1245 | case BPF_MAP_KEY_ALL: | ||
1246 | err = foreach_key_array_all(func, arg, name, | ||
1247 | map_fd, &def, op); | ||
1248 | break; | ||
1249 | case BPF_MAP_KEY_RANGES: | ||
1250 | err = foreach_key_array_ranges(func, arg, name, | ||
1251 | map_fd, &def, | ||
1252 | op); | ||
1253 | break; | ||
1254 | default: | ||
1255 | pr_debug("ERROR: keytype for map '%s' invalid\n", | ||
1256 | name); | ||
1257 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1258 | } | ||
1259 | if (err) | ||
1260 | return err; | ||
1261 | break; | ||
1262 | default: | ||
1263 | pr_debug("ERROR: type of '%s' incorrect\n", name); | ||
1264 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE; | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1268 | return 0; | ||
1269 | } | ||
1270 | |||
1271 | static int | ||
1272 | apply_config_value_for_key(int map_fd, void *pkey, | ||
1273 | size_t val_size, u64 val) | ||
1274 | { | ||
1275 | int err = 0; | ||
1276 | |||
1277 | switch (val_size) { | ||
1278 | case 1: { | ||
1279 | u8 _val = (u8)(val); | ||
1280 | err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); | ||
1281 | break; | ||
1282 | } | ||
1283 | case 2: { | ||
1284 | u16 _val = (u16)(val); | ||
1285 | err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); | ||
1286 | break; | ||
1287 | } | ||
1288 | case 4: { | ||
1289 | u32 _val = (u32)(val); | ||
1290 | err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY); | ||
1291 | break; | ||
1292 | } | ||
1293 | case 8: { | ||
1294 | err = bpf_map_update_elem(map_fd, pkey, &val, BPF_ANY); | ||
1295 | break; | ||
1296 | } | ||
1297 | default: | ||
1298 | pr_debug("ERROR: invalid value size\n"); | ||
1299 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE; | ||
1300 | } | ||
1301 | if (err && errno) | ||
1302 | err = -errno; | ||
1303 | return err; | ||
1304 | } | ||
1305 | |||
1306 | static int | ||
1307 | apply_config_evsel_for_key(const char *name, int map_fd, void *pkey, | ||
1308 | struct perf_evsel *evsel) | ||
1309 | { | ||
1310 | struct xyarray *xy = evsel->fd; | ||
1311 | struct perf_event_attr *attr; | ||
1312 | unsigned int key, events; | ||
1313 | bool check_pass = false; | ||
1314 | int *evt_fd; | ||
1315 | int err; | ||
1316 | |||
1317 | if (!xy) { | ||
1318 | pr_debug("ERROR: evsel not ready for map %s\n", name); | ||
1319 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
1320 | } | ||
1321 | |||
1322 | if (xy->row_size / xy->entry_size != 1) { | ||
1323 | pr_debug("ERROR: Dimension of target event is incorrect for map %s\n", | ||
1324 | name); | ||
1325 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM; | ||
1326 | } | ||
1327 | |||
1328 | attr = &evsel->attr; | ||
1329 | if (attr->inherit) { | ||
1330 | pr_debug("ERROR: Can't put inherit event into map %s\n", name); | ||
1331 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH; | ||
1332 | } | ||
1333 | |||
1334 | if (perf_evsel__is_bpf_output(evsel)) | ||
1335 | check_pass = true; | ||
1336 | if (attr->type == PERF_TYPE_RAW) | ||
1337 | check_pass = true; | ||
1338 | if (attr->type == PERF_TYPE_HARDWARE) | ||
1339 | check_pass = true; | ||
1340 | if (!check_pass) { | ||
1341 | pr_debug("ERROR: Event type is wrong for map %s\n", name); | ||
1342 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE; | ||
1343 | } | ||
1344 | |||
1345 | events = xy->entries / (xy->row_size / xy->entry_size); | ||
1346 | key = *((unsigned int *)pkey); | ||
1347 | if (key >= events) { | ||
1348 | pr_debug("ERROR: there is no event %d for map %s\n", | ||
1349 | key, name); | ||
1350 | return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE; | ||
1351 | } | ||
1352 | evt_fd = xyarray__entry(xy, key, 0); | ||
1353 | err = bpf_map_update_elem(map_fd, pkey, evt_fd, BPF_ANY); | ||
1354 | if (err && errno) | ||
1355 | err = -errno; | ||
1356 | return err; | ||
1357 | } | ||
1358 | |||
1359 | static int | ||
1360 | apply_obj_config_map_for_key(const char *name, int map_fd, | ||
1361 | struct bpf_map_def *pdef __maybe_unused, | ||
1362 | struct bpf_map_op *op, | ||
1363 | void *pkey, void *arg __maybe_unused) | ||
1364 | { | ||
1365 | int err; | ||
1366 | |||
1367 | switch (op->op_type) { | ||
1368 | case BPF_MAP_OP_SET_VALUE: | ||
1369 | err = apply_config_value_for_key(map_fd, pkey, | ||
1370 | pdef->value_size, | ||
1371 | op->v.value); | ||
1372 | break; | ||
1373 | case BPF_MAP_OP_SET_EVSEL: | ||
1374 | err = apply_config_evsel_for_key(name, map_fd, pkey, | ||
1375 | op->v.evsel); | ||
1376 | break; | ||
1377 | default: | ||
1378 | pr_debug("ERROR: unknown value type for '%s'\n", name); | ||
1379 | err = -BPF_LOADER_ERRNO__INTERNAL; | ||
1380 | } | ||
1381 | return err; | ||
1382 | } | ||
1383 | |||
1384 | static int | ||
1385 | apply_obj_config_map(struct bpf_map *map) | ||
1386 | { | ||
1387 | return bpf_map_config_foreach_key(map, | ||
1388 | apply_obj_config_map_for_key, | ||
1389 | NULL); | ||
1390 | } | ||
1391 | |||
1392 | static int | ||
1393 | apply_obj_config_object(struct bpf_object *obj) | ||
1394 | { | ||
1395 | struct bpf_map *map; | ||
1396 | int err; | ||
1397 | |||
1398 | bpf_map__for_each(map, obj) { | ||
1399 | err = apply_obj_config_map(map); | ||
1400 | if (err) | ||
1401 | return err; | ||
1402 | } | ||
1403 | return 0; | ||
1404 | } | ||
1405 | |||
1406 | int bpf__apply_obj_config(void) | ||
1407 | { | ||
1408 | struct bpf_object *obj, *tmp; | ||
1409 | int err; | ||
1410 | |||
1411 | bpf_object__for_each_safe(obj, tmp) { | ||
1412 | err = apply_obj_config_object(obj); | ||
1413 | if (err) | ||
1414 | return err; | ||
1415 | } | ||
1416 | |||
1417 | return 0; | ||
1418 | } | ||
1419 | |||
742 | #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) | 1420 | #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) |
743 | #define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) | 1421 | #define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) |
744 | #define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) | 1422 | #define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) |
@@ -753,6 +1431,20 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { | |||
753 | [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", | 1431 | [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", |
754 | [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", | 1432 | [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", |
755 | [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", | 1433 | [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", |
1434 | [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option", | ||
1435 | [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (missing '=')", | ||
1436 | [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object map config option", | ||
1437 | [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map doesn't exist", | ||
1438 | [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map", | ||
1439 | [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type", | ||
1440 | [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size", | ||
1441 | [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size", | ||
1442 | [ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting", | ||
1443 | [ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting", | ||
1444 | [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large", | ||
1445 | [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event", | ||
1446 | [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map", | ||
1447 | [ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large", | ||
756 | }; | 1448 | }; |
757 | 1449 | ||
758 | static int | 1450 | static int |
@@ -872,3 +1564,29 @@ int bpf__strerror_load(struct bpf_object *obj, | |||
872 | bpf__strerror_end(buf, size); | 1564 | bpf__strerror_end(buf, size); |
873 | return 0; | 1565 | return 0; |
874 | } | 1566 | } |
1567 | |||
1568 | int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused, | ||
1569 | struct parse_events_term *term __maybe_unused, | ||
1570 | struct perf_evlist *evlist __maybe_unused, | ||
1571 | int *error_pos __maybe_unused, int err, | ||
1572 | char *buf, size_t size) | ||
1573 | { | ||
1574 | bpf__strerror_head(err, buf, size); | ||
1575 | bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, | ||
1576 | "Can't use this config term with this map type"); | ||
1577 | bpf__strerror_end(buf, size); | ||
1578 | return 0; | ||
1579 | } | ||
1580 | |||
1581 | int bpf__strerror_apply_obj_config(int err, char *buf, size_t size) | ||
1582 | { | ||
1583 | bpf__strerror_head(err, buf, size); | ||
1584 | bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, | ||
1585 | "Cannot set event to BPF map in multi-thread tracing"); | ||
1586 | bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, | ||
1587 | "%s (Hint: use -i to turn off inherit)", emsg); | ||
1588 | bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, | ||
1589 | "Can only put raw, hardware and BPF output event into a BPF map"); | ||
1590 | bpf__strerror_end(buf, size); | ||
1591 | return 0; | ||
1592 | } | ||
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index 6fdc0457e2b6..be4311944e3d 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <string.h> | 10 | #include <string.h> |
11 | #include <bpf/libbpf.h> | 11 | #include <bpf/libbpf.h> |
12 | #include "probe-event.h" | 12 | #include "probe-event.h" |
13 | #include "evlist.h" | ||
13 | #include "debug.h" | 14 | #include "debug.h" |
14 | 15 | ||
15 | enum bpf_loader_errno { | 16 | enum bpf_loader_errno { |
@@ -24,10 +25,25 @@ enum bpf_loader_errno { | |||
24 | BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */ | 25 | BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */ |
25 | BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */ | 26 | BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */ |
26 | BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */ | 27 | BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */ |
28 | BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */ | ||
29 | BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */ | ||
30 | BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object map config option */ | ||
31 | BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */ | ||
32 | BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */ | ||
33 | BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */ | ||
34 | BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */ | ||
35 | BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */ | ||
36 | BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT, /* Event not found for map setting */ | ||
37 | BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE, /* Invalid map size for event setting */ | ||
38 | BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */ | ||
39 | BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */ | ||
40 | BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */ | ||
41 | BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG, /* Index too large */ | ||
27 | __BPF_LOADER_ERRNO__END, | 42 | __BPF_LOADER_ERRNO__END, |
28 | }; | 43 | }; |
29 | 44 | ||
30 | struct bpf_object; | 45 | struct bpf_object; |
46 | struct parse_events_term; | ||
31 | #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" | 47 | #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" |
32 | 48 | ||
33 | typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, | 49 | typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, |
@@ -53,6 +69,16 @@ int bpf__strerror_load(struct bpf_object *obj, int err, | |||
53 | char *buf, size_t size); | 69 | char *buf, size_t size); |
54 | int bpf__foreach_tev(struct bpf_object *obj, | 70 | int bpf__foreach_tev(struct bpf_object *obj, |
55 | bpf_prog_iter_callback_t func, void *arg); | 71 | bpf_prog_iter_callback_t func, void *arg); |
72 | |||
73 | int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term, | ||
74 | struct perf_evlist *evlist, int *error_pos); | ||
75 | int bpf__strerror_config_obj(struct bpf_object *obj, | ||
76 | struct parse_events_term *term, | ||
77 | struct perf_evlist *evlist, | ||
78 | int *error_pos, int err, char *buf, | ||
79 | size_t size); | ||
80 | int bpf__apply_obj_config(void); | ||
81 | int bpf__strerror_apply_obj_config(int err, char *buf, size_t size); | ||
56 | #else | 82 | #else |
57 | static inline struct bpf_object * | 83 | static inline struct bpf_object * |
58 | bpf__prepare_load(const char *filename __maybe_unused, | 84 | bpf__prepare_load(const char *filename __maybe_unused, |
@@ -84,6 +110,21 @@ bpf__foreach_tev(struct bpf_object *obj __maybe_unused, | |||
84 | } | 110 | } |
85 | 111 | ||
86 | static inline int | 112 | static inline int |
113 | bpf__config_obj(struct bpf_object *obj __maybe_unused, | ||
114 | struct parse_events_term *term __maybe_unused, | ||
115 | struct perf_evlist *evlist __maybe_unused, | ||
116 | int *error_pos __maybe_unused) | ||
117 | { | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static inline int | ||
122 | bpf__apply_obj_config(void) | ||
123 | { | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static inline int | ||
87 | __bpf_strerror(char *buf, size_t size) | 128 | __bpf_strerror(char *buf, size_t size) |
88 | { | 129 | { |
89 | if (!size) | 130 | if (!size) |
@@ -118,5 +159,23 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused, | |||
118 | { | 159 | { |
119 | return __bpf_strerror(buf, size); | 160 | return __bpf_strerror(buf, size); |
120 | } | 161 | } |
162 | |||
163 | static inline int | ||
164 | bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused, | ||
165 | struct parse_events_term *term __maybe_unused, | ||
166 | struct perf_evlist *evlist __maybe_unused, | ||
167 | int *error_pos __maybe_unused, | ||
168 | int err __maybe_unused, | ||
169 | char *buf, size_t size) | ||
170 | { | ||
171 | return __bpf_strerror(buf, size); | ||
172 | } | ||
173 | |||
174 | static inline int | ||
175 | bpf__strerror_apply_obj_config(int err __maybe_unused, | ||
176 | char *buf, size_t size) | ||
177 | { | ||
178 | return __bpf_strerror(buf, size); | ||
179 | } | ||
121 | #endif | 180 | #endif |
122 | #endif | 181 | #endif |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 6a7e273a514a..0573c2ec861d 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -28,7 +28,6 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
28 | struct machine *machine) | 28 | struct machine *machine) |
29 | { | 29 | { |
30 | struct addr_location al; | 30 | struct addr_location al; |
31 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
32 | struct thread *thread = machine__findnew_thread(machine, sample->pid, | 31 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
33 | sample->tid); | 32 | sample->tid); |
34 | 33 | ||
@@ -38,7 +37,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
38 | return -1; | 37 | return -1; |
39 | } | 38 | } |
40 | 39 | ||
41 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al); | 40 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, &al); |
42 | 41 | ||
43 | if (al.map != NULL) | 42 | if (al.map != NULL) |
44 | al.map->dso->hit = 1; | 43 | al.map->dso->hit = 1; |
@@ -166,6 +165,50 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) | |||
166 | return build_id__filename(build_id_hex, bf, size); | 165 | return build_id__filename(build_id_hex, bf, size); |
167 | } | 166 | } |
168 | 167 | ||
168 | bool dso__build_id_is_kmod(const struct dso *dso, char *bf, size_t size) | ||
169 | { | ||
170 | char *id_name, *ch; | ||
171 | struct stat sb; | ||
172 | |||
173 | id_name = dso__build_id_filename(dso, bf, size); | ||
174 | if (!id_name) | ||
175 | goto err; | ||
176 | if (access(id_name, F_OK)) | ||
177 | goto err; | ||
178 | if (lstat(id_name, &sb) == -1) | ||
179 | goto err; | ||
180 | if ((size_t)sb.st_size > size - 1) | ||
181 | goto err; | ||
182 | if (readlink(id_name, bf, size - 1) < 0) | ||
183 | goto err; | ||
184 | |||
185 | bf[sb.st_size] = '\0'; | ||
186 | |||
187 | /* | ||
188 | * link should be: | ||
189 | * ../../lib/modules/4.4.0-rc4/kernel/net/ipv4/netfilter/nf_nat_ipv4.ko/a09fe3eb3147dafa4e3b31dbd6257e4d696bdc92 | ||
190 | */ | ||
191 | ch = strrchr(bf, '/'); | ||
192 | if (!ch) | ||
193 | goto err; | ||
194 | if (ch - 3 < bf) | ||
195 | goto err; | ||
196 | |||
197 | return strncmp(".ko", ch - 3, 3) == 0; | ||
198 | err: | ||
199 | /* | ||
200 | * If dso__build_id_filename work, get id_name again, | ||
201 | * because id_name points to bf and is broken. | ||
202 | */ | ||
203 | if (id_name) | ||
204 | id_name = dso__build_id_filename(dso, bf, size); | ||
205 | pr_err("Invalid build id: %s\n", id_name ? : | ||
206 | dso->long_name ? : | ||
207 | dso->short_name ? : | ||
208 | "[unknown]"); | ||
209 | return false; | ||
210 | } | ||
211 | |||
169 | #define dsos__for_each_with_build_id(pos, head) \ | 212 | #define dsos__for_each_with_build_id(pos, head) \ |
170 | list_for_each_entry(pos, head, node) \ | 213 | list_for_each_entry(pos, head, node) \ |
171 | if (!pos->has_build_id) \ | 214 | if (!pos->has_build_id) \ |
@@ -211,6 +254,7 @@ static int machine__write_buildid_table(struct machine *machine, int fd) | |||
211 | dsos__for_each_with_build_id(pos, &machine->dsos.head) { | 254 | dsos__for_each_with_build_id(pos, &machine->dsos.head) { |
212 | const char *name; | 255 | const char *name; |
213 | size_t name_len; | 256 | size_t name_len; |
257 | bool in_kernel = false; | ||
214 | 258 | ||
215 | if (!pos->hit) | 259 | if (!pos->hit) |
216 | continue; | 260 | continue; |
@@ -227,8 +271,11 @@ static int machine__write_buildid_table(struct machine *machine, int fd) | |||
227 | name_len = pos->long_name_len + 1; | 271 | name_len = pos->long_name_len + 1; |
228 | } | 272 | } |
229 | 273 | ||
274 | in_kernel = pos->kernel || | ||
275 | is_kernel_module(name, | ||
276 | PERF_RECORD_MISC_CPUMODE_UNKNOWN); | ||
230 | err = write_buildid(name, name_len, pos->build_id, machine->pid, | 277 | err = write_buildid(name, name_len, pos->build_id, machine->pid, |
231 | pos->kernel ? kmisc : umisc, fd); | 278 | in_kernel ? kmisc : umisc, fd); |
232 | if (err) | 279 | if (err) |
233 | break; | 280 | break; |
234 | } | 281 | } |
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 27a14a8a945b..64af3e20610d 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -16,6 +16,7 @@ int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id); | |||
16 | int filename__sprintf_build_id(const char *pathname, char *sbuild_id); | 16 | int filename__sprintf_build_id(const char *pathname, char *sbuild_id); |
17 | 17 | ||
18 | char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); | 18 | char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); |
19 | bool dso__build_id_is_kmod(const struct dso *dso, char *bf, size_t size); | ||
19 | 20 | ||
20 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | 21 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, |
21 | struct perf_sample *sample, struct perf_evsel *evsel, | 22 | struct perf_sample *sample, struct perf_evsel *evsel, |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 07b5d63947b1..1f5a93c2c9a2 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -23,14 +23,17 @@ | |||
23 | #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" | 23 | #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" |
24 | #define PERF_PAGER_ENVIRONMENT "PERF_PAGER" | 24 | #define PERF_PAGER_ENVIRONMENT "PERF_PAGER" |
25 | 25 | ||
26 | extern const char *config_exclusive_filename; | ||
27 | |||
26 | typedef int (*config_fn_t)(const char *, const char *, void *); | 28 | typedef int (*config_fn_t)(const char *, const char *, void *); |
27 | extern int perf_default_config(const char *, const char *, void *); | 29 | int perf_default_config(const char *, const char *, void *); |
28 | extern int perf_config(config_fn_t fn, void *); | 30 | int perf_config(config_fn_t fn, void *); |
29 | extern int perf_config_int(const char *, const char *); | 31 | int perf_config_int(const char *, const char *); |
30 | extern u64 perf_config_u64(const char *, const char *); | 32 | u64 perf_config_u64(const char *, const char *); |
31 | extern int perf_config_bool(const char *, const char *); | 33 | int perf_config_bool(const char *, const char *); |
32 | extern int config_error_nonbool(const char *); | 34 | int config_error_nonbool(const char *); |
33 | extern const char *perf_config_dirname(const char *, const char *); | 35 | const char *perf_config_dirname(const char *, const char *); |
36 | const char *perf_etc_perfconfig(void); | ||
34 | 37 | ||
35 | char *alias_lookup(const char *alias); | 38 | char *alias_lookup(const char *alias); |
36 | int split_cmdline(char *cmdline, const char ***argv); | 39 | int split_cmdline(char *cmdline, const char ***argv); |
@@ -61,13 +64,9 @@ static inline int is_absolute_path(const char *path) | |||
61 | return path[0] == '/'; | 64 | return path[0] == '/'; |
62 | } | 65 | } |
63 | 66 | ||
64 | const char *make_nonrelative_path(const char *path); | ||
65 | char *strip_path_suffix(const char *path, const char *suffix); | 67 | char *strip_path_suffix(const char *path, const char *suffix); |
66 | 68 | ||
67 | extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | 69 | char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); |
68 | extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); | 70 | char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); |
69 | |||
70 | extern char *perf_pathdup(const char *fmt, ...) | ||
71 | __attribute__((format (printf, 1, 2))); | ||
72 | 71 | ||
73 | #endif /* __PERF_CACHE_H */ | 72 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 53c43eb9489e..24b4bd0d7754 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -416,7 +416,7 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
416 | /* | 416 | /* |
417 | * Fill the node with callchain values | 417 | * Fill the node with callchain values |
418 | */ | 418 | */ |
419 | static void | 419 | static int |
420 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | 420 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
421 | { | 421 | { |
422 | struct callchain_cursor_node *cursor_node; | 422 | struct callchain_cursor_node *cursor_node; |
@@ -433,7 +433,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
433 | call = zalloc(sizeof(*call)); | 433 | call = zalloc(sizeof(*call)); |
434 | if (!call) { | 434 | if (!call) { |
435 | perror("not enough memory for the code path tree"); | 435 | perror("not enough memory for the code path tree"); |
436 | return; | 436 | return -1; |
437 | } | 437 | } |
438 | call->ip = cursor_node->ip; | 438 | call->ip = cursor_node->ip; |
439 | call->ms.sym = cursor_node->sym; | 439 | call->ms.sym = cursor_node->sym; |
@@ -443,6 +443,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
443 | callchain_cursor_advance(cursor); | 443 | callchain_cursor_advance(cursor); |
444 | cursor_node = callchain_cursor_current(cursor); | 444 | cursor_node = callchain_cursor_current(cursor); |
445 | } | 445 | } |
446 | return 0; | ||
446 | } | 447 | } |
447 | 448 | ||
448 | static struct callchain_node * | 449 | static struct callchain_node * |
@@ -453,7 +454,19 @@ add_child(struct callchain_node *parent, | |||
453 | struct callchain_node *new; | 454 | struct callchain_node *new; |
454 | 455 | ||
455 | new = create_child(parent, false); | 456 | new = create_child(parent, false); |
456 | fill_node(new, cursor); | 457 | if (new == NULL) |
458 | return NULL; | ||
459 | |||
460 | if (fill_node(new, cursor) < 0) { | ||
461 | struct callchain_list *call, *tmp; | ||
462 | |||
463 | list_for_each_entry_safe(call, tmp, &new->val, list) { | ||
464 | list_del(&call->list); | ||
465 | free(call); | ||
466 | } | ||
467 | free(new); | ||
468 | return NULL; | ||
469 | } | ||
457 | 470 | ||
458 | new->children_hit = 0; | 471 | new->children_hit = 0; |
459 | new->hit = period; | 472 | new->hit = period; |
@@ -462,16 +475,32 @@ add_child(struct callchain_node *parent, | |||
462 | return new; | 475 | return new; |
463 | } | 476 | } |
464 | 477 | ||
465 | static s64 match_chain(struct callchain_cursor_node *node, | 478 | enum match_result { |
466 | struct callchain_list *cnode) | 479 | MATCH_ERROR = -1, |
480 | MATCH_EQ, | ||
481 | MATCH_LT, | ||
482 | MATCH_GT, | ||
483 | }; | ||
484 | |||
485 | static enum match_result match_chain(struct callchain_cursor_node *node, | ||
486 | struct callchain_list *cnode) | ||
467 | { | 487 | { |
468 | struct symbol *sym = node->sym; | 488 | struct symbol *sym = node->sym; |
489 | u64 left, right; | ||
469 | 490 | ||
470 | if (cnode->ms.sym && sym && | 491 | if (cnode->ms.sym && sym && |
471 | callchain_param.key == CCKEY_FUNCTION) | 492 | callchain_param.key == CCKEY_FUNCTION) { |
472 | return cnode->ms.sym->start - sym->start; | 493 | left = cnode->ms.sym->start; |
473 | else | 494 | right = sym->start; |
474 | return cnode->ip - node->ip; | 495 | } else { |
496 | left = cnode->ip; | ||
497 | right = node->ip; | ||
498 | } | ||
499 | |||
500 | if (left == right) | ||
501 | return MATCH_EQ; | ||
502 | |||
503 | return left > right ? MATCH_GT : MATCH_LT; | ||
475 | } | 504 | } |
476 | 505 | ||
477 | /* | 506 | /* |
@@ -479,7 +508,7 @@ static s64 match_chain(struct callchain_cursor_node *node, | |||
479 | * give a part of its callchain to the created child. | 508 | * give a part of its callchain to the created child. |
480 | * Then create another child to host the given callchain of new branch | 509 | * Then create another child to host the given callchain of new branch |
481 | */ | 510 | */ |
482 | static void | 511 | static int |
483 | split_add_child(struct callchain_node *parent, | 512 | split_add_child(struct callchain_node *parent, |
484 | struct callchain_cursor *cursor, | 513 | struct callchain_cursor *cursor, |
485 | struct callchain_list *to_split, | 514 | struct callchain_list *to_split, |
@@ -491,6 +520,8 @@ split_add_child(struct callchain_node *parent, | |||
491 | 520 | ||
492 | /* split */ | 521 | /* split */ |
493 | new = create_child(parent, true); | 522 | new = create_child(parent, true); |
523 | if (new == NULL) | ||
524 | return -1; | ||
494 | 525 | ||
495 | /* split the callchain and move a part to the new child */ | 526 | /* split the callchain and move a part to the new child */ |
496 | old_tail = parent->val.prev; | 527 | old_tail = parent->val.prev; |
@@ -524,6 +555,8 @@ split_add_child(struct callchain_node *parent, | |||
524 | 555 | ||
525 | node = callchain_cursor_current(cursor); | 556 | node = callchain_cursor_current(cursor); |
526 | new = add_child(parent, cursor, period); | 557 | new = add_child(parent, cursor, period); |
558 | if (new == NULL) | ||
559 | return -1; | ||
527 | 560 | ||
528 | /* | 561 | /* |
529 | * This is second child since we moved parent's children | 562 | * This is second child since we moved parent's children |
@@ -534,7 +567,7 @@ split_add_child(struct callchain_node *parent, | |||
534 | cnode = list_first_entry(&first->val, struct callchain_list, | 567 | cnode = list_first_entry(&first->val, struct callchain_list, |
535 | list); | 568 | list); |
536 | 569 | ||
537 | if (match_chain(node, cnode) < 0) | 570 | if (match_chain(node, cnode) == MATCH_LT) |
538 | pp = &p->rb_left; | 571 | pp = &p->rb_left; |
539 | else | 572 | else |
540 | pp = &p->rb_right; | 573 | pp = &p->rb_right; |
@@ -545,14 +578,15 @@ split_add_child(struct callchain_node *parent, | |||
545 | parent->hit = period; | 578 | parent->hit = period; |
546 | parent->count = 1; | 579 | parent->count = 1; |
547 | } | 580 | } |
581 | return 0; | ||
548 | } | 582 | } |
549 | 583 | ||
550 | static int | 584 | static enum match_result |
551 | append_chain(struct callchain_node *root, | 585 | append_chain(struct callchain_node *root, |
552 | struct callchain_cursor *cursor, | 586 | struct callchain_cursor *cursor, |
553 | u64 period); | 587 | u64 period); |
554 | 588 | ||
555 | static void | 589 | static int |
556 | append_chain_children(struct callchain_node *root, | 590 | append_chain_children(struct callchain_node *root, |
557 | struct callchain_cursor *cursor, | 591 | struct callchain_cursor *cursor, |
558 | u64 period) | 592 | u64 period) |
@@ -564,36 +598,42 @@ append_chain_children(struct callchain_node *root, | |||
564 | 598 | ||
565 | node = callchain_cursor_current(cursor); | 599 | node = callchain_cursor_current(cursor); |
566 | if (!node) | 600 | if (!node) |
567 | return; | 601 | return -1; |
568 | 602 | ||
569 | /* lookup in childrens */ | 603 | /* lookup in childrens */ |
570 | while (*p) { | 604 | while (*p) { |
571 | s64 ret; | 605 | enum match_result ret; |
572 | 606 | ||
573 | parent = *p; | 607 | parent = *p; |
574 | rnode = rb_entry(parent, struct callchain_node, rb_node_in); | 608 | rnode = rb_entry(parent, struct callchain_node, rb_node_in); |
575 | 609 | ||
576 | /* If at least first entry matches, rely to children */ | 610 | /* If at least first entry matches, rely to children */ |
577 | ret = append_chain(rnode, cursor, period); | 611 | ret = append_chain(rnode, cursor, period); |
578 | if (ret == 0) | 612 | if (ret == MATCH_EQ) |
579 | goto inc_children_hit; | 613 | goto inc_children_hit; |
614 | if (ret == MATCH_ERROR) | ||
615 | return -1; | ||
580 | 616 | ||
581 | if (ret < 0) | 617 | if (ret == MATCH_LT) |
582 | p = &parent->rb_left; | 618 | p = &parent->rb_left; |
583 | else | 619 | else |
584 | p = &parent->rb_right; | 620 | p = &parent->rb_right; |
585 | } | 621 | } |
586 | /* nothing in children, add to the current node */ | 622 | /* nothing in children, add to the current node */ |
587 | rnode = add_child(root, cursor, period); | 623 | rnode = add_child(root, cursor, period); |
624 | if (rnode == NULL) | ||
625 | return -1; | ||
626 | |||
588 | rb_link_node(&rnode->rb_node_in, parent, p); | 627 | rb_link_node(&rnode->rb_node_in, parent, p); |
589 | rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); | 628 | rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); |
590 | 629 | ||
591 | inc_children_hit: | 630 | inc_children_hit: |
592 | root->children_hit += period; | 631 | root->children_hit += period; |
593 | root->children_count++; | 632 | root->children_count++; |
633 | return 0; | ||
594 | } | 634 | } |
595 | 635 | ||
596 | static int | 636 | static enum match_result |
597 | append_chain(struct callchain_node *root, | 637 | append_chain(struct callchain_node *root, |
598 | struct callchain_cursor *cursor, | 638 | struct callchain_cursor *cursor, |
599 | u64 period) | 639 | u64 period) |
@@ -602,7 +642,7 @@ append_chain(struct callchain_node *root, | |||
602 | u64 start = cursor->pos; | 642 | u64 start = cursor->pos; |
603 | bool found = false; | 643 | bool found = false; |
604 | u64 matches; | 644 | u64 matches; |
605 | int cmp = 0; | 645 | enum match_result cmp = MATCH_ERROR; |
606 | 646 | ||
607 | /* | 647 | /* |
608 | * Lookup in the current node | 648 | * Lookup in the current node |
@@ -618,7 +658,7 @@ append_chain(struct callchain_node *root, | |||
618 | break; | 658 | break; |
619 | 659 | ||
620 | cmp = match_chain(node, cnode); | 660 | cmp = match_chain(node, cnode); |
621 | if (cmp) | 661 | if (cmp != MATCH_EQ) |
622 | break; | 662 | break; |
623 | 663 | ||
624 | found = true; | 664 | found = true; |
@@ -628,7 +668,7 @@ append_chain(struct callchain_node *root, | |||
628 | 668 | ||
629 | /* matches not, relay no the parent */ | 669 | /* matches not, relay no the parent */ |
630 | if (!found) { | 670 | if (!found) { |
631 | WARN_ONCE(!cmp, "Chain comparison error\n"); | 671 | WARN_ONCE(cmp == MATCH_ERROR, "Chain comparison error\n"); |
632 | return cmp; | 672 | return cmp; |
633 | } | 673 | } |
634 | 674 | ||
@@ -636,21 +676,25 @@ append_chain(struct callchain_node *root, | |||
636 | 676 | ||
637 | /* we match only a part of the node. Split it and add the new chain */ | 677 | /* we match only a part of the node. Split it and add the new chain */ |
638 | if (matches < root->val_nr) { | 678 | if (matches < root->val_nr) { |
639 | split_add_child(root, cursor, cnode, start, matches, period); | 679 | if (split_add_child(root, cursor, cnode, start, matches, |
640 | return 0; | 680 | period) < 0) |
681 | return MATCH_ERROR; | ||
682 | |||
683 | return MATCH_EQ; | ||
641 | } | 684 | } |
642 | 685 | ||
643 | /* we match 100% of the path, increment the hit */ | 686 | /* we match 100% of the path, increment the hit */ |
644 | if (matches == root->val_nr && cursor->pos == cursor->nr) { | 687 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
645 | root->hit += period; | 688 | root->hit += period; |
646 | root->count++; | 689 | root->count++; |
647 | return 0; | 690 | return MATCH_EQ; |
648 | } | 691 | } |
649 | 692 | ||
650 | /* We match the node and still have a part remaining */ | 693 | /* We match the node and still have a part remaining */ |
651 | append_chain_children(root, cursor, period); | 694 | if (append_chain_children(root, cursor, period) < 0) |
695 | return MATCH_ERROR; | ||
652 | 696 | ||
653 | return 0; | 697 | return MATCH_EQ; |
654 | } | 698 | } |
655 | 699 | ||
656 | int callchain_append(struct callchain_root *root, | 700 | int callchain_append(struct callchain_root *root, |
@@ -662,7 +706,8 @@ int callchain_append(struct callchain_root *root, | |||
662 | 706 | ||
663 | callchain_cursor_commit(cursor); | 707 | callchain_cursor_commit(cursor); |
664 | 708 | ||
665 | append_chain_children(&root->node, cursor, period); | 709 | if (append_chain_children(&root->node, cursor, period) < 0) |
710 | return -1; | ||
666 | 711 | ||
667 | if (cursor->nr > root->max_depth) | 712 | if (cursor->nr > root->max_depth) |
668 | root->max_depth = cursor->nr; | 713 | root->max_depth = cursor->nr; |
@@ -690,7 +735,8 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
690 | 735 | ||
691 | if (src->hit) { | 736 | if (src->hit) { |
692 | callchain_cursor_commit(cursor); | 737 | callchain_cursor_commit(cursor); |
693 | append_chain_children(dst, cursor, src->hit); | 738 | if (append_chain_children(dst, cursor, src->hit) < 0) |
739 | return -1; | ||
694 | } | 740 | } |
695 | 741 | ||
696 | n = rb_first(&src->rb_root_in); | 742 | n = rb_first(&src->rb_root_in); |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 18dd22269764..d2a9e694810c 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -220,7 +220,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
220 | bool hide_unresolved); | 220 | bool hide_unresolved); |
221 | 221 | ||
222 | extern const char record_callchain_help[]; | 222 | extern const char record_callchain_help[]; |
223 | extern int parse_callchain_record(const char *arg, struct callchain_param *param); | 223 | int parse_callchain_record(const char *arg, struct callchain_param *param); |
224 | int parse_callchain_record_opt(const char *arg, struct callchain_param *param); | 224 | int parse_callchain_record_opt(const char *arg, struct callchain_param *param); |
225 | int parse_callchain_report_opt(const char *arg); | 225 | int parse_callchain_report_opt(const char *arg); |
226 | int parse_callchain_top_opt(const char *arg); | 226 | int parse_callchain_top_opt(const char *arg); |
@@ -236,7 +236,7 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | |||
236 | } | 236 | } |
237 | 237 | ||
238 | #ifdef HAVE_SKIP_CALLCHAIN_IDX | 238 | #ifdef HAVE_SKIP_CALLCHAIN_IDX |
239 | extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain); | 239 | int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain); |
240 | #else | 240 | #else |
241 | static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, | 241 | static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, |
242 | struct ip_callchain *chain __maybe_unused) | 242 | struct ip_callchain *chain __maybe_unused) |
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h index b4b8cb42fe5e..31f8dcdbd7ef 100644 --- a/tools/perf/util/cgroup.h +++ b/tools/perf/util/cgroup.h | |||
@@ -13,7 +13,7 @@ struct cgroup_sel { | |||
13 | 13 | ||
14 | 14 | ||
15 | extern int nr_cgroups; /* number of explicit cgroups defined */ | 15 | extern int nr_cgroups; /* number of explicit cgroups defined */ |
16 | extern void close_cgroup(struct cgroup_sel *cgrp); | 16 | void close_cgroup(struct cgroup_sel *cgrp); |
17 | extern int parse_cgroups(const struct option *opt, const char *str, int unset); | 17 | int parse_cgroups(const struct option *opt, const char *str, int unset); |
18 | 18 | ||
19 | #endif /* __CGROUP_H__ */ | 19 | #endif /* __CGROUP_H__ */ |
diff --git a/tools/perf/util/cloexec.h b/tools/perf/util/cloexec.h index 3bee6773ddb0..d0d465953d36 100644 --- a/tools/perf/util/cloexec.h +++ b/tools/perf/util/cloexec.h | |||
@@ -5,7 +5,7 @@ unsigned long perf_event_open_cloexec_flag(void); | |||
5 | 5 | ||
6 | #ifdef __GLIBC_PREREQ | 6 | #ifdef __GLIBC_PREREQ |
7 | #if !__GLIBC_PREREQ(2, 6) && !defined(__UCLIBC__) | 7 | #if !__GLIBC_PREREQ(2, 6) && !defined(__UCLIBC__) |
8 | extern int sched_getcpu(void) __THROW; | 8 | int sched_getcpu(void) __THROW; |
9 | #endif | 9 | #endif |
10 | #endif | 10 | #endif |
11 | 11 | ||
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e5fb88bab9e1..43e84aa27e4a 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -32,14 +32,15 @@ int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) | |||
32 | return 0; | 32 | return 0; |
33 | } | 33 | } |
34 | 34 | ||
35 | int perf_color_default_config(const char *var, const char *value, void *cb) | 35 | int perf_color_default_config(const char *var, const char *value, |
36 | void *cb __maybe_unused) | ||
36 | { | 37 | { |
37 | if (!strcmp(var, "color.ui")) { | 38 | if (!strcmp(var, "color.ui")) { |
38 | perf_use_color_default = perf_config_colorbool(var, value, -1); | 39 | perf_use_color_default = perf_config_colorbool(var, value, -1); |
39 | return 0; | 40 | return 0; |
40 | } | 41 | } |
41 | 42 | ||
42 | return perf_default_config(var, value, cb); | 43 | return 0; |
43 | } | 44 | } |
44 | 45 | ||
45 | static int __color_vsnprintf(char *bf, size_t size, const char *color, | 46 | static int __color_vsnprintf(char *bf, size_t size, const char *color, |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index d3e12e30e1d5..4e727635476e 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -26,7 +26,7 @@ static const char *config_file_name; | |||
26 | static int config_linenr; | 26 | static int config_linenr; |
27 | static int config_file_eof; | 27 | static int config_file_eof; |
28 | 28 | ||
29 | static const char *config_exclusive_filename; | 29 | const char *config_exclusive_filename; |
30 | 30 | ||
31 | static int get_next_char(void) | 31 | static int get_next_char(void) |
32 | { | 32 | { |
@@ -434,7 +434,7 @@ static int perf_config_from_file(config_fn_t fn, const char *filename, void *dat | |||
434 | return ret; | 434 | return ret; |
435 | } | 435 | } |
436 | 436 | ||
437 | static const char *perf_etc_perfconfig(void) | 437 | const char *perf_etc_perfconfig(void) |
438 | { | 438 | { |
439 | static const char *system_wide; | 439 | static const char *system_wide; |
440 | if (!system_wide) | 440 | if (!system_wide) |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index fa935093a599..9bcf2bed3a6d 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -8,6 +8,10 @@ | |||
8 | #include <linux/bitmap.h> | 8 | #include <linux/bitmap.h> |
9 | #include "asm/bug.h" | 9 | #include "asm/bug.h" |
10 | 10 | ||
11 | static int max_cpu_num; | ||
12 | static int max_node_num; | ||
13 | static int *cpunode_map; | ||
14 | |||
11 | static struct cpu_map *cpu_map__default_new(void) | 15 | static struct cpu_map *cpu_map__default_new(void) |
12 | { | 16 | { |
13 | struct cpu_map *cpus; | 17 | struct cpu_map *cpus; |
@@ -486,6 +490,32 @@ out: | |||
486 | pr_err("Failed to read max nodes, using default of %d\n", max_node_num); | 490 | pr_err("Failed to read max nodes, using default of %d\n", max_node_num); |
487 | } | 491 | } |
488 | 492 | ||
493 | int cpu__max_node(void) | ||
494 | { | ||
495 | if (unlikely(!max_node_num)) | ||
496 | set_max_node_num(); | ||
497 | |||
498 | return max_node_num; | ||
499 | } | ||
500 | |||
501 | int cpu__max_cpu(void) | ||
502 | { | ||
503 | if (unlikely(!max_cpu_num)) | ||
504 | set_max_cpu_num(); | ||
505 | |||
506 | return max_cpu_num; | ||
507 | } | ||
508 | |||
509 | int cpu__get_node(int cpu) | ||
510 | { | ||
511 | if (unlikely(cpunode_map == NULL)) { | ||
512 | pr_debug("cpu_map not initialized\n"); | ||
513 | return -1; | ||
514 | } | ||
515 | |||
516 | return cpunode_map[cpu]; | ||
517 | } | ||
518 | |||
489 | static int init_cpunode_map(void) | 519 | static int init_cpunode_map(void) |
490 | { | 520 | { |
491 | int i; | 521 | int i; |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 71c41b9efabb..81a2562aaa2b 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -57,37 +57,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map) | |||
57 | return map ? map->map[0] == -1 : true; | 57 | return map ? map->map[0] == -1 : true; |
58 | } | 58 | } |
59 | 59 | ||
60 | int max_cpu_num; | ||
61 | int max_node_num; | ||
62 | int *cpunode_map; | ||
63 | |||
64 | int cpu__setup_cpunode_map(void); | 60 | int cpu__setup_cpunode_map(void); |
65 | 61 | ||
66 | static inline int cpu__max_node(void) | 62 | int cpu__max_node(void); |
67 | { | 63 | int cpu__max_cpu(void); |
68 | if (unlikely(!max_node_num)) | 64 | int cpu__get_node(int cpu); |
69 | pr_debug("cpu_map not initialized\n"); | ||
70 | |||
71 | return max_node_num; | ||
72 | } | ||
73 | |||
74 | static inline int cpu__max_cpu(void) | ||
75 | { | ||
76 | if (unlikely(!max_cpu_num)) | ||
77 | pr_debug("cpu_map not initialized\n"); | ||
78 | |||
79 | return max_cpu_num; | ||
80 | } | ||
81 | |||
82 | static inline int cpu__get_node(int cpu) | ||
83 | { | ||
84 | if (unlikely(cpunode_map == NULL)) { | ||
85 | pr_debug("cpu_map not initialized\n"); | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | return cpunode_map[cpu]; | ||
90 | } | ||
91 | 65 | ||
92 | int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, | 66 | int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, |
93 | int (*f)(struct cpu_map *map, int cpu, void *data), | 67 | int (*f)(struct cpu_map *map, int cpu, void *data), |
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index aada3ac5e891..d4a5a21c2a7e 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
@@ -32,8 +32,17 @@ unsigned char sane_ctype[256] = { | |||
32 | 32 | ||
33 | const char *graph_line = | 33 | const char *graph_line = |
34 | "_____________________________________________________________________" | 34 | "_____________________________________________________________________" |
35 | "_____________________________________________________________________" | ||
35 | "_____________________________________________________________________"; | 36 | "_____________________________________________________________________"; |
36 | const char *graph_dotted_line = | 37 | const char *graph_dotted_line = |
37 | "---------------------------------------------------------------------" | 38 | "---------------------------------------------------------------------" |
38 | "---------------------------------------------------------------------" | 39 | "---------------------------------------------------------------------" |
39 | "---------------------------------------------------------------------"; | 40 | "---------------------------------------------------------------------"; |
41 | const char *spaces = | ||
42 | " " | ||
43 | " " | ||
44 | " "; | ||
45 | const char *dots = | ||
46 | "....................................................................." | ||
47 | "....................................................................." | ||
48 | "....................................................................."; | ||
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 34cd1e4039d3..bbf69d248ec5 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c | |||
@@ -352,6 +352,84 @@ static int add_tracepoint_values(struct ctf_writer *cw, | |||
352 | return ret; | 352 | return ret; |
353 | } | 353 | } |
354 | 354 | ||
355 | static int | ||
356 | add_bpf_output_values(struct bt_ctf_event_class *event_class, | ||
357 | struct bt_ctf_event *event, | ||
358 | struct perf_sample *sample) | ||
359 | { | ||
360 | struct bt_ctf_field_type *len_type, *seq_type; | ||
361 | struct bt_ctf_field *len_field, *seq_field; | ||
362 | unsigned int raw_size = sample->raw_size; | ||
363 | unsigned int nr_elements = raw_size / sizeof(u32); | ||
364 | unsigned int i; | ||
365 | int ret; | ||
366 | |||
367 | if (nr_elements * sizeof(u32) != raw_size) | ||
368 | pr_warning("Incorrect raw_size (%u) in bpf output event, skip %lu bytes\n", | ||
369 | raw_size, nr_elements * sizeof(u32) - raw_size); | ||
370 | |||
371 | len_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_len"); | ||
372 | len_field = bt_ctf_field_create(len_type); | ||
373 | if (!len_field) { | ||
374 | pr_err("failed to create 'raw_len' for bpf output event\n"); | ||
375 | ret = -1; | ||
376 | goto put_len_type; | ||
377 | } | ||
378 | |||
379 | ret = bt_ctf_field_unsigned_integer_set_value(len_field, nr_elements); | ||
380 | if (ret) { | ||
381 | pr_err("failed to set field value for raw_len\n"); | ||
382 | goto put_len_field; | ||
383 | } | ||
384 | ret = bt_ctf_event_set_payload(event, "raw_len", len_field); | ||
385 | if (ret) { | ||
386 | pr_err("failed to set payload to raw_len\n"); | ||
387 | goto put_len_field; | ||
388 | } | ||
389 | |||
390 | seq_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_data"); | ||
391 | seq_field = bt_ctf_field_create(seq_type); | ||
392 | if (!seq_field) { | ||
393 | pr_err("failed to create 'raw_data' for bpf output event\n"); | ||
394 | ret = -1; | ||
395 | goto put_seq_type; | ||
396 | } | ||
397 | |||
398 | ret = bt_ctf_field_sequence_set_length(seq_field, len_field); | ||
399 | if (ret) { | ||
400 | pr_err("failed to set length of 'raw_data'\n"); | ||
401 | goto put_seq_field; | ||
402 | } | ||
403 | |||
404 | for (i = 0; i < nr_elements; i++) { | ||
405 | struct bt_ctf_field *elem_field = | ||
406 | bt_ctf_field_sequence_get_field(seq_field, i); | ||
407 | |||
408 | ret = bt_ctf_field_unsigned_integer_set_value(elem_field, | ||
409 | ((u32 *)(sample->raw_data))[i]); | ||
410 | |||
411 | bt_ctf_field_put(elem_field); | ||
412 | if (ret) { | ||
413 | pr_err("failed to set raw_data[%d]\n", i); | ||
414 | goto put_seq_field; | ||
415 | } | ||
416 | } | ||
417 | |||
418 | ret = bt_ctf_event_set_payload(event, "raw_data", seq_field); | ||
419 | if (ret) | ||
420 | pr_err("failed to set payload for raw_data\n"); | ||
421 | |||
422 | put_seq_field: | ||
423 | bt_ctf_field_put(seq_field); | ||
424 | put_seq_type: | ||
425 | bt_ctf_field_type_put(seq_type); | ||
426 | put_len_field: | ||
427 | bt_ctf_field_put(len_field); | ||
428 | put_len_type: | ||
429 | bt_ctf_field_type_put(len_type); | ||
430 | return ret; | ||
431 | } | ||
432 | |||
355 | static int add_generic_values(struct ctf_writer *cw, | 433 | static int add_generic_values(struct ctf_writer *cw, |
356 | struct bt_ctf_event *event, | 434 | struct bt_ctf_event *event, |
357 | struct perf_evsel *evsel, | 435 | struct perf_evsel *evsel, |
@@ -554,7 +632,7 @@ static bool is_flush_needed(struct ctf_stream *cs) | |||
554 | } | 632 | } |
555 | 633 | ||
556 | static int process_sample_event(struct perf_tool *tool, | 634 | static int process_sample_event(struct perf_tool *tool, |
557 | union perf_event *_event __maybe_unused, | 635 | union perf_event *_event, |
558 | struct perf_sample *sample, | 636 | struct perf_sample *sample, |
559 | struct perf_evsel *evsel, | 637 | struct perf_evsel *evsel, |
560 | struct machine *machine __maybe_unused) | 638 | struct machine *machine __maybe_unused) |
@@ -597,6 +675,12 @@ static int process_sample_event(struct perf_tool *tool, | |||
597 | return -1; | 675 | return -1; |
598 | } | 676 | } |
599 | 677 | ||
678 | if (perf_evsel__is_bpf_output(evsel)) { | ||
679 | ret = add_bpf_output_values(event_class, event, sample); | ||
680 | if (ret) | ||
681 | return -1; | ||
682 | } | ||
683 | |||
600 | cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel)); | 684 | cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel)); |
601 | if (cs) { | 685 | if (cs) { |
602 | if (is_flush_needed(cs)) | 686 | if (is_flush_needed(cs)) |
@@ -744,6 +828,25 @@ static int add_tracepoint_types(struct ctf_writer *cw, | |||
744 | return ret; | 828 | return ret; |
745 | } | 829 | } |
746 | 830 | ||
831 | static int add_bpf_output_types(struct ctf_writer *cw, | ||
832 | struct bt_ctf_event_class *class) | ||
833 | { | ||
834 | struct bt_ctf_field_type *len_type = cw->data.u32; | ||
835 | struct bt_ctf_field_type *seq_base_type = cw->data.u32_hex; | ||
836 | struct bt_ctf_field_type *seq_type; | ||
837 | int ret; | ||
838 | |||
839 | ret = bt_ctf_event_class_add_field(class, len_type, "raw_len"); | ||
840 | if (ret) | ||
841 | return ret; | ||
842 | |||
843 | seq_type = bt_ctf_field_type_sequence_create(seq_base_type, "raw_len"); | ||
844 | if (!seq_type) | ||
845 | return -1; | ||
846 | |||
847 | return bt_ctf_event_class_add_field(class, seq_type, "raw_data"); | ||
848 | } | ||
849 | |||
747 | static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, | 850 | static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, |
748 | struct bt_ctf_event_class *event_class) | 851 | struct bt_ctf_event_class *event_class) |
749 | { | 852 | { |
@@ -755,7 +858,8 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, | |||
755 | * ctf event header | 858 | * ctf event header |
756 | * PERF_SAMPLE_READ - TODO | 859 | * PERF_SAMPLE_READ - TODO |
757 | * PERF_SAMPLE_CALLCHAIN - TODO | 860 | * PERF_SAMPLE_CALLCHAIN - TODO |
758 | * PERF_SAMPLE_RAW - tracepoint fields are handled separately | 861 | * PERF_SAMPLE_RAW - tracepoint fields and BPF output |
862 | * are handled separately | ||
759 | * PERF_SAMPLE_BRANCH_STACK - TODO | 863 | * PERF_SAMPLE_BRANCH_STACK - TODO |
760 | * PERF_SAMPLE_REGS_USER - TODO | 864 | * PERF_SAMPLE_REGS_USER - TODO |
761 | * PERF_SAMPLE_STACK_USER - TODO | 865 | * PERF_SAMPLE_STACK_USER - TODO |
@@ -824,6 +928,12 @@ static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel) | |||
824 | goto err; | 928 | goto err; |
825 | } | 929 | } |
826 | 930 | ||
931 | if (perf_evsel__is_bpf_output(evsel)) { | ||
932 | ret = add_bpf_output_types(cw, event_class); | ||
933 | if (ret) | ||
934 | goto err; | ||
935 | } | ||
936 | |||
827 | ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class); | 937 | ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class); |
828 | if (ret) { | 938 | if (ret) { |
829 | pr("Failed to add event class into stream.\n"); | 939 | pr("Failed to add event class into stream.\n"); |
@@ -858,6 +968,23 @@ static int setup_events(struct ctf_writer *cw, struct perf_session *session) | |||
858 | return 0; | 968 | return 0; |
859 | } | 969 | } |
860 | 970 | ||
971 | static void cleanup_events(struct perf_session *session) | ||
972 | { | ||
973 | struct perf_evlist *evlist = session->evlist; | ||
974 | struct perf_evsel *evsel; | ||
975 | |||
976 | evlist__for_each(evlist, evsel) { | ||
977 | struct evsel_priv *priv; | ||
978 | |||
979 | priv = evsel->priv; | ||
980 | bt_ctf_event_class_put(priv->event_class); | ||
981 | zfree(&evsel->priv); | ||
982 | } | ||
983 | |||
984 | perf_evlist__delete(evlist); | ||
985 | session->evlist = NULL; | ||
986 | } | ||
987 | |||
861 | static int setup_streams(struct ctf_writer *cw, struct perf_session *session) | 988 | static int setup_streams(struct ctf_writer *cw, struct perf_session *session) |
862 | { | 989 | { |
863 | struct ctf_stream **stream; | 990 | struct ctf_stream **stream; |
@@ -953,6 +1080,12 @@ static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex) | |||
953 | bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL)) | 1080 | bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL)) |
954 | goto err; | 1081 | goto err; |
955 | 1082 | ||
1083 | #if __BYTE_ORDER == __BIG_ENDIAN | ||
1084 | bt_ctf_field_type_set_byte_order(type, BT_CTF_BYTE_ORDER_BIG_ENDIAN); | ||
1085 | #else | ||
1086 | bt_ctf_field_type_set_byte_order(type, BT_CTF_BYTE_ORDER_LITTLE_ENDIAN); | ||
1087 | #endif | ||
1088 | |||
956 | pr2("Created type: INTEGER %d-bit %ssigned %s\n", | 1089 | pr2("Created type: INTEGER %d-bit %ssigned %s\n", |
957 | size, sign ? "un" : "", hex ? "hex" : ""); | 1090 | size, sign ? "un" : "", hex ? "hex" : ""); |
958 | return type; | 1091 | return type; |
@@ -1100,7 +1233,7 @@ static int convert__config(const char *var, const char *value, void *cb) | |||
1100 | return 0; | 1233 | return 0; |
1101 | } | 1234 | } |
1102 | 1235 | ||
1103 | return perf_default_config(var, value, cb); | 1236 | return 0; |
1104 | } | 1237 | } |
1105 | 1238 | ||
1106 | int bt_convert__perf2ctf(const char *input, const char *path, bool force) | 1239 | int bt_convert__perf2ctf(const char *input, const char *path, bool force) |
@@ -1171,6 +1304,7 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force) | |||
1171 | (double) c.events_size / 1024.0 / 1024.0, | 1304 | (double) c.events_size / 1024.0 / 1024.0, |
1172 | c.events_count); | 1305 | c.events_count); |
1173 | 1306 | ||
1307 | cleanup_events(session); | ||
1174 | perf_session__delete(session); | 1308 | perf_session__delete(session); |
1175 | ctf_writer__cleanup(cw); | 1309 | ctf_writer__cleanup(cw); |
1176 | 1310 | ||
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index 1c9689e4cc17..049438d51b9a 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c | |||
@@ -333,7 +333,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, | |||
333 | sample_addr_correlates_sym(&evsel->attr)) { | 333 | sample_addr_correlates_sym(&evsel->attr)) { |
334 | struct addr_location addr_al; | 334 | struct addr_location addr_al; |
335 | 335 | ||
336 | perf_event__preprocess_sample_addr(event, sample, thread, &addr_al); | 336 | thread__resolve(thread, &addr_al, sample); |
337 | err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id, | 337 | err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id, |
338 | &es.addr_sym_db_id, &es.addr_offset); | 338 | &es.addr_sym_db_id, &es.addr_offset); |
339 | if (err) | 339 | if (err) |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 86d9c7302598..8c4212abd19b 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <stdarg.h> | 6 | #include <stdarg.h> |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
8 | #include <api/debug.h> | ||
8 | 9 | ||
9 | #include "cache.h" | 10 | #include "cache.h" |
10 | #include "color.h" | 11 | #include "color.h" |
@@ -22,7 +23,7 @@ int debug_ordered_events; | |||
22 | static int redirect_to_stderr; | 23 | static int redirect_to_stderr; |
23 | int debug_data_convert; | 24 | int debug_data_convert; |
24 | 25 | ||
25 | static int _eprintf(int level, int var, const char *fmt, va_list args) | 26 | int veprintf(int level, int var, const char *fmt, va_list args) |
26 | { | 27 | { |
27 | int ret = 0; | 28 | int ret = 0; |
28 | 29 | ||
@@ -36,24 +37,19 @@ static int _eprintf(int level, int var, const char *fmt, va_list args) | |||
36 | return ret; | 37 | return ret; |
37 | } | 38 | } |
38 | 39 | ||
39 | int veprintf(int level, int var, const char *fmt, va_list args) | ||
40 | { | ||
41 | return _eprintf(level, var, fmt, args); | ||
42 | } | ||
43 | |||
44 | int eprintf(int level, int var, const char *fmt, ...) | 40 | int eprintf(int level, int var, const char *fmt, ...) |
45 | { | 41 | { |
46 | va_list args; | 42 | va_list args; |
47 | int ret; | 43 | int ret; |
48 | 44 | ||
49 | va_start(args, fmt); | 45 | va_start(args, fmt); |
50 | ret = _eprintf(level, var, fmt, args); | 46 | ret = veprintf(level, var, fmt, args); |
51 | va_end(args); | 47 | va_end(args); |
52 | 48 | ||
53 | return ret; | 49 | return ret; |
54 | } | 50 | } |
55 | 51 | ||
56 | static int __eprintf_time(u64 t, const char *fmt, va_list args) | 52 | static int veprintf_time(u64 t, const char *fmt, va_list args) |
57 | { | 53 | { |
58 | int ret = 0; | 54 | int ret = 0; |
59 | u64 secs, usecs, nsecs = t; | 55 | u64 secs, usecs, nsecs = t; |
@@ -75,7 +71,7 @@ int eprintf_time(int level, int var, u64 t, const char *fmt, ...) | |||
75 | 71 | ||
76 | if (var >= level) { | 72 | if (var >= level) { |
77 | va_start(args, fmt); | 73 | va_start(args, fmt); |
78 | ret = __eprintf_time(t, fmt, args); | 74 | ret = veprintf_time(t, fmt, args); |
79 | va_end(args); | 75 | va_end(args); |
80 | } | 76 | } |
81 | 77 | ||
@@ -91,7 +87,7 @@ void pr_stat(const char *fmt, ...) | |||
91 | va_list args; | 87 | va_list args; |
92 | 88 | ||
93 | va_start(args, fmt); | 89 | va_start(args, fmt); |
94 | _eprintf(1, verbose, fmt, args); | 90 | veprintf(1, verbose, fmt, args); |
95 | va_end(args); | 91 | va_end(args); |
96 | eprintf(1, verbose, "\n"); | 92 | eprintf(1, verbose, "\n"); |
97 | } | 93 | } |
@@ -110,40 +106,61 @@ int dump_printf(const char *fmt, ...) | |||
110 | return ret; | 106 | return ret; |
111 | } | 107 | } |
112 | 108 | ||
109 | static void trace_event_printer(enum binary_printer_ops op, | ||
110 | unsigned int val, void *extra) | ||
111 | { | ||
112 | const char *color = PERF_COLOR_BLUE; | ||
113 | union perf_event *event = (union perf_event *)extra; | ||
114 | unsigned char ch = (unsigned char)val; | ||
115 | |||
116 | switch (op) { | ||
117 | case BINARY_PRINT_DATA_BEGIN: | ||
118 | printf("."); | ||
119 | color_fprintf(stdout, color, "\n. ... raw event: size %d bytes\n", | ||
120 | event->header.size); | ||
121 | break; | ||
122 | case BINARY_PRINT_LINE_BEGIN: | ||
123 | printf("."); | ||
124 | break; | ||
125 | case BINARY_PRINT_ADDR: | ||
126 | color_fprintf(stdout, color, " %04x: ", val); | ||
127 | break; | ||
128 | case BINARY_PRINT_NUM_DATA: | ||
129 | color_fprintf(stdout, color, " %02x", val); | ||
130 | break; | ||
131 | case BINARY_PRINT_NUM_PAD: | ||
132 | color_fprintf(stdout, color, " "); | ||
133 | break; | ||
134 | case BINARY_PRINT_SEP: | ||
135 | color_fprintf(stdout, color, " "); | ||
136 | break; | ||
137 | case BINARY_PRINT_CHAR_DATA: | ||
138 | color_fprintf(stdout, color, "%c", | ||
139 | isprint(ch) ? ch : '.'); | ||
140 | break; | ||
141 | case BINARY_PRINT_CHAR_PAD: | ||
142 | color_fprintf(stdout, color, " "); | ||
143 | break; | ||
144 | case BINARY_PRINT_LINE_END: | ||
145 | color_fprintf(stdout, color, "\n"); | ||
146 | break; | ||
147 | case BINARY_PRINT_DATA_END: | ||
148 | printf("\n"); | ||
149 | break; | ||
150 | default: | ||
151 | break; | ||
152 | } | ||
153 | } | ||
154 | |||
113 | void trace_event(union perf_event *event) | 155 | void trace_event(union perf_event *event) |
114 | { | 156 | { |
115 | unsigned char *raw_event = (void *)event; | 157 | unsigned char *raw_event = (void *)event; |
116 | const char *color = PERF_COLOR_BLUE; | ||
117 | int i, j; | ||
118 | 158 | ||
119 | if (!dump_trace) | 159 | if (!dump_trace) |
120 | return; | 160 | return; |
121 | 161 | ||
122 | printf("."); | 162 | print_binary(raw_event, event->header.size, 16, |
123 | color_fprintf(stdout, color, "\n. ... raw event: size %d bytes\n", | 163 | trace_event_printer, event); |
124 | event->header.size); | ||
125 | |||
126 | for (i = 0; i < event->header.size; i++) { | ||
127 | if ((i & 15) == 0) { | ||
128 | printf("."); | ||
129 | color_fprintf(stdout, color, " %04x: ", i); | ||
130 | } | ||
131 | |||
132 | color_fprintf(stdout, color, " %02x", raw_event[i]); | ||
133 | |||
134 | if (((i & 15) == 15) || i == event->header.size-1) { | ||
135 | color_fprintf(stdout, color, " "); | ||
136 | for (j = 0; j < 15-(i & 15); j++) | ||
137 | color_fprintf(stdout, color, " "); | ||
138 | for (j = i & ~15; j <= i; j++) { | ||
139 | color_fprintf(stdout, color, "%c", | ||
140 | isprint(raw_event[j]) ? | ||
141 | raw_event[j] : '.'); | ||
142 | } | ||
143 | color_fprintf(stdout, color, "\n"); | ||
144 | } | ||
145 | } | ||
146 | printf(".\n"); | ||
147 | } | 164 | } |
148 | 165 | ||
149 | static struct debug_variable { | 166 | static struct debug_variable { |
@@ -192,3 +209,23 @@ int perf_debug_option(const char *str) | |||
192 | free(s); | 209 | free(s); |
193 | return 0; | 210 | return 0; |
194 | } | 211 | } |
212 | |||
213 | #define DEBUG_WRAPPER(__n, __l) \ | ||
214 | static int pr_ ## __n ## _wrapper(const char *fmt, ...) \ | ||
215 | { \ | ||
216 | va_list args; \ | ||
217 | int ret; \ | ||
218 | \ | ||
219 | va_start(args, fmt); \ | ||
220 | ret = veprintf(__l, verbose, fmt, args); \ | ||
221 | va_end(args); \ | ||
222 | return ret; \ | ||
223 | } | ||
224 | |||
225 | DEBUG_WRAPPER(warning, 0); | ||
226 | DEBUG_WRAPPER(debug, 1); | ||
227 | |||
228 | void perf_debug_setup(void) | ||
229 | { | ||
230 | libapi_set_print(pr_warning_wrapper, pr_warning_wrapper, pr_debug_wrapper); | ||
231 | } | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 8b9a088c32ab..14bafda79eda 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -53,5 +53,6 @@ int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__( | |||
53 | int veprintf(int level, int var, const char *fmt, va_list args); | 53 | int veprintf(int level, int var, const char *fmt, va_list args); |
54 | 54 | ||
55 | int perf_debug_option(const char *str); | 55 | int perf_debug_option(const char *str); |
56 | void perf_debug_setup(void); | ||
56 | 57 | ||
57 | #endif /* __PERF_DEBUG_H */ | 58 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/demangle-java.c b/tools/perf/util/demangle-java.c new file mode 100644 index 000000000000..3e6062ab2cdd --- /dev/null +++ b/tools/perf/util/demangle-java.c | |||
@@ -0,0 +1,199 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include "util.h" | ||
5 | #include "debug.h" | ||
6 | #include "symbol.h" | ||
7 | |||
8 | #include "demangle-java.h" | ||
9 | |||
10 | enum { | ||
11 | MODE_PREFIX = 0, | ||
12 | MODE_CLASS = 1, | ||
13 | MODE_FUNC = 2, | ||
14 | MODE_TYPE = 3, | ||
15 | MODE_CTYPE = 3, /* class arg */ | ||
16 | }; | ||
17 | |||
18 | #define BASE_ENT(c, n) [c - 'A']=n | ||
19 | static const char *base_types['Z' - 'A' + 1] = { | ||
20 | BASE_ENT('B', "byte" ), | ||
21 | BASE_ENT('C', "char" ), | ||
22 | BASE_ENT('D', "double" ), | ||
23 | BASE_ENT('F', "float" ), | ||
24 | BASE_ENT('I', "int" ), | ||
25 | BASE_ENT('J', "long" ), | ||
26 | BASE_ENT('S', "short" ), | ||
27 | BASE_ENT('Z', "bool" ), | ||
28 | }; | ||
29 | |||
30 | /* | ||
31 | * demangle Java symbol between str and end positions and stores | ||
32 | * up to maxlen characters into buf. The parser starts in mode. | ||
33 | * | ||
34 | * Use MODE_PREFIX to process entire prototype till end position | ||
35 | * Use MODE_TYPE to process return type if str starts on return type char | ||
36 | * | ||
37 | * Return: | ||
38 | * success: buf | ||
39 | * error : NULL | ||
40 | */ | ||
41 | static char * | ||
42 | __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode) | ||
43 | { | ||
44 | int rlen = 0; | ||
45 | int array = 0; | ||
46 | int narg = 0; | ||
47 | const char *q; | ||
48 | |||
49 | if (!end) | ||
50 | end = str + strlen(str); | ||
51 | |||
52 | for (q = str; q != end; q++) { | ||
53 | |||
54 | if (rlen == (maxlen - 1)) | ||
55 | break; | ||
56 | |||
57 | switch (*q) { | ||
58 | case 'L': | ||
59 | if (mode == MODE_PREFIX || mode == MODE_CTYPE) { | ||
60 | if (mode == MODE_CTYPE) { | ||
61 | if (narg) | ||
62 | rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); | ||
63 | narg++; | ||
64 | } | ||
65 | rlen += scnprintf(buf + rlen, maxlen - rlen, "class "); | ||
66 | if (mode == MODE_PREFIX) | ||
67 | mode = MODE_CLASS; | ||
68 | } else | ||
69 | buf[rlen++] = *q; | ||
70 | break; | ||
71 | case 'B': | ||
72 | case 'C': | ||
73 | case 'D': | ||
74 | case 'F': | ||
75 | case 'I': | ||
76 | case 'J': | ||
77 | case 'S': | ||
78 | case 'Z': | ||
79 | if (mode == MODE_TYPE) { | ||
80 | if (narg) | ||
81 | rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); | ||
82 | rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']); | ||
83 | while (array--) | ||
84 | rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); | ||
85 | array = 0; | ||
86 | narg++; | ||
87 | } else | ||
88 | buf[rlen++] = *q; | ||
89 | break; | ||
90 | case 'V': | ||
91 | if (mode == MODE_TYPE) { | ||
92 | rlen += scnprintf(buf + rlen, maxlen - rlen, "void"); | ||
93 | while (array--) | ||
94 | rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); | ||
95 | array = 0; | ||
96 | } else | ||
97 | buf[rlen++] = *q; | ||
98 | break; | ||
99 | case '[': | ||
100 | if (mode != MODE_TYPE) | ||
101 | goto error; | ||
102 | array++; | ||
103 | break; | ||
104 | case '(': | ||
105 | if (mode != MODE_FUNC) | ||
106 | goto error; | ||
107 | buf[rlen++] = *q; | ||
108 | mode = MODE_TYPE; | ||
109 | break; | ||
110 | case ')': | ||
111 | if (mode != MODE_TYPE) | ||
112 | goto error; | ||
113 | buf[rlen++] = *q; | ||
114 | narg = 0; | ||
115 | break; | ||
116 | case ';': | ||
117 | if (mode != MODE_CLASS && mode != MODE_CTYPE) | ||
118 | goto error; | ||
119 | /* safe because at least one other char to process */ | ||
120 | if (isalpha(*(q + 1))) | ||
121 | rlen += scnprintf(buf + rlen, maxlen - rlen, "."); | ||
122 | if (mode == MODE_CLASS) | ||
123 | mode = MODE_FUNC; | ||
124 | else if (mode == MODE_CTYPE) | ||
125 | mode = MODE_TYPE; | ||
126 | break; | ||
127 | case '/': | ||
128 | if (mode != MODE_CLASS && mode != MODE_CTYPE) | ||
129 | goto error; | ||
130 | rlen += scnprintf(buf + rlen, maxlen - rlen, "."); | ||
131 | break; | ||
132 | default : | ||
133 | buf[rlen++] = *q; | ||
134 | } | ||
135 | } | ||
136 | buf[rlen] = '\0'; | ||
137 | return buf; | ||
138 | error: | ||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Demangle Java function signature (openJDK, not GCJ) | ||
144 | * input: | ||
145 | * str: string to parse. String is not modified | ||
146 | * flags: comobination of JAVA_DEMANGLE_* flags to modify demangling | ||
147 | * return: | ||
148 | * if input can be demangled, then a newly allocated string is returned. | ||
149 | * if input cannot be demangled, then NULL is returned | ||
150 | * | ||
151 | * Note: caller is responsible for freeing demangled string | ||
152 | */ | ||
153 | char * | ||
154 | java_demangle_sym(const char *str, int flags) | ||
155 | { | ||
156 | char *buf, *ptr; | ||
157 | char *p; | ||
158 | size_t len, l1 = 0; | ||
159 | |||
160 | if (!str) | ||
161 | return NULL; | ||
162 | |||
163 | /* find start of retunr type */ | ||
164 | p = strrchr(str, ')'); | ||
165 | if (!p) | ||
166 | return NULL; | ||
167 | |||
168 | /* | ||
169 | * expansion factor estimated to 3x | ||
170 | */ | ||
171 | len = strlen(str) * 3 + 1; | ||
172 | buf = malloc(len); | ||
173 | if (!buf) | ||
174 | return NULL; | ||
175 | |||
176 | buf[0] = '\0'; | ||
177 | if (!(flags & JAVA_DEMANGLE_NORET)) { | ||
178 | /* | ||
179 | * get return type first | ||
180 | */ | ||
181 | ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE); | ||
182 | if (!ptr) | ||
183 | goto error; | ||
184 | |||
185 | /* add space between return type and function prototype */ | ||
186 | l1 = strlen(buf); | ||
187 | buf[l1++] = ' '; | ||
188 | } | ||
189 | |||
190 | /* process function up to return type */ | ||
191 | ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX); | ||
192 | if (!ptr) | ||
193 | goto error; | ||
194 | |||
195 | return buf; | ||
196 | error: | ||
197 | free(buf); | ||
198 | return NULL; | ||
199 | } | ||
diff --git a/tools/perf/util/demangle-java.h b/tools/perf/util/demangle-java.h new file mode 100644 index 000000000000..a981c1f968fe --- /dev/null +++ b/tools/perf/util/demangle-java.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef __PERF_DEMANGLE_JAVA | ||
2 | #define __PERF_DEMANGLE_JAVA 1 | ||
3 | /* | ||
4 | * demangle function flags | ||
5 | */ | ||
6 | #define JAVA_DEMANGLE_NORET 0x1 /* do not process return type */ | ||
7 | |||
8 | char * java_demangle_sym(const char *str, int flags); | ||
9 | |||
10 | #endif /* __PERF_DEMANGLE_JAVA */ | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index e8e9a9dbf5e3..8e6395439ca0 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -52,6 +52,11 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
52 | debuglink--; | 52 | debuglink--; |
53 | if (*debuglink == '/') | 53 | if (*debuglink == '/') |
54 | debuglink++; | 54 | debuglink++; |
55 | |||
56 | ret = -1; | ||
57 | if (!is_regular_file(filename)) | ||
58 | break; | ||
59 | |||
55 | ret = filename__read_debuglink(filename, debuglink, | 60 | ret = filename__read_debuglink(filename, debuglink, |
56 | size - (debuglink - filename)); | 61 | size - (debuglink - filename)); |
57 | } | 62 | } |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 45ec4d0a50ed..0953280629cf 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -162,6 +162,7 @@ struct dso { | |||
162 | u8 loaded; | 162 | u8 loaded; |
163 | u8 rel; | 163 | u8 rel; |
164 | u8 build_id[BUILD_ID_SIZE]; | 164 | u8 build_id[BUILD_ID_SIZE]; |
165 | u64 text_offset; | ||
165 | const char *short_name; | 166 | const char *short_name; |
166 | const char *long_name; | 167 | const char *long_name; |
167 | u16 long_name_len; | 168 | u16 long_name_len; |
@@ -301,7 +302,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path, | |||
301 | * TODO | 302 | * TODO |
302 | */ | 303 | */ |
303 | int dso__data_get_fd(struct dso *dso, struct machine *machine); | 304 | int dso__data_get_fd(struct dso *dso, struct machine *machine); |
304 | void dso__data_put_fd(struct dso *dso __maybe_unused); | 305 | void dso__data_put_fd(struct dso *dso); |
305 | void dso__data_close(struct dso *dso); | 306 | void dso__data_close(struct dso *dso); |
306 | 307 | ||
307 | off_t dso__data_size(struct dso *dso, struct machine *machine); | 308 | off_t dso__data_size(struct dso *dso, struct machine *machine); |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index a509aa8433a1..577e600c8eb1 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -915,7 +915,7 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf) | |||
915 | tmp = "*"; | 915 | tmp = "*"; |
916 | else if (tag == DW_TAG_subroutine_type) { | 916 | else if (tag == DW_TAG_subroutine_type) { |
917 | /* Function pointer */ | 917 | /* Function pointer */ |
918 | strbuf_addf(buf, "(function_type)"); | 918 | strbuf_add(buf, "(function_type)", 15); |
919 | return 0; | 919 | return 0; |
920 | } else { | 920 | } else { |
921 | if (!dwarf_diename(&type)) | 921 | if (!dwarf_diename(&type)) |
@@ -932,7 +932,7 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf) | |||
932 | } | 932 | } |
933 | ret = die_get_typename(&type, buf); | 933 | ret = die_get_typename(&type, buf); |
934 | if (ret == 0) | 934 | if (ret == 0) |
935 | strbuf_addf(buf, "%s", tmp); | 935 | strbuf_addstr(buf, tmp); |
936 | 936 | ||
937 | return ret; | 937 | return ret; |
938 | } | 938 | } |
@@ -951,7 +951,7 @@ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf) | |||
951 | ret = die_get_typename(vr_die, buf); | 951 | ret = die_get_typename(vr_die, buf); |
952 | if (ret < 0) { | 952 | if (ret < 0) { |
953 | pr_debug("Failed to get type, make it unknown.\n"); | 953 | pr_debug("Failed to get type, make it unknown.\n"); |
954 | strbuf_addf(buf, "(unknown_type)"); | 954 | strbuf_add(buf, " (unknown_type)", 14); |
955 | } | 955 | } |
956 | 956 | ||
957 | strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); | 957 | strbuf_addf(buf, "\t%s", dwarf_diename(vr_die)); |
@@ -1013,7 +1013,7 @@ static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die, | |||
1013 | } | 1013 | } |
1014 | 1014 | ||
1015 | if (!first) | 1015 | if (!first) |
1016 | strbuf_addf(buf, "]>"); | 1016 | strbuf_add(buf, "]>", 2); |
1017 | 1017 | ||
1018 | out: | 1018 | out: |
1019 | free(scopes); | 1019 | free(scopes); |
@@ -1076,7 +1076,7 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf) | |||
1076 | } | 1076 | } |
1077 | 1077 | ||
1078 | if (!first) | 1078 | if (!first) |
1079 | strbuf_addf(buf, "]>"); | 1079 | strbuf_add(buf, "]>", 2); |
1080 | 1080 | ||
1081 | return ret; | 1081 | return ret; |
1082 | } | 1082 | } |
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index c42ec366f2a7..dc0ce1adb075 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -25,48 +25,48 @@ | |||
25 | #include <elfutils/version.h> | 25 | #include <elfutils/version.h> |
26 | 26 | ||
27 | /* Find the realpath of the target file */ | 27 | /* Find the realpath of the target file */ |
28 | extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname); | 28 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname); |
29 | 29 | ||
30 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | 30 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ |
31 | extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | 31 | const char *cu_get_comp_dir(Dwarf_Die *cu_die); |
32 | 32 | ||
33 | /* Get a line number and file name for given address */ | 33 | /* Get a line number and file name for given address */ |
34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | 34 | int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, |
35 | const char **fname, int *lineno); | 35 | const char **fname, int *lineno); |
36 | 36 | ||
37 | /* Walk on funcitons at given address */ | 37 | /* Walk on funcitons at given address */ |
38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | 38 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, |
39 | int (*callback)(Dwarf_Die *, void *), void *data); | 39 | int (*callback)(Dwarf_Die *, void *), void *data); |
40 | 40 | ||
41 | /* Ensure that this DIE is a subprogram and definition (not declaration) */ | 41 | /* Ensure that this DIE is a subprogram and definition (not declaration) */ |
42 | extern bool die_is_func_def(Dwarf_Die *dw_die); | 42 | bool die_is_func_def(Dwarf_Die *dw_die); |
43 | 43 | ||
44 | /* Ensure that this DIE is an instance of a subprogram */ | 44 | /* Ensure that this DIE is an instance of a subprogram */ |
45 | extern bool die_is_func_instance(Dwarf_Die *dw_die); | 45 | bool die_is_func_instance(Dwarf_Die *dw_die); |
46 | 46 | ||
47 | /* Compare diename and tname */ | 47 | /* Compare diename and tname */ |
48 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | 48 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname); |
49 | 49 | ||
50 | /* Matching diename with glob pattern */ | 50 | /* Matching diename with glob pattern */ |
51 | extern bool die_match_name(Dwarf_Die *dw_die, const char *glob); | 51 | bool die_match_name(Dwarf_Die *dw_die, const char *glob); |
52 | 52 | ||
53 | /* Get callsite line number of inline-function instance */ | 53 | /* Get callsite line number of inline-function instance */ |
54 | extern int die_get_call_lineno(Dwarf_Die *in_die); | 54 | int die_get_call_lineno(Dwarf_Die *in_die); |
55 | 55 | ||
56 | /* Get callsite file name of inlined function instance */ | 56 | /* Get callsite file name of inlined function instance */ |
57 | extern const char *die_get_call_file(Dwarf_Die *in_die); | 57 | const char *die_get_call_file(Dwarf_Die *in_die); |
58 | 58 | ||
59 | /* Get type die */ | 59 | /* Get type die */ |
60 | extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | 60 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); |
61 | 61 | ||
62 | /* Get a type die, but skip qualifiers and typedef */ | 62 | /* Get a type die, but skip qualifiers and typedef */ |
63 | extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | 63 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); |
64 | 64 | ||
65 | /* Check whether the DIE is signed or not */ | 65 | /* Check whether the DIE is signed or not */ |
66 | extern bool die_is_signed_type(Dwarf_Die *tp_die); | 66 | bool die_is_signed_type(Dwarf_Die *tp_die); |
67 | 67 | ||
68 | /* Get data_member_location offset */ | 68 | /* Get data_member_location offset */ |
69 | extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs); | 69 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs); |
70 | 70 | ||
71 | /* Return values for die_find_child() callbacks */ | 71 | /* Return values for die_find_child() callbacks */ |
72 | enum { | 72 | enum { |
@@ -77,29 +77,29 @@ enum { | |||
77 | }; | 77 | }; |
78 | 78 | ||
79 | /* Search child DIEs */ | 79 | /* Search child DIEs */ |
80 | extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | 80 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, |
81 | int (*callback)(Dwarf_Die *, void *), | 81 | int (*callback)(Dwarf_Die *, void *), |
82 | void *data, Dwarf_Die *die_mem); | 82 | void *data, Dwarf_Die *die_mem); |
83 | 83 | ||
84 | /* Search a non-inlined function including given address */ | 84 | /* Search a non-inlined function including given address */ |
85 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | 85 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, |
86 | Dwarf_Die *die_mem); | 86 | Dwarf_Die *die_mem); |
87 | 87 | ||
88 | /* Search a non-inlined function with tail call at given address */ | 88 | /* Search a non-inlined function with tail call at given address */ |
89 | Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | 89 | Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, |
90 | Dwarf_Die *die_mem); | 90 | Dwarf_Die *die_mem); |
91 | 91 | ||
92 | /* Search the top inlined function including given address */ | 92 | /* Search the top inlined function including given address */ |
93 | extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 93 | Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
94 | Dwarf_Die *die_mem); | 94 | Dwarf_Die *die_mem); |
95 | 95 | ||
96 | /* Search the deepest inlined function including given address */ | 96 | /* Search the deepest inlined function including given address */ |
97 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 97 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
98 | Dwarf_Die *die_mem); | 98 | Dwarf_Die *die_mem); |
99 | 99 | ||
100 | /* Walk on the instances of given DIE */ | 100 | /* Walk on the instances of given DIE */ |
101 | extern int die_walk_instances(Dwarf_Die *in_die, | 101 | int die_walk_instances(Dwarf_Die *in_die, |
102 | int (*callback)(Dwarf_Die *, void *), void *data); | 102 | int (*callback)(Dwarf_Die *, void *), void *data); |
103 | 103 | ||
104 | /* Walker on lines (Note: line number will not be sorted) */ | 104 | /* Walker on lines (Note: line number will not be sorted) */ |
105 | typedef int (* line_walk_callback_t) (const char *fname, int lineno, | 105 | typedef int (* line_walk_callback_t) (const char *fname, int lineno, |
@@ -109,22 +109,20 @@ typedef int (* line_walk_callback_t) (const char *fname, int lineno, | |||
109 | * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on | 109 | * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on |
110 | * the lines inside the subprogram, otherwise the DIE must be a CU DIE. | 110 | * the lines inside the subprogram, otherwise the DIE must be a CU DIE. |
111 | */ | 111 | */ |
112 | extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, | 112 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data); |
113 | void *data); | ||
114 | 113 | ||
115 | /* Find a variable called 'name' at given address */ | 114 | /* Find a variable called 'name' at given address */ |
116 | extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | 115 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, |
117 | Dwarf_Addr addr, Dwarf_Die *die_mem); | 116 | Dwarf_Addr addr, Dwarf_Die *die_mem); |
118 | 117 | ||
119 | /* Find a member called 'name' */ | 118 | /* Find a member called 'name' */ |
120 | extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | 119 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, |
121 | Dwarf_Die *die_mem); | 120 | Dwarf_Die *die_mem); |
122 | 121 | ||
123 | /* Get the name of given variable DIE */ | 122 | /* Get the name of given variable DIE */ |
124 | extern int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf); | 123 | int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf); |
125 | 124 | ||
126 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | 125 | /* Get the name and type of given variable DIE, stored as "type\tname" */ |
127 | extern int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf); | 126 | int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf); |
128 | extern int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, | 127 | int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf); |
129 | struct strbuf *buf); | ||
130 | #endif | 128 | #endif |
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 7dd5939dea2e..49a11d9d8b8f 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c | |||
@@ -6,6 +6,8 @@ struct perf_env perf_env; | |||
6 | 6 | ||
7 | void perf_env__exit(struct perf_env *env) | 7 | void perf_env__exit(struct perf_env *env) |
8 | { | 8 | { |
9 | int i; | ||
10 | |||
9 | zfree(&env->hostname); | 11 | zfree(&env->hostname); |
10 | zfree(&env->os_release); | 12 | zfree(&env->os_release); |
11 | zfree(&env->version); | 13 | zfree(&env->version); |
@@ -19,6 +21,10 @@ void perf_env__exit(struct perf_env *env) | |||
19 | zfree(&env->numa_nodes); | 21 | zfree(&env->numa_nodes); |
20 | zfree(&env->pmu_mappings); | 22 | zfree(&env->pmu_mappings); |
21 | zfree(&env->cpu); | 23 | zfree(&env->cpu); |
24 | |||
25 | for (i = 0; i < env->caches_cnt; i++) | ||
26 | cpu_cache_level__free(&env->caches[i]); | ||
27 | zfree(&env->caches); | ||
22 | } | 28 | } |
23 | 29 | ||
24 | int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) | 30 | int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) |
@@ -75,3 +81,10 @@ int perf_env__read_cpu_topology_map(struct perf_env *env) | |||
75 | env->nr_cpus_avail = nr_cpus; | 81 | env->nr_cpus_avail = nr_cpus; |
76 | return 0; | 82 | return 0; |
77 | } | 83 | } |
84 | |||
85 | void cpu_cache_level__free(struct cpu_cache_level *cache) | ||
86 | { | ||
87 | free(cache->type); | ||
88 | free(cache->map); | ||
89 | free(cache->size); | ||
90 | } | ||
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 0132b9557c02..56cffb60a0b4 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h | |||
@@ -1,11 +1,23 @@ | |||
1 | #ifndef __PERF_ENV_H | 1 | #ifndef __PERF_ENV_H |
2 | #define __PERF_ENV_H | 2 | #define __PERF_ENV_H |
3 | 3 | ||
4 | #include <linux/types.h> | ||
5 | |||
4 | struct cpu_topology_map { | 6 | struct cpu_topology_map { |
5 | int socket_id; | 7 | int socket_id; |
6 | int core_id; | 8 | int core_id; |
7 | }; | 9 | }; |
8 | 10 | ||
11 | struct cpu_cache_level { | ||
12 | u32 level; | ||
13 | u32 line_size; | ||
14 | u32 sets; | ||
15 | u32 ways; | ||
16 | char *type; | ||
17 | char *size; | ||
18 | char *map; | ||
19 | }; | ||
20 | |||
9 | struct perf_env { | 21 | struct perf_env { |
10 | char *hostname; | 22 | char *hostname; |
11 | char *os_release; | 23 | char *os_release; |
@@ -31,6 +43,8 @@ struct perf_env { | |||
31 | char *numa_nodes; | 43 | char *numa_nodes; |
32 | char *pmu_mappings; | 44 | char *pmu_mappings; |
33 | struct cpu_topology_map *cpu; | 45 | struct cpu_topology_map *cpu; |
46 | struct cpu_cache_level *caches; | ||
47 | int caches_cnt; | ||
34 | }; | 48 | }; |
35 | 49 | ||
36 | extern struct perf_env perf_env; | 50 | extern struct perf_env perf_env; |
@@ -41,4 +55,5 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]); | |||
41 | 55 | ||
42 | int perf_env__read_cpu_topology_map(struct perf_env *env); | 56 | int perf_env__read_cpu_topology_map(struct perf_env *env); |
43 | 57 | ||
58 | void cpu_cache_level__free(struct cpu_cache_level *cache); | ||
44 | #endif /* __PERF_ENV_H */ | 59 | #endif /* __PERF_ENV_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 85155e91b61b..dad55d04ffdd 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -56,13 +56,22 @@ const char *perf_event__name(unsigned int id) | |||
56 | return perf_event__names[id]; | 56 | return perf_event__names[id]; |
57 | } | 57 | } |
58 | 58 | ||
59 | static struct perf_sample synth_sample = { | 59 | static int perf_tool__process_synth_event(struct perf_tool *tool, |
60 | union perf_event *event, | ||
61 | struct machine *machine, | ||
62 | perf_event__handler_t process) | ||
63 | { | ||
64 | struct perf_sample synth_sample = { | ||
60 | .pid = -1, | 65 | .pid = -1, |
61 | .tid = -1, | 66 | .tid = -1, |
62 | .time = -1, | 67 | .time = -1, |
63 | .stream_id = -1, | 68 | .stream_id = -1, |
64 | .cpu = -1, | 69 | .cpu = -1, |
65 | .period = 1, | 70 | .period = 1, |
71 | .cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK, | ||
72 | }; | ||
73 | |||
74 | return process(tool, event, &synth_sample, machine); | ||
66 | }; | 75 | }; |
67 | 76 | ||
68 | /* | 77 | /* |
@@ -186,7 +195,7 @@ pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
186 | if (perf_event__prepare_comm(event, pid, machine, &tgid, &ppid) != 0) | 195 | if (perf_event__prepare_comm(event, pid, machine, &tgid, &ppid) != 0) |
187 | return -1; | 196 | return -1; |
188 | 197 | ||
189 | if (process(tool, event, &synth_sample, machine) != 0) | 198 | if (perf_tool__process_synth_event(tool, event, machine, process) != 0) |
190 | return -1; | 199 | return -1; |
191 | 200 | ||
192 | return tgid; | 201 | return tgid; |
@@ -218,7 +227,7 @@ static int perf_event__synthesize_fork(struct perf_tool *tool, | |||
218 | 227 | ||
219 | event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); | 228 | event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); |
220 | 229 | ||
221 | if (process(tool, event, &synth_sample, machine) != 0) | 230 | if (perf_tool__process_synth_event(tool, event, machine, process) != 0) |
222 | return -1; | 231 | return -1; |
223 | 232 | ||
224 | return 0; | 233 | return 0; |
@@ -282,7 +291,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
282 | strcpy(execname, ""); | 291 | strcpy(execname, ""); |
283 | 292 | ||
284 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 293 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
285 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n", | 294 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %[^\n]\n", |
286 | &event->mmap2.start, &event->mmap2.len, prot, | 295 | &event->mmap2.start, &event->mmap2.len, prot, |
287 | &event->mmap2.pgoff, &event->mmap2.maj, | 296 | &event->mmap2.pgoff, &event->mmap2.maj, |
288 | &event->mmap2.min, | 297 | &event->mmap2.min, |
@@ -344,7 +353,7 @@ out: | |||
344 | event->mmap2.pid = tgid; | 353 | event->mmap2.pid = tgid; |
345 | event->mmap2.tid = pid; | 354 | event->mmap2.tid = pid; |
346 | 355 | ||
347 | if (process(tool, event, &synth_sample, machine) != 0) { | 356 | if (perf_tool__process_synth_event(tool, event, machine, process) != 0) { |
348 | rc = -1; | 357 | rc = -1; |
349 | break; | 358 | break; |
350 | } | 359 | } |
@@ -402,7 +411,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, | |||
402 | 411 | ||
403 | memcpy(event->mmap.filename, pos->dso->long_name, | 412 | memcpy(event->mmap.filename, pos->dso->long_name, |
404 | pos->dso->long_name_len + 1); | 413 | pos->dso->long_name_len + 1); |
405 | if (process(tool, event, &synth_sample, machine) != 0) { | 414 | if (perf_tool__process_synth_event(tool, event, machine, process) != 0) { |
406 | rc = -1; | 415 | rc = -1; |
407 | break; | 416 | break; |
408 | } | 417 | } |
@@ -472,7 +481,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, | |||
472 | /* | 481 | /* |
473 | * Send the prepared comm event | 482 | * Send the prepared comm event |
474 | */ | 483 | */ |
475 | if (process(tool, comm_event, &synth_sample, machine) != 0) | 484 | if (perf_tool__process_synth_event(tool, comm_event, machine, process) != 0) |
476 | break; | 485 | break; |
477 | 486 | ||
478 | rc = 0; | 487 | rc = 0; |
@@ -701,7 +710,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
701 | event->mmap.len = map->end - event->mmap.start; | 710 | event->mmap.len = map->end - event->mmap.start; |
702 | event->mmap.pid = machine->pid; | 711 | event->mmap.pid = machine->pid; |
703 | 712 | ||
704 | err = process(tool, event, &synth_sample, machine); | 713 | err = perf_tool__process_synth_event(tool, event, machine, process); |
705 | free(event); | 714 | free(event); |
706 | 715 | ||
707 | return err; | 716 | return err; |
@@ -1295,12 +1304,9 @@ void thread__find_addr_location(struct thread *thread, | |||
1295 | * Callers need to drop the reference to al->thread, obtained in | 1304 | * Callers need to drop the reference to al->thread, obtained in |
1296 | * machine__findnew_thread() | 1305 | * machine__findnew_thread() |
1297 | */ | 1306 | */ |
1298 | int perf_event__preprocess_sample(const union perf_event *event, | 1307 | int machine__resolve(struct machine *machine, struct addr_location *al, |
1299 | struct machine *machine, | 1308 | struct perf_sample *sample) |
1300 | struct addr_location *al, | ||
1301 | struct perf_sample *sample) | ||
1302 | { | 1309 | { |
1303 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1304 | struct thread *thread = machine__findnew_thread(machine, sample->pid, | 1310 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
1305 | sample->tid); | 1311 | sample->tid); |
1306 | 1312 | ||
@@ -1315,11 +1321,11 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
1315 | * events, but for older perf.data files there was no such thing, so do | 1321 | * events, but for older perf.data files there was no such thing, so do |
1316 | * it now. | 1322 | * it now. |
1317 | */ | 1323 | */ |
1318 | if (cpumode == PERF_RECORD_MISC_KERNEL && | 1324 | if (sample->cpumode == PERF_RECORD_MISC_KERNEL && |
1319 | machine__kernel_map(machine) == NULL) | 1325 | machine__kernel_map(machine) == NULL) |
1320 | machine__create_kernel_maps(machine); | 1326 | machine__create_kernel_maps(machine); |
1321 | 1327 | ||
1322 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al); | 1328 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->ip, al); |
1323 | dump_printf(" ...... dso: %s\n", | 1329 | dump_printf(" ...... dso: %s\n", |
1324 | al->map ? al->map->dso->long_name : | 1330 | al->map ? al->map->dso->long_name : |
1325 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 1331 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
@@ -1395,16 +1401,12 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr) | |||
1395 | return false; | 1401 | return false; |
1396 | } | 1402 | } |
1397 | 1403 | ||
1398 | void perf_event__preprocess_sample_addr(union perf_event *event, | 1404 | void thread__resolve(struct thread *thread, struct addr_location *al, |
1399 | struct perf_sample *sample, | 1405 | struct perf_sample *sample) |
1400 | struct thread *thread, | ||
1401 | struct addr_location *al) | ||
1402 | { | 1406 | { |
1403 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 1407 | thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, sample->addr, al); |
1404 | |||
1405 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al); | ||
1406 | if (!al->map) | 1408 | if (!al->map) |
1407 | thread__find_addr_map(thread, cpumode, MAP__VARIABLE, | 1409 | thread__find_addr_map(thread, sample->cpumode, MAP__VARIABLE, |
1408 | sample->addr, al); | 1410 | sample->addr, al); |
1409 | 1411 | ||
1410 | al->cpu = sample->cpu; | 1412 | al->cpu = sample->cpu; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b7ffb7ee9971..6bb1c928350d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -192,6 +192,7 @@ struct perf_sample { | |||
192 | u64 data_src; | 192 | u64 data_src; |
193 | u32 flags; | 193 | u32 flags; |
194 | u16 insn_len; | 194 | u16 insn_len; |
195 | u8 cpumode; | ||
195 | void *raw_data; | 196 | void *raw_data; |
196 | struct ip_callchain *callchain; | 197 | struct ip_callchain *callchain; |
197 | struct branch_stack *branch_stack; | 198 | struct branch_stack *branch_stack; |
@@ -597,10 +598,8 @@ int perf_event__process(struct perf_tool *tool, | |||
597 | 598 | ||
598 | struct addr_location; | 599 | struct addr_location; |
599 | 600 | ||
600 | int perf_event__preprocess_sample(const union perf_event *event, | 601 | int machine__resolve(struct machine *machine, struct addr_location *al, |
601 | struct machine *machine, | 602 | struct perf_sample *sample); |
602 | struct addr_location *al, | ||
603 | struct perf_sample *sample); | ||
604 | 603 | ||
605 | void addr_location__put(struct addr_location *al); | 604 | void addr_location__put(struct addr_location *al); |
606 | 605 | ||
@@ -608,10 +607,8 @@ struct thread; | |||
608 | 607 | ||
609 | bool is_bts_event(struct perf_event_attr *attr); | 608 | bool is_bts_event(struct perf_event_attr *attr); |
610 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); | 609 | bool sample_addr_correlates_sym(struct perf_event_attr *attr); |
611 | void perf_event__preprocess_sample_addr(union perf_event *event, | 610 | void thread__resolve(struct thread *thread, struct addr_location *al, |
612 | struct perf_sample *sample, | 611 | struct perf_sample *sample); |
613 | struct thread *thread, | ||
614 | struct addr_location *al); | ||
615 | 612 | ||
616 | const char *perf_event__name(unsigned int id); | 613 | const char *perf_event__name(unsigned int id); |
617 | 614 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d81f13de2476..86a03836a83f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -1181,12 +1181,12 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
1181 | */ | 1181 | */ |
1182 | if (cpus != evlist->cpus) { | 1182 | if (cpus != evlist->cpus) { |
1183 | cpu_map__put(evlist->cpus); | 1183 | cpu_map__put(evlist->cpus); |
1184 | evlist->cpus = cpus; | 1184 | evlist->cpus = cpu_map__get(cpus); |
1185 | } | 1185 | } |
1186 | 1186 | ||
1187 | if (threads != evlist->threads) { | 1187 | if (threads != evlist->threads) { |
1188 | thread_map__put(evlist->threads); | 1188 | thread_map__put(evlist->threads); |
1189 | evlist->threads = threads; | 1189 | evlist->threads = thread_map__get(threads); |
1190 | } | 1190 | } |
1191 | 1191 | ||
1192 | perf_evlist__propagate_maps(evlist); | 1192 | perf_evlist__propagate_maps(evlist); |
@@ -1223,6 +1223,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | |||
1223 | int err = 0; | 1223 | int err = 0; |
1224 | 1224 | ||
1225 | evlist__for_each(evlist, evsel) { | 1225 | evlist__for_each(evlist, evsel) { |
1226 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | ||
1227 | continue; | ||
1228 | |||
1226 | err = perf_evsel__set_filter(evsel, filter); | 1229 | err = perf_evsel__set_filter(evsel, filter); |
1227 | if (err) | 1230 | if (err) |
1228 | break; | 1231 | break; |
@@ -1624,7 +1627,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | |||
1624 | return printed + fprintf(fp, "\n"); | 1627 | return printed + fprintf(fp, "\n"); |
1625 | } | 1628 | } |
1626 | 1629 | ||
1627 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | 1630 | int perf_evlist__strerror_open(struct perf_evlist *evlist, |
1628 | int err, char *buf, size_t size) | 1631 | int err, char *buf, size_t size) |
1629 | { | 1632 | { |
1630 | int printed, value; | 1633 | int printed, value; |
@@ -1652,7 +1655,25 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
1652 | "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" | 1655 | "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" |
1653 | "Hint:\tThe current value is %d.", value); | 1656 | "Hint:\tThe current value is %d.", value); |
1654 | break; | 1657 | break; |
1658 | case EINVAL: { | ||
1659 | struct perf_evsel *first = perf_evlist__first(evlist); | ||
1660 | int max_freq; | ||
1661 | |||
1662 | if (sysctl__read_int("kernel/perf_event_max_sample_rate", &max_freq) < 0) | ||
1663 | goto out_default; | ||
1664 | |||
1665 | if (first->attr.sample_freq < (u64)max_freq) | ||
1666 | goto out_default; | ||
1667 | |||
1668 | printed = scnprintf(buf, size, | ||
1669 | "Error:\t%s.\n" | ||
1670 | "Hint:\tCheck /proc/sys/kernel/perf_event_max_sample_rate.\n" | ||
1671 | "Hint:\tThe current value is %d and %" PRIu64 " is being requested.", | ||
1672 | emsg, max_freq, first->attr.sample_freq); | ||
1673 | break; | ||
1674 | } | ||
1655 | default: | 1675 | default: |
1676 | out_default: | ||
1656 | scnprintf(buf, size, "%s", emsg); | 1677 | scnprintf(buf, size, "%s", emsg); |
1657 | break; | 1678 | break; |
1658 | } | 1679 | } |
@@ -1723,3 +1744,19 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | |||
1723 | 1744 | ||
1724 | tracking_evsel->tracking = true; | 1745 | tracking_evsel->tracking = true; |
1725 | } | 1746 | } |
1747 | |||
1748 | struct perf_evsel * | ||
1749 | perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, | ||
1750 | const char *str) | ||
1751 | { | ||
1752 | struct perf_evsel *evsel; | ||
1753 | |||
1754 | evlist__for_each(evlist, evsel) { | ||
1755 | if (!evsel->name) | ||
1756 | continue; | ||
1757 | if (strcmp(str, evsel->name) == 0) | ||
1758 | return evsel; | ||
1759 | } | ||
1760 | |||
1761 | return NULL; | ||
1762 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 7c4d9a206776..a0d15221db6e 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -294,4 +294,7 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | |||
294 | struct perf_evsel *tracking_evsel); | 294 | struct perf_evsel *tracking_evsel); |
295 | 295 | ||
296 | void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr); | 296 | void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr); |
297 | |||
298 | struct perf_evsel * | ||
299 | perf_evlist__find_evsel_by_str(struct perf_evlist *evlist, const char *str); | ||
297 | #endif /* __PERF_EVLIST_H */ | 300 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index cdbaf9b51e42..738ce226002b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -225,6 +225,11 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
225 | if (evsel != NULL) | 225 | if (evsel != NULL) |
226 | perf_evsel__init(evsel, attr, idx); | 226 | perf_evsel__init(evsel, attr, idx); |
227 | 227 | ||
228 | if (perf_evsel__is_bpf_output(evsel)) { | ||
229 | evsel->attr.sample_type |= PERF_SAMPLE_RAW; | ||
230 | evsel->attr.sample_period = 1; | ||
231 | } | ||
232 | |||
228 | return evsel; | 233 | return evsel; |
229 | } | 234 | } |
230 | 235 | ||
@@ -898,6 +903,16 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
898 | if (evsel->precise_max) | 903 | if (evsel->precise_max) |
899 | perf_event_attr__set_max_precise_ip(attr); | 904 | perf_event_attr__set_max_precise_ip(attr); |
900 | 905 | ||
906 | if (opts->all_user) { | ||
907 | attr->exclude_kernel = 1; | ||
908 | attr->exclude_user = 0; | ||
909 | } | ||
910 | |||
911 | if (opts->all_kernel) { | ||
912 | attr->exclude_kernel = 0; | ||
913 | attr->exclude_user = 1; | ||
914 | } | ||
915 | |||
901 | /* | 916 | /* |
902 | * Apply event specific term settings, | 917 | * Apply event specific term settings, |
903 | * it overloads any global configuration. | 918 | * it overloads any global configuration. |
@@ -1628,6 +1643,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1628 | data->stream_id = data->id = data->time = -1ULL; | 1643 | data->stream_id = data->id = data->time = -1ULL; |
1629 | data->period = evsel->attr.sample_period; | 1644 | data->period = evsel->attr.sample_period; |
1630 | data->weight = 0; | 1645 | data->weight = 0; |
1646 | data->cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1631 | 1647 | ||
1632 | if (event->header.type != PERF_RECORD_SAMPLE) { | 1648 | if (event->header.type != PERF_RECORD_SAMPLE) { |
1633 | if (!evsel->attr.sample_id_all) | 1649 | if (!evsel->attr.sample_id_all) |
@@ -2362,12 +2378,15 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
2362 | case EPERM: | 2378 | case EPERM: |
2363 | case EACCES: | 2379 | case EACCES: |
2364 | return scnprintf(msg, size, | 2380 | return scnprintf(msg, size, |
2365 | "You may not have permission to collect %sstats.\n" | 2381 | "You may not have permission to collect %sstats.\n\n" |
2366 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | 2382 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n" |
2367 | " -1 - Not paranoid at all\n" | 2383 | "which controls use of the performance events system by\n" |
2368 | " 0 - Disallow raw tracepoint access for unpriv\n" | 2384 | "unprivileged users (without CAP_SYS_ADMIN).\n\n" |
2369 | " 1 - Disallow cpu events for unpriv\n" | 2385 | "The default value is 1:\n\n" |
2370 | " 2 - Disallow kernel profiling for unpriv", | 2386 | " -1: Allow use of (almost) all events by all users\n" |
2387 | ">= 0: Disallow raw tracepoint access by users without CAP_IOC_LOCK\n" | ||
2388 | ">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n" | ||
2389 | ">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN", | ||
2371 | target->system_wide ? "system-wide " : ""); | 2390 | target->system_wide ? "system-wide " : ""); |
2372 | case ENOENT: | 2391 | case ENOENT: |
2373 | return scnprintf(msg, size, "The %s event is not supported.", | 2392 | return scnprintf(msg, size, "The %s event is not supported.", |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8e75434bd01c..501ea6e565f1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -93,10 +93,8 @@ struct perf_evsel { | |||
93 | const char *unit; | 93 | const char *unit; |
94 | struct event_format *tp_format; | 94 | struct event_format *tp_format; |
95 | off_t id_offset; | 95 | off_t id_offset; |
96 | union { | 96 | void *priv; |
97 | void *priv; | 97 | u64 db_id; |
98 | u64 db_id; | ||
99 | }; | ||
100 | struct cgroup_sel *cgrp; | 98 | struct cgroup_sel *cgrp; |
101 | void *handler; | 99 | void *handler; |
102 | struct cpu_map *cpus; | 100 | struct cpu_map *cpus; |
@@ -364,6 +362,14 @@ static inline bool perf_evsel__is_function_event(struct perf_evsel *evsel) | |||
364 | #undef FUNCTION_EVENT | 362 | #undef FUNCTION_EVENT |
365 | } | 363 | } |
366 | 364 | ||
365 | static inline bool perf_evsel__is_bpf_output(struct perf_evsel *evsel) | ||
366 | { | ||
367 | struct perf_event_attr *attr = &evsel->attr; | ||
368 | |||
369 | return (attr->config == PERF_COUNT_SW_BPF_OUTPUT) && | ||
370 | (attr->type == PERF_TYPE_SOFTWARE); | ||
371 | } | ||
372 | |||
367 | struct perf_attr_details { | 373 | struct perf_attr_details { |
368 | bool freq; | 374 | bool freq; |
369 | bool verbose; | 375 | bool verbose; |
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c new file mode 100644 index 000000000000..c1ef805c6a8f --- /dev/null +++ b/tools/perf/util/genelf.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * genelf.c | ||
3 | * Copyright (C) 2014, Google, Inc | ||
4 | * | ||
5 | * Contributed by: | ||
6 | * Stephane Eranian <eranian@gmail.com> | ||
7 | * | ||
8 | * Released under the GPL v2. (and only v2, not any later version) | ||
9 | */ | ||
10 | |||
11 | #include <sys/types.h> | ||
12 | #include <stdio.h> | ||
13 | #include <getopt.h> | ||
14 | #include <stddef.h> | ||
15 | #include <libelf.h> | ||
16 | #include <string.h> | ||
17 | #include <stdlib.h> | ||
18 | #include <inttypes.h> | ||
19 | #include <limits.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <err.h> | ||
22 | #include <dwarf.h> | ||
23 | |||
24 | #include "perf.h" | ||
25 | #include "genelf.h" | ||
26 | #include "../util/jitdump.h" | ||
27 | |||
28 | #define JVMTI | ||
29 | |||
30 | #define BUILD_ID_URANDOM /* different uuid for each run */ | ||
31 | |||
32 | #ifdef HAVE_LIBCRYPTO | ||
33 | |||
34 | #define BUILD_ID_MD5 | ||
35 | #undef BUILD_ID_SHA /* does not seem to work well when linked with Java */ | ||
36 | #undef BUILD_ID_URANDOM /* different uuid for each run */ | ||
37 | |||
38 | #ifdef BUILD_ID_SHA | ||
39 | #include <openssl/sha.h> | ||
40 | #endif | ||
41 | |||
42 | #ifdef BUILD_ID_MD5 | ||
43 | #include <openssl/md5.h> | ||
44 | #endif | ||
45 | #endif | ||
46 | |||
47 | |||
48 | typedef struct { | ||
49 | unsigned int namesz; /* Size of entry's owner string */ | ||
50 | unsigned int descsz; /* Size of the note descriptor */ | ||
51 | unsigned int type; /* Interpretation of the descriptor */ | ||
52 | char name[0]; /* Start of the name+desc data */ | ||
53 | } Elf_Note; | ||
54 | |||
55 | struct options { | ||
56 | char *output; | ||
57 | int fd; | ||
58 | }; | ||
59 | |||
60 | static char shd_string_table[] = { | ||
61 | 0, | ||
62 | '.', 't', 'e', 'x', 't', 0, /* 1 */ | ||
63 | '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0, /* 7 */ | ||
64 | '.', 's', 'y', 'm', 't', 'a', 'b', 0, /* 17 */ | ||
65 | '.', 's', 't', 'r', 't', 'a', 'b', 0, /* 25 */ | ||
66 | '.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0, /* 33 */ | ||
67 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ | ||
68 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ | ||
69 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ | ||
70 | }; | ||
71 | |||
72 | static struct buildid_note { | ||
73 | Elf_Note desc; /* descsz: size of build-id, must be multiple of 4 */ | ||
74 | char name[4]; /* GNU\0 */ | ||
75 | char build_id[20]; | ||
76 | } bnote; | ||
77 | |||
78 | static Elf_Sym symtab[]={ | ||
79 | /* symbol 0 MUST be the undefined symbol */ | ||
80 | { .st_name = 0, /* index in sym_string table */ | ||
81 | .st_info = ELF_ST_TYPE(STT_NOTYPE), | ||
82 | .st_shndx = 0, /* for now */ | ||
83 | .st_value = 0x0, | ||
84 | .st_other = ELF_ST_VIS(STV_DEFAULT), | ||
85 | .st_size = 0, | ||
86 | }, | ||
87 | { .st_name = 1, /* index in sym_string table */ | ||
88 | .st_info = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC), | ||
89 | .st_shndx = 1, | ||
90 | .st_value = 0, /* for now */ | ||
91 | .st_other = ELF_ST_VIS(STV_DEFAULT), | ||
92 | .st_size = 0, /* for now */ | ||
93 | } | ||
94 | }; | ||
95 | |||
96 | #ifdef BUILD_ID_URANDOM | ||
97 | static void | ||
98 | gen_build_id(struct buildid_note *note, | ||
99 | unsigned long load_addr __maybe_unused, | ||
100 | const void *code __maybe_unused, | ||
101 | size_t csize __maybe_unused) | ||
102 | { | ||
103 | int fd; | ||
104 | size_t sz = sizeof(note->build_id); | ||
105 | ssize_t sret; | ||
106 | |||
107 | fd = open("/dev/urandom", O_RDONLY); | ||
108 | if (fd == -1) | ||
109 | err(1, "cannot access /dev/urandom for builid"); | ||
110 | |||
111 | sret = read(fd, note->build_id, sz); | ||
112 | |||
113 | close(fd); | ||
114 | |||
115 | if (sret != (ssize_t)sz) | ||
116 | memset(note->build_id, 0, sz); | ||
117 | } | ||
118 | #endif | ||
119 | |||
120 | #ifdef BUILD_ID_SHA | ||
121 | static void | ||
122 | gen_build_id(struct buildid_note *note, | ||
123 | unsigned long load_addr __maybe_unused, | ||
124 | const void *code, | ||
125 | size_t csize) | ||
126 | { | ||
127 | if (sizeof(note->build_id) < SHA_DIGEST_LENGTH) | ||
128 | errx(1, "build_id too small for SHA1"); | ||
129 | |||
130 | SHA1(code, csize, (unsigned char *)note->build_id); | ||
131 | } | ||
132 | #endif | ||
133 | |||
134 | #ifdef BUILD_ID_MD5 | ||
135 | static void | ||
136 | gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *code, size_t csize) | ||
137 | { | ||
138 | MD5_CTX context; | ||
139 | |||
140 | if (sizeof(note->build_id) < 16) | ||
141 | errx(1, "build_id too small for MD5"); | ||
142 | |||
143 | MD5_Init(&context); | ||
144 | MD5_Update(&context, &load_addr, sizeof(load_addr)); | ||
145 | MD5_Update(&context, code, csize); | ||
146 | MD5_Final((unsigned char *)note->build_id, &context); | ||
147 | } | ||
148 | #endif | ||
149 | |||
150 | /* | ||
151 | * fd: file descriptor open for writing for the output file | ||
152 | * load_addr: code load address (could be zero, just used for buildid) | ||
153 | * sym: function name (for native code - used as the symbol) | ||
154 | * code: the native code | ||
155 | * csize: the code size in bytes | ||
156 | */ | ||
157 | int | ||
158 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, | ||
159 | const void *code, int csize, | ||
160 | void *debug, int nr_debug_entries) | ||
161 | { | ||
162 | Elf *e; | ||
163 | Elf_Data *d; | ||
164 | Elf_Scn *scn; | ||
165 | Elf_Ehdr *ehdr; | ||
166 | Elf_Shdr *shdr; | ||
167 | char *strsym = NULL; | ||
168 | int symlen; | ||
169 | int retval = -1; | ||
170 | |||
171 | if (elf_version(EV_CURRENT) == EV_NONE) { | ||
172 | warnx("ELF initialization failed"); | ||
173 | return -1; | ||
174 | } | ||
175 | |||
176 | e = elf_begin(fd, ELF_C_WRITE, NULL); | ||
177 | if (!e) { | ||
178 | warnx("elf_begin failed"); | ||
179 | goto error; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * setup ELF header | ||
184 | */ | ||
185 | ehdr = elf_newehdr(e); | ||
186 | if (!ehdr) { | ||
187 | warnx("cannot get ehdr"); | ||
188 | goto error; | ||
189 | } | ||
190 | |||
191 | ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN; | ||
192 | ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS; | ||
193 | ehdr->e_machine = GEN_ELF_ARCH; | ||
194 | ehdr->e_type = ET_DYN; | ||
195 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET; | ||
196 | ehdr->e_version = EV_CURRENT; | ||
197 | ehdr->e_shstrndx= 2; /* shdr index for section name */ | ||
198 | |||
199 | /* | ||
200 | * setup text section | ||
201 | */ | ||
202 | scn = elf_newscn(e); | ||
203 | if (!scn) { | ||
204 | warnx("cannot create section"); | ||
205 | goto error; | ||
206 | } | ||
207 | |||
208 | d = elf_newdata(scn); | ||
209 | if (!d) { | ||
210 | warnx("cannot get new data"); | ||
211 | goto error; | ||
212 | } | ||
213 | |||
214 | d->d_align = 16; | ||
215 | d->d_off = 0LL; | ||
216 | d->d_buf = (void *)code; | ||
217 | d->d_type = ELF_T_BYTE; | ||
218 | d->d_size = csize; | ||
219 | d->d_version = EV_CURRENT; | ||
220 | |||
221 | shdr = elf_getshdr(scn); | ||
222 | if (!shdr) { | ||
223 | warnx("cannot get section header"); | ||
224 | goto error; | ||
225 | } | ||
226 | |||
227 | shdr->sh_name = 1; | ||
228 | shdr->sh_type = SHT_PROGBITS; | ||
229 | shdr->sh_addr = GEN_ELF_TEXT_OFFSET; | ||
230 | shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | ||
231 | shdr->sh_entsize = 0; | ||
232 | |||
233 | /* | ||
234 | * setup section headers string table | ||
235 | */ | ||
236 | scn = elf_newscn(e); | ||
237 | if (!scn) { | ||
238 | warnx("cannot create section"); | ||
239 | goto error; | ||
240 | } | ||
241 | |||
242 | d = elf_newdata(scn); | ||
243 | if (!d) { | ||
244 | warnx("cannot get new data"); | ||
245 | goto error; | ||
246 | } | ||
247 | |||
248 | d->d_align = 1; | ||
249 | d->d_off = 0LL; | ||
250 | d->d_buf = shd_string_table; | ||
251 | d->d_type = ELF_T_BYTE; | ||
252 | d->d_size = sizeof(shd_string_table); | ||
253 | d->d_version = EV_CURRENT; | ||
254 | |||
255 | shdr = elf_getshdr(scn); | ||
256 | if (!shdr) { | ||
257 | warnx("cannot get section header"); | ||
258 | goto error; | ||
259 | } | ||
260 | |||
261 | shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */ | ||
262 | shdr->sh_type = SHT_STRTAB; | ||
263 | shdr->sh_flags = 0; | ||
264 | shdr->sh_entsize = 0; | ||
265 | |||
266 | /* | ||
267 | * setup symtab section | ||
268 | */ | ||
269 | symtab[1].st_size = csize; | ||
270 | symtab[1].st_value = GEN_ELF_TEXT_OFFSET; | ||
271 | |||
272 | scn = elf_newscn(e); | ||
273 | if (!scn) { | ||
274 | warnx("cannot create section"); | ||
275 | goto error; | ||
276 | } | ||
277 | |||
278 | d = elf_newdata(scn); | ||
279 | if (!d) { | ||
280 | warnx("cannot get new data"); | ||
281 | goto error; | ||
282 | } | ||
283 | |||
284 | d->d_align = 8; | ||
285 | d->d_off = 0LL; | ||
286 | d->d_buf = symtab; | ||
287 | d->d_type = ELF_T_SYM; | ||
288 | d->d_size = sizeof(symtab); | ||
289 | d->d_version = EV_CURRENT; | ||
290 | |||
291 | shdr = elf_getshdr(scn); | ||
292 | if (!shdr) { | ||
293 | warnx("cannot get section header"); | ||
294 | goto error; | ||
295 | } | ||
296 | |||
297 | shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */ | ||
298 | shdr->sh_type = SHT_SYMTAB; | ||
299 | shdr->sh_flags = 0; | ||
300 | shdr->sh_entsize = sizeof(Elf_Sym); | ||
301 | shdr->sh_link = 4; /* index of .strtab section */ | ||
302 | |||
303 | /* | ||
304 | * setup symbols string table | ||
305 | * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry | ||
306 | */ | ||
307 | symlen = 2 + strlen(sym); | ||
308 | strsym = calloc(1, symlen); | ||
309 | if (!strsym) { | ||
310 | warnx("cannot allocate strsym"); | ||
311 | goto error; | ||
312 | } | ||
313 | strcpy(strsym + 1, sym); | ||
314 | |||
315 | scn = elf_newscn(e); | ||
316 | if (!scn) { | ||
317 | warnx("cannot create section"); | ||
318 | goto error; | ||
319 | } | ||
320 | |||
321 | d = elf_newdata(scn); | ||
322 | if (!d) { | ||
323 | warnx("cannot get new data"); | ||
324 | goto error; | ||
325 | } | ||
326 | |||
327 | d->d_align = 1; | ||
328 | d->d_off = 0LL; | ||
329 | d->d_buf = strsym; | ||
330 | d->d_type = ELF_T_BYTE; | ||
331 | d->d_size = symlen; | ||
332 | d->d_version = EV_CURRENT; | ||
333 | |||
334 | shdr = elf_getshdr(scn); | ||
335 | if (!shdr) { | ||
336 | warnx("cannot get section header"); | ||
337 | goto error; | ||
338 | } | ||
339 | |||
340 | shdr->sh_name = 25; /* offset in shd_string_table */ | ||
341 | shdr->sh_type = SHT_STRTAB; | ||
342 | shdr->sh_flags = 0; | ||
343 | shdr->sh_entsize = 0; | ||
344 | |||
345 | /* | ||
346 | * setup build-id section | ||
347 | */ | ||
348 | scn = elf_newscn(e); | ||
349 | if (!scn) { | ||
350 | warnx("cannot create section"); | ||
351 | goto error; | ||
352 | } | ||
353 | |||
354 | d = elf_newdata(scn); | ||
355 | if (!d) { | ||
356 | warnx("cannot get new data"); | ||
357 | goto error; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * build-id generation | ||
362 | */ | ||
363 | gen_build_id(&bnote, load_addr, code, csize); | ||
364 | bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */ | ||
365 | bnote.desc.descsz = sizeof(bnote.build_id); | ||
366 | bnote.desc.type = NT_GNU_BUILD_ID; | ||
367 | strcpy(bnote.name, "GNU"); | ||
368 | |||
369 | d->d_align = 4; | ||
370 | d->d_off = 0LL; | ||
371 | d->d_buf = &bnote; | ||
372 | d->d_type = ELF_T_BYTE; | ||
373 | d->d_size = sizeof(bnote); | ||
374 | d->d_version = EV_CURRENT; | ||
375 | |||
376 | shdr = elf_getshdr(scn); | ||
377 | if (!shdr) { | ||
378 | warnx("cannot get section header"); | ||
379 | goto error; | ||
380 | } | ||
381 | |||
382 | shdr->sh_name = 33; /* offset in shd_string_table */ | ||
383 | shdr->sh_type = SHT_NOTE; | ||
384 | shdr->sh_addr = 0x0; | ||
385 | shdr->sh_flags = SHF_ALLOC; | ||
386 | shdr->sh_size = sizeof(bnote); | ||
387 | shdr->sh_entsize = 0; | ||
388 | |||
389 | if (debug && nr_debug_entries) { | ||
390 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); | ||
391 | if (retval) | ||
392 | goto error; | ||
393 | } else { | ||
394 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
395 | warnx("elf_update 4 failed"); | ||
396 | goto error; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | retval = 0; | ||
401 | error: | ||
402 | (void)elf_end(e); | ||
403 | |||
404 | free(strsym); | ||
405 | |||
406 | |||
407 | return retval; | ||
408 | } | ||
409 | |||
410 | #ifndef JVMTI | ||
411 | |||
412 | static unsigned char x86_code[] = { | ||
413 | 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ | ||
414 | 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ | ||
415 | 0xCD, 0x80 /* int $0x80 */ | ||
416 | }; | ||
417 | |||
418 | static struct options options; | ||
419 | |||
420 | int main(int argc, char **argv) | ||
421 | { | ||
422 | int c, fd, ret; | ||
423 | |||
424 | while ((c = getopt(argc, argv, "o:h")) != -1) { | ||
425 | switch (c) { | ||
426 | case 'o': | ||
427 | options.output = optarg; | ||
428 | break; | ||
429 | case 'h': | ||
430 | printf("Usage: genelf -o output_file [-h]\n"); | ||
431 | return 0; | ||
432 | default: | ||
433 | errx(1, "unknown option"); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | fd = open(options.output, O_CREAT|O_TRUNC|O_RDWR, 0666); | ||
438 | if (fd == -1) | ||
439 | err(1, "cannot create file %s", options.output); | ||
440 | |||
441 | ret = jit_write_elf(fd, "main", x86_code, sizeof(x86_code)); | ||
442 | close(fd); | ||
443 | |||
444 | if (ret != 0) | ||
445 | unlink(options.output); | ||
446 | |||
447 | return ret; | ||
448 | } | ||
449 | #endif | ||
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h new file mode 100644 index 000000000000..2fbeb59c4bdd --- /dev/null +++ b/tools/perf/util/genelf.h | |||
@@ -0,0 +1,61 @@ | |||
1 | #ifndef __GENELF_H__ | ||
2 | #define __GENELF_H__ | ||
3 | |||
4 | /* genelf.c */ | ||
5 | int jit_write_elf(int fd, uint64_t code_addr, const char *sym, | ||
6 | const void *code, int csize, void *debug, int nr_debug_entries); | ||
7 | /* genelf_debug.c */ | ||
8 | int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); | ||
9 | |||
10 | #if defined(__arm__) | ||
11 | #define GEN_ELF_ARCH EM_ARM | ||
12 | #define GEN_ELF_CLASS ELFCLASS32 | ||
13 | #elif defined(__aarch64__) | ||
14 | #define GEN_ELF_ARCH EM_AARCH64 | ||
15 | #define GEN_ELF_CLASS ELFCLASS64 | ||
16 | #elif defined(__x86_64__) | ||
17 | #define GEN_ELF_ARCH EM_X86_64 | ||
18 | #define GEN_ELF_CLASS ELFCLASS64 | ||
19 | #elif defined(__i386__) | ||
20 | #define GEN_ELF_ARCH EM_386 | ||
21 | #define GEN_ELF_CLASS ELFCLASS32 | ||
22 | #elif defined(__powerpc64__) | ||
23 | #define GEN_ELF_ARCH EM_PPC64 | ||
24 | #define GEN_ELF_CLASS ELFCLASS64 | ||
25 | #elif defined(__powerpc__) | ||
26 | #define GEN_ELF_ARCH EM_PPC | ||
27 | #define GEN_ELF_CLASS ELFCLASS32 | ||
28 | #else | ||
29 | #error "unsupported architecture" | ||
30 | #endif | ||
31 | |||
32 | #if __BYTE_ORDER == __BIG_ENDIAN | ||
33 | #define GEN_ELF_ENDIAN ELFDATA2MSB | ||
34 | #else | ||
35 | #define GEN_ELF_ENDIAN ELFDATA2LSB | ||
36 | #endif | ||
37 | |||
38 | #if GEN_ELF_CLASS == ELFCLASS64 | ||
39 | #define elf_newehdr elf64_newehdr | ||
40 | #define elf_getshdr elf64_getshdr | ||
41 | #define Elf_Ehdr Elf64_Ehdr | ||
42 | #define Elf_Shdr Elf64_Shdr | ||
43 | #define Elf_Sym Elf64_Sym | ||
44 | #define ELF_ST_TYPE(a) ELF64_ST_TYPE(a) | ||
45 | #define ELF_ST_BIND(a) ELF64_ST_BIND(a) | ||
46 | #define ELF_ST_VIS(a) ELF64_ST_VISIBILITY(a) | ||
47 | #else | ||
48 | #define elf_newehdr elf32_newehdr | ||
49 | #define elf_getshdr elf32_getshdr | ||
50 | #define Elf_Ehdr Elf32_Ehdr | ||
51 | #define Elf_Shdr Elf32_Shdr | ||
52 | #define Elf_Sym Elf32_Sym | ||
53 | #define ELF_ST_TYPE(a) ELF32_ST_TYPE(a) | ||
54 | #define ELF_ST_BIND(a) ELF32_ST_BIND(a) | ||
55 | #define ELF_ST_VIS(a) ELF32_ST_VISIBILITY(a) | ||
56 | #endif | ||
57 | |||
58 | /* The .text section is directly after the ELF header */ | ||
59 | #define GEN_ELF_TEXT_OFFSET sizeof(Elf_Ehdr) | ||
60 | |||
61 | #endif | ||
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c new file mode 100644 index 000000000000..5980f7d256b1 --- /dev/null +++ b/tools/perf/util/genelf_debug.c | |||
@@ -0,0 +1,610 @@ | |||
1 | /* | ||
2 | * genelf_debug.c | ||
3 | * Copyright (C) 2015, Google, Inc | ||
4 | * | ||
5 | * Contributed by: | ||
6 | * Stephane Eranian <eranian@google.com> | ||
7 | * | ||
8 | * Released under the GPL v2. | ||
9 | * | ||
10 | * based on GPLv2 source code from Oprofile | ||
11 | * @remark Copyright 2007 OProfile authors | ||
12 | * @author Philippe Elie | ||
13 | */ | ||
14 | #include <sys/types.h> | ||
15 | #include <stdio.h> | ||
16 | #include <getopt.h> | ||
17 | #include <stddef.h> | ||
18 | #include <libelf.h> | ||
19 | #include <string.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <inttypes.h> | ||
22 | #include <limits.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <err.h> | ||
25 | #include <dwarf.h> | ||
26 | |||
27 | #include "perf.h" | ||
28 | #include "genelf.h" | ||
29 | #include "../util/jitdump.h" | ||
30 | |||
31 | #define BUFFER_EXT_DFL_SIZE (4 * 1024) | ||
32 | |||
33 | typedef uint32_t uword; | ||
34 | typedef uint16_t uhalf; | ||
35 | typedef int32_t sword; | ||
36 | typedef int16_t shalf; | ||
37 | typedef uint8_t ubyte; | ||
38 | typedef int8_t sbyte; | ||
39 | |||
40 | struct buffer_ext { | ||
41 | size_t cur_pos; | ||
42 | size_t max_sz; | ||
43 | void *data; | ||
44 | }; | ||
45 | |||
46 | static void | ||
47 | buffer_ext_dump(struct buffer_ext *be, const char *msg) | ||
48 | { | ||
49 | size_t i; | ||
50 | warnx("DUMP for %s", msg); | ||
51 | for (i = 0 ; i < be->cur_pos; i++) | ||
52 | warnx("%4zu 0x%02x", i, (((char *)be->data)[i]) & 0xff); | ||
53 | } | ||
54 | |||
55 | static inline int | ||
56 | buffer_ext_add(struct buffer_ext *be, void *addr, size_t sz) | ||
57 | { | ||
58 | void *tmp; | ||
59 | size_t be_sz = be->max_sz; | ||
60 | |||
61 | retry: | ||
62 | if ((be->cur_pos + sz) < be_sz) { | ||
63 | memcpy(be->data + be->cur_pos, addr, sz); | ||
64 | be->cur_pos += sz; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | if (!be_sz) | ||
69 | be_sz = BUFFER_EXT_DFL_SIZE; | ||
70 | else | ||
71 | be_sz <<= 1; | ||
72 | |||
73 | tmp = realloc(be->data, be_sz); | ||
74 | if (!tmp) | ||
75 | return -1; | ||
76 | |||
77 | be->data = tmp; | ||
78 | be->max_sz = be_sz; | ||
79 | |||
80 | goto retry; | ||
81 | } | ||
82 | |||
83 | static void | ||
84 | buffer_ext_init(struct buffer_ext *be) | ||
85 | { | ||
86 | be->data = NULL; | ||
87 | be->cur_pos = 0; | ||
88 | be->max_sz = 0; | ||
89 | } | ||
90 | |||
91 | static inline size_t | ||
92 | buffer_ext_size(struct buffer_ext *be) | ||
93 | { | ||
94 | return be->cur_pos; | ||
95 | } | ||
96 | |||
97 | static inline void * | ||
98 | buffer_ext_addr(struct buffer_ext *be) | ||
99 | { | ||
100 | return be->data; | ||
101 | } | ||
102 | |||
103 | struct debug_line_header { | ||
104 | // Not counting this field | ||
105 | uword total_length; | ||
106 | // version number (2 currently) | ||
107 | uhalf version; | ||
108 | // relative offset from next field to | ||
109 | // program statement | ||
110 | uword prolog_length; | ||
111 | ubyte minimum_instruction_length; | ||
112 | ubyte default_is_stmt; | ||
113 | // line_base - see DWARF 2 specs | ||
114 | sbyte line_base; | ||
115 | // line_range - see DWARF 2 specs | ||
116 | ubyte line_range; | ||
117 | // number of opcode + 1 | ||
118 | ubyte opcode_base; | ||
119 | /* follow the array of opcode args nr: ubytes [nr_opcode_base] */ | ||
120 | /* follow the search directories index, zero terminated string | ||
121 | * terminated by an empty string. | ||
122 | */ | ||
123 | /* follow an array of { filename, LEB128, LEB128, LEB128 }, first is | ||
124 | * the directory index entry, 0 means current directory, then mtime | ||
125 | * and filesize, last entry is followed by en empty string. | ||
126 | */ | ||
127 | /* follow the first program statement */ | ||
128 | } __attribute__((packed)); | ||
129 | |||
130 | /* DWARF 2 spec talk only about one possible compilation unit header while | ||
131 | * binutils can handle two flavours of dwarf 2, 32 and 64 bits, this is not | ||
132 | * related to the used arch, an ELF 32 can hold more than 4 Go of debug | ||
133 | * information. For now we handle only DWARF 2 32 bits comp unit. It'll only | ||
134 | * become a problem if we generate more than 4GB of debug information. | ||
135 | */ | ||
136 | struct compilation_unit_header { | ||
137 | uword total_length; | ||
138 | uhalf version; | ||
139 | uword debug_abbrev_offset; | ||
140 | ubyte pointer_size; | ||
141 | } __attribute__((packed)); | ||
142 | |||
143 | #define DW_LNS_num_opcode (DW_LNS_set_isa + 1) | ||
144 | |||
145 | /* field filled at run time are marked with -1 */ | ||
146 | static struct debug_line_header const default_debug_line_header = { | ||
147 | .total_length = -1, | ||
148 | .version = 2, | ||
149 | .prolog_length = -1, | ||
150 | .minimum_instruction_length = 1, /* could be better when min instruction size != 1 */ | ||
151 | .default_is_stmt = 1, /* we don't take care about basic block */ | ||
152 | .line_base = -5, /* sensible value for line base ... */ | ||
153 | .line_range = -14, /* ... and line range are guessed statically */ | ||
154 | .opcode_base = DW_LNS_num_opcode | ||
155 | }; | ||
156 | |||
157 | static ubyte standard_opcode_length[] = | ||
158 | { | ||
159 | 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 | ||
160 | }; | ||
161 | #if 0 | ||
162 | { | ||
163 | [DW_LNS_advance_pc] = 1, | ||
164 | [DW_LNS_advance_line] = 1, | ||
165 | [DW_LNS_set_file] = 1, | ||
166 | [DW_LNS_set_column] = 1, | ||
167 | [DW_LNS_fixed_advance_pc] = 1, | ||
168 | [DW_LNS_set_isa] = 1, | ||
169 | }; | ||
170 | #endif | ||
171 | |||
172 | /* field filled at run time are marked with -1 */ | ||
173 | static struct compilation_unit_header default_comp_unit_header = { | ||
174 | .total_length = -1, | ||
175 | .version = 2, | ||
176 | .debug_abbrev_offset = 0, /* we reuse the same abbrev entries for all comp unit */ | ||
177 | .pointer_size = sizeof(void *) | ||
178 | }; | ||
179 | |||
180 | static void emit_uword(struct buffer_ext *be, uword data) | ||
181 | { | ||
182 | buffer_ext_add(be, &data, sizeof(uword)); | ||
183 | } | ||
184 | |||
185 | static void emit_string(struct buffer_ext *be, const char *s) | ||
186 | { | ||
187 | buffer_ext_add(be, (void *)s, strlen(s) + 1); | ||
188 | } | ||
189 | |||
190 | static void emit_unsigned_LEB128(struct buffer_ext *be, | ||
191 | unsigned long data) | ||
192 | { | ||
193 | do { | ||
194 | ubyte cur = data & 0x7F; | ||
195 | data >>= 7; | ||
196 | if (data) | ||
197 | cur |= 0x80; | ||
198 | buffer_ext_add(be, &cur, 1); | ||
199 | } while (data); | ||
200 | } | ||
201 | |||
202 | static void emit_signed_LEB128(struct buffer_ext *be, long data) | ||
203 | { | ||
204 | int more = 1; | ||
205 | int negative = data < 0; | ||
206 | int size = sizeof(long) * CHAR_BIT; | ||
207 | while (more) { | ||
208 | ubyte cur = data & 0x7F; | ||
209 | data >>= 7; | ||
210 | if (negative) | ||
211 | data |= - (1 << (size - 7)); | ||
212 | if ((data == 0 && !(cur & 0x40)) || | ||
213 | (data == -1l && (cur & 0x40))) | ||
214 | more = 0; | ||
215 | else | ||
216 | cur |= 0x80; | ||
217 | buffer_ext_add(be, &cur, 1); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static void emit_extended_opcode(struct buffer_ext *be, ubyte opcode, | ||
222 | void *data, size_t data_len) | ||
223 | { | ||
224 | buffer_ext_add(be, (char *)"", 1); | ||
225 | |||
226 | emit_unsigned_LEB128(be, data_len + 1); | ||
227 | |||
228 | buffer_ext_add(be, &opcode, 1); | ||
229 | buffer_ext_add(be, data, data_len); | ||
230 | } | ||
231 | |||
232 | static void emit_opcode(struct buffer_ext *be, ubyte opcode) | ||
233 | { | ||
234 | buffer_ext_add(be, &opcode, 1); | ||
235 | } | ||
236 | |||
237 | static void emit_opcode_signed(struct buffer_ext *be, | ||
238 | ubyte opcode, long data) | ||
239 | { | ||
240 | buffer_ext_add(be, &opcode, 1); | ||
241 | emit_signed_LEB128(be, data); | ||
242 | } | ||
243 | |||
244 | static void emit_opcode_unsigned(struct buffer_ext *be, ubyte opcode, | ||
245 | unsigned long data) | ||
246 | { | ||
247 | buffer_ext_add(be, &opcode, 1); | ||
248 | emit_unsigned_LEB128(be, data); | ||
249 | } | ||
250 | |||
251 | static void emit_advance_pc(struct buffer_ext *be, unsigned long delta_pc) | ||
252 | { | ||
253 | emit_opcode_unsigned(be, DW_LNS_advance_pc, delta_pc); | ||
254 | } | ||
255 | |||
256 | static void emit_advance_lineno(struct buffer_ext *be, long delta_lineno) | ||
257 | { | ||
258 | emit_opcode_signed(be, DW_LNS_advance_line, delta_lineno); | ||
259 | } | ||
260 | |||
261 | static void emit_lne_end_of_sequence(struct buffer_ext *be) | ||
262 | { | ||
263 | emit_extended_opcode(be, DW_LNE_end_sequence, NULL, 0); | ||
264 | } | ||
265 | |||
266 | static void emit_set_file(struct buffer_ext *be, unsigned long idx) | ||
267 | { | ||
268 | emit_opcode_unsigned(be, DW_LNS_set_file, idx); | ||
269 | } | ||
270 | |||
271 | static void emit_lne_define_filename(struct buffer_ext *be, | ||
272 | const char *filename) | ||
273 | { | ||
274 | buffer_ext_add(be, (void *)"", 1); | ||
275 | |||
276 | /* LNE field, strlen(filename) + zero termination, 3 bytes for: the dir entry, timestamp, filesize */ | ||
277 | emit_unsigned_LEB128(be, strlen(filename) + 5); | ||
278 | emit_opcode(be, DW_LNE_define_file); | ||
279 | emit_string(be, filename); | ||
280 | /* directory index 0=do not know */ | ||
281 | emit_unsigned_LEB128(be, 0); | ||
282 | /* last modification date on file 0=do not know */ | ||
283 | emit_unsigned_LEB128(be, 0); | ||
284 | /* filesize 0=do not know */ | ||
285 | emit_unsigned_LEB128(be, 0); | ||
286 | } | ||
287 | |||
288 | static void emit_lne_set_address(struct buffer_ext *be, | ||
289 | void *address) | ||
290 | { | ||
291 | emit_extended_opcode(be, DW_LNE_set_address, &address, sizeof(unsigned long)); | ||
292 | } | ||
293 | |||
294 | static ubyte get_special_opcode(struct debug_entry *ent, | ||
295 | unsigned int last_line, | ||
296 | unsigned long last_vma) | ||
297 | { | ||
298 | unsigned int temp; | ||
299 | unsigned long delta_addr; | ||
300 | |||
301 | /* | ||
302 | * delta from line_base | ||
303 | */ | ||
304 | temp = (ent->lineno - last_line) - default_debug_line_header.line_base; | ||
305 | |||
306 | if (temp >= default_debug_line_header.line_range) | ||
307 | return 0; | ||
308 | |||
309 | /* | ||
310 | * delta of addresses | ||
311 | */ | ||
312 | delta_addr = (ent->addr - last_vma) / default_debug_line_header.minimum_instruction_length; | ||
313 | |||
314 | /* This is not sufficient to ensure opcode will be in [0-256] but | ||
315 | * sufficient to ensure when summing with the delta lineno we will | ||
316 | * not overflow the unsigned long opcode */ | ||
317 | |||
318 | if (delta_addr <= 256 / default_debug_line_header.line_range) { | ||
319 | unsigned long opcode = temp + | ||
320 | (delta_addr * default_debug_line_header.line_range) + | ||
321 | default_debug_line_header.opcode_base; | ||
322 | |||
323 | return opcode <= 255 ? opcode : 0; | ||
324 | } | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void emit_lineno_info(struct buffer_ext *be, | ||
329 | struct debug_entry *ent, size_t nr_entry, | ||
330 | unsigned long code_addr) | ||
331 | { | ||
332 | size_t i; | ||
333 | |||
334 | /* | ||
335 | * Machine state at start of a statement program | ||
336 | * address = 0 | ||
337 | * file = 1 | ||
338 | * line = 1 | ||
339 | * column = 0 | ||
340 | * is_stmt = default_is_stmt as given in the debug_line_header | ||
341 | * basic block = 0 | ||
342 | * end sequence = 0 | ||
343 | */ | ||
344 | |||
345 | /* start state of the state machine we take care of */ | ||
346 | unsigned long last_vma = code_addr; | ||
347 | char const *cur_filename = NULL; | ||
348 | unsigned long cur_file_idx = 0; | ||
349 | int last_line = 1; | ||
350 | |||
351 | emit_lne_set_address(be, (void *)code_addr); | ||
352 | |||
353 | for (i = 0; i < nr_entry; i++, ent = debug_entry_next(ent)) { | ||
354 | int need_copy = 0; | ||
355 | ubyte special_opcode; | ||
356 | |||
357 | /* | ||
358 | * check if filename changed, if so add it | ||
359 | */ | ||
360 | if (!cur_filename || strcmp(cur_filename, ent->name)) { | ||
361 | emit_lne_define_filename(be, ent->name); | ||
362 | cur_filename = ent->name; | ||
363 | emit_set_file(be, ++cur_file_idx); | ||
364 | need_copy = 1; | ||
365 | } | ||
366 | |||
367 | special_opcode = get_special_opcode(ent, last_line, last_vma); | ||
368 | if (special_opcode != 0) { | ||
369 | last_line = ent->lineno; | ||
370 | last_vma = ent->addr; | ||
371 | emit_opcode(be, special_opcode); | ||
372 | } else { | ||
373 | /* | ||
374 | * lines differ, emit line delta | ||
375 | */ | ||
376 | if (last_line != ent->lineno) { | ||
377 | emit_advance_lineno(be, ent->lineno - last_line); | ||
378 | last_line = ent->lineno; | ||
379 | need_copy = 1; | ||
380 | } | ||
381 | /* | ||
382 | * addresses differ, emit address delta | ||
383 | */ | ||
384 | if (last_vma != ent->addr) { | ||
385 | emit_advance_pc(be, ent->addr - last_vma); | ||
386 | last_vma = ent->addr; | ||
387 | need_copy = 1; | ||
388 | } | ||
389 | /* | ||
390 | * add new row to matrix | ||
391 | */ | ||
392 | if (need_copy) | ||
393 | emit_opcode(be, DW_LNS_copy); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | static void add_debug_line(struct buffer_ext *be, | ||
399 | struct debug_entry *ent, size_t nr_entry, | ||
400 | unsigned long code_addr) | ||
401 | { | ||
402 | struct debug_line_header * dbg_header; | ||
403 | size_t old_size; | ||
404 | |||
405 | old_size = buffer_ext_size(be); | ||
406 | |||
407 | buffer_ext_add(be, (void *)&default_debug_line_header, | ||
408 | sizeof(default_debug_line_header)); | ||
409 | |||
410 | buffer_ext_add(be, &standard_opcode_length, sizeof(standard_opcode_length)); | ||
411 | |||
412 | // empty directory entry | ||
413 | buffer_ext_add(be, (void *)"", 1); | ||
414 | |||
415 | // empty filename directory | ||
416 | buffer_ext_add(be, (void *)"", 1); | ||
417 | |||
418 | dbg_header = buffer_ext_addr(be) + old_size; | ||
419 | dbg_header->prolog_length = (buffer_ext_size(be) - old_size) - | ||
420 | offsetof(struct debug_line_header, minimum_instruction_length); | ||
421 | |||
422 | emit_lineno_info(be, ent, nr_entry, code_addr); | ||
423 | |||
424 | emit_lne_end_of_sequence(be); | ||
425 | |||
426 | dbg_header = buffer_ext_addr(be) + old_size; | ||
427 | dbg_header->total_length = (buffer_ext_size(be) - old_size) - | ||
428 | offsetof(struct debug_line_header, version); | ||
429 | } | ||
430 | |||
431 | static void | ||
432 | add_debug_abbrev(struct buffer_ext *be) | ||
433 | { | ||
434 | emit_unsigned_LEB128(be, 1); | ||
435 | emit_unsigned_LEB128(be, DW_TAG_compile_unit); | ||
436 | emit_unsigned_LEB128(be, DW_CHILDREN_yes); | ||
437 | emit_unsigned_LEB128(be, DW_AT_stmt_list); | ||
438 | emit_unsigned_LEB128(be, DW_FORM_data4); | ||
439 | emit_unsigned_LEB128(be, 0); | ||
440 | emit_unsigned_LEB128(be, 0); | ||
441 | emit_unsigned_LEB128(be, 0); | ||
442 | } | ||
443 | |||
444 | static void | ||
445 | add_compilation_unit(struct buffer_ext *be, | ||
446 | size_t offset_debug_line) | ||
447 | { | ||
448 | struct compilation_unit_header *comp_unit_header; | ||
449 | size_t old_size = buffer_ext_size(be); | ||
450 | |||
451 | buffer_ext_add(be, &default_comp_unit_header, | ||
452 | sizeof(default_comp_unit_header)); | ||
453 | |||
454 | emit_unsigned_LEB128(be, 1); | ||
455 | emit_uword(be, offset_debug_line); | ||
456 | |||
457 | comp_unit_header = buffer_ext_addr(be) + old_size; | ||
458 | comp_unit_header->total_length = (buffer_ext_size(be) - old_size) - | ||
459 | offsetof(struct compilation_unit_header, version); | ||
460 | } | ||
461 | |||
462 | static int | ||
463 | jit_process_debug_info(uint64_t code_addr, | ||
464 | void *debug, int nr_debug_entries, | ||
465 | struct buffer_ext *dl, | ||
466 | struct buffer_ext *da, | ||
467 | struct buffer_ext *di) | ||
468 | { | ||
469 | struct debug_entry *ent = debug; | ||
470 | int i; | ||
471 | |||
472 | for (i = 0; i < nr_debug_entries; i++) { | ||
473 | ent->addr = ent->addr - code_addr; | ||
474 | ent = debug_entry_next(ent); | ||
475 | } | ||
476 | add_compilation_unit(di, buffer_ext_size(dl)); | ||
477 | add_debug_line(dl, debug, nr_debug_entries, 0); | ||
478 | add_debug_abbrev(da); | ||
479 | if (0) buffer_ext_dump(da, "abbrev"); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | int | ||
485 | jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries) | ||
486 | { | ||
487 | Elf_Data *d; | ||
488 | Elf_Scn *scn; | ||
489 | Elf_Shdr *shdr; | ||
490 | struct buffer_ext dl, di, da; | ||
491 | int ret; | ||
492 | |||
493 | buffer_ext_init(&dl); | ||
494 | buffer_ext_init(&di); | ||
495 | buffer_ext_init(&da); | ||
496 | |||
497 | ret = jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di); | ||
498 | if (ret) | ||
499 | return -1; | ||
500 | /* | ||
501 | * setup .debug_line section | ||
502 | */ | ||
503 | scn = elf_newscn(e); | ||
504 | if (!scn) { | ||
505 | warnx("cannot create section"); | ||
506 | return -1; | ||
507 | } | ||
508 | |||
509 | d = elf_newdata(scn); | ||
510 | if (!d) { | ||
511 | warnx("cannot get new data"); | ||
512 | return -1; | ||
513 | } | ||
514 | |||
515 | d->d_align = 1; | ||
516 | d->d_off = 0LL; | ||
517 | d->d_buf = buffer_ext_addr(&dl); | ||
518 | d->d_type = ELF_T_BYTE; | ||
519 | d->d_size = buffer_ext_size(&dl); | ||
520 | d->d_version = EV_CURRENT; | ||
521 | |||
522 | shdr = elf_getshdr(scn); | ||
523 | if (!shdr) { | ||
524 | warnx("cannot get section header"); | ||
525 | return -1; | ||
526 | } | ||
527 | |||
528 | shdr->sh_name = 52; /* .debug_line */ | ||
529 | shdr->sh_type = SHT_PROGBITS; | ||
530 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
531 | shdr->sh_flags = 0; | ||
532 | shdr->sh_entsize = 0; | ||
533 | |||
534 | /* | ||
535 | * setup .debug_info section | ||
536 | */ | ||
537 | scn = elf_newscn(e); | ||
538 | if (!scn) { | ||
539 | warnx("cannot create section"); | ||
540 | return -1; | ||
541 | } | ||
542 | |||
543 | d = elf_newdata(scn); | ||
544 | if (!d) { | ||
545 | warnx("cannot get new data"); | ||
546 | return -1; | ||
547 | } | ||
548 | |||
549 | d->d_align = 1; | ||
550 | d->d_off = 0LL; | ||
551 | d->d_buf = buffer_ext_addr(&di); | ||
552 | d->d_type = ELF_T_BYTE; | ||
553 | d->d_size = buffer_ext_size(&di); | ||
554 | d->d_version = EV_CURRENT; | ||
555 | |||
556 | shdr = elf_getshdr(scn); | ||
557 | if (!shdr) { | ||
558 | warnx("cannot get section header"); | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | shdr->sh_name = 64; /* .debug_info */ | ||
563 | shdr->sh_type = SHT_PROGBITS; | ||
564 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
565 | shdr->sh_flags = 0; | ||
566 | shdr->sh_entsize = 0; | ||
567 | |||
568 | /* | ||
569 | * setup .debug_abbrev section | ||
570 | */ | ||
571 | scn = elf_newscn(e); | ||
572 | if (!scn) { | ||
573 | warnx("cannot create section"); | ||
574 | return -1; | ||
575 | } | ||
576 | |||
577 | d = elf_newdata(scn); | ||
578 | if (!d) { | ||
579 | warnx("cannot get new data"); | ||
580 | return -1; | ||
581 | } | ||
582 | |||
583 | d->d_align = 1; | ||
584 | d->d_off = 0LL; | ||
585 | d->d_buf = buffer_ext_addr(&da); | ||
586 | d->d_type = ELF_T_BYTE; | ||
587 | d->d_size = buffer_ext_size(&da); | ||
588 | d->d_version = EV_CURRENT; | ||
589 | |||
590 | shdr = elf_getshdr(scn); | ||
591 | if (!shdr) { | ||
592 | warnx("cannot get section header"); | ||
593 | return -1; | ||
594 | } | ||
595 | |||
596 | shdr->sh_name = 76; /* .debug_info */ | ||
597 | shdr->sh_type = SHT_PROGBITS; | ||
598 | shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */ | ||
599 | shdr->sh_flags = 0; | ||
600 | shdr->sh_entsize = 0; | ||
601 | |||
602 | /* | ||
603 | * now we update the ELF image with all the sections | ||
604 | */ | ||
605 | if (elf_update(e, ELF_C_WRITE) < 0) { | ||
606 | warnx("elf_update debug failed"); | ||
607 | return -1; | ||
608 | } | ||
609 | return 0; | ||
610 | } | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f50b7235ecb6..90680ec9f8b8 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include "strbuf.h" | 23 | #include "strbuf.h" |
24 | #include "build-id.h" | 24 | #include "build-id.h" |
25 | #include "data.h" | 25 | #include "data.h" |
26 | #include <api/fs/fs.h> | ||
27 | #include "asm/bug.h" | ||
26 | 28 | ||
27 | /* | 29 | /* |
28 | * magic2 = "PERFILE2" | 30 | * magic2 = "PERFILE2" |
@@ -868,6 +870,199 @@ static int write_auxtrace(int fd, struct perf_header *h, | |||
868 | return err; | 870 | return err; |
869 | } | 871 | } |
870 | 872 | ||
873 | static int cpu_cache_level__sort(const void *a, const void *b) | ||
874 | { | ||
875 | struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; | ||
876 | struct cpu_cache_level *cache_b = (struct cpu_cache_level *)b; | ||
877 | |||
878 | return cache_a->level - cache_b->level; | ||
879 | } | ||
880 | |||
881 | static bool cpu_cache_level__cmp(struct cpu_cache_level *a, struct cpu_cache_level *b) | ||
882 | { | ||
883 | if (a->level != b->level) | ||
884 | return false; | ||
885 | |||
886 | if (a->line_size != b->line_size) | ||
887 | return false; | ||
888 | |||
889 | if (a->sets != b->sets) | ||
890 | return false; | ||
891 | |||
892 | if (a->ways != b->ways) | ||
893 | return false; | ||
894 | |||
895 | if (strcmp(a->type, b->type)) | ||
896 | return false; | ||
897 | |||
898 | if (strcmp(a->size, b->size)) | ||
899 | return false; | ||
900 | |||
901 | if (strcmp(a->map, b->map)) | ||
902 | return false; | ||
903 | |||
904 | return true; | ||
905 | } | ||
906 | |||
907 | static int cpu_cache_level__read(struct cpu_cache_level *cache, u32 cpu, u16 level) | ||
908 | { | ||
909 | char path[PATH_MAX], file[PATH_MAX]; | ||
910 | struct stat st; | ||
911 | size_t len; | ||
912 | |||
913 | scnprintf(path, PATH_MAX, "devices/system/cpu/cpu%d/cache/index%d/", cpu, level); | ||
914 | scnprintf(file, PATH_MAX, "%s/%s", sysfs__mountpoint(), path); | ||
915 | |||
916 | if (stat(file, &st)) | ||
917 | return 1; | ||
918 | |||
919 | scnprintf(file, PATH_MAX, "%s/level", path); | ||
920 | if (sysfs__read_int(file, (int *) &cache->level)) | ||
921 | return -1; | ||
922 | |||
923 | scnprintf(file, PATH_MAX, "%s/coherency_line_size", path); | ||
924 | if (sysfs__read_int(file, (int *) &cache->line_size)) | ||
925 | return -1; | ||
926 | |||
927 | scnprintf(file, PATH_MAX, "%s/number_of_sets", path); | ||
928 | if (sysfs__read_int(file, (int *) &cache->sets)) | ||
929 | return -1; | ||
930 | |||
931 | scnprintf(file, PATH_MAX, "%s/ways_of_associativity", path); | ||
932 | if (sysfs__read_int(file, (int *) &cache->ways)) | ||
933 | return -1; | ||
934 | |||
935 | scnprintf(file, PATH_MAX, "%s/type", path); | ||
936 | if (sysfs__read_str(file, &cache->type, &len)) | ||
937 | return -1; | ||
938 | |||
939 | cache->type[len] = 0; | ||
940 | cache->type = rtrim(cache->type); | ||
941 | |||
942 | scnprintf(file, PATH_MAX, "%s/size", path); | ||
943 | if (sysfs__read_str(file, &cache->size, &len)) { | ||
944 | free(cache->type); | ||
945 | return -1; | ||
946 | } | ||
947 | |||
948 | cache->size[len] = 0; | ||
949 | cache->size = rtrim(cache->size); | ||
950 | |||
951 | scnprintf(file, PATH_MAX, "%s/shared_cpu_list", path); | ||
952 | if (sysfs__read_str(file, &cache->map, &len)) { | ||
953 | free(cache->map); | ||
954 | free(cache->type); | ||
955 | return -1; | ||
956 | } | ||
957 | |||
958 | cache->map[len] = 0; | ||
959 | cache->map = rtrim(cache->map); | ||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | static void cpu_cache_level__fprintf(FILE *out, struct cpu_cache_level *c) | ||
964 | { | ||
965 | fprintf(out, "L%d %-15s %8s [%s]\n", c->level, c->type, c->size, c->map); | ||
966 | } | ||
967 | |||
968 | static int build_caches(struct cpu_cache_level caches[], u32 size, u32 *cntp) | ||
969 | { | ||
970 | u32 i, cnt = 0; | ||
971 | long ncpus; | ||
972 | u32 nr, cpu; | ||
973 | u16 level; | ||
974 | |||
975 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
976 | if (ncpus < 0) | ||
977 | return -1; | ||
978 | |||
979 | nr = (u32)(ncpus & UINT_MAX); | ||
980 | |||
981 | for (cpu = 0; cpu < nr; cpu++) { | ||
982 | for (level = 0; level < 10; level++) { | ||
983 | struct cpu_cache_level c; | ||
984 | int err; | ||
985 | |||
986 | err = cpu_cache_level__read(&c, cpu, level); | ||
987 | if (err < 0) | ||
988 | return err; | ||
989 | |||
990 | if (err == 1) | ||
991 | break; | ||
992 | |||
993 | for (i = 0; i < cnt; i++) { | ||
994 | if (cpu_cache_level__cmp(&c, &caches[i])) | ||
995 | break; | ||
996 | } | ||
997 | |||
998 | if (i == cnt) | ||
999 | caches[cnt++] = c; | ||
1000 | else | ||
1001 | cpu_cache_level__free(&c); | ||
1002 | |||
1003 | if (WARN_ONCE(cnt == size, "way too many cpu caches..")) | ||
1004 | goto out; | ||
1005 | } | ||
1006 | } | ||
1007 | out: | ||
1008 | *cntp = cnt; | ||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | #define MAX_CACHES 2000 | ||
1013 | |||
1014 | static int write_cache(int fd, struct perf_header *h __maybe_unused, | ||
1015 | struct perf_evlist *evlist __maybe_unused) | ||
1016 | { | ||
1017 | struct cpu_cache_level caches[MAX_CACHES]; | ||
1018 | u32 cnt = 0, i, version = 1; | ||
1019 | int ret; | ||
1020 | |||
1021 | ret = build_caches(caches, MAX_CACHES, &cnt); | ||
1022 | if (ret) | ||
1023 | goto out; | ||
1024 | |||
1025 | qsort(&caches, cnt, sizeof(struct cpu_cache_level), cpu_cache_level__sort); | ||
1026 | |||
1027 | ret = do_write(fd, &version, sizeof(u32)); | ||
1028 | if (ret < 0) | ||
1029 | goto out; | ||
1030 | |||
1031 | ret = do_write(fd, &cnt, sizeof(u32)); | ||
1032 | if (ret < 0) | ||
1033 | goto out; | ||
1034 | |||
1035 | for (i = 0; i < cnt; i++) { | ||
1036 | struct cpu_cache_level *c = &caches[i]; | ||
1037 | |||
1038 | #define _W(v) \ | ||
1039 | ret = do_write(fd, &c->v, sizeof(u32)); \ | ||
1040 | if (ret < 0) \ | ||
1041 | goto out; | ||
1042 | |||
1043 | _W(level) | ||
1044 | _W(line_size) | ||
1045 | _W(sets) | ||
1046 | _W(ways) | ||
1047 | #undef _W | ||
1048 | |||
1049 | #define _W(v) \ | ||
1050 | ret = do_write_string(fd, (const char *) c->v); \ | ||
1051 | if (ret < 0) \ | ||
1052 | goto out; | ||
1053 | |||
1054 | _W(type) | ||
1055 | _W(size) | ||
1056 | _W(map) | ||
1057 | #undef _W | ||
1058 | } | ||
1059 | |||
1060 | out: | ||
1061 | for (i = 0; i < cnt; i++) | ||
1062 | cpu_cache_level__free(&caches[i]); | ||
1063 | return ret; | ||
1064 | } | ||
1065 | |||
871 | static int write_stat(int fd __maybe_unused, | 1066 | static int write_stat(int fd __maybe_unused, |
872 | struct perf_header *h __maybe_unused, | 1067 | struct perf_header *h __maybe_unused, |
873 | struct perf_evlist *evlist __maybe_unused) | 1068 | struct perf_evlist *evlist __maybe_unused) |
@@ -1172,6 +1367,18 @@ static void print_stat(struct perf_header *ph __maybe_unused, | |||
1172 | fprintf(fp, "# contains stat data\n"); | 1367 | fprintf(fp, "# contains stat data\n"); |
1173 | } | 1368 | } |
1174 | 1369 | ||
1370 | static void print_cache(struct perf_header *ph __maybe_unused, | ||
1371 | int fd __maybe_unused, FILE *fp __maybe_unused) | ||
1372 | { | ||
1373 | int i; | ||
1374 | |||
1375 | fprintf(fp, "# CPU cache info:\n"); | ||
1376 | for (i = 0; i < ph->env.caches_cnt; i++) { | ||
1377 | fprintf(fp, "# "); | ||
1378 | cpu_cache_level__fprintf(fp, &ph->env.caches[i]); | ||
1379 | } | ||
1380 | } | ||
1381 | |||
1175 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, | 1382 | static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, |
1176 | FILE *fp) | 1383 | FILE *fp) |
1177 | { | 1384 | { |
@@ -1665,11 +1872,6 @@ static int process_cpu_topology(struct perf_file_section *section, | |||
1665 | if (ph->needs_swap) | 1872 | if (ph->needs_swap) |
1666 | nr = bswap_32(nr); | 1873 | nr = bswap_32(nr); |
1667 | 1874 | ||
1668 | if (nr > (u32)cpu_nr) { | ||
1669 | pr_debug("core_id number is too big." | ||
1670 | "You may need to upgrade the perf tool.\n"); | ||
1671 | goto free_cpu; | ||
1672 | } | ||
1673 | ph->env.cpu[i].core_id = nr; | 1875 | ph->env.cpu[i].core_id = nr; |
1674 | 1876 | ||
1675 | ret = readn(fd, &nr, sizeof(nr)); | 1877 | ret = readn(fd, &nr, sizeof(nr)); |
@@ -1920,6 +2122,68 @@ static int process_auxtrace(struct perf_file_section *section, | |||
1920 | return err; | 2122 | return err; |
1921 | } | 2123 | } |
1922 | 2124 | ||
2125 | static int process_cache(struct perf_file_section *section __maybe_unused, | ||
2126 | struct perf_header *ph __maybe_unused, int fd __maybe_unused, | ||
2127 | void *data __maybe_unused) | ||
2128 | { | ||
2129 | struct cpu_cache_level *caches; | ||
2130 | u32 cnt, i, version; | ||
2131 | |||
2132 | if (readn(fd, &version, sizeof(version)) != sizeof(version)) | ||
2133 | return -1; | ||
2134 | |||
2135 | if (ph->needs_swap) | ||
2136 | version = bswap_32(version); | ||
2137 | |||
2138 | if (version != 1) | ||
2139 | return -1; | ||
2140 | |||
2141 | if (readn(fd, &cnt, sizeof(cnt)) != sizeof(cnt)) | ||
2142 | return -1; | ||
2143 | |||
2144 | if (ph->needs_swap) | ||
2145 | cnt = bswap_32(cnt); | ||
2146 | |||
2147 | caches = zalloc(sizeof(*caches) * cnt); | ||
2148 | if (!caches) | ||
2149 | return -1; | ||
2150 | |||
2151 | for (i = 0; i < cnt; i++) { | ||
2152 | struct cpu_cache_level c; | ||
2153 | |||
2154 | #define _R(v) \ | ||
2155 | if (readn(fd, &c.v, sizeof(u32)) != sizeof(u32))\ | ||
2156 | goto out_free_caches; \ | ||
2157 | if (ph->needs_swap) \ | ||
2158 | c.v = bswap_32(c.v); \ | ||
2159 | |||
2160 | _R(level) | ||
2161 | _R(line_size) | ||
2162 | _R(sets) | ||
2163 | _R(ways) | ||
2164 | #undef _R | ||
2165 | |||
2166 | #define _R(v) \ | ||
2167 | c.v = do_read_string(fd, ph); \ | ||
2168 | if (!c.v) \ | ||
2169 | goto out_free_caches; | ||
2170 | |||
2171 | _R(type) | ||
2172 | _R(size) | ||
2173 | _R(map) | ||
2174 | #undef _R | ||
2175 | |||
2176 | caches[i] = c; | ||
2177 | } | ||
2178 | |||
2179 | ph->env.caches = caches; | ||
2180 | ph->env.caches_cnt = cnt; | ||
2181 | return 0; | ||
2182 | out_free_caches: | ||
2183 | free(caches); | ||
2184 | return -1; | ||
2185 | } | ||
2186 | |||
1923 | struct feature_ops { | 2187 | struct feature_ops { |
1924 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 2188 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1925 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 2189 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1962,6 +2226,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1962 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | 2226 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), |
1963 | FEAT_OPP(HEADER_AUXTRACE, auxtrace), | 2227 | FEAT_OPP(HEADER_AUXTRACE, auxtrace), |
1964 | FEAT_OPA(HEADER_STAT, stat), | 2228 | FEAT_OPA(HEADER_STAT, stat), |
2229 | FEAT_OPF(HEADER_CACHE, cache), | ||
1965 | }; | 2230 | }; |
1966 | 2231 | ||
1967 | struct header_print_data { | 2232 | struct header_print_data { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index cff9892452ee..d306ca118449 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -32,6 +32,7 @@ enum { | |||
32 | HEADER_GROUP_DESC, | 32 | HEADER_GROUP_DESC, |
33 | HEADER_AUXTRACE, | 33 | HEADER_AUXTRACE, |
34 | HEADER_STAT, | 34 | HEADER_STAT, |
35 | HEADER_CACHE, | ||
35 | HEADER_LAST_FEATURE, | 36 | HEADER_LAST_FEATURE, |
36 | HEADER_FEAT_BITS = 256, | 37 | HEADER_FEAT_BITS = 256, |
37 | }; | 38 | }; |
@@ -120,7 +121,7 @@ int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, | |||
120 | perf_event__handler_t process); | 121 | perf_event__handler_t process); |
121 | int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, | 122 | int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, |
122 | struct perf_evlist **pevlist); | 123 | struct perf_evlist **pevlist); |
123 | int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, | 124 | int perf_event__process_event_update(struct perf_tool *tool, |
124 | union perf_event *event, | 125 | union perf_event *event, |
125 | struct perf_evlist **pevlist); | 126 | struct perf_evlist **pevlist); |
126 | size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); | 127 | size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); |
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c index dc1e41c9b054..43a98a4dc1e1 100644 --- a/tools/perf/util/help-unknown-cmd.c +++ b/tools/perf/util/help-unknown-cmd.c | |||
@@ -6,7 +6,8 @@ | |||
6 | static int autocorrect; | 6 | static int autocorrect; |
7 | static struct cmdnames aliases; | 7 | static struct cmdnames aliases; |
8 | 8 | ||
9 | static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) | 9 | static int perf_unknown_cmd_config(const char *var, const char *value, |
10 | void *cb __maybe_unused) | ||
10 | { | 11 | { |
11 | if (!strcmp(var, "help.autocorrect")) | 12 | if (!strcmp(var, "help.autocorrect")) |
12 | autocorrect = perf_config_int(var,value); | 13 | autocorrect = perf_config_int(var,value); |
@@ -14,7 +15,7 @@ static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) | |||
14 | if (!prefixcmp(var, "alias.")) | 15 | if (!prefixcmp(var, "alias.")) |
15 | add_cmdname(&aliases, var + 6, strlen(var + 6)); | 16 | add_cmdname(&aliases, var + 6, strlen(var + 6)); |
16 | 17 | ||
17 | return perf_default_config(var, value, cb); | 18 | return 0; |
18 | } | 19 | } |
19 | 20 | ||
20 | static int levenshtein_compare(const void *p1, const void *p2) | 21 | static int levenshtein_compare(const void *p1, const void *p2) |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 68a7612019dc..31c4641fe5ff 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -179,6 +179,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
179 | if (h->transaction) | 179 | if (h->transaction) |
180 | hists__new_col_len(hists, HISTC_TRANSACTION, | 180 | hists__new_col_len(hists, HISTC_TRANSACTION, |
181 | hist_entry__transaction_len()); | 181 | hist_entry__transaction_len()); |
182 | |||
183 | if (h->trace_output) | ||
184 | hists__new_col_len(hists, HISTC_TRACE, strlen(h->trace_output)); | ||
182 | } | 185 | } |
183 | 186 | ||
184 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 187 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
@@ -245,6 +248,8 @@ static void he_stat__decay(struct he_stat *he_stat) | |||
245 | /* XXX need decay for weight too? */ | 248 | /* XXX need decay for weight too? */ |
246 | } | 249 | } |
247 | 250 | ||
251 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he); | ||
252 | |||
248 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 253 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
249 | { | 254 | { |
250 | u64 prev_period = he->stat.period; | 255 | u64 prev_period = he->stat.period; |
@@ -260,21 +265,45 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | |||
260 | 265 | ||
261 | diff = prev_period - he->stat.period; | 266 | diff = prev_period - he->stat.period; |
262 | 267 | ||
263 | hists->stats.total_period -= diff; | 268 | if (!he->depth) { |
264 | if (!he->filtered) | 269 | hists->stats.total_period -= diff; |
265 | hists->stats.total_non_filtered_period -= diff; | 270 | if (!he->filtered) |
271 | hists->stats.total_non_filtered_period -= diff; | ||
272 | } | ||
273 | |||
274 | if (!he->leaf) { | ||
275 | struct hist_entry *child; | ||
276 | struct rb_node *node = rb_first(&he->hroot_out); | ||
277 | while (node) { | ||
278 | child = rb_entry(node, struct hist_entry, rb_node); | ||
279 | node = rb_next(node); | ||
280 | |||
281 | if (hists__decay_entry(hists, child)) | ||
282 | hists__delete_entry(hists, child); | ||
283 | } | ||
284 | } | ||
266 | 285 | ||
267 | return he->stat.period == 0; | 286 | return he->stat.period == 0; |
268 | } | 287 | } |
269 | 288 | ||
270 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he) | 289 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he) |
271 | { | 290 | { |
272 | rb_erase(&he->rb_node, &hists->entries); | 291 | struct rb_root *root_in; |
292 | struct rb_root *root_out; | ||
273 | 293 | ||
274 | if (sort__need_collapse) | 294 | if (he->parent_he) { |
275 | rb_erase(&he->rb_node_in, &hists->entries_collapsed); | 295 | root_in = &he->parent_he->hroot_in; |
276 | else | 296 | root_out = &he->parent_he->hroot_out; |
277 | rb_erase(&he->rb_node_in, hists->entries_in); | 297 | } else { |
298 | if (sort__need_collapse) | ||
299 | root_in = &hists->entries_collapsed; | ||
300 | else | ||
301 | root_in = hists->entries_in; | ||
302 | root_out = &hists->entries; | ||
303 | } | ||
304 | |||
305 | rb_erase(&he->rb_node_in, root_in); | ||
306 | rb_erase(&he->rb_node, root_out); | ||
278 | 307 | ||
279 | --hists->nr_entries; | 308 | --hists->nr_entries; |
280 | if (!he->filtered) | 309 | if (!he->filtered) |
@@ -393,6 +422,9 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
393 | } | 422 | } |
394 | INIT_LIST_HEAD(&he->pairs.node); | 423 | INIT_LIST_HEAD(&he->pairs.node); |
395 | thread__get(he->thread); | 424 | thread__get(he->thread); |
425 | |||
426 | if (!symbol_conf.report_hierarchy) | ||
427 | he->leaf = true; | ||
396 | } | 428 | } |
397 | 429 | ||
398 | return he; | 430 | return he; |
@@ -405,6 +437,16 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
405 | return 0; | 437 | return 0; |
406 | } | 438 | } |
407 | 439 | ||
440 | static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period) | ||
441 | { | ||
442 | if (!symbol_conf.use_callchain) | ||
443 | return; | ||
444 | |||
445 | he->hists->callchain_period += period; | ||
446 | if (!he->filtered) | ||
447 | he->hists->callchain_non_filtered_period += period; | ||
448 | } | ||
449 | |||
408 | static struct hist_entry *hists__findnew_entry(struct hists *hists, | 450 | static struct hist_entry *hists__findnew_entry(struct hists *hists, |
409 | struct hist_entry *entry, | 451 | struct hist_entry *entry, |
410 | struct addr_location *al, | 452 | struct addr_location *al, |
@@ -432,8 +474,10 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, | |||
432 | cmp = hist_entry__cmp(he, entry); | 474 | cmp = hist_entry__cmp(he, entry); |
433 | 475 | ||
434 | if (!cmp) { | 476 | if (!cmp) { |
435 | if (sample_self) | 477 | if (sample_self) { |
436 | he_stat__add_period(&he->stat, period, weight); | 478 | he_stat__add_period(&he->stat, period, weight); |
479 | hist_entry__add_callchain_period(he, period); | ||
480 | } | ||
437 | if (symbol_conf.cumulate_callchain) | 481 | if (symbol_conf.cumulate_callchain) |
438 | he_stat__add_period(he->stat_acc, period, weight); | 482 | he_stat__add_period(he->stat_acc, period, weight); |
439 | 483 | ||
@@ -466,6 +510,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, | |||
466 | if (!he) | 510 | if (!he) |
467 | return NULL; | 511 | return NULL; |
468 | 512 | ||
513 | if (sample_self) | ||
514 | hist_entry__add_callchain_period(he, period); | ||
469 | hists->nr_entries++; | 515 | hists->nr_entries++; |
470 | 516 | ||
471 | rb_link_node(&he->rb_node_in, parent, p); | 517 | rb_link_node(&he->rb_node_in, parent, p); |
@@ -624,7 +670,7 @@ iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
624 | } | 670 | } |
625 | 671 | ||
626 | static int | 672 | static int |
627 | iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused, | 673 | iter_add_single_branch_entry(struct hist_entry_iter *iter, |
628 | struct addr_location *al __maybe_unused) | 674 | struct addr_location *al __maybe_unused) |
629 | { | 675 | { |
630 | /* to avoid calling callback function */ | 676 | /* to avoid calling callback function */ |
@@ -951,10 +997,15 @@ out: | |||
951 | int64_t | 997 | int64_t |
952 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 998 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
953 | { | 999 | { |
1000 | struct hists *hists = left->hists; | ||
954 | struct perf_hpp_fmt *fmt; | 1001 | struct perf_hpp_fmt *fmt; |
955 | int64_t cmp = 0; | 1002 | int64_t cmp = 0; |
956 | 1003 | ||
957 | perf_hpp__for_each_sort_list(fmt) { | 1004 | hists__for_each_sort_list(hists, fmt) { |
1005 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1006 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
1007 | continue; | ||
1008 | |||
958 | cmp = fmt->cmp(fmt, left, right); | 1009 | cmp = fmt->cmp(fmt, left, right); |
959 | if (cmp) | 1010 | if (cmp) |
960 | break; | 1011 | break; |
@@ -966,10 +1017,15 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
966 | int64_t | 1017 | int64_t |
967 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | 1018 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) |
968 | { | 1019 | { |
1020 | struct hists *hists = left->hists; | ||
969 | struct perf_hpp_fmt *fmt; | 1021 | struct perf_hpp_fmt *fmt; |
970 | int64_t cmp = 0; | 1022 | int64_t cmp = 0; |
971 | 1023 | ||
972 | perf_hpp__for_each_sort_list(fmt) { | 1024 | hists__for_each_sort_list(hists, fmt) { |
1025 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1026 | !perf_hpp__defined_dynamic_entry(fmt, hists)) | ||
1027 | continue; | ||
1028 | |||
973 | cmp = fmt->collapse(fmt, left, right); | 1029 | cmp = fmt->collapse(fmt, left, right); |
974 | if (cmp) | 1030 | if (cmp) |
975 | break; | 1031 | break; |
@@ -1006,17 +1062,250 @@ void hist_entry__delete(struct hist_entry *he) | |||
1006 | } | 1062 | } |
1007 | 1063 | ||
1008 | /* | 1064 | /* |
1065 | * If this is not the last column, then we need to pad it according to the | ||
1066 | * pre-calculated max lenght for this column, otherwise don't bother adding | ||
1067 | * spaces because that would break viewing this with, for instance, 'less', | ||
1068 | * that would show tons of trailing spaces when a long C++ demangled method | ||
1069 | * names is sampled. | ||
1070 | */ | ||
1071 | int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, | ||
1072 | struct perf_hpp_fmt *fmt, int printed) | ||
1073 | { | ||
1074 | if (!list_is_last(&fmt->list, &he->hists->hpp_list->fields)) { | ||
1075 | const int width = fmt->width(fmt, hpp, hists_to_evsel(he->hists)); | ||
1076 | if (printed < width) { | ||
1077 | advance_hpp(hpp, printed); | ||
1078 | printed = scnprintf(hpp->buf, hpp->size, "%-*s", width - printed, " "); | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | return printed; | ||
1083 | } | ||
1084 | |||
1085 | /* | ||
1009 | * collapse the histogram | 1086 | * collapse the histogram |
1010 | */ | 1087 | */ |
1011 | 1088 | ||
1012 | bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | 1089 | static void hists__apply_filters(struct hists *hists, struct hist_entry *he); |
1013 | struct rb_root *root, struct hist_entry *he) | 1090 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *he, |
1091 | enum hist_filter type); | ||
1092 | |||
1093 | typedef bool (*fmt_chk_fn)(struct perf_hpp_fmt *fmt); | ||
1094 | |||
1095 | static bool check_thread_entry(struct perf_hpp_fmt *fmt) | ||
1096 | { | ||
1097 | return perf_hpp__is_thread_entry(fmt) || perf_hpp__is_comm_entry(fmt); | ||
1098 | } | ||
1099 | |||
1100 | static void hist_entry__check_and_remove_filter(struct hist_entry *he, | ||
1101 | enum hist_filter type, | ||
1102 | fmt_chk_fn check) | ||
1103 | { | ||
1104 | struct perf_hpp_fmt *fmt; | ||
1105 | bool type_match = false; | ||
1106 | struct hist_entry *parent = he->parent_he; | ||
1107 | |||
1108 | switch (type) { | ||
1109 | case HIST_FILTER__THREAD: | ||
1110 | if (symbol_conf.comm_list == NULL && | ||
1111 | symbol_conf.pid_list == NULL && | ||
1112 | symbol_conf.tid_list == NULL) | ||
1113 | return; | ||
1114 | break; | ||
1115 | case HIST_FILTER__DSO: | ||
1116 | if (symbol_conf.dso_list == NULL) | ||
1117 | return; | ||
1118 | break; | ||
1119 | case HIST_FILTER__SYMBOL: | ||
1120 | if (symbol_conf.sym_list == NULL) | ||
1121 | return; | ||
1122 | break; | ||
1123 | case HIST_FILTER__PARENT: | ||
1124 | case HIST_FILTER__GUEST: | ||
1125 | case HIST_FILTER__HOST: | ||
1126 | case HIST_FILTER__SOCKET: | ||
1127 | default: | ||
1128 | return; | ||
1129 | } | ||
1130 | |||
1131 | /* if it's filtered by own fmt, it has to have filter bits */ | ||
1132 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
1133 | if (check(fmt)) { | ||
1134 | type_match = true; | ||
1135 | break; | ||
1136 | } | ||
1137 | } | ||
1138 | |||
1139 | if (type_match) { | ||
1140 | /* | ||
1141 | * If the filter is for current level entry, propagate | ||
1142 | * filter marker to parents. The marker bit was | ||
1143 | * already set by default so it only needs to clear | ||
1144 | * non-filtered entries. | ||
1145 | */ | ||
1146 | if (!(he->filtered & (1 << type))) { | ||
1147 | while (parent) { | ||
1148 | parent->filtered &= ~(1 << type); | ||
1149 | parent = parent->parent_he; | ||
1150 | } | ||
1151 | } | ||
1152 | } else { | ||
1153 | /* | ||
1154 | * If current entry doesn't have matching formats, set | ||
1155 | * filter marker for upper level entries. it will be | ||
1156 | * cleared if its lower level entries is not filtered. | ||
1157 | * | ||
1158 | * For lower-level entries, it inherits parent's | ||
1159 | * filter bit so that lower level entries of a | ||
1160 | * non-filtered entry won't set the filter marker. | ||
1161 | */ | ||
1162 | if (parent == NULL) | ||
1163 | he->filtered |= (1 << type); | ||
1164 | else | ||
1165 | he->filtered |= (parent->filtered & (1 << type)); | ||
1166 | } | ||
1167 | } | ||
1168 | |||
1169 | static void hist_entry__apply_hierarchy_filters(struct hist_entry *he) | ||
1170 | { | ||
1171 | hist_entry__check_and_remove_filter(he, HIST_FILTER__THREAD, | ||
1172 | check_thread_entry); | ||
1173 | |||
1174 | hist_entry__check_and_remove_filter(he, HIST_FILTER__DSO, | ||
1175 | perf_hpp__is_dso_entry); | ||
1176 | |||
1177 | hist_entry__check_and_remove_filter(he, HIST_FILTER__SYMBOL, | ||
1178 | perf_hpp__is_sym_entry); | ||
1179 | |||
1180 | hists__apply_filters(he->hists, he); | ||
1181 | } | ||
1182 | |||
1183 | static struct hist_entry *hierarchy_insert_entry(struct hists *hists, | ||
1184 | struct rb_root *root, | ||
1185 | struct hist_entry *he, | ||
1186 | struct hist_entry *parent_he, | ||
1187 | struct perf_hpp_list *hpp_list) | ||
1188 | { | ||
1189 | struct rb_node **p = &root->rb_node; | ||
1190 | struct rb_node *parent = NULL; | ||
1191 | struct hist_entry *iter, *new; | ||
1192 | struct perf_hpp_fmt *fmt; | ||
1193 | int64_t cmp; | ||
1194 | |||
1195 | while (*p != NULL) { | ||
1196 | parent = *p; | ||
1197 | iter = rb_entry(parent, struct hist_entry, rb_node_in); | ||
1198 | |||
1199 | cmp = 0; | ||
1200 | perf_hpp_list__for_each_sort_list(hpp_list, fmt) { | ||
1201 | cmp = fmt->collapse(fmt, iter, he); | ||
1202 | if (cmp) | ||
1203 | break; | ||
1204 | } | ||
1205 | |||
1206 | if (!cmp) { | ||
1207 | he_stat__add_stat(&iter->stat, &he->stat); | ||
1208 | return iter; | ||
1209 | } | ||
1210 | |||
1211 | if (cmp < 0) | ||
1212 | p = &parent->rb_left; | ||
1213 | else | ||
1214 | p = &parent->rb_right; | ||
1215 | } | ||
1216 | |||
1217 | new = hist_entry__new(he, true); | ||
1218 | if (new == NULL) | ||
1219 | return NULL; | ||
1220 | |||
1221 | hists->nr_entries++; | ||
1222 | |||
1223 | /* save related format list for output */ | ||
1224 | new->hpp_list = hpp_list; | ||
1225 | new->parent_he = parent_he; | ||
1226 | |||
1227 | hist_entry__apply_hierarchy_filters(new); | ||
1228 | |||
1229 | /* some fields are now passed to 'new' */ | ||
1230 | perf_hpp_list__for_each_sort_list(hpp_list, fmt) { | ||
1231 | if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) | ||
1232 | he->trace_output = NULL; | ||
1233 | else | ||
1234 | new->trace_output = NULL; | ||
1235 | |||
1236 | if (perf_hpp__is_srcline_entry(fmt)) | ||
1237 | he->srcline = NULL; | ||
1238 | else | ||
1239 | new->srcline = NULL; | ||
1240 | |||
1241 | if (perf_hpp__is_srcfile_entry(fmt)) | ||
1242 | he->srcfile = NULL; | ||
1243 | else | ||
1244 | new->srcfile = NULL; | ||
1245 | } | ||
1246 | |||
1247 | rb_link_node(&new->rb_node_in, parent, p); | ||
1248 | rb_insert_color(&new->rb_node_in, root); | ||
1249 | return new; | ||
1250 | } | ||
1251 | |||
1252 | static int hists__hierarchy_insert_entry(struct hists *hists, | ||
1253 | struct rb_root *root, | ||
1254 | struct hist_entry *he) | ||
1255 | { | ||
1256 | struct perf_hpp_list_node *node; | ||
1257 | struct hist_entry *new_he = NULL; | ||
1258 | struct hist_entry *parent = NULL; | ||
1259 | int depth = 0; | ||
1260 | int ret = 0; | ||
1261 | |||
1262 | list_for_each_entry(node, &hists->hpp_formats, list) { | ||
1263 | /* skip period (overhead) and elided columns */ | ||
1264 | if (node->level == 0 || node->skip) | ||
1265 | continue; | ||
1266 | |||
1267 | /* insert copy of 'he' for each fmt into the hierarchy */ | ||
1268 | new_he = hierarchy_insert_entry(hists, root, he, parent, &node->hpp); | ||
1269 | if (new_he == NULL) { | ||
1270 | ret = -1; | ||
1271 | break; | ||
1272 | } | ||
1273 | |||
1274 | root = &new_he->hroot_in; | ||
1275 | new_he->depth = depth++; | ||
1276 | parent = new_he; | ||
1277 | } | ||
1278 | |||
1279 | if (new_he) { | ||
1280 | new_he->leaf = true; | ||
1281 | |||
1282 | if (symbol_conf.use_callchain) { | ||
1283 | callchain_cursor_reset(&callchain_cursor); | ||
1284 | if (callchain_merge(&callchain_cursor, | ||
1285 | new_he->callchain, | ||
1286 | he->callchain) < 0) | ||
1287 | ret = -1; | ||
1288 | } | ||
1289 | } | ||
1290 | |||
1291 | /* 'he' is no longer used */ | ||
1292 | hist_entry__delete(he); | ||
1293 | |||
1294 | /* return 0 (or -1) since it already applied filters */ | ||
1295 | return ret; | ||
1296 | } | ||
1297 | |||
1298 | int hists__collapse_insert_entry(struct hists *hists, struct rb_root *root, | ||
1299 | struct hist_entry *he) | ||
1014 | { | 1300 | { |
1015 | struct rb_node **p = &root->rb_node; | 1301 | struct rb_node **p = &root->rb_node; |
1016 | struct rb_node *parent = NULL; | 1302 | struct rb_node *parent = NULL; |
1017 | struct hist_entry *iter; | 1303 | struct hist_entry *iter; |
1018 | int64_t cmp; | 1304 | int64_t cmp; |
1019 | 1305 | ||
1306 | if (symbol_conf.report_hierarchy) | ||
1307 | return hists__hierarchy_insert_entry(hists, root, he); | ||
1308 | |||
1020 | while (*p != NULL) { | 1309 | while (*p != NULL) { |
1021 | parent = *p; | 1310 | parent = *p; |
1022 | iter = rb_entry(parent, struct hist_entry, rb_node_in); | 1311 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
@@ -1024,18 +1313,21 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
1024 | cmp = hist_entry__collapse(iter, he); | 1313 | cmp = hist_entry__collapse(iter, he); |
1025 | 1314 | ||
1026 | if (!cmp) { | 1315 | if (!cmp) { |
1316 | int ret = 0; | ||
1317 | |||
1027 | he_stat__add_stat(&iter->stat, &he->stat); | 1318 | he_stat__add_stat(&iter->stat, &he->stat); |
1028 | if (symbol_conf.cumulate_callchain) | 1319 | if (symbol_conf.cumulate_callchain) |
1029 | he_stat__add_stat(iter->stat_acc, he->stat_acc); | 1320 | he_stat__add_stat(iter->stat_acc, he->stat_acc); |
1030 | 1321 | ||
1031 | if (symbol_conf.use_callchain) { | 1322 | if (symbol_conf.use_callchain) { |
1032 | callchain_cursor_reset(&callchain_cursor); | 1323 | callchain_cursor_reset(&callchain_cursor); |
1033 | callchain_merge(&callchain_cursor, | 1324 | if (callchain_merge(&callchain_cursor, |
1034 | iter->callchain, | 1325 | iter->callchain, |
1035 | he->callchain); | 1326 | he->callchain) < 0) |
1327 | ret = -1; | ||
1036 | } | 1328 | } |
1037 | hist_entry__delete(he); | 1329 | hist_entry__delete(he); |
1038 | return false; | 1330 | return ret; |
1039 | } | 1331 | } |
1040 | 1332 | ||
1041 | if (cmp < 0) | 1333 | if (cmp < 0) |
@@ -1047,7 +1339,7 @@ bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
1047 | 1339 | ||
1048 | rb_link_node(&he->rb_node_in, parent, p); | 1340 | rb_link_node(&he->rb_node_in, parent, p); |
1049 | rb_insert_color(&he->rb_node_in, root); | 1341 | rb_insert_color(&he->rb_node_in, root); |
1050 | return true; | 1342 | return 1; |
1051 | } | 1343 | } |
1052 | 1344 | ||
1053 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists) | 1345 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
@@ -1073,14 +1365,15 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
1073 | hists__filter_entry_by_socket(hists, he); | 1365 | hists__filter_entry_by_socket(hists, he); |
1074 | } | 1366 | } |
1075 | 1367 | ||
1076 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | 1368 | int hists__collapse_resort(struct hists *hists, struct ui_progress *prog) |
1077 | { | 1369 | { |
1078 | struct rb_root *root; | 1370 | struct rb_root *root; |
1079 | struct rb_node *next; | 1371 | struct rb_node *next; |
1080 | struct hist_entry *n; | 1372 | struct hist_entry *n; |
1373 | int ret; | ||
1081 | 1374 | ||
1082 | if (!sort__need_collapse) | 1375 | if (!sort__need_collapse) |
1083 | return; | 1376 | return 0; |
1084 | 1377 | ||
1085 | hists->nr_entries = 0; | 1378 | hists->nr_entries = 0; |
1086 | 1379 | ||
@@ -1095,7 +1388,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1095 | next = rb_next(&n->rb_node_in); | 1388 | next = rb_next(&n->rb_node_in); |
1096 | 1389 | ||
1097 | rb_erase(&n->rb_node_in, root); | 1390 | rb_erase(&n->rb_node_in, root); |
1098 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { | 1391 | ret = hists__collapse_insert_entry(hists, &hists->entries_collapsed, n); |
1392 | if (ret < 0) | ||
1393 | return -1; | ||
1394 | |||
1395 | if (ret) { | ||
1099 | /* | 1396 | /* |
1100 | * If it wasn't combined with one of the entries already | 1397 | * If it wasn't combined with one of the entries already |
1101 | * collapsed, we need to apply the filters that may have | 1398 | * collapsed, we need to apply the filters that may have |
@@ -1106,14 +1403,16 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1106 | if (prog) | 1403 | if (prog) |
1107 | ui_progress__update(prog, 1); | 1404 | ui_progress__update(prog, 1); |
1108 | } | 1405 | } |
1406 | return 0; | ||
1109 | } | 1407 | } |
1110 | 1408 | ||
1111 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) | 1409 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) |
1112 | { | 1410 | { |
1411 | struct hists *hists = a->hists; | ||
1113 | struct perf_hpp_fmt *fmt; | 1412 | struct perf_hpp_fmt *fmt; |
1114 | int64_t cmp = 0; | 1413 | int64_t cmp = 0; |
1115 | 1414 | ||
1116 | perf_hpp__for_each_sort_list(fmt) { | 1415 | hists__for_each_sort_list(hists, fmt) { |
1117 | if (perf_hpp__should_skip(fmt, a->hists)) | 1416 | if (perf_hpp__should_skip(fmt, a->hists)) |
1118 | continue; | 1417 | continue; |
1119 | 1418 | ||
@@ -1154,6 +1453,113 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h) | |||
1154 | hists->stats.total_period += h->stat.period; | 1453 | hists->stats.total_period += h->stat.period; |
1155 | } | 1454 | } |
1156 | 1455 | ||
1456 | static void hierarchy_recalc_total_periods(struct hists *hists) | ||
1457 | { | ||
1458 | struct rb_node *node; | ||
1459 | struct hist_entry *he; | ||
1460 | |||
1461 | node = rb_first(&hists->entries); | ||
1462 | |||
1463 | hists->stats.total_period = 0; | ||
1464 | hists->stats.total_non_filtered_period = 0; | ||
1465 | |||
1466 | /* | ||
1467 | * recalculate total period using top-level entries only | ||
1468 | * since lower level entries only see non-filtered entries | ||
1469 | * but upper level entries have sum of both entries. | ||
1470 | */ | ||
1471 | while (node) { | ||
1472 | he = rb_entry(node, struct hist_entry, rb_node); | ||
1473 | node = rb_next(node); | ||
1474 | |||
1475 | hists->stats.total_period += he->stat.period; | ||
1476 | if (!he->filtered) | ||
1477 | hists->stats.total_non_filtered_period += he->stat.period; | ||
1478 | } | ||
1479 | } | ||
1480 | |||
1481 | static void hierarchy_insert_output_entry(struct rb_root *root, | ||
1482 | struct hist_entry *he) | ||
1483 | { | ||
1484 | struct rb_node **p = &root->rb_node; | ||
1485 | struct rb_node *parent = NULL; | ||
1486 | struct hist_entry *iter; | ||
1487 | struct perf_hpp_fmt *fmt; | ||
1488 | |||
1489 | while (*p != NULL) { | ||
1490 | parent = *p; | ||
1491 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
1492 | |||
1493 | if (hist_entry__sort(he, iter) > 0) | ||
1494 | p = &parent->rb_left; | ||
1495 | else | ||
1496 | p = &parent->rb_right; | ||
1497 | } | ||
1498 | |||
1499 | rb_link_node(&he->rb_node, parent, p); | ||
1500 | rb_insert_color(&he->rb_node, root); | ||
1501 | |||
1502 | /* update column width of dynamic entry */ | ||
1503 | perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) { | ||
1504 | if (perf_hpp__is_dynamic_entry(fmt)) | ||
1505 | fmt->sort(fmt, he, NULL); | ||
1506 | } | ||
1507 | } | ||
1508 | |||
1509 | static void hists__hierarchy_output_resort(struct hists *hists, | ||
1510 | struct ui_progress *prog, | ||
1511 | struct rb_root *root_in, | ||
1512 | struct rb_root *root_out, | ||
1513 | u64 min_callchain_hits, | ||
1514 | bool use_callchain) | ||
1515 | { | ||
1516 | struct rb_node *node; | ||
1517 | struct hist_entry *he; | ||
1518 | |||
1519 | *root_out = RB_ROOT; | ||
1520 | node = rb_first(root_in); | ||
1521 | |||
1522 | while (node) { | ||
1523 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
1524 | node = rb_next(node); | ||
1525 | |||
1526 | hierarchy_insert_output_entry(root_out, he); | ||
1527 | |||
1528 | if (prog) | ||
1529 | ui_progress__update(prog, 1); | ||
1530 | |||
1531 | if (!he->leaf) { | ||
1532 | hists__hierarchy_output_resort(hists, prog, | ||
1533 | &he->hroot_in, | ||
1534 | &he->hroot_out, | ||
1535 | min_callchain_hits, | ||
1536 | use_callchain); | ||
1537 | hists->nr_entries++; | ||
1538 | if (!he->filtered) { | ||
1539 | hists->nr_non_filtered_entries++; | ||
1540 | hists__calc_col_len(hists, he); | ||
1541 | } | ||
1542 | |||
1543 | continue; | ||
1544 | } | ||
1545 | |||
1546 | if (!use_callchain) | ||
1547 | continue; | ||
1548 | |||
1549 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
1550 | u64 total = he->stat.period; | ||
1551 | |||
1552 | if (symbol_conf.cumulate_callchain) | ||
1553 | total = he->stat_acc->period; | ||
1554 | |||
1555 | min_callchain_hits = total * (callchain_param.min_percent / 100); | ||
1556 | } | ||
1557 | |||
1558 | callchain_param.sort(&he->sorted_chain, he->callchain, | ||
1559 | min_callchain_hits, &callchain_param); | ||
1560 | } | ||
1561 | } | ||
1562 | |||
1157 | static void __hists__insert_output_entry(struct rb_root *entries, | 1563 | static void __hists__insert_output_entry(struct rb_root *entries, |
1158 | struct hist_entry *he, | 1564 | struct hist_entry *he, |
1159 | u64 min_callchain_hits, | 1565 | u64 min_callchain_hits, |
@@ -1162,10 +1568,20 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1162 | struct rb_node **p = &entries->rb_node; | 1568 | struct rb_node **p = &entries->rb_node; |
1163 | struct rb_node *parent = NULL; | 1569 | struct rb_node *parent = NULL; |
1164 | struct hist_entry *iter; | 1570 | struct hist_entry *iter; |
1571 | struct perf_hpp_fmt *fmt; | ||
1572 | |||
1573 | if (use_callchain) { | ||
1574 | if (callchain_param.mode == CHAIN_GRAPH_REL) { | ||
1575 | u64 total = he->stat.period; | ||
1576 | |||
1577 | if (symbol_conf.cumulate_callchain) | ||
1578 | total = he->stat_acc->period; | ||
1165 | 1579 | ||
1166 | if (use_callchain) | 1580 | min_callchain_hits = total * (callchain_param.min_percent / 100); |
1581 | } | ||
1167 | callchain_param.sort(&he->sorted_chain, he->callchain, | 1582 | callchain_param.sort(&he->sorted_chain, he->callchain, |
1168 | min_callchain_hits, &callchain_param); | 1583 | min_callchain_hits, &callchain_param); |
1584 | } | ||
1169 | 1585 | ||
1170 | while (*p != NULL) { | 1586 | while (*p != NULL) { |
1171 | parent = *p; | 1587 | parent = *p; |
@@ -1179,23 +1595,41 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1179 | 1595 | ||
1180 | rb_link_node(&he->rb_node, parent, p); | 1596 | rb_link_node(&he->rb_node, parent, p); |
1181 | rb_insert_color(&he->rb_node, entries); | 1597 | rb_insert_color(&he->rb_node, entries); |
1598 | |||
1599 | perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) { | ||
1600 | if (perf_hpp__is_dynamic_entry(fmt) && | ||
1601 | perf_hpp__defined_dynamic_entry(fmt, he->hists)) | ||
1602 | fmt->sort(fmt, he, NULL); /* update column width */ | ||
1603 | } | ||
1182 | } | 1604 | } |
1183 | 1605 | ||
1184 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) | 1606 | static void output_resort(struct hists *hists, struct ui_progress *prog, |
1607 | bool use_callchain) | ||
1185 | { | 1608 | { |
1186 | struct rb_root *root; | 1609 | struct rb_root *root; |
1187 | struct rb_node *next; | 1610 | struct rb_node *next; |
1188 | struct hist_entry *n; | 1611 | struct hist_entry *n; |
1612 | u64 callchain_total; | ||
1189 | u64 min_callchain_hits; | 1613 | u64 min_callchain_hits; |
1190 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
1191 | bool use_callchain; | ||
1192 | 1614 | ||
1193 | if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph) | 1615 | callchain_total = hists->callchain_period; |
1194 | use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN; | 1616 | if (symbol_conf.filter_relative) |
1195 | else | 1617 | callchain_total = hists->callchain_non_filtered_period; |
1196 | use_callchain = symbol_conf.use_callchain; | ||
1197 | 1618 | ||
1198 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); | 1619 | min_callchain_hits = callchain_total * (callchain_param.min_percent / 100); |
1620 | |||
1621 | hists__reset_stats(hists); | ||
1622 | hists__reset_col_len(hists); | ||
1623 | |||
1624 | if (symbol_conf.report_hierarchy) { | ||
1625 | hists__hierarchy_output_resort(hists, prog, | ||
1626 | &hists->entries_collapsed, | ||
1627 | &hists->entries, | ||
1628 | min_callchain_hits, | ||
1629 | use_callchain); | ||
1630 | hierarchy_recalc_total_periods(hists); | ||
1631 | return; | ||
1632 | } | ||
1199 | 1633 | ||
1200 | if (sort__need_collapse) | 1634 | if (sort__need_collapse) |
1201 | root = &hists->entries_collapsed; | 1635 | root = &hists->entries_collapsed; |
@@ -1205,9 +1639,6 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog) | |||
1205 | next = rb_first(root); | 1639 | next = rb_first(root); |
1206 | hists->entries = RB_ROOT; | 1640 | hists->entries = RB_ROOT; |
1207 | 1641 | ||
1208 | hists__reset_stats(hists); | ||
1209 | hists__reset_col_len(hists); | ||
1210 | |||
1211 | while (next) { | 1642 | while (next) { |
1212 | n = rb_entry(next, struct hist_entry, rb_node_in); | 1643 | n = rb_entry(next, struct hist_entry, rb_node_in); |
1213 | next = rb_next(&n->rb_node_in); | 1644 | next = rb_next(&n->rb_node_in); |
@@ -1223,15 +1654,136 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog) | |||
1223 | } | 1654 | } |
1224 | } | 1655 | } |
1225 | 1656 | ||
1657 | void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog) | ||
1658 | { | ||
1659 | bool use_callchain; | ||
1660 | |||
1661 | if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph) | ||
1662 | use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN; | ||
1663 | else | ||
1664 | use_callchain = symbol_conf.use_callchain; | ||
1665 | |||
1666 | output_resort(evsel__hists(evsel), prog, use_callchain); | ||
1667 | } | ||
1668 | |||
1669 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) | ||
1670 | { | ||
1671 | output_resort(hists, prog, symbol_conf.use_callchain); | ||
1672 | } | ||
1673 | |||
1674 | static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd) | ||
1675 | { | ||
1676 | if (he->leaf || hmd == HMD_FORCE_SIBLING) | ||
1677 | return false; | ||
1678 | |||
1679 | if (he->unfolded || hmd == HMD_FORCE_CHILD) | ||
1680 | return true; | ||
1681 | |||
1682 | return false; | ||
1683 | } | ||
1684 | |||
1685 | struct rb_node *rb_hierarchy_last(struct rb_node *node) | ||
1686 | { | ||
1687 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1688 | |||
1689 | while (can_goto_child(he, HMD_NORMAL)) { | ||
1690 | node = rb_last(&he->hroot_out); | ||
1691 | he = rb_entry(node, struct hist_entry, rb_node); | ||
1692 | } | ||
1693 | return node; | ||
1694 | } | ||
1695 | |||
1696 | struct rb_node *__rb_hierarchy_next(struct rb_node *node, enum hierarchy_move_dir hmd) | ||
1697 | { | ||
1698 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1699 | |||
1700 | if (can_goto_child(he, hmd)) | ||
1701 | node = rb_first(&he->hroot_out); | ||
1702 | else | ||
1703 | node = rb_next(node); | ||
1704 | |||
1705 | while (node == NULL) { | ||
1706 | he = he->parent_he; | ||
1707 | if (he == NULL) | ||
1708 | break; | ||
1709 | |||
1710 | node = rb_next(&he->rb_node); | ||
1711 | } | ||
1712 | return node; | ||
1713 | } | ||
1714 | |||
1715 | struct rb_node *rb_hierarchy_prev(struct rb_node *node) | ||
1716 | { | ||
1717 | struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node); | ||
1718 | |||
1719 | node = rb_prev(node); | ||
1720 | if (node) | ||
1721 | return rb_hierarchy_last(node); | ||
1722 | |||
1723 | he = he->parent_he; | ||
1724 | if (he == NULL) | ||
1725 | return NULL; | ||
1726 | |||
1727 | return &he->rb_node; | ||
1728 | } | ||
1729 | |||
1730 | bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit) | ||
1731 | { | ||
1732 | struct rb_node *node; | ||
1733 | struct hist_entry *child; | ||
1734 | float percent; | ||
1735 | |||
1736 | if (he->leaf) | ||
1737 | return false; | ||
1738 | |||
1739 | node = rb_first(&he->hroot_out); | ||
1740 | child = rb_entry(node, struct hist_entry, rb_node); | ||
1741 | |||
1742 | while (node && child->filtered) { | ||
1743 | node = rb_next(node); | ||
1744 | child = rb_entry(node, struct hist_entry, rb_node); | ||
1745 | } | ||
1746 | |||
1747 | if (node) | ||
1748 | percent = hist_entry__get_percent_limit(child); | ||
1749 | else | ||
1750 | percent = 0; | ||
1751 | |||
1752 | return node && percent >= limit; | ||
1753 | } | ||
1754 | |||
1226 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, | 1755 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
1227 | enum hist_filter filter) | 1756 | enum hist_filter filter) |
1228 | { | 1757 | { |
1229 | h->filtered &= ~(1 << filter); | 1758 | h->filtered &= ~(1 << filter); |
1759 | |||
1760 | if (symbol_conf.report_hierarchy) { | ||
1761 | struct hist_entry *parent = h->parent_he; | ||
1762 | |||
1763 | while (parent) { | ||
1764 | he_stat__add_stat(&parent->stat, &h->stat); | ||
1765 | |||
1766 | parent->filtered &= ~(1 << filter); | ||
1767 | |||
1768 | if (parent->filtered) | ||
1769 | goto next; | ||
1770 | |||
1771 | /* force fold unfiltered entry for simplicity */ | ||
1772 | parent->unfolded = false; | ||
1773 | parent->has_no_entry = false; | ||
1774 | parent->row_offset = 0; | ||
1775 | parent->nr_rows = 0; | ||
1776 | next: | ||
1777 | parent = parent->parent_he; | ||
1778 | } | ||
1779 | } | ||
1780 | |||
1230 | if (h->filtered) | 1781 | if (h->filtered) |
1231 | return; | 1782 | return; |
1232 | 1783 | ||
1233 | /* force fold unfiltered entry for simplicity */ | 1784 | /* force fold unfiltered entry for simplicity */ |
1234 | h->unfolded = false; | 1785 | h->unfolded = false; |
1786 | h->has_no_entry = false; | ||
1235 | h->row_offset = 0; | 1787 | h->row_offset = 0; |
1236 | h->nr_rows = 0; | 1788 | h->nr_rows = 0; |
1237 | 1789 | ||
@@ -1254,28 +1806,6 @@ static bool hists__filter_entry_by_dso(struct hists *hists, | |||
1254 | return false; | 1806 | return false; |
1255 | } | 1807 | } |
1256 | 1808 | ||
1257 | void hists__filter_by_dso(struct hists *hists) | ||
1258 | { | ||
1259 | struct rb_node *nd; | ||
1260 | |||
1261 | hists->stats.nr_non_filtered_samples = 0; | ||
1262 | |||
1263 | hists__reset_filter_stats(hists); | ||
1264 | hists__reset_col_len(hists); | ||
1265 | |||
1266 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1267 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1268 | |||
1269 | if (symbol_conf.exclude_other && !h->parent) | ||
1270 | continue; | ||
1271 | |||
1272 | if (hists__filter_entry_by_dso(hists, h)) | ||
1273 | continue; | ||
1274 | |||
1275 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); | ||
1276 | } | ||
1277 | } | ||
1278 | |||
1279 | static bool hists__filter_entry_by_thread(struct hists *hists, | 1809 | static bool hists__filter_entry_by_thread(struct hists *hists, |
1280 | struct hist_entry *he) | 1810 | struct hist_entry *he) |
1281 | { | 1811 | { |
@@ -1288,25 +1818,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists, | |||
1288 | return false; | 1818 | return false; |
1289 | } | 1819 | } |
1290 | 1820 | ||
1291 | void hists__filter_by_thread(struct hists *hists) | ||
1292 | { | ||
1293 | struct rb_node *nd; | ||
1294 | |||
1295 | hists->stats.nr_non_filtered_samples = 0; | ||
1296 | |||
1297 | hists__reset_filter_stats(hists); | ||
1298 | hists__reset_col_len(hists); | ||
1299 | |||
1300 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1301 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1302 | |||
1303 | if (hists__filter_entry_by_thread(hists, h)) | ||
1304 | continue; | ||
1305 | |||
1306 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); | ||
1307 | } | ||
1308 | } | ||
1309 | |||
1310 | static bool hists__filter_entry_by_symbol(struct hists *hists, | 1821 | static bool hists__filter_entry_by_symbol(struct hists *hists, |
1311 | struct hist_entry *he) | 1822 | struct hist_entry *he) |
1312 | { | 1823 | { |
@@ -1320,7 +1831,21 @@ static bool hists__filter_entry_by_symbol(struct hists *hists, | |||
1320 | return false; | 1831 | return false; |
1321 | } | 1832 | } |
1322 | 1833 | ||
1323 | void hists__filter_by_symbol(struct hists *hists) | 1834 | static bool hists__filter_entry_by_socket(struct hists *hists, |
1835 | struct hist_entry *he) | ||
1836 | { | ||
1837 | if ((hists->socket_filter > -1) && | ||
1838 | (he->socket != hists->socket_filter)) { | ||
1839 | he->filtered |= (1 << HIST_FILTER__SOCKET); | ||
1840 | return true; | ||
1841 | } | ||
1842 | |||
1843 | return false; | ||
1844 | } | ||
1845 | |||
1846 | typedef bool (*filter_fn_t)(struct hists *hists, struct hist_entry *he); | ||
1847 | |||
1848 | static void hists__filter_by_type(struct hists *hists, int type, filter_fn_t filter) | ||
1324 | { | 1849 | { |
1325 | struct rb_node *nd; | 1850 | struct rb_node *nd; |
1326 | 1851 | ||
@@ -1332,42 +1857,155 @@ void hists__filter_by_symbol(struct hists *hists) | |||
1332 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 1857 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
1333 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1858 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1334 | 1859 | ||
1335 | if (hists__filter_entry_by_symbol(hists, h)) | 1860 | if (filter(hists, h)) |
1336 | continue; | 1861 | continue; |
1337 | 1862 | ||
1338 | hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL); | 1863 | hists__remove_entry_filter(hists, h, type); |
1339 | } | 1864 | } |
1340 | } | 1865 | } |
1341 | 1866 | ||
1342 | static bool hists__filter_entry_by_socket(struct hists *hists, | 1867 | static void resort_filtered_entry(struct rb_root *root, struct hist_entry *he) |
1343 | struct hist_entry *he) | ||
1344 | { | 1868 | { |
1345 | if ((hists->socket_filter > -1) && | 1869 | struct rb_node **p = &root->rb_node; |
1346 | (he->socket != hists->socket_filter)) { | 1870 | struct rb_node *parent = NULL; |
1347 | he->filtered |= (1 << HIST_FILTER__SOCKET); | 1871 | struct hist_entry *iter; |
1348 | return true; | 1872 | struct rb_root new_root = RB_ROOT; |
1873 | struct rb_node *nd; | ||
1874 | |||
1875 | while (*p != NULL) { | ||
1876 | parent = *p; | ||
1877 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
1878 | |||
1879 | if (hist_entry__sort(he, iter) > 0) | ||
1880 | p = &(*p)->rb_left; | ||
1881 | else | ||
1882 | p = &(*p)->rb_right; | ||
1349 | } | 1883 | } |
1350 | 1884 | ||
1351 | return false; | 1885 | rb_link_node(&he->rb_node, parent, p); |
1886 | rb_insert_color(&he->rb_node, root); | ||
1887 | |||
1888 | if (he->leaf || he->filtered) | ||
1889 | return; | ||
1890 | |||
1891 | nd = rb_first(&he->hroot_out); | ||
1892 | while (nd) { | ||
1893 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1894 | |||
1895 | nd = rb_next(nd); | ||
1896 | rb_erase(&h->rb_node, &he->hroot_out); | ||
1897 | |||
1898 | resort_filtered_entry(&new_root, h); | ||
1899 | } | ||
1900 | |||
1901 | he->hroot_out = new_root; | ||
1352 | } | 1902 | } |
1353 | 1903 | ||
1354 | void hists__filter_by_socket(struct hists *hists) | 1904 | static void hists__filter_hierarchy(struct hists *hists, int type, const void *arg) |
1355 | { | 1905 | { |
1356 | struct rb_node *nd; | 1906 | struct rb_node *nd; |
1907 | struct rb_root new_root = RB_ROOT; | ||
1357 | 1908 | ||
1358 | hists->stats.nr_non_filtered_samples = 0; | 1909 | hists->stats.nr_non_filtered_samples = 0; |
1359 | 1910 | ||
1360 | hists__reset_filter_stats(hists); | 1911 | hists__reset_filter_stats(hists); |
1361 | hists__reset_col_len(hists); | 1912 | hists__reset_col_len(hists); |
1362 | 1913 | ||
1363 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 1914 | nd = rb_first(&hists->entries); |
1915 | while (nd) { | ||
1364 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1916 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1917 | int ret; | ||
1365 | 1918 | ||
1366 | if (hists__filter_entry_by_socket(hists, h)) | 1919 | ret = hist_entry__filter(h, type, arg); |
1367 | continue; | ||
1368 | 1920 | ||
1369 | hists__remove_entry_filter(hists, h, HIST_FILTER__SOCKET); | 1921 | /* |
1922 | * case 1. non-matching type | ||
1923 | * zero out the period, set filter marker and move to child | ||
1924 | */ | ||
1925 | if (ret < 0) { | ||
1926 | memset(&h->stat, 0, sizeof(h->stat)); | ||
1927 | h->filtered |= (1 << type); | ||
1928 | |||
1929 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_CHILD); | ||
1930 | } | ||
1931 | /* | ||
1932 | * case 2. matched type (filter out) | ||
1933 | * set filter marker and move to next | ||
1934 | */ | ||
1935 | else if (ret == 1) { | ||
1936 | h->filtered |= (1 << type); | ||
1937 | |||
1938 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING); | ||
1939 | } | ||
1940 | /* | ||
1941 | * case 3. ok (not filtered) | ||
1942 | * add period to hists and parents, erase the filter marker | ||
1943 | * and move to next sibling | ||
1944 | */ | ||
1945 | else { | ||
1946 | hists__remove_entry_filter(hists, h, type); | ||
1947 | |||
1948 | nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING); | ||
1949 | } | ||
1950 | } | ||
1951 | |||
1952 | hierarchy_recalc_total_periods(hists); | ||
1953 | |||
1954 | /* | ||
1955 | * resort output after applying a new filter since filter in a lower | ||
1956 | * hierarchy can change periods in a upper hierarchy. | ||
1957 | */ | ||
1958 | nd = rb_first(&hists->entries); | ||
1959 | while (nd) { | ||
1960 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1961 | |||
1962 | nd = rb_next(nd); | ||
1963 | rb_erase(&h->rb_node, &hists->entries); | ||
1964 | |||
1965 | resort_filtered_entry(&new_root, h); | ||
1370 | } | 1966 | } |
1967 | |||
1968 | hists->entries = new_root; | ||
1969 | } | ||
1970 | |||
1971 | void hists__filter_by_thread(struct hists *hists) | ||
1972 | { | ||
1973 | if (symbol_conf.report_hierarchy) | ||
1974 | hists__filter_hierarchy(hists, HIST_FILTER__THREAD, | ||
1975 | hists->thread_filter); | ||
1976 | else | ||
1977 | hists__filter_by_type(hists, HIST_FILTER__THREAD, | ||
1978 | hists__filter_entry_by_thread); | ||
1979 | } | ||
1980 | |||
1981 | void hists__filter_by_dso(struct hists *hists) | ||
1982 | { | ||
1983 | if (symbol_conf.report_hierarchy) | ||
1984 | hists__filter_hierarchy(hists, HIST_FILTER__DSO, | ||
1985 | hists->dso_filter); | ||
1986 | else | ||
1987 | hists__filter_by_type(hists, HIST_FILTER__DSO, | ||
1988 | hists__filter_entry_by_dso); | ||
1989 | } | ||
1990 | |||
1991 | void hists__filter_by_symbol(struct hists *hists) | ||
1992 | { | ||
1993 | if (symbol_conf.report_hierarchy) | ||
1994 | hists__filter_hierarchy(hists, HIST_FILTER__SYMBOL, | ||
1995 | hists->symbol_filter_str); | ||
1996 | else | ||
1997 | hists__filter_by_type(hists, HIST_FILTER__SYMBOL, | ||
1998 | hists__filter_entry_by_symbol); | ||
1999 | } | ||
2000 | |||
2001 | void hists__filter_by_socket(struct hists *hists) | ||
2002 | { | ||
2003 | if (symbol_conf.report_hierarchy) | ||
2004 | hists__filter_hierarchy(hists, HIST_FILTER__SOCKET, | ||
2005 | &hists->socket_filter); | ||
2006 | else | ||
2007 | hists__filter_by_type(hists, HIST_FILTER__SOCKET, | ||
2008 | hists__filter_entry_by_socket); | ||
1371 | } | 2009 | } |
1372 | 2010 | ||
1373 | void events_stats__inc(struct events_stats *stats, u32 type) | 2011 | void events_stats__inc(struct events_stats *stats, u32 type) |
@@ -1585,7 +2223,7 @@ int perf_hist_config(const char *var, const char *value) | |||
1585 | return 0; | 2223 | return 0; |
1586 | } | 2224 | } |
1587 | 2225 | ||
1588 | int __hists__init(struct hists *hists) | 2226 | int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list) |
1589 | { | 2227 | { |
1590 | memset(hists, 0, sizeof(*hists)); | 2228 | memset(hists, 0, sizeof(*hists)); |
1591 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | 2229 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; |
@@ -1594,6 +2232,8 @@ int __hists__init(struct hists *hists) | |||
1594 | hists->entries = RB_ROOT; | 2232 | hists->entries = RB_ROOT; |
1595 | pthread_mutex_init(&hists->lock, NULL); | 2233 | pthread_mutex_init(&hists->lock, NULL); |
1596 | hists->socket_filter = -1; | 2234 | hists->socket_filter = -1; |
2235 | hists->hpp_list = hpp_list; | ||
2236 | INIT_LIST_HEAD(&hists->hpp_formats); | ||
1597 | return 0; | 2237 | return 0; |
1598 | } | 2238 | } |
1599 | 2239 | ||
@@ -1622,15 +2262,26 @@ static void hists__delete_all_entries(struct hists *hists) | |||
1622 | static void hists_evsel__exit(struct perf_evsel *evsel) | 2262 | static void hists_evsel__exit(struct perf_evsel *evsel) |
1623 | { | 2263 | { |
1624 | struct hists *hists = evsel__hists(evsel); | 2264 | struct hists *hists = evsel__hists(evsel); |
2265 | struct perf_hpp_fmt *fmt, *pos; | ||
2266 | struct perf_hpp_list_node *node, *tmp; | ||
1625 | 2267 | ||
1626 | hists__delete_all_entries(hists); | 2268 | hists__delete_all_entries(hists); |
2269 | |||
2270 | list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) { | ||
2271 | perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) { | ||
2272 | list_del(&fmt->list); | ||
2273 | free(fmt); | ||
2274 | } | ||
2275 | list_del(&node->list); | ||
2276 | free(node); | ||
2277 | } | ||
1627 | } | 2278 | } |
1628 | 2279 | ||
1629 | static int hists_evsel__init(struct perf_evsel *evsel) | 2280 | static int hists_evsel__init(struct perf_evsel *evsel) |
1630 | { | 2281 | { |
1631 | struct hists *hists = evsel__hists(evsel); | 2282 | struct hists *hists = evsel__hists(evsel); |
1632 | 2283 | ||
1633 | __hists__init(hists); | 2284 | __hists__init(hists, &perf_hpp_list); |
1634 | return 0; | 2285 | return 0; |
1635 | } | 2286 | } |
1636 | 2287 | ||
@@ -1649,3 +2300,9 @@ int hists__init(void) | |||
1649 | 2300 | ||
1650 | return err; | 2301 | return err; |
1651 | } | 2302 | } |
2303 | |||
2304 | void perf_hpp_list__init(struct perf_hpp_list *list) | ||
2305 | { | ||
2306 | INIT_LIST_HEAD(&list->fields); | ||
2307 | INIT_LIST_HEAD(&list->sorts); | ||
2308 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d4ec4822a103..bec0cd660fbd 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -66,6 +66,8 @@ struct hists { | |||
66 | struct rb_root entries_collapsed; | 66 | struct rb_root entries_collapsed; |
67 | u64 nr_entries; | 67 | u64 nr_entries; |
68 | u64 nr_non_filtered_entries; | 68 | u64 nr_non_filtered_entries; |
69 | u64 callchain_period; | ||
70 | u64 callchain_non_filtered_period; | ||
69 | struct thread *thread_filter; | 71 | struct thread *thread_filter; |
70 | const struct dso *dso_filter; | 72 | const struct dso *dso_filter; |
71 | const char *uid_filter_str; | 73 | const char *uid_filter_str; |
@@ -75,6 +77,9 @@ struct hists { | |||
75 | u64 event_stream; | 77 | u64 event_stream; |
76 | u16 col_len[HISTC_NR_COLS]; | 78 | u16 col_len[HISTC_NR_COLS]; |
77 | int socket_filter; | 79 | int socket_filter; |
80 | struct perf_hpp_list *hpp_list; | ||
81 | struct list_head hpp_formats; | ||
82 | int nr_hpp_node; | ||
78 | }; | 83 | }; |
79 | 84 | ||
80 | struct hist_entry_iter; | 85 | struct hist_entry_iter; |
@@ -121,15 +126,21 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
121 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, | 126 | int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, |
122 | int max_stack_depth, void *arg); | 127 | int max_stack_depth, void *arg); |
123 | 128 | ||
129 | struct perf_hpp; | ||
130 | struct perf_hpp_fmt; | ||
131 | |||
124 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 132 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
125 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 133 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
126 | int hist_entry__transaction_len(void); | 134 | int hist_entry__transaction_len(void); |
127 | int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, | 135 | int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, |
128 | struct hists *hists); | 136 | struct hists *hists); |
137 | int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, | ||
138 | struct perf_hpp_fmt *fmt, int printed); | ||
129 | void hist_entry__delete(struct hist_entry *he); | 139 | void hist_entry__delete(struct hist_entry *he); |
130 | 140 | ||
141 | void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog); | ||
131 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); | 142 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); |
132 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 143 | int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
133 | 144 | ||
134 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 145 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
135 | void hists__delete_entries(struct hists *hists); | 146 | void hists__delete_entries(struct hists *hists); |
@@ -185,10 +196,10 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) | |||
185 | } | 196 | } |
186 | 197 | ||
187 | int hists__init(void); | 198 | int hists__init(void); |
188 | int __hists__init(struct hists *hists); | 199 | int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list); |
189 | 200 | ||
190 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists); | 201 | struct rb_root *hists__get_rotate_entries_in(struct hists *hists); |
191 | bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | 202 | int hists__collapse_insert_entry(struct hists *hists, |
192 | struct rb_root *root, struct hist_entry *he); | 203 | struct rb_root *root, struct hist_entry *he); |
193 | 204 | ||
194 | struct perf_hpp { | 205 | struct perf_hpp { |
@@ -214,28 +225,64 @@ struct perf_hpp_fmt { | |||
214 | struct hist_entry *a, struct hist_entry *b); | 225 | struct hist_entry *a, struct hist_entry *b); |
215 | int64_t (*sort)(struct perf_hpp_fmt *fmt, | 226 | int64_t (*sort)(struct perf_hpp_fmt *fmt, |
216 | struct hist_entry *a, struct hist_entry *b); | 227 | struct hist_entry *a, struct hist_entry *b); |
228 | bool (*equal)(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); | ||
229 | void (*free)(struct perf_hpp_fmt *fmt); | ||
217 | 230 | ||
218 | struct list_head list; | 231 | struct list_head list; |
219 | struct list_head sort_list; | 232 | struct list_head sort_list; |
220 | bool elide; | 233 | bool elide; |
221 | int len; | 234 | int len; |
222 | int user_len; | 235 | int user_len; |
236 | int idx; | ||
237 | int level; | ||
238 | }; | ||
239 | |||
240 | struct perf_hpp_list { | ||
241 | struct list_head fields; | ||
242 | struct list_head sorts; | ||
223 | }; | 243 | }; |
224 | 244 | ||
225 | extern struct list_head perf_hpp__list; | 245 | extern struct perf_hpp_list perf_hpp_list; |
226 | extern struct list_head perf_hpp__sort_list; | 246 | |
247 | struct perf_hpp_list_node { | ||
248 | struct list_head list; | ||
249 | struct perf_hpp_list hpp; | ||
250 | int level; | ||
251 | bool skip; | ||
252 | }; | ||
253 | |||
254 | void perf_hpp_list__column_register(struct perf_hpp_list *list, | ||
255 | struct perf_hpp_fmt *format); | ||
256 | void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, | ||
257 | struct perf_hpp_fmt *format); | ||
258 | |||
259 | static inline void perf_hpp__column_register(struct perf_hpp_fmt *format) | ||
260 | { | ||
261 | perf_hpp_list__column_register(&perf_hpp_list, format); | ||
262 | } | ||
263 | |||
264 | static inline void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) | ||
265 | { | ||
266 | perf_hpp_list__register_sort_field(&perf_hpp_list, format); | ||
267 | } | ||
268 | |||
269 | #define perf_hpp_list__for_each_format(_list, format) \ | ||
270 | list_for_each_entry(format, &(_list)->fields, list) | ||
227 | 271 | ||
228 | #define perf_hpp__for_each_format(format) \ | 272 | #define perf_hpp_list__for_each_format_safe(_list, format, tmp) \ |
229 | list_for_each_entry(format, &perf_hpp__list, list) | 273 | list_for_each_entry_safe(format, tmp, &(_list)->fields, list) |
230 | 274 | ||
231 | #define perf_hpp__for_each_format_safe(format, tmp) \ | 275 | #define perf_hpp_list__for_each_sort_list(_list, format) \ |
232 | list_for_each_entry_safe(format, tmp, &perf_hpp__list, list) | 276 | list_for_each_entry(format, &(_list)->sorts, sort_list) |
233 | 277 | ||
234 | #define perf_hpp__for_each_sort_list(format) \ | 278 | #define perf_hpp_list__for_each_sort_list_safe(_list, format, tmp) \ |
235 | list_for_each_entry(format, &perf_hpp__sort_list, sort_list) | 279 | list_for_each_entry_safe(format, tmp, &(_list)->sorts, sort_list) |
236 | 280 | ||
237 | #define perf_hpp__for_each_sort_list_safe(format, tmp) \ | 281 | #define hists__for_each_format(hists, format) \ |
238 | list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list) | 282 | perf_hpp_list__for_each_format((hists)->hpp_list, fmt) |
283 | |||
284 | #define hists__for_each_sort_list(hists, format) \ | ||
285 | perf_hpp_list__for_each_sort_list((hists)->hpp_list, fmt) | ||
239 | 286 | ||
240 | extern struct perf_hpp_fmt perf_hpp__format[]; | 287 | extern struct perf_hpp_fmt perf_hpp__format[]; |
241 | 288 | ||
@@ -254,21 +301,29 @@ enum { | |||
254 | }; | 301 | }; |
255 | 302 | ||
256 | void perf_hpp__init(void); | 303 | void perf_hpp__init(void); |
257 | void perf_hpp__column_register(struct perf_hpp_fmt *format); | ||
258 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format); | 304 | void perf_hpp__column_unregister(struct perf_hpp_fmt *format); |
259 | void perf_hpp__column_enable(unsigned col); | ||
260 | void perf_hpp__column_disable(unsigned col); | ||
261 | void perf_hpp__cancel_cumulate(void); | 305 | void perf_hpp__cancel_cumulate(void); |
306 | void perf_hpp__setup_output_field(struct perf_hpp_list *list); | ||
307 | void perf_hpp__reset_output_field(struct perf_hpp_list *list); | ||
308 | void perf_hpp__append_sort_keys(struct perf_hpp_list *list); | ||
309 | int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, | ||
310 | struct perf_evlist *evlist); | ||
262 | 311 | ||
263 | void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); | ||
264 | void perf_hpp__setup_output_field(void); | ||
265 | void perf_hpp__reset_output_field(void); | ||
266 | void perf_hpp__append_sort_keys(void); | ||
267 | 312 | ||
268 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); | 313 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); |
269 | bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); | ||
270 | bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format); | 314 | bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format); |
271 | bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists); | 315 | bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists); |
316 | bool perf_hpp__is_trace_entry(struct perf_hpp_fmt *fmt); | ||
317 | bool perf_hpp__is_srcline_entry(struct perf_hpp_fmt *fmt); | ||
318 | bool perf_hpp__is_srcfile_entry(struct perf_hpp_fmt *fmt); | ||
319 | bool perf_hpp__is_thread_entry(struct perf_hpp_fmt *fmt); | ||
320 | bool perf_hpp__is_comm_entry(struct perf_hpp_fmt *fmt); | ||
321 | bool perf_hpp__is_dso_entry(struct perf_hpp_fmt *fmt); | ||
322 | bool perf_hpp__is_sym_entry(struct perf_hpp_fmt *fmt); | ||
323 | |||
324 | struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt); | ||
325 | |||
326 | int hist_entry__filter(struct hist_entry *he, int type, const void *arg); | ||
272 | 327 | ||
273 | static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format, | 328 | static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format, |
274 | struct hists *hists) | 329 | struct hists *hists) |
@@ -372,13 +427,35 @@ static inline int script_browse(const char *script_opt __maybe_unused) | |||
372 | #endif | 427 | #endif |
373 | 428 | ||
374 | unsigned int hists__sort_list_width(struct hists *hists); | 429 | unsigned int hists__sort_list_width(struct hists *hists); |
430 | unsigned int hists__overhead_width(struct hists *hists); | ||
375 | 431 | ||
376 | void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, | 432 | void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, |
377 | struct perf_sample *sample, bool nonany_branch_mode); | 433 | struct perf_sample *sample, bool nonany_branch_mode); |
378 | 434 | ||
379 | struct option; | 435 | struct option; |
380 | int parse_filter_percentage(const struct option *opt __maybe_unused, | 436 | int parse_filter_percentage(const struct option *opt, const char *arg, int unset); |
381 | const char *arg, int unset __maybe_unused); | ||
382 | int perf_hist_config(const char *var, const char *value); | 437 | int perf_hist_config(const char *var, const char *value); |
383 | 438 | ||
439 | void perf_hpp_list__init(struct perf_hpp_list *list); | ||
440 | |||
441 | enum hierarchy_move_dir { | ||
442 | HMD_NORMAL, | ||
443 | HMD_FORCE_SIBLING, | ||
444 | HMD_FORCE_CHILD, | ||
445 | }; | ||
446 | |||
447 | struct rb_node *rb_hierarchy_last(struct rb_node *node); | ||
448 | struct rb_node *__rb_hierarchy_next(struct rb_node *node, | ||
449 | enum hierarchy_move_dir hmd); | ||
450 | struct rb_node *rb_hierarchy_prev(struct rb_node *node); | ||
451 | |||
452 | static inline struct rb_node *rb_hierarchy_next(struct rb_node *node) | ||
453 | { | ||
454 | return __rb_hierarchy_next(node, HMD_NORMAL); | ||
455 | } | ||
456 | |||
457 | #define HIERARCHY_INDENT 3 | ||
458 | |||
459 | bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit); | ||
460 | |||
384 | #endif /* __PERF_HIST_H */ | 461 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index eb0e7f8bf515..abf1366e2a24 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c | |||
@@ -279,6 +279,7 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | |||
279 | event.sample.header.misc = PERF_RECORD_MISC_USER; | 279 | event.sample.header.misc = PERF_RECORD_MISC_USER; |
280 | event.sample.header.size = sizeof(struct perf_event_header); | 280 | event.sample.header.size = sizeof(struct perf_event_header); |
281 | 281 | ||
282 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
282 | sample.ip = le64_to_cpu(branch->from); | 283 | sample.ip = le64_to_cpu(branch->from); |
283 | sample.pid = btsq->pid; | 284 | sample.pid = btsq->pid; |
284 | sample.tid = btsq->tid; | 285 | sample.tid = btsq->tid; |
@@ -678,7 +679,7 @@ static int intel_bts_process_auxtrace_event(struct perf_session *session, | |||
678 | return 0; | 679 | return 0; |
679 | } | 680 | } |
680 | 681 | ||
681 | static int intel_bts_flush(struct perf_session *session __maybe_unused, | 682 | static int intel_bts_flush(struct perf_session *session, |
682 | struct perf_tool *tool __maybe_unused) | 683 | struct perf_tool *tool __maybe_unused) |
683 | { | 684 | { |
684 | struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts, | 685 | struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts, |
diff --git a/tools/perf/util/intel-pt-decoder/insn.c b/tools/perf/util/intel-pt-decoder/insn.c index 47314a64399c..9f26eae6c9f0 100644 --- a/tools/perf/util/intel-pt-decoder/insn.c +++ b/tools/perf/util/intel-pt-decoder/insn.c | |||
@@ -374,7 +374,7 @@ void insn_get_displacement(struct insn *insn) | |||
374 | if (mod == 3) | 374 | if (mod == 3) |
375 | goto out; | 375 | goto out; |
376 | if (mod == 1) { | 376 | if (mod == 1) { |
377 | insn->displacement.value = get_next(char, insn); | 377 | insn->displacement.value = get_next(signed char, insn); |
378 | insn->displacement.nbytes = 1; | 378 | insn->displacement.nbytes = 1; |
379 | } else if (insn->addr_bytes == 2) { | 379 | } else if (insn->addr_bytes == 2) { |
380 | if ((mod == 0 && rm == 6) || mod == 2) { | 380 | if ((mod == 0 && rm == 6) || mod == 2) { |
@@ -532,7 +532,7 @@ void insn_get_immediate(struct insn *insn) | |||
532 | 532 | ||
533 | switch (inat_immediate_size(insn->attr)) { | 533 | switch (inat_immediate_size(insn->attr)) { |
534 | case INAT_IMM_BYTE: | 534 | case INAT_IMM_BYTE: |
535 | insn->immediate.value = get_next(char, insn); | 535 | insn->immediate.value = get_next(signed char, insn); |
536 | insn->immediate.nbytes = 1; | 536 | insn->immediate.nbytes = 1; |
537 | break; | 537 | break; |
538 | case INAT_IMM_WORD: | 538 | case INAT_IMM_WORD: |
@@ -566,7 +566,7 @@ void insn_get_immediate(struct insn *insn) | |||
566 | goto err_out; | 566 | goto err_out; |
567 | } | 567 | } |
568 | if (inat_has_second_immediate(insn->attr)) { | 568 | if (inat_has_second_immediate(insn->attr)) { |
569 | insn->immediate2.value = get_next(char, insn); | 569 | insn->immediate2.value = get_next(signed char, insn); |
570 | insn->immediate2.nbytes = 1; | 570 | insn->immediate2.nbytes = 1; |
571 | } | 571 | } |
572 | done: | 572 | done: |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 05d815851be1..407f11b97c8d 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -979,6 +979,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) | |||
979 | if (!pt->timeless_decoding) | 979 | if (!pt->timeless_decoding) |
980 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); | 980 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); |
981 | 981 | ||
982 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
982 | sample.ip = ptq->state->from_ip; | 983 | sample.ip = ptq->state->from_ip; |
983 | sample.pid = ptq->pid; | 984 | sample.pid = ptq->pid; |
984 | sample.tid = ptq->tid; | 985 | sample.tid = ptq->tid; |
@@ -1035,6 +1036,7 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) | |||
1035 | if (!pt->timeless_decoding) | 1036 | if (!pt->timeless_decoding) |
1036 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); | 1037 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); |
1037 | 1038 | ||
1039 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
1038 | sample.ip = ptq->state->from_ip; | 1040 | sample.ip = ptq->state->from_ip; |
1039 | sample.pid = ptq->pid; | 1041 | sample.pid = ptq->pid; |
1040 | sample.tid = ptq->tid; | 1042 | sample.tid = ptq->tid; |
@@ -1092,6 +1094,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) | |||
1092 | if (!pt->timeless_decoding) | 1094 | if (!pt->timeless_decoding) |
1093 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); | 1095 | sample.time = tsc_to_perf_time(ptq->timestamp, &pt->tc); |
1094 | 1096 | ||
1097 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
1095 | sample.ip = ptq->state->from_ip; | 1098 | sample.ip = ptq->state->from_ip; |
1096 | sample.pid = ptq->pid; | 1099 | sample.pid = ptq->pid; |
1097 | sample.tid = ptq->tid; | 1100 | sample.tid = ptq->tid; |
diff --git a/tools/perf/util/jit.h b/tools/perf/util/jit.h new file mode 100644 index 000000000000..3f42ee4d2a0b --- /dev/null +++ b/tools/perf/util/jit.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #ifndef __JIT_H__ | ||
2 | #define __JIT_H__ | ||
3 | |||
4 | #include <data.h> | ||
5 | |||
6 | int jit_process(struct perf_session *session, struct perf_data_file *output, | ||
7 | struct machine *machine, char *filename, pid_t pid, u64 *nbytes); | ||
8 | |||
9 | int jit_inject_record(const char *filename); | ||
10 | |||
11 | #endif /* __JIT_H__ */ | ||
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c new file mode 100644 index 000000000000..ad0c0bb1fbc7 --- /dev/null +++ b/tools/perf/util/jitdump.c | |||
@@ -0,0 +1,699 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdio.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <unistd.h> | ||
7 | #include <inttypes.h> | ||
8 | #include <byteswap.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <sys/mman.h> | ||
11 | |||
12 | #include "util.h" | ||
13 | #include "event.h" | ||
14 | #include "debug.h" | ||
15 | #include "evlist.h" | ||
16 | #include "symbol.h" | ||
17 | #include "strlist.h" | ||
18 | #include <elf.h> | ||
19 | |||
20 | #include "session.h" | ||
21 | #include "jit.h" | ||
22 | #include "jitdump.h" | ||
23 | #include "genelf.h" | ||
24 | #include "../builtin.h" | ||
25 | |||
26 | struct jit_buf_desc { | ||
27 | struct perf_data_file *output; | ||
28 | struct perf_session *session; | ||
29 | struct machine *machine; | ||
30 | union jr_entry *entry; | ||
31 | void *buf; | ||
32 | uint64_t sample_type; | ||
33 | size_t bufsize; | ||
34 | FILE *in; | ||
35 | bool needs_bswap; /* handles cross-endianess */ | ||
36 | void *debug_data; | ||
37 | size_t nr_debug_entries; | ||
38 | uint32_t code_load_count; | ||
39 | u64 bytes_written; | ||
40 | struct rb_root code_root; | ||
41 | char dir[PATH_MAX]; | ||
42 | }; | ||
43 | |||
44 | struct debug_line_info { | ||
45 | unsigned long vma; | ||
46 | unsigned int lineno; | ||
47 | /* The filename format is unspecified, absolute path, relative etc. */ | ||
48 | char const filename[0]; | ||
49 | }; | ||
50 | |||
51 | struct jit_tool { | ||
52 | struct perf_tool tool; | ||
53 | struct perf_data_file output; | ||
54 | struct perf_data_file input; | ||
55 | u64 bytes_written; | ||
56 | }; | ||
57 | |||
58 | #define hmax(a, b) ((a) > (b) ? (a) : (b)) | ||
59 | #define get_jit_tool(t) (container_of(tool, struct jit_tool, tool)) | ||
60 | |||
61 | static int | ||
62 | jit_emit_elf(char *filename, | ||
63 | const char *sym, | ||
64 | uint64_t code_addr, | ||
65 | const void *code, | ||
66 | int csize, | ||
67 | void *debug, | ||
68 | int nr_debug_entries) | ||
69 | { | ||
70 | int ret, fd; | ||
71 | |||
72 | if (verbose > 0) | ||
73 | fprintf(stderr, "write ELF image %s\n", filename); | ||
74 | |||
75 | fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644); | ||
76 | if (fd == -1) { | ||
77 | pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(errno)); | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); | ||
82 | |||
83 | close(fd); | ||
84 | |||
85 | if (ret) | ||
86 | unlink(filename); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static void | ||
92 | jit_close(struct jit_buf_desc *jd) | ||
93 | { | ||
94 | if (!(jd && jd->in)) | ||
95 | return; | ||
96 | funlockfile(jd->in); | ||
97 | fclose(jd->in); | ||
98 | jd->in = NULL; | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | jit_validate_events(struct perf_session *session) | ||
103 | { | ||
104 | struct perf_evsel *evsel; | ||
105 | |||
106 | /* | ||
107 | * check that all events use CLOCK_MONOTONIC | ||
108 | */ | ||
109 | evlist__for_each(session->evlist, evsel) { | ||
110 | if (evsel->attr.use_clockid == 0 || evsel->attr.clockid != CLOCK_MONOTONIC) | ||
111 | return -1; | ||
112 | } | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int | ||
117 | jit_open(struct jit_buf_desc *jd, const char *name) | ||
118 | { | ||
119 | struct jitheader header; | ||
120 | struct jr_prefix *prefix; | ||
121 | ssize_t bs, bsz = 0; | ||
122 | void *n, *buf = NULL; | ||
123 | int ret, retval = -1; | ||
124 | |||
125 | jd->in = fopen(name, "r"); | ||
126 | if (!jd->in) | ||
127 | return -1; | ||
128 | |||
129 | bsz = hmax(sizeof(header), sizeof(*prefix)); | ||
130 | |||
131 | buf = malloc(bsz); | ||
132 | if (!buf) | ||
133 | goto error; | ||
134 | |||
135 | /* | ||
136 | * protect from writer modifying the file while we are reading it | ||
137 | */ | ||
138 | flockfile(jd->in); | ||
139 | |||
140 | ret = fread(buf, sizeof(header), 1, jd->in); | ||
141 | if (ret != 1) | ||
142 | goto error; | ||
143 | |||
144 | memcpy(&header, buf, sizeof(header)); | ||
145 | |||
146 | if (header.magic != JITHEADER_MAGIC) { | ||
147 | if (header.magic != JITHEADER_MAGIC_SW) | ||
148 | goto error; | ||
149 | jd->needs_bswap = true; | ||
150 | } | ||
151 | |||
152 | if (jd->needs_bswap) { | ||
153 | header.version = bswap_32(header.version); | ||
154 | header.total_size = bswap_32(header.total_size); | ||
155 | header.pid = bswap_32(header.pid); | ||
156 | header.elf_mach = bswap_32(header.elf_mach); | ||
157 | header.timestamp = bswap_64(header.timestamp); | ||
158 | header.flags = bswap_64(header.flags); | ||
159 | } | ||
160 | |||
161 | if (verbose > 2) | ||
162 | pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", | ||
163 | header.version, | ||
164 | header.total_size, | ||
165 | (unsigned long long)header.timestamp, | ||
166 | header.pid, | ||
167 | header.elf_mach); | ||
168 | |||
169 | if (header.flags & JITDUMP_FLAGS_RESERVED) { | ||
170 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", | ||
171 | (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); | ||
172 | goto error; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * validate event is using the correct clockid | ||
177 | */ | ||
178 | if (jit_validate_events(jd->session)) { | ||
179 | pr_err("error, jitted code must be sampled with perf record -k 1\n"); | ||
180 | goto error; | ||
181 | } | ||
182 | |||
183 | bs = header.total_size - sizeof(header); | ||
184 | |||
185 | if (bs > bsz) { | ||
186 | n = realloc(buf, bs); | ||
187 | if (!n) | ||
188 | goto error; | ||
189 | bsz = bs; | ||
190 | buf = n; | ||
191 | /* read extra we do not know about */ | ||
192 | ret = fread(buf, bs - bsz, 1, jd->in); | ||
193 | if (ret != 1) | ||
194 | goto error; | ||
195 | } | ||
196 | /* | ||
197 | * keep dirname for generating files and mmap records | ||
198 | */ | ||
199 | strcpy(jd->dir, name); | ||
200 | dirname(jd->dir); | ||
201 | |||
202 | return 0; | ||
203 | error: | ||
204 | funlockfile(jd->in); | ||
205 | fclose(jd->in); | ||
206 | return retval; | ||
207 | } | ||
208 | |||
209 | static union jr_entry * | ||
210 | jit_get_next_entry(struct jit_buf_desc *jd) | ||
211 | { | ||
212 | struct jr_prefix *prefix; | ||
213 | union jr_entry *jr; | ||
214 | void *addr; | ||
215 | size_t bs, size; | ||
216 | int id, ret; | ||
217 | |||
218 | if (!(jd && jd->in)) | ||
219 | return NULL; | ||
220 | |||
221 | if (jd->buf == NULL) { | ||
222 | size_t sz = getpagesize(); | ||
223 | if (sz < sizeof(*prefix)) | ||
224 | sz = sizeof(*prefix); | ||
225 | |||
226 | jd->buf = malloc(sz); | ||
227 | if (jd->buf == NULL) | ||
228 | return NULL; | ||
229 | |||
230 | jd->bufsize = sz; | ||
231 | } | ||
232 | |||
233 | prefix = jd->buf; | ||
234 | |||
235 | /* | ||
236 | * file is still locked at this point | ||
237 | */ | ||
238 | ret = fread(prefix, sizeof(*prefix), 1, jd->in); | ||
239 | if (ret != 1) | ||
240 | return NULL; | ||
241 | |||
242 | if (jd->needs_bswap) { | ||
243 | prefix->id = bswap_32(prefix->id); | ||
244 | prefix->total_size = bswap_32(prefix->total_size); | ||
245 | prefix->timestamp = bswap_64(prefix->timestamp); | ||
246 | } | ||
247 | id = prefix->id; | ||
248 | size = prefix->total_size; | ||
249 | |||
250 | bs = (size_t)size; | ||
251 | if (bs < sizeof(*prefix)) | ||
252 | return NULL; | ||
253 | |||
254 | if (id >= JIT_CODE_MAX) { | ||
255 | pr_warning("next_entry: unknown prefix %d, skipping\n", id); | ||
256 | return NULL; | ||
257 | } | ||
258 | if (bs > jd->bufsize) { | ||
259 | void *n; | ||
260 | n = realloc(jd->buf, bs); | ||
261 | if (!n) | ||
262 | return NULL; | ||
263 | jd->buf = n; | ||
264 | jd->bufsize = bs; | ||
265 | } | ||
266 | |||
267 | addr = ((void *)jd->buf) + sizeof(*prefix); | ||
268 | |||
269 | ret = fread(addr, bs - sizeof(*prefix), 1, jd->in); | ||
270 | if (ret != 1) | ||
271 | return NULL; | ||
272 | |||
273 | jr = (union jr_entry *)jd->buf; | ||
274 | |||
275 | switch(id) { | ||
276 | case JIT_CODE_DEBUG_INFO: | ||
277 | if (jd->needs_bswap) { | ||
278 | uint64_t n; | ||
279 | jr->info.code_addr = bswap_64(jr->info.code_addr); | ||
280 | jr->info.nr_entry = bswap_64(jr->info.nr_entry); | ||
281 | for (n = 0 ; n < jr->info.nr_entry; n++) { | ||
282 | jr->info.entries[n].addr = bswap_64(jr->info.entries[n].addr); | ||
283 | jr->info.entries[n].lineno = bswap_32(jr->info.entries[n].lineno); | ||
284 | jr->info.entries[n].discrim = bswap_32(jr->info.entries[n].discrim); | ||
285 | } | ||
286 | } | ||
287 | break; | ||
288 | case JIT_CODE_CLOSE: | ||
289 | break; | ||
290 | case JIT_CODE_LOAD: | ||
291 | if (jd->needs_bswap) { | ||
292 | jr->load.pid = bswap_32(jr->load.pid); | ||
293 | jr->load.tid = bswap_32(jr->load.tid); | ||
294 | jr->load.vma = bswap_64(jr->load.vma); | ||
295 | jr->load.code_addr = bswap_64(jr->load.code_addr); | ||
296 | jr->load.code_size = bswap_64(jr->load.code_size); | ||
297 | jr->load.code_index= bswap_64(jr->load.code_index); | ||
298 | } | ||
299 | jd->code_load_count++; | ||
300 | break; | ||
301 | case JIT_CODE_MOVE: | ||
302 | if (jd->needs_bswap) { | ||
303 | jr->move.pid = bswap_32(jr->move.pid); | ||
304 | jr->move.tid = bswap_32(jr->move.tid); | ||
305 | jr->move.vma = bswap_64(jr->move.vma); | ||
306 | jr->move.old_code_addr = bswap_64(jr->move.old_code_addr); | ||
307 | jr->move.new_code_addr = bswap_64(jr->move.new_code_addr); | ||
308 | jr->move.code_size = bswap_64(jr->move.code_size); | ||
309 | jr->move.code_index = bswap_64(jr->move.code_index); | ||
310 | } | ||
311 | break; | ||
312 | case JIT_CODE_MAX: | ||
313 | default: | ||
314 | return NULL; | ||
315 | } | ||
316 | return jr; | ||
317 | } | ||
318 | |||
319 | static int | ||
320 | jit_inject_event(struct jit_buf_desc *jd, union perf_event *event) | ||
321 | { | ||
322 | ssize_t size; | ||
323 | |||
324 | size = perf_data_file__write(jd->output, event, event->header.size); | ||
325 | if (size < 0) | ||
326 | return -1; | ||
327 | |||
328 | jd->bytes_written += size; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | ||
333 | { | ||
334 | struct perf_sample sample; | ||
335 | union perf_event *event; | ||
336 | struct perf_tool *tool = jd->session->tool; | ||
337 | uint64_t code, addr; | ||
338 | uintptr_t uaddr; | ||
339 | char *filename; | ||
340 | struct stat st; | ||
341 | size_t size; | ||
342 | u16 idr_size; | ||
343 | const char *sym; | ||
344 | uint32_t count; | ||
345 | int ret, csize; | ||
346 | pid_t pid, tid; | ||
347 | struct { | ||
348 | u32 pid, tid; | ||
349 | u64 time; | ||
350 | } *id; | ||
351 | |||
352 | pid = jr->load.pid; | ||
353 | tid = jr->load.tid; | ||
354 | csize = jr->load.code_size; | ||
355 | addr = jr->load.code_addr; | ||
356 | sym = (void *)((unsigned long)jr + sizeof(jr->load)); | ||
357 | code = (unsigned long)jr + jr->load.p.total_size - csize; | ||
358 | count = jr->load.code_index; | ||
359 | idr_size = jd->machine->id_hdr_size; | ||
360 | |||
361 | event = calloc(1, sizeof(*event) + idr_size); | ||
362 | if (!event) | ||
363 | return -1; | ||
364 | |||
365 | filename = event->mmap2.filename; | ||
366 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%u.so", | ||
367 | jd->dir, | ||
368 | pid, | ||
369 | count); | ||
370 | |||
371 | size++; /* for \0 */ | ||
372 | |||
373 | size = PERF_ALIGN(size, sizeof(u64)); | ||
374 | uaddr = (uintptr_t)code; | ||
375 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); | ||
376 | |||
377 | if (jd->debug_data && jd->nr_debug_entries) { | ||
378 | free(jd->debug_data); | ||
379 | jd->debug_data = NULL; | ||
380 | jd->nr_debug_entries = 0; | ||
381 | } | ||
382 | |||
383 | if (ret) { | ||
384 | free(event); | ||
385 | return -1; | ||
386 | } | ||
387 | if (stat(filename, &st)) | ||
388 | memset(&st, 0, sizeof(stat)); | ||
389 | |||
390 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
391 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
392 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
393 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
394 | |||
395 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
396 | event->mmap2.start = addr; | ||
397 | event->mmap2.len = csize; | ||
398 | event->mmap2.pid = pid; | ||
399 | event->mmap2.tid = tid; | ||
400 | event->mmap2.ino = st.st_ino; | ||
401 | event->mmap2.maj = major(st.st_dev); | ||
402 | event->mmap2.min = minor(st.st_dev); | ||
403 | event->mmap2.prot = st.st_mode; | ||
404 | event->mmap2.flags = MAP_SHARED; | ||
405 | event->mmap2.ino_generation = 1; | ||
406 | |||
407 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
408 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
409 | id->pid = pid; | ||
410 | id->tid = tid; | ||
411 | } | ||
412 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
413 | id->time = jr->load.p.timestamp; | ||
414 | |||
415 | /* | ||
416 | * create pseudo sample to induce dso hit increment | ||
417 | * use first address as sample address | ||
418 | */ | ||
419 | memset(&sample, 0, sizeof(sample)); | ||
420 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
421 | sample.pid = pid; | ||
422 | sample.tid = tid; | ||
423 | sample.time = id->time; | ||
424 | sample.ip = addr; | ||
425 | |||
426 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
427 | if (ret) | ||
428 | return ret; | ||
429 | |||
430 | ret = jit_inject_event(jd, event); | ||
431 | /* | ||
432 | * mark dso as use to generate buildid in the header | ||
433 | */ | ||
434 | if (!ret) | ||
435 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
436 | |||
437 | return ret; | ||
438 | } | ||
439 | |||
440 | static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | ||
441 | { | ||
442 | struct perf_sample sample; | ||
443 | union perf_event *event; | ||
444 | struct perf_tool *tool = jd->session->tool; | ||
445 | char *filename; | ||
446 | size_t size; | ||
447 | struct stat st; | ||
448 | u16 idr_size; | ||
449 | int ret; | ||
450 | pid_t pid, tid; | ||
451 | struct { | ||
452 | u32 pid, tid; | ||
453 | u64 time; | ||
454 | } *id; | ||
455 | |||
456 | pid = jr->move.pid; | ||
457 | tid = jr->move.tid; | ||
458 | idr_size = jd->machine->id_hdr_size; | ||
459 | |||
460 | /* | ||
461 | * +16 to account for sample_id_all (hack) | ||
462 | */ | ||
463 | event = calloc(1, sizeof(*event) + 16); | ||
464 | if (!event) | ||
465 | return -1; | ||
466 | |||
467 | filename = event->mmap2.filename; | ||
468 | size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%"PRIu64, | ||
469 | jd->dir, | ||
470 | pid, | ||
471 | jr->move.code_index); | ||
472 | |||
473 | size++; /* for \0 */ | ||
474 | |||
475 | if (stat(filename, &st)) | ||
476 | memset(&st, 0, sizeof(stat)); | ||
477 | |||
478 | size = PERF_ALIGN(size, sizeof(u64)); | ||
479 | |||
480 | event->mmap2.header.type = PERF_RECORD_MMAP2; | ||
481 | event->mmap2.header.misc = PERF_RECORD_MISC_USER; | ||
482 | event->mmap2.header.size = (sizeof(event->mmap2) - | ||
483 | (sizeof(event->mmap2.filename) - size) + idr_size); | ||
484 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | ||
485 | event->mmap2.start = jr->move.new_code_addr; | ||
486 | event->mmap2.len = jr->move.code_size; | ||
487 | event->mmap2.pid = pid; | ||
488 | event->mmap2.tid = tid; | ||
489 | event->mmap2.ino = st.st_ino; | ||
490 | event->mmap2.maj = major(st.st_dev); | ||
491 | event->mmap2.min = minor(st.st_dev); | ||
492 | event->mmap2.prot = st.st_mode; | ||
493 | event->mmap2.flags = MAP_SHARED; | ||
494 | event->mmap2.ino_generation = 1; | ||
495 | |||
496 | id = (void *)((unsigned long)event + event->mmap.header.size - idr_size); | ||
497 | if (jd->sample_type & PERF_SAMPLE_TID) { | ||
498 | id->pid = pid; | ||
499 | id->tid = tid; | ||
500 | } | ||
501 | if (jd->sample_type & PERF_SAMPLE_TIME) | ||
502 | id->time = jr->load.p.timestamp; | ||
503 | |||
504 | /* | ||
505 | * create pseudo sample to induce dso hit increment | ||
506 | * use first address as sample address | ||
507 | */ | ||
508 | memset(&sample, 0, sizeof(sample)); | ||
509 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
510 | sample.pid = pid; | ||
511 | sample.tid = tid; | ||
512 | sample.time = id->time; | ||
513 | sample.ip = jr->move.new_code_addr; | ||
514 | |||
515 | ret = perf_event__process_mmap2(tool, event, &sample, jd->machine); | ||
516 | if (ret) | ||
517 | return ret; | ||
518 | |||
519 | ret = jit_inject_event(jd, event); | ||
520 | if (!ret) | ||
521 | build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine); | ||
522 | |||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) | ||
527 | { | ||
528 | void *data; | ||
529 | size_t sz; | ||
530 | |||
531 | if (!(jd && jr)) | ||
532 | return -1; | ||
533 | |||
534 | sz = jr->prefix.total_size - sizeof(jr->info); | ||
535 | data = malloc(sz); | ||
536 | if (!data) | ||
537 | return -1; | ||
538 | |||
539 | memcpy(data, &jr->info.entries, sz); | ||
540 | |||
541 | jd->debug_data = data; | ||
542 | |||
543 | /* | ||
544 | * we must use nr_entry instead of size here because | ||
545 | * we cannot distinguish actual entry from padding otherwise | ||
546 | */ | ||
547 | jd->nr_debug_entries = jr->info.nr_entry; | ||
548 | |||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static int | ||
553 | jit_process_dump(struct jit_buf_desc *jd) | ||
554 | { | ||
555 | union jr_entry *jr; | ||
556 | int ret; | ||
557 | |||
558 | while ((jr = jit_get_next_entry(jd))) { | ||
559 | switch(jr->prefix.id) { | ||
560 | case JIT_CODE_LOAD: | ||
561 | ret = jit_repipe_code_load(jd, jr); | ||
562 | break; | ||
563 | case JIT_CODE_MOVE: | ||
564 | ret = jit_repipe_code_move(jd, jr); | ||
565 | break; | ||
566 | case JIT_CODE_DEBUG_INFO: | ||
567 | ret = jit_repipe_debug_info(jd, jr); | ||
568 | break; | ||
569 | default: | ||
570 | ret = 0; | ||
571 | continue; | ||
572 | } | ||
573 | } | ||
574 | return ret; | ||
575 | } | ||
576 | |||
577 | static int | ||
578 | jit_inject(struct jit_buf_desc *jd, char *path) | ||
579 | { | ||
580 | int ret; | ||
581 | |||
582 | if (verbose > 0) | ||
583 | fprintf(stderr, "injecting: %s\n", path); | ||
584 | |||
585 | ret = jit_open(jd, path); | ||
586 | if (ret) | ||
587 | return -1; | ||
588 | |||
589 | ret = jit_process_dump(jd); | ||
590 | |||
591 | jit_close(jd); | ||
592 | |||
593 | if (verbose > 0) | ||
594 | fprintf(stderr, "injected: %s (%d)\n", path, ret); | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * File must be with pattern .../jit-XXXX.dump | ||
601 | * where XXXX is the PID of the process which did the mmap() | ||
602 | * as captured in the RECORD_MMAP record | ||
603 | */ | ||
604 | static int | ||
605 | jit_detect(char *mmap_name, pid_t pid) | ||
606 | { | ||
607 | char *p; | ||
608 | char *end = NULL; | ||
609 | pid_t pid2; | ||
610 | |||
611 | if (verbose > 2) | ||
612 | fprintf(stderr, "jit marker trying : %s\n", mmap_name); | ||
613 | /* | ||
614 | * get file name | ||
615 | */ | ||
616 | p = strrchr(mmap_name, '/'); | ||
617 | if (!p) | ||
618 | return -1; | ||
619 | |||
620 | /* | ||
621 | * match prefix | ||
622 | */ | ||
623 | if (strncmp(p, "/jit-", 5)) | ||
624 | return -1; | ||
625 | |||
626 | /* | ||
627 | * skip prefix | ||
628 | */ | ||
629 | p += 5; | ||
630 | |||
631 | /* | ||
632 | * must be followed by a pid | ||
633 | */ | ||
634 | if (!isdigit(*p)) | ||
635 | return -1; | ||
636 | |||
637 | pid2 = (int)strtol(p, &end, 10); | ||
638 | if (!end) | ||
639 | return -1; | ||
640 | |||
641 | /* | ||
642 | * pid does not match mmap pid | ||
643 | * pid==0 in system-wide mode (synthesized) | ||
644 | */ | ||
645 | if (pid && pid2 != pid) | ||
646 | return -1; | ||
647 | /* | ||
648 | * validate suffix | ||
649 | */ | ||
650 | if (strcmp(end, ".dump")) | ||
651 | return -1; | ||
652 | |||
653 | if (verbose > 0) | ||
654 | fprintf(stderr, "jit marker found: %s\n", mmap_name); | ||
655 | |||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | int | ||
660 | jit_process(struct perf_session *session, | ||
661 | struct perf_data_file *output, | ||
662 | struct machine *machine, | ||
663 | char *filename, | ||
664 | pid_t pid, | ||
665 | u64 *nbytes) | ||
666 | { | ||
667 | struct perf_evsel *first; | ||
668 | struct jit_buf_desc jd; | ||
669 | int ret; | ||
670 | |||
671 | /* | ||
672 | * first, detect marker mmap (i.e., the jitdump mmap) | ||
673 | */ | ||
674 | if (jit_detect(filename, pid)) | ||
675 | return 0; | ||
676 | |||
677 | memset(&jd, 0, sizeof(jd)); | ||
678 | |||
679 | jd.session = session; | ||
680 | jd.output = output; | ||
681 | jd.machine = machine; | ||
682 | |||
683 | /* | ||
684 | * track sample_type to compute id_all layout | ||
685 | * perf sets the same sample type to all events as of now | ||
686 | */ | ||
687 | first = perf_evlist__first(session->evlist); | ||
688 | jd.sample_type = first->attr.sample_type; | ||
689 | |||
690 | *nbytes = 0; | ||
691 | |||
692 | ret = jit_inject(&jd, filename); | ||
693 | if (!ret) { | ||
694 | *nbytes = jd.bytes_written; | ||
695 | ret = 1; | ||
696 | } | ||
697 | |||
698 | return ret; | ||
699 | } | ||
diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h new file mode 100644 index 000000000000..b66c1f503d9e --- /dev/null +++ b/tools/perf/util/jitdump.h | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * jitdump.h: jitted code info encapsulation file format | ||
3 | * | ||
4 | * Adapted from OProfile GPLv2 support jidump.h: | ||
5 | * Copyright 2007 OProfile authors | ||
6 | * Jens Wilke | ||
7 | * Daniel Hansel | ||
8 | * Copyright IBM Corporation 2007 | ||
9 | */ | ||
10 | #ifndef JITDUMP_H | ||
11 | #define JITDUMP_H | ||
12 | |||
13 | #include <sys/time.h> | ||
14 | #include <time.h> | ||
15 | #include <stdint.h> | ||
16 | |||
17 | /* JiTD */ | ||
18 | #define JITHEADER_MAGIC 0x4A695444 | ||
19 | #define JITHEADER_MAGIC_SW 0x4454694A | ||
20 | |||
21 | #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) | ||
22 | |||
23 | #define JITHEADER_VERSION 1 | ||
24 | |||
25 | enum jitdump_flags_bits { | ||
26 | JITDUMP_FLAGS_MAX_BIT, | ||
27 | }; | ||
28 | |||
29 | #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \ | ||
30 | (~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) | ||
31 | |||
32 | struct jitheader { | ||
33 | uint32_t magic; /* characters "jItD" */ | ||
34 | uint32_t version; /* header version */ | ||
35 | uint32_t total_size; /* total size of header */ | ||
36 | uint32_t elf_mach; /* elf mach target */ | ||
37 | uint32_t pad1; /* reserved */ | ||
38 | uint32_t pid; /* JIT process id */ | ||
39 | uint64_t timestamp; /* timestamp */ | ||
40 | uint64_t flags; /* flags */ | ||
41 | }; | ||
42 | |||
43 | enum jit_record_type { | ||
44 | JIT_CODE_LOAD = 0, | ||
45 | JIT_CODE_MOVE = 1, | ||
46 | JIT_CODE_DEBUG_INFO = 2, | ||
47 | JIT_CODE_CLOSE = 3, | ||
48 | |||
49 | JIT_CODE_MAX, | ||
50 | }; | ||
51 | |||
52 | /* record prefix (mandatory in each record) */ | ||
53 | struct jr_prefix { | ||
54 | uint32_t id; | ||
55 | uint32_t total_size; | ||
56 | uint64_t timestamp; | ||
57 | }; | ||
58 | |||
59 | struct jr_code_load { | ||
60 | struct jr_prefix p; | ||
61 | |||
62 | uint32_t pid; | ||
63 | uint32_t tid; | ||
64 | uint64_t vma; | ||
65 | uint64_t code_addr; | ||
66 | uint64_t code_size; | ||
67 | uint64_t code_index; | ||
68 | }; | ||
69 | |||
70 | struct jr_code_close { | ||
71 | struct jr_prefix p; | ||
72 | }; | ||
73 | |||
74 | struct jr_code_move { | ||
75 | struct jr_prefix p; | ||
76 | |||
77 | uint32_t pid; | ||
78 | uint32_t tid; | ||
79 | uint64_t vma; | ||
80 | uint64_t old_code_addr; | ||
81 | uint64_t new_code_addr; | ||
82 | uint64_t code_size; | ||
83 | uint64_t code_index; | ||
84 | }; | ||
85 | |||
86 | struct debug_entry { | ||
87 | uint64_t addr; | ||
88 | int lineno; /* source line number starting at 1 */ | ||
89 | int discrim; /* column discriminator, 0 is default */ | ||
90 | const char name[0]; /* null terminated filename, \xff\0 if same as previous entry */ | ||
91 | }; | ||
92 | |||
93 | struct jr_code_debug_info { | ||
94 | struct jr_prefix p; | ||
95 | |||
96 | uint64_t code_addr; | ||
97 | uint64_t nr_entry; | ||
98 | struct debug_entry entries[0]; | ||
99 | }; | ||
100 | |||
101 | union jr_entry { | ||
102 | struct jr_code_debug_info info; | ||
103 | struct jr_code_close close; | ||
104 | struct jr_code_load load; | ||
105 | struct jr_code_move move; | ||
106 | struct jr_prefix prefix; | ||
107 | }; | ||
108 | |||
109 | static inline struct debug_entry * | ||
110 | debug_entry_next(struct debug_entry *ent) | ||
111 | { | ||
112 | void *a = ent + 1; | ||
113 | size_t l = strlen(ent->name) + 1; | ||
114 | return a + l; | ||
115 | } | ||
116 | |||
117 | static inline char * | ||
118 | debug_entry_file(struct debug_entry *ent) | ||
119 | { | ||
120 | void *a = ent + 1; | ||
121 | return a; | ||
122 | } | ||
123 | |||
124 | #endif /* !JITDUMP_H */ | ||
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index ae825d4ec110..d01e73592f6e 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h | |||
@@ -122,6 +122,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm, | |||
122 | 122 | ||
123 | bool kvm_exit_event(struct perf_evsel *evsel); | 123 | bool kvm_exit_event(struct perf_evsel *evsel); |
124 | bool kvm_entry_event(struct perf_evsel *evsel); | 124 | bool kvm_entry_event(struct perf_evsel *evsel); |
125 | int setup_kvm_events_tp(struct perf_kvm_stat *kvm); | ||
125 | 126 | ||
126 | #define define_exit_reasons_table(name, symbols) \ | 127 | #define define_exit_reasons_table(name, symbols) \ |
127 | static struct exit_reasons_table name[] = { \ | 128 | static struct exit_reasons_table name[] = { \ |
@@ -133,8 +134,13 @@ bool kvm_entry_event(struct perf_evsel *evsel); | |||
133 | */ | 134 | */ |
134 | int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid); | 135 | int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid); |
135 | 136 | ||
136 | extern const char * const kvm_events_tp[]; | 137 | extern const char *kvm_events_tp[]; |
137 | extern struct kvm_reg_events_ops kvm_reg_events_ops[]; | 138 | extern struct kvm_reg_events_ops kvm_reg_events_ops[]; |
138 | extern const char * const kvm_skip_events[]; | 139 | extern const char * const kvm_skip_events[]; |
140 | extern const char *vcpu_id_str; | ||
141 | extern const int decode_str_len; | ||
142 | extern const char *kvm_exit_reason; | ||
143 | extern const char *kvm_entry_trace; | ||
144 | extern const char *kvm_exit_trace; | ||
139 | 145 | ||
140 | #endif /* __PERF_KVM_STAT_H */ | 146 | #endif /* __PERF_KVM_STAT_H */ |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 00724d496d38..33071d6159bc 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
@@ -3,11 +3,11 @@ | |||
3 | * Copyright (C) 2015, Huawei Inc. | 3 | * Copyright (C) 2015, Huawei Inc. |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <limits.h> | ||
6 | #include <stdio.h> | 7 | #include <stdio.h> |
7 | #include "util.h" | 8 | #include <stdlib.h> |
8 | #include "debug.h" | 9 | #include "debug.h" |
9 | #include "llvm-utils.h" | 10 | #include "llvm-utils.h" |
10 | #include "cache.h" | ||
11 | 11 | ||
12 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | 12 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ |
13 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ | 13 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ |
@@ -98,11 +98,12 @@ read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) | |||
98 | void *buf = NULL; | 98 | void *buf = NULL; |
99 | FILE *file = NULL; | 99 | FILE *file = NULL; |
100 | size_t read_sz = 0, buf_sz = 0; | 100 | size_t read_sz = 0, buf_sz = 0; |
101 | char serr[STRERR_BUFSIZE]; | ||
101 | 102 | ||
102 | file = popen(cmd, "r"); | 103 | file = popen(cmd, "r"); |
103 | if (!file) { | 104 | if (!file) { |
104 | pr_err("ERROR: unable to popen cmd: %s\n", | 105 | pr_err("ERROR: unable to popen cmd: %s\n", |
105 | strerror(errno)); | 106 | strerror_r(errno, serr, sizeof(serr))); |
106 | return -EINVAL; | 107 | return -EINVAL; |
107 | } | 108 | } |
108 | 109 | ||
@@ -136,7 +137,7 @@ read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) | |||
136 | 137 | ||
137 | if (ferror(file)) { | 138 | if (ferror(file)) { |
138 | pr_err("ERROR: error occurred when reading from pipe: %s\n", | 139 | pr_err("ERROR: error occurred when reading from pipe: %s\n", |
139 | strerror(errno)); | 140 | strerror_r(errno, serr, sizeof(serr))); |
140 | err = -EIO; | 141 | err = -EIO; |
141 | goto errout; | 142 | goto errout; |
142 | } | 143 | } |
@@ -334,10 +335,18 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
334 | unsigned int kernel_version; | 335 | unsigned int kernel_version; |
335 | char linux_version_code_str[64]; | 336 | char linux_version_code_str[64]; |
336 | const char *clang_opt = llvm_param.clang_opt; | 337 | const char *clang_opt = llvm_param.clang_opt; |
337 | char clang_path[PATH_MAX], nr_cpus_avail_str[64]; | 338 | char clang_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64]; |
339 | char serr[STRERR_BUFSIZE]; | ||
338 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; | 340 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; |
339 | const char *template = llvm_param.clang_bpf_cmd_template; | 341 | const char *template = llvm_param.clang_bpf_cmd_template; |
340 | 342 | ||
343 | if (path[0] != '-' && realpath(path, abspath) == NULL) { | ||
344 | err = errno; | ||
345 | pr_err("ERROR: problems with path %s: %s\n", | ||
346 | path, strerror_r(err, serr, sizeof(serr))); | ||
347 | return -err; | ||
348 | } | ||
349 | |||
341 | if (!template) | 350 | if (!template) |
342 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; | 351 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; |
343 | 352 | ||
@@ -362,7 +371,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
362 | if (nr_cpus_avail <= 0) { | 371 | if (nr_cpus_avail <= 0) { |
363 | pr_err( | 372 | pr_err( |
364 | "WARNING:\tunable to get available CPUs in this system: %s\n" | 373 | "WARNING:\tunable to get available CPUs in this system: %s\n" |
365 | " \tUse 128 instead.\n", strerror(errno)); | 374 | " \tUse 128 instead.\n", strerror_r(errno, serr, sizeof(serr))); |
366 | nr_cpus_avail = 128; | 375 | nr_cpus_avail = 128; |
367 | } | 376 | } |
368 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", | 377 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", |
@@ -387,8 +396,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
387 | * stdin to be source file (testing). | 396 | * stdin to be source file (testing). |
388 | */ | 397 | */ |
389 | force_set_env("CLANG_SOURCE", | 398 | force_set_env("CLANG_SOURCE", |
390 | (path[0] == '-') ? path : | 399 | (path[0] == '-') ? path : abspath); |
391 | make_nonrelative_path(path)); | ||
392 | 400 | ||
393 | pr_debug("llvm compiling command template: %s\n", template); | 401 | pr_debug("llvm compiling command template: %s\n", template); |
394 | err = read_from_pipe(template, &obj_buf, &obj_buf_sz); | 402 | err = read_from_pipe(template, &obj_buf, &obj_buf_sz); |
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index 5b3cf1c229e2..23b9a743fe72 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h | |||
@@ -39,11 +39,10 @@ struct llvm_param { | |||
39 | }; | 39 | }; |
40 | 40 | ||
41 | extern struct llvm_param llvm_param; | 41 | extern struct llvm_param llvm_param; |
42 | extern int perf_llvm_config(const char *var, const char *value); | 42 | int perf_llvm_config(const char *var, const char *value); |
43 | 43 | ||
44 | extern int llvm__compile_bpf(const char *path, void **p_obj_buf, | 44 | int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz); |
45 | size_t *p_obj_buf_sz); | ||
46 | 45 | ||
47 | /* This function is for test__llvm() use only */ | 46 | /* This function is for test__llvm() use only */ |
48 | extern int llvm__search_clang(void); | 47 | int llvm__search_clang(void); |
49 | #endif | 48 | #endif |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index ad79297c76c8..80b9b6a87990 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1301,9 +1301,8 @@ out_problem: | |||
1301 | 1301 | ||
1302 | int machine__process_mmap2_event(struct machine *machine, | 1302 | int machine__process_mmap2_event(struct machine *machine, |
1303 | union perf_event *event, | 1303 | union perf_event *event, |
1304 | struct perf_sample *sample __maybe_unused) | 1304 | struct perf_sample *sample) |
1305 | { | 1305 | { |
1306 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1307 | struct thread *thread; | 1306 | struct thread *thread; |
1308 | struct map *map; | 1307 | struct map *map; |
1309 | enum map_type type; | 1308 | enum map_type type; |
@@ -1312,8 +1311,8 @@ int machine__process_mmap2_event(struct machine *machine, | |||
1312 | if (dump_trace) | 1311 | if (dump_trace) |
1313 | perf_event__fprintf_mmap2(event, stdout); | 1312 | perf_event__fprintf_mmap2(event, stdout); |
1314 | 1313 | ||
1315 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | 1314 | if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || |
1316 | cpumode == PERF_RECORD_MISC_KERNEL) { | 1315 | sample->cpumode == PERF_RECORD_MISC_KERNEL) { |
1317 | ret = machine__process_kernel_mmap_event(machine, event); | 1316 | ret = machine__process_kernel_mmap_event(machine, event); |
1318 | if (ret < 0) | 1317 | if (ret < 0) |
1319 | goto out_problem; | 1318 | goto out_problem; |
@@ -1355,9 +1354,8 @@ out_problem: | |||
1355 | } | 1354 | } |
1356 | 1355 | ||
1357 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, | 1356 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, |
1358 | struct perf_sample *sample __maybe_unused) | 1357 | struct perf_sample *sample) |
1359 | { | 1358 | { |
1360 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1361 | struct thread *thread; | 1359 | struct thread *thread; |
1362 | struct map *map; | 1360 | struct map *map; |
1363 | enum map_type type; | 1361 | enum map_type type; |
@@ -1366,8 +1364,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
1366 | if (dump_trace) | 1364 | if (dump_trace) |
1367 | perf_event__fprintf_mmap(event, stdout); | 1365 | perf_event__fprintf_mmap(event, stdout); |
1368 | 1366 | ||
1369 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | 1367 | if (sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL || |
1370 | cpumode == PERF_RECORD_MISC_KERNEL) { | 1368 | sample->cpumode == PERF_RECORD_MISC_KERNEL) { |
1371 | ret = machine__process_kernel_mmap_event(machine, event); | 1369 | ret = machine__process_kernel_mmap_event(machine, event); |
1372 | if (ret < 0) | 1370 | if (ret < 0) |
1373 | goto out_problem; | 1371 | goto out_problem; |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2c2b443df5ba..8499db281158 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -94,7 +94,7 @@ int machine__process_aux_event(struct machine *machine, | |||
94 | union perf_event *event); | 94 | union perf_event *event); |
95 | int machine__process_itrace_start_event(struct machine *machine, | 95 | int machine__process_itrace_start_event(struct machine *machine, |
96 | union perf_event *event); | 96 | union perf_event *event); |
97 | int machine__process_switch_event(struct machine *machine __maybe_unused, | 97 | int machine__process_switch_event(struct machine *machine, |
98 | union perf_event *event); | 98 | union perf_event *event); |
99 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, | 99 | int machine__process_mmap_event(struct machine *machine, union perf_event *event, |
100 | struct perf_sample *sample); | 100 | struct perf_sample *sample); |
@@ -180,6 +180,16 @@ struct symbol *machine__find_kernel_symbol(struct machine *machine, | |||
180 | } | 180 | } |
181 | 181 | ||
182 | static inline | 182 | static inline |
183 | struct symbol *machine__find_kernel_symbol_by_name(struct machine *machine, | ||
184 | enum map_type type, const char *name, | ||
185 | struct map **mapp, | ||
186 | symbol_filter_t filter) | ||
187 | { | ||
188 | return map_groups__find_symbol_by_name(&machine->kmaps, type, name, | ||
189 | mapp, filter); | ||
190 | } | ||
191 | |||
192 | static inline | ||
183 | struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr, | 193 | struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr, |
184 | struct map **mapp, | 194 | struct map **mapp, |
185 | symbol_filter_t filter) | 195 | symbol_filter_t filter) |
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c new file mode 100644 index 000000000000..75465f89a413 --- /dev/null +++ b/tools/perf/util/mem-events.c | |||
@@ -0,0 +1,255 @@ | |||
1 | #include <stddef.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include <errno.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/stat.h> | ||
7 | #include <unistd.h> | ||
8 | #include <api/fs/fs.h> | ||
9 | #include "mem-events.h" | ||
10 | #include "debug.h" | ||
11 | #include "symbol.h" | ||
12 | |||
13 | #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s } | ||
14 | |||
15 | struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = { | ||
16 | E("ldlat-loads", "cpu/mem-loads,ldlat=30/P", "mem-loads"), | ||
17 | E("ldlat-stores", "cpu/mem-stores/P", "mem-stores"), | ||
18 | }; | ||
19 | #undef E | ||
20 | |||
21 | #undef E | ||
22 | |||
23 | char *perf_mem_events__name(int i) | ||
24 | { | ||
25 | return (char *)perf_mem_events[i].name; | ||
26 | } | ||
27 | |||
28 | int perf_mem_events__parse(const char *str) | ||
29 | { | ||
30 | char *tok, *saveptr = NULL; | ||
31 | bool found = false; | ||
32 | char *buf; | ||
33 | int j; | ||
34 | |||
35 | /* We need buffer that we know we can write to. */ | ||
36 | buf = malloc(strlen(str) + 1); | ||
37 | if (!buf) | ||
38 | return -ENOMEM; | ||
39 | |||
40 | strcpy(buf, str); | ||
41 | |||
42 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
43 | |||
44 | while (tok) { | ||
45 | for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { | ||
46 | struct perf_mem_event *e = &perf_mem_events[j]; | ||
47 | |||
48 | if (strstr(e->tag, tok)) | ||
49 | e->record = found = true; | ||
50 | } | ||
51 | |||
52 | tok = strtok_r(NULL, ",", &saveptr); | ||
53 | } | ||
54 | |||
55 | free(buf); | ||
56 | |||
57 | if (found) | ||
58 | return 0; | ||
59 | |||
60 | pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str); | ||
61 | return -1; | ||
62 | } | ||
63 | |||
64 | int perf_mem_events__init(void) | ||
65 | { | ||
66 | const char *mnt = sysfs__mount(); | ||
67 | bool found = false; | ||
68 | int j; | ||
69 | |||
70 | if (!mnt) | ||
71 | return -ENOENT; | ||
72 | |||
73 | for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) { | ||
74 | char path[PATH_MAX]; | ||
75 | struct perf_mem_event *e = &perf_mem_events[j]; | ||
76 | struct stat st; | ||
77 | |||
78 | scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s", | ||
79 | mnt, e->sysfs_name); | ||
80 | |||
81 | if (!stat(path, &st)) | ||
82 | e->supported = found = true; | ||
83 | } | ||
84 | |||
85 | return found ? 0 : -ENOENT; | ||
86 | } | ||
87 | |||
88 | static const char * const tlb_access[] = { | ||
89 | "N/A", | ||
90 | "HIT", | ||
91 | "MISS", | ||
92 | "L1", | ||
93 | "L2", | ||
94 | "Walker", | ||
95 | "Fault", | ||
96 | }; | ||
97 | |||
98 | int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info) | ||
99 | { | ||
100 | size_t l = 0, i; | ||
101 | u64 m = PERF_MEM_TLB_NA; | ||
102 | u64 hit, miss; | ||
103 | |||
104 | sz -= 1; /* -1 for null termination */ | ||
105 | out[0] = '\0'; | ||
106 | |||
107 | if (mem_info) | ||
108 | m = mem_info->data_src.mem_dtlb; | ||
109 | |||
110 | hit = m & PERF_MEM_TLB_HIT; | ||
111 | miss = m & PERF_MEM_TLB_MISS; | ||
112 | |||
113 | /* already taken care of */ | ||
114 | m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); | ||
115 | |||
116 | for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) { | ||
117 | if (!(m & 0x1)) | ||
118 | continue; | ||
119 | if (l) { | ||
120 | strcat(out, " or "); | ||
121 | l += 4; | ||
122 | } | ||
123 | l += scnprintf(out + l, sz - l, tlb_access[i]); | ||
124 | } | ||
125 | if (*out == '\0') | ||
126 | l += scnprintf(out, sz - l, "N/A"); | ||
127 | if (hit) | ||
128 | l += scnprintf(out + l, sz - l, " hit"); | ||
129 | if (miss) | ||
130 | l += scnprintf(out + l, sz - l, " miss"); | ||
131 | |||
132 | return l; | ||
133 | } | ||
134 | |||
135 | static const char * const mem_lvl[] = { | ||
136 | "N/A", | ||
137 | "HIT", | ||
138 | "MISS", | ||
139 | "L1", | ||
140 | "LFB", | ||
141 | "L2", | ||
142 | "L3", | ||
143 | "Local RAM", | ||
144 | "Remote RAM (1 hop)", | ||
145 | "Remote RAM (2 hops)", | ||
146 | "Remote Cache (1 hop)", | ||
147 | "Remote Cache (2 hops)", | ||
148 | "I/O", | ||
149 | "Uncached", | ||
150 | }; | ||
151 | |||
152 | int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) | ||
153 | { | ||
154 | size_t i, l = 0; | ||
155 | u64 m = PERF_MEM_LVL_NA; | ||
156 | u64 hit, miss; | ||
157 | |||
158 | if (mem_info) | ||
159 | m = mem_info->data_src.mem_lvl; | ||
160 | |||
161 | sz -= 1; /* -1 for null termination */ | ||
162 | out[0] = '\0'; | ||
163 | |||
164 | hit = m & PERF_MEM_LVL_HIT; | ||
165 | miss = m & PERF_MEM_LVL_MISS; | ||
166 | |||
167 | /* already taken care of */ | ||
168 | m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); | ||
169 | |||
170 | for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { | ||
171 | if (!(m & 0x1)) | ||
172 | continue; | ||
173 | if (l) { | ||
174 | strcat(out, " or "); | ||
175 | l += 4; | ||
176 | } | ||
177 | l += scnprintf(out + l, sz - l, mem_lvl[i]); | ||
178 | } | ||
179 | if (*out == '\0') | ||
180 | l += scnprintf(out, sz - l, "N/A"); | ||
181 | if (hit) | ||
182 | l += scnprintf(out + l, sz - l, " hit"); | ||
183 | if (miss) | ||
184 | l += scnprintf(out + l, sz - l, " miss"); | ||
185 | |||
186 | return l; | ||
187 | } | ||
188 | |||
189 | static const char * const snoop_access[] = { | ||
190 | "N/A", | ||
191 | "None", | ||
192 | "Miss", | ||
193 | "Hit", | ||
194 | "HitM", | ||
195 | }; | ||
196 | |||
197 | int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info) | ||
198 | { | ||
199 | size_t i, l = 0; | ||
200 | u64 m = PERF_MEM_SNOOP_NA; | ||
201 | |||
202 | sz -= 1; /* -1 for null termination */ | ||
203 | out[0] = '\0'; | ||
204 | |||
205 | if (mem_info) | ||
206 | m = mem_info->data_src.mem_snoop; | ||
207 | |||
208 | for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) { | ||
209 | if (!(m & 0x1)) | ||
210 | continue; | ||
211 | if (l) { | ||
212 | strcat(out, " or "); | ||
213 | l += 4; | ||
214 | } | ||
215 | l += scnprintf(out + l, sz - l, snoop_access[i]); | ||
216 | } | ||
217 | |||
218 | if (*out == '\0') | ||
219 | l += scnprintf(out, sz - l, "N/A"); | ||
220 | |||
221 | return l; | ||
222 | } | ||
223 | |||
224 | int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info) | ||
225 | { | ||
226 | u64 mask = PERF_MEM_LOCK_NA; | ||
227 | int l; | ||
228 | |||
229 | if (mem_info) | ||
230 | mask = mem_info->data_src.mem_lock; | ||
231 | |||
232 | if (mask & PERF_MEM_LOCK_NA) | ||
233 | l = scnprintf(out, sz, "N/A"); | ||
234 | else if (mask & PERF_MEM_LOCK_LOCKED) | ||
235 | l = scnprintf(out, sz, "Yes"); | ||
236 | else | ||
237 | l = scnprintf(out, sz, "No"); | ||
238 | |||
239 | return l; | ||
240 | } | ||
241 | |||
242 | int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info) | ||
243 | { | ||
244 | int i = 0; | ||
245 | |||
246 | i += perf_mem__lvl_scnprintf(out, sz, mem_info); | ||
247 | i += scnprintf(out + i, sz - i, "|SNP "); | ||
248 | i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info); | ||
249 | i += scnprintf(out + i, sz - i, "|TLB "); | ||
250 | i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info); | ||
251 | i += scnprintf(out + i, sz - i, "|LCK "); | ||
252 | i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info); | ||
253 | |||
254 | return i; | ||
255 | } | ||
diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h new file mode 100644 index 000000000000..5d6d93066a6e --- /dev/null +++ b/tools/perf/util/mem-events.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef __PERF_MEM_EVENTS_H | ||
2 | #define __PERF_MEM_EVENTS_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | |||
6 | struct perf_mem_event { | ||
7 | bool record; | ||
8 | bool supported; | ||
9 | const char *tag; | ||
10 | const char *name; | ||
11 | const char *sysfs_name; | ||
12 | }; | ||
13 | |||
14 | enum { | ||
15 | PERF_MEM_EVENTS__LOAD, | ||
16 | PERF_MEM_EVENTS__STORE, | ||
17 | PERF_MEM_EVENTS__MAX, | ||
18 | }; | ||
19 | |||
20 | extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX]; | ||
21 | |||
22 | int perf_mem_events__parse(const char *str); | ||
23 | int perf_mem_events__init(void); | ||
24 | |||
25 | char *perf_mem_events__name(int i); | ||
26 | |||
27 | struct mem_info; | ||
28 | int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info); | ||
29 | int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info); | ||
30 | int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info); | ||
31 | int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info); | ||
32 | |||
33 | int perf_script__meminfo_scnprintf(char *bf, size_t size, struct mem_info *mem_info); | ||
34 | |||
35 | #endif /* __PERF_MEM_EVENTS_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 813d9b272c81..4c19d5e79d8c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -279,7 +279,24 @@ const char *event_type(int type) | |||
279 | return "unknown"; | 279 | return "unknown"; |
280 | } | 280 | } |
281 | 281 | ||
282 | static int parse_events__is_name_term(struct parse_events_term *term) | ||
283 | { | ||
284 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | ||
285 | } | ||
282 | 286 | ||
287 | static char *get_config_name(struct list_head *head_terms) | ||
288 | { | ||
289 | struct parse_events_term *term; | ||
290 | |||
291 | if (!head_terms) | ||
292 | return NULL; | ||
293 | |||
294 | list_for_each_entry(term, head_terms, list) | ||
295 | if (parse_events__is_name_term(term)) | ||
296 | return term->val.str; | ||
297 | |||
298 | return NULL; | ||
299 | } | ||
283 | 300 | ||
284 | static struct perf_evsel * | 301 | static struct perf_evsel * |
285 | __add_event(struct list_head *list, int *idx, | 302 | __add_event(struct list_head *list, int *idx, |
@@ -333,11 +350,25 @@ static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES] | |||
333 | return -1; | 350 | return -1; |
334 | } | 351 | } |
335 | 352 | ||
353 | typedef int config_term_func_t(struct perf_event_attr *attr, | ||
354 | struct parse_events_term *term, | ||
355 | struct parse_events_error *err); | ||
356 | static int config_term_common(struct perf_event_attr *attr, | ||
357 | struct parse_events_term *term, | ||
358 | struct parse_events_error *err); | ||
359 | static int config_attr(struct perf_event_attr *attr, | ||
360 | struct list_head *head, | ||
361 | struct parse_events_error *err, | ||
362 | config_term_func_t config_term); | ||
363 | |||
336 | int parse_events_add_cache(struct list_head *list, int *idx, | 364 | int parse_events_add_cache(struct list_head *list, int *idx, |
337 | char *type, char *op_result1, char *op_result2) | 365 | char *type, char *op_result1, char *op_result2, |
366 | struct parse_events_error *err, | ||
367 | struct list_head *head_config) | ||
338 | { | 368 | { |
339 | struct perf_event_attr attr; | 369 | struct perf_event_attr attr; |
340 | char name[MAX_NAME_LEN]; | 370 | LIST_HEAD(config_terms); |
371 | char name[MAX_NAME_LEN], *config_name; | ||
341 | int cache_type = -1, cache_op = -1, cache_result = -1; | 372 | int cache_type = -1, cache_op = -1, cache_result = -1; |
342 | char *op_result[2] = { op_result1, op_result2 }; | 373 | char *op_result[2] = { op_result1, op_result2 }; |
343 | int i, n; | 374 | int i, n; |
@@ -351,6 +382,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, | |||
351 | if (cache_type == -1) | 382 | if (cache_type == -1) |
352 | return -EINVAL; | 383 | return -EINVAL; |
353 | 384 | ||
385 | config_name = get_config_name(head_config); | ||
354 | n = snprintf(name, MAX_NAME_LEN, "%s", type); | 386 | n = snprintf(name, MAX_NAME_LEN, "%s", type); |
355 | 387 | ||
356 | for (i = 0; (i < 2) && (op_result[i]); i++) { | 388 | for (i = 0; (i < 2) && (op_result[i]); i++) { |
@@ -391,7 +423,16 @@ int parse_events_add_cache(struct list_head *list, int *idx, | |||
391 | memset(&attr, 0, sizeof(attr)); | 423 | memset(&attr, 0, sizeof(attr)); |
392 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); | 424 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
393 | attr.type = PERF_TYPE_HW_CACHE; | 425 | attr.type = PERF_TYPE_HW_CACHE; |
394 | return add_event(list, idx, &attr, name, NULL); | 426 | |
427 | if (head_config) { | ||
428 | if (config_attr(&attr, head_config, err, | ||
429 | config_term_common)) | ||
430 | return -EINVAL; | ||
431 | |||
432 | if (get_config_terms(head_config, &config_terms)) | ||
433 | return -ENOMEM; | ||
434 | } | ||
435 | return add_event(list, idx, &attr, config_name ? : name, &config_terms); | ||
395 | } | 436 | } |
396 | 437 | ||
397 | static void tracepoint_error(struct parse_events_error *e, int err, | 438 | static void tracepoint_error(struct parse_events_error *e, int err, |
@@ -540,6 +581,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx, | |||
540 | struct __add_bpf_event_param { | 581 | struct __add_bpf_event_param { |
541 | struct parse_events_evlist *data; | 582 | struct parse_events_evlist *data; |
542 | struct list_head *list; | 583 | struct list_head *list; |
584 | struct list_head *head_config; | ||
543 | }; | 585 | }; |
544 | 586 | ||
545 | static int add_bpf_event(struct probe_trace_event *tev, int fd, | 587 | static int add_bpf_event(struct probe_trace_event *tev, int fd, |
@@ -556,7 +598,8 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd, | |||
556 | tev->group, tev->event, fd); | 598 | tev->group, tev->event, fd); |
557 | 599 | ||
558 | err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, | 600 | err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, |
559 | tev->event, evlist->error, NULL); | 601 | tev->event, evlist->error, |
602 | param->head_config); | ||
560 | if (err) { | 603 | if (err) { |
561 | struct perf_evsel *evsel, *tmp; | 604 | struct perf_evsel *evsel, *tmp; |
562 | 605 | ||
@@ -581,11 +624,12 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd, | |||
581 | 624 | ||
582 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, | 625 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, |
583 | struct list_head *list, | 626 | struct list_head *list, |
584 | struct bpf_object *obj) | 627 | struct bpf_object *obj, |
628 | struct list_head *head_config) | ||
585 | { | 629 | { |
586 | int err; | 630 | int err; |
587 | char errbuf[BUFSIZ]; | 631 | char errbuf[BUFSIZ]; |
588 | struct __add_bpf_event_param param = {data, list}; | 632 | struct __add_bpf_event_param param = {data, list, head_config}; |
589 | static bool registered_unprobe_atexit = false; | 633 | static bool registered_unprobe_atexit = false; |
590 | 634 | ||
591 | if (IS_ERR(obj) || !obj) { | 635 | if (IS_ERR(obj) || !obj) { |
@@ -631,17 +675,99 @@ errout: | |||
631 | return err; | 675 | return err; |
632 | } | 676 | } |
633 | 677 | ||
678 | static int | ||
679 | parse_events_config_bpf(struct parse_events_evlist *data, | ||
680 | struct bpf_object *obj, | ||
681 | struct list_head *head_config) | ||
682 | { | ||
683 | struct parse_events_term *term; | ||
684 | int error_pos; | ||
685 | |||
686 | if (!head_config || list_empty(head_config)) | ||
687 | return 0; | ||
688 | |||
689 | list_for_each_entry(term, head_config, list) { | ||
690 | char errbuf[BUFSIZ]; | ||
691 | int err; | ||
692 | |||
693 | if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) { | ||
694 | snprintf(errbuf, sizeof(errbuf), | ||
695 | "Invalid config term for BPF object"); | ||
696 | errbuf[BUFSIZ - 1] = '\0'; | ||
697 | |||
698 | data->error->idx = term->err_term; | ||
699 | data->error->str = strdup(errbuf); | ||
700 | return -EINVAL; | ||
701 | } | ||
702 | |||
703 | err = bpf__config_obj(obj, term, data->evlist, &error_pos); | ||
704 | if (err) { | ||
705 | bpf__strerror_config_obj(obj, term, data->evlist, | ||
706 | &error_pos, err, errbuf, | ||
707 | sizeof(errbuf)); | ||
708 | data->error->help = strdup( | ||
709 | "Hint:\tValid config terms:\n" | ||
710 | " \tmap:[<arraymap>].value<indices>=[value]\n" | ||
711 | " \tmap:[<eventmap>].event<indices>=[event]\n" | ||
712 | "\n" | ||
713 | " \twhere <indices> is something like [0,3...5] or [all]\n" | ||
714 | " \t(add -v to see detail)"); | ||
715 | data->error->str = strdup(errbuf); | ||
716 | if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE) | ||
717 | data->error->idx = term->err_val; | ||
718 | else | ||
719 | data->error->idx = term->err_term + error_pos; | ||
720 | return err; | ||
721 | } | ||
722 | } | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | /* | ||
727 | * Split config terms: | ||
728 | * perf record -e bpf.c/call-graph=fp,map:array.value[0]=1/ ... | ||
729 | * 'call-graph=fp' is 'evt config', should be applied to each | ||
730 | * events in bpf.c. | ||
731 | * 'map:array.value[0]=1' is 'obj config', should be processed | ||
732 | * with parse_events_config_bpf. | ||
733 | * | ||
734 | * Move object config terms from the first list to obj_head_config. | ||
735 | */ | ||
736 | static void | ||
737 | split_bpf_config_terms(struct list_head *evt_head_config, | ||
738 | struct list_head *obj_head_config) | ||
739 | { | ||
740 | struct parse_events_term *term, *temp; | ||
741 | |||
742 | /* | ||
743 | * Currectly, all possible user config term | ||
744 | * belong to bpf object. parse_events__is_hardcoded_term() | ||
745 | * happends to be a good flag. | ||
746 | * | ||
747 | * See parse_events_config_bpf() and | ||
748 | * config_term_tracepoint(). | ||
749 | */ | ||
750 | list_for_each_entry_safe(term, temp, evt_head_config, list) | ||
751 | if (!parse_events__is_hardcoded_term(term)) | ||
752 | list_move_tail(&term->list, obj_head_config); | ||
753 | } | ||
754 | |||
634 | int parse_events_load_bpf(struct parse_events_evlist *data, | 755 | int parse_events_load_bpf(struct parse_events_evlist *data, |
635 | struct list_head *list, | 756 | struct list_head *list, |
636 | char *bpf_file_name, | 757 | char *bpf_file_name, |
637 | bool source) | 758 | bool source, |
759 | struct list_head *head_config) | ||
638 | { | 760 | { |
761 | int err; | ||
639 | struct bpf_object *obj; | 762 | struct bpf_object *obj; |
763 | LIST_HEAD(obj_head_config); | ||
764 | |||
765 | if (head_config) | ||
766 | split_bpf_config_terms(head_config, &obj_head_config); | ||
640 | 767 | ||
641 | obj = bpf__prepare_load(bpf_file_name, source); | 768 | obj = bpf__prepare_load(bpf_file_name, source); |
642 | if (IS_ERR(obj)) { | 769 | if (IS_ERR(obj)) { |
643 | char errbuf[BUFSIZ]; | 770 | char errbuf[BUFSIZ]; |
644 | int err; | ||
645 | 771 | ||
646 | err = PTR_ERR(obj); | 772 | err = PTR_ERR(obj); |
647 | 773 | ||
@@ -659,7 +785,18 @@ int parse_events_load_bpf(struct parse_events_evlist *data, | |||
659 | return err; | 785 | return err; |
660 | } | 786 | } |
661 | 787 | ||
662 | return parse_events_load_bpf_obj(data, list, obj); | 788 | err = parse_events_load_bpf_obj(data, list, obj, head_config); |
789 | if (err) | ||
790 | return err; | ||
791 | err = parse_events_config_bpf(data, obj, &obj_head_config); | ||
792 | |||
793 | /* | ||
794 | * Caller doesn't know anything about obj_head_config, | ||
795 | * so combine them together again before returnning. | ||
796 | */ | ||
797 | if (head_config) | ||
798 | list_splice_tail(&obj_head_config, head_config); | ||
799 | return err; | ||
663 | } | 800 | } |
664 | 801 | ||
665 | static int | 802 | static int |
@@ -746,9 +883,59 @@ static int check_type_val(struct parse_events_term *term, | |||
746 | return -EINVAL; | 883 | return -EINVAL; |
747 | } | 884 | } |
748 | 885 | ||
749 | typedef int config_term_func_t(struct perf_event_attr *attr, | 886 | /* |
750 | struct parse_events_term *term, | 887 | * Update according to parse-events.l |
751 | struct parse_events_error *err); | 888 | */ |
889 | static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { | ||
890 | [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>", | ||
891 | [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config", | ||
892 | [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1", | ||
893 | [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2", | ||
894 | [PARSE_EVENTS__TERM_TYPE_NAME] = "name", | ||
895 | [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period", | ||
896 | [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq", | ||
897 | [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type", | ||
898 | [PARSE_EVENTS__TERM_TYPE_TIME] = "time", | ||
899 | [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph", | ||
900 | [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size", | ||
901 | [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", | ||
902 | [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", | ||
903 | }; | ||
904 | |||
905 | static bool config_term_shrinked; | ||
906 | |||
907 | static bool | ||
908 | config_term_avail(int term_type, struct parse_events_error *err) | ||
909 | { | ||
910 | if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) { | ||
911 | err->str = strdup("Invalid term_type"); | ||
912 | return false; | ||
913 | } | ||
914 | if (!config_term_shrinked) | ||
915 | return true; | ||
916 | |||
917 | switch (term_type) { | ||
918 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
919 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
920 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
921 | case PARSE_EVENTS__TERM_TYPE_NAME: | ||
922 | return true; | ||
923 | default: | ||
924 | if (!err) | ||
925 | return false; | ||
926 | |||
927 | /* term_type is validated so indexing is safe */ | ||
928 | if (asprintf(&err->str, "'%s' is not usable in 'perf stat'", | ||
929 | config_term_names[term_type]) < 0) | ||
930 | err->str = NULL; | ||
931 | return false; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | void parse_events__shrink_config_terms(void) | ||
936 | { | ||
937 | config_term_shrinked = true; | ||
938 | } | ||
752 | 939 | ||
753 | static int config_term_common(struct perf_event_attr *attr, | 940 | static int config_term_common(struct perf_event_attr *attr, |
754 | struct parse_events_term *term, | 941 | struct parse_events_term *term, |
@@ -815,6 +1002,17 @@ do { \ | |||
815 | return -EINVAL; | 1002 | return -EINVAL; |
816 | } | 1003 | } |
817 | 1004 | ||
1005 | /* | ||
1006 | * Check term availbility after basic checking so | ||
1007 | * PARSE_EVENTS__TERM_TYPE_USER can be found and filtered. | ||
1008 | * | ||
1009 | * If check availbility at the entry of this function, | ||
1010 | * user will see "'<sysfs term>' is not usable in 'perf stat'" | ||
1011 | * if an invalid config term is provided for legacy events | ||
1012 | * (for example, instructions/badterm/...), which is confusing. | ||
1013 | */ | ||
1014 | if (!config_term_avail(term->type_term, err)) | ||
1015 | return -EINVAL; | ||
818 | return 0; | 1016 | return 0; |
819 | #undef CHECK_TYPE_VAL | 1017 | #undef CHECK_TYPE_VAL |
820 | } | 1018 | } |
@@ -961,23 +1159,8 @@ int parse_events_add_numeric(struct parse_events_evlist *data, | |||
961 | return -ENOMEM; | 1159 | return -ENOMEM; |
962 | } | 1160 | } |
963 | 1161 | ||
964 | return add_event(list, &data->idx, &attr, NULL, &config_terms); | 1162 | return add_event(list, &data->idx, &attr, |
965 | } | 1163 | get_config_name(head_config), &config_terms); |
966 | |||
967 | static int parse_events__is_name_term(struct parse_events_term *term) | ||
968 | { | ||
969 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | ||
970 | } | ||
971 | |||
972 | static char *pmu_event_name(struct list_head *head_terms) | ||
973 | { | ||
974 | struct parse_events_term *term; | ||
975 | |||
976 | list_for_each_entry(term, head_terms, list) | ||
977 | if (parse_events__is_name_term(term)) | ||
978 | return term->val.str; | ||
979 | |||
980 | return NULL; | ||
981 | } | 1164 | } |
982 | 1165 | ||
983 | int parse_events_add_pmu(struct parse_events_evlist *data, | 1166 | int parse_events_add_pmu(struct parse_events_evlist *data, |
@@ -1024,7 +1207,7 @@ int parse_events_add_pmu(struct parse_events_evlist *data, | |||
1024 | return -EINVAL; | 1207 | return -EINVAL; |
1025 | 1208 | ||
1026 | evsel = __add_event(list, &data->idx, &attr, | 1209 | evsel = __add_event(list, &data->idx, &attr, |
1027 | pmu_event_name(head_config), pmu->cpus, | 1210 | get_config_name(head_config), pmu->cpus, |
1028 | &config_terms); | 1211 | &config_terms); |
1029 | if (evsel) { | 1212 | if (evsel) { |
1030 | evsel->unit = info.unit; | 1213 | evsel->unit = info.unit; |
@@ -1386,8 +1569,7 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
1386 | return 0; | 1569 | return 0; |
1387 | } | 1570 | } |
1388 | 1571 | ||
1389 | if (data.terms) | 1572 | parse_events_terms__delete(data.terms); |
1390 | parse_events__free_terms(data.terms); | ||
1391 | return ret; | 1573 | return ret; |
1392 | } | 1574 | } |
1393 | 1575 | ||
@@ -1395,9 +1577,10 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
1395 | struct parse_events_error *err) | 1577 | struct parse_events_error *err) |
1396 | { | 1578 | { |
1397 | struct parse_events_evlist data = { | 1579 | struct parse_events_evlist data = { |
1398 | .list = LIST_HEAD_INIT(data.list), | 1580 | .list = LIST_HEAD_INIT(data.list), |
1399 | .idx = evlist->nr_entries, | 1581 | .idx = evlist->nr_entries, |
1400 | .error = err, | 1582 | .error = err, |
1583 | .evlist = evlist, | ||
1401 | }; | 1584 | }; |
1402 | int ret; | 1585 | int ret; |
1403 | 1586 | ||
@@ -2068,12 +2251,29 @@ int parse_events_term__clone(struct parse_events_term **new, | |||
2068 | term->err_term, term->err_val); | 2251 | term->err_term, term->err_val); |
2069 | } | 2252 | } |
2070 | 2253 | ||
2071 | void parse_events__free_terms(struct list_head *terms) | 2254 | void parse_events_terms__purge(struct list_head *terms) |
2072 | { | 2255 | { |
2073 | struct parse_events_term *term, *h; | 2256 | struct parse_events_term *term, *h; |
2074 | 2257 | ||
2075 | list_for_each_entry_safe(term, h, terms, list) | 2258 | list_for_each_entry_safe(term, h, terms, list) { |
2259 | if (term->array.nr_ranges) | ||
2260 | free(term->array.ranges); | ||
2261 | list_del_init(&term->list); | ||
2076 | free(term); | 2262 | free(term); |
2263 | } | ||
2264 | } | ||
2265 | |||
2266 | void parse_events_terms__delete(struct list_head *terms) | ||
2267 | { | ||
2268 | if (!terms) | ||
2269 | return; | ||
2270 | parse_events_terms__purge(terms); | ||
2271 | free(terms); | ||
2272 | } | ||
2273 | |||
2274 | void parse_events__clear_array(struct parse_events_array *a) | ||
2275 | { | ||
2276 | free(a->ranges); | ||
2077 | } | 2277 | } |
2078 | 2278 | ||
2079 | void parse_events_evlist_error(struct parse_events_evlist *data, | 2279 | void parse_events_evlist_error(struct parse_events_evlist *data, |
@@ -2088,6 +2288,33 @@ void parse_events_evlist_error(struct parse_events_evlist *data, | |||
2088 | WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); | 2288 | WARN_ONCE(!err->str, "WARNING: failed to allocate error string"); |
2089 | } | 2289 | } |
2090 | 2290 | ||
2291 | static void config_terms_list(char *buf, size_t buf_sz) | ||
2292 | { | ||
2293 | int i; | ||
2294 | bool first = true; | ||
2295 | |||
2296 | buf[0] = '\0'; | ||
2297 | for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) { | ||
2298 | const char *name = config_term_names[i]; | ||
2299 | |||
2300 | if (!config_term_avail(i, NULL)) | ||
2301 | continue; | ||
2302 | if (!name) | ||
2303 | continue; | ||
2304 | if (name[0] == '<') | ||
2305 | continue; | ||
2306 | |||
2307 | if (strlen(buf) + strlen(name) + 2 >= buf_sz) | ||
2308 | return; | ||
2309 | |||
2310 | if (!first) | ||
2311 | strcat(buf, ","); | ||
2312 | else | ||
2313 | first = false; | ||
2314 | strcat(buf, name); | ||
2315 | } | ||
2316 | } | ||
2317 | |||
2091 | /* | 2318 | /* |
2092 | * Return string contains valid config terms of an event. | 2319 | * Return string contains valid config terms of an event. |
2093 | * @additional_terms: For terms such as PMU sysfs terms. | 2320 | * @additional_terms: For terms such as PMU sysfs terms. |
@@ -2095,17 +2322,18 @@ void parse_events_evlist_error(struct parse_events_evlist *data, | |||
2095 | char *parse_events_formats_error_string(char *additional_terms) | 2322 | char *parse_events_formats_error_string(char *additional_terms) |
2096 | { | 2323 | { |
2097 | char *str; | 2324 | char *str; |
2098 | static const char *static_terms = "config,config1,config2,name," | 2325 | /* "branch_type" is the longest name */ |
2099 | "period,freq,branch_type,time," | 2326 | char static_terms[__PARSE_EVENTS__TERM_TYPE_NR * |
2100 | "call-graph,stack-size\n"; | 2327 | (sizeof("branch_type") - 1)]; |
2101 | 2328 | ||
2329 | config_terms_list(static_terms, sizeof(static_terms)); | ||
2102 | /* valid terms */ | 2330 | /* valid terms */ |
2103 | if (additional_terms) { | 2331 | if (additional_terms) { |
2104 | if (!asprintf(&str, "valid terms: %s,%s", | 2332 | if (asprintf(&str, "valid terms: %s,%s", |
2105 | additional_terms, static_terms)) | 2333 | additional_terms, static_terms) < 0) |
2106 | goto fail; | 2334 | goto fail; |
2107 | } else { | 2335 | } else { |
2108 | if (!asprintf(&str, "valid terms: %s", static_terms)) | 2336 | if (asprintf(&str, "valid terms: %s", static_terms) < 0) |
2109 | goto fail; | 2337 | goto fail; |
2110 | } | 2338 | } |
2111 | return str; | 2339 | return str; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f1a6db107241..d740c3ca9a1d 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -22,19 +22,18 @@ struct tracepoint_path { | |||
22 | struct tracepoint_path *next; | 22 | struct tracepoint_path *next; |
23 | }; | 23 | }; |
24 | 24 | ||
25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 25 | struct tracepoint_path *tracepoint_id_to_path(u64 config); |
26 | extern struct tracepoint_path *tracepoint_name_to_path(const char *name); | 26 | struct tracepoint_path *tracepoint_name_to_path(const char *name); |
27 | extern bool have_tracepoints(struct list_head *evlist); | 27 | bool have_tracepoints(struct list_head *evlist); |
28 | 28 | ||
29 | const char *event_type(int type); | 29 | const char *event_type(int type); |
30 | 30 | ||
31 | extern int parse_events_option(const struct option *opt, const char *str, | 31 | int parse_events_option(const struct option *opt, const char *str, int unset); |
32 | int unset); | 32 | int parse_events(struct perf_evlist *evlist, const char *str, |
33 | extern int parse_events(struct perf_evlist *evlist, const char *str, | 33 | struct parse_events_error *error); |
34 | struct parse_events_error *error); | 34 | int parse_events_terms(struct list_head *terms, const char *str); |
35 | extern int parse_events_terms(struct list_head *terms, const char *str); | 35 | int parse_filter(const struct option *opt, const char *str, int unset); |
36 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 36 | int exclude_perf(const struct option *opt, const char *arg, int unset); |
37 | extern int exclude_perf(const struct option *opt, const char *arg, int unset); | ||
38 | 37 | ||
39 | #define EVENTS_HELP_MAX (128*1024) | 38 | #define EVENTS_HELP_MAX (128*1024) |
40 | 39 | ||
@@ -68,11 +67,21 @@ enum { | |||
68 | PARSE_EVENTS__TERM_TYPE_CALLGRAPH, | 67 | PARSE_EVENTS__TERM_TYPE_CALLGRAPH, |
69 | PARSE_EVENTS__TERM_TYPE_STACKSIZE, | 68 | PARSE_EVENTS__TERM_TYPE_STACKSIZE, |
70 | PARSE_EVENTS__TERM_TYPE_NOINHERIT, | 69 | PARSE_EVENTS__TERM_TYPE_NOINHERIT, |
71 | PARSE_EVENTS__TERM_TYPE_INHERIT | 70 | PARSE_EVENTS__TERM_TYPE_INHERIT, |
71 | __PARSE_EVENTS__TERM_TYPE_NR, | ||
72 | }; | ||
73 | |||
74 | struct parse_events_array { | ||
75 | size_t nr_ranges; | ||
76 | struct { | ||
77 | unsigned int start; | ||
78 | size_t length; | ||
79 | } *ranges; | ||
72 | }; | 80 | }; |
73 | 81 | ||
74 | struct parse_events_term { | 82 | struct parse_events_term { |
75 | char *config; | 83 | char *config; |
84 | struct parse_events_array array; | ||
76 | union { | 85 | union { |
77 | char *str; | 86 | char *str; |
78 | u64 num; | 87 | u64 num; |
@@ -98,12 +107,14 @@ struct parse_events_evlist { | |||
98 | int idx; | 107 | int idx; |
99 | int nr_groups; | 108 | int nr_groups; |
100 | struct parse_events_error *error; | 109 | struct parse_events_error *error; |
110 | struct perf_evlist *evlist; | ||
101 | }; | 111 | }; |
102 | 112 | ||
103 | struct parse_events_terms { | 113 | struct parse_events_terms { |
104 | struct list_head *terms; | 114 | struct list_head *terms; |
105 | }; | 115 | }; |
106 | 116 | ||
117 | void parse_events__shrink_config_terms(void); | ||
107 | int parse_events__is_hardcoded_term(struct parse_events_term *term); | 118 | int parse_events__is_hardcoded_term(struct parse_events_term *term); |
108 | int parse_events_term__num(struct parse_events_term **term, | 119 | int parse_events_term__num(struct parse_events_term **term, |
109 | int type_term, char *config, u64 num, | 120 | int type_term, char *config, u64 num, |
@@ -115,7 +126,9 @@ int parse_events_term__sym_hw(struct parse_events_term **term, | |||
115 | char *config, unsigned idx); | 126 | char *config, unsigned idx); |
116 | int parse_events_term__clone(struct parse_events_term **new, | 127 | int parse_events_term__clone(struct parse_events_term **new, |
117 | struct parse_events_term *term); | 128 | struct parse_events_term *term); |
118 | void parse_events__free_terms(struct list_head *terms); | 129 | void parse_events_terms__delete(struct list_head *terms); |
130 | void parse_events_terms__purge(struct list_head *terms); | ||
131 | void parse_events__clear_array(struct parse_events_array *a); | ||
119 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); | 132 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
120 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | 133 | int parse_events__modifier_group(struct list_head *list, char *event_mod); |
121 | int parse_events_name(struct list_head *list, char *name); | 134 | int parse_events_name(struct list_head *list, char *name); |
@@ -126,18 +139,22 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx, | |||
126 | int parse_events_load_bpf(struct parse_events_evlist *data, | 139 | int parse_events_load_bpf(struct parse_events_evlist *data, |
127 | struct list_head *list, | 140 | struct list_head *list, |
128 | char *bpf_file_name, | 141 | char *bpf_file_name, |
129 | bool source); | 142 | bool source, |
143 | struct list_head *head_config); | ||
130 | /* Provide this function for perf test */ | 144 | /* Provide this function for perf test */ |
131 | struct bpf_object; | 145 | struct bpf_object; |
132 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, | 146 | int parse_events_load_bpf_obj(struct parse_events_evlist *data, |
133 | struct list_head *list, | 147 | struct list_head *list, |
134 | struct bpf_object *obj); | 148 | struct bpf_object *obj, |
149 | struct list_head *head_config); | ||
135 | int parse_events_add_numeric(struct parse_events_evlist *data, | 150 | int parse_events_add_numeric(struct parse_events_evlist *data, |
136 | struct list_head *list, | 151 | struct list_head *list, |
137 | u32 type, u64 config, | 152 | u32 type, u64 config, |
138 | struct list_head *head_config); | 153 | struct list_head *head_config); |
139 | int parse_events_add_cache(struct list_head *list, int *idx, | 154 | int parse_events_add_cache(struct list_head *list, int *idx, |
140 | char *type, char *op_result1, char *op_result2); | 155 | char *type, char *op_result1, char *op_result2, |
156 | struct parse_events_error *error, | ||
157 | struct list_head *head_config); | ||
141 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 158 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
142 | void *ptr, char *type, u64 len); | 159 | void *ptr, char *type, u64 len); |
143 | int parse_events_add_pmu(struct parse_events_evlist *data, | 160 | int parse_events_add_pmu(struct parse_events_evlist *data, |
@@ -165,7 +182,7 @@ void print_symbol_events(const char *event_glob, unsigned type, | |||
165 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | 182 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob, |
166 | bool name_only); | 183 | bool name_only); |
167 | int print_hwcache_events(const char *event_glob, bool name_only); | 184 | int print_hwcache_events(const char *event_glob, bool name_only); |
168 | extern int is_valid_tracepoint(const char *event_string); | 185 | int is_valid_tracepoint(const char *event_string); |
169 | 186 | ||
170 | int valid_event_mount(const char *eventfs); | 187 | int valid_event_mount(const char *eventfs); |
171 | char *parse_events_formats_error_string(char *additional_terms); | 188 | char *parse_events_formats_error_string(char *additional_terms); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 58c5831ffd5c..1477fbc78993 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -9,8 +9,8 @@ | |||
9 | %{ | 9 | %{ |
10 | #include <errno.h> | 10 | #include <errno.h> |
11 | #include "../perf.h" | 11 | #include "../perf.h" |
12 | #include "parse-events-bison.h" | ||
13 | #include "parse-events.h" | 12 | #include "parse-events.h" |
13 | #include "parse-events-bison.h" | ||
14 | 14 | ||
15 | char *parse_events_get_text(yyscan_t yyscanner); | 15 | char *parse_events_get_text(yyscan_t yyscanner); |
16 | YYSTYPE *parse_events_get_lval(yyscan_t yyscanner); | 16 | YYSTYPE *parse_events_get_lval(yyscan_t yyscanner); |
@@ -111,6 +111,7 @@ do { \ | |||
111 | %x mem | 111 | %x mem |
112 | %s config | 112 | %s config |
113 | %x event | 113 | %x event |
114 | %x array | ||
114 | 115 | ||
115 | group [^,{}/]*[{][^}]*[}][^,{}/]* | 116 | group [^,{}/]*[{][^}]*[}][^,{}/]* |
116 | event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* | 117 | event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* |
@@ -122,7 +123,7 @@ num_dec [0-9]+ | |||
122 | num_hex 0x[a-fA-F0-9]+ | 123 | num_hex 0x[a-fA-F0-9]+ |
123 | num_raw_hex [a-fA-F0-9]+ | 124 | num_raw_hex [a-fA-F0-9]+ |
124 | name [a-zA-Z_*?][a-zA-Z0-9_*?.]* | 125 | name [a-zA-Z_*?][a-zA-Z0-9_*?.]* |
125 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]* | 126 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]* |
126 | /* If you add a modifier you need to update check_modifier() */ | 127 | /* If you add a modifier you need to update check_modifier() */ |
127 | modifier_event [ukhpPGHSDI]+ | 128 | modifier_event [ukhpPGHSDI]+ |
128 | modifier_bp [rwx]{1,3} | 129 | modifier_bp [rwx]{1,3} |
@@ -176,10 +177,17 @@ modifier_bp [rwx]{1,3} | |||
176 | 177 | ||
177 | } | 178 | } |
178 | 179 | ||
180 | <array>{ | ||
181 | "]" { BEGIN(config); return ']'; } | ||
182 | {num_dec} { return value(yyscanner, 10); } | ||
183 | {num_hex} { return value(yyscanner, 16); } | ||
184 | , { return ','; } | ||
185 | "\.\.\." { return PE_ARRAY_RANGE; } | ||
186 | } | ||
187 | |||
179 | <config>{ | 188 | <config>{ |
180 | /* | 189 | /* |
181 | * Please update parse_events_formats_error_string any time | 190 | * Please update config_term_names when new static term is added. |
182 | * new static term is added. | ||
183 | */ | 191 | */ |
184 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | 192 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } |
185 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | 193 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } |
@@ -196,6 +204,8 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } | |||
196 | , { return ','; } | 204 | , { return ','; } |
197 | "/" { BEGIN(INITIAL); return '/'; } | 205 | "/" { BEGIN(INITIAL); return '/'; } |
198 | {name_minus} { return str(yyscanner, PE_NAME); } | 206 | {name_minus} { return str(yyscanner, PE_NAME); } |
207 | \[all\] { return PE_ARRAY_ALL; } | ||
208 | "[" { BEGIN(array); return '['; } | ||
199 | } | 209 | } |
200 | 210 | ||
201 | <mem>{ | 211 | <mem>{ |
@@ -238,6 +248,7 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU | |||
238 | alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } | 248 | alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } |
239 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | 249 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } |
240 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } | 250 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } |
251 | bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); } | ||
241 | 252 | ||
242 | /* | 253 | /* |
243 | * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. | 254 | * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index ad379968d4c1..5be4a5f216d6 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -28,7 +28,7 @@ do { \ | |||
28 | INIT_LIST_HEAD(list); \ | 28 | INIT_LIST_HEAD(list); \ |
29 | } while (0) | 29 | } while (0) |
30 | 30 | ||
31 | static inc_group_count(struct list_head *list, | 31 | static void inc_group_count(struct list_head *list, |
32 | struct parse_events_evlist *data) | 32 | struct parse_events_evlist *data) |
33 | { | 33 | { |
34 | /* Count groups only have more than 1 members */ | 34 | /* Count groups only have more than 1 members */ |
@@ -48,6 +48,7 @@ static inc_group_count(struct list_head *list, | |||
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 | %token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT |
51 | %token PE_ARRAY_ALL PE_ARRAY_RANGE | ||
51 | %type <num> PE_VALUE | 52 | %type <num> PE_VALUE |
52 | %type <num> PE_VALUE_SYM_HW | 53 | %type <num> PE_VALUE_SYM_HW |
53 | %type <num> PE_VALUE_SYM_SW | 54 | %type <num> PE_VALUE_SYM_SW |
@@ -64,6 +65,7 @@ static inc_group_count(struct list_head *list, | |||
64 | %type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT | 65 | %type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT |
65 | %type <num> value_sym | 66 | %type <num> value_sym |
66 | %type <head> event_config | 67 | %type <head> event_config |
68 | %type <head> opt_event_config | ||
67 | %type <term> event_term | 69 | %type <term> event_term |
68 | %type <head> event_pmu | 70 | %type <head> event_pmu |
69 | %type <head> event_legacy_symbol | 71 | %type <head> event_legacy_symbol |
@@ -82,6 +84,9 @@ static inc_group_count(struct list_head *list, | |||
82 | %type <head> group_def | 84 | %type <head> group_def |
83 | %type <head> group | 85 | %type <head> group |
84 | %type <head> groups | 86 | %type <head> groups |
87 | %type <array> array | ||
88 | %type <array> array_term | ||
89 | %type <array> array_terms | ||
85 | 90 | ||
86 | %union | 91 | %union |
87 | { | 92 | { |
@@ -93,6 +98,7 @@ static inc_group_count(struct list_head *list, | |||
93 | char *sys; | 98 | char *sys; |
94 | char *event; | 99 | char *event; |
95 | } tracepoint_name; | 100 | } tracepoint_name; |
101 | struct parse_events_array array; | ||
96 | } | 102 | } |
97 | %% | 103 | %% |
98 | 104 | ||
@@ -211,24 +217,14 @@ event_def: event_pmu | | |||
211 | event_bpf_file | 217 | event_bpf_file |
212 | 218 | ||
213 | event_pmu: | 219 | event_pmu: |
214 | PE_NAME '/' event_config '/' | 220 | PE_NAME opt_event_config |
215 | { | 221 | { |
216 | struct parse_events_evlist *data = _data; | 222 | struct parse_events_evlist *data = _data; |
217 | struct list_head *list; | 223 | struct list_head *list; |
218 | 224 | ||
219 | ALLOC_LIST(list); | 225 | ALLOC_LIST(list); |
220 | ABORT_ON(parse_events_add_pmu(data, list, $1, $3)); | 226 | ABORT_ON(parse_events_add_pmu(data, list, $1, $2)); |
221 | parse_events__free_terms($3); | 227 | parse_events_terms__delete($2); |
222 | $$ = list; | ||
223 | } | ||
224 | | | ||
225 | PE_NAME '/' '/' | ||
226 | { | ||
227 | struct parse_events_evlist *data = _data; | ||
228 | struct list_head *list; | ||
229 | |||
230 | ALLOC_LIST(list); | ||
231 | ABORT_ON(parse_events_add_pmu(data, list, $1, NULL)); | ||
232 | $$ = list; | 228 | $$ = list; |
233 | } | 229 | } |
234 | | | 230 | | |
@@ -246,7 +242,7 @@ PE_KERNEL_PMU_EVENT sep_dc | |||
246 | 242 | ||
247 | ALLOC_LIST(list); | 243 | ALLOC_LIST(list); |
248 | ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); | 244 | ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); |
249 | parse_events__free_terms(head); | 245 | parse_events_terms__delete(head); |
250 | $$ = list; | 246 | $$ = list; |
251 | } | 247 | } |
252 | | | 248 | | |
@@ -266,7 +262,7 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc | |||
266 | 262 | ||
267 | ALLOC_LIST(list); | 263 | ALLOC_LIST(list); |
268 | ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); | 264 | ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); |
269 | parse_events__free_terms(head); | 265 | parse_events_terms__delete(head); |
270 | $$ = list; | 266 | $$ = list; |
271 | } | 267 | } |
272 | 268 | ||
@@ -285,7 +281,7 @@ value_sym '/' event_config '/' | |||
285 | 281 | ||
286 | ALLOC_LIST(list); | 282 | ALLOC_LIST(list); |
287 | ABORT_ON(parse_events_add_numeric(data, list, type, config, $3)); | 283 | ABORT_ON(parse_events_add_numeric(data, list, type, config, $3)); |
288 | parse_events__free_terms($3); | 284 | parse_events_terms__delete($3); |
289 | $$ = list; | 285 | $$ = list; |
290 | } | 286 | } |
291 | | | 287 | | |
@@ -302,33 +298,39 @@ value_sym sep_slash_dc | |||
302 | } | 298 | } |
303 | 299 | ||
304 | event_legacy_cache: | 300 | event_legacy_cache: |
305 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | 301 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config |
306 | { | 302 | { |
307 | struct parse_events_evlist *data = _data; | 303 | struct parse_events_evlist *data = _data; |
304 | struct parse_events_error *error = data->error; | ||
308 | struct list_head *list; | 305 | struct list_head *list; |
309 | 306 | ||
310 | ALLOC_LIST(list); | 307 | ALLOC_LIST(list); |
311 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5)); | 308 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5, error, $6)); |
309 | parse_events_terms__delete($6); | ||
312 | $$ = list; | 310 | $$ = list; |
313 | } | 311 | } |
314 | | | 312 | | |
315 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | 313 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config |
316 | { | 314 | { |
317 | struct parse_events_evlist *data = _data; | 315 | struct parse_events_evlist *data = _data; |
316 | struct parse_events_error *error = data->error; | ||
318 | struct list_head *list; | 317 | struct list_head *list; |
319 | 318 | ||
320 | ALLOC_LIST(list); | 319 | ALLOC_LIST(list); |
321 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL)); | 320 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL, error, $4)); |
321 | parse_events_terms__delete($4); | ||
322 | $$ = list; | 322 | $$ = list; |
323 | } | 323 | } |
324 | | | 324 | | |
325 | PE_NAME_CACHE_TYPE | 325 | PE_NAME_CACHE_TYPE opt_event_config |
326 | { | 326 | { |
327 | struct parse_events_evlist *data = _data; | 327 | struct parse_events_evlist *data = _data; |
328 | struct parse_events_error *error = data->error; | ||
328 | struct list_head *list; | 329 | struct list_head *list; |
329 | 330 | ||
330 | ALLOC_LIST(list); | 331 | ALLOC_LIST(list); |
331 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL)); | 332 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL, error, $2)); |
333 | parse_events_terms__delete($2); | ||
332 | $$ = list; | 334 | $$ = list; |
333 | } | 335 | } |
334 | 336 | ||
@@ -378,24 +380,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
378 | } | 380 | } |
379 | 381 | ||
380 | event_legacy_tracepoint: | 382 | event_legacy_tracepoint: |
381 | tracepoint_name | 383 | tracepoint_name opt_event_config |
382 | { | ||
383 | struct parse_events_evlist *data = _data; | ||
384 | struct parse_events_error *error = data->error; | ||
385 | struct list_head *list; | ||
386 | |||
387 | ALLOC_LIST(list); | ||
388 | if (error) | ||
389 | error->idx = @1.first_column; | ||
390 | |||
391 | if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event, | ||
392 | error, NULL)) | ||
393 | return -1; | ||
394 | |||
395 | $$ = list; | ||
396 | } | ||
397 | | | ||
398 | tracepoint_name '/' event_config '/' | ||
399 | { | 384 | { |
400 | struct parse_events_evlist *data = _data; | 385 | struct parse_events_evlist *data = _data; |
401 | struct parse_events_error *error = data->error; | 386 | struct parse_events_error *error = data->error; |
@@ -406,7 +391,7 @@ tracepoint_name '/' event_config '/' | |||
406 | error->idx = @1.first_column; | 391 | error->idx = @1.first_column; |
407 | 392 | ||
408 | if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event, | 393 | if (parse_events_add_tracepoint(list, &data->idx, $1.sys, $1.event, |
409 | error, $3)) | 394 | error, $2)) |
410 | return -1; | 395 | return -1; |
411 | 396 | ||
412 | $$ = list; | 397 | $$ = list; |
@@ -433,49 +418,68 @@ PE_NAME ':' PE_NAME | |||
433 | } | 418 | } |
434 | 419 | ||
435 | event_legacy_numeric: | 420 | event_legacy_numeric: |
436 | PE_VALUE ':' PE_VALUE | 421 | PE_VALUE ':' PE_VALUE opt_event_config |
437 | { | 422 | { |
438 | struct parse_events_evlist *data = _data; | 423 | struct parse_events_evlist *data = _data; |
439 | struct list_head *list; | 424 | struct list_head *list; |
440 | 425 | ||
441 | ALLOC_LIST(list); | 426 | ALLOC_LIST(list); |
442 | ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, NULL)); | 427 | ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, $4)); |
428 | parse_events_terms__delete($4); | ||
443 | $$ = list; | 429 | $$ = list; |
444 | } | 430 | } |
445 | 431 | ||
446 | event_legacy_raw: | 432 | event_legacy_raw: |
447 | PE_RAW | 433 | PE_RAW opt_event_config |
448 | { | 434 | { |
449 | struct parse_events_evlist *data = _data; | 435 | struct parse_events_evlist *data = _data; |
450 | struct list_head *list; | 436 | struct list_head *list; |
451 | 437 | ||
452 | ALLOC_LIST(list); | 438 | ALLOC_LIST(list); |
453 | ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, NULL)); | 439 | ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, $2)); |
440 | parse_events_terms__delete($2); | ||
454 | $$ = list; | 441 | $$ = list; |
455 | } | 442 | } |
456 | 443 | ||
457 | event_bpf_file: | 444 | event_bpf_file: |
458 | PE_BPF_OBJECT | 445 | PE_BPF_OBJECT opt_event_config |
459 | { | 446 | { |
460 | struct parse_events_evlist *data = _data; | 447 | struct parse_events_evlist *data = _data; |
461 | struct parse_events_error *error = data->error; | 448 | struct parse_events_error *error = data->error; |
462 | struct list_head *list; | 449 | struct list_head *list; |
463 | 450 | ||
464 | ALLOC_LIST(list); | 451 | ALLOC_LIST(list); |
465 | ABORT_ON(parse_events_load_bpf(data, list, $1, false)); | 452 | ABORT_ON(parse_events_load_bpf(data, list, $1, false, $2)); |
453 | parse_events_terms__delete($2); | ||
466 | $$ = list; | 454 | $$ = list; |
467 | } | 455 | } |
468 | | | 456 | | |
469 | PE_BPF_SOURCE | 457 | PE_BPF_SOURCE opt_event_config |
470 | { | 458 | { |
471 | struct parse_events_evlist *data = _data; | 459 | struct parse_events_evlist *data = _data; |
472 | struct list_head *list; | 460 | struct list_head *list; |
473 | 461 | ||
474 | ALLOC_LIST(list); | 462 | ALLOC_LIST(list); |
475 | ABORT_ON(parse_events_load_bpf(data, list, $1, true)); | 463 | ABORT_ON(parse_events_load_bpf(data, list, $1, true, $2)); |
464 | parse_events_terms__delete($2); | ||
476 | $$ = list; | 465 | $$ = list; |
477 | } | 466 | } |
478 | 467 | ||
468 | opt_event_config: | ||
469 | '/' event_config '/' | ||
470 | { | ||
471 | $$ = $2; | ||
472 | } | ||
473 | | | ||
474 | '/' '/' | ||
475 | { | ||
476 | $$ = NULL; | ||
477 | } | ||
478 | | | ||
479 | { | ||
480 | $$ = NULL; | ||
481 | } | ||
482 | |||
479 | start_terms: event_config | 483 | start_terms: event_config |
480 | { | 484 | { |
481 | struct parse_events_terms *data = _data; | 485 | struct parse_events_terms *data = _data; |
@@ -573,6 +577,86 @@ PE_TERM | |||
573 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL)); | 577 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL)); |
574 | $$ = term; | 578 | $$ = term; |
575 | } | 579 | } |
580 | | | ||
581 | PE_NAME array '=' PE_NAME | ||
582 | { | ||
583 | struct parse_events_term *term; | ||
584 | int i; | ||
585 | |||
586 | ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
587 | $1, $4, &@1, &@4)); | ||
588 | |||
589 | term->array = $2; | ||
590 | $$ = term; | ||
591 | } | ||
592 | | | ||
593 | PE_NAME array '=' PE_VALUE | ||
594 | { | ||
595 | struct parse_events_term *term; | ||
596 | |||
597 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
598 | $1, $4, &@1, &@4)); | ||
599 | term->array = $2; | ||
600 | $$ = term; | ||
601 | } | ||
602 | |||
603 | array: | ||
604 | '[' array_terms ']' | ||
605 | { | ||
606 | $$ = $2; | ||
607 | } | ||
608 | | | ||
609 | PE_ARRAY_ALL | ||
610 | { | ||
611 | $$.nr_ranges = 0; | ||
612 | $$.ranges = NULL; | ||
613 | } | ||
614 | |||
615 | array_terms: | ||
616 | array_terms ',' array_term | ||
617 | { | ||
618 | struct parse_events_array new_array; | ||
619 | |||
620 | new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges; | ||
621 | new_array.ranges = malloc(sizeof(new_array.ranges[0]) * | ||
622 | new_array.nr_ranges); | ||
623 | ABORT_ON(!new_array.ranges); | ||
624 | memcpy(&new_array.ranges[0], $1.ranges, | ||
625 | $1.nr_ranges * sizeof(new_array.ranges[0])); | ||
626 | memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges, | ||
627 | $3.nr_ranges * sizeof(new_array.ranges[0])); | ||
628 | free($1.ranges); | ||
629 | free($3.ranges); | ||
630 | $$ = new_array; | ||
631 | } | ||
632 | | | ||
633 | array_term | ||
634 | |||
635 | array_term: | ||
636 | PE_VALUE | ||
637 | { | ||
638 | struct parse_events_array array; | ||
639 | |||
640 | array.nr_ranges = 1; | ||
641 | array.ranges = malloc(sizeof(array.ranges[0])); | ||
642 | ABORT_ON(!array.ranges); | ||
643 | array.ranges[0].start = $1; | ||
644 | array.ranges[0].length = 1; | ||
645 | $$ = array; | ||
646 | } | ||
647 | | | ||
648 | PE_VALUE PE_ARRAY_RANGE PE_VALUE | ||
649 | { | ||
650 | struct parse_events_array array; | ||
651 | |||
652 | ABORT_ON($3 < $1); | ||
653 | array.nr_ranges = 1; | ||
654 | array.ranges = malloc(sizeof(array.ranges[0])); | ||
655 | ABORT_ON(!array.ranges); | ||
656 | array.ranges[0].start = $1; | ||
657 | array.ranges[0].length = $3 - $1 + 1; | ||
658 | $$ = array; | ||
659 | } | ||
576 | 660 | ||
577 | sep_dc: ':' | | 661 | sep_dc: ':' | |
578 | 662 | ||
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index 3654d964e49d..3bf6bf82ff2d 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c | |||
@@ -41,36 +41,6 @@ static char *cleanup_path(char *path) | |||
41 | return path; | 41 | return path; |
42 | } | 42 | } |
43 | 43 | ||
44 | static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args) | ||
45 | { | ||
46 | const char *perf_dir = get_perf_dir(); | ||
47 | size_t len; | ||
48 | |||
49 | len = strlen(perf_dir); | ||
50 | if (n < len + 1) | ||
51 | goto bad; | ||
52 | memcpy(buf, perf_dir, len); | ||
53 | if (len && !is_dir_sep(perf_dir[len-1])) | ||
54 | buf[len++] = '/'; | ||
55 | len += vsnprintf(buf + len, n - len, fmt, args); | ||
56 | if (len >= n) | ||
57 | goto bad; | ||
58 | return cleanup_path(buf); | ||
59 | bad: | ||
60 | strlcpy(buf, bad_path, n); | ||
61 | return buf; | ||
62 | } | ||
63 | |||
64 | char *perf_pathdup(const char *fmt, ...) | ||
65 | { | ||
66 | char path[PATH_MAX]; | ||
67 | va_list args; | ||
68 | va_start(args, fmt); | ||
69 | (void)perf_vsnpath(path, sizeof(path), fmt, args); | ||
70 | va_end(args); | ||
71 | return xstrdup(path); | ||
72 | } | ||
73 | |||
74 | char *mkpath(const char *fmt, ...) | 44 | char *mkpath(const char *fmt, ...) |
75 | { | 45 | { |
76 | va_list args; | 46 | va_list args; |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b597bcc8fc78..adef23b1352e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -98,7 +98,7 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * | |||
98 | char scale[128]; | 98 | char scale[128]; |
99 | int fd, ret = -1; | 99 | int fd, ret = -1; |
100 | char path[PATH_MAX]; | 100 | char path[PATH_MAX]; |
101 | const char *lc; | 101 | char *lc; |
102 | 102 | ||
103 | snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); | 103 | snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); |
104 | 104 | ||
@@ -124,6 +124,17 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * | |||
124 | lc = setlocale(LC_NUMERIC, NULL); | 124 | lc = setlocale(LC_NUMERIC, NULL); |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * The lc string may be allocated in static storage, | ||
128 | * so get a dynamic copy to make it survive setlocale | ||
129 | * call below. | ||
130 | */ | ||
131 | lc = strdup(lc); | ||
132 | if (!lc) { | ||
133 | ret = -ENOMEM; | ||
134 | goto error; | ||
135 | } | ||
136 | |||
137 | /* | ||
127 | * force to C locale to ensure kernel | 138 | * force to C locale to ensure kernel |
128 | * scale string is converted correctly. | 139 | * scale string is converted correctly. |
129 | * kernel uses default C locale. | 140 | * kernel uses default C locale. |
@@ -135,6 +146,8 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char * | |||
135 | /* restore locale */ | 146 | /* restore locale */ |
136 | setlocale(LC_NUMERIC, lc); | 147 | setlocale(LC_NUMERIC, lc); |
137 | 148 | ||
149 | free(lc); | ||
150 | |||
138 | ret = 0; | 151 | ret = 0; |
139 | error: | 152 | error: |
140 | close(fd); | 153 | close(fd); |
@@ -153,7 +166,7 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n | |||
153 | if (fd == -1) | 166 | if (fd == -1) |
154 | return -1; | 167 | return -1; |
155 | 168 | ||
156 | sret = read(fd, alias->unit, UNIT_MAX_LEN); | 169 | sret = read(fd, alias->unit, UNIT_MAX_LEN); |
157 | if (sret < 0) | 170 | if (sret < 0) |
158 | goto error; | 171 | goto error; |
159 | 172 | ||
@@ -284,13 +297,12 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
284 | { | 297 | { |
285 | struct dirent *evt_ent; | 298 | struct dirent *evt_ent; |
286 | DIR *event_dir; | 299 | DIR *event_dir; |
287 | int ret = 0; | ||
288 | 300 | ||
289 | event_dir = opendir(dir); | 301 | event_dir = opendir(dir); |
290 | if (!event_dir) | 302 | if (!event_dir) |
291 | return -EINVAL; | 303 | return -EINVAL; |
292 | 304 | ||
293 | while (!ret && (evt_ent = readdir(event_dir))) { | 305 | while ((evt_ent = readdir(event_dir))) { |
294 | char path[PATH_MAX]; | 306 | char path[PATH_MAX]; |
295 | char *name = evt_ent->d_name; | 307 | char *name = evt_ent->d_name; |
296 | FILE *file; | 308 | FILE *file; |
@@ -306,17 +318,19 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
306 | 318 | ||
307 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 319 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
308 | 320 | ||
309 | ret = -EINVAL; | ||
310 | file = fopen(path, "r"); | 321 | file = fopen(path, "r"); |
311 | if (!file) | 322 | if (!file) { |
312 | break; | 323 | pr_debug("Cannot open %s\n", path); |
324 | continue; | ||
325 | } | ||
313 | 326 | ||
314 | ret = perf_pmu__new_alias(head, dir, name, file); | 327 | if (perf_pmu__new_alias(head, dir, name, file) < 0) |
328 | pr_debug("Cannot set up %s\n", name); | ||
315 | fclose(file); | 329 | fclose(file); |
316 | } | 330 | } |
317 | 331 | ||
318 | closedir(event_dir); | 332 | closedir(event_dir); |
319 | return ret; | 333 | return 0; |
320 | } | 334 | } |
321 | 335 | ||
322 | /* | 336 | /* |
@@ -354,7 +368,7 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias, | |||
354 | list_for_each_entry(term, &alias->terms, list) { | 368 | list_for_each_entry(term, &alias->terms, list) { |
355 | ret = parse_events_term__clone(&cloned, term); | 369 | ret = parse_events_term__clone(&cloned, term); |
356 | if (ret) { | 370 | if (ret) { |
357 | parse_events__free_terms(&list); | 371 | parse_events_terms__purge(&list); |
358 | return ret; | 372 | return ret; |
359 | } | 373 | } |
360 | list_add_tail(&cloned->list, &list); | 374 | list_add_tail(&cloned->list, &list); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 93996ec4bbe3..8319fbb08636 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -2179,7 +2179,7 @@ static int perf_probe_event__sprintf(const char *group, const char *event, | |||
2179 | strbuf_addf(result, " in %s", module); | 2179 | strbuf_addf(result, " in %s", module); |
2180 | 2180 | ||
2181 | if (pev->nargs > 0) { | 2181 | if (pev->nargs > 0) { |
2182 | strbuf_addstr(result, " with"); | 2182 | strbuf_add(result, " with", 5); |
2183 | for (i = 0; i < pev->nargs; i++) { | 2183 | for (i = 0; i < pev->nargs; i++) { |
2184 | ret = synthesize_perf_probe_arg(&pev->args[i], | 2184 | ret = synthesize_perf_probe_arg(&pev->args[i], |
2185 | buf, 128); | 2185 | buf, 128); |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ba926c30f8cd..e54e7b011577 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -114,49 +114,44 @@ int init_probe_symbol_maps(bool user_only); | |||
114 | void exit_probe_symbol_maps(void); | 114 | void exit_probe_symbol_maps(void); |
115 | 115 | ||
116 | /* Command string to events */ | 116 | /* Command string to events */ |
117 | extern int parse_perf_probe_command(const char *cmd, | 117 | int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev); |
118 | struct perf_probe_event *pev); | 118 | int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev); |
119 | extern int parse_probe_trace_command(const char *cmd, | ||
120 | struct probe_trace_event *tev); | ||
121 | 119 | ||
122 | /* Events to command string */ | 120 | /* Events to command string */ |
123 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | 121 | char *synthesize_perf_probe_command(struct perf_probe_event *pev); |
124 | extern char *synthesize_probe_trace_command(struct probe_trace_event *tev); | 122 | char *synthesize_probe_trace_command(struct probe_trace_event *tev); |
125 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, | 123 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len); |
126 | size_t len); | ||
127 | 124 | ||
128 | /* Check the perf_probe_event needs debuginfo */ | 125 | /* Check the perf_probe_event needs debuginfo */ |
129 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); | 126 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); |
130 | 127 | ||
131 | /* Release event contents */ | 128 | /* Release event contents */ |
132 | extern void clear_perf_probe_event(struct perf_probe_event *pev); | 129 | void clear_perf_probe_event(struct perf_probe_event *pev); |
133 | extern void clear_probe_trace_event(struct probe_trace_event *tev); | 130 | void clear_probe_trace_event(struct probe_trace_event *tev); |
134 | 131 | ||
135 | /* Command string to line-range */ | 132 | /* Command string to line-range */ |
136 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); | 133 | int parse_line_range_desc(const char *cmd, struct line_range *lr); |
137 | 134 | ||
138 | /* Release line range members */ | 135 | /* Release line range members */ |
139 | extern void line_range__clear(struct line_range *lr); | 136 | void line_range__clear(struct line_range *lr); |
140 | 137 | ||
141 | /* Initialize line range */ | 138 | /* Initialize line range */ |
142 | extern int line_range__init(struct line_range *lr); | 139 | int line_range__init(struct line_range *lr); |
143 | 140 | ||
144 | extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); | 141 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); |
145 | extern int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs); | 142 | int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs); |
146 | extern int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs); | 143 | int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs); |
147 | extern void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs); | 144 | void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs); |
148 | extern int del_perf_probe_events(struct strfilter *filter); | 145 | int del_perf_probe_events(struct strfilter *filter); |
149 | 146 | ||
150 | extern int show_perf_probe_event(const char *group, const char *event, | 147 | int show_perf_probe_event(const char *group, const char *event, |
151 | struct perf_probe_event *pev, | 148 | struct perf_probe_event *pev, |
152 | const char *module, bool use_stdout); | 149 | const char *module, bool use_stdout); |
153 | extern int show_perf_probe_events(struct strfilter *filter); | 150 | int show_perf_probe_events(struct strfilter *filter); |
154 | extern int show_line_range(struct line_range *lr, const char *module, | 151 | int show_line_range(struct line_range *lr, const char *module, bool user); |
155 | bool user); | 152 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
156 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 153 | struct strfilter *filter); |
157 | struct strfilter *filter); | 154 | int show_available_funcs(const char *module, struct strfilter *filter, bool user); |
158 | extern int show_available_funcs(const char *module, struct strfilter *filter, | ||
159 | bool user); | ||
160 | bool arch__prefers_symtab(void); | 155 | bool arch__prefers_symtab(void); |
161 | void arch__fix_tev_from_maps(struct perf_probe_event *pev, | 156 | void arch__fix_tev_from_maps(struct perf_probe_event *pev, |
162 | struct probe_trace_event *tev, struct map *map); | 157 | struct probe_trace_event *tev, struct map *map); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4ce5c5e18f48..b3bd0fba0237 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -1314,18 +1314,18 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
1314 | if (probe_conf.show_location_range) { | 1314 | if (probe_conf.show_location_range) { |
1315 | if (!externs) { | 1315 | if (!externs) { |
1316 | if (ret) | 1316 | if (ret) |
1317 | strbuf_addf(&buf, "[INV]\t"); | 1317 | strbuf_add(&buf, "[INV]\t", 6); |
1318 | else | 1318 | else |
1319 | strbuf_addf(&buf, "[VAL]\t"); | 1319 | strbuf_add(&buf, "[VAL]\t", 6); |
1320 | } else | 1320 | } else |
1321 | strbuf_addf(&buf, "[EXT]\t"); | 1321 | strbuf_add(&buf, "[EXT]\t", 6); |
1322 | } | 1322 | } |
1323 | 1323 | ||
1324 | ret2 = die_get_varname(die_mem, &buf); | 1324 | ret2 = die_get_varname(die_mem, &buf); |
1325 | 1325 | ||
1326 | if (!ret2 && probe_conf.show_location_range && | 1326 | if (!ret2 && probe_conf.show_location_range && |
1327 | !externs) { | 1327 | !externs) { |
1328 | strbuf_addf(&buf, "\t"); | 1328 | strbuf_addch(&buf, '\t'); |
1329 | ret2 = die_get_var_range(&af->pf.sp_die, | 1329 | ret2 = die_get_var_range(&af->pf.sp_die, |
1330 | die_mem, &buf); | 1330 | die_mem, &buf); |
1331 | } | 1331 | } |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 0aec7704e395..51137fccb9c8 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -34,27 +34,25 @@ struct debuginfo { | |||
34 | }; | 34 | }; |
35 | 35 | ||
36 | /* This also tries to open distro debuginfo */ | 36 | /* This also tries to open distro debuginfo */ |
37 | extern struct debuginfo *debuginfo__new(const char *path); | 37 | struct debuginfo *debuginfo__new(const char *path); |
38 | extern void debuginfo__delete(struct debuginfo *dbg); | 38 | void debuginfo__delete(struct debuginfo *dbg); |
39 | 39 | ||
40 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 40 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
41 | extern int debuginfo__find_trace_events(struct debuginfo *dbg, | 41 | int debuginfo__find_trace_events(struct debuginfo *dbg, |
42 | struct perf_probe_event *pev, | 42 | struct perf_probe_event *pev, |
43 | struct probe_trace_event **tevs); | 43 | struct probe_trace_event **tevs); |
44 | 44 | ||
45 | /* Find a perf_probe_point from debuginfo */ | 45 | /* Find a perf_probe_point from debuginfo */ |
46 | extern int debuginfo__find_probe_point(struct debuginfo *dbg, | 46 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, |
47 | unsigned long addr, | 47 | struct perf_probe_point *ppt); |
48 | struct perf_probe_point *ppt); | ||
49 | 48 | ||
50 | /* Find a line range */ | 49 | /* Find a line range */ |
51 | extern int debuginfo__find_line_range(struct debuginfo *dbg, | 50 | int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr); |
52 | struct line_range *lr); | ||
53 | 51 | ||
54 | /* Find available variables */ | 52 | /* Find available variables */ |
55 | extern int debuginfo__find_available_vars_at(struct debuginfo *dbg, | 53 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, |
56 | struct perf_probe_event *pev, | 54 | struct perf_probe_event *pev, |
57 | struct variable_list **vls); | 55 | struct variable_list **vls); |
58 | 56 | ||
59 | /* Find a src file from a DWARF tag path */ | 57 | /* Find a src file from a DWARF tag path */ |
60 | int get_real_path(const char *raw_path, const char *comp_dir, | 58 | int get_real_path(const char *raw_path, const char *comp_dir, |
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index 172889ea234f..3340c9c4a6ca 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h | |||
@@ -24,6 +24,6 @@ | |||
24 | * sq_quote() in a real application. | 24 | * sq_quote() in a real application. |
25 | */ | 25 | */ |
26 | 26 | ||
27 | extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); | 27 | void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen); |
28 | 28 | ||
29 | #endif /* __PERF_QUOTE_H */ | 29 | #endif /* __PERF_QUOTE_H */ |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 544509c159ce..b3aabc0d4eb0 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -187,6 +187,9 @@ static void define_event_symbols(struct event_format *event, | |||
187 | const char *ev_name, | 187 | const char *ev_name, |
188 | struct print_arg *args) | 188 | struct print_arg *args) |
189 | { | 189 | { |
190 | if (args == NULL) | ||
191 | return; | ||
192 | |||
190 | switch (args->type) { | 193 | switch (args->type) { |
191 | case PRINT_NULL: | 194 | case PRINT_NULL: |
192 | break; | 195 | break; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index d72fafc1c800..fbd05242b4e5 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -205,6 +205,9 @@ static void define_event_symbols(struct event_format *event, | |||
205 | const char *ev_name, | 205 | const char *ev_name, |
206 | struct print_arg *args) | 206 | struct print_arg *args) |
207 | { | 207 | { |
208 | if (args == NULL) | ||
209 | return; | ||
210 | |||
208 | switch (args->type) { | 211 | switch (args->type) { |
209 | case PRINT_NULL: | 212 | case PRINT_NULL: |
210 | break; | 213 | break; |
@@ -1091,8 +1094,6 @@ static int python_start_script(const char *script, int argc, const char **argv) | |||
1091 | goto error; | 1094 | goto error; |
1092 | } | 1095 | } |
1093 | 1096 | ||
1094 | free(command_line); | ||
1095 | |||
1096 | set_table_handlers(tables); | 1097 | set_table_handlers(tables); |
1097 | 1098 | ||
1098 | if (tables->db_export_mode) { | 1099 | if (tables->db_export_mode) { |
@@ -1101,6 +1102,8 @@ static int python_start_script(const char *script, int argc, const char **argv) | |||
1101 | goto error; | 1102 | goto error; |
1102 | } | 1103 | } |
1103 | 1104 | ||
1105 | free(command_line); | ||
1106 | |||
1104 | return err; | 1107 | return err; |
1105 | error: | 1108 | error: |
1106 | Py_Finalize(); | 1109 | Py_Finalize(); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 40b7a0d0905b..4abd85c6346d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -240,14 +240,6 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused, | |||
240 | return 0; | 240 | return 0; |
241 | } | 241 | } |
242 | 242 | ||
243 | static int process_build_id_stub(struct perf_tool *tool __maybe_unused, | ||
244 | union perf_event *event __maybe_unused, | ||
245 | struct perf_session *session __maybe_unused) | ||
246 | { | ||
247 | dump_printf(": unhandled!\n"); | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, | 243 | static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, |
252 | union perf_event *event __maybe_unused, | 244 | union perf_event *event __maybe_unused, |
253 | struct ordered_events *oe __maybe_unused) | 245 | struct ordered_events *oe __maybe_unused) |
@@ -260,23 +252,6 @@ static int process_finished_round(struct perf_tool *tool, | |||
260 | union perf_event *event, | 252 | union perf_event *event, |
261 | struct ordered_events *oe); | 253 | struct ordered_events *oe); |
262 | 254 | ||
263 | static int process_id_index_stub(struct perf_tool *tool __maybe_unused, | ||
264 | union perf_event *event __maybe_unused, | ||
265 | struct perf_session *perf_session | ||
266 | __maybe_unused) | ||
267 | { | ||
268 | dump_printf(": unhandled!\n"); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused, | ||
273 | union perf_event *event __maybe_unused, | ||
274 | struct perf_session *session __maybe_unused) | ||
275 | { | ||
276 | dump_printf(": unhandled!\n"); | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int skipn(int fd, off_t n) | 255 | static int skipn(int fd, off_t n) |
281 | { | 256 | { |
282 | char buf[4096]; | 257 | char buf[4096]; |
@@ -303,10 +278,9 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, | |||
303 | return event->auxtrace.size; | 278 | return event->auxtrace.size; |
304 | } | 279 | } |
305 | 280 | ||
306 | static | 281 | static int process_event_op2_stub(struct perf_tool *tool __maybe_unused, |
307 | int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, | 282 | union perf_event *event __maybe_unused, |
308 | union perf_event *event __maybe_unused, | 283 | struct perf_session *session __maybe_unused) |
309 | struct perf_session *session __maybe_unused) | ||
310 | { | 284 | { |
311 | dump_printf(": unhandled!\n"); | 285 | dump_printf(": unhandled!\n"); |
312 | return 0; | 286 | return 0; |
@@ -410,7 +384,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
410 | if (tool->tracing_data == NULL) | 384 | if (tool->tracing_data == NULL) |
411 | tool->tracing_data = process_event_synth_tracing_data_stub; | 385 | tool->tracing_data = process_event_synth_tracing_data_stub; |
412 | if (tool->build_id == NULL) | 386 | if (tool->build_id == NULL) |
413 | tool->build_id = process_build_id_stub; | 387 | tool->build_id = process_event_op2_stub; |
414 | if (tool->finished_round == NULL) { | 388 | if (tool->finished_round == NULL) { |
415 | if (tool->ordered_events) | 389 | if (tool->ordered_events) |
416 | tool->finished_round = process_finished_round; | 390 | tool->finished_round = process_finished_round; |
@@ -418,13 +392,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
418 | tool->finished_round = process_finished_round_stub; | 392 | tool->finished_round = process_finished_round_stub; |
419 | } | 393 | } |
420 | if (tool->id_index == NULL) | 394 | if (tool->id_index == NULL) |
421 | tool->id_index = process_id_index_stub; | 395 | tool->id_index = process_event_op2_stub; |
422 | if (tool->auxtrace_info == NULL) | 396 | if (tool->auxtrace_info == NULL) |
423 | tool->auxtrace_info = process_event_auxtrace_info_stub; | 397 | tool->auxtrace_info = process_event_op2_stub; |
424 | if (tool->auxtrace == NULL) | 398 | if (tool->auxtrace == NULL) |
425 | tool->auxtrace = process_event_auxtrace_stub; | 399 | tool->auxtrace = process_event_auxtrace_stub; |
426 | if (tool->auxtrace_error == NULL) | 400 | if (tool->auxtrace_error == NULL) |
427 | tool->auxtrace_error = process_event_auxtrace_error_stub; | 401 | tool->auxtrace_error = process_event_op2_stub; |
428 | if (tool->thread_map == NULL) | 402 | if (tool->thread_map == NULL) |
429 | tool->thread_map = process_event_thread_map_stub; | 403 | tool->thread_map = process_event_thread_map_stub; |
430 | if (tool->cpu_map == NULL) | 404 | if (tool->cpu_map == NULL) |
@@ -1133,12 +1107,11 @@ static struct machine *machines__find_for_cpumode(struct machines *machines, | |||
1133 | union perf_event *event, | 1107 | union perf_event *event, |
1134 | struct perf_sample *sample) | 1108 | struct perf_sample *sample) |
1135 | { | 1109 | { |
1136 | const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1137 | struct machine *machine; | 1110 | struct machine *machine; |
1138 | 1111 | ||
1139 | if (perf_guest && | 1112 | if (perf_guest && |
1140 | ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || | 1113 | ((sample->cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || |
1141 | (cpumode == PERF_RECORD_MISC_GUEST_USER))) { | 1114 | (sample->cpumode == PERF_RECORD_MISC_GUEST_USER))) { |
1142 | u32 pid; | 1115 | u32 pid; |
1143 | 1116 | ||
1144 | if (event->header.type == PERF_RECORD_MMAP | 1117 | if (event->header.type == PERF_RECORD_MMAP |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 1833103768cb..c8680984d2d6 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -22,6 +22,7 @@ cflags = getenv('CFLAGS', '').split() | |||
22 | # switch off several checks (need to be at the end of cflags list) | 22 | # switch off several checks (need to be at the end of cflags list) |
23 | cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ] | 23 | cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ] |
24 | 24 | ||
25 | src_perf = getenv('srctree') + '/tools/perf' | ||
25 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 26 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
26 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 27 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
27 | libtraceevent = getenv('LIBTRACEEVENT') | 28 | libtraceevent = getenv('LIBTRACEEVENT') |
@@ -30,6 +31,9 @@ libapikfs = getenv('LIBAPI') | |||
30 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | 31 | ext_sources = [f.strip() for f in file('util/python-ext-sources') |
31 | if len(f.strip()) > 0 and f[0] != '#'] | 32 | if len(f.strip()) > 0 and f[0] != '#'] |
32 | 33 | ||
34 | # use full paths with source files | ||
35 | ext_sources = map(lambda x: '%s/%s' % (src_perf, x) , ext_sources) | ||
36 | |||
33 | perf = Extension('perf', | 37 | perf = Extension('perf', |
34 | sources = ext_sources, | 38 | sources = ext_sources, |
35 | include_dirs = ['util/include'], | 39 | include_dirs = ['util/include'], |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index ec722346e6ff..47966a1618c7 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "evsel.h" | 6 | #include "evsel.h" |
7 | #include "evlist.h" | 7 | #include "evlist.h" |
8 | #include <traceevent/event-parse.h> | 8 | #include <traceevent/event-parse.h> |
9 | #include "mem-events.h" | ||
9 | 10 | ||
10 | regex_t parent_regex; | 11 | regex_t parent_regex; |
11 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 12 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
@@ -25,9 +26,19 @@ int sort__has_parent = 0; | |||
25 | int sort__has_sym = 0; | 26 | int sort__has_sym = 0; |
26 | int sort__has_dso = 0; | 27 | int sort__has_dso = 0; |
27 | int sort__has_socket = 0; | 28 | int sort__has_socket = 0; |
29 | int sort__has_thread = 0; | ||
30 | int sort__has_comm = 0; | ||
28 | enum sort_mode sort__mode = SORT_MODE__NORMAL; | 31 | enum sort_mode sort__mode = SORT_MODE__NORMAL; |
29 | 32 | ||
30 | 33 | /* | |
34 | * Replaces all occurrences of a char used with the: | ||
35 | * | ||
36 | * -t, --field-separator | ||
37 | * | ||
38 | * option, that uses a special separator character and don't pad with spaces, | ||
39 | * replacing all occurances of this separator in symbol names (and other | ||
40 | * output) with a '.' character, that thus it's the only non valid separator. | ||
41 | */ | ||
31 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 42 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
32 | { | 43 | { |
33 | int n; | 44 | int n; |
@@ -80,10 +91,21 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, | |||
80 | width, width, comm ?: ""); | 91 | width, width, comm ?: ""); |
81 | } | 92 | } |
82 | 93 | ||
94 | static int hist_entry__thread_filter(struct hist_entry *he, int type, const void *arg) | ||
95 | { | ||
96 | const struct thread *th = arg; | ||
97 | |||
98 | if (type != HIST_FILTER__THREAD) | ||
99 | return -1; | ||
100 | |||
101 | return th && he->thread != th; | ||
102 | } | ||
103 | |||
83 | struct sort_entry sort_thread = { | 104 | struct sort_entry sort_thread = { |
84 | .se_header = " Pid:Command", | 105 | .se_header = " Pid:Command", |
85 | .se_cmp = sort__thread_cmp, | 106 | .se_cmp = sort__thread_cmp, |
86 | .se_snprintf = hist_entry__thread_snprintf, | 107 | .se_snprintf = hist_entry__thread_snprintf, |
108 | .se_filter = hist_entry__thread_filter, | ||
87 | .se_width_idx = HISTC_THREAD, | 109 | .se_width_idx = HISTC_THREAD, |
88 | }; | 110 | }; |
89 | 111 | ||
@@ -121,6 +143,7 @@ struct sort_entry sort_comm = { | |||
121 | .se_collapse = sort__comm_collapse, | 143 | .se_collapse = sort__comm_collapse, |
122 | .se_sort = sort__comm_sort, | 144 | .se_sort = sort__comm_sort, |
123 | .se_snprintf = hist_entry__comm_snprintf, | 145 | .se_snprintf = hist_entry__comm_snprintf, |
146 | .se_filter = hist_entry__thread_filter, | ||
124 | .se_width_idx = HISTC_COMM, | 147 | .se_width_idx = HISTC_COMM, |
125 | }; | 148 | }; |
126 | 149 | ||
@@ -170,10 +193,21 @@ static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, | |||
170 | return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); | 193 | return _hist_entry__dso_snprintf(he->ms.map, bf, size, width); |
171 | } | 194 | } |
172 | 195 | ||
196 | static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *arg) | ||
197 | { | ||
198 | const struct dso *dso = arg; | ||
199 | |||
200 | if (type != HIST_FILTER__DSO) | ||
201 | return -1; | ||
202 | |||
203 | return dso && (!he->ms.map || he->ms.map->dso != dso); | ||
204 | } | ||
205 | |||
173 | struct sort_entry sort_dso = { | 206 | struct sort_entry sort_dso = { |
174 | .se_header = "Shared Object", | 207 | .se_header = "Shared Object", |
175 | .se_cmp = sort__dso_cmp, | 208 | .se_cmp = sort__dso_cmp, |
176 | .se_snprintf = hist_entry__dso_snprintf, | 209 | .se_snprintf = hist_entry__dso_snprintf, |
210 | .se_filter = hist_entry__dso_filter, | ||
177 | .se_width_idx = HISTC_DSO, | 211 | .se_width_idx = HISTC_DSO, |
178 | }; | 212 | }; |
179 | 213 | ||
@@ -246,10 +280,8 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
246 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); | 280 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); |
247 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", | 281 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", |
248 | ip - map->unmap_ip(map, sym->start)); | 282 | ip - map->unmap_ip(map, sym->start)); |
249 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
250 | width - ret, ""); | ||
251 | } else { | 283 | } else { |
252 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | 284 | ret += repsep_snprintf(bf + ret, size - ret, "%.*s", |
253 | width - ret, | 285 | width - ret, |
254 | sym->name); | 286 | sym->name); |
255 | } | 287 | } |
@@ -257,14 +289,9 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
257 | size_t len = BITS_PER_LONG / 4; | 289 | size_t len = BITS_PER_LONG / 4; |
258 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", | 290 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", |
259 | len, ip); | 291 | len, ip); |
260 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
261 | width - ret, ""); | ||
262 | } | 292 | } |
263 | 293 | ||
264 | if (ret > width) | 294 | return ret; |
265 | bf[width] = '\0'; | ||
266 | |||
267 | return width; | ||
268 | } | 295 | } |
269 | 296 | ||
270 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | 297 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, |
@@ -274,46 +301,56 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | |||
274 | he->level, bf, size, width); | 301 | he->level, bf, size, width); |
275 | } | 302 | } |
276 | 303 | ||
304 | static int hist_entry__sym_filter(struct hist_entry *he, int type, const void *arg) | ||
305 | { | ||
306 | const char *sym = arg; | ||
307 | |||
308 | if (type != HIST_FILTER__SYMBOL) | ||
309 | return -1; | ||
310 | |||
311 | return sym && (!he->ms.sym || !strstr(he->ms.sym->name, sym)); | ||
312 | } | ||
313 | |||
277 | struct sort_entry sort_sym = { | 314 | struct sort_entry sort_sym = { |
278 | .se_header = "Symbol", | 315 | .se_header = "Symbol", |
279 | .se_cmp = sort__sym_cmp, | 316 | .se_cmp = sort__sym_cmp, |
280 | .se_sort = sort__sym_sort, | 317 | .se_sort = sort__sym_sort, |
281 | .se_snprintf = hist_entry__sym_snprintf, | 318 | .se_snprintf = hist_entry__sym_snprintf, |
319 | .se_filter = hist_entry__sym_filter, | ||
282 | .se_width_idx = HISTC_SYMBOL, | 320 | .se_width_idx = HISTC_SYMBOL, |
283 | }; | 321 | }; |
284 | 322 | ||
285 | /* --sort srcline */ | 323 | /* --sort srcline */ |
286 | 324 | ||
325 | static char *hist_entry__get_srcline(struct hist_entry *he) | ||
326 | { | ||
327 | struct map *map = he->ms.map; | ||
328 | |||
329 | if (!map) | ||
330 | return SRCLINE_UNKNOWN; | ||
331 | |||
332 | return get_srcline(map->dso, map__rip_2objdump(map, he->ip), | ||
333 | he->ms.sym, true); | ||
334 | } | ||
335 | |||
287 | static int64_t | 336 | static int64_t |
288 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | 337 | sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) |
289 | { | 338 | { |
290 | if (!left->srcline) { | 339 | if (!left->srcline) |
291 | if (!left->ms.map) | 340 | left->srcline = hist_entry__get_srcline(left); |
292 | left->srcline = SRCLINE_UNKNOWN; | 341 | if (!right->srcline) |
293 | else { | 342 | right->srcline = hist_entry__get_srcline(right); |
294 | struct map *map = left->ms.map; | 343 | |
295 | left->srcline = get_srcline(map->dso, | ||
296 | map__rip_2objdump(map, left->ip), | ||
297 | left->ms.sym, true); | ||
298 | } | ||
299 | } | ||
300 | if (!right->srcline) { | ||
301 | if (!right->ms.map) | ||
302 | right->srcline = SRCLINE_UNKNOWN; | ||
303 | else { | ||
304 | struct map *map = right->ms.map; | ||
305 | right->srcline = get_srcline(map->dso, | ||
306 | map__rip_2objdump(map, right->ip), | ||
307 | right->ms.sym, true); | ||
308 | } | ||
309 | } | ||
310 | return strcmp(right->srcline, left->srcline); | 344 | return strcmp(right->srcline, left->srcline); |
311 | } | 345 | } |
312 | 346 | ||
313 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 347 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
314 | size_t size, unsigned int width) | 348 | size_t size, unsigned int width) |
315 | { | 349 | { |
316 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline); | 350 | if (!he->srcline) |
351 | he->srcline = hist_entry__get_srcline(he); | ||
352 | |||
353 | return repsep_snprintf(bf, size, "%-.*s", width, he->srcline); | ||
317 | } | 354 | } |
318 | 355 | ||
319 | struct sort_entry sort_srcline = { | 356 | struct sort_entry sort_srcline = { |
@@ -327,11 +364,14 @@ struct sort_entry sort_srcline = { | |||
327 | 364 | ||
328 | static char no_srcfile[1]; | 365 | static char no_srcfile[1]; |
329 | 366 | ||
330 | static char *get_srcfile(struct hist_entry *e) | 367 | static char *hist_entry__get_srcfile(struct hist_entry *e) |
331 | { | 368 | { |
332 | char *sf, *p; | 369 | char *sf, *p; |
333 | struct map *map = e->ms.map; | 370 | struct map *map = e->ms.map; |
334 | 371 | ||
372 | if (!map) | ||
373 | return no_srcfile; | ||
374 | |||
335 | sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip), | 375 | sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip), |
336 | e->ms.sym, false, true); | 376 | e->ms.sym, false, true); |
337 | if (!strcmp(sf, SRCLINE_UNKNOWN)) | 377 | if (!strcmp(sf, SRCLINE_UNKNOWN)) |
@@ -348,25 +388,21 @@ static char *get_srcfile(struct hist_entry *e) | |||
348 | static int64_t | 388 | static int64_t |
349 | sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right) | 389 | sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right) |
350 | { | 390 | { |
351 | if (!left->srcfile) { | 391 | if (!left->srcfile) |
352 | if (!left->ms.map) | 392 | left->srcfile = hist_entry__get_srcfile(left); |
353 | left->srcfile = no_srcfile; | 393 | if (!right->srcfile) |
354 | else | 394 | right->srcfile = hist_entry__get_srcfile(right); |
355 | left->srcfile = get_srcfile(left); | 395 | |
356 | } | ||
357 | if (!right->srcfile) { | ||
358 | if (!right->ms.map) | ||
359 | right->srcfile = no_srcfile; | ||
360 | else | ||
361 | right->srcfile = get_srcfile(right); | ||
362 | } | ||
363 | return strcmp(right->srcfile, left->srcfile); | 396 | return strcmp(right->srcfile, left->srcfile); |
364 | } | 397 | } |
365 | 398 | ||
366 | static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf, | 399 | static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf, |
367 | size_t size, unsigned int width) | 400 | size_t size, unsigned int width) |
368 | { | 401 | { |
369 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcfile); | 402 | if (!he->srcfile) |
403 | he->srcfile = hist_entry__get_srcfile(he); | ||
404 | |||
405 | return repsep_snprintf(bf, size, "%-.*s", width, he->srcfile); | ||
370 | } | 406 | } |
371 | 407 | ||
372 | struct sort_entry sort_srcfile = { | 408 | struct sort_entry sort_srcfile = { |
@@ -439,10 +475,21 @@ static int hist_entry__socket_snprintf(struct hist_entry *he, char *bf, | |||
439 | return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket); | 475 | return repsep_snprintf(bf, size, "%*.*d", width, width-3, he->socket); |
440 | } | 476 | } |
441 | 477 | ||
478 | static int hist_entry__socket_filter(struct hist_entry *he, int type, const void *arg) | ||
479 | { | ||
480 | int sk = *(const int *)arg; | ||
481 | |||
482 | if (type != HIST_FILTER__SOCKET) | ||
483 | return -1; | ||
484 | |||
485 | return sk >= 0 && he->socket != sk; | ||
486 | } | ||
487 | |||
442 | struct sort_entry sort_socket = { | 488 | struct sort_entry sort_socket = { |
443 | .se_header = "Socket", | 489 | .se_header = "Socket", |
444 | .se_cmp = sort__socket_cmp, | 490 | .se_cmp = sort__socket_cmp, |
445 | .se_snprintf = hist_entry__socket_snprintf, | 491 | .se_snprintf = hist_entry__socket_snprintf, |
492 | .se_filter = hist_entry__socket_filter, | ||
446 | .se_width_idx = HISTC_SOCKET, | 493 | .se_width_idx = HISTC_SOCKET, |
447 | }; | 494 | }; |
448 | 495 | ||
@@ -483,9 +530,6 @@ sort__trace_cmp(struct hist_entry *left, struct hist_entry *right) | |||
483 | if (right->trace_output == NULL) | 530 | if (right->trace_output == NULL) |
484 | right->trace_output = get_trace_output(right); | 531 | right->trace_output = get_trace_output(right); |
485 | 532 | ||
486 | hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output)); | ||
487 | hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output)); | ||
488 | |||
489 | return strcmp(right->trace_output, left->trace_output); | 533 | return strcmp(right->trace_output, left->trace_output); |
490 | } | 534 | } |
491 | 535 | ||
@@ -496,11 +540,11 @@ static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, | |||
496 | 540 | ||
497 | evsel = hists_to_evsel(he->hists); | 541 | evsel = hists_to_evsel(he->hists); |
498 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 542 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
499 | return scnprintf(bf, size, "%-*.*s", width, width, "N/A"); | 543 | return scnprintf(bf, size, "%-.*s", width, "N/A"); |
500 | 544 | ||
501 | if (he->trace_output == NULL) | 545 | if (he->trace_output == NULL) |
502 | he->trace_output = get_trace_output(he); | 546 | he->trace_output = get_trace_output(he); |
503 | return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output); | 547 | return repsep_snprintf(bf, size, "%-.*s", width, he->trace_output); |
504 | } | 548 | } |
505 | 549 | ||
506 | struct sort_entry sort_trace = { | 550 | struct sort_entry sort_trace = { |
@@ -532,6 +576,18 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, | |||
532 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | 576 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); |
533 | } | 577 | } |
534 | 578 | ||
579 | static int hist_entry__dso_from_filter(struct hist_entry *he, int type, | ||
580 | const void *arg) | ||
581 | { | ||
582 | const struct dso *dso = arg; | ||
583 | |||
584 | if (type != HIST_FILTER__DSO) | ||
585 | return -1; | ||
586 | |||
587 | return dso && (!he->branch_info || !he->branch_info->from.map || | ||
588 | he->branch_info->from.map->dso != dso); | ||
589 | } | ||
590 | |||
535 | static int64_t | 591 | static int64_t |
536 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 592 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
537 | { | 593 | { |
@@ -552,6 +608,18 @@ static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, | |||
552 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | 608 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); |
553 | } | 609 | } |
554 | 610 | ||
611 | static int hist_entry__dso_to_filter(struct hist_entry *he, int type, | ||
612 | const void *arg) | ||
613 | { | ||
614 | const struct dso *dso = arg; | ||
615 | |||
616 | if (type != HIST_FILTER__DSO) | ||
617 | return -1; | ||
618 | |||
619 | return dso && (!he->branch_info || !he->branch_info->to.map || | ||
620 | he->branch_info->to.map->dso != dso); | ||
621 | } | ||
622 | |||
555 | static int64_t | 623 | static int64_t |
556 | sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | 624 | sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) |
557 | { | 625 | { |
@@ -613,10 +681,35 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, | |||
613 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | 681 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); |
614 | } | 682 | } |
615 | 683 | ||
684 | static int hist_entry__sym_from_filter(struct hist_entry *he, int type, | ||
685 | const void *arg) | ||
686 | { | ||
687 | const char *sym = arg; | ||
688 | |||
689 | if (type != HIST_FILTER__SYMBOL) | ||
690 | return -1; | ||
691 | |||
692 | return sym && !(he->branch_info && he->branch_info->from.sym && | ||
693 | strstr(he->branch_info->from.sym->name, sym)); | ||
694 | } | ||
695 | |||
696 | static int hist_entry__sym_to_filter(struct hist_entry *he, int type, | ||
697 | const void *arg) | ||
698 | { | ||
699 | const char *sym = arg; | ||
700 | |||
701 | if (type != HIST_FILTER__SYMBOL) | ||
702 | return -1; | ||
703 | |||
704 | return sym && !(he->branch_info && he->branch_info->to.sym && | ||
705 | strstr(he->branch_info->to.sym->name, sym)); | ||
706 | } | ||
707 | |||
616 | struct sort_entry sort_dso_from = { | 708 | struct sort_entry sort_dso_from = { |
617 | .se_header = "Source Shared Object", | 709 | .se_header = "Source Shared Object", |
618 | .se_cmp = sort__dso_from_cmp, | 710 | .se_cmp = sort__dso_from_cmp, |
619 | .se_snprintf = hist_entry__dso_from_snprintf, | 711 | .se_snprintf = hist_entry__dso_from_snprintf, |
712 | .se_filter = hist_entry__dso_from_filter, | ||
620 | .se_width_idx = HISTC_DSO_FROM, | 713 | .se_width_idx = HISTC_DSO_FROM, |
621 | }; | 714 | }; |
622 | 715 | ||
@@ -624,6 +717,7 @@ struct sort_entry sort_dso_to = { | |||
624 | .se_header = "Target Shared Object", | 717 | .se_header = "Target Shared Object", |
625 | .se_cmp = sort__dso_to_cmp, | 718 | .se_cmp = sort__dso_to_cmp, |
626 | .se_snprintf = hist_entry__dso_to_snprintf, | 719 | .se_snprintf = hist_entry__dso_to_snprintf, |
720 | .se_filter = hist_entry__dso_to_filter, | ||
627 | .se_width_idx = HISTC_DSO_TO, | 721 | .se_width_idx = HISTC_DSO_TO, |
628 | }; | 722 | }; |
629 | 723 | ||
@@ -631,6 +725,7 @@ struct sort_entry sort_sym_from = { | |||
631 | .se_header = "Source Symbol", | 725 | .se_header = "Source Symbol", |
632 | .se_cmp = sort__sym_from_cmp, | 726 | .se_cmp = sort__sym_from_cmp, |
633 | .se_snprintf = hist_entry__sym_from_snprintf, | 727 | .se_snprintf = hist_entry__sym_from_snprintf, |
728 | .se_filter = hist_entry__sym_from_filter, | ||
634 | .se_width_idx = HISTC_SYMBOL_FROM, | 729 | .se_width_idx = HISTC_SYMBOL_FROM, |
635 | }; | 730 | }; |
636 | 731 | ||
@@ -638,6 +733,7 @@ struct sort_entry sort_sym_to = { | |||
638 | .se_header = "Target Symbol", | 733 | .se_header = "Target Symbol", |
639 | .se_cmp = sort__sym_to_cmp, | 734 | .se_cmp = sort__sym_to_cmp, |
640 | .se_snprintf = hist_entry__sym_to_snprintf, | 735 | .se_snprintf = hist_entry__sym_to_snprintf, |
736 | .se_filter = hist_entry__sym_to_filter, | ||
641 | .se_width_idx = HISTC_SYMBOL_TO, | 737 | .se_width_idx = HISTC_SYMBOL_TO, |
642 | }; | 738 | }; |
643 | 739 | ||
@@ -797,20 +893,10 @@ sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) | |||
797 | static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, | 893 | static int hist_entry__locked_snprintf(struct hist_entry *he, char *bf, |
798 | size_t size, unsigned int width) | 894 | size_t size, unsigned int width) |
799 | { | 895 | { |
800 | const char *out; | 896 | char out[10]; |
801 | u64 mask = PERF_MEM_LOCK_NA; | ||
802 | 897 | ||
803 | if (he->mem_info) | 898 | perf_mem__lck_scnprintf(out, sizeof(out), he->mem_info); |
804 | mask = he->mem_info->data_src.mem_lock; | 899 | return repsep_snprintf(bf, size, "%.*s", width, out); |
805 | |||
806 | if (mask & PERF_MEM_LOCK_NA) | ||
807 | out = "N/A"; | ||
808 | else if (mask & PERF_MEM_LOCK_LOCKED) | ||
809 | out = "Yes"; | ||
810 | else | ||
811 | out = "No"; | ||
812 | |||
813 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
814 | } | 900 | } |
815 | 901 | ||
816 | static int64_t | 902 | static int64_t |
@@ -832,54 +918,12 @@ sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) | |||
832 | return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); | 918 | return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); |
833 | } | 919 | } |
834 | 920 | ||
835 | static const char * const tlb_access[] = { | ||
836 | "N/A", | ||
837 | "HIT", | ||
838 | "MISS", | ||
839 | "L1", | ||
840 | "L2", | ||
841 | "Walker", | ||
842 | "Fault", | ||
843 | }; | ||
844 | #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) | ||
845 | |||
846 | static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, | 921 | static int hist_entry__tlb_snprintf(struct hist_entry *he, char *bf, |
847 | size_t size, unsigned int width) | 922 | size_t size, unsigned int width) |
848 | { | 923 | { |
849 | char out[64]; | 924 | char out[64]; |
850 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
851 | size_t l = 0, i; | ||
852 | u64 m = PERF_MEM_TLB_NA; | ||
853 | u64 hit, miss; | ||
854 | |||
855 | out[0] = '\0'; | ||
856 | |||
857 | if (he->mem_info) | ||
858 | m = he->mem_info->data_src.mem_dtlb; | ||
859 | |||
860 | hit = m & PERF_MEM_TLB_HIT; | ||
861 | miss = m & PERF_MEM_TLB_MISS; | ||
862 | |||
863 | /* already taken care of */ | ||
864 | m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); | ||
865 | |||
866 | for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { | ||
867 | if (!(m & 0x1)) | ||
868 | continue; | ||
869 | if (l) { | ||
870 | strcat(out, " or "); | ||
871 | l += 4; | ||
872 | } | ||
873 | strncat(out, tlb_access[i], sz - l); | ||
874 | l += strlen(tlb_access[i]); | ||
875 | } | ||
876 | if (*out == '\0') | ||
877 | strcpy(out, "N/A"); | ||
878 | if (hit) | ||
879 | strncat(out, " hit", sz - l); | ||
880 | if (miss) | ||
881 | strncat(out, " miss", sz - l); | ||
882 | 925 | ||
926 | perf_mem__tlb_scnprintf(out, sizeof(out), he->mem_info); | ||
883 | return repsep_snprintf(bf, size, "%-*s", width, out); | 927 | return repsep_snprintf(bf, size, "%-*s", width, out); |
884 | } | 928 | } |
885 | 929 | ||
@@ -902,61 +946,12 @@ sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) | |||
902 | return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); | 946 | return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); |
903 | } | 947 | } |
904 | 948 | ||
905 | static const char * const mem_lvl[] = { | ||
906 | "N/A", | ||
907 | "HIT", | ||
908 | "MISS", | ||
909 | "L1", | ||
910 | "LFB", | ||
911 | "L2", | ||
912 | "L3", | ||
913 | "Local RAM", | ||
914 | "Remote RAM (1 hop)", | ||
915 | "Remote RAM (2 hops)", | ||
916 | "Remote Cache (1 hop)", | ||
917 | "Remote Cache (2 hops)", | ||
918 | "I/O", | ||
919 | "Uncached", | ||
920 | }; | ||
921 | #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) | ||
922 | |||
923 | static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, | 949 | static int hist_entry__lvl_snprintf(struct hist_entry *he, char *bf, |
924 | size_t size, unsigned int width) | 950 | size_t size, unsigned int width) |
925 | { | 951 | { |
926 | char out[64]; | 952 | char out[64]; |
927 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
928 | size_t i, l = 0; | ||
929 | u64 m = PERF_MEM_LVL_NA; | ||
930 | u64 hit, miss; | ||
931 | |||
932 | if (he->mem_info) | ||
933 | m = he->mem_info->data_src.mem_lvl; | ||
934 | |||
935 | out[0] = '\0'; | ||
936 | |||
937 | hit = m & PERF_MEM_LVL_HIT; | ||
938 | miss = m & PERF_MEM_LVL_MISS; | ||
939 | |||
940 | /* already taken care of */ | ||
941 | m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); | ||
942 | |||
943 | for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { | ||
944 | if (!(m & 0x1)) | ||
945 | continue; | ||
946 | if (l) { | ||
947 | strcat(out, " or "); | ||
948 | l += 4; | ||
949 | } | ||
950 | strncat(out, mem_lvl[i], sz - l); | ||
951 | l += strlen(mem_lvl[i]); | ||
952 | } | ||
953 | if (*out == '\0') | ||
954 | strcpy(out, "N/A"); | ||
955 | if (hit) | ||
956 | strncat(out, " hit", sz - l); | ||
957 | if (miss) | ||
958 | strncat(out, " miss", sz - l); | ||
959 | 953 | ||
954 | perf_mem__lvl_scnprintf(out, sizeof(out), he->mem_info); | ||
960 | return repsep_snprintf(bf, size, "%-*s", width, out); | 955 | return repsep_snprintf(bf, size, "%-*s", width, out); |
961 | } | 956 | } |
962 | 957 | ||
@@ -979,51 +974,15 @@ sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) | |||
979 | return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); | 974 | return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); |
980 | } | 975 | } |
981 | 976 | ||
982 | static const char * const snoop_access[] = { | ||
983 | "N/A", | ||
984 | "None", | ||
985 | "Miss", | ||
986 | "Hit", | ||
987 | "HitM", | ||
988 | }; | ||
989 | #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) | ||
990 | |||
991 | static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, | 977 | static int hist_entry__snoop_snprintf(struct hist_entry *he, char *bf, |
992 | size_t size, unsigned int width) | 978 | size_t size, unsigned int width) |
993 | { | 979 | { |
994 | char out[64]; | 980 | char out[64]; |
995 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
996 | size_t i, l = 0; | ||
997 | u64 m = PERF_MEM_SNOOP_NA; | ||
998 | |||
999 | out[0] = '\0'; | ||
1000 | |||
1001 | if (he->mem_info) | ||
1002 | m = he->mem_info->data_src.mem_snoop; | ||
1003 | |||
1004 | for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { | ||
1005 | if (!(m & 0x1)) | ||
1006 | continue; | ||
1007 | if (l) { | ||
1008 | strcat(out, " or "); | ||
1009 | l += 4; | ||
1010 | } | ||
1011 | strncat(out, snoop_access[i], sz - l); | ||
1012 | l += strlen(snoop_access[i]); | ||
1013 | } | ||
1014 | |||
1015 | if (*out == '\0') | ||
1016 | strcpy(out, "N/A"); | ||
1017 | 981 | ||
982 | perf_mem__snp_scnprintf(out, sizeof(out), he->mem_info); | ||
1018 | return repsep_snprintf(bf, size, "%-*s", width, out); | 983 | return repsep_snprintf(bf, size, "%-*s", width, out); |
1019 | } | 984 | } |
1020 | 985 | ||
1021 | static inline u64 cl_address(u64 address) | ||
1022 | { | ||
1023 | /* return the cacheline of the address */ | ||
1024 | return (address & ~(cacheline_size - 1)); | ||
1025 | } | ||
1026 | |||
1027 | static int64_t | 986 | static int64_t |
1028 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) | 987 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right) |
1029 | { | 988 | { |
@@ -1440,20 +1399,6 @@ struct hpp_sort_entry { | |||
1440 | struct sort_entry *se; | 1399 | struct sort_entry *se; |
1441 | }; | 1400 | }; |
1442 | 1401 | ||
1443 | bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
1444 | { | ||
1445 | struct hpp_sort_entry *hse_a; | ||
1446 | struct hpp_sort_entry *hse_b; | ||
1447 | |||
1448 | if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) | ||
1449 | return false; | ||
1450 | |||
1451 | hse_a = container_of(a, struct hpp_sort_entry, hpp); | ||
1452 | hse_b = container_of(b, struct hpp_sort_entry, hpp); | ||
1453 | |||
1454 | return hse_a->se == hse_b->se; | ||
1455 | } | ||
1456 | |||
1457 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 1402 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) |
1458 | { | 1403 | { |
1459 | struct hpp_sort_entry *hse; | 1404 | struct hpp_sort_entry *hse; |
@@ -1539,8 +1484,56 @@ static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt, | |||
1539 | return sort_fn(a, b); | 1484 | return sort_fn(a, b); |
1540 | } | 1485 | } |
1541 | 1486 | ||
1487 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) | ||
1488 | { | ||
1489 | return format->header == __sort__hpp_header; | ||
1490 | } | ||
1491 | |||
1492 | #define MK_SORT_ENTRY_CHK(key) \ | ||
1493 | bool perf_hpp__is_ ## key ## _entry(struct perf_hpp_fmt *fmt) \ | ||
1494 | { \ | ||
1495 | struct hpp_sort_entry *hse; \ | ||
1496 | \ | ||
1497 | if (!perf_hpp__is_sort_entry(fmt)) \ | ||
1498 | return false; \ | ||
1499 | \ | ||
1500 | hse = container_of(fmt, struct hpp_sort_entry, hpp); \ | ||
1501 | return hse->se == &sort_ ## key ; \ | ||
1502 | } | ||
1503 | |||
1504 | MK_SORT_ENTRY_CHK(trace) | ||
1505 | MK_SORT_ENTRY_CHK(srcline) | ||
1506 | MK_SORT_ENTRY_CHK(srcfile) | ||
1507 | MK_SORT_ENTRY_CHK(thread) | ||
1508 | MK_SORT_ENTRY_CHK(comm) | ||
1509 | MK_SORT_ENTRY_CHK(dso) | ||
1510 | MK_SORT_ENTRY_CHK(sym) | ||
1511 | |||
1512 | |||
1513 | static bool __sort__hpp_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
1514 | { | ||
1515 | struct hpp_sort_entry *hse_a; | ||
1516 | struct hpp_sort_entry *hse_b; | ||
1517 | |||
1518 | if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) | ||
1519 | return false; | ||
1520 | |||
1521 | hse_a = container_of(a, struct hpp_sort_entry, hpp); | ||
1522 | hse_b = container_of(b, struct hpp_sort_entry, hpp); | ||
1523 | |||
1524 | return hse_a->se == hse_b->se; | ||
1525 | } | ||
1526 | |||
1527 | static void hse_free(struct perf_hpp_fmt *fmt) | ||
1528 | { | ||
1529 | struct hpp_sort_entry *hse; | ||
1530 | |||
1531 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1532 | free(hse); | ||
1533 | } | ||
1534 | |||
1542 | static struct hpp_sort_entry * | 1535 | static struct hpp_sort_entry * |
1543 | __sort_dimension__alloc_hpp(struct sort_dimension *sd) | 1536 | __sort_dimension__alloc_hpp(struct sort_dimension *sd, int level) |
1544 | { | 1537 | { |
1545 | struct hpp_sort_entry *hse; | 1538 | struct hpp_sort_entry *hse; |
1546 | 1539 | ||
@@ -1560,40 +1553,92 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
1560 | hse->hpp.cmp = __sort__hpp_cmp; | 1553 | hse->hpp.cmp = __sort__hpp_cmp; |
1561 | hse->hpp.collapse = __sort__hpp_collapse; | 1554 | hse->hpp.collapse = __sort__hpp_collapse; |
1562 | hse->hpp.sort = __sort__hpp_sort; | 1555 | hse->hpp.sort = __sort__hpp_sort; |
1556 | hse->hpp.equal = __sort__hpp_equal; | ||
1557 | hse->hpp.free = hse_free; | ||
1563 | 1558 | ||
1564 | INIT_LIST_HEAD(&hse->hpp.list); | 1559 | INIT_LIST_HEAD(&hse->hpp.list); |
1565 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1560 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
1566 | hse->hpp.elide = false; | 1561 | hse->hpp.elide = false; |
1567 | hse->hpp.len = 0; | 1562 | hse->hpp.len = 0; |
1568 | hse->hpp.user_len = 0; | 1563 | hse->hpp.user_len = 0; |
1564 | hse->hpp.level = level; | ||
1569 | 1565 | ||
1570 | return hse; | 1566 | return hse; |
1571 | } | 1567 | } |
1572 | 1568 | ||
1573 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) | 1569 | static void hpp_free(struct perf_hpp_fmt *fmt) |
1574 | { | 1570 | { |
1575 | return format->header == __sort__hpp_header; | 1571 | free(fmt); |
1572 | } | ||
1573 | |||
1574 | static struct perf_hpp_fmt *__hpp_dimension__alloc_hpp(struct hpp_dimension *hd, | ||
1575 | int level) | ||
1576 | { | ||
1577 | struct perf_hpp_fmt *fmt; | ||
1578 | |||
1579 | fmt = memdup(hd->fmt, sizeof(*fmt)); | ||
1580 | if (fmt) { | ||
1581 | INIT_LIST_HEAD(&fmt->list); | ||
1582 | INIT_LIST_HEAD(&fmt->sort_list); | ||
1583 | fmt->free = hpp_free; | ||
1584 | fmt->level = level; | ||
1585 | } | ||
1586 | |||
1587 | return fmt; | ||
1588 | } | ||
1589 | |||
1590 | int hist_entry__filter(struct hist_entry *he, int type, const void *arg) | ||
1591 | { | ||
1592 | struct perf_hpp_fmt *fmt; | ||
1593 | struct hpp_sort_entry *hse; | ||
1594 | int ret = -1; | ||
1595 | int r; | ||
1596 | |||
1597 | perf_hpp_list__for_each_format(he->hpp_list, fmt) { | ||
1598 | if (!perf_hpp__is_sort_entry(fmt)) | ||
1599 | continue; | ||
1600 | |||
1601 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1602 | if (hse->se->se_filter == NULL) | ||
1603 | continue; | ||
1604 | |||
1605 | /* | ||
1606 | * hist entry is filtered if any of sort key in the hpp list | ||
1607 | * is applied. But it should skip non-matched filter types. | ||
1608 | */ | ||
1609 | r = hse->se->se_filter(he, type, arg); | ||
1610 | if (r >= 0) { | ||
1611 | if (ret < 0) | ||
1612 | ret = 0; | ||
1613 | ret |= r; | ||
1614 | } | ||
1615 | } | ||
1616 | |||
1617 | return ret; | ||
1576 | } | 1618 | } |
1577 | 1619 | ||
1578 | static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) | 1620 | static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd, |
1621 | struct perf_hpp_list *list, | ||
1622 | int level) | ||
1579 | { | 1623 | { |
1580 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); | 1624 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, level); |
1581 | 1625 | ||
1582 | if (hse == NULL) | 1626 | if (hse == NULL) |
1583 | return -1; | 1627 | return -1; |
1584 | 1628 | ||
1585 | perf_hpp__register_sort_field(&hse->hpp); | 1629 | perf_hpp_list__register_sort_field(list, &hse->hpp); |
1586 | return 0; | 1630 | return 0; |
1587 | } | 1631 | } |
1588 | 1632 | ||
1589 | static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) | 1633 | static int __sort_dimension__add_hpp_output(struct sort_dimension *sd, |
1634 | struct perf_hpp_list *list) | ||
1590 | { | 1635 | { |
1591 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); | 1636 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd, 0); |
1592 | 1637 | ||
1593 | if (hse == NULL) | 1638 | if (hse == NULL) |
1594 | return -1; | 1639 | return -1; |
1595 | 1640 | ||
1596 | perf_hpp__column_register(&hse->hpp); | 1641 | perf_hpp_list__column_register(list, &hse->hpp); |
1597 | return 0; | 1642 | return 0; |
1598 | } | 1643 | } |
1599 | 1644 | ||
@@ -1727,6 +1772,9 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | |||
1727 | if (hde->raw_trace) | 1772 | if (hde->raw_trace) |
1728 | goto raw_field; | 1773 | goto raw_field; |
1729 | 1774 | ||
1775 | if (!he->trace_output) | ||
1776 | he->trace_output = get_trace_output(he); | ||
1777 | |||
1730 | field = hde->field; | 1778 | field = hde->field; |
1731 | namelen = strlen(field->name); | 1779 | namelen = strlen(field->name); |
1732 | str = he->trace_output; | 1780 | str = he->trace_output; |
@@ -1776,6 +1824,11 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, | |||
1776 | 1824 | ||
1777 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | 1825 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); |
1778 | 1826 | ||
1827 | if (b == NULL) { | ||
1828 | update_dynamic_len(hde, a); | ||
1829 | return 0; | ||
1830 | } | ||
1831 | |||
1779 | field = hde->field; | 1832 | field = hde->field; |
1780 | if (field->flags & FIELD_IS_DYNAMIC) { | 1833 | if (field->flags & FIELD_IS_DYNAMIC) { |
1781 | unsigned long long dyn; | 1834 | unsigned long long dyn; |
@@ -1790,9 +1843,6 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, | |||
1790 | } else { | 1843 | } else { |
1791 | offset = field->offset; | 1844 | offset = field->offset; |
1792 | size = field->size; | 1845 | size = field->size; |
1793 | |||
1794 | update_dynamic_len(hde, a); | ||
1795 | update_dynamic_len(hde, b); | ||
1796 | } | 1846 | } |
1797 | 1847 | ||
1798 | return memcmp(a->raw_data + offset, b->raw_data + offset, size); | 1848 | return memcmp(a->raw_data + offset, b->raw_data + offset, size); |
@@ -1803,8 +1853,31 @@ bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt) | |||
1803 | return fmt->cmp == __sort__hde_cmp; | 1853 | return fmt->cmp == __sort__hde_cmp; |
1804 | } | 1854 | } |
1805 | 1855 | ||
1856 | static bool __sort__hde_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
1857 | { | ||
1858 | struct hpp_dynamic_entry *hde_a; | ||
1859 | struct hpp_dynamic_entry *hde_b; | ||
1860 | |||
1861 | if (!perf_hpp__is_dynamic_entry(a) || !perf_hpp__is_dynamic_entry(b)) | ||
1862 | return false; | ||
1863 | |||
1864 | hde_a = container_of(a, struct hpp_dynamic_entry, hpp); | ||
1865 | hde_b = container_of(b, struct hpp_dynamic_entry, hpp); | ||
1866 | |||
1867 | return hde_a->field == hde_b->field; | ||
1868 | } | ||
1869 | |||
1870 | static void hde_free(struct perf_hpp_fmt *fmt) | ||
1871 | { | ||
1872 | struct hpp_dynamic_entry *hde; | ||
1873 | |||
1874 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1875 | free(hde); | ||
1876 | } | ||
1877 | |||
1806 | static struct hpp_dynamic_entry * | 1878 | static struct hpp_dynamic_entry * |
1807 | __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) | 1879 | __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field, |
1880 | int level) | ||
1808 | { | 1881 | { |
1809 | struct hpp_dynamic_entry *hde; | 1882 | struct hpp_dynamic_entry *hde; |
1810 | 1883 | ||
@@ -1827,16 +1900,47 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) | |||
1827 | hde->hpp.cmp = __sort__hde_cmp; | 1900 | hde->hpp.cmp = __sort__hde_cmp; |
1828 | hde->hpp.collapse = __sort__hde_cmp; | 1901 | hde->hpp.collapse = __sort__hde_cmp; |
1829 | hde->hpp.sort = __sort__hde_cmp; | 1902 | hde->hpp.sort = __sort__hde_cmp; |
1903 | hde->hpp.equal = __sort__hde_equal; | ||
1904 | hde->hpp.free = hde_free; | ||
1830 | 1905 | ||
1831 | INIT_LIST_HEAD(&hde->hpp.list); | 1906 | INIT_LIST_HEAD(&hde->hpp.list); |
1832 | INIT_LIST_HEAD(&hde->hpp.sort_list); | 1907 | INIT_LIST_HEAD(&hde->hpp.sort_list); |
1833 | hde->hpp.elide = false; | 1908 | hde->hpp.elide = false; |
1834 | hde->hpp.len = 0; | 1909 | hde->hpp.len = 0; |
1835 | hde->hpp.user_len = 0; | 1910 | hde->hpp.user_len = 0; |
1911 | hde->hpp.level = level; | ||
1836 | 1912 | ||
1837 | return hde; | 1913 | return hde; |
1838 | } | 1914 | } |
1839 | 1915 | ||
1916 | struct perf_hpp_fmt *perf_hpp_fmt__dup(struct perf_hpp_fmt *fmt) | ||
1917 | { | ||
1918 | struct perf_hpp_fmt *new_fmt = NULL; | ||
1919 | |||
1920 | if (perf_hpp__is_sort_entry(fmt)) { | ||
1921 | struct hpp_sort_entry *hse, *new_hse; | ||
1922 | |||
1923 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1924 | new_hse = memdup(hse, sizeof(*hse)); | ||
1925 | if (new_hse) | ||
1926 | new_fmt = &new_hse->hpp; | ||
1927 | } else if (perf_hpp__is_dynamic_entry(fmt)) { | ||
1928 | struct hpp_dynamic_entry *hde, *new_hde; | ||
1929 | |||
1930 | hde = container_of(fmt, struct hpp_dynamic_entry, hpp); | ||
1931 | new_hde = memdup(hde, sizeof(*hde)); | ||
1932 | if (new_hde) | ||
1933 | new_fmt = &new_hde->hpp; | ||
1934 | } else { | ||
1935 | new_fmt = memdup(fmt, sizeof(*fmt)); | ||
1936 | } | ||
1937 | |||
1938 | INIT_LIST_HEAD(&new_fmt->list); | ||
1939 | INIT_LIST_HEAD(&new_fmt->sort_list); | ||
1940 | |||
1941 | return new_fmt; | ||
1942 | } | ||
1943 | |||
1840 | static int parse_field_name(char *str, char **event, char **field, char **opt) | 1944 | static int parse_field_name(char *str, char **event, char **field, char **opt) |
1841 | { | 1945 | { |
1842 | char *event_name, *field_name, *opt_name; | 1946 | char *event_name, *field_name, *opt_name; |
@@ -1908,11 +2012,11 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam | |||
1908 | 2012 | ||
1909 | static int __dynamic_dimension__add(struct perf_evsel *evsel, | 2013 | static int __dynamic_dimension__add(struct perf_evsel *evsel, |
1910 | struct format_field *field, | 2014 | struct format_field *field, |
1911 | bool raw_trace) | 2015 | bool raw_trace, int level) |
1912 | { | 2016 | { |
1913 | struct hpp_dynamic_entry *hde; | 2017 | struct hpp_dynamic_entry *hde; |
1914 | 2018 | ||
1915 | hde = __alloc_dynamic_entry(evsel, field); | 2019 | hde = __alloc_dynamic_entry(evsel, field, level); |
1916 | if (hde == NULL) | 2020 | if (hde == NULL) |
1917 | return -ENOMEM; | 2021 | return -ENOMEM; |
1918 | 2022 | ||
@@ -1922,14 +2026,14 @@ static int __dynamic_dimension__add(struct perf_evsel *evsel, | |||
1922 | return 0; | 2026 | return 0; |
1923 | } | 2027 | } |
1924 | 2028 | ||
1925 | static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace) | 2029 | static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace, int level) |
1926 | { | 2030 | { |
1927 | int ret; | 2031 | int ret; |
1928 | struct format_field *field; | 2032 | struct format_field *field; |
1929 | 2033 | ||
1930 | field = evsel->tp_format->format.fields; | 2034 | field = evsel->tp_format->format.fields; |
1931 | while (field) { | 2035 | while (field) { |
1932 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | 2036 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level); |
1933 | if (ret < 0) | 2037 | if (ret < 0) |
1934 | return ret; | 2038 | return ret; |
1935 | 2039 | ||
@@ -1938,7 +2042,8 @@ static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace) | |||
1938 | return 0; | 2042 | return 0; |
1939 | } | 2043 | } |
1940 | 2044 | ||
1941 | static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) | 2045 | static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace, |
2046 | int level) | ||
1942 | { | 2047 | { |
1943 | int ret; | 2048 | int ret; |
1944 | struct perf_evsel *evsel; | 2049 | struct perf_evsel *evsel; |
@@ -1947,7 +2052,7 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) | |||
1947 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 2052 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
1948 | continue; | 2053 | continue; |
1949 | 2054 | ||
1950 | ret = add_evsel_fields(evsel, raw_trace); | 2055 | ret = add_evsel_fields(evsel, raw_trace, level); |
1951 | if (ret < 0) | 2056 | if (ret < 0) |
1952 | return ret; | 2057 | return ret; |
1953 | } | 2058 | } |
@@ -1955,7 +2060,7 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) | |||
1955 | } | 2060 | } |
1956 | 2061 | ||
1957 | static int add_all_matching_fields(struct perf_evlist *evlist, | 2062 | static int add_all_matching_fields(struct perf_evlist *evlist, |
1958 | char *field_name, bool raw_trace) | 2063 | char *field_name, bool raw_trace, int level) |
1959 | { | 2064 | { |
1960 | int ret = -ESRCH; | 2065 | int ret = -ESRCH; |
1961 | struct perf_evsel *evsel; | 2066 | struct perf_evsel *evsel; |
@@ -1969,14 +2074,15 @@ static int add_all_matching_fields(struct perf_evlist *evlist, | |||
1969 | if (field == NULL) | 2074 | if (field == NULL) |
1970 | continue; | 2075 | continue; |
1971 | 2076 | ||
1972 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | 2077 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level); |
1973 | if (ret < 0) | 2078 | if (ret < 0) |
1974 | break; | 2079 | break; |
1975 | } | 2080 | } |
1976 | return ret; | 2081 | return ret; |
1977 | } | 2082 | } |
1978 | 2083 | ||
1979 | static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) | 2084 | static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok, |
2085 | int level) | ||
1980 | { | 2086 | { |
1981 | char *str, *event_name, *field_name, *opt_name; | 2087 | char *str, *event_name, *field_name, *opt_name; |
1982 | struct perf_evsel *evsel; | 2088 | struct perf_evsel *evsel; |
@@ -2006,12 +2112,12 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) | |||
2006 | } | 2112 | } |
2007 | 2113 | ||
2008 | if (!strcmp(field_name, "trace_fields")) { | 2114 | if (!strcmp(field_name, "trace_fields")) { |
2009 | ret = add_all_dynamic_fields(evlist, raw_trace); | 2115 | ret = add_all_dynamic_fields(evlist, raw_trace, level); |
2010 | goto out; | 2116 | goto out; |
2011 | } | 2117 | } |
2012 | 2118 | ||
2013 | if (event_name == NULL) { | 2119 | if (event_name == NULL) { |
2014 | ret = add_all_matching_fields(evlist, field_name, raw_trace); | 2120 | ret = add_all_matching_fields(evlist, field_name, raw_trace, level); |
2015 | goto out; | 2121 | goto out; |
2016 | } | 2122 | } |
2017 | 2123 | ||
@@ -2029,7 +2135,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) | |||
2029 | } | 2135 | } |
2030 | 2136 | ||
2031 | if (!strcmp(field_name, "*")) { | 2137 | if (!strcmp(field_name, "*")) { |
2032 | ret = add_evsel_fields(evsel, raw_trace); | 2138 | ret = add_evsel_fields(evsel, raw_trace, level); |
2033 | } else { | 2139 | } else { |
2034 | field = pevent_find_any_field(evsel->tp_format, field_name); | 2140 | field = pevent_find_any_field(evsel->tp_format, field_name); |
2035 | if (field == NULL) { | 2141 | if (field == NULL) { |
@@ -2038,7 +2144,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) | |||
2038 | return -ENOENT; | 2144 | return -ENOENT; |
2039 | } | 2145 | } |
2040 | 2146 | ||
2041 | ret = __dynamic_dimension__add(evsel, field, raw_trace); | 2147 | ret = __dynamic_dimension__add(evsel, field, raw_trace, level); |
2042 | } | 2148 | } |
2043 | 2149 | ||
2044 | out: | 2150 | out: |
@@ -2046,12 +2152,14 @@ out: | |||
2046 | return ret; | 2152 | return ret; |
2047 | } | 2153 | } |
2048 | 2154 | ||
2049 | static int __sort_dimension__add(struct sort_dimension *sd) | 2155 | static int __sort_dimension__add(struct sort_dimension *sd, |
2156 | struct perf_hpp_list *list, | ||
2157 | int level) | ||
2050 | { | 2158 | { |
2051 | if (sd->taken) | 2159 | if (sd->taken) |
2052 | return 0; | 2160 | return 0; |
2053 | 2161 | ||
2054 | if (__sort_dimension__add_hpp_sort(sd) < 0) | 2162 | if (__sort_dimension__add_hpp_sort(sd, list, level) < 0) |
2055 | return -1; | 2163 | return -1; |
2056 | 2164 | ||
2057 | if (sd->entry->se_collapse) | 2165 | if (sd->entry->se_collapse) |
@@ -2062,46 +2170,63 @@ static int __sort_dimension__add(struct sort_dimension *sd) | |||
2062 | return 0; | 2170 | return 0; |
2063 | } | 2171 | } |
2064 | 2172 | ||
2065 | static int __hpp_dimension__add(struct hpp_dimension *hd) | 2173 | static int __hpp_dimension__add(struct hpp_dimension *hd, |
2174 | struct perf_hpp_list *list, | ||
2175 | int level) | ||
2066 | { | 2176 | { |
2067 | if (!hd->taken) { | 2177 | struct perf_hpp_fmt *fmt; |
2068 | hd->taken = 1; | ||
2069 | 2178 | ||
2070 | perf_hpp__register_sort_field(hd->fmt); | 2179 | if (hd->taken) |
2071 | } | 2180 | return 0; |
2181 | |||
2182 | fmt = __hpp_dimension__alloc_hpp(hd, level); | ||
2183 | if (!fmt) | ||
2184 | return -1; | ||
2185 | |||
2186 | hd->taken = 1; | ||
2187 | perf_hpp_list__register_sort_field(list, fmt); | ||
2072 | return 0; | 2188 | return 0; |
2073 | } | 2189 | } |
2074 | 2190 | ||
2075 | static int __sort_dimension__add_output(struct sort_dimension *sd) | 2191 | static int __sort_dimension__add_output(struct perf_hpp_list *list, |
2192 | struct sort_dimension *sd) | ||
2076 | { | 2193 | { |
2077 | if (sd->taken) | 2194 | if (sd->taken) |
2078 | return 0; | 2195 | return 0; |
2079 | 2196 | ||
2080 | if (__sort_dimension__add_hpp_output(sd) < 0) | 2197 | if (__sort_dimension__add_hpp_output(sd, list) < 0) |
2081 | return -1; | 2198 | return -1; |
2082 | 2199 | ||
2083 | sd->taken = 1; | 2200 | sd->taken = 1; |
2084 | return 0; | 2201 | return 0; |
2085 | } | 2202 | } |
2086 | 2203 | ||
2087 | static int __hpp_dimension__add_output(struct hpp_dimension *hd) | 2204 | static int __hpp_dimension__add_output(struct perf_hpp_list *list, |
2205 | struct hpp_dimension *hd) | ||
2088 | { | 2206 | { |
2089 | if (!hd->taken) { | 2207 | struct perf_hpp_fmt *fmt; |
2090 | hd->taken = 1; | ||
2091 | 2208 | ||
2092 | perf_hpp__column_register(hd->fmt); | 2209 | if (hd->taken) |
2093 | } | 2210 | return 0; |
2211 | |||
2212 | fmt = __hpp_dimension__alloc_hpp(hd, 0); | ||
2213 | if (!fmt) | ||
2214 | return -1; | ||
2215 | |||
2216 | hd->taken = 1; | ||
2217 | perf_hpp_list__column_register(list, fmt); | ||
2094 | return 0; | 2218 | return 0; |
2095 | } | 2219 | } |
2096 | 2220 | ||
2097 | int hpp_dimension__add_output(unsigned col) | 2221 | int hpp_dimension__add_output(unsigned col) |
2098 | { | 2222 | { |
2099 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 2223 | BUG_ON(col >= PERF_HPP__MAX_INDEX); |
2100 | return __hpp_dimension__add_output(&hpp_sort_dimensions[col]); | 2224 | return __hpp_dimension__add_output(&perf_hpp_list, &hpp_sort_dimensions[col]); |
2101 | } | 2225 | } |
2102 | 2226 | ||
2103 | static int sort_dimension__add(const char *tok, | 2227 | static int sort_dimension__add(struct perf_hpp_list *list, const char *tok, |
2104 | struct perf_evlist *evlist __maybe_unused) | 2228 | struct perf_evlist *evlist, |
2229 | int level) | ||
2105 | { | 2230 | { |
2106 | unsigned int i; | 2231 | unsigned int i; |
2107 | 2232 | ||
@@ -2136,9 +2261,13 @@ static int sort_dimension__add(const char *tok, | |||
2136 | sort__has_dso = 1; | 2261 | sort__has_dso = 1; |
2137 | } else if (sd->entry == &sort_socket) { | 2262 | } else if (sd->entry == &sort_socket) { |
2138 | sort__has_socket = 1; | 2263 | sort__has_socket = 1; |
2264 | } else if (sd->entry == &sort_thread) { | ||
2265 | sort__has_thread = 1; | ||
2266 | } else if (sd->entry == &sort_comm) { | ||
2267 | sort__has_comm = 1; | ||
2139 | } | 2268 | } |
2140 | 2269 | ||
2141 | return __sort_dimension__add(sd); | 2270 | return __sort_dimension__add(sd, list, level); |
2142 | } | 2271 | } |
2143 | 2272 | ||
2144 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { | 2273 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { |
@@ -2147,7 +2276,7 @@ static int sort_dimension__add(const char *tok, | |||
2147 | if (strncasecmp(tok, hd->name, strlen(tok))) | 2276 | if (strncasecmp(tok, hd->name, strlen(tok))) |
2148 | continue; | 2277 | continue; |
2149 | 2278 | ||
2150 | return __hpp_dimension__add(hd); | 2279 | return __hpp_dimension__add(hd, list, level); |
2151 | } | 2280 | } |
2152 | 2281 | ||
2153 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | 2282 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { |
@@ -2162,7 +2291,7 @@ static int sort_dimension__add(const char *tok, | |||
2162 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) | 2291 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) |
2163 | sort__has_sym = 1; | 2292 | sort__has_sym = 1; |
2164 | 2293 | ||
2165 | __sort_dimension__add(sd); | 2294 | __sort_dimension__add(sd, list, level); |
2166 | return 0; | 2295 | return 0; |
2167 | } | 2296 | } |
2168 | 2297 | ||
@@ -2178,16 +2307,60 @@ static int sort_dimension__add(const char *tok, | |||
2178 | if (sd->entry == &sort_mem_daddr_sym) | 2307 | if (sd->entry == &sort_mem_daddr_sym) |
2179 | sort__has_sym = 1; | 2308 | sort__has_sym = 1; |
2180 | 2309 | ||
2181 | __sort_dimension__add(sd); | 2310 | __sort_dimension__add(sd, list, level); |
2182 | return 0; | 2311 | return 0; |
2183 | } | 2312 | } |
2184 | 2313 | ||
2185 | if (!add_dynamic_entry(evlist, tok)) | 2314 | if (!add_dynamic_entry(evlist, tok, level)) |
2186 | return 0; | 2315 | return 0; |
2187 | 2316 | ||
2188 | return -ESRCH; | 2317 | return -ESRCH; |
2189 | } | 2318 | } |
2190 | 2319 | ||
2320 | static int setup_sort_list(struct perf_hpp_list *list, char *str, | ||
2321 | struct perf_evlist *evlist) | ||
2322 | { | ||
2323 | char *tmp, *tok; | ||
2324 | int ret = 0; | ||
2325 | int level = 0; | ||
2326 | int next_level = 1; | ||
2327 | bool in_group = false; | ||
2328 | |||
2329 | do { | ||
2330 | tok = str; | ||
2331 | tmp = strpbrk(str, "{}, "); | ||
2332 | if (tmp) { | ||
2333 | if (in_group) | ||
2334 | next_level = level; | ||
2335 | else | ||
2336 | next_level = level + 1; | ||
2337 | |||
2338 | if (*tmp == '{') | ||
2339 | in_group = true; | ||
2340 | else if (*tmp == '}') | ||
2341 | in_group = false; | ||
2342 | |||
2343 | *tmp = '\0'; | ||
2344 | str = tmp + 1; | ||
2345 | } | ||
2346 | |||
2347 | if (*tok) { | ||
2348 | ret = sort_dimension__add(list, tok, evlist, level); | ||
2349 | if (ret == -EINVAL) { | ||
2350 | error("Invalid --sort key: `%s'", tok); | ||
2351 | break; | ||
2352 | } else if (ret == -ESRCH) { | ||
2353 | error("Unknown --sort key: `%s'", tok); | ||
2354 | break; | ||
2355 | } | ||
2356 | } | ||
2357 | |||
2358 | level = next_level; | ||
2359 | } while (tmp); | ||
2360 | |||
2361 | return ret; | ||
2362 | } | ||
2363 | |||
2191 | static const char *get_default_sort_order(struct perf_evlist *evlist) | 2364 | static const char *get_default_sort_order(struct perf_evlist *evlist) |
2192 | { | 2365 | { |
2193 | const char *default_sort_orders[] = { | 2366 | const char *default_sort_orders[] = { |
@@ -2282,7 +2455,7 @@ static char *setup_overhead(char *keys) | |||
2282 | 2455 | ||
2283 | static int __setup_sorting(struct perf_evlist *evlist) | 2456 | static int __setup_sorting(struct perf_evlist *evlist) |
2284 | { | 2457 | { |
2285 | char *tmp, *tok, *str; | 2458 | char *str; |
2286 | const char *sort_keys; | 2459 | const char *sort_keys; |
2287 | int ret = 0; | 2460 | int ret = 0; |
2288 | 2461 | ||
@@ -2320,17 +2493,7 @@ static int __setup_sorting(struct perf_evlist *evlist) | |||
2320 | } | 2493 | } |
2321 | } | 2494 | } |
2322 | 2495 | ||
2323 | for (tok = strtok_r(str, ", ", &tmp); | 2496 | ret = setup_sort_list(&perf_hpp_list, str, evlist); |
2324 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
2325 | ret = sort_dimension__add(tok, evlist); | ||
2326 | if (ret == -EINVAL) { | ||
2327 | error("Invalid --sort key: `%s'", tok); | ||
2328 | break; | ||
2329 | } else if (ret == -ESRCH) { | ||
2330 | error("Unknown --sort key: `%s'", tok); | ||
2331 | break; | ||
2332 | } | ||
2333 | } | ||
2334 | 2497 | ||
2335 | free(str); | 2498 | free(str); |
2336 | return ret; | 2499 | return ret; |
@@ -2341,7 +2504,7 @@ void perf_hpp__set_elide(int idx, bool elide) | |||
2341 | struct perf_hpp_fmt *fmt; | 2504 | struct perf_hpp_fmt *fmt; |
2342 | struct hpp_sort_entry *hse; | 2505 | struct hpp_sort_entry *hse; |
2343 | 2506 | ||
2344 | perf_hpp__for_each_format(fmt) { | 2507 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
2345 | if (!perf_hpp__is_sort_entry(fmt)) | 2508 | if (!perf_hpp__is_sort_entry(fmt)) |
2346 | continue; | 2509 | continue; |
2347 | 2510 | ||
@@ -2401,7 +2564,7 @@ void sort__setup_elide(FILE *output) | |||
2401 | struct perf_hpp_fmt *fmt; | 2564 | struct perf_hpp_fmt *fmt; |
2402 | struct hpp_sort_entry *hse; | 2565 | struct hpp_sort_entry *hse; |
2403 | 2566 | ||
2404 | perf_hpp__for_each_format(fmt) { | 2567 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
2405 | if (!perf_hpp__is_sort_entry(fmt)) | 2568 | if (!perf_hpp__is_sort_entry(fmt)) |
2406 | continue; | 2569 | continue; |
2407 | 2570 | ||
@@ -2413,7 +2576,7 @@ void sort__setup_elide(FILE *output) | |||
2413 | * It makes no sense to elide all of sort entries. | 2576 | * It makes no sense to elide all of sort entries. |
2414 | * Just revert them to show up again. | 2577 | * Just revert them to show up again. |
2415 | */ | 2578 | */ |
2416 | perf_hpp__for_each_format(fmt) { | 2579 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
2417 | if (!perf_hpp__is_sort_entry(fmt)) | 2580 | if (!perf_hpp__is_sort_entry(fmt)) |
2418 | continue; | 2581 | continue; |
2419 | 2582 | ||
@@ -2421,7 +2584,7 @@ void sort__setup_elide(FILE *output) | |||
2421 | return; | 2584 | return; |
2422 | } | 2585 | } |
2423 | 2586 | ||
2424 | perf_hpp__for_each_format(fmt) { | 2587 | perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { |
2425 | if (!perf_hpp__is_sort_entry(fmt)) | 2588 | if (!perf_hpp__is_sort_entry(fmt)) |
2426 | continue; | 2589 | continue; |
2427 | 2590 | ||
@@ -2429,7 +2592,7 @@ void sort__setup_elide(FILE *output) | |||
2429 | } | 2592 | } |
2430 | } | 2593 | } |
2431 | 2594 | ||
2432 | static int output_field_add(char *tok) | 2595 | static int output_field_add(struct perf_hpp_list *list, char *tok) |
2433 | { | 2596 | { |
2434 | unsigned int i; | 2597 | unsigned int i; |
2435 | 2598 | ||
@@ -2439,7 +2602,7 @@ static int output_field_add(char *tok) | |||
2439 | if (strncasecmp(tok, sd->name, strlen(tok))) | 2602 | if (strncasecmp(tok, sd->name, strlen(tok))) |
2440 | continue; | 2603 | continue; |
2441 | 2604 | ||
2442 | return __sort_dimension__add_output(sd); | 2605 | return __sort_dimension__add_output(list, sd); |
2443 | } | 2606 | } |
2444 | 2607 | ||
2445 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { | 2608 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { |
@@ -2448,7 +2611,7 @@ static int output_field_add(char *tok) | |||
2448 | if (strncasecmp(tok, hd->name, strlen(tok))) | 2611 | if (strncasecmp(tok, hd->name, strlen(tok))) |
2449 | continue; | 2612 | continue; |
2450 | 2613 | ||
2451 | return __hpp_dimension__add_output(hd); | 2614 | return __hpp_dimension__add_output(list, hd); |
2452 | } | 2615 | } |
2453 | 2616 | ||
2454 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | 2617 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { |
@@ -2457,7 +2620,7 @@ static int output_field_add(char *tok) | |||
2457 | if (strncasecmp(tok, sd->name, strlen(tok))) | 2620 | if (strncasecmp(tok, sd->name, strlen(tok))) |
2458 | continue; | 2621 | continue; |
2459 | 2622 | ||
2460 | return __sort_dimension__add_output(sd); | 2623 | return __sort_dimension__add_output(list, sd); |
2461 | } | 2624 | } |
2462 | 2625 | ||
2463 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { | 2626 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { |
@@ -2466,12 +2629,32 @@ static int output_field_add(char *tok) | |||
2466 | if (strncasecmp(tok, sd->name, strlen(tok))) | 2629 | if (strncasecmp(tok, sd->name, strlen(tok))) |
2467 | continue; | 2630 | continue; |
2468 | 2631 | ||
2469 | return __sort_dimension__add_output(sd); | 2632 | return __sort_dimension__add_output(list, sd); |
2470 | } | 2633 | } |
2471 | 2634 | ||
2472 | return -ESRCH; | 2635 | return -ESRCH; |
2473 | } | 2636 | } |
2474 | 2637 | ||
2638 | static int setup_output_list(struct perf_hpp_list *list, char *str) | ||
2639 | { | ||
2640 | char *tmp, *tok; | ||
2641 | int ret = 0; | ||
2642 | |||
2643 | for (tok = strtok_r(str, ", ", &tmp); | ||
2644 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
2645 | ret = output_field_add(list, tok); | ||
2646 | if (ret == -EINVAL) { | ||
2647 | error("Invalid --fields key: `%s'", tok); | ||
2648 | break; | ||
2649 | } else if (ret == -ESRCH) { | ||
2650 | error("Unknown --fields key: `%s'", tok); | ||
2651 | break; | ||
2652 | } | ||
2653 | } | ||
2654 | |||
2655 | return ret; | ||
2656 | } | ||
2657 | |||
2475 | static void reset_dimensions(void) | 2658 | static void reset_dimensions(void) |
2476 | { | 2659 | { |
2477 | unsigned int i; | 2660 | unsigned int i; |
@@ -2496,7 +2679,7 @@ bool is_strict_order(const char *order) | |||
2496 | 2679 | ||
2497 | static int __setup_output_field(void) | 2680 | static int __setup_output_field(void) |
2498 | { | 2681 | { |
2499 | char *tmp, *tok, *str, *strp; | 2682 | char *str, *strp; |
2500 | int ret = -EINVAL; | 2683 | int ret = -EINVAL; |
2501 | 2684 | ||
2502 | if (field_order == NULL) | 2685 | if (field_order == NULL) |
@@ -2516,17 +2699,7 @@ static int __setup_output_field(void) | |||
2516 | goto out; | 2699 | goto out; |
2517 | } | 2700 | } |
2518 | 2701 | ||
2519 | for (tok = strtok_r(strp, ", ", &tmp); | 2702 | ret = setup_output_list(&perf_hpp_list, strp); |
2520 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
2521 | ret = output_field_add(tok); | ||
2522 | if (ret == -EINVAL) { | ||
2523 | error("Invalid --fields key: `%s'", tok); | ||
2524 | break; | ||
2525 | } else if (ret == -ESRCH) { | ||
2526 | error("Unknown --fields key: `%s'", tok); | ||
2527 | break; | ||
2528 | } | ||
2529 | } | ||
2530 | 2703 | ||
2531 | out: | 2704 | out: |
2532 | free(str); | 2705 | free(str); |
@@ -2542,7 +2715,7 @@ int setup_sorting(struct perf_evlist *evlist) | |||
2542 | return err; | 2715 | return err; |
2543 | 2716 | ||
2544 | if (parent_pattern != default_parent_pattern) { | 2717 | if (parent_pattern != default_parent_pattern) { |
2545 | err = sort_dimension__add("parent", evlist); | 2718 | err = sort_dimension__add(&perf_hpp_list, "parent", evlist, -1); |
2546 | if (err < 0) | 2719 | if (err < 0) |
2547 | return err; | 2720 | return err; |
2548 | } | 2721 | } |
@@ -2560,9 +2733,13 @@ int setup_sorting(struct perf_evlist *evlist) | |||
2560 | return err; | 2733 | return err; |
2561 | 2734 | ||
2562 | /* copy sort keys to output fields */ | 2735 | /* copy sort keys to output fields */ |
2563 | perf_hpp__setup_output_field(); | 2736 | perf_hpp__setup_output_field(&perf_hpp_list); |
2564 | /* and then copy output fields to sort keys */ | 2737 | /* and then copy output fields to sort keys */ |
2565 | perf_hpp__append_sort_keys(); | 2738 | perf_hpp__append_sort_keys(&perf_hpp_list); |
2739 | |||
2740 | /* setup hists-specific output fields */ | ||
2741 | if (perf_hpp__setup_hists_formats(&perf_hpp_list, evlist) < 0) | ||
2742 | return -1; | ||
2566 | 2743 | ||
2567 | return 0; | 2744 | return 0; |
2568 | } | 2745 | } |
@@ -2578,5 +2755,5 @@ void reset_output_field(void) | |||
2578 | sort_order = NULL; | 2755 | sort_order = NULL; |
2579 | 2756 | ||
2580 | reset_dimensions(); | 2757 | reset_dimensions(); |
2581 | perf_hpp__reset_output_field(); | 2758 | perf_hpp__reset_output_field(&perf_hpp_list); |
2582 | } | 2759 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 687bbb124428..3f4e35998119 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -32,9 +32,12 @@ extern const char default_sort_order[]; | |||
32 | extern regex_t ignore_callees_regex; | 32 | extern regex_t ignore_callees_regex; |
33 | extern int have_ignore_callees; | 33 | extern int have_ignore_callees; |
34 | extern int sort__need_collapse; | 34 | extern int sort__need_collapse; |
35 | extern int sort__has_dso; | ||
35 | extern int sort__has_parent; | 36 | extern int sort__has_parent; |
36 | extern int sort__has_sym; | 37 | extern int sort__has_sym; |
37 | extern int sort__has_socket; | 38 | extern int sort__has_socket; |
39 | extern int sort__has_thread; | ||
40 | extern int sort__has_comm; | ||
38 | extern enum sort_mode sort__mode; | 41 | extern enum sort_mode sort__mode; |
39 | extern struct sort_entry sort_comm; | 42 | extern struct sort_entry sort_comm; |
40 | extern struct sort_entry sort_dso; | 43 | extern struct sort_entry sort_dso; |
@@ -94,9 +97,11 @@ struct hist_entry { | |||
94 | s32 socket; | 97 | s32 socket; |
95 | s32 cpu; | 98 | s32 cpu; |
96 | u8 cpumode; | 99 | u8 cpumode; |
100 | u8 depth; | ||
97 | 101 | ||
98 | /* We are added by hists__add_dummy_entry. */ | 102 | /* We are added by hists__add_dummy_entry. */ |
99 | bool dummy; | 103 | bool dummy; |
104 | bool leaf; | ||
100 | 105 | ||
101 | char level; | 106 | char level; |
102 | u8 filtered; | 107 | u8 filtered; |
@@ -113,18 +118,28 @@ struct hist_entry { | |||
113 | bool init_have_children; | 118 | bool init_have_children; |
114 | bool unfolded; | 119 | bool unfolded; |
115 | bool has_children; | 120 | bool has_children; |
121 | bool has_no_entry; | ||
116 | }; | 122 | }; |
117 | }; | 123 | }; |
118 | char *srcline; | 124 | char *srcline; |
119 | char *srcfile; | 125 | char *srcfile; |
120 | struct symbol *parent; | 126 | struct symbol *parent; |
121 | struct rb_root sorted_chain; | ||
122 | struct branch_info *branch_info; | 127 | struct branch_info *branch_info; |
123 | struct hists *hists; | 128 | struct hists *hists; |
124 | struct mem_info *mem_info; | 129 | struct mem_info *mem_info; |
125 | void *raw_data; | 130 | void *raw_data; |
126 | u32 raw_size; | 131 | u32 raw_size; |
127 | void *trace_output; | 132 | void *trace_output; |
133 | struct perf_hpp_list *hpp_list; | ||
134 | struct hist_entry *parent_he; | ||
135 | union { | ||
136 | /* this is for hierarchical entry structure */ | ||
137 | struct { | ||
138 | struct rb_root hroot_in; | ||
139 | struct rb_root hroot_out; | ||
140 | }; /* non-leaf entries */ | ||
141 | struct rb_root sorted_chain; /* leaf entry has callchains */ | ||
142 | }; | ||
128 | struct callchain_root callchain[0]; /* must be last member */ | 143 | struct callchain_root callchain[0]; /* must be last member */ |
129 | }; | 144 | }; |
130 | 145 | ||
@@ -160,6 +175,17 @@ static inline float hist_entry__get_percent_limit(struct hist_entry *he) | |||
160 | return period * 100.0 / total_period; | 175 | return period * 100.0 / total_period; |
161 | } | 176 | } |
162 | 177 | ||
178 | static inline u64 cl_address(u64 address) | ||
179 | { | ||
180 | /* return the cacheline of the address */ | ||
181 | return (address & ~(cacheline_size - 1)); | ||
182 | } | ||
183 | |||
184 | static inline u64 cl_offset(u64 address) | ||
185 | { | ||
186 | /* return the cacheline of the address */ | ||
187 | return (address & (cacheline_size - 1)); | ||
188 | } | ||
163 | 189 | ||
164 | enum sort_mode { | 190 | enum sort_mode { |
165 | SORT_MODE__NORMAL, | 191 | SORT_MODE__NORMAL, |
@@ -221,6 +247,7 @@ struct sort_entry { | |||
221 | int64_t (*se_sort)(struct hist_entry *, struct hist_entry *); | 247 | int64_t (*se_sort)(struct hist_entry *, struct hist_entry *); |
222 | int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, | 248 | int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, |
223 | unsigned int width); | 249 | unsigned int width); |
250 | int (*se_filter)(struct hist_entry *he, int type, const void *arg); | ||
224 | u8 se_width_idx; | 251 | u8 se_width_idx; |
225 | }; | 252 | }; |
226 | 253 | ||
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 6ac03146889d..fdb71961143e 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include "evsel.h" | 2 | #include "evsel.h" |
3 | #include "stat.h" | 3 | #include "stat.h" |
4 | #include "color.h" | 4 | #include "color.h" |
5 | #include "pmu.h" | ||
5 | 6 | ||
6 | enum { | 7 | enum { |
7 | CTX_BIT_USER = 1 << 0, | 8 | CTX_BIT_USER = 1 << 0, |
@@ -14,6 +15,13 @@ enum { | |||
14 | 15 | ||
15 | #define NUM_CTX CTX_BIT_MAX | 16 | #define NUM_CTX CTX_BIT_MAX |
16 | 17 | ||
18 | /* | ||
19 | * AGGR_GLOBAL: Use CPU 0 | ||
20 | * AGGR_SOCKET: Use first CPU of socket | ||
21 | * AGGR_CORE: Use first CPU of core | ||
22 | * AGGR_NONE: Use matching CPU | ||
23 | * AGGR_THREAD: Not supported? | ||
24 | */ | ||
17 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 25 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
18 | static struct stats runtime_cycles_stats[NUM_CTX][MAX_NR_CPUS]; | 26 | static struct stats runtime_cycles_stats[NUM_CTX][MAX_NR_CPUS]; |
19 | static struct stats runtime_stalled_cycles_front_stats[NUM_CTX][MAX_NR_CPUS]; | 27 | static struct stats runtime_stalled_cycles_front_stats[NUM_CTX][MAX_NR_CPUS]; |
@@ -28,9 +36,15 @@ static struct stats runtime_dtlb_cache_stats[NUM_CTX][MAX_NR_CPUS]; | |||
28 | static struct stats runtime_cycles_in_tx_stats[NUM_CTX][MAX_NR_CPUS]; | 36 | static struct stats runtime_cycles_in_tx_stats[NUM_CTX][MAX_NR_CPUS]; |
29 | static struct stats runtime_transaction_stats[NUM_CTX][MAX_NR_CPUS]; | 37 | static struct stats runtime_transaction_stats[NUM_CTX][MAX_NR_CPUS]; |
30 | static struct stats runtime_elision_stats[NUM_CTX][MAX_NR_CPUS]; | 38 | static struct stats runtime_elision_stats[NUM_CTX][MAX_NR_CPUS]; |
39 | static bool have_frontend_stalled; | ||
31 | 40 | ||
32 | struct stats walltime_nsecs_stats; | 41 | struct stats walltime_nsecs_stats; |
33 | 42 | ||
43 | void perf_stat__init_shadow_stats(void) | ||
44 | { | ||
45 | have_frontend_stalled = pmu_have_event("cpu", "stalled-cycles-frontend"); | ||
46 | } | ||
47 | |||
34 | static int evsel_context(struct perf_evsel *evsel) | 48 | static int evsel_context(struct perf_evsel *evsel) |
35 | { | 49 | { |
36 | int ctx = 0; | 50 | int ctx = 0; |
@@ -137,9 +151,9 @@ static const char *get_ratio_color(enum grc_type type, double ratio) | |||
137 | return color; | 151 | return color; |
138 | } | 152 | } |
139 | 153 | ||
140 | static void print_stalled_cycles_frontend(FILE *out, int cpu, | 154 | static void print_stalled_cycles_frontend(int cpu, |
141 | struct perf_evsel *evsel | 155 | struct perf_evsel *evsel, double avg, |
142 | __maybe_unused, double avg) | 156 | struct perf_stat_output_ctx *out) |
143 | { | 157 | { |
144 | double total, ratio = 0.0; | 158 | double total, ratio = 0.0; |
145 | const char *color; | 159 | const char *color; |
@@ -152,14 +166,16 @@ static void print_stalled_cycles_frontend(FILE *out, int cpu, | |||
152 | 166 | ||
153 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); | 167 | color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio); |
154 | 168 | ||
155 | fprintf(out, " # "); | 169 | if (ratio) |
156 | color_fprintf(out, color, "%6.2f%%", ratio); | 170 | out->print_metric(out->ctx, color, "%7.2f%%", "frontend cycles idle", |
157 | fprintf(out, " frontend cycles idle "); | 171 | ratio); |
172 | else | ||
173 | out->print_metric(out->ctx, NULL, NULL, "frontend cycles idle", 0); | ||
158 | } | 174 | } |
159 | 175 | ||
160 | static void print_stalled_cycles_backend(FILE *out, int cpu, | 176 | static void print_stalled_cycles_backend(int cpu, |
161 | struct perf_evsel *evsel | 177 | struct perf_evsel *evsel, double avg, |
162 | __maybe_unused, double avg) | 178 | struct perf_stat_output_ctx *out) |
163 | { | 179 | { |
164 | double total, ratio = 0.0; | 180 | double total, ratio = 0.0; |
165 | const char *color; | 181 | const char *color; |
@@ -172,14 +188,13 @@ static void print_stalled_cycles_backend(FILE *out, int cpu, | |||
172 | 188 | ||
173 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); | 189 | color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio); |
174 | 190 | ||
175 | fprintf(out, " # "); | 191 | out->print_metric(out->ctx, color, "%6.2f%%", "backend cycles idle", ratio); |
176 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
177 | fprintf(out, " backend cycles idle "); | ||
178 | } | 192 | } |
179 | 193 | ||
180 | static void print_branch_misses(FILE *out, int cpu, | 194 | static void print_branch_misses(int cpu, |
181 | struct perf_evsel *evsel __maybe_unused, | 195 | struct perf_evsel *evsel, |
182 | double avg) | 196 | double avg, |
197 | struct perf_stat_output_ctx *out) | ||
183 | { | 198 | { |
184 | double total, ratio = 0.0; | 199 | double total, ratio = 0.0; |
185 | const char *color; | 200 | const char *color; |
@@ -192,14 +207,13 @@ static void print_branch_misses(FILE *out, int cpu, | |||
192 | 207 | ||
193 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 208 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
194 | 209 | ||
195 | fprintf(out, " # "); | 210 | out->print_metric(out->ctx, color, "%7.2f%%", "of all branches", ratio); |
196 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
197 | fprintf(out, " of all branches "); | ||
198 | } | 211 | } |
199 | 212 | ||
200 | static void print_l1_dcache_misses(FILE *out, int cpu, | 213 | static void print_l1_dcache_misses(int cpu, |
201 | struct perf_evsel *evsel __maybe_unused, | 214 | struct perf_evsel *evsel, |
202 | double avg) | 215 | double avg, |
216 | struct perf_stat_output_ctx *out) | ||
203 | { | 217 | { |
204 | double total, ratio = 0.0; | 218 | double total, ratio = 0.0; |
205 | const char *color; | 219 | const char *color; |
@@ -212,14 +226,13 @@ static void print_l1_dcache_misses(FILE *out, int cpu, | |||
212 | 226 | ||
213 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 227 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
214 | 228 | ||
215 | fprintf(out, " # "); | 229 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-dcache hits", ratio); |
216 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
217 | fprintf(out, " of all L1-dcache hits "); | ||
218 | } | 230 | } |
219 | 231 | ||
220 | static void print_l1_icache_misses(FILE *out, int cpu, | 232 | static void print_l1_icache_misses(int cpu, |
221 | struct perf_evsel *evsel __maybe_unused, | 233 | struct perf_evsel *evsel, |
222 | double avg) | 234 | double avg, |
235 | struct perf_stat_output_ctx *out) | ||
223 | { | 236 | { |
224 | double total, ratio = 0.0; | 237 | double total, ratio = 0.0; |
225 | const char *color; | 238 | const char *color; |
@@ -231,15 +244,13 @@ static void print_l1_icache_misses(FILE *out, int cpu, | |||
231 | ratio = avg / total * 100.0; | 244 | ratio = avg / total * 100.0; |
232 | 245 | ||
233 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 246 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
234 | 247 | out->print_metric(out->ctx, color, "%7.2f%%", "of all L1-icache hits", ratio); | |
235 | fprintf(out, " # "); | ||
236 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
237 | fprintf(out, " of all L1-icache hits "); | ||
238 | } | 248 | } |
239 | 249 | ||
240 | static void print_dtlb_cache_misses(FILE *out, int cpu, | 250 | static void print_dtlb_cache_misses(int cpu, |
241 | struct perf_evsel *evsel __maybe_unused, | 251 | struct perf_evsel *evsel, |
242 | double avg) | 252 | double avg, |
253 | struct perf_stat_output_ctx *out) | ||
243 | { | 254 | { |
244 | double total, ratio = 0.0; | 255 | double total, ratio = 0.0; |
245 | const char *color; | 256 | const char *color; |
@@ -251,15 +262,13 @@ static void print_dtlb_cache_misses(FILE *out, int cpu, | |||
251 | ratio = avg / total * 100.0; | 262 | ratio = avg / total * 100.0; |
252 | 263 | ||
253 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 264 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
254 | 265 | out->print_metric(out->ctx, color, "%7.2f%%", "of all dTLB cache hits", ratio); | |
255 | fprintf(out, " # "); | ||
256 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
257 | fprintf(out, " of all dTLB cache hits "); | ||
258 | } | 266 | } |
259 | 267 | ||
260 | static void print_itlb_cache_misses(FILE *out, int cpu, | 268 | static void print_itlb_cache_misses(int cpu, |
261 | struct perf_evsel *evsel __maybe_unused, | 269 | struct perf_evsel *evsel, |
262 | double avg) | 270 | double avg, |
271 | struct perf_stat_output_ctx *out) | ||
263 | { | 272 | { |
264 | double total, ratio = 0.0; | 273 | double total, ratio = 0.0; |
265 | const char *color; | 274 | const char *color; |
@@ -271,15 +280,13 @@ static void print_itlb_cache_misses(FILE *out, int cpu, | |||
271 | ratio = avg / total * 100.0; | 280 | ratio = avg / total * 100.0; |
272 | 281 | ||
273 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 282 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
274 | 283 | out->print_metric(out->ctx, color, "%7.2f%%", "of all iTLB cache hits", ratio); | |
275 | fprintf(out, " # "); | ||
276 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
277 | fprintf(out, " of all iTLB cache hits "); | ||
278 | } | 284 | } |
279 | 285 | ||
280 | static void print_ll_cache_misses(FILE *out, int cpu, | 286 | static void print_ll_cache_misses(int cpu, |
281 | struct perf_evsel *evsel __maybe_unused, | 287 | struct perf_evsel *evsel, |
282 | double avg) | 288 | double avg, |
289 | struct perf_stat_output_ctx *out) | ||
283 | { | 290 | { |
284 | double total, ratio = 0.0; | 291 | double total, ratio = 0.0; |
285 | const char *color; | 292 | const char *color; |
@@ -291,15 +298,15 @@ static void print_ll_cache_misses(FILE *out, int cpu, | |||
291 | ratio = avg / total * 100.0; | 298 | ratio = avg / total * 100.0; |
292 | 299 | ||
293 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); | 300 | color = get_ratio_color(GRC_CACHE_MISSES, ratio); |
294 | 301 | out->print_metric(out->ctx, color, "%7.2f%%", "of all LL-cache hits", ratio); | |
295 | fprintf(out, " # "); | ||
296 | color_fprintf(out, color, "%6.2f%%", ratio); | ||
297 | fprintf(out, " of all LL-cache hits "); | ||
298 | } | 302 | } |
299 | 303 | ||
300 | void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | 304 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, |
301 | double avg, int cpu, enum aggr_mode aggr) | 305 | double avg, int cpu, |
306 | struct perf_stat_output_ctx *out) | ||
302 | { | 307 | { |
308 | void *ctxp = out->ctx; | ||
309 | print_metric_t print_metric = out->print_metric; | ||
303 | double total, ratio = 0.0, total2; | 310 | double total, ratio = 0.0, total2; |
304 | int ctx = evsel_context(evsel); | 311 | int ctx = evsel_context(evsel); |
305 | 312 | ||
@@ -307,119 +314,145 @@ void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | |||
307 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); | 314 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
308 | if (total) { | 315 | if (total) { |
309 | ratio = avg / total; | 316 | ratio = avg / total; |
310 | fprintf(out, " # %5.2f insns per cycle ", ratio); | 317 | print_metric(ctxp, NULL, "%7.2f ", |
318 | "insn per cycle", ratio); | ||
311 | } else { | 319 | } else { |
312 | fprintf(out, " "); | 320 | print_metric(ctxp, NULL, NULL, "insn per cycle", 0); |
313 | } | 321 | } |
314 | total = avg_stats(&runtime_stalled_cycles_front_stats[ctx][cpu]); | 322 | total = avg_stats(&runtime_stalled_cycles_front_stats[ctx][cpu]); |
315 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[ctx][cpu])); | 323 | total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[ctx][cpu])); |
316 | 324 | ||
317 | if (total && avg) { | 325 | if (total && avg) { |
326 | out->new_line(ctxp); | ||
318 | ratio = total / avg; | 327 | ratio = total / avg; |
319 | fprintf(out, "\n"); | 328 | print_metric(ctxp, NULL, "%7.2f ", |
320 | if (aggr == AGGR_NONE) | 329 | "stalled cycles per insn", |
321 | fprintf(out, " "); | 330 | ratio); |
322 | fprintf(out, " # %5.2f stalled cycles per insn", ratio); | 331 | } else if (have_frontend_stalled) { |
332 | print_metric(ctxp, NULL, NULL, | ||
333 | "stalled cycles per insn", 0); | ||
323 | } | 334 | } |
324 | 335 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) { | |
325 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && | 336 | if (runtime_branches_stats[ctx][cpu].n != 0) |
326 | runtime_branches_stats[ctx][cpu].n != 0) { | 337 | print_branch_misses(cpu, evsel, avg, out); |
327 | print_branch_misses(out, cpu, evsel, avg); | 338 | else |
339 | print_metric(ctxp, NULL, NULL, "of all branches", 0); | ||
328 | } else if ( | 340 | } else if ( |
329 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 341 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
330 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | | 342 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1D | |
331 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 343 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
332 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 344 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
333 | runtime_l1_dcache_stats[ctx][cpu].n != 0) { | 345 | if (runtime_l1_dcache_stats[ctx][cpu].n != 0) |
334 | print_l1_dcache_misses(out, cpu, evsel, avg); | 346 | print_l1_dcache_misses(cpu, evsel, avg, out); |
347 | else | ||
348 | print_metric(ctxp, NULL, NULL, "of all L1-dcache hits", 0); | ||
335 | } else if ( | 349 | } else if ( |
336 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 350 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
337 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | | 351 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_L1I | |
338 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 352 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
339 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 353 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
340 | runtime_l1_icache_stats[ctx][cpu].n != 0) { | 354 | if (runtime_l1_icache_stats[ctx][cpu].n != 0) |
341 | print_l1_icache_misses(out, cpu, evsel, avg); | 355 | print_l1_icache_misses(cpu, evsel, avg, out); |
356 | else | ||
357 | print_metric(ctxp, NULL, NULL, "of all L1-icache hits", 0); | ||
342 | } else if ( | 358 | } else if ( |
343 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 359 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
344 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | | 360 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_DTLB | |
345 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 361 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
346 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 362 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
347 | runtime_dtlb_cache_stats[ctx][cpu].n != 0) { | 363 | if (runtime_dtlb_cache_stats[ctx][cpu].n != 0) |
348 | print_dtlb_cache_misses(out, cpu, evsel, avg); | 364 | print_dtlb_cache_misses(cpu, evsel, avg, out); |
365 | else | ||
366 | print_metric(ctxp, NULL, NULL, "of all dTLB cache hits", 0); | ||
349 | } else if ( | 367 | } else if ( |
350 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 368 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
351 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | | 369 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_ITLB | |
352 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 370 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
353 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 371 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
354 | runtime_itlb_cache_stats[ctx][cpu].n != 0) { | 372 | if (runtime_itlb_cache_stats[ctx][cpu].n != 0) |
355 | print_itlb_cache_misses(out, cpu, evsel, avg); | 373 | print_itlb_cache_misses(cpu, evsel, avg, out); |
374 | else | ||
375 | print_metric(ctxp, NULL, NULL, "of all iTLB cache hits", 0); | ||
356 | } else if ( | 376 | } else if ( |
357 | evsel->attr.type == PERF_TYPE_HW_CACHE && | 377 | evsel->attr.type == PERF_TYPE_HW_CACHE && |
358 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | | 378 | evsel->attr.config == ( PERF_COUNT_HW_CACHE_LL | |
359 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | | 379 | ((PERF_COUNT_HW_CACHE_OP_READ) << 8) | |
360 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) && | 380 | ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) { |
361 | runtime_ll_cache_stats[ctx][cpu].n != 0) { | 381 | if (runtime_ll_cache_stats[ctx][cpu].n != 0) |
362 | print_ll_cache_misses(out, cpu, evsel, avg); | 382 | print_ll_cache_misses(cpu, evsel, avg, out); |
363 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES) && | 383 | else |
364 | runtime_cacherefs_stats[ctx][cpu].n != 0) { | 384 | print_metric(ctxp, NULL, NULL, "of all LL-cache hits", 0); |
385 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) { | ||
365 | total = avg_stats(&runtime_cacherefs_stats[ctx][cpu]); | 386 | total = avg_stats(&runtime_cacherefs_stats[ctx][cpu]); |
366 | 387 | ||
367 | if (total) | 388 | if (total) |
368 | ratio = avg * 100 / total; | 389 | ratio = avg * 100 / total; |
369 | 390 | ||
370 | fprintf(out, " # %8.3f %% of all cache refs ", ratio); | 391 | if (runtime_cacherefs_stats[ctx][cpu].n != 0) |
371 | 392 | print_metric(ctxp, NULL, "%8.3f %%", | |
393 | "of all cache refs", ratio); | ||
394 | else | ||
395 | print_metric(ctxp, NULL, NULL, "of all cache refs", 0); | ||
372 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { | 396 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) { |
373 | print_stalled_cycles_frontend(out, cpu, evsel, avg); | 397 | print_stalled_cycles_frontend(cpu, evsel, avg, out); |
374 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { | 398 | } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) { |
375 | print_stalled_cycles_backend(out, cpu, evsel, avg); | 399 | print_stalled_cycles_backend(cpu, evsel, avg, out); |
376 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { | 400 | } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) { |
377 | total = avg_stats(&runtime_nsecs_stats[cpu]); | 401 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
378 | 402 | ||
379 | if (total) { | 403 | if (total) { |
380 | ratio = avg / total; | 404 | ratio = avg / total; |
381 | fprintf(out, " # %8.3f GHz ", ratio); | 405 | print_metric(ctxp, NULL, "%8.3f", "GHz", ratio); |
382 | } else { | 406 | } else { |
383 | fprintf(out, " "); | 407 | print_metric(ctxp, NULL, NULL, "Ghz", 0); |
384 | } | 408 | } |
385 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { | 409 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) { |
386 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); | 410 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
387 | if (total) | 411 | if (total) |
388 | fprintf(out, | 412 | print_metric(ctxp, NULL, |
389 | " # %5.2f%% transactional cycles ", | 413 | "%7.2f%%", "transactional cycles", |
390 | 100.0 * (avg / total)); | 414 | 100.0 * (avg / total)); |
415 | else | ||
416 | print_metric(ctxp, NULL, NULL, "transactional cycles", | ||
417 | 0); | ||
391 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { | 418 | } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) { |
392 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); | 419 | total = avg_stats(&runtime_cycles_stats[ctx][cpu]); |
393 | total2 = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); | 420 | total2 = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
394 | if (total2 < avg) | 421 | if (total2 < avg) |
395 | total2 = avg; | 422 | total2 = avg; |
396 | if (total) | 423 | if (total) |
397 | fprintf(out, | 424 | print_metric(ctxp, NULL, "%7.2f%%", "aborted cycles", |
398 | " # %5.2f%% aborted cycles ", | ||
399 | 100.0 * ((total2-avg) / total)); | 425 | 100.0 * ((total2-avg) / total)); |
400 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START) && | 426 | else |
401 | runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { | 427 | print_metric(ctxp, NULL, NULL, "aborted cycles", 0); |
428 | } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) { | ||
402 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); | 429 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
403 | 430 | ||
404 | if (avg) | 431 | if (avg) |
405 | ratio = total / avg; | 432 | ratio = total / avg; |
406 | 433 | ||
407 | fprintf(out, " # %8.0f cycles / transaction ", ratio); | 434 | if (runtime_cycles_in_tx_stats[ctx][cpu].n != 0) |
408 | } else if (perf_stat_evsel__is(evsel, ELISION_START) && | 435 | print_metric(ctxp, NULL, "%8.0f", |
409 | runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { | 436 | "cycles / transaction", ratio); |
437 | else | ||
438 | print_metric(ctxp, NULL, NULL, "cycles / transaction", | ||
439 | 0); | ||
440 | } else if (perf_stat_evsel__is(evsel, ELISION_START)) { | ||
410 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); | 441 | total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); |
411 | 442 | ||
412 | if (avg) | 443 | if (avg) |
413 | ratio = total / avg; | 444 | ratio = total / avg; |
414 | 445 | ||
415 | fprintf(out, " # %8.0f cycles / elision ", ratio); | 446 | print_metric(ctxp, NULL, "%8.0f", "cycles / elision", ratio); |
416 | } else if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) { | 447 | } else if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) { |
417 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) | 448 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) |
418 | fprintf(out, " # %8.3f CPUs utilized ", avg / ratio); | 449 | print_metric(ctxp, NULL, "%8.3f", "CPUs utilized", |
450 | avg / ratio); | ||
419 | else | 451 | else |
420 | fprintf(out, " "); | 452 | print_metric(ctxp, NULL, NULL, "CPUs utilized", 0); |
421 | } else if (runtime_nsecs_stats[cpu].n != 0) { | 453 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
422 | char unit = 'M'; | 454 | char unit = 'M'; |
455 | char unit_buf[10]; | ||
423 | 456 | ||
424 | total = avg_stats(&runtime_nsecs_stats[cpu]); | 457 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
425 | 458 | ||
@@ -429,9 +462,9 @@ void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | |||
429 | ratio *= 1000; | 462 | ratio *= 1000; |
430 | unit = 'K'; | 463 | unit = 'K'; |
431 | } | 464 | } |
432 | 465 | snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit); | |
433 | fprintf(out, " # %8.3f %c/sec ", ratio, unit); | 466 | print_metric(ctxp, NULL, "%8.3f", unit_buf, ratio); |
434 | } else { | 467 | } else { |
435 | fprintf(out, " "); | 468 | print_metric(ctxp, NULL, NULL, NULL, 0); |
436 | } | 469 | } |
437 | } | 470 | } |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index afb0c45eba34..4d9b481cf3b6 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
@@ -97,7 +97,7 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel) | |||
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | 100 | static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) |
101 | { | 101 | { |
102 | int i; | 102 | int i; |
103 | struct perf_stat_evsel *ps = evsel->priv; | 103 | struct perf_stat_evsel *ps = evsel->priv; |
@@ -108,7 +108,7 @@ void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | |||
108 | perf_stat_evsel_id_init(evsel); | 108 | perf_stat_evsel_id_init(evsel); |
109 | } | 109 | } |
110 | 110 | ||
111 | int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | 111 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) |
112 | { | 112 | { |
113 | evsel->priv = zalloc(sizeof(struct perf_stat_evsel)); | 113 | evsel->priv = zalloc(sizeof(struct perf_stat_evsel)); |
114 | if (evsel->priv == NULL) | 114 | if (evsel->priv == NULL) |
@@ -117,13 +117,13 @@ int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | |||
117 | return 0; | 117 | return 0; |
118 | } | 118 | } |
119 | 119 | ||
120 | void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | 120 | static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) |
121 | { | 121 | { |
122 | zfree(&evsel->priv); | 122 | zfree(&evsel->priv); |
123 | } | 123 | } |
124 | 124 | ||
125 | int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, | 125 | static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, |
126 | int ncpus, int nthreads) | 126 | int ncpus, int nthreads) |
127 | { | 127 | { |
128 | struct perf_counts *counts; | 128 | struct perf_counts *counts; |
129 | 129 | ||
@@ -134,13 +134,13 @@ int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, | |||
134 | return counts ? 0 : -ENOMEM; | 134 | return counts ? 0 : -ENOMEM; |
135 | } | 135 | } |
136 | 136 | ||
137 | void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) | 137 | static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) |
138 | { | 138 | { |
139 | perf_counts__delete(evsel->prev_raw_counts); | 139 | perf_counts__delete(evsel->prev_raw_counts); |
140 | evsel->prev_raw_counts = NULL; | 140 | evsel->prev_raw_counts = NULL; |
141 | } | 141 | } |
142 | 142 | ||
143 | int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw) | 143 | static int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw) |
144 | { | 144 | { |
145 | int ncpus = perf_evsel__nr_cpus(evsel); | 145 | int ncpus = perf_evsel__nr_cpus(evsel); |
146 | int nthreads = thread_map__nr(evsel->threads); | 146 | int nthreads = thread_map__nr(evsel->threads); |
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 086f4e128d63..0150e786ccc7 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
@@ -68,21 +68,23 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel); | |||
68 | 68 | ||
69 | extern struct stats walltime_nsecs_stats; | 69 | extern struct stats walltime_nsecs_stats; |
70 | 70 | ||
71 | typedef void (*print_metric_t)(void *ctx, const char *color, const char *unit, | ||
72 | const char *fmt, double val); | ||
73 | typedef void (*new_line_t )(void *ctx); | ||
74 | |||
75 | void perf_stat__init_shadow_stats(void); | ||
71 | void perf_stat__reset_shadow_stats(void); | 76 | void perf_stat__reset_shadow_stats(void); |
72 | void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count, | 77 | void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count, |
73 | int cpu); | 78 | int cpu); |
74 | void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | 79 | struct perf_stat_output_ctx { |
75 | double avg, int cpu, enum aggr_mode aggr); | 80 | void *ctx; |
76 | 81 | print_metric_t print_metric; | |
77 | void perf_evsel__reset_stat_priv(struct perf_evsel *evsel); | 82 | new_line_t new_line; |
78 | int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel); | 83 | }; |
79 | void perf_evsel__free_stat_priv(struct perf_evsel *evsel); | ||
80 | |||
81 | int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel, | ||
82 | int ncpus, int nthreads); | ||
83 | void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel); | ||
84 | 84 | ||
85 | int perf_evsel__alloc_stats(struct perf_evsel *evsel, bool alloc_raw); | 85 | void perf_stat__print_shadow_stats(struct perf_evsel *evsel, |
86 | double avg, int cpu, | ||
87 | struct perf_stat_output_ctx *out); | ||
86 | 88 | ||
87 | int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw); | 89 | int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw); |
88 | void perf_evlist__free_stats(struct perf_evlist *evlist); | 90 | void perf_evlist__free_stats(struct perf_evlist *evlist); |
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 25671fa16618..8fb73295ec34 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c | |||
@@ -51,28 +51,11 @@ void strbuf_grow(struct strbuf *sb, size_t extra) | |||
51 | ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); | 51 | ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); |
52 | } | 52 | } |
53 | 53 | ||
54 | static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, | 54 | void strbuf_addch(struct strbuf *sb, int c) |
55 | const void *data, size_t dlen) | ||
56 | { | 55 | { |
57 | if (pos + len < pos) | 56 | strbuf_grow(sb, 1); |
58 | die("you want to use way too much memory"); | 57 | sb->buf[sb->len++] = c; |
59 | if (pos > sb->len) | 58 | sb->buf[sb->len] = '\0'; |
60 | die("`pos' is too far after the end of the buffer"); | ||
61 | if (pos + len > sb->len) | ||
62 | die("`pos + len' is too far after the end of the buffer"); | ||
63 | |||
64 | if (dlen >= len) | ||
65 | strbuf_grow(sb, dlen - len); | ||
66 | memmove(sb->buf + pos + dlen, | ||
67 | sb->buf + pos + len, | ||
68 | sb->len - pos - len); | ||
69 | memcpy(sb->buf + pos, data, dlen); | ||
70 | strbuf_setlen(sb, sb->len + dlen - len); | ||
71 | } | ||
72 | |||
73 | void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) | ||
74 | { | ||
75 | strbuf_splice(sb, pos, len, NULL, 0); | ||
76 | } | 59 | } |
77 | 60 | ||
78 | void strbuf_add(struct strbuf *sb, const void *data, size_t len) | 61 | void strbuf_add(struct strbuf *sb, const void *data, size_t len) |
@@ -82,7 +65,7 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len) | |||
82 | strbuf_setlen(sb, sb->len + len); | 65 | strbuf_setlen(sb, sb->len + len); |
83 | } | 66 | } |
84 | 67 | ||
85 | void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap) | 68 | static void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap) |
86 | { | 69 | { |
87 | int len; | 70 | int len; |
88 | va_list ap_saved; | 71 | va_list ap_saved; |
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index 529f2f035249..ab9be0fbbd40 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
@@ -51,16 +51,16 @@ struct strbuf { | |||
51 | #define STRBUF_INIT { 0, 0, strbuf_slopbuf } | 51 | #define STRBUF_INIT { 0, 0, strbuf_slopbuf } |
52 | 52 | ||
53 | /*----- strbuf life cycle -----*/ | 53 | /*----- strbuf life cycle -----*/ |
54 | extern void strbuf_init(struct strbuf *buf, ssize_t hint); | 54 | void strbuf_init(struct strbuf *buf, ssize_t hint); |
55 | extern void strbuf_release(struct strbuf *); | 55 | void strbuf_release(struct strbuf *buf); |
56 | extern char *strbuf_detach(struct strbuf *, size_t *); | 56 | char *strbuf_detach(struct strbuf *buf, size_t *); |
57 | 57 | ||
58 | /*----- strbuf size related -----*/ | 58 | /*----- strbuf size related -----*/ |
59 | static inline ssize_t strbuf_avail(const struct strbuf *sb) { | 59 | static inline ssize_t strbuf_avail(const struct strbuf *sb) { |
60 | return sb->alloc ? sb->alloc - sb->len - 1 : 0; | 60 | return sb->alloc ? sb->alloc - sb->len - 1 : 0; |
61 | } | 61 | } |
62 | 62 | ||
63 | extern void strbuf_grow(struct strbuf *, size_t); | 63 | void strbuf_grow(struct strbuf *buf, size_t); |
64 | 64 | ||
65 | static inline void strbuf_setlen(struct strbuf *sb, size_t len) { | 65 | static inline void strbuf_setlen(struct strbuf *sb, size_t len) { |
66 | if (!sb->alloc) | 66 | if (!sb->alloc) |
@@ -71,24 +71,17 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) { | |||
71 | } | 71 | } |
72 | 72 | ||
73 | /*----- add data in your buffer -----*/ | 73 | /*----- add data in your buffer -----*/ |
74 | static inline void strbuf_addch(struct strbuf *sb, int c) { | 74 | void strbuf_addch(struct strbuf *sb, int c); |
75 | strbuf_grow(sb, 1); | ||
76 | sb->buf[sb->len++] = c; | ||
77 | sb->buf[sb->len] = '\0'; | ||
78 | } | ||
79 | |||
80 | extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); | ||
81 | 75 | ||
82 | extern void strbuf_add(struct strbuf *, const void *, size_t); | 76 | void strbuf_add(struct strbuf *buf, const void *, size_t); |
83 | static inline void strbuf_addstr(struct strbuf *sb, const char *s) { | 77 | static inline void strbuf_addstr(struct strbuf *sb, const char *s) { |
84 | strbuf_add(sb, s, strlen(s)); | 78 | strbuf_add(sb, s, strlen(s)); |
85 | } | 79 | } |
86 | 80 | ||
87 | __attribute__((format(printf,2,3))) | 81 | __attribute__((format(printf,2,3))) |
88 | extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); | 82 | void strbuf_addf(struct strbuf *sb, const char *fmt, ...); |
89 | extern void strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap); | ||
90 | 83 | ||
91 | /* XXX: if read fails, any partial read is undone */ | 84 | /* XXX: if read fails, any partial read is undone */ |
92 | extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); | 85 | ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint); |
93 | 86 | ||
94 | #endif /* __PERF_STRBUF_H */ | 87 | #endif /* __PERF_STRBUF_H */ |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index 9292a5291445..946fdf2db97c 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
@@ -3,32 +3,31 @@ | |||
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); | 6 | void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); |
7 | extern void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | 7 | void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); |
8 | extern void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | 8 | void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); |
9 | extern void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | 9 | void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); |
10 | extern void svg_box(int Yslot, u64 start, u64 end, const char *type); | 10 | void svg_box(int Yslot, u64 start, u64 end, const char *type); |
11 | extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); | 11 | void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); |
12 | extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); | 12 | void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); |
13 | extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); | 13 | void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); |
14 | extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); | 14 | void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); |
15 | 15 | ||
16 | 16 | ||
17 | extern void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace); | 17 | void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace); |
18 | extern void svg_cstate(int cpu, u64 start, u64 end, int type); | 18 | void svg_cstate(int cpu, u64 start, u64 end, int type); |
19 | extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq); | 19 | void svg_pstate(int cpu, u64 start, u64 end, u64 freq); |
20 | 20 | ||
21 | 21 | ||
22 | extern void svg_time_grid(double min_thickness); | 22 | void svg_time_grid(double min_thickness); |
23 | extern void svg_io_legenda(void); | 23 | void svg_io_legenda(void); |
24 | extern void svg_legenda(void); | 24 | void svg_legenda(void); |
25 | extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); | 25 | void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); |
26 | extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); | 26 | void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); |
27 | extern void svg_interrupt(u64 start, int row, const char *backtrace); | 27 | void svg_interrupt(u64 start, int row, const char *backtrace); |
28 | extern void svg_text(int Yslot, u64 start, const char *text); | 28 | void svg_text(int Yslot, u64 start, const char *text); |
29 | extern void svg_close(void); | 29 | void svg_close(void); |
30 | extern int svg_build_topology_map(char *sib_core, int sib_core_nr, | 30 | int svg_build_topology_map(char *sib_core, int sib_core_nr, char *sib_thr, int sib_thr_nr); |
31 | char *sib_thr, int sib_thr_nr); | ||
32 | 31 | ||
33 | extern int svg_page_width; | 32 | extern int svg_page_width; |
34 | extern u64 svg_highlight; | 33 | extern u64 svg_highlight; |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 562b8ebeae5b..bc229a74c6a9 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <inttypes.h> | 6 | #include <inttypes.h> |
7 | 7 | ||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "demangle-java.h" | ||
9 | #include "machine.h" | 10 | #include "machine.h" |
10 | #include "vdso.h" | 11 | #include "vdso.h" |
11 | #include <symbol/kallsyms.h> | 12 | #include <symbol/kallsyms.h> |
@@ -792,6 +793,7 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
792 | uint32_t idx; | 793 | uint32_t idx; |
793 | GElf_Ehdr ehdr; | 794 | GElf_Ehdr ehdr; |
794 | GElf_Shdr shdr; | 795 | GElf_Shdr shdr; |
796 | GElf_Shdr tshdr; | ||
795 | Elf_Data *syms, *opddata = NULL; | 797 | Elf_Data *syms, *opddata = NULL; |
796 | GElf_Sym sym; | 798 | GElf_Sym sym; |
797 | Elf_Scn *sec, *sec_strndx; | 799 | Elf_Scn *sec, *sec_strndx; |
@@ -831,6 +833,9 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
831 | sec = syms_ss->symtab; | 833 | sec = syms_ss->symtab; |
832 | shdr = syms_ss->symshdr; | 834 | shdr = syms_ss->symshdr; |
833 | 835 | ||
836 | if (elf_section_by_name(elf, &ehdr, &tshdr, ".text", NULL)) | ||
837 | dso->text_offset = tshdr.sh_addr - tshdr.sh_offset; | ||
838 | |||
834 | if (runtime_ss->opdsec) | 839 | if (runtime_ss->opdsec) |
835 | opddata = elf_rawdata(runtime_ss->opdsec, NULL); | 840 | opddata = elf_rawdata(runtime_ss->opdsec, NULL); |
836 | 841 | ||
@@ -879,12 +884,8 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
879 | * Handle any relocation of vdso necessary because older kernels | 884 | * Handle any relocation of vdso necessary because older kernels |
880 | * attempted to prelink vdso to its virtual address. | 885 | * attempted to prelink vdso to its virtual address. |
881 | */ | 886 | */ |
882 | if (dso__is_vdso(dso)) { | 887 | if (dso__is_vdso(dso)) |
883 | GElf_Shdr tshdr; | 888 | map->reloc = map->start - dso->text_offset; |
884 | |||
885 | if (elf_section_by_name(elf, &ehdr, &tshdr, ".text", NULL)) | ||
886 | map->reloc = map->start - tshdr.sh_addr + tshdr.sh_offset; | ||
887 | } | ||
888 | 889 | ||
889 | dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap); | 890 | dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap); |
890 | /* | 891 | /* |
@@ -1077,6 +1078,8 @@ new_symbol: | |||
1077 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; | 1078 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; |
1078 | 1079 | ||
1079 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); | 1080 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); |
1081 | if (demangled == NULL) | ||
1082 | demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); | ||
1080 | if (demangled != NULL) | 1083 | if (demangled != NULL) |
1081 | elf_name = demangled; | 1084 | elf_name = demangled; |
1082 | } | 1085 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ab02209a7cf3..e7588dc91518 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1466,7 +1466,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1466 | * Read the build id if possible. This is required for | 1466 | * Read the build id if possible. This is required for |
1467 | * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work | 1467 | * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work |
1468 | */ | 1468 | */ |
1469 | if (filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0) | 1469 | if (is_regular_file(name) && |
1470 | filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0) | ||
1470 | dso__set_build_id(dso, build_id); | 1471 | dso__set_build_id(dso, build_id); |
1471 | 1472 | ||
1472 | /* | 1473 | /* |
@@ -1487,6 +1488,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1487 | root_dir, name, PATH_MAX)) | 1488 | root_dir, name, PATH_MAX)) |
1488 | continue; | 1489 | continue; |
1489 | 1490 | ||
1491 | if (!is_regular_file(name)) | ||
1492 | continue; | ||
1493 | |||
1490 | /* Name is now the name of the next image to try */ | 1494 | /* Name is now the name of the next image to try */ |
1491 | if (symsrc__init(ss, dso, name, symtab_type) < 0) | 1495 | if (symsrc__init(ss, dso, name, symtab_type) < 0) |
1492 | continue; | 1496 | continue; |
@@ -1525,6 +1529,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1525 | if (!runtime_ss && syms_ss) | 1529 | if (!runtime_ss && syms_ss) |
1526 | runtime_ss = syms_ss; | 1530 | runtime_ss = syms_ss; |
1527 | 1531 | ||
1532 | if (syms_ss && syms_ss->type == DSO_BINARY_TYPE__BUILD_ID_CACHE) | ||
1533 | if (dso__build_id_is_kmod(dso, name, PATH_MAX)) | ||
1534 | kmod = true; | ||
1535 | |||
1528 | if (syms_ss) | 1536 | if (syms_ss) |
1529 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); | 1537 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); |
1530 | else | 1538 | else |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ccd1caa40e11..c8b7544d9267 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -34,8 +34,8 @@ | |||
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | #ifdef HAVE_LIBELF_SUPPORT | 36 | #ifdef HAVE_LIBELF_SUPPORT |
37 | extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | 37 | Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, |
38 | GElf_Shdr *shp, const char *name, size_t *idx); | 38 | GElf_Shdr *shp, const char *name, size_t *idx); |
39 | #endif | 39 | #endif |
40 | 40 | ||
41 | #ifndef DMGL_PARAMS | 41 | #ifndef DMGL_PARAMS |
@@ -110,7 +110,8 @@ struct symbol_conf { | |||
110 | has_filter, | 110 | has_filter, |
111 | show_ref_callgraph, | 111 | show_ref_callgraph, |
112 | hide_unresolved, | 112 | hide_unresolved, |
113 | raw_trace; | 113 | raw_trace, |
114 | report_hierarchy; | ||
114 | const char *vmlinux_name, | 115 | const char *vmlinux_name, |
115 | *kallsyms_name, | 116 | *kallsyms_name, |
116 | *source_prefix, | 117 | *source_prefix, |
diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c index 802bb868d446..8ae051e0ec79 100644 --- a/tools/perf/util/trace-event.c +++ b/tools/perf/util/trace-event.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/err.h> | 10 | #include <linux/err.h> |
11 | #include <traceevent/event-parse.h> | 11 | #include <traceevent/event-parse.h> |
12 | #include <api/fs/tracing_path.h> | 12 | #include <api/fs/tracing_path.h> |
13 | #include <api/fs/fs.h> | ||
13 | #include "trace-event.h" | 14 | #include "trace-event.h" |
14 | #include "machine.h" | 15 | #include "machine.h" |
15 | #include "util.h" | 16 | #include "util.h" |
diff --git a/tools/perf/util/tsc.c b/tools/perf/util/tsc.c index 4d4210d4e13d..1b741646eed0 100644 --- a/tools/perf/util/tsc.c +++ b/tools/perf/util/tsc.c | |||
@@ -19,7 +19,7 @@ u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc) | |||
19 | u64 quot, rem; | 19 | u64 quot, rem; |
20 | 20 | ||
21 | quot = cyc >> tc->time_shift; | 21 | quot = cyc >> tc->time_shift; |
22 | rem = cyc & ((1 << tc->time_shift) - 1); | 22 | rem = cyc & (((u64)1 << tc->time_shift) - 1); |
23 | return tc->time_zero + quot * tc->time_mult + | 23 | return tc->time_zero + quot * tc->time_mult + |
24 | ((rem * tc->time_mult) >> tc->time_shift); | 24 | ((rem * tc->time_mult) >> tc->time_shift); |
25 | } | 25 | } |
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index 6adfa18cdd4e..996046a66fe5 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c | |||
@@ -41,15 +41,9 @@ static void warn_builtin(const char *warn, va_list params) | |||
41 | /* If we are in a dlopen()ed .so write to a global variable would segfault | 41 | /* If we are in a dlopen()ed .so write to a global variable would segfault |
42 | * (ugh), so keep things static. */ | 42 | * (ugh), so keep things static. */ |
43 | static void (*usage_routine)(const char *err) NORETURN = usage_builtin; | 43 | static void (*usage_routine)(const char *err) NORETURN = usage_builtin; |
44 | static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin; | ||
45 | static void (*error_routine)(const char *err, va_list params) = error_builtin; | 44 | static void (*error_routine)(const char *err, va_list params) = error_builtin; |
46 | static void (*warn_routine)(const char *err, va_list params) = warn_builtin; | 45 | static void (*warn_routine)(const char *err, va_list params) = warn_builtin; |
47 | 46 | ||
48 | void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN) | ||
49 | { | ||
50 | die_routine = routine; | ||
51 | } | ||
52 | |||
53 | void set_warning_routine(void (*routine)(const char *err, va_list params)) | 47 | void set_warning_routine(void (*routine)(const char *err, va_list params)) |
54 | { | 48 | { |
55 | warn_routine = routine; | 49 | warn_routine = routine; |
@@ -65,7 +59,7 @@ void die(const char *err, ...) | |||
65 | va_list params; | 59 | va_list params; |
66 | 60 | ||
67 | va_start(params, err); | 61 | va_start(params, err); |
68 | die_routine(err, params); | 62 | die_builtin(err, params); |
69 | va_end(params); | 63 | va_end(params); |
70 | } | 64 | } |
71 | 65 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index ead9509835d2..b7766c577b01 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <limits.h> | 14 | #include <limits.h> |
15 | #include <byteswap.h> | 15 | #include <byteswap.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/log2.h> | ||
17 | #include <unistd.h> | 18 | #include <unistd.h> |
18 | #include "callchain.h" | 19 | #include "callchain.h" |
19 | #include "strlist.h" | 20 | #include "strlist.h" |
@@ -507,54 +508,6 @@ int parse_callchain_record(const char *arg, struct callchain_param *param) | |||
507 | return ret; | 508 | return ret; |
508 | } | 509 | } |
509 | 510 | ||
510 | int filename__read_str(const char *filename, char **buf, size_t *sizep) | ||
511 | { | ||
512 | size_t size = 0, alloc_size = 0; | ||
513 | void *bf = NULL, *nbf; | ||
514 | int fd, n, err = 0; | ||
515 | char sbuf[STRERR_BUFSIZE]; | ||
516 | |||
517 | fd = open(filename, O_RDONLY); | ||
518 | if (fd < 0) | ||
519 | return -errno; | ||
520 | |||
521 | do { | ||
522 | if (size == alloc_size) { | ||
523 | alloc_size += BUFSIZ; | ||
524 | nbf = realloc(bf, alloc_size); | ||
525 | if (!nbf) { | ||
526 | err = -ENOMEM; | ||
527 | break; | ||
528 | } | ||
529 | |||
530 | bf = nbf; | ||
531 | } | ||
532 | |||
533 | n = read(fd, bf + size, alloc_size - size); | ||
534 | if (n < 0) { | ||
535 | if (size) { | ||
536 | pr_warning("read failed %d: %s\n", errno, | ||
537 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
538 | err = 0; | ||
539 | } else | ||
540 | err = -errno; | ||
541 | |||
542 | break; | ||
543 | } | ||
544 | |||
545 | size += n; | ||
546 | } while (n > 0); | ||
547 | |||
548 | if (!err) { | ||
549 | *sizep = size; | ||
550 | *buf = bf; | ||
551 | } else | ||
552 | free(bf); | ||
553 | |||
554 | close(fd); | ||
555 | return err; | ||
556 | } | ||
557 | |||
558 | const char *get_filename_for_perf_kvm(void) | 511 | const char *get_filename_for_perf_kvm(void) |
559 | { | 512 | { |
560 | const char *filename; | 513 | const char *filename; |
@@ -691,3 +644,66 @@ out: | |||
691 | 644 | ||
692 | return tip; | 645 | return tip; |
693 | } | 646 | } |
647 | |||
648 | bool is_regular_file(const char *file) | ||
649 | { | ||
650 | struct stat st; | ||
651 | |||
652 | if (stat(file, &st)) | ||
653 | return false; | ||
654 | |||
655 | return S_ISREG(st.st_mode); | ||
656 | } | ||
657 | |||
658 | int fetch_current_timestamp(char *buf, size_t sz) | ||
659 | { | ||
660 | struct timeval tv; | ||
661 | struct tm tm; | ||
662 | char dt[32]; | ||
663 | |||
664 | if (gettimeofday(&tv, NULL) || !localtime_r(&tv.tv_sec, &tm)) | ||
665 | return -1; | ||
666 | |||
667 | if (!strftime(dt, sizeof(dt), "%Y%m%d%H%M%S", &tm)) | ||
668 | return -1; | ||
669 | |||
670 | scnprintf(buf, sz, "%s%02u", dt, (unsigned)tv.tv_usec / 10000); | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | void print_binary(unsigned char *data, size_t len, | ||
676 | size_t bytes_per_line, print_binary_t printer, | ||
677 | void *extra) | ||
678 | { | ||
679 | size_t i, j, mask; | ||
680 | |||
681 | if (!printer) | ||
682 | return; | ||
683 | |||
684 | bytes_per_line = roundup_pow_of_two(bytes_per_line); | ||
685 | mask = bytes_per_line - 1; | ||
686 | |||
687 | printer(BINARY_PRINT_DATA_BEGIN, 0, extra); | ||
688 | for (i = 0; i < len; i++) { | ||
689 | if ((i & mask) == 0) { | ||
690 | printer(BINARY_PRINT_LINE_BEGIN, -1, extra); | ||
691 | printer(BINARY_PRINT_ADDR, i, extra); | ||
692 | } | ||
693 | |||
694 | printer(BINARY_PRINT_NUM_DATA, data[i], extra); | ||
695 | |||
696 | if (((i & mask) == mask) || i == len - 1) { | ||
697 | for (j = 0; j < mask-(i & mask); j++) | ||
698 | printer(BINARY_PRINT_NUM_PAD, -1, extra); | ||
699 | |||
700 | printer(BINARY_PRINT_SEP, i, extra); | ||
701 | for (j = i & ~mask; j <= i; j++) | ||
702 | printer(BINARY_PRINT_CHAR_DATA, data[j], extra); | ||
703 | for (j = 0; j < mask-(i & mask); j++) | ||
704 | printer(BINARY_PRINT_CHAR_PAD, i, extra); | ||
705 | printer(BINARY_PRINT_LINE_END, -1, extra); | ||
706 | } | ||
707 | } | ||
708 | printer(BINARY_PRINT_DATA_END, -1, extra); | ||
709 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fe915e616f9b..8298d607c738 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -82,6 +82,8 @@ | |||
82 | 82 | ||
83 | extern const char *graph_line; | 83 | extern const char *graph_line; |
84 | extern const char *graph_dotted_line; | 84 | extern const char *graph_dotted_line; |
85 | extern const char *spaces; | ||
86 | extern const char *dots; | ||
85 | extern char buildid_dir[]; | 87 | extern char buildid_dir[]; |
86 | 88 | ||
87 | /* On most systems <limits.h> would have given us this, but | 89 | /* On most systems <limits.h> would have given us this, but |
@@ -131,25 +133,15 @@ extern char buildid_dir[]; | |||
131 | #define PERF_GTK_DSO "libperf-gtk.so" | 133 | #define PERF_GTK_DSO "libperf-gtk.so" |
132 | 134 | ||
133 | /* General helper functions */ | 135 | /* General helper functions */ |
134 | extern void usage(const char *err) NORETURN; | 136 | void usage(const char *err) NORETURN; |
135 | extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); | 137 | void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); |
136 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); | 138 | int error(const char *err, ...) __attribute__((format (printf, 1, 2))); |
137 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); | 139 | void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); |
138 | 140 | ||
139 | #include "../../../include/linux/stringify.h" | 141 | void set_warning_routine(void (*routine)(const char *err, va_list params)); |
140 | 142 | ||
141 | #define DIE_IF(cnd) \ | 143 | int prefixcmp(const char *str, const char *prefix); |
142 | do { if (cnd) \ | 144 | void set_buildid_dir(const char *dir); |
143 | die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \ | ||
144 | __stringify(cnd) "\n"); \ | ||
145 | } while (0) | ||
146 | |||
147 | |||
148 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | ||
149 | extern void set_warning_routine(void (*routine)(const char *err, va_list params)); | ||
150 | |||
151 | extern int prefixcmp(const char *str, const char *prefix); | ||
152 | extern void set_buildid_dir(const char *dir); | ||
153 | 145 | ||
154 | #ifdef __GLIBC_PREREQ | 146 | #ifdef __GLIBC_PREREQ |
155 | #if __GLIBC_PREREQ(2, 1) | 147 | #if __GLIBC_PREREQ(2, 1) |
@@ -170,8 +162,7 @@ static inline char *gitstrchrnul(const char *s, int c) | |||
170 | /* | 162 | /* |
171 | * Wrappers: | 163 | * Wrappers: |
172 | */ | 164 | */ |
173 | extern char *xstrdup(const char *str); | 165 | void *xrealloc(void *ptr, size_t size) __attribute__((weak)); |
174 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); | ||
175 | 166 | ||
176 | 167 | ||
177 | static inline void *zalloc(size_t size) | 168 | static inline void *zalloc(size_t size) |
@@ -303,7 +294,6 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |||
303 | bool show_sym, bool unwind_inlines); | 294 | bool show_sym, bool unwind_inlines); |
304 | void free_srcline(char *srcline); | 295 | void free_srcline(char *srcline); |
305 | 296 | ||
306 | int filename__read_str(const char *filename, char **buf, size_t *sizep); | ||
307 | int perf_event_paranoid(void); | 297 | int perf_event_paranoid(void); |
308 | 298 | ||
309 | void mem_bswap_64(void *src, int byte_size); | 299 | void mem_bswap_64(void *src, int byte_size); |
@@ -343,5 +333,27 @@ int fetch_kernel_version(unsigned int *puint, | |||
343 | #define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) | 333 | #define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) |
344 | 334 | ||
345 | const char *perf_tip(const char *dirpath); | 335 | const char *perf_tip(const char *dirpath); |
336 | bool is_regular_file(const char *file); | ||
337 | int fetch_current_timestamp(char *buf, size_t sz); | ||
338 | |||
339 | enum binary_printer_ops { | ||
340 | BINARY_PRINT_DATA_BEGIN, | ||
341 | BINARY_PRINT_LINE_BEGIN, | ||
342 | BINARY_PRINT_ADDR, | ||
343 | BINARY_PRINT_NUM_DATA, | ||
344 | BINARY_PRINT_NUM_PAD, | ||
345 | BINARY_PRINT_SEP, | ||
346 | BINARY_PRINT_CHAR_DATA, | ||
347 | BINARY_PRINT_CHAR_PAD, | ||
348 | BINARY_PRINT_LINE_END, | ||
349 | BINARY_PRINT_DATA_END, | ||
350 | }; | ||
351 | |||
352 | typedef void (*print_binary_t)(enum binary_printer_ops, | ||
353 | unsigned int val, | ||
354 | void *extra); | ||
346 | 355 | ||
356 | void print_binary(unsigned char *data, size_t len, | ||
357 | size_t bytes_per_line, print_binary_t printer, | ||
358 | void *extra); | ||
347 | #endif /* GIT_COMPAT_UTIL_H */ | 359 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 19f15b650703..5f1a07c4b87b 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c | |||
@@ -12,18 +12,6 @@ static inline void release_pack_memory(size_t size __maybe_unused, | |||
12 | { | 12 | { |
13 | } | 13 | } |
14 | 14 | ||
15 | char *xstrdup(const char *str) | ||
16 | { | ||
17 | char *ret = strdup(str); | ||
18 | if (!ret) { | ||
19 | release_pack_memory(strlen(str) + 1, -1); | ||
20 | ret = strdup(str); | ||
21 | if (!ret) | ||
22 | die("Out of memory, strdup failed"); | ||
23 | } | ||
24 | return ret; | ||
25 | } | ||
26 | |||
27 | void *xrealloc(void *ptr, size_t size) | 15 | void *xrealloc(void *ptr, size_t size) |
28 | { | 16 | { |
29 | void *ret = realloc(ptr, size); | 17 | void *ret = realloc(ptr, size); |