aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2014-06-18 18:26:19 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2014-06-18 18:26:19 -0400
commit03ab3da3b215bac4ebb093c808d54596e03e3225 (patch)
treea42534bb7f314b561b362ad0b5af7eff8dbb9726 /tools/perf
parent6229ad278ca74acdbc8bd3a3d469322a3de91039 (diff)
parent7171511eaec5bf23fb06078f59784a3a0626b38f (diff)
Merge tag 'v3.16-rc1' into x86/cpufeature
Linux 3.16-rc1 Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Documentation/perf-diff.txt26
-rw-r--r--tools/perf/Documentation/perf-record.txt3
-rw-r--r--tools/perf/Documentation/perf-report.txt48
-rw-r--r--tools/perf/Documentation/perf-top.txt38
-rw-r--r--tools/perf/MANIFEST2
-rw-r--r--tools/perf/Makefile.perf26
-rw-r--r--tools/perf/arch/arm/Makefile7
-rw-r--r--tools/perf/arch/arm/include/perf_regs.h7
-rw-r--r--tools/perf/arch/arm/tests/dwarf-unwind.c60
-rw-r--r--tools/perf/arch/arm/tests/regs_load.S58
-rw-r--r--tools/perf/arch/arm/util/unwind-libdw.c36
-rw-r--r--tools/perf/arch/arm64/Makefile7
-rw-r--r--tools/perf/arch/arm64/include/perf_regs.h88
-rw-r--r--tools/perf/arch/arm64/util/dwarf-regs.c80
-rw-r--r--tools/perf/arch/arm64/util/unwind-libunwind.c82
-rw-r--r--tools/perf/arch/x86/include/perf_regs.h2
-rw-r--r--tools/perf/arch/x86/tests/dwarf-unwind.c2
-rw-r--r--tools/perf/arch/x86/util/tsc.c2
-rw-r--r--tools/perf/arch/x86/util/tsc.h2
-rw-r--r--tools/perf/builtin-annotate.c8
-rw-r--r--tools/perf/builtin-diff.c52
-rw-r--r--tools/perf/builtin-inject.c2
-rw-r--r--tools/perf/builtin-kmem.c88
-rw-r--r--tools/perf/builtin-lock.c10
-rw-r--r--tools/perf/builtin-mem.c15
-rw-r--r--tools/perf/builtin-record.c163
-rw-r--r--tools/perf/builtin-report.c372
-rw-r--r--tools/perf/builtin-sched.c82
-rw-r--r--tools/perf/builtin-top.c112
-rw-r--r--tools/perf/config/Makefile23
-rw-r--r--tools/perf/config/feature-checks/Makefile4
-rw-r--r--tools/perf/config/feature-checks/test-all.c5
-rw-r--r--tools/perf/config/feature-checks/test-on-exit.c16
-rw-r--r--tools/perf/perf-completion.sh4
-rw-r--r--tools/perf/perf-sys.h190
-rw-r--r--tools/perf/perf.c8
-rw-r--r--tools/perf/perf.h248
-rw-r--r--tools/perf/tests/attr.c7
-rw-r--r--tools/perf/tests/builtin-test.c22
-rw-r--r--tools/perf/tests/code-reading.c5
-rw-r--r--tools/perf/tests/dso-data.c2
-rw-r--r--tools/perf/tests/dwarf-unwind.c2
-rw-r--r--tools/perf/tests/evsel-tp-sched.c3
-rw-r--r--tools/perf/tests/hists_common.c209
-rw-r--r--tools/perf/tests/hists_common.h75
-rw-r--r--tools/perf/tests/hists_cumulate.c726
-rw-r--r--tools/perf/tests/hists_filter.c289
-rw-r--r--tools/perf/tests/hists_link.c209
-rw-r--r--tools/perf/tests/hists_output.c621
-rw-r--r--tools/perf/tests/keep-tracking.c2
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c233
-rw-r--r--tools/perf/tests/parse-events.c142
-rw-r--r--tools/perf/tests/parse-no-sample-id-all.c2
-rw-r--r--tools/perf/tests/perf-time-to-tsc.c3
-rw-r--r--tools/perf/tests/rdpmc.c2
-rw-r--r--tools/perf/tests/sample-parsing.c2
-rw-r--r--tools/perf/tests/tests.h7
-rw-r--r--tools/perf/tests/thread-mg-share.c90
-rw-r--r--tools/perf/ui/browser.c2
-rw-r--r--tools/perf/ui/browser.h4
-rw-r--r--tools/perf/ui/browsers/hists.c270
-rw-r--r--tools/perf/ui/gtk/hists.c77
-rw-r--r--tools/perf/ui/hist.c371
-rw-r--r--tools/perf/ui/progress.h2
-rw-r--r--tools/perf/ui/setup.c2
-rw-r--r--tools/perf/ui/stdio/hist.c89
-rw-r--r--tools/perf/util/annotate.h2
-rw-r--r--tools/perf/util/build-id.c2
-rw-r--r--tools/perf/util/build-id.h2
-rw-r--r--tools/perf/util/callchain.c123
-rw-r--r--tools/perf/util/callchain.h19
-rw-r--r--tools/perf/util/config.c4
-rw-r--r--tools/perf/util/cpumap.c160
-rw-r--r--tools/perf/util/cpumap.h35
-rw-r--r--tools/perf/util/dso.h2
-rw-r--r--tools/perf/util/dwarf-aux.c7
-rw-r--r--tools/perf/util/event.c4
-rw-r--r--tools/perf/util/event.h24
-rw-r--r--tools/perf/util/evsel.h9
-rw-r--r--tools/perf/util/header.h4
-rw-r--r--tools/perf/util/hist.c668
-rw-r--r--tools/perf/util/hist.h101
-rw-r--r--tools/perf/util/include/linux/bitmap.h3
-rw-r--r--tools/perf/util/include/linux/export.h6
-rw-r--r--tools/perf/util/include/linux/list.h1
-rw-r--r--tools/perf/util/include/linux/types.h29
-rw-r--r--tools/perf/util/machine.c11
-rw-r--r--tools/perf/util/map.c118
-rw-r--r--tools/perf/util/map.h14
-rw-r--r--tools/perf/util/pager.c12
-rw-r--r--tools/perf/util/parse-events.h3
-rw-r--r--tools/perf/util/parse-events.y14
-rw-r--r--tools/perf/util/perf_regs.h2
-rw-r--r--tools/perf/util/pmu.c6
-rw-r--r--tools/perf/util/pmu.h2
-rw-r--r--tools/perf/util/probe-finder.c4
-rw-r--r--tools/perf/util/session.c5
-rw-r--r--tools/perf/util/sort.c523
-rw-r--r--tools/perf/util/sort.h26
-rw-r--r--tools/perf/util/stat.h2
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/svghelper.h2
-rw-r--r--tools/perf/util/symbol.c11
-rw-r--r--tools/perf/util/symbol.h5
-rw-r--r--tools/perf/util/thread.c52
-rw-r--r--tools/perf/util/thread.h3
-rw-r--r--tools/perf/util/top.h2
-rw-r--r--tools/perf/util/types.h24
-rw-r--r--tools/perf/util/unwind-libdw.c2
-rw-r--r--tools/perf/util/unwind.h2
-rw-r--r--tools/perf/util/util.c2
-rw-r--r--tools/perf/util/util.h2
-rw-r--r--tools/perf/util/values.h2
113 files changed, 5843 insertions, 1663 deletions
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index fdfceee0ffd0..b3b8abae62b8 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -33,21 +33,25 @@ OPTIONS
33-d:: 33-d::
34--dsos=:: 34--dsos=::
35 Only consider symbols in these dsos. CSV that understands 35 Only consider symbols in these dsos. CSV that understands
36 file://filename entries. 36 file://filename entries. This option will affect the percentage
37 of the Baseline/Delta column. See --percentage for more info.
37 38
38-C:: 39-C::
39--comms=:: 40--comms=::
40 Only consider symbols in these comms. CSV that understands 41 Only consider symbols in these comms. CSV that understands
41 file://filename entries. 42 file://filename entries. This option will affect the percentage
43 of the Baseline/Delta column. See --percentage for more info.
42 44
43-S:: 45-S::
44--symbols=:: 46--symbols=::
45 Only consider these symbols. CSV that understands 47 Only consider these symbols. CSV that understands
46 file://filename entries. 48 file://filename entries. This option will affect the percentage
49 of the Baseline/Delta column. See --percentage for more info.
47 50
48-s:: 51-s::
49--sort=:: 52--sort=::
50 Sort by key(s): pid, comm, dso, symbol. 53 Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline.
54 Please see description of --sort in the perf-report man page.
51 55
52-t:: 56-t::
53--field-separator=:: 57--field-separator=::
@@ -89,6 +93,14 @@ OPTIONS
89--order:: 93--order::
90 Specify compute sorting column number. 94 Specify compute sorting column number.
91 95
96--percentage::
97 Determine how to display the overhead percentage of filtered entries.
98 Filters can be applied by --comms, --dsos and/or --symbols options.
99
100 "relative" means it's relative to filtered entries only so that the
101 sum of shown entries will be always 100%. "absolute" means it retains
102 the original value before and after the filter is applied.
103
92COMPARISON 104COMPARISON
93---------- 105----------
94The comparison is governed by the baseline file. The baseline perf.data 106The comparison is governed by the baseline file. The baseline perf.data
@@ -157,6 +169,10 @@ with:
157 - period_percent being the % of the hist entry period value within 169 - period_percent being the % of the hist entry period value within
158 single data file 170 single data file
159 171
172 - with filtering by -C, -d and/or -S, period_percent might be changed
173 relative to how entries are filtered. Use --percentage=absolute to
174 prevent such fluctuation.
175
160ratio 176ratio
161~~~~~ 177~~~~~
162If specified the 'Ratio' column is displayed with value 'r' computed as: 178If specified the 'Ratio' column is displayed with value 'r' computed as:
@@ -187,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
187 203
188SEE ALSO 204SEE ALSO
189-------- 205--------
190linkperf:perf-record[1] 206linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index c71b0f36d9e8..d460049cae8e 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -184,9 +184,10 @@ following filters are defined:
184 - in_tx: only when the target is in a hardware transaction 184 - in_tx: only when the target is in a hardware transaction
185 - no_tx: only when the target is not in a hardware transaction 185 - no_tx: only when the target is not in a hardware transaction
186 - abort_tx: only when the target is a hardware transaction abort 186 - abort_tx: only when the target is a hardware transaction abort
187 - cond: conditional branches
187 188
188+ 189+
189The option requires at least one branch type among any, any_call, any_ret, ind_call. 190The option requires at least one branch type among any, any_call, any_ret, ind_call, cond.
190The privilege levels may be omitted, in which case, the privilege levels of the associated 191The privilege levels may be omitted, in which case, the privilege levels of the associated
191event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege 192event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
192levels are subject to permissions. When sampling on multiple events, branch stack sampling 193levels are subject to permissions. When sampling on multiple events, branch stack sampling
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 8eab8a4bdeb8..cefdf430d1b4 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -25,10 +25,6 @@ OPTIONS
25--verbose:: 25--verbose::
26 Be more verbose. (show symbol address, etc) 26 Be more verbose. (show symbol address, etc)
27 27
28-d::
29--dsos=::
30 Only consider symbols in these dsos. CSV that understands
31 file://filename entries.
32-n:: 28-n::
33--show-nr-samples:: 29--show-nr-samples::
34 Show the number of samples for each symbol 30 Show the number of samples for each symbol
@@ -42,11 +38,18 @@ OPTIONS
42-c:: 38-c::
43--comms=:: 39--comms=::
44 Only consider symbols in these comms. CSV that understands 40 Only consider symbols in these comms. CSV that understands
45 file://filename entries. 41 file://filename entries. This option will affect the percentage of
42 the overhead column. See --percentage for more info.
43-d::
44--dsos=::
45 Only consider symbols in these dsos. CSV that understands
46 file://filename entries. This option will affect the percentage of
47 the overhead column. See --percentage for more info.
46-S:: 48-S::
47--symbols=:: 49--symbols=::
48 Only consider these symbols. CSV that understands 50 Only consider these symbols. CSV that understands
49 file://filename entries. 51 file://filename entries. This option will affect the percentage of
52 the overhead column. See --percentage for more info.
50 53
51--symbol-filter=:: 54--symbol-filter=::
52 Only show symbols that match (partially) with this filter. 55 Only show symbols that match (partially) with this filter.
@@ -76,6 +79,15 @@ OPTIONS
76 abort cost. This is the global weight. 79 abort cost. This is the global weight.
77 - local_weight: Local weight version of the weight above. 80 - local_weight: Local weight version of the weight above.
78 - transaction: Transaction abort flags. 81 - transaction: Transaction abort flags.
82 - overhead: Overhead percentage of sample
83 - overhead_sys: Overhead percentage of sample running in system mode
84 - overhead_us: Overhead percentage of sample running in user mode
85 - overhead_guest_sys: Overhead percentage of sample running in system mode
86 on guest machine
87 - overhead_guest_us: Overhead percentage of sample running in user mode on
88 guest machine
89 - sample: Number of sample
90 - period: Raw number of event count of sample
79 91
80 By default, comm, dso and symbol keys are used. 92 By default, comm, dso and symbol keys are used.
81 (i.e. --sort comm,dso,symbol) 93 (i.e. --sort comm,dso,symbol)
@@ -95,6 +107,16 @@ OPTIONS
95 And default sort keys are changed to comm, dso_from, symbol_from, dso_to 107 And default sort keys are changed to comm, dso_from, symbol_from, dso_to
96 and symbol_to, see '--branch-stack'. 108 and symbol_to, see '--branch-stack'.
97 109
110-F::
111--fields=::
112 Specify output field - multiple keys can be specified in CSV format.
113 Following fields are available:
114 overhead, overhead_sys, overhead_us, overhead_children, sample and period.
115 Also it can contain any sort key(s).
116
117 By default, every sort keys not specified in -F will be appended
118 automatically.
119
98-p:: 120-p::
99--parent=<regex>:: 121--parent=<regex>::
100 A regex filter to identify parent. The parent is a caller of this 122 A regex filter to identify parent. The parent is a caller of this
@@ -141,6 +163,11 @@ OPTIONS
141 163
142 Default: fractal,0.5,callee,function. 164 Default: fractal,0.5,callee,function.
143 165
166--children::
167 Accumulate callchain of children to parent entry so that then can
168 show up in the output. The output will have a new "Children" column
169 and will be sorted on the data. It requires callchains are recorded.
170
144--max-stack:: 171--max-stack::
145 Set the stack depth limit when parsing the callchain, anything 172 Set the stack depth limit when parsing the callchain, anything
146 beyond the specified depth will be ignored. This is a trade-off 173 beyond the specified depth will be ignored. This is a trade-off
@@ -237,6 +264,15 @@ OPTIONS
237 Do not show entries which have an overhead under that percent. 264 Do not show entries which have an overhead under that percent.
238 (Default: 0). 265 (Default: 0).
239 266
267--percentage::
268 Determine how to display the overhead percentage of filtered entries.
269 Filters can be applied by --comms, --dsos and/or --symbols options and
270 Zoom operations on the TUI (thread, dso, etc).
271
272 "relative" means it's relative to filtered entries only so that the
273 sum of shown entries will be always 100%. "absolute" means it retains
274 the original value before and after the filter is applied.
275
240--header:: 276--header::
241 Show header information in the perf.data file. This includes 277 Show header information in the perf.data file. This includes
242 various information like hostname, OS and perf version, cpu/mem 278 various information like hostname, OS and perf version, cpu/mem
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 976b00c6cdb1..180ae02137a5 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -113,7 +113,17 @@ Default is to monitor all CPUS.
113-s:: 113-s::
114--sort:: 114--sort::
115 Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, 115 Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight,
116 local_weight, abort, in_tx, transaction 116 local_weight, abort, in_tx, transaction, overhead, sample, period.
117 Please see description of --sort in the perf-report man page.
118
119--fields=::
120 Specify output field - multiple keys can be specified in CSV format.
121 Following fields are available:
122 overhead, overhead_sys, overhead_us, overhead_children, sample and period.
123 Also it can contain any sort key(s).
124
125 By default, every sort keys not specified in --field will be appended
126 automatically.
117 127
118-n:: 128-n::
119--show-nr-samples:: 129--show-nr-samples::
@@ -123,13 +133,16 @@ Default is to monitor all CPUS.
123 Show a column with the sum of periods. 133 Show a column with the sum of periods.
124 134
125--dsos:: 135--dsos::
126 Only consider symbols in these dsos. 136 Only consider symbols in these dsos. This option will affect the
137 percentage of the overhead column. See --percentage for more info.
127 138
128--comms:: 139--comms::
129 Only consider symbols in these comms. 140 Only consider symbols in these comms. This option will affect the
141 percentage of the overhead column. See --percentage for more info.
130 142
131--symbols:: 143--symbols::
132 Only consider these symbols. 144 Only consider these symbols. This option will affect the
145 percentage of the overhead column. See --percentage for more info.
133 146
134-M:: 147-M::
135--disassembler-style=:: Set disassembler style for objdump. 148--disassembler-style=:: Set disassembler style for objdump.
@@ -148,6 +161,12 @@ Default is to monitor all CPUS.
148 Setup and enable call-graph (stack chain/backtrace) recording, 161 Setup and enable call-graph (stack chain/backtrace) recording,
149 implies -g. 162 implies -g.
150 163
164--children::
165 Accumulate callchain of children to parent entry so that then can
166 show up in the output. The output will have a new "Children" column
167 and will be sorted on the data. It requires -g/--call-graph option
168 enabled.
169
151--max-stack:: 170--max-stack::
152 Set the stack depth limit when parsing the callchain, anything 171 Set the stack depth limit when parsing the callchain, anything
153 beyond the specified depth will be ignored. This is a trade-off 172 beyond the specified depth will be ignored. This is a trade-off
@@ -165,6 +184,15 @@ Default is to monitor all CPUS.
165 Do not show entries which have an overhead under that percent. 184 Do not show entries which have an overhead under that percent.
166 (Default: 0). 185 (Default: 0).
167 186
187--percentage::
188 Determine how to display the overhead percentage of filtered entries.
189 Filters can be applied by --comms, --dsos and/or --symbols options and
190 Zoom operations on the TUI (thread, dso, etc).
191
192 "relative" means it's relative to filtered entries only so that the
193 sum of shown entries will be always 100%. "absolute" means it retains
194 the original value before and after the filter is applied.
195
168INTERACTIVE PROMPTING KEYS 196INTERACTIVE PROMPTING KEYS
169-------------------------- 197--------------------------
170 198
@@ -200,4 +228,4 @@ Pressing any unmapped key displays a menu, and prompts for input.
200 228
201SEE ALSO 229SEE ALSO
202-------- 230--------
203linkperf:perf-stat[1], linkperf:perf-list[1] 231linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1]
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index c0c87c87b60f..45da209b6ed3 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -7,6 +7,8 @@ tools/lib/symbol/kallsyms.h
7tools/include/asm/bug.h 7tools/include/asm/bug.h
8tools/include/linux/compiler.h 8tools/include/linux/compiler.h
9tools/include/linux/hash.h 9tools/include/linux/hash.h
10tools/include/linux/export.h
11tools/include/linux/types.h
10include/linux/const.h 12include/linux/const.h
11include/linux/perf_event.h 13include/linux/perf_event.h
12include/linux/rbtree.h 14include/linux/rbtree.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 895edd32930c..ae20edfcc3f7 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -222,12 +222,12 @@ LIB_H += util/include/linux/const.h
222LIB_H += util/include/linux/ctype.h 222LIB_H += util/include/linux/ctype.h
223LIB_H += util/include/linux/kernel.h 223LIB_H += util/include/linux/kernel.h
224LIB_H += util/include/linux/list.h 224LIB_H += util/include/linux/list.h
225LIB_H += util/include/linux/export.h 225LIB_H += ../include/linux/export.h
226LIB_H += util/include/linux/poison.h 226LIB_H += util/include/linux/poison.h
227LIB_H += util/include/linux/rbtree.h 227LIB_H += util/include/linux/rbtree.h
228LIB_H += util/include/linux/rbtree_augmented.h 228LIB_H += util/include/linux/rbtree_augmented.h
229LIB_H += util/include/linux/string.h 229LIB_H += util/include/linux/string.h
230LIB_H += util/include/linux/types.h 230LIB_H += ../include/linux/types.h
231LIB_H += util/include/linux/linkage.h 231LIB_H += util/include/linux/linkage.h
232LIB_H += util/include/asm/asm-offsets.h 232LIB_H += util/include/asm/asm-offsets.h
233LIB_H += ../include/asm/bug.h 233LIB_H += ../include/asm/bug.h
@@ -252,7 +252,6 @@ LIB_H += util/event.h
252LIB_H += util/evsel.h 252LIB_H += util/evsel.h
253LIB_H += util/evlist.h 253LIB_H += util/evlist.h
254LIB_H += util/exec_cmd.h 254LIB_H += util/exec_cmd.h
255LIB_H += util/types.h
256LIB_H += util/levenshtein.h 255LIB_H += util/levenshtein.h
257LIB_H += util/machine.h 256LIB_H += util/machine.h
258LIB_H += util/map.h 257LIB_H += util/map.h
@@ -397,7 +396,11 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o
397LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o 396LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
398LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o 397LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
399LIB_OBJS += $(OUTPUT)tests/pmu.o 398LIB_OBJS += $(OUTPUT)tests/pmu.o
399LIB_OBJS += $(OUTPUT)tests/hists_common.o
400LIB_OBJS += $(OUTPUT)tests/hists_link.o 400LIB_OBJS += $(OUTPUT)tests/hists_link.o
401LIB_OBJS += $(OUTPUT)tests/hists_filter.o
402LIB_OBJS += $(OUTPUT)tests/hists_output.o
403LIB_OBJS += $(OUTPUT)tests/hists_cumulate.o
401LIB_OBJS += $(OUTPUT)tests/python-use.o 404LIB_OBJS += $(OUTPUT)tests/python-use.o
402LIB_OBJS += $(OUTPUT)tests/bp_signal.o 405LIB_OBJS += $(OUTPUT)tests/bp_signal.o
403LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o 406LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
@@ -410,10 +413,12 @@ LIB_OBJS += $(OUTPUT)tests/code-reading.o
410LIB_OBJS += $(OUTPUT)tests/sample-parsing.o 413LIB_OBJS += $(OUTPUT)tests/sample-parsing.o
411LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o 414LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o
412ifndef NO_DWARF_UNWIND 415ifndef NO_DWARF_UNWIND
413ifeq ($(ARCH),x86) 416ifeq ($(ARCH),$(filter $(ARCH),x86 arm))
414LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o 417LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o
415endif 418endif
416endif 419endif
420LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
421LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
417 422
418BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o 423BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
419BUILTIN_OBJS += $(OUTPUT)builtin-bench.o 424BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
@@ -784,8 +789,8 @@ help:
784 @echo '' 789 @echo ''
785 @echo 'Perf install targets:' 790 @echo 'Perf install targets:'
786 @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' 791 @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed'
787 @echo ' HINT: use "make prefix=<path> <install target>" to install to a particular' 792 @echo ' HINT: use "prefix" or "DESTDIR" to install to a particular'
788 @echo ' path like make prefix=/usr/local install install-doc' 793 @echo ' path like "make prefix=/usr/local install install-doc"'
789 @echo ' install - install compiled binaries' 794 @echo ' install - install compiled binaries'
790 @echo ' install-doc - install *all* documentation' 795 @echo ' install-doc - install *all* documentation'
791 @echo ' install-man - install manpage documentation' 796 @echo ' install-man - install manpage documentation'
@@ -810,17 +815,20 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
810$(DOC_TARGETS): 815$(DOC_TARGETS):
811 $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) 816 $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
812 817
818TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
819TAG_FILES= ../../include/uapi/linux/perf_event.h
820
813TAGS: 821TAGS:
814 $(RM) TAGS 822 $(RM) TAGS
815 $(FIND) . -name '*.[hcS]' -print | xargs etags -a 823 $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs etags -a $(TAG_FILES)
816 824
817tags: 825tags:
818 $(RM) tags 826 $(RM) tags
819 $(FIND) . -name '*.[hcS]' -print | xargs ctags -a 827 $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs ctags -a $(TAG_FILES)
820 828
821cscope: 829cscope:
822 $(RM) cscope* 830 $(RM) cscope*
823 $(FIND) . -name '*.[hcS]' -print | xargs cscope -b 831 $(FIND) $(TAG_FOLDERS) -name '*.[hcS]' -print | xargs cscope -b $(TAG_FILES)
824 832
825### Detect prefix changes 833### Detect prefix changes
826TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ 834TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile
index 67e9b3d38e89..09d62153d384 100644
--- a/tools/perf/arch/arm/Makefile
+++ b/tools/perf/arch/arm/Makefile
@@ -5,3 +5,10 @@ endif
5ifndef NO_LIBUNWIND 5ifndef NO_LIBUNWIND
6LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o 6LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
7endif 7endif
8ifndef NO_LIBDW_DWARF_UNWIND
9LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o
10endif
11ifndef NO_DWARF_UNWIND
12LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o
13LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o
14endif
diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h
index 2a1cfde66b69..f619c9c5a4bf 100644
--- a/tools/perf/arch/arm/include/perf_regs.h
+++ b/tools/perf/arch/arm/include/perf_regs.h
@@ -2,10 +2,15 @@
2#define ARCH_PERF_REGS_H 2#define ARCH_PERF_REGS_H
3 3
4#include <stdlib.h> 4#include <stdlib.h>
5#include "../../util/types.h" 5#include <linux/types.h>
6#include <asm/perf_regs.h> 6#include <asm/perf_regs.h>
7 7
8void perf_regs_load(u64 *regs);
9
8#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1) 10#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1)
11#define PERF_REGS_MAX PERF_REG_ARM_MAX
12#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
13
9#define PERF_REG_IP PERF_REG_ARM_PC 14#define PERF_REG_IP PERF_REG_ARM_PC
10#define PERF_REG_SP PERF_REG_ARM_SP 15#define PERF_REG_SP PERF_REG_ARM_SP
11 16
diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c
new file mode 100644
index 000000000000..9f870d27cb39
--- /dev/null
+++ b/tools/perf/arch/arm/tests/dwarf-unwind.c
@@ -0,0 +1,60 @@
1#include <string.h>
2#include "perf_regs.h"
3#include "thread.h"
4#include "map.h"
5#include "event.h"
6#include "tests/tests.h"
7
8#define STACK_SIZE 8192
9
10static int sample_ustack(struct perf_sample *sample,
11 struct thread *thread, u64 *regs)
12{
13 struct stack_dump *stack = &sample->user_stack;
14 struct map *map;
15 unsigned long sp;
16 u64 stack_size, *buf;
17
18 buf = malloc(STACK_SIZE);
19 if (!buf) {
20 pr_debug("failed to allocate sample uregs data\n");
21 return -1;
22 }
23
24 sp = (unsigned long) regs[PERF_REG_ARM_SP];
25
26 map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp);
27 if (!map) {
28 pr_debug("failed to get stack map\n");
29 free(buf);
30 return -1;
31 }
32
33 stack_size = map->end - sp;
34 stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
35
36 memcpy(buf, (void *) sp, stack_size);
37 stack->data = (char *) buf;
38 stack->size = stack_size;
39 return 0;
40}
41
42int test__arch_unwind_sample(struct perf_sample *sample,
43 struct thread *thread)
44{
45 struct regs_dump *regs = &sample->user_regs;
46 u64 *buf;
47
48 buf = calloc(1, sizeof(u64) * PERF_REGS_MAX);
49 if (!buf) {
50 pr_debug("failed to allocate sample uregs data\n");
51 return -1;
52 }
53
54 perf_regs_load(buf);
55 regs->abi = PERF_SAMPLE_REGS_ABI;
56 regs->regs = buf;
57 regs->mask = PERF_REGS_MASK;
58
59 return sample_ustack(sample, thread, buf);
60}
diff --git a/tools/perf/arch/arm/tests/regs_load.S b/tools/perf/arch/arm/tests/regs_load.S
new file mode 100644
index 000000000000..e09e983946fe
--- /dev/null
+++ b/tools/perf/arch/arm/tests/regs_load.S
@@ -0,0 +1,58 @@
1#include <linux/linkage.h>
2
3#define R0 0x00
4#define R1 0x08
5#define R2 0x10
6#define R3 0x18
7#define R4 0x20
8#define R5 0x28
9#define R6 0x30
10#define R7 0x38
11#define R8 0x40
12#define R9 0x48
13#define SL 0x50
14#define FP 0x58
15#define IP 0x60
16#define SP 0x68
17#define LR 0x70
18#define PC 0x78
19
20/*
21 * Implementation of void perf_regs_load(u64 *regs);
22 *
23 * This functions fills in the 'regs' buffer from the actual registers values,
24 * in the way the perf built-in unwinding test expects them:
25 * - the PC at the time at the call to this function. Since this function
26 * is called using a bl instruction, the PC value is taken from LR.
27 * The built-in unwinding test then unwinds the call stack from the dwarf
28 * information in unwind__get_entries.
29 *
30 * Notes:
31 * - the 8 bytes stride in the registers offsets comes from the fact
32 * that the registers are stored in an u64 array (u64 *regs),
33 * - the regs buffer needs to be zeroed before the call to this function,
34 * in this case using a calloc in dwarf-unwind.c.
35 */
36
37.text
38.type perf_regs_load,%function
39ENTRY(perf_regs_load)
40 str r0, [r0, #R0]
41 str r1, [r0, #R1]
42 str r2, [r0, #R2]
43 str r3, [r0, #R3]
44 str r4, [r0, #R4]
45 str r5, [r0, #R5]
46 str r6, [r0, #R6]
47 str r7, [r0, #R7]
48 str r8, [r0, #R8]
49 str r9, [r0, #R9]
50 str sl, [r0, #SL]
51 str fp, [r0, #FP]
52 str ip, [r0, #IP]
53 str sp, [r0, #SP]
54 str lr, [r0, #LR]
55 str lr, [r0, #PC] // store pc as lr in order to skip the call
56 // to this function
57 mov pc, lr
58ENDPROC(perf_regs_load)
diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c
new file mode 100644
index 000000000000..b4176c60117a
--- /dev/null
+++ b/tools/perf/arch/arm/util/unwind-libdw.c
@@ -0,0 +1,36 @@
1#include <elfutils/libdwfl.h>
2#include "../../util/unwind-libdw.h"
3#include "../../util/perf_regs.h"
4
5bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
6{
7 struct unwind_info *ui = arg;
8 struct regs_dump *user_regs = &ui->sample->user_regs;
9 Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX];
10
11#define REG(r) ({ \
12 Dwarf_Word val = 0; \
13 perf_reg_value(&val, user_regs, PERF_REG_ARM_##r); \
14 val; \
15})
16
17 dwarf_regs[0] = REG(R0);
18 dwarf_regs[1] = REG(R1);
19 dwarf_regs[2] = REG(R2);
20 dwarf_regs[3] = REG(R3);
21 dwarf_regs[4] = REG(R4);
22 dwarf_regs[5] = REG(R5);
23 dwarf_regs[6] = REG(R6);
24 dwarf_regs[7] = REG(R7);
25 dwarf_regs[8] = REG(R8);
26 dwarf_regs[9] = REG(R9);
27 dwarf_regs[10] = REG(R10);
28 dwarf_regs[11] = REG(FP);
29 dwarf_regs[12] = REG(IP);
30 dwarf_regs[13] = REG(SP);
31 dwarf_regs[14] = REG(LR);
32 dwarf_regs[15] = REG(PC);
33
34 return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX,
35 dwarf_regs);
36}
diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile
new file mode 100644
index 000000000000..67e9b3d38e89
--- /dev/null
+++ b/tools/perf/arch/arm64/Makefile
@@ -0,0 +1,7 @@
1ifndef NO_DWARF
2PERF_HAVE_DWARF_REGS := 1
3LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
4endif
5ifndef NO_LIBUNWIND
6LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o
7endif
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h
new file mode 100644
index 000000000000..e9441b9e2a30
--- /dev/null
+++ b/tools/perf/arch/arm64/include/perf_regs.h
@@ -0,0 +1,88 @@
1#ifndef ARCH_PERF_REGS_H
2#define ARCH_PERF_REGS_H
3
4#include <stdlib.h>
5#include <linux/types.h>
6#include <asm/perf_regs.h>
7
8#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
9#define PERF_REG_IP PERF_REG_ARM64_PC
10#define PERF_REG_SP PERF_REG_ARM64_SP
11
12static inline const char *perf_reg_name(int id)
13{
14 switch (id) {
15 case PERF_REG_ARM64_X0:
16 return "x0";
17 case PERF_REG_ARM64_X1:
18 return "x1";
19 case PERF_REG_ARM64_X2:
20 return "x2";
21 case PERF_REG_ARM64_X3:
22 return "x3";
23 case PERF_REG_ARM64_X4:
24 return "x4";
25 case PERF_REG_ARM64_X5:
26 return "x5";
27 case PERF_REG_ARM64_X6:
28 return "x6";
29 case PERF_REG_ARM64_X7:
30 return "x7";
31 case PERF_REG_ARM64_X8:
32 return "x8";
33 case PERF_REG_ARM64_X9:
34 return "x9";
35 case PERF_REG_ARM64_X10:
36 return "x10";
37 case PERF_REG_ARM64_X11:
38 return "x11";
39 case PERF_REG_ARM64_X12:
40 return "x12";
41 case PERF_REG_ARM64_X13:
42 return "x13";
43 case PERF_REG_ARM64_X14:
44 return "x14";
45 case PERF_REG_ARM64_X15:
46 return "x15";
47 case PERF_REG_ARM64_X16:
48 return "x16";
49 case PERF_REG_ARM64_X17:
50 return "x17";
51 case PERF_REG_ARM64_X18:
52 return "x18";
53 case PERF_REG_ARM64_X19:
54 return "x19";
55 case PERF_REG_ARM64_X20:
56 return "x20";
57 case PERF_REG_ARM64_X21:
58 return "x21";
59 case PERF_REG_ARM64_X22:
60 return "x22";
61 case PERF_REG_ARM64_X23:
62 return "x23";
63 case PERF_REG_ARM64_X24:
64 return "x24";
65 case PERF_REG_ARM64_X25:
66 return "x25";
67 case PERF_REG_ARM64_X26:
68 return "x26";
69 case PERF_REG_ARM64_X27:
70 return "x27";
71 case PERF_REG_ARM64_X28:
72 return "x28";
73 case PERF_REG_ARM64_X29:
74 return "x29";
75 case PERF_REG_ARM64_SP:
76 return "sp";
77 case PERF_REG_ARM64_LR:
78 return "lr";
79 case PERF_REG_ARM64_PC:
80 return "pc";
81 default:
82 return NULL;
83 }
84
85 return NULL;
86}
87
88#endif /* ARCH_PERF_REGS_H */
diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c
new file mode 100644
index 000000000000..d49efeb8172e
--- /dev/null
+++ b/tools/perf/arch/arm64/util/dwarf-regs.c
@@ -0,0 +1,80 @@
1/*
2 * Mapping of DWARF debug register numbers into register names.
3 *
4 * Copyright (C) 2010 Will Deacon, ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <stddef.h>
12#include <dwarf-regs.h>
13
14struct pt_regs_dwarfnum {
15 const char *name;
16 unsigned int dwarfnum;
17};
18
19#define STR(s) #s
20#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
21#define GPR_DWARFNUM_NAME(num) \
22 {.name = STR(%x##num), .dwarfnum = num}
23#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
24
25/*
26 * Reference:
27 * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf
28 */
29static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
30 GPR_DWARFNUM_NAME(0),
31 GPR_DWARFNUM_NAME(1),
32 GPR_DWARFNUM_NAME(2),
33 GPR_DWARFNUM_NAME(3),
34 GPR_DWARFNUM_NAME(4),
35 GPR_DWARFNUM_NAME(5),
36 GPR_DWARFNUM_NAME(6),
37 GPR_DWARFNUM_NAME(7),
38 GPR_DWARFNUM_NAME(8),
39 GPR_DWARFNUM_NAME(9),
40 GPR_DWARFNUM_NAME(10),
41 GPR_DWARFNUM_NAME(11),
42 GPR_DWARFNUM_NAME(12),
43 GPR_DWARFNUM_NAME(13),
44 GPR_DWARFNUM_NAME(14),
45 GPR_DWARFNUM_NAME(15),
46 GPR_DWARFNUM_NAME(16),
47 GPR_DWARFNUM_NAME(17),
48 GPR_DWARFNUM_NAME(18),
49 GPR_DWARFNUM_NAME(19),
50 GPR_DWARFNUM_NAME(20),
51 GPR_DWARFNUM_NAME(21),
52 GPR_DWARFNUM_NAME(22),
53 GPR_DWARFNUM_NAME(23),
54 GPR_DWARFNUM_NAME(24),
55 GPR_DWARFNUM_NAME(25),
56 GPR_DWARFNUM_NAME(26),
57 GPR_DWARFNUM_NAME(27),
58 GPR_DWARFNUM_NAME(28),
59 GPR_DWARFNUM_NAME(29),
60 REG_DWARFNUM_NAME("%lr", 30),
61 REG_DWARFNUM_NAME("%sp", 31),
62 REG_DWARFNUM_END,
63};
64
65/**
66 * get_arch_regstr() - lookup register name from it's DWARF register number
67 * @n: the DWARF register number
68 *
69 * get_arch_regstr() returns the name of the register in struct
70 * regdwarfnum_table from it's DWARF register number. If the register is not
71 * found in the table, this returns NULL;
72 */
73const char *get_arch_regstr(unsigned int n)
74{
75 const struct pt_regs_dwarfnum *roff;
76 for (roff = regdwarfnum_table; roff->name != NULL; roff++)
77 if (roff->dwarfnum == n)
78 return roff->name;
79 return NULL;
80}
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c
new file mode 100644
index 000000000000..436ee43859dc
--- /dev/null
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -0,0 +1,82 @@
1
2#include <errno.h>
3#include <libunwind.h>
4#include "perf_regs.h"
5#include "../../util/unwind.h"
6
7int libunwind__arch_reg_id(int regnum)
8{
9 switch (regnum) {
10 case UNW_AARCH64_X0:
11 return PERF_REG_ARM64_X0;
12 case UNW_AARCH64_X1:
13 return PERF_REG_ARM64_X1;
14 case UNW_AARCH64_X2:
15 return PERF_REG_ARM64_X2;
16 case UNW_AARCH64_X3:
17 return PERF_REG_ARM64_X3;
18 case UNW_AARCH64_X4:
19 return PERF_REG_ARM64_X4;
20 case UNW_AARCH64_X5:
21 return PERF_REG_ARM64_X5;
22 case UNW_AARCH64_X6:
23 return PERF_REG_ARM64_X6;
24 case UNW_AARCH64_X7:
25 return PERF_REG_ARM64_X7;
26 case UNW_AARCH64_X8:
27 return PERF_REG_ARM64_X8;
28 case UNW_AARCH64_X9:
29 return PERF_REG_ARM64_X9;
30 case UNW_AARCH64_X10:
31 return PERF_REG_ARM64_X10;
32 case UNW_AARCH64_X11:
33 return PERF_REG_ARM64_X11;
34 case UNW_AARCH64_X12:
35 return PERF_REG_ARM64_X12;
36 case UNW_AARCH64_X13:
37 return PERF_REG_ARM64_X13;
38 case UNW_AARCH64_X14:
39 return PERF_REG_ARM64_X14;
40 case UNW_AARCH64_X15:
41 return PERF_REG_ARM64_X15;
42 case UNW_AARCH64_X16:
43 return PERF_REG_ARM64_X16;
44 case UNW_AARCH64_X17:
45 return PERF_REG_ARM64_X17;
46 case UNW_AARCH64_X18:
47 return PERF_REG_ARM64_X18;
48 case UNW_AARCH64_X19:
49 return PERF_REG_ARM64_X19;
50 case UNW_AARCH64_X20:
51 return PERF_REG_ARM64_X20;
52 case UNW_AARCH64_X21:
53 return PERF_REG_ARM64_X21;
54 case UNW_AARCH64_X22:
55 return PERF_REG_ARM64_X22;
56 case UNW_AARCH64_X23:
57 return PERF_REG_ARM64_X23;
58 case UNW_AARCH64_X24:
59 return PERF_REG_ARM64_X24;
60 case UNW_AARCH64_X25:
61 return PERF_REG_ARM64_X25;
62 case UNW_AARCH64_X26:
63 return PERF_REG_ARM64_X26;
64 case UNW_AARCH64_X27:
65 return PERF_REG_ARM64_X27;
66 case UNW_AARCH64_X28:
67 return PERF_REG_ARM64_X28;
68 case UNW_AARCH64_X29:
69 return PERF_REG_ARM64_X29;
70 case UNW_AARCH64_X30:
71 return PERF_REG_ARM64_LR;
72 case UNW_AARCH64_SP:
73 return PERF_REG_ARM64_SP;
74 case UNW_AARCH64_PC:
75 return PERF_REG_ARM64_PC;
76 default:
77 pr_err("unwind: invalid reg id %d\n", regnum);
78 return -EINVAL;
79 }
80
81 return -EINVAL;
82}
diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h
index fc819ca34a7e..7df517acfef8 100644
--- a/tools/perf/arch/x86/include/perf_regs.h
+++ b/tools/perf/arch/x86/include/perf_regs.h
@@ -2,7 +2,7 @@
2#define ARCH_PERF_REGS_H 2#define ARCH_PERF_REGS_H
3 3
4#include <stdlib.h> 4#include <stdlib.h>
5#include "../../util/types.h" 5#include <linux/types.h>
6#include <asm/perf_regs.h> 6#include <asm/perf_regs.h>
7 7
8void perf_regs_load(u64 *regs); 8void perf_regs_load(u64 *regs);
diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c
index 83bc2385e6d3..9f89f899ccc7 100644
--- a/tools/perf/arch/x86/tests/dwarf-unwind.c
+++ b/tools/perf/arch/x86/tests/dwarf-unwind.c
@@ -23,7 +23,7 @@ static int sample_ustack(struct perf_sample *sample,
23 23
24 sp = (unsigned long) regs[PERF_REG_X86_SP]; 24 sp = (unsigned long) regs[PERF_REG_X86_SP];
25 25
26 map = map_groups__find(&thread->mg, MAP__VARIABLE, (u64) sp); 26 map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp);
27 if (!map) { 27 if (!map) {
28 pr_debug("failed to get stack map\n"); 28 pr_debug("failed to get stack map\n");
29 free(buf); 29 free(buf);
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
index b2519e49424f..40021fa3129b 100644
--- a/tools/perf/arch/x86/util/tsc.c
+++ b/tools/perf/arch/x86/util/tsc.c
@@ -4,7 +4,7 @@
4#include <linux/perf_event.h> 4#include <linux/perf_event.h>
5 5
6#include "../../perf.h" 6#include "../../perf.h"
7#include "../../util/types.h" 7#include <linux/types.h>
8#include "../../util/debug.h" 8#include "../../util/debug.h"
9#include "tsc.h" 9#include "tsc.h"
10 10
diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h
index a24dec81c795..2affe0366b59 100644
--- a/tools/perf/arch/x86/util/tsc.h
+++ b/tools/perf/arch/x86/util/tsc.h
@@ -1,7 +1,7 @@
1#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ 1#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
2#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ 2#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
3 3
4#include "../../util/types.h" 4#include <linux/types.h>
5 5
6struct perf_tsc_conversion { 6struct perf_tsc_conversion {
7 u16 time_shift; 7 u16 time_shift;
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 0da603b79b61..1ec429fef2be 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -46,7 +46,7 @@ struct perf_annotate {
46}; 46};
47 47
48static int perf_evsel__add_sample(struct perf_evsel *evsel, 48static int perf_evsel__add_sample(struct perf_evsel *evsel,
49 struct perf_sample *sample, 49 struct perf_sample *sample __maybe_unused,
50 struct addr_location *al, 50 struct addr_location *al,
51 struct perf_annotate *ann) 51 struct perf_annotate *ann)
52{ 52{
@@ -65,13 +65,13 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
65 return 0; 65 return 0;
66 } 66 }
67 67
68 he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0); 68 he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0,
69 true);
69 if (he == NULL) 70 if (he == NULL)
70 return -ENOMEM; 71 return -ENOMEM;
71 72
72 ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); 73 ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
73 evsel->hists.stats.total_period += sample->period; 74 hists__inc_nr_samples(&evsel->hists, true);
74 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
75 return ret; 75 return ret;
76} 76}
77 77
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 204fffe22532..9a5a035cb426 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -60,7 +60,6 @@ static int data__files_cnt;
60#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) 60#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
61#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) 61#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
62 62
63static char diff__default_sort_order[] = "dso,symbol";
64static bool force; 63static bool force;
65static bool show_period; 64static bool show_period;
66static bool show_formula; 65static bool show_formula;
@@ -220,7 +219,8 @@ static int setup_compute(const struct option *opt, const char *str,
220 219
221static double period_percent(struct hist_entry *he, u64 period) 220static double period_percent(struct hist_entry *he, u64 period)
222{ 221{
223 u64 total = he->hists->stats.total_period; 222 u64 total = hists__total_period(he->hists);
223
224 return (period * 100.0) / total; 224 return (period * 100.0) / total;
225} 225}
226 226
@@ -259,11 +259,18 @@ static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
259static int formula_delta(struct hist_entry *he, struct hist_entry *pair, 259static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
260 char *buf, size_t size) 260 char *buf, size_t size)
261{ 261{
262 u64 he_total = he->hists->stats.total_period;
263 u64 pair_total = pair->hists->stats.total_period;
264
265 if (symbol_conf.filter_relative) {
266 he_total = he->hists->stats.total_non_filtered_period;
267 pair_total = pair->hists->stats.total_non_filtered_period;
268 }
262 return scnprintf(buf, size, 269 return scnprintf(buf, size,
263 "(%" PRIu64 " * 100 / %" PRIu64 ") - " 270 "(%" PRIu64 " * 100 / %" PRIu64 ") - "
264 "(%" PRIu64 " * 100 / %" PRIu64 ")", 271 "(%" PRIu64 " * 100 / %" PRIu64 ")",
265 pair->stat.period, pair->hists->stats.total_period, 272 pair->stat.period, pair_total,
266 he->stat.period, he->hists->stats.total_period); 273 he->stat.period, he_total);
267} 274}
268 275
269static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, 276static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
@@ -308,7 +315,7 @@ static int hists__add_entry(struct hists *hists,
308 u64 weight, u64 transaction) 315 u64 weight, u64 transaction)
309{ 316{
310 if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight, 317 if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight,
311 transaction) != NULL) 318 transaction, true) != NULL)
312 return 0; 319 return 0;
313 return -ENOMEM; 320 return -ENOMEM;
314} 321}
@@ -327,16 +334,22 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
327 return -1; 334 return -1;
328 } 335 }
329 336
330 if (al.filtered)
331 return 0;
332
333 if (hists__add_entry(&evsel->hists, &al, sample->period, 337 if (hists__add_entry(&evsel->hists, &al, sample->period,
334 sample->weight, sample->transaction)) { 338 sample->weight, sample->transaction)) {
335 pr_warning("problem incrementing symbol period, skipping event\n"); 339 pr_warning("problem incrementing symbol period, skipping event\n");
336 return -1; 340 return -1;
337 } 341 }
338 342
343 /*
344 * The total_period is updated here before going to the output
345 * tree since normally only the baseline hists will call
346 * hists__output_resort() and precompute needs the total
347 * period in order to sort entries by percentage delta.
348 */
339 evsel->hists.stats.total_period += sample->period; 349 evsel->hists.stats.total_period += sample->period;
350 if (!al.filtered)
351 evsel->hists.stats.total_non_filtered_period += sample->period;
352
340 return 0; 353 return 0;
341} 354}
342 355
@@ -564,8 +577,7 @@ static void hists__compute_resort(struct hists *hists)
564 hists->entries = RB_ROOT; 577 hists->entries = RB_ROOT;
565 next = rb_first(root); 578 next = rb_first(root);
566 579
567 hists->nr_entries = 0; 580 hists__reset_stats(hists);
568 hists->stats.total_period = 0;
569 hists__reset_col_len(hists); 581 hists__reset_col_len(hists);
570 582
571 while (next != NULL) { 583 while (next != NULL) {
@@ -575,7 +587,10 @@ static void hists__compute_resort(struct hists *hists)
575 next = rb_next(&he->rb_node_in); 587 next = rb_next(&he->rb_node_in);
576 588
577 insert_hist_entry_by_compute(&hists->entries, he, compute); 589 insert_hist_entry_by_compute(&hists->entries, he, compute);
578 hists__inc_nr_entries(hists, he); 590 hists__inc_stats(hists, he);
591
592 if (!he->filtered)
593 hists__calc_col_len(hists, he);
579 } 594 }
580} 595}
581 596
@@ -725,20 +740,24 @@ static const struct option options[] = {
725 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", 740 OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
726 "only consider these symbols"), 741 "only consider these symbols"),
727 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 742 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
728 "sort by key(s): pid, comm, dso, symbol, parent"), 743 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
744 " Please refer the man page for the complete list."),
729 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", 745 OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
730 "separator for columns, no spaces will be added between " 746 "separator for columns, no spaces will be added between "
731 "columns '.' is reserved."), 747 "columns '.' is reserved."),
732 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", 748 OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
733 "Look for files with symbols relative to this directory"), 749 "Look for files with symbols relative to this directory"),
734 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), 750 OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
751 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
752 "How to display percentage of filtered entries", parse_filter_percentage),
735 OPT_END() 753 OPT_END()
736}; 754};
737 755
738static double baseline_percent(struct hist_entry *he) 756static double baseline_percent(struct hist_entry *he)
739{ 757{
740 struct hists *hists = he->hists; 758 u64 total = hists__total_period(he->hists);
741 return 100.0 * he->stat.period / hists->stats.total_period; 759
760 return 100.0 * he->stat.period / total;
742} 761}
743 762
744static int hpp__color_baseline(struct perf_hpp_fmt *fmt, 763static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
@@ -1120,7 +1139,8 @@ static int data_init(int argc, const char **argv)
1120 1139
1121int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) 1140int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1122{ 1141{
1123 sort_order = diff__default_sort_order; 1142 perf_config(perf_default_config, NULL);
1143
1124 argc = parse_options(argc, argv, options, diff_usage, 0); 1144 argc = parse_options(argc, argv, options, diff_usage, 0);
1125 1145
1126 if (symbol__init() < 0) 1146 if (symbol__init() < 0)
@@ -1131,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
1131 1151
1132 ui_init(); 1152 ui_init();
1133 1153
1154 sort__mode = SORT_MODE__DIFF;
1155
1134 if (setup_sorting() < 0) 1156 if (setup_sorting() < 0)
1135 usage_with_options(diff_usage, options); 1157 usage_with_options(diff_usage, options);
1136 1158
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3a7387551369..6a3af0013d68 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -209,7 +209,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
209 209
210 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 210 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
211 211
212 thread = machine__findnew_thread(machine, sample->pid, sample->pid); 212 thread = machine__findnew_thread(machine, sample->pid, sample->tid);
213 if (thread == NULL) { 213 if (thread == NULL) {
214 pr_err("problem processing %d event, skipping it.\n", 214 pr_err("problem processing %d event, skipping it.\n",
215 event->header.type); 215 event->header.type);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 929462aa4943..bef3376bfaf3 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -14,6 +14,7 @@
14#include "util/parse-options.h" 14#include "util/parse-options.h"
15#include "util/trace-event.h" 15#include "util/trace-event.h"
16#include "util/data.h" 16#include "util/data.h"
17#include "util/cpumap.h"
17 18
18#include "util/debug.h" 19#include "util/debug.h"
19 20
@@ -31,9 +32,6 @@ static int caller_lines = -1;
31 32
32static bool raw_ip; 33static bool raw_ip;
33 34
34static int *cpunode_map;
35static int max_cpu_num;
36
37struct alloc_stat { 35struct alloc_stat {
38 u64 call_site; 36 u64 call_site;
39 u64 ptr; 37 u64 ptr;
@@ -55,76 +53,6 @@ static struct rb_root root_caller_sorted;
55static unsigned long total_requested, total_allocated; 53static unsigned long total_requested, total_allocated;
56static unsigned long nr_allocs, nr_cross_allocs; 54static unsigned long nr_allocs, nr_cross_allocs;
57 55
58#define PATH_SYS_NODE "/sys/devices/system/node"
59
60static int init_cpunode_map(void)
61{
62 FILE *fp;
63 int i, err = -1;
64
65 fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
66 if (!fp) {
67 max_cpu_num = 4096;
68 return 0;
69 }
70
71 if (fscanf(fp, "%d", &max_cpu_num) < 1) {
72 pr_err("Failed to read 'kernel_max' from sysfs");
73 goto out_close;
74 }
75
76 max_cpu_num++;
77
78 cpunode_map = calloc(max_cpu_num, sizeof(int));
79 if (!cpunode_map) {
80 pr_err("%s: calloc failed\n", __func__);
81 goto out_close;
82 }
83
84 for (i = 0; i < max_cpu_num; i++)
85 cpunode_map[i] = -1;
86
87 err = 0;
88out_close:
89 fclose(fp);
90 return err;
91}
92
93static int setup_cpunode_map(void)
94{
95 struct dirent *dent1, *dent2;
96 DIR *dir1, *dir2;
97 unsigned int cpu, mem;
98 char buf[PATH_MAX];
99
100 if (init_cpunode_map())
101 return -1;
102
103 dir1 = opendir(PATH_SYS_NODE);
104 if (!dir1)
105 return 0;
106
107 while ((dent1 = readdir(dir1)) != NULL) {
108 if (dent1->d_type != DT_DIR ||
109 sscanf(dent1->d_name, "node%u", &mem) < 1)
110 continue;
111
112 snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name);
113 dir2 = opendir(buf);
114 if (!dir2)
115 continue;
116 while ((dent2 = readdir(dir2)) != NULL) {
117 if (dent2->d_type != DT_LNK ||
118 sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
119 continue;
120 cpunode_map[cpu] = mem;
121 }
122 closedir(dir2);
123 }
124 closedir(dir1);
125 return 0;
126}
127
128static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, 56static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
129 int bytes_req, int bytes_alloc, int cpu) 57 int bytes_req, int bytes_alloc, int cpu)
130{ 58{
@@ -235,7 +163,7 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
235 int ret = perf_evsel__process_alloc_event(evsel, sample); 163 int ret = perf_evsel__process_alloc_event(evsel, sample);
236 164
237 if (!ret) { 165 if (!ret) {
238 int node1 = cpunode_map[sample->cpu], 166 int node1 = cpu__get_node(sample->cpu),
239 node2 = perf_evsel__intval(evsel, sample, "node"); 167 node2 = perf_evsel__intval(evsel, sample, "node");
240 168
241 if (node1 != node2) 169 if (node1 != node2)
@@ -307,7 +235,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
307 struct machine *machine) 235 struct machine *machine)
308{ 236{
309 struct thread *thread = machine__findnew_thread(machine, sample->pid, 237 struct thread *thread = machine__findnew_thread(machine, sample->pid,
310 sample->pid); 238 sample->tid);
311 239
312 if (thread == NULL) { 240 if (thread == NULL) {
313 pr_debug("problem processing %d event, skipping it.\n", 241 pr_debug("problem processing %d event, skipping it.\n",
@@ -756,11 +684,13 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
756 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), 684 OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
757 OPT_END() 685 OPT_END()
758 }; 686 };
759 const char * const kmem_usage[] = { 687 const char *const kmem_subcommands[] = { "record", "stat", NULL };
760 "perf kmem [<options>] {record|stat}", 688 const char *kmem_usage[] = {
689 NULL,
761 NULL 690 NULL
762 }; 691 };
763 argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); 692 argc = parse_options_subcommand(argc, argv, kmem_options,
693 kmem_subcommands, kmem_usage, 0);
764 694
765 if (!argc) 695 if (!argc)
766 usage_with_options(kmem_usage, kmem_options); 696 usage_with_options(kmem_usage, kmem_options);
@@ -770,7 +700,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
770 if (!strncmp(argv[0], "rec", 3)) { 700 if (!strncmp(argv[0], "rec", 3)) {
771 return __cmd_record(argc, argv); 701 return __cmd_record(argc, argv);
772 } else if (!strcmp(argv[0], "stat")) { 702 } else if (!strcmp(argv[0], "stat")) {
773 if (setup_cpunode_map()) 703 if (cpu__setup_cpunode_map())
774 return -1; 704 return -1;
775 705
776 if (list_empty(&caller_sort)) 706 if (list_empty(&caller_sort))
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index c852c7a85d32..6148afc995c6 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -961,8 +961,10 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
961 "perf lock info [<options>]", 961 "perf lock info [<options>]",
962 NULL 962 NULL
963 }; 963 };
964 const char * const lock_usage[] = { 964 const char *const lock_subcommands[] = { "record", "report", "script",
965 "perf lock [<options>] {record|report|script|info}", 965 "info", NULL };
966 const char *lock_usage[] = {
967 NULL,
966 NULL 968 NULL
967 }; 969 };
968 const char * const report_usage[] = { 970 const char * const report_usage[] = {
@@ -976,8 +978,8 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
976 for (i = 0; i < LOCKHASH_SIZE; i++) 978 for (i = 0; i < LOCKHASH_SIZE; i++)
977 INIT_LIST_HEAD(lockhash_table + i); 979 INIT_LIST_HEAD(lockhash_table + i);
978 980
979 argc = parse_options(argc, argv, lock_options, lock_usage, 981 argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
980 PARSE_OPT_STOP_AT_NON_OPTION); 982 lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
981 if (!argc) 983 if (!argc)
982 usage_with_options(lock_usage, lock_options); 984 usage_with_options(lock_usage, lock_options);
983 985
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index 2e3ade69a58e..4a1a6c94a5eb 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -21,11 +21,6 @@ struct perf_mem {
21 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 21 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
22}; 22};
23 23
24static const char * const mem_usage[] = {
25 "perf mem [<options>] {record <command> |report}",
26 NULL
27};
28
29static int __cmd_record(int argc, const char **argv) 24static int __cmd_record(int argc, const char **argv)
30{ 25{
31 int rec_argc, i = 0, j; 26 int rec_argc, i = 0, j;
@@ -220,9 +215,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
220 " between columns '.' is reserved."), 215 " between columns '.' is reserved."),
221 OPT_END() 216 OPT_END()
222 }; 217 };
218 const char *const mem_subcommands[] = { "record", "report", NULL };
219 const char *mem_usage[] = {
220 NULL,
221 NULL
222 };
223
223 224
224 argc = parse_options(argc, argv, mem_options, mem_usage, 225 argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
225 PARSE_OPT_STOP_AT_NON_OPTION); 226 mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
226 227
227 if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) 228 if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation))
228 usage_with_options(mem_usage, mem_options); 229 usage_with_options(mem_usage, mem_options);
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 8ce62ef7f6c3..378b85b731a7 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -30,37 +30,6 @@
30#include <sched.h> 30#include <sched.h>
31#include <sys/mman.h> 31#include <sys/mman.h>
32 32
33#ifndef HAVE_ON_EXIT_SUPPORT
34#ifndef ATEXIT_MAX
35#define ATEXIT_MAX 32
36#endif
37static int __on_exit_count = 0;
38typedef void (*on_exit_func_t) (int, void *);
39static on_exit_func_t __on_exit_funcs[ATEXIT_MAX];
40static void *__on_exit_args[ATEXIT_MAX];
41static int __exitcode = 0;
42static void __handle_on_exit_funcs(void);
43static int on_exit(on_exit_func_t function, void *arg);
44#define exit(x) (exit)(__exitcode = (x))
45
46static int on_exit(on_exit_func_t function, void *arg)
47{
48 if (__on_exit_count == ATEXIT_MAX)
49 return -ENOMEM;
50 else if (__on_exit_count == 0)
51 atexit(__handle_on_exit_funcs);
52 __on_exit_funcs[__on_exit_count] = function;
53 __on_exit_args[__on_exit_count++] = arg;
54 return 0;
55}
56
57static void __handle_on_exit_funcs(void)
58{
59 int i;
60 for (i = 0; i < __on_exit_count; i++)
61 __on_exit_funcs[i] (__exitcode, __on_exit_args[i]);
62}
63#endif
64 33
65struct record { 34struct record {
66 struct perf_tool tool; 35 struct perf_tool tool;
@@ -147,29 +116,19 @@ static void sig_handler(int sig)
147{ 116{
148 if (sig == SIGCHLD) 117 if (sig == SIGCHLD)
149 child_finished = 1; 118 child_finished = 1;
119 else
120 signr = sig;
150 121
151 done = 1; 122 done = 1;
152 signr = sig;
153} 123}
154 124
155static void record__sig_exit(int exit_status __maybe_unused, void *arg) 125static void record__sig_exit(void)
156{ 126{
157 struct record *rec = arg; 127 if (signr == -1)
158 int status;
159
160 if (rec->evlist->workload.pid > 0) {
161 if (!child_finished)
162 kill(rec->evlist->workload.pid, SIGTERM);
163
164 wait(&status);
165 if (WIFSIGNALED(status))
166 psignal(WTERMSIG(status), rec->progname);
167 }
168
169 if (signr == -1 || signr == SIGUSR1)
170 return; 128 return;
171 129
172 signal(signr, SIG_DFL); 130 signal(signr, SIG_DFL);
131 raise(signr);
173} 132}
174 133
175static int record__open(struct record *rec) 134static int record__open(struct record *rec)
@@ -243,27 +202,6 @@ static int process_buildids(struct record *rec)
243 size, &build_id__mark_dso_hit_ops); 202 size, &build_id__mark_dso_hit_ops);
244} 203}
245 204
246static void record__exit(int status, void *arg)
247{
248 struct record *rec = arg;
249 struct perf_data_file *file = &rec->file;
250
251 if (status != 0)
252 return;
253
254 if (!file->is_pipe) {
255 rec->session->header.data_size += rec->bytes_written;
256
257 if (!rec->no_buildid)
258 process_buildids(rec);
259 perf_session__write_header(rec->session, rec->evlist,
260 file->fd, true);
261 perf_session__delete(rec->session);
262 perf_evlist__delete(rec->evlist);
263 symbol__exit();
264 }
265}
266
267static void perf_event__synthesize_guest_os(struct machine *machine, void *data) 205static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
268{ 206{
269 int err; 207 int err;
@@ -344,18 +282,19 @@ static volatile int workload_exec_errno;
344 * if the fork fails, since we asked by setting its 282 * if the fork fails, since we asked by setting its
345 * want_signal to true. 283 * want_signal to true.
346 */ 284 */
347static void workload_exec_failed_signal(int signo, siginfo_t *info, 285static void workload_exec_failed_signal(int signo __maybe_unused,
286 siginfo_t *info,
348 void *ucontext __maybe_unused) 287 void *ucontext __maybe_unused)
349{ 288{
350 workload_exec_errno = info->si_value.sival_int; 289 workload_exec_errno = info->si_value.sival_int;
351 done = 1; 290 done = 1;
352 signr = signo;
353 child_finished = 1; 291 child_finished = 1;
354} 292}
355 293
356static int __cmd_record(struct record *rec, int argc, const char **argv) 294static int __cmd_record(struct record *rec, int argc, const char **argv)
357{ 295{
358 int err; 296 int err;
297 int status = 0;
359 unsigned long waking = 0; 298 unsigned long waking = 0;
360 const bool forks = argc > 0; 299 const bool forks = argc > 0;
361 struct machine *machine; 300 struct machine *machine;
@@ -367,7 +306,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
367 306
368 rec->progname = argv[0]; 307 rec->progname = argv[0];
369 308
370 on_exit(record__sig_exit, rec); 309 atexit(record__sig_exit);
371 signal(SIGCHLD, sig_handler); 310 signal(SIGCHLD, sig_handler);
372 signal(SIGINT, sig_handler); 311 signal(SIGINT, sig_handler);
373 signal(SIGTERM, sig_handler); 312 signal(SIGTERM, sig_handler);
@@ -388,32 +327,28 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
388 workload_exec_failed_signal); 327 workload_exec_failed_signal);
389 if (err < 0) { 328 if (err < 0) {
390 pr_err("Couldn't run the workload!\n"); 329 pr_err("Couldn't run the workload!\n");
330 status = err;
391 goto out_delete_session; 331 goto out_delete_session;
392 } 332 }
393 } 333 }
394 334
395 if (record__open(rec) != 0) { 335 if (record__open(rec) != 0) {
396 err = -1; 336 err = -1;
397 goto out_delete_session; 337 goto out_child;
398 } 338 }
399 339
400 if (!rec->evlist->nr_groups) 340 if (!rec->evlist->nr_groups)
401 perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); 341 perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
402 342
403 /*
404 * perf_session__delete(session) will be called at record__exit()
405 */
406 on_exit(record__exit, rec);
407
408 if (file->is_pipe) { 343 if (file->is_pipe) {
409 err = perf_header__write_pipe(file->fd); 344 err = perf_header__write_pipe(file->fd);
410 if (err < 0) 345 if (err < 0)
411 goto out_delete_session; 346 goto out_child;
412 } else { 347 } else {
413 err = perf_session__write_header(session, rec->evlist, 348 err = perf_session__write_header(session, rec->evlist,
414 file->fd, false); 349 file->fd, false);
415 if (err < 0) 350 if (err < 0)
416 goto out_delete_session; 351 goto out_child;
417 } 352 }
418 353
419 if (!rec->no_buildid 354 if (!rec->no_buildid
@@ -421,7 +356,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
421 pr_err("Couldn't generate buildids. " 356 pr_err("Couldn't generate buildids. "
422 "Use --no-buildid to profile anyway.\n"); 357 "Use --no-buildid to profile anyway.\n");
423 err = -1; 358 err = -1;
424 goto out_delete_session; 359 goto out_child;
425 } 360 }
426 361
427 machine = &session->machines.host; 362 machine = &session->machines.host;
@@ -431,7 +366,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
431 process_synthesized_event); 366 process_synthesized_event);
432 if (err < 0) { 367 if (err < 0) {
433 pr_err("Couldn't synthesize attrs.\n"); 368 pr_err("Couldn't synthesize attrs.\n");
434 goto out_delete_session; 369 goto out_child;
435 } 370 }
436 371
437 if (have_tracepoints(&rec->evlist->entries)) { 372 if (have_tracepoints(&rec->evlist->entries)) {
@@ -447,7 +382,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
447 process_synthesized_event); 382 process_synthesized_event);
448 if (err <= 0) { 383 if (err <= 0) {
449 pr_err("Couldn't record tracing data.\n"); 384 pr_err("Couldn't record tracing data.\n");
450 goto out_delete_session; 385 goto out_child;
451 } 386 }
452 rec->bytes_written += err; 387 rec->bytes_written += err;
453 } 388 }
@@ -475,7 +410,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
475 err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, 410 err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads,
476 process_synthesized_event, opts->sample_address); 411 process_synthesized_event, opts->sample_address);
477 if (err != 0) 412 if (err != 0)
478 goto out_delete_session; 413 goto out_child;
479 414
480 if (rec->realtime_prio) { 415 if (rec->realtime_prio) {
481 struct sched_param param; 416 struct sched_param param;
@@ -484,7 +419,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
484 if (sched_setscheduler(0, SCHED_FIFO, &param)) { 419 if (sched_setscheduler(0, SCHED_FIFO, &param)) {
485 pr_err("Could not set realtime priority.\n"); 420 pr_err("Could not set realtime priority.\n");
486 err = -1; 421 err = -1;
487 goto out_delete_session; 422 goto out_child;
488 } 423 }
489 } 424 }
490 425
@@ -512,13 +447,19 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
512 447
513 if (record__mmap_read_all(rec) < 0) { 448 if (record__mmap_read_all(rec) < 0) {
514 err = -1; 449 err = -1;
515 goto out_delete_session; 450 goto out_child;
516 } 451 }
517 452
518 if (hits == rec->samples) { 453 if (hits == rec->samples) {
519 if (done) 454 if (done)
520 break; 455 break;
521 err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); 456 err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1);
457 /*
458 * Propagate error, only if there's any. Ignore positive
459 * number of returned events and interrupt error.
460 */
461 if (err > 0 || (err < 0 && errno == EINTR))
462 err = 0;
522 waking++; 463 waking++;
523 } 464 }
524 465
@@ -538,28 +479,52 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
538 const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); 479 const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
539 pr_err("Workload failed: %s\n", emsg); 480 pr_err("Workload failed: %s\n", emsg);
540 err = -1; 481 err = -1;
541 goto out_delete_session; 482 goto out_child;
542 } 483 }
543 484
544 if (quiet || signr == SIGUSR1) 485 if (!quiet) {
545 return 0; 486 fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
546 487
547 fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); 488 /*
489 * Approximate RIP event size: 24 bytes.
490 */
491 fprintf(stderr,
492 "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
493 (double)rec->bytes_written / 1024.0 / 1024.0,
494 file->path,
495 rec->bytes_written / 24);
496 }
548 497
549 /* 498out_child:
550 * Approximate RIP event size: 24 bytes. 499 if (forks) {
551 */ 500 int exit_status;
552 fprintf(stderr,
553 "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
554 (double)rec->bytes_written / 1024.0 / 1024.0,
555 file->path,
556 rec->bytes_written / 24);
557 501
558 return 0; 502 if (!child_finished)
503 kill(rec->evlist->workload.pid, SIGTERM);
504
505 wait(&exit_status);
506
507 if (err < 0)
508 status = err;
509 else if (WIFEXITED(exit_status))
510 status = WEXITSTATUS(exit_status);
511 else if (WIFSIGNALED(exit_status))
512 signr = WTERMSIG(exit_status);
513 } else
514 status = err;
515
516 if (!err && !file->is_pipe) {
517 rec->session->header.data_size += rec->bytes_written;
518
519 if (!rec->no_buildid)
520 process_buildids(rec);
521 perf_session__write_header(rec->session, rec->evlist,
522 file->fd, true);
523 }
559 524
560out_delete_session: 525out_delete_session:
561 perf_session__delete(session); 526 perf_session__delete(session);
562 return err; 527 return status;
563} 528}
564 529
565#define BRANCH_OPT(n, m) \ 530#define BRANCH_OPT(n, m) \
@@ -583,6 +548,7 @@ static const struct branch_mode branch_modes[] = {
583 BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX), 548 BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
584 BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX), 549 BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
585 BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX), 550 BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
551 BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
586 BRANCH_END 552 BRANCH_END
587}; 553};
588 554
@@ -988,6 +954,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
988 954
989 err = __cmd_record(&record, argc, argv); 955 err = __cmd_record(&record, argc, argv);
990out_symbol_exit: 956out_symbol_exit:
957 perf_evlist__delete(rec->evlist);
991 symbol__exit(); 958 symbol__exit();
992 return err; 959 return err;
993} 960}
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index c8f21137dfd8..21d830bafff3 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -57,6 +57,7 @@ struct report {
57 const char *cpu_list; 57 const char *cpu_list;
58 const char *symbol_filter_str; 58 const char *symbol_filter_str;
59 float min_percent; 59 float min_percent;
60 u64 nr_entries;
60 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 61 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
61}; 62};
62 63
@@ -71,150 +72,69 @@ static int report__config(const char *var, const char *value, void *cb)
71 rep->min_percent = strtof(value, NULL); 72 rep->min_percent = strtof(value, NULL);
72 return 0; 73 return 0;
73 } 74 }
75 if (!strcmp(var, "report.children")) {
76 symbol_conf.cumulate_callchain = perf_config_bool(var, value);
77 return 0;
78 }
74 79
75 return perf_default_config(var, value, cb); 80 return perf_default_config(var, value, cb);
76} 81}
77 82
78static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, 83static void report__inc_stats(struct report *rep, struct hist_entry *he)
79 struct perf_sample *sample, struct perf_evsel *evsel)
80{ 84{
81 struct symbol *parent = NULL;
82 struct hist_entry *he;
83 struct mem_info *mi, *mx;
84 uint64_t cost;
85 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
86
87 if (err)
88 return err;
89
90 mi = sample__resolve_mem(sample, al);
91 if (!mi)
92 return -ENOMEM;
93
94 if (rep->hide_unresolved && !al->sym)
95 return 0;
96
97 cost = sample->weight;
98 if (!cost)
99 cost = 1;
100
101 /* 85 /*
102 * must pass period=weight in order to get the correct 86 * The @he is either of a newly created one or an existing one
103 * sorting from hists__collapse_resort() which is solely 87 * merging current sample. We only want to count a new one so
104 * based on periods. We want sorting be done on nr_events * weight 88 * checking ->nr_events being 1.
105 * and this is indirectly achieved by passing period=weight here
106 * and the he_stat__add_period() function.
107 */ 89 */
108 he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi, 90 if (he->stat.nr_events == 1)
109 cost, cost, 0); 91 rep->nr_entries++;
110 if (!he)
111 return -ENOMEM;
112
113 if (ui__has_annotation()) {
114 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
115 if (err)
116 goto out;
117
118 mx = he->mem_info;
119 err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx);
120 if (err)
121 goto out;
122 }
123
124 evsel->hists.stats.total_period += cost;
125 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
126 err = hist_entry__append_callchain(he, sample);
127out:
128 return err;
129} 92}
130 93
131static int report__add_branch_hist_entry(struct report *rep, struct addr_location *al, 94static int hist_iter__report_callback(struct hist_entry_iter *iter,
132 struct perf_sample *sample, struct perf_evsel *evsel) 95 struct addr_location *al, bool single,
96 void *arg)
133{ 97{
134 struct symbol *parent = NULL; 98 int err = 0;
135 unsigned i; 99 struct report *rep = arg;
136 struct hist_entry *he; 100 struct hist_entry *he = iter->he;
137 struct branch_info *bi, *bx; 101 struct perf_evsel *evsel = iter->evsel;
138 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); 102 struct mem_info *mi;
139 103 struct branch_info *bi;
140 if (err)
141 return err;
142
143 bi = sample__resolve_bstack(sample, al);
144 if (!bi)
145 return -ENOMEM;
146
147 for (i = 0; i < sample->branch_stack->nr; i++) {
148 if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
149 continue;
150 104
151 err = -ENOMEM; 105 report__inc_stats(rep, he);
152 106
153 /* overwrite the 'al' to branch-to info */ 107 if (!ui__has_annotation())
154 al->map = bi[i].to.map; 108 return 0;
155 al->sym = bi[i].to.sym;
156 al->addr = bi[i].to.addr;
157 /*
158 * The report shows the percentage of total branches captured
159 * and not events sampled. Thus we use a pseudo period of 1.
160 */
161 he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL,
162 1, 1, 0);
163 if (he) {
164 if (ui__has_annotation()) {
165 bx = he->branch_info;
166 err = addr_map_symbol__inc_samples(&bx->from,
167 evsel->idx);
168 if (err)
169 goto out;
170
171 err = addr_map_symbol__inc_samples(&bx->to,
172 evsel->idx);
173 if (err)
174 goto out;
175 }
176 109
177 evsel->hists.stats.total_period += 1; 110 if (sort__mode == SORT_MODE__BRANCH) {
178 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 111 bi = he->branch_info;
179 } else 112 err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);
113 if (err)
180 goto out; 114 goto out;
181 }
182 err = 0;
183out:
184 free(bi);
185 return err;
186}
187 115
188static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel, 116 err = addr_map_symbol__inc_samples(&bi->to, evsel->idx);
189 struct addr_location *al, struct perf_sample *sample)
190{
191 struct symbol *parent = NULL;
192 struct hist_entry *he;
193 int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack);
194
195 if (err)
196 return err;
197 117
198 he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, 118 } else if (rep->mem_mode) {
199 sample->period, sample->weight, 119 mi = he->mem_info;
200 sample->transaction); 120 err = addr_map_symbol__inc_samples(&mi->daddr, evsel->idx);
201 if (he == NULL) 121 if (err)
202 return -ENOMEM; 122 goto out;
203 123
204 err = hist_entry__append_callchain(he, sample); 124 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
205 if (err)
206 goto out;
207 125
208 if (ui__has_annotation()) 126 } else if (symbol_conf.cumulate_callchain) {
127 if (single)
128 err = hist_entry__inc_addr_samples(he, evsel->idx,
129 al->addr);
130 } else {
209 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); 131 err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
132 }
210 133
211 evsel->hists.stats.total_period += sample->period;
212 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
213out: 134out:
214 return err; 135 return err;
215} 136}
216 137
217
218static int process_sample_event(struct perf_tool *tool, 138static int process_sample_event(struct perf_tool *tool,
219 union perf_event *event, 139 union perf_event *event,
220 struct perf_sample *sample, 140 struct perf_sample *sample,
@@ -223,6 +143,10 @@ static int process_sample_event(struct perf_tool *tool,
223{ 143{
224 struct report *rep = container_of(tool, struct report, tool); 144 struct report *rep = container_of(tool, struct report, tool);
225 struct addr_location al; 145 struct addr_location al;
146 struct hist_entry_iter iter = {
147 .hide_unresolved = rep->hide_unresolved,
148 .add_entry_cb = hist_iter__report_callback,
149 };
226 int ret; 150 int ret;
227 151
228 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { 152 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
@@ -237,22 +161,23 @@ static int process_sample_event(struct perf_tool *tool,
237 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) 161 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
238 return 0; 162 return 0;
239 163
240 if (sort__mode == SORT_MODE__BRANCH) { 164 if (sort__mode == SORT_MODE__BRANCH)
241 ret = report__add_branch_hist_entry(rep, &al, sample, evsel); 165 iter.ops = &hist_iter_branch;
242 if (ret < 0) 166 else if (rep->mem_mode)
243 pr_debug("problem adding lbr entry, skipping event\n"); 167 iter.ops = &hist_iter_mem;
244 } else if (rep->mem_mode == 1) { 168 else if (symbol_conf.cumulate_callchain)
245 ret = report__add_mem_hist_entry(rep, &al, sample, evsel); 169 iter.ops = &hist_iter_cumulative;
246 if (ret < 0) 170 else
247 pr_debug("problem adding mem entry, skipping event\n"); 171 iter.ops = &hist_iter_normal;
248 } else { 172
249 if (al.map != NULL) 173 if (al.map != NULL)
250 al.map->dso->hit = 1; 174 al.map->dso->hit = 1;
175
176 ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
177 rep);
178 if (ret < 0)
179 pr_debug("problem adding hist entry, skipping event\n");
251 180
252 ret = report__add_hist_entry(rep, evsel, &al, sample);
253 if (ret < 0)
254 pr_debug("problem incrementing symbol period, skipping event\n");
255 }
256 return ret; 181 return ret;
257} 182}
258 183
@@ -309,6 +234,14 @@ static int report__setup_sample_type(struct report *rep)
309 } 234 }
310 } 235 }
311 236
237 if (symbol_conf.cumulate_callchain) {
238 /* Silently ignore if callchain is missing */
239 if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
240 symbol_conf.cumulate_callchain = false;
241 perf_hpp__cancel_cumulate();
242 }
243 }
244
312 if (sort__mode == SORT_MODE__BRANCH) { 245 if (sort__mode == SORT_MODE__BRANCH) {
313 if (!is_pipe && 246 if (!is_pipe &&
314 !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { 247 !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
@@ -337,6 +270,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
337 char buf[512]; 270 char buf[512];
338 size_t size = sizeof(buf); 271 size_t size = sizeof(buf);
339 272
273 if (symbol_conf.filter_relative) {
274 nr_samples = hists->stats.nr_non_filtered_samples;
275 nr_events = hists->stats.total_non_filtered_period;
276 }
277
340 if (perf_evsel__is_group_event(evsel)) { 278 if (perf_evsel__is_group_event(evsel)) {
341 struct perf_evsel *pos; 279 struct perf_evsel *pos;
342 280
@@ -344,8 +282,13 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
344 evname = buf; 282 evname = buf;
345 283
346 for_each_group_member(pos, evsel) { 284 for_each_group_member(pos, evsel) {
347 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 285 if (symbol_conf.filter_relative) {
348 nr_events += pos->hists.stats.total_period; 286 nr_samples += pos->hists.stats.nr_non_filtered_samples;
287 nr_events += pos->hists.stats.total_non_filtered_period;
288 } else {
289 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
290 nr_events += pos->hists.stats.total_period;
291 }
349 } 292 }
350 } 293 }
351 294
@@ -470,24 +413,12 @@ static int report__browse_hists(struct report *rep)
470 return ret; 413 return ret;
471} 414}
472 415
473static u64 report__collapse_hists(struct report *rep) 416static void report__collapse_hists(struct report *rep)
474{ 417{
475 struct ui_progress prog; 418 struct ui_progress prog;
476 struct perf_evsel *pos; 419 struct perf_evsel *pos;
477 u64 nr_samples = 0;
478 /*
479 * Count number of histogram entries to use when showing progress,
480 * reusing nr_samples variable.
481 */
482 evlist__for_each(rep->session->evlist, pos)
483 nr_samples += pos->hists.nr_entries;
484 420
485 ui_progress__init(&prog, nr_samples, "Merging related events..."); 421 ui_progress__init(&prog, rep->nr_entries, "Merging related events...");
486 /*
487 * Count total number of samples, will be used to check if this
488 * session had any.
489 */
490 nr_samples = 0;
491 422
492 evlist__for_each(rep->session->evlist, pos) { 423 evlist__for_each(rep->session->evlist, pos) {
493 struct hists *hists = &pos->hists; 424 struct hists *hists = &pos->hists;
@@ -496,7 +427,6 @@ static u64 report__collapse_hists(struct report *rep)
496 hists->symbol_filter_str = rep->symbol_filter_str; 427 hists->symbol_filter_str = rep->symbol_filter_str;
497 428
498 hists__collapse_resort(hists, &prog); 429 hists__collapse_resort(hists, &prog);
499 nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
500 430
501 /* Non-group events are considered as leader */ 431 /* Non-group events are considered as leader */
502 if (symbol_conf.event_group && 432 if (symbol_conf.event_group &&
@@ -509,14 +439,11 @@ static u64 report__collapse_hists(struct report *rep)
509 } 439 }
510 440
511 ui_progress__finish(); 441 ui_progress__finish();
512
513 return nr_samples;
514} 442}
515 443
516static int __cmd_report(struct report *rep) 444static int __cmd_report(struct report *rep)
517{ 445{
518 int ret; 446 int ret;
519 u64 nr_samples;
520 struct perf_session *session = rep->session; 447 struct perf_session *session = rep->session;
521 struct perf_evsel *pos; 448 struct perf_evsel *pos;
522 struct perf_data_file *file = session->file; 449 struct perf_data_file *file = session->file;
@@ -556,12 +483,12 @@ static int __cmd_report(struct report *rep)
556 } 483 }
557 } 484 }
558 485
559 nr_samples = report__collapse_hists(rep); 486 report__collapse_hists(rep);
560 487
561 if (session_done()) 488 if (session_done())
562 return 0; 489 return 0;
563 490
564 if (nr_samples == 0) { 491 if (rep->nr_entries == 0) {
565 ui__error("The %s file has no samples!\n", file->path); 492 ui__error("The %s file has no samples!\n", file->path);
566 return 0; 493 return 0;
567 } 494 }
@@ -573,11 +500,9 @@ static int __cmd_report(struct report *rep)
573} 500}
574 501
575static int 502static int
576parse_callchain_opt(const struct option *opt, const char *arg, int unset) 503report_parse_callchain_opt(const struct option *opt, const char *arg, int unset)
577{ 504{
578 struct report *rep = (struct report *)opt->value; 505 struct report *rep = (struct report *)opt->value;
579 char *tok, *tok2;
580 char *endptr;
581 506
582 /* 507 /*
583 * --no-call-graph 508 * --no-call-graph
@@ -587,80 +512,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
587 return 0; 512 return 0;
588 } 513 }
589 514
590 symbol_conf.use_callchain = true; 515 return parse_callchain_report_opt(arg);
591
592 if (!arg)
593 return 0;
594
595 tok = strtok((char *)arg, ",");
596 if (!tok)
597 return -1;
598
599 /* get the output mode */
600 if (!strncmp(tok, "graph", strlen(arg)))
601 callchain_param.mode = CHAIN_GRAPH_ABS;
602
603 else if (!strncmp(tok, "flat", strlen(arg)))
604 callchain_param.mode = CHAIN_FLAT;
605
606 else if (!strncmp(tok, "fractal", strlen(arg)))
607 callchain_param.mode = CHAIN_GRAPH_REL;
608
609 else if (!strncmp(tok, "none", strlen(arg))) {
610 callchain_param.mode = CHAIN_NONE;
611 symbol_conf.use_callchain = false;
612
613 return 0;
614 }
615
616 else
617 return -1;
618
619 /* get the min percentage */
620 tok = strtok(NULL, ",");
621 if (!tok)
622 goto setup;
623
624 callchain_param.min_percent = strtod(tok, &endptr);
625 if (tok == endptr)
626 return -1;
627
628 /* get the print limit */
629 tok2 = strtok(NULL, ",");
630 if (!tok2)
631 goto setup;
632
633 if (tok2[0] != 'c') {
634 callchain_param.print_limit = strtoul(tok2, &endptr, 0);
635 tok2 = strtok(NULL, ",");
636 if (!tok2)
637 goto setup;
638 }
639
640 /* get the call chain order */
641 if (!strncmp(tok2, "caller", strlen("caller")))
642 callchain_param.order = ORDER_CALLER;
643 else if (!strncmp(tok2, "callee", strlen("callee")))
644 callchain_param.order = ORDER_CALLEE;
645 else
646 return -1;
647
648 /* Get the sort key */
649 tok2 = strtok(NULL, ",");
650 if (!tok2)
651 goto setup;
652 if (!strncmp(tok2, "function", strlen("function")))
653 callchain_param.key = CCKEY_FUNCTION;
654 else if (!strncmp(tok2, "address", strlen("address")))
655 callchain_param.key = CCKEY_ADDRESS;
656 else
657 return -1;
658setup:
659 if (callchain_register_param(&callchain_param) < 0) {
660 pr_err("Can't register callchain params\n");
661 return -1;
662 }
663 return 0;
664} 516}
665 517
666int 518int
@@ -760,10 +612,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
760 OPT_BOOLEAN(0, "header-only", &report.header_only, 612 OPT_BOOLEAN(0, "header-only", &report.header_only,
761 "Show only data header."), 613 "Show only data header."),
762 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 614 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
763 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," 615 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
764 " dso_to, dso_from, symbol_to, symbol_from, mispredict," 616 " Please refer the man page for the complete list."),
765 " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " 617 OPT_STRING('F', "fields", &field_order, "key[,keys...]",
766 "snoop, locked, abort, in_tx, transaction"), 618 "output field(s): overhead, period, sample plus all of sort keys"),
767 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 619 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
768 "Show sample percentage for different cpu modes"), 620 "Show sample percentage for different cpu modes"),
769 OPT_STRING('p', "parent", &parent_pattern, "regex", 621 OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -772,7 +624,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
772 "Only display entries with parent-match"), 624 "Only display entries with parent-match"),
773 OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", 625 OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
774 "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " 626 "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
775 "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), 627 "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
628 OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
629 "Accumulate callchains of children and show total overhead as well"),
776 OPT_INTEGER(0, "max-stack", &report.max_stack, 630 OPT_INTEGER(0, "max-stack", &report.max_stack,
777 "Set the maximum stack depth when parsing the callchain, " 631 "Set the maximum stack depth when parsing the callchain, "
778 "anything beyond the specified depth will be ignored. " 632 "anything beyond the specified depth will be ignored. "
@@ -823,6 +677,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
823 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), 677 OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
824 OPT_CALLBACK(0, "percent-limit", &report, "percent", 678 OPT_CALLBACK(0, "percent-limit", &report, "percent",
825 "Don't show entries under that percent", parse_percent_limit), 679 "Don't show entries under that percent", parse_percent_limit),
680 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
681 "how to display percentage of filtered entries", parse_filter_percentage),
826 OPT_END() 682 OPT_END()
827 }; 683 };
828 struct perf_data_file file = { 684 struct perf_data_file file = {
@@ -863,55 +719,37 @@ repeat:
863 has_br_stack = perf_header__has_feat(&session->header, 719 has_br_stack = perf_header__has_feat(&session->header,
864 HEADER_BRANCH_STACK); 720 HEADER_BRANCH_STACK);
865 721
866 if (branch_mode == -1 && has_br_stack) 722 if (branch_mode == -1 && has_br_stack) {
867 sort__mode = SORT_MODE__BRANCH; 723 sort__mode = SORT_MODE__BRANCH;
868 724 symbol_conf.cumulate_callchain = false;
869 /* sort__mode could be NORMAL if --no-branch-stack */
870 if (sort__mode == SORT_MODE__BRANCH) {
871 /*
872 * if no sort_order is provided, then specify
873 * branch-mode specific order
874 */
875 if (sort_order == default_sort_order)
876 sort_order = "comm,dso_from,symbol_from,"
877 "dso_to,symbol_to";
878
879 } 725 }
726
880 if (report.mem_mode) { 727 if (report.mem_mode) {
881 if (sort__mode == SORT_MODE__BRANCH) { 728 if (sort__mode == SORT_MODE__BRANCH) {
882 pr_err("branch and mem mode incompatible\n"); 729 pr_err("branch and mem mode incompatible\n");
883 goto error; 730 goto error;
884 } 731 }
885 sort__mode = SORT_MODE__MEMORY; 732 sort__mode = SORT_MODE__MEMORY;
886 733 symbol_conf.cumulate_callchain = false;
887 /*
888 * if no sort_order is provided, then specify
889 * branch-mode specific order
890 */
891 if (sort_order == default_sort_order)
892 sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
893 } 734 }
894 735
895 if (setup_sorting() < 0) { 736 if (setup_sorting() < 0) {
896 parse_options_usage(report_usage, options, "s", 1); 737 if (sort_order)
738 parse_options_usage(report_usage, options, "s", 1);
739 if (field_order)
740 parse_options_usage(sort_order ? NULL : report_usage,
741 options, "F", 1);
897 goto error; 742 goto error;
898 } 743 }
899 744
900 if (parent_pattern != default_parent_pattern) {
901 if (sort_dimension__add("parent") < 0)
902 goto error;
903 }
904
905 /* Force tty output for header output. */ 745 /* Force tty output for header output. */
906 if (report.header || report.header_only) 746 if (report.header || report.header_only)
907 use_browser = 0; 747 use_browser = 0;
908 748
909 if (strcmp(input_name, "-") != 0) 749 if (strcmp(input_name, "-") != 0)
910 setup_browser(true); 750 setup_browser(true);
911 else { 751 else
912 use_browser = 0; 752 use_browser = 0;
913 perf_hpp__init();
914 }
915 753
916 if (report.header || report.header_only) { 754 if (report.header || report.header_only) {
917 perf_session__fprintf_info(session, stdout, 755 perf_session__fprintf_info(session, stdout,
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 9ac0a495c954..c38d06c04775 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -66,7 +66,7 @@ struct sched_atom {
66 struct task_desc *wakee; 66 struct task_desc *wakee;
67}; 67};
68 68
69#define TASK_STATE_TO_CHAR_STR "RSDTtZX" 69#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"
70 70
71enum thread_state { 71enum thread_state {
72 THREAD_SLEEPING = 0, 72 THREAD_SLEEPING = 0,
@@ -149,7 +149,6 @@ struct perf_sched {
149 unsigned long nr_runs; 149 unsigned long nr_runs;
150 unsigned long nr_timestamps; 150 unsigned long nr_timestamps;
151 unsigned long nr_unordered_timestamps; 151 unsigned long nr_unordered_timestamps;
152 unsigned long nr_state_machine_bugs;
153 unsigned long nr_context_switch_bugs; 152 unsigned long nr_context_switch_bugs;
154 unsigned long nr_events; 153 unsigned long nr_events;
155 unsigned long nr_lost_chunks; 154 unsigned long nr_lost_chunks;
@@ -1007,17 +1006,12 @@ static int latency_wakeup_event(struct perf_sched *sched,
1007 struct perf_sample *sample, 1006 struct perf_sample *sample,
1008 struct machine *machine) 1007 struct machine *machine)
1009{ 1008{
1010 const u32 pid = perf_evsel__intval(evsel, sample, "pid"), 1009 const u32 pid = perf_evsel__intval(evsel, sample, "pid");
1011 success = perf_evsel__intval(evsel, sample, "success");
1012 struct work_atoms *atoms; 1010 struct work_atoms *atoms;
1013 struct work_atom *atom; 1011 struct work_atom *atom;
1014 struct thread *wakee; 1012 struct thread *wakee;
1015 u64 timestamp = sample->time; 1013 u64 timestamp = sample->time;
1016 1014
1017 /* Note for later, it may be interesting to observe the failing cases */
1018 if (!success)
1019 return 0;
1020
1021 wakee = machine__findnew_thread(machine, 0, pid); 1015 wakee = machine__findnew_thread(machine, 0, pid);
1022 atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); 1016 atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
1023 if (!atoms) { 1017 if (!atoms) {
@@ -1037,12 +1031,18 @@ static int latency_wakeup_event(struct perf_sched *sched,
1037 atom = list_entry(atoms->work_list.prev, struct work_atom, list); 1031 atom = list_entry(atoms->work_list.prev, struct work_atom, list);
1038 1032
1039 /* 1033 /*
1034 * As we do not guarantee the wakeup event happens when
1035 * task is out of run queue, also may happen when task is
1036 * on run queue and wakeup only change ->state to TASK_RUNNING,
1037 * then we should not set the ->wake_up_time when wake up a
1038 * task which is on run queue.
1039 *
1040 * You WILL be missing events if you've recorded only 1040 * You WILL be missing events if you've recorded only
1041 * one CPU, or are only looking at only one, so don't 1041 * one CPU, or are only looking at only one, so don't
1042 * make useless noise. 1042 * skip in this case.
1043 */ 1043 */
1044 if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING) 1044 if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING)
1045 sched->nr_state_machine_bugs++; 1045 return 0;
1046 1046
1047 sched->nr_timestamps++; 1047 sched->nr_timestamps++;
1048 if (atom->sched_out_time > timestamp) { 1048 if (atom->sched_out_time > timestamp) {
@@ -1266,9 +1266,8 @@ static int process_sched_wakeup_event(struct perf_tool *tool,
1266static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, 1266static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
1267 struct perf_sample *sample, struct machine *machine) 1267 struct perf_sample *sample, struct machine *machine)
1268{ 1268{
1269 const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), 1269 const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid");
1270 next_pid = perf_evsel__intval(evsel, sample, "next_pid"); 1270 struct thread *sched_in;
1271 struct thread *sched_out __maybe_unused, *sched_in;
1272 int new_shortname; 1271 int new_shortname;
1273 u64 timestamp0, timestamp = sample->time; 1272 u64 timestamp0, timestamp = sample->time;
1274 s64 delta; 1273 s64 delta;
@@ -1291,7 +1290,6 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
1291 return -1; 1290 return -1;
1292 } 1291 }
1293 1292
1294 sched_out = machine__findnew_thread(machine, 0, prev_pid);
1295 sched_in = machine__findnew_thread(machine, 0, next_pid); 1293 sched_in = machine__findnew_thread(machine, 0, next_pid);
1296 1294
1297 sched->curr_thread[this_cpu] = sched_in; 1295 sched->curr_thread[this_cpu] = sched_in;
@@ -1300,17 +1298,25 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
1300 1298
1301 new_shortname = 0; 1299 new_shortname = 0;
1302 if (!sched_in->shortname[0]) { 1300 if (!sched_in->shortname[0]) {
1303 sched_in->shortname[0] = sched->next_shortname1; 1301 if (!strcmp(thread__comm_str(sched_in), "swapper")) {
1304 sched_in->shortname[1] = sched->next_shortname2; 1302 /*
1305 1303 * Don't allocate a letter-number for swapper:0
1306 if (sched->next_shortname1 < 'Z') { 1304 * as a shortname. Instead, we use '.' for it.
1307 sched->next_shortname1++; 1305 */
1306 sched_in->shortname[0] = '.';
1307 sched_in->shortname[1] = ' ';
1308 } else { 1308 } else {
1309 sched->next_shortname1='A'; 1309 sched_in->shortname[0] = sched->next_shortname1;
1310 if (sched->next_shortname2 < '9') { 1310 sched_in->shortname[1] = sched->next_shortname2;
1311 sched->next_shortname2++; 1311
1312 if (sched->next_shortname1 < 'Z') {
1313 sched->next_shortname1++;
1312 } else { 1314 } else {
1313 sched->next_shortname2='0'; 1315 sched->next_shortname1 = 'A';
1316 if (sched->next_shortname2 < '9')
1317 sched->next_shortname2++;
1318 else
1319 sched->next_shortname2 = '0';
1314 } 1320 }
1315 } 1321 }
1316 new_shortname = 1; 1322 new_shortname = 1;
@@ -1322,12 +1328,9 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
1322 else 1328 else
1323 printf("*"); 1329 printf("*");
1324 1330
1325 if (sched->curr_thread[cpu]) { 1331 if (sched->curr_thread[cpu])
1326 if (sched->curr_thread[cpu]->tid) 1332 printf("%2s ", sched->curr_thread[cpu]->shortname);
1327 printf("%2s ", sched->curr_thread[cpu]->shortname); 1333 else
1328 else
1329 printf(". ");
1330 } else
1331 printf(" "); 1334 printf(" ");
1332 } 1335 }
1333 1336
@@ -1425,7 +1428,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
1425 int err = 0; 1428 int err = 0;
1426 1429
1427 evsel->hists.stats.total_period += sample->period; 1430 evsel->hists.stats.total_period += sample->period;
1428 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); 1431 hists__inc_nr_samples(&evsel->hists, true);
1429 1432
1430 if (evsel->handler != NULL) { 1433 if (evsel->handler != NULL) {
1431 tracepoint_handler f = evsel->handler; 1434 tracepoint_handler f = evsel->handler;
@@ -1496,14 +1499,6 @@ static void print_bad_events(struct perf_sched *sched)
1496 (double)sched->nr_lost_events/(double)sched->nr_events * 100.0, 1499 (double)sched->nr_lost_events/(double)sched->nr_events * 100.0,
1497 sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks); 1500 sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks);
1498 } 1501 }
1499 if (sched->nr_state_machine_bugs && sched->nr_timestamps) {
1500 printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)",
1501 (double)sched->nr_state_machine_bugs/(double)sched->nr_timestamps*100.0,
1502 sched->nr_state_machine_bugs, sched->nr_timestamps);
1503 if (sched->nr_lost_events)
1504 printf(" (due to lost events?)");
1505 printf("\n");
1506 }
1507 if (sched->nr_context_switch_bugs && sched->nr_timestamps) { 1502 if (sched->nr_context_switch_bugs && sched->nr_timestamps) {
1508 printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", 1503 printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)",
1509 (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0, 1504 (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0,
@@ -1635,6 +1630,7 @@ static int __cmd_record(int argc, const char **argv)
1635 "-e", "sched:sched_stat_runtime", 1630 "-e", "sched:sched_stat_runtime",
1636 "-e", "sched:sched_process_fork", 1631 "-e", "sched:sched_process_fork",
1637 "-e", "sched:sched_wakeup", 1632 "-e", "sched:sched_wakeup",
1633 "-e", "sched:sched_wakeup_new",
1638 "-e", "sched:sched_migrate_task", 1634 "-e", "sched:sched_migrate_task",
1639 }; 1635 };
1640 1636
@@ -1713,8 +1709,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1713 "perf sched replay [<options>]", 1709 "perf sched replay [<options>]",
1714 NULL 1710 NULL
1715 }; 1711 };
1716 const char * const sched_usage[] = { 1712 const char *const sched_subcommands[] = { "record", "latency", "map",
1717 "perf sched [<options>] {record|latency|map|replay|script}", 1713 "replay", "script", NULL };
1714 const char *sched_usage[] = {
1715 NULL,
1718 NULL 1716 NULL
1719 }; 1717 };
1720 struct trace_sched_handler lat_ops = { 1718 struct trace_sched_handler lat_ops = {
@@ -1736,8 +1734,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
1736 for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) 1734 for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++)
1737 sched.curr_pid[i] = -1; 1735 sched.curr_pid[i] = -1;
1738 1736
1739 argc = parse_options(argc, argv, sched_options, sched_usage, 1737 argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
1740 PARSE_OPT_STOP_AT_NON_OPTION); 1738 sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1741 if (!argc) 1739 if (!argc)
1742 usage_with_options(sched_usage, sched_options); 1740 usage_with_options(sched_usage, sched_options);
1743 1741
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 65aaa5bbf7ec..377971dc89a3 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -196,6 +196,12 @@ static void perf_top__record_precise_ip(struct perf_top *top,
196 196
197 pthread_mutex_unlock(&notes->lock); 197 pthread_mutex_unlock(&notes->lock);
198 198
199 /*
200 * This function is now called with he->hists->lock held.
201 * Release it before going to sleep.
202 */
203 pthread_mutex_unlock(&he->hists->lock);
204
199 if (err == -ERANGE && !he->ms.map->erange_warned) 205 if (err == -ERANGE && !he->ms.map->erange_warned)
200 ui__warn_map_erange(he->ms.map, sym, ip); 206 ui__warn_map_erange(he->ms.map, sym, ip);
201 else if (err == -ENOMEM) { 207 else if (err == -ENOMEM) {
@@ -203,6 +209,8 @@ static void perf_top__record_precise_ip(struct perf_top *top,
203 sym->name); 209 sym->name);
204 sleep(1); 210 sleep(1);
205 } 211 }
212
213 pthread_mutex_lock(&he->hists->lock);
206} 214}
207 215
208static void perf_top__show_details(struct perf_top *top) 216static void perf_top__show_details(struct perf_top *top)
@@ -238,24 +246,6 @@ out_unlock:
238 pthread_mutex_unlock(&notes->lock); 246 pthread_mutex_unlock(&notes->lock);
239} 247}
240 248
241static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
242 struct addr_location *al,
243 struct perf_sample *sample)
244{
245 struct hist_entry *he;
246
247 pthread_mutex_lock(&evsel->hists.lock);
248 he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL,
249 sample->period, sample->weight,
250 sample->transaction);
251 pthread_mutex_unlock(&evsel->hists.lock);
252 if (he == NULL)
253 return NULL;
254
255 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
256 return he;
257}
258
259static void perf_top__print_sym_table(struct perf_top *top) 249static void perf_top__print_sym_table(struct perf_top *top)
260{ 250{
261 char bf[160]; 251 char bf[160];
@@ -659,6 +649,26 @@ static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
659 return 0; 649 return 0;
660} 650}
661 651
652static int hist_iter__top_callback(struct hist_entry_iter *iter,
653 struct addr_location *al, bool single,
654 void *arg)
655{
656 struct perf_top *top = arg;
657 struct hist_entry *he = iter->he;
658 struct perf_evsel *evsel = iter->evsel;
659
660 if (sort__has_sym && single) {
661 u64 ip = al->addr;
662
663 if (al->map)
664 ip = al->map->unmap_ip(al->map, ip);
665
666 perf_top__record_precise_ip(top, he, evsel->idx, ip);
667 }
668
669 return 0;
670}
671
662static void perf_event__process_sample(struct perf_tool *tool, 672static void perf_event__process_sample(struct perf_tool *tool,
663 const union perf_event *event, 673 const union perf_event *event,
664 struct perf_evsel *evsel, 674 struct perf_evsel *evsel,
@@ -666,8 +676,6 @@ static void perf_event__process_sample(struct perf_tool *tool,
666 struct machine *machine) 676 struct machine *machine)
667{ 677{
668 struct perf_top *top = container_of(tool, struct perf_top, tool); 678 struct perf_top *top = container_of(tool, struct perf_top, tool);
669 struct symbol *parent = NULL;
670 u64 ip = sample->ip;
671 struct addr_location al; 679 struct addr_location al;
672 int err; 680 int err;
673 681
@@ -694,8 +702,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
694 if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) 702 if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
695 top->exact_samples++; 703 top->exact_samples++;
696 704
697 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 || 705 if (perf_event__preprocess_sample(event, machine, &al, sample) < 0)
698 al.filtered)
699 return; 706 return;
700 707
701 if (!top->kptr_restrict_warned && 708 if (!top->kptr_restrict_warned &&
@@ -743,25 +750,23 @@ static void perf_event__process_sample(struct perf_tool *tool,
743 } 750 }
744 751
745 if (al.sym == NULL || !al.sym->ignore) { 752 if (al.sym == NULL || !al.sym->ignore) {
746 struct hist_entry *he; 753 struct hist_entry_iter iter = {
754 .add_entry_cb = hist_iter__top_callback,
755 };
747 756
748 err = sample__resolve_callchain(sample, &parent, evsel, &al, 757 if (symbol_conf.cumulate_callchain)
749 top->max_stack); 758 iter.ops = &hist_iter_cumulative;
750 if (err) 759 else
751 return; 760 iter.ops = &hist_iter_normal;
752 761
753 he = perf_evsel__add_hist_entry(evsel, &al, sample); 762 pthread_mutex_lock(&evsel->hists.lock);
754 if (he == NULL) {
755 pr_err("Problem incrementing symbol period, skipping event\n");
756 return;
757 }
758 763
759 err = hist_entry__append_callchain(he, sample); 764 err = hist_entry_iter__add(&iter, &al, evsel, sample,
760 if (err) 765 top->max_stack, top);
761 return; 766 if (err < 0)
767 pr_err("Problem incrementing symbol period, skipping event\n");
762 768
763 if (sort__has_sym) 769 pthread_mutex_unlock(&evsel->hists.lock);
764 perf_top__record_precise_ip(top, he, evsel->idx, ip);
765 } 770 }
766 771
767 return; 772 return;
@@ -999,6 +1004,10 @@ static int perf_top_config(const char *var, const char *value, void *cb)
999 1004
1000 if (!strcmp(var, "top.call-graph")) 1005 if (!strcmp(var, "top.call-graph"))
1001 return record_parse_callchain(value, &top->record_opts); 1006 return record_parse_callchain(value, &top->record_opts);
1007 if (!strcmp(var, "top.children")) {
1008 symbol_conf.cumulate_callchain = perf_config_bool(var, value);
1009 return 0;
1010 }
1002 1011
1003 return perf_default_config(var, value, cb); 1012 return perf_default_config(var, value, cb);
1004} 1013}
@@ -1081,8 +1090,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1081 OPT_INCR('v', "verbose", &verbose, 1090 OPT_INCR('v', "verbose", &verbose,
1082 "be more verbose (show counter open errors, etc)"), 1091 "be more verbose (show counter open errors, etc)"),
1083 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 1092 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1084 "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," 1093 "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..."
1085 " abort, in_tx, transaction"), 1094 " Please refer the man page for the complete list."),
1095 OPT_STRING(0, "fields", &field_order, "key[,keys...]",
1096 "output field(s): overhead, period, sample plus all of sort keys"),
1086 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, 1097 OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
1087 "Show a column with the number of samples"), 1098 "Show a column with the number of samples"),
1088 OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, 1099 OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
@@ -1091,6 +1102,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1091 OPT_CALLBACK(0, "call-graph", &top.record_opts, 1102 OPT_CALLBACK(0, "call-graph", &top.record_opts,
1092 "mode[,dump_size]", record_callchain_help, 1103 "mode[,dump_size]", record_callchain_help,
1093 &parse_callchain_opt), 1104 &parse_callchain_opt),
1105 OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
1106 "Accumulate callchains of children and show total overhead as well"),
1094 OPT_INTEGER(0, "max-stack", &top.max_stack, 1107 OPT_INTEGER(0, "max-stack", &top.max_stack,
1095 "Set the maximum stack depth when parsing the callchain. " 1108 "Set the maximum stack depth when parsing the callchain. "
1096 "Default: " __stringify(PERF_MAX_STACK_DEPTH)), 1109 "Default: " __stringify(PERF_MAX_STACK_DEPTH)),
@@ -1116,6 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1116 OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), 1129 OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
1117 OPT_CALLBACK(0, "percent-limit", &top, "percent", 1130 OPT_CALLBACK(0, "percent-limit", &top, "percent",
1118 "Don't show entries under that percent", parse_percent_limit), 1131 "Don't show entries under that percent", parse_percent_limit),
1132 OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
1133 "How to display percentage of filtered entries", parse_filter_percentage),
1119 OPT_END() 1134 OPT_END()
1120 }; 1135 };
1121 const char * const top_usage[] = { 1136 const char * const top_usage[] = {
@@ -1133,17 +1148,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1133 if (argc) 1148 if (argc)
1134 usage_with_options(top_usage, options); 1149 usage_with_options(top_usage, options);
1135 1150
1136 if (sort_order == default_sort_order) 1151 sort__mode = SORT_MODE__TOP;
1137 sort_order = "dso,symbol"; 1152 /* display thread wants entries to be collapsed in a different tree */
1153 sort__need_collapse = 1;
1138 1154
1139 if (setup_sorting() < 0) { 1155 if (setup_sorting() < 0) {
1140 parse_options_usage(top_usage, options, "s", 1); 1156 if (sort_order)
1157 parse_options_usage(top_usage, options, "s", 1);
1158 if (field_order)
1159 parse_options_usage(sort_order ? NULL : top_usage,
1160 options, "fields", 0);
1141 goto out_delete_evlist; 1161 goto out_delete_evlist;
1142 } 1162 }
1143 1163
1144 /* display thread wants entries to be collapsed in a different tree */
1145 sort__need_collapse = 1;
1146
1147 if (top.use_stdio) 1164 if (top.use_stdio)
1148 use_browser = 0; 1165 use_browser = 0;
1149 else if (top.use_tui) 1166 else if (top.use_tui)
@@ -1192,6 +1209,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1192 1209
1193 top.sym_evsel = perf_evlist__first(top.evlist); 1210 top.sym_evsel = perf_evlist__first(top.evlist);
1194 1211
1212 if (!symbol_conf.use_callchain) {
1213 symbol_conf.cumulate_callchain = false;
1214 perf_hpp__cancel_cumulate();
1215 }
1216
1195 symbol_conf.priv_size = sizeof(struct annotation); 1217 symbol_conf.priv_size = sizeof(struct annotation);
1196 1218
1197 symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); 1219 symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 802cf544202b..4f100b54ba8b 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -29,16 +29,22 @@ ifeq ($(ARCH),x86)
29 endif 29 endif
30 NO_PERF_REGS := 0 30 NO_PERF_REGS := 0
31endif 31endif
32
32ifeq ($(ARCH),arm) 33ifeq ($(ARCH),arm)
33 NO_PERF_REGS := 0 34 NO_PERF_REGS := 0
34 LIBUNWIND_LIBS = -lunwind -lunwind-arm 35 LIBUNWIND_LIBS = -lunwind -lunwind-arm
35endif 36endif
36 37
37# So far there's only x86 libdw unwind support merged in perf. 38ifeq ($(ARCH),arm64)
39 NO_PERF_REGS := 0
40 LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
41endif
42
43# So far there's only x86 and arm libdw unwind support merged in perf.
38# Disable it on all other architectures in case libdw unwind 44# Disable it on all other architectures in case libdw unwind
39# support is detected in system. Add supported architectures 45# support is detected in system. Add supported architectures
40# to the check. 46# to the check.
41ifneq ($(ARCH),x86) 47ifneq ($(ARCH),$(filter $(ARCH),x86 arm))
42 NO_LIBDW_DWARF_UNWIND := 1 48 NO_LIBDW_DWARF_UNWIND := 1
43endif 49endif
44 50
@@ -168,7 +174,6 @@ CORE_FEATURE_TESTS = \
168 libpython-version \ 174 libpython-version \
169 libslang \ 175 libslang \
170 libunwind \ 176 libunwind \
171 on-exit \
172 stackprotector-all \ 177 stackprotector-all \
173 timerfd \ 178 timerfd \
174 libdw-dwarf-unwind 179 libdw-dwarf-unwind
@@ -194,7 +199,6 @@ VF_FEATURE_TESTS = \
194 libelf-getphdrnum \ 199 libelf-getphdrnum \
195 libelf-mmap \ 200 libelf-mmap \
196 libpython-version \ 201 libpython-version \
197 on-exit \
198 stackprotector-all \ 202 stackprotector-all \
199 timerfd \ 203 timerfd \
200 libunwind-debug-frame \ 204 libunwind-debug-frame \
@@ -370,7 +374,7 @@ else
370endif 374endif
371 375
372ifndef NO_LIBUNWIND 376ifndef NO_LIBUNWIND
373 ifeq ($(ARCH),arm) 377 ifeq ($(ARCH),$(filter $(ARCH),arm arm64))
374 $(call feature_check,libunwind-debug-frame) 378 $(call feature_check,libunwind-debug-frame)
375 ifneq ($(feature-libunwind-debug-frame), 1) 379 ifneq ($(feature-libunwind-debug-frame), 1)
376 msg := $(warning No debug_frame support found in libunwind); 380 msg := $(warning No debug_frame support found in libunwind);
@@ -443,6 +447,7 @@ else
443 ifneq ($(feature-libperl), 1) 447 ifneq ($(feature-libperl), 1)
444 CFLAGS += -DNO_LIBPERL 448 CFLAGS += -DNO_LIBPERL
445 NO_LIBPERL := 1 449 NO_LIBPERL := 1
450 msg := $(warning Missing perl devel files. Disabling perl scripting support, consider installing perl-ExtUtils-Embed);
446 else 451 else
447 LDFLAGS += $(PERL_EMBED_LDFLAGS) 452 LDFLAGS += $(PERL_EMBED_LDFLAGS)
448 EXTLIBS += $(PERL_EMBED_LIBADD) 453 EXTLIBS += $(PERL_EMBED_LIBADD)
@@ -565,12 +570,6 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),)
565 CFLAGS += -DHAVE_LIBBFD_SUPPORT 570 CFLAGS += -DHAVE_LIBBFD_SUPPORT
566endif 571endif
567 572
568ifndef NO_ON_EXIT
569 ifeq ($(feature-on-exit), 1)
570 CFLAGS += -DHAVE_ON_EXIT_SUPPORT
571 endif
572endif
573
574ifndef NO_BACKTRACE 573ifndef NO_BACKTRACE
575 ifeq ($(feature-backtrace), 1) 574 ifeq ($(feature-backtrace), 1)
576 CFLAGS += -DHAVE_BACKTRACE_SUPPORT 575 CFLAGS += -DHAVE_BACKTRACE_SUPPORT
@@ -601,7 +600,7 @@ endif
601 600
602# Make the path relative to DESTDIR, not to prefix 601# Make the path relative to DESTDIR, not to prefix
603ifndef DESTDIR 602ifndef DESTDIR
604prefix = $(HOME) 603prefix ?= $(HOME)
605endif 604endif
606bindir_relative = bin 605bindir_relative = bin
607bindir = $(prefix)/$(bindir_relative) 606bindir = $(prefix)/$(bindir_relative)
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile
index 2da103c53f89..64c84e5f0514 100644
--- a/tools/perf/config/feature-checks/Makefile
+++ b/tools/perf/config/feature-checks/Makefile
@@ -24,7 +24,6 @@ FILES= \
24 test-libslang.bin \ 24 test-libslang.bin \
25 test-libunwind.bin \ 25 test-libunwind.bin \
26 test-libunwind-debug-frame.bin \ 26 test-libunwind-debug-frame.bin \
27 test-on-exit.bin \
28 test-stackprotector-all.bin \ 27 test-stackprotector-all.bin \
29 test-timerfd.bin \ 28 test-timerfd.bin \
30 test-libdw-dwarf-unwind.bin 29 test-libdw-dwarf-unwind.bin
@@ -133,9 +132,6 @@ test-liberty-z.bin:
133test-cplus-demangle.bin: 132test-cplus-demangle.bin:
134 $(BUILD) -liberty 133 $(BUILD) -liberty
135 134
136test-on-exit.bin:
137 $(BUILD)
138
139test-backtrace.bin: 135test-backtrace.bin:
140 $(BUILD) 136 $(BUILD)
141 137
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c
index fc37eb3ca17b..fe5c1e5c952f 100644
--- a/tools/perf/config/feature-checks/test-all.c
+++ b/tools/perf/config/feature-checks/test-all.c
@@ -69,10 +69,6 @@
69# include "test-libbfd.c" 69# include "test-libbfd.c"
70#undef main 70#undef main
71 71
72#define main main_test_on_exit
73# include "test-on-exit.c"
74#undef main
75
76#define main main_test_backtrace 72#define main main_test_backtrace
77# include "test-backtrace.c" 73# include "test-backtrace.c"
78#undef main 74#undef main
@@ -110,7 +106,6 @@ int main(int argc, char *argv[])
110 main_test_gtk2(argc, argv); 106 main_test_gtk2(argc, argv);
111 main_test_gtk2_infobar(argc, argv); 107 main_test_gtk2_infobar(argc, argv);
112 main_test_libbfd(); 108 main_test_libbfd();
113 main_test_on_exit();
114 main_test_backtrace(); 109 main_test_backtrace();
115 main_test_libnuma(); 110 main_test_libnuma();
116 main_test_timerfd(); 111 main_test_timerfd();
diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c
deleted file mode 100644
index 8e88b16e6ded..000000000000
--- a/tools/perf/config/feature-checks/test-on-exit.c
+++ /dev/null
@@ -1,16 +0,0 @@
1#include <stdio.h>
2#include <stdlib.h>
3
4static void exit_fn(int status, void *__data)
5{
6 printf("exit status: %d, data: %d\n", status, *(int *)__data);
7}
8
9static int data = 123;
10
11int main(void)
12{
13 on_exit(exit_fn, &data);
14
15 return 321;
16}
diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh
index ae3a57694b6b..33569847fdcc 100644
--- a/tools/perf/perf-completion.sh
+++ b/tools/perf/perf-completion.sh
@@ -121,8 +121,8 @@ __perf_main ()
121 elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then 121 elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
122 evts=$($cmd list --raw-dump) 122 evts=$($cmd list --raw-dump)
123 __perfcomp_colon "$evts" "$cur" 123 __perfcomp_colon "$evts" "$cur"
124 # List subcommands for 'perf kvm' 124 # List subcommands for perf commands
125 elif [[ $prev == "kvm" ]]; then 125 elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then
126 subcmds=$($cmd $prev --list-cmds) 126 subcmds=$($cmd $prev --list-cmds)
127 __perfcomp_colon "$subcmds" "$cur" 127 __perfcomp_colon "$subcmds" "$cur"
128 # List long option names 128 # List long option names
diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h
new file mode 100644
index 000000000000..5268a1481d23
--- /dev/null
+++ b/tools/perf/perf-sys.h
@@ -0,0 +1,190 @@
1#ifndef _PERF_SYS_H
2#define _PERF_SYS_H
3
4#include <unistd.h>
5#include <sys/types.h>
6#include <sys/syscall.h>
7#include <linux/types.h>
8#include <linux/perf_event.h>
9#include <asm/unistd.h>
10
11#if defined(__i386__)
12#define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
13#define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
14#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
15#define cpu_relax() asm volatile("rep; nop" ::: "memory");
16#define CPUINFO_PROC "model name"
17#ifndef __NR_perf_event_open
18# define __NR_perf_event_open 336
19#endif
20#ifndef __NR_futex
21# define __NR_futex 240
22#endif
23#ifndef __NR_gettid
24# define __NR_gettid 224
25#endif
26#endif
27
28#if defined(__x86_64__)
29#define mb() asm volatile("mfence" ::: "memory")
30#define wmb() asm volatile("sfence" ::: "memory")
31#define rmb() asm volatile("lfence" ::: "memory")
32#define cpu_relax() asm volatile("rep; nop" ::: "memory");
33#define CPUINFO_PROC "model name"
34#ifndef __NR_perf_event_open
35# define __NR_perf_event_open 298
36#endif
37#ifndef __NR_futex
38# define __NR_futex 202
39#endif
40#ifndef __NR_gettid
41# define __NR_gettid 186
42#endif
43#endif
44
45#ifdef __powerpc__
46#include "../../arch/powerpc/include/uapi/asm/unistd.h"
47#define mb() asm volatile ("sync" ::: "memory")
48#define wmb() asm volatile ("sync" ::: "memory")
49#define rmb() asm volatile ("sync" ::: "memory")
50#define CPUINFO_PROC "cpu"
51#endif
52
53#ifdef __s390__
54#define mb() asm volatile("bcr 15,0" ::: "memory")
55#define wmb() asm volatile("bcr 15,0" ::: "memory")
56#define rmb() asm volatile("bcr 15,0" ::: "memory")
57#endif
58
59#ifdef __sh__
60#if defined(__SH4A__) || defined(__SH5__)
61# define mb() asm volatile("synco" ::: "memory")
62# define wmb() asm volatile("synco" ::: "memory")
63# define rmb() asm volatile("synco" ::: "memory")
64#else
65# define mb() asm volatile("" ::: "memory")
66# define wmb() asm volatile("" ::: "memory")
67# define rmb() asm volatile("" ::: "memory")
68#endif
69#define CPUINFO_PROC "cpu type"
70#endif
71
72#ifdef __hppa__
73#define mb() asm volatile("" ::: "memory")
74#define wmb() asm volatile("" ::: "memory")
75#define rmb() asm volatile("" ::: "memory")
76#define CPUINFO_PROC "cpu"
77#endif
78
79#ifdef __sparc__
80#ifdef __LP64__
81#define mb() asm volatile("ba,pt %%xcc, 1f\n" \
82 "membar #StoreLoad\n" \
83 "1:\n":::"memory")
84#else
85#define mb() asm volatile("":::"memory")
86#endif
87#define wmb() asm volatile("":::"memory")
88#define rmb() asm volatile("":::"memory")
89#define CPUINFO_PROC "cpu"
90#endif
91
92#ifdef __alpha__
93#define mb() asm volatile("mb" ::: "memory")
94#define wmb() asm volatile("wmb" ::: "memory")
95#define rmb() asm volatile("mb" ::: "memory")
96#define CPUINFO_PROC "cpu model"
97#endif
98
99#ifdef __ia64__
100#define mb() asm volatile ("mf" ::: "memory")
101#define wmb() asm volatile ("mf" ::: "memory")
102#define rmb() asm volatile ("mf" ::: "memory")
103#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
104#define CPUINFO_PROC "model name"
105#endif
106
107#ifdef __arm__
108/*
109 * Use the __kuser_memory_barrier helper in the CPU helper page. See
110 * arch/arm/kernel/entry-armv.S in the kernel source for details.
111 */
112#define mb() ((void(*)(void))0xffff0fa0)()
113#define wmb() ((void(*)(void))0xffff0fa0)()
114#define rmb() ((void(*)(void))0xffff0fa0)()
115#define CPUINFO_PROC "Processor"
116#endif
117
118#ifdef __aarch64__
119#define mb() asm volatile("dmb ish" ::: "memory")
120#define wmb() asm volatile("dmb ishst" ::: "memory")
121#define rmb() asm volatile("dmb ishld" ::: "memory")
122#define cpu_relax() asm volatile("yield" ::: "memory")
123#endif
124
125#ifdef __mips__
126#define mb() asm volatile( \
127 ".set mips2\n\t" \
128 "sync\n\t" \
129 ".set mips0" \
130 : /* no output */ \
131 : /* no input */ \
132 : "memory")
133#define wmb() mb()
134#define rmb() mb()
135#define CPUINFO_PROC "cpu model"
136#endif
137
138#ifdef __arc__
139#define mb() asm volatile("" ::: "memory")
140#define wmb() asm volatile("" ::: "memory")
141#define rmb() asm volatile("" ::: "memory")
142#define CPUINFO_PROC "Processor"
143#endif
144
145#ifdef __metag__
146#define mb() asm volatile("" ::: "memory")
147#define wmb() asm volatile("" ::: "memory")
148#define rmb() asm volatile("" ::: "memory")
149#define CPUINFO_PROC "CPU"
150#endif
151
152#ifdef __xtensa__
153#define mb() asm volatile("memw" ::: "memory")
154#define wmb() asm volatile("memw" ::: "memory")
155#define rmb() asm volatile("" ::: "memory")
156#define CPUINFO_PROC "core ID"
157#endif
158
159#ifdef __tile__
160#define mb() asm volatile ("mf" ::: "memory")
161#define wmb() asm volatile ("mf" ::: "memory")
162#define rmb() asm volatile ("mf" ::: "memory")
163#define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory")
164#define CPUINFO_PROC "model name"
165#endif
166
167#define barrier() asm volatile ("" ::: "memory")
168
169#ifndef cpu_relax
170#define cpu_relax() barrier()
171#endif
172
173static inline int
174sys_perf_event_open(struct perf_event_attr *attr,
175 pid_t pid, int cpu, int group_fd,
176 unsigned long flags)
177{
178 int fd;
179
180 fd = syscall(__NR_perf_event_open, attr, pid, cpu,
181 group_fd, flags);
182
183#ifdef HAVE_ATTR_TEST
184 if (unlikely(test_attr__enabled))
185 test_attr__open(attr, pid, cpu, fd, group_fd, flags);
186#endif
187 return fd;
188}
189
190#endif /* _PERF_SYS_H */
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 431798a4110d..78f7b920e548 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -481,14 +481,18 @@ int main(int argc, const char **argv)
481 fprintf(stderr, "cannot handle %s internally", cmd); 481 fprintf(stderr, "cannot handle %s internally", cmd);
482 goto out; 482 goto out;
483 } 483 }
484#ifdef HAVE_LIBAUDIT_SUPPORT
485 if (!prefixcmp(cmd, "trace")) { 484 if (!prefixcmp(cmd, "trace")) {
485#ifdef HAVE_LIBAUDIT_SUPPORT
486 set_buildid_dir(); 486 set_buildid_dir();
487 setup_path(); 487 setup_path();
488 argv[0] = "trace"; 488 argv[0] = "trace";
489 return cmd_trace(argc, argv, NULL); 489 return cmd_trace(argc, argv, NULL);
490 } 490#else
491 fprintf(stderr,
492 "trace command not available: missing audit-libs devel package at build time.\n");
493 goto out;
491#endif 494#endif
495 }
492 /* Look for flags.. */ 496 /* Look for flags.. */
493 argv++; 497 argv++;
494 argc--; 498 argc--;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index 5c11ecad02a9..510c65f72858 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -1,182 +1,18 @@
1#ifndef _PERF_PERF_H 1#ifndef _PERF_PERF_H
2#define _PERF_PERF_H 2#define _PERF_PERF_H
3 3
4#include <asm/unistd.h>
5
6#if defined(__i386__)
7#define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
8#define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
9#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
10#define cpu_relax() asm volatile("rep; nop" ::: "memory");
11#define CPUINFO_PROC "model name"
12#ifndef __NR_perf_event_open
13# define __NR_perf_event_open 336
14#endif
15#ifndef __NR_futex
16# define __NR_futex 240
17#endif
18#endif
19
20#if defined(__x86_64__)
21#define mb() asm volatile("mfence" ::: "memory")
22#define wmb() asm volatile("sfence" ::: "memory")
23#define rmb() asm volatile("lfence" ::: "memory")
24#define cpu_relax() asm volatile("rep; nop" ::: "memory");
25#define CPUINFO_PROC "model name"
26#ifndef __NR_perf_event_open
27# define __NR_perf_event_open 298
28#endif
29#ifndef __NR_futex
30# define __NR_futex 202
31#endif
32#endif
33
34#ifdef __powerpc__
35#include "../../arch/powerpc/include/uapi/asm/unistd.h"
36#define mb() asm volatile ("sync" ::: "memory")
37#define wmb() asm volatile ("sync" ::: "memory")
38#define rmb() asm volatile ("sync" ::: "memory")
39#define CPUINFO_PROC "cpu"
40#endif
41
42#ifdef __s390__
43#define mb() asm volatile("bcr 15,0" ::: "memory")
44#define wmb() asm volatile("bcr 15,0" ::: "memory")
45#define rmb() asm volatile("bcr 15,0" ::: "memory")
46#endif
47
48#ifdef __sh__
49#if defined(__SH4A__) || defined(__SH5__)
50# define mb() asm volatile("synco" ::: "memory")
51# define wmb() asm volatile("synco" ::: "memory")
52# define rmb() asm volatile("synco" ::: "memory")
53#else
54# define mb() asm volatile("" ::: "memory")
55# define wmb() asm volatile("" ::: "memory")
56# define rmb() asm volatile("" ::: "memory")
57#endif
58#define CPUINFO_PROC "cpu type"
59#endif
60
61#ifdef __hppa__
62#define mb() asm volatile("" ::: "memory")
63#define wmb() asm volatile("" ::: "memory")
64#define rmb() asm volatile("" ::: "memory")
65#define CPUINFO_PROC "cpu"
66#endif
67
68#ifdef __sparc__
69#ifdef __LP64__
70#define mb() asm volatile("ba,pt %%xcc, 1f\n" \
71 "membar #StoreLoad\n" \
72 "1:\n":::"memory")
73#else
74#define mb() asm volatile("":::"memory")
75#endif
76#define wmb() asm volatile("":::"memory")
77#define rmb() asm volatile("":::"memory")
78#define CPUINFO_PROC "cpu"
79#endif
80
81#ifdef __alpha__
82#define mb() asm volatile("mb" ::: "memory")
83#define wmb() asm volatile("wmb" ::: "memory")
84#define rmb() asm volatile("mb" ::: "memory")
85#define CPUINFO_PROC "cpu model"
86#endif
87
88#ifdef __ia64__
89#define mb() asm volatile ("mf" ::: "memory")
90#define wmb() asm volatile ("mf" ::: "memory")
91#define rmb() asm volatile ("mf" ::: "memory")
92#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
93#define CPUINFO_PROC "model name"
94#endif
95
96#ifdef __arm__
97/*
98 * Use the __kuser_memory_barrier helper in the CPU helper page. See
99 * arch/arm/kernel/entry-armv.S in the kernel source for details.
100 */
101#define mb() ((void(*)(void))0xffff0fa0)()
102#define wmb() ((void(*)(void))0xffff0fa0)()
103#define rmb() ((void(*)(void))0xffff0fa0)()
104#define CPUINFO_PROC "Processor"
105#endif
106
107#ifdef __aarch64__
108#define mb() asm volatile("dmb ish" ::: "memory")
109#define wmb() asm volatile("dmb ishst" ::: "memory")
110#define rmb() asm volatile("dmb ishld" ::: "memory")
111#define cpu_relax() asm volatile("yield" ::: "memory")
112#endif
113
114#ifdef __mips__
115#define mb() asm volatile( \
116 ".set mips2\n\t" \
117 "sync\n\t" \
118 ".set mips0" \
119 : /* no output */ \
120 : /* no input */ \
121 : "memory")
122#define wmb() mb()
123#define rmb() mb()
124#define CPUINFO_PROC "cpu model"
125#endif
126
127#ifdef __arc__
128#define mb() asm volatile("" ::: "memory")
129#define wmb() asm volatile("" ::: "memory")
130#define rmb() asm volatile("" ::: "memory")
131#define CPUINFO_PROC "Processor"
132#endif
133
134#ifdef __metag__
135#define mb() asm volatile("" ::: "memory")
136#define wmb() asm volatile("" ::: "memory")
137#define rmb() asm volatile("" ::: "memory")
138#define CPUINFO_PROC "CPU"
139#endif
140
141#ifdef __xtensa__
142#define mb() asm volatile("memw" ::: "memory")
143#define wmb() asm volatile("memw" ::: "memory")
144#define rmb() asm volatile("" ::: "memory")
145#define CPUINFO_PROC "core ID"
146#endif
147
148#ifdef __tile__
149#define mb() asm volatile ("mf" ::: "memory")
150#define wmb() asm volatile ("mf" ::: "memory")
151#define rmb() asm volatile ("mf" ::: "memory")
152#define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory")
153#define CPUINFO_PROC "model name"
154#endif
155
156#define barrier() asm volatile ("" ::: "memory")
157
158#ifndef cpu_relax
159#define cpu_relax() barrier()
160#endif
161
162#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
163
164
165#include <time.h> 4#include <time.h>
166#include <unistd.h>
167#include <sys/types.h>
168#include <sys/syscall.h>
169
170#include <linux/perf_event.h>
171#include "util/types.h"
172#include <stdbool.h> 5#include <stdbool.h>
6#include <linux/types.h>
7#include <linux/perf_event.h>
173 8
174/* 9extern bool test_attr__enabled;
175 * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all 10void test_attr__init(void);
176 * counters in the current task. 11void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
177 */ 12 int fd, int group_fd, unsigned long flags);
178#define PR_TASK_PERF_EVENTS_DISABLE 31 13
179#define PR_TASK_PERF_EVENTS_ENABLE 32 14#define HAVE_ATTR_TEST
15#include "perf-sys.h"
180 16
181#ifndef NSEC_PER_SEC 17#ifndef NSEC_PER_SEC
182# define NSEC_PER_SEC 1000000000ULL 18# define NSEC_PER_SEC 1000000000ULL
@@ -193,67 +29,8 @@ static inline unsigned long long rdclock(void)
193 return ts.tv_sec * 1000000000ULL + ts.tv_nsec; 29 return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
194} 30}
195 31
196/*
197 * Pick up some kernel type conventions:
198 */
199#define __user
200#define asmlinkage
201
202#define unlikely(x) __builtin_expect(!!(x), 0)
203#define min(x, y) ({ \
204 typeof(x) _min1 = (x); \
205 typeof(y) _min2 = (y); \
206 (void) (&_min1 == &_min2); \
207 _min1 < _min2 ? _min1 : _min2; })
208
209extern bool test_attr__enabled;
210void test_attr__init(void);
211void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
212 int fd, int group_fd, unsigned long flags);
213
214static inline int
215sys_perf_event_open(struct perf_event_attr *attr,
216 pid_t pid, int cpu, int group_fd,
217 unsigned long flags)
218{
219 int fd;
220
221 fd = syscall(__NR_perf_event_open, attr, pid, cpu,
222 group_fd, flags);
223
224 if (unlikely(test_attr__enabled))
225 test_attr__open(attr, pid, cpu, fd, group_fd, flags);
226
227 return fd;
228}
229
230#define MAX_COUNTERS 256
231#define MAX_NR_CPUS 256 32#define MAX_NR_CPUS 256
232 33
233struct ip_callchain {
234 u64 nr;
235 u64 ips[0];
236};
237
238struct branch_flags {
239 u64 mispred:1;
240 u64 predicted:1;
241 u64 in_tx:1;
242 u64 abort:1;
243 u64 reserved:60;
244};
245
246struct branch_entry {
247 u64 from;
248 u64 to;
249 struct branch_flags flags;
250};
251
252struct branch_stack {
253 u64 nr;
254 struct branch_entry entries[0];
255};
256
257extern const char *input_name; 34extern const char *input_name;
258extern bool perf_host, perf_guest; 35extern bool perf_host, perf_guest;
259extern const char perf_version_string[]; 36extern const char perf_version_string[];
@@ -262,13 +39,6 @@ void pthread__unblock_sigwinch(void);
262 39
263#include "util/target.h" 40#include "util/target.h"
264 41
265enum perf_call_graph_mode {
266 CALLCHAIN_NONE,
267 CALLCHAIN_FP,
268 CALLCHAIN_DWARF,
269 CALLCHAIN_MAX
270};
271
272struct record_opts { 42struct record_opts {
273 struct target target; 43 struct target target;
274 int call_graph; 44 int call_graph;
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c
index 00218f503b2e..2dfc9ad0e6f2 100644
--- a/tools/perf/tests/attr.c
+++ b/tools/perf/tests/attr.c
@@ -1,4 +1,3 @@
1
2/* 1/*
3 * The struct perf_event_attr test support. 2 * The struct perf_event_attr test support.
4 * 3 *
@@ -19,14 +18,8 @@
19 * permissions. All the event text files are stored there. 18 * permissions. All the event text files are stored there.
20 */ 19 */
21 20
22/*
23 * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select
24 * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu.
25 */
26#define __SANE_USERSPACE_TYPES__
27#include <stdlib.h> 21#include <stdlib.h>
28#include <stdio.h> 22#include <stdio.h>
29#include <inttypes.h>
30#include <linux/types.h> 23#include <linux/types.h>
31#include <linux/kernel.h> 24#include <linux/kernel.h>
32#include "../perf.h" 25#include "../perf.h"
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index b11bf8a08430..802e3cd50f6f 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -115,7 +115,7 @@ static struct test {
115 .desc = "Test parsing with no sample_id_all bit set", 115 .desc = "Test parsing with no sample_id_all bit set",
116 .func = test__parse_no_sample_id_all, 116 .func = test__parse_no_sample_id_all,
117 }, 117 },
118#if defined(__x86_64__) || defined(__i386__) 118#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
119#ifdef HAVE_DWARF_UNWIND_SUPPORT 119#ifdef HAVE_DWARF_UNWIND_SUPPORT
120 { 120 {
121 .desc = "Test dwarf unwind", 121 .desc = "Test dwarf unwind",
@@ -124,6 +124,26 @@ static struct test {
124#endif 124#endif
125#endif 125#endif
126 { 126 {
127 .desc = "Test filtering hist entries",
128 .func = test__hists_filter,
129 },
130 {
131 .desc = "Test mmap thread lookup",
132 .func = test__mmap_thread_lookup,
133 },
134 {
135 .desc = "Test thread mg sharing",
136 .func = test__thread_mg_share,
137 },
138 {
139 .desc = "Test output sorting of hist entries",
140 .func = test__hists_output,
141 },
142 {
143 .desc = "Test cumulation of child hist entries",
144 .func = test__hists_cumulate,
145 },
146 {
127 .func = NULL, 147 .func = NULL,
128 }, 148 },
129}; 149};
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
index bfb186900ac0..67f2d6323558 100644
--- a/tools/perf/tests/code-reading.c
+++ b/tools/perf/tests/code-reading.c
@@ -1,8 +1,7 @@
1#include <sys/types.h> 1#include <linux/types.h>
2#include <stdlib.h> 2#include <stdlib.h>
3#include <unistd.h> 3#include <unistd.h>
4#include <stdio.h> 4#include <stdio.h>
5#include <inttypes.h>
6#include <ctype.h> 5#include <ctype.h>
7#include <string.h> 6#include <string.h>
8 7
@@ -257,7 +256,7 @@ static int process_sample_event(struct machine *machine,
257 return -1; 256 return -1;
258 } 257 }
259 258
260 thread = machine__findnew_thread(machine, sample.pid, sample.pid); 259 thread = machine__findnew_thread(machine, sample.pid, sample.tid);
261 if (!thread) { 260 if (!thread) {
262 pr_debug("machine__findnew_thread failed\n"); 261 pr_debug("machine__findnew_thread failed\n");
263 return -1; 262 return -1;
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c
index 9cc81a3eb9b4..3e6cb171e3d3 100644
--- a/tools/perf/tests/dso-data.c
+++ b/tools/perf/tests/dso-data.c
@@ -1,7 +1,7 @@
1#include "util.h" 1#include "util.h"
2 2
3#include <stdlib.h> 3#include <stdlib.h>
4#include <sys/types.h> 4#include <linux/types.h>
5#include <sys/stat.h> 5#include <sys/stat.h>
6#include <fcntl.h> 6#include <fcntl.h>
7#include <string.h> 7#include <string.h>
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index c059ee81c038..108f0cd49f4e 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -1,5 +1,5 @@
1#include <linux/compiler.h> 1#include <linux/compiler.h>
2#include <sys/types.h> 2#include <linux/types.h>
3#include <unistd.h> 3#include <unistd.h>
4#include "tests.h" 4#include "tests.h"
5#include "debug.h" 5#include "debug.h"
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c
index 4774f7fbb758..35d7fdb2328d 100644
--- a/tools/perf/tests/evsel-tp-sched.c
+++ b/tools/perf/tests/evsel-tp-sched.c
@@ -74,9 +74,6 @@ int test__perf_evsel__tp_sched_test(void)
74 if (perf_evsel__test_field(evsel, "prio", 4, true)) 74 if (perf_evsel__test_field(evsel, "prio", 4, true))
75 ret = -1; 75 ret = -1;
76 76
77 if (perf_evsel__test_field(evsel, "success", 4, true))
78 ret = -1;
79
80 if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) 77 if (perf_evsel__test_field(evsel, "target_cpu", 4, true))
81 ret = -1; 78 ret = -1;
82 79
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
new file mode 100644
index 000000000000..a62c09134516
--- /dev/null
+++ b/tools/perf/tests/hists_common.c
@@ -0,0 +1,209 @@
1#include "perf.h"
2#include "util/debug.h"
3#include "util/symbol.h"
4#include "util/sort.h"
5#include "util/evsel.h"
6#include "util/evlist.h"
7#include "util/machine.h"
8#include "util/thread.h"
9#include "tests/hists_common.h"
10
11static struct {
12 u32 pid;
13 const char *comm;
14} fake_threads[] = {
15 { FAKE_PID_PERF1, "perf" },
16 { FAKE_PID_PERF2, "perf" },
17 { FAKE_PID_BASH, "bash" },
18};
19
20static struct {
21 u32 pid;
22 u64 start;
23 const char *filename;
24} fake_mmap_info[] = {
25 { FAKE_PID_PERF1, FAKE_MAP_PERF, "perf" },
26 { FAKE_PID_PERF1, FAKE_MAP_LIBC, "libc" },
27 { FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" },
28 { FAKE_PID_PERF2, FAKE_MAP_PERF, "perf" },
29 { FAKE_PID_PERF2, FAKE_MAP_LIBC, "libc" },
30 { FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" },
31 { FAKE_PID_BASH, FAKE_MAP_BASH, "bash" },
32 { FAKE_PID_BASH, FAKE_MAP_LIBC, "libc" },
33 { FAKE_PID_BASH, FAKE_MAP_KERNEL, "[kernel]" },
34};
35
36struct fake_sym {
37 u64 start;
38 u64 length;
39 const char *name;
40};
41
42static struct fake_sym perf_syms[] = {
43 { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
44 { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" },
45 { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" },
46};
47
48static struct fake_sym bash_syms[] = {
49 { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" },
50 { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" },
51 { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" },
52};
53
54static struct fake_sym libc_syms[] = {
55 { 700, 100, "malloc" },
56 { 800, 100, "free" },
57 { 900, 100, "realloc" },
58 { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" },
59 { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" },
60 { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" },
61};
62
63static struct fake_sym kernel_syms[] = {
64 { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" },
65 { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" },
66 { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" },
67};
68
69static struct {
70 const char *dso_name;
71 struct fake_sym *syms;
72 size_t nr_syms;
73} fake_symbols[] = {
74 { "perf", perf_syms, ARRAY_SIZE(perf_syms) },
75 { "bash", bash_syms, ARRAY_SIZE(bash_syms) },
76 { "libc", libc_syms, ARRAY_SIZE(libc_syms) },
77 { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) },
78};
79
80struct machine *setup_fake_machine(struct machines *machines)
81{
82 struct machine *machine = machines__find(machines, HOST_KERNEL_ID);
83 size_t i;
84
85 if (machine == NULL) {
86 pr_debug("Not enough memory for machine setup\n");
87 return NULL;
88 }
89
90 for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
91 struct thread *thread;
92
93 thread = machine__findnew_thread(machine, fake_threads[i].pid,
94 fake_threads[i].pid);
95 if (thread == NULL)
96 goto out;
97
98 thread__set_comm(thread, fake_threads[i].comm, 0);
99 }
100
101 for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
102 union perf_event fake_mmap_event = {
103 .mmap = {
104 .header = { .misc = PERF_RECORD_MISC_USER, },
105 .pid = fake_mmap_info[i].pid,
106 .tid = fake_mmap_info[i].pid,
107 .start = fake_mmap_info[i].start,
108 .len = FAKE_MAP_LENGTH,
109 .pgoff = 0ULL,
110 },
111 };
112
113 strcpy(fake_mmap_event.mmap.filename,
114 fake_mmap_info[i].filename);
115
116 machine__process_mmap_event(machine, &fake_mmap_event, NULL);
117 }
118
119 for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
120 size_t k;
121 struct dso *dso;
122
123 dso = __dsos__findnew(&machine->user_dsos,
124 fake_symbols[i].dso_name);
125 if (dso == NULL)
126 goto out;
127
128 /* emulate dso__load() */
129 dso__set_loaded(dso, MAP__FUNCTION);
130
131 for (k = 0; k < fake_symbols[i].nr_syms; k++) {
132 struct symbol *sym;
133 struct fake_sym *fsym = &fake_symbols[i].syms[k];
134
135 sym = symbol__new(fsym->start, fsym->length,
136 STB_GLOBAL, fsym->name);
137 if (sym == NULL)
138 goto out;
139
140 symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
141 }
142 }
143
144 return machine;
145
146out:
147 pr_debug("Not enough memory for machine setup\n");
148 machine__delete_threads(machine);
149 machine__delete(machine);
150 return NULL;
151}
152
153void print_hists_in(struct hists *hists)
154{
155 int i = 0;
156 struct rb_root *root;
157 struct rb_node *node;
158
159 if (sort__need_collapse)
160 root = &hists->entries_collapsed;
161 else
162 root = hists->entries_in;
163
164 pr_info("----- %s --------\n", __func__);
165 node = rb_first(root);
166 while (node) {
167 struct hist_entry *he;
168
169 he = rb_entry(node, struct hist_entry, rb_node_in);
170
171 if (!he->filtered) {
172 pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
173 i, thread__comm_str(he->thread),
174 he->ms.map->dso->short_name,
175 he->ms.sym->name, he->stat.period);
176 }
177
178 i++;
179 node = rb_next(node);
180 }
181}
182
183void print_hists_out(struct hists *hists)
184{
185 int i = 0;
186 struct rb_root *root;
187 struct rb_node *node;
188
189 root = &hists->entries;
190
191 pr_info("----- %s --------\n", __func__);
192 node = rb_first(root);
193 while (node) {
194 struct hist_entry *he;
195
196 he = rb_entry(node, struct hist_entry, rb_node);
197
198 if (!he->filtered) {
199 pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"/%"PRIu64"\n",
200 i, thread__comm_str(he->thread), he->thread->tid,
201 he->ms.map->dso->short_name,
202 he->ms.sym->name, he->stat.period,
203 he->stat_acc ? he->stat_acc->period : 0);
204 }
205
206 i++;
207 node = rb_next(node);
208 }
209}
diff --git a/tools/perf/tests/hists_common.h b/tools/perf/tests/hists_common.h
new file mode 100644
index 000000000000..888254e8665c
--- /dev/null
+++ b/tools/perf/tests/hists_common.h
@@ -0,0 +1,75 @@
1#ifndef __PERF_TESTS__HISTS_COMMON_H__
2#define __PERF_TESTS__HISTS_COMMON_H__
3
4struct machine;
5struct machines;
6
7#define FAKE_PID_PERF1 100
8#define FAKE_PID_PERF2 200
9#define FAKE_PID_BASH 300
10
11#define FAKE_MAP_PERF 0x400000
12#define FAKE_MAP_BASH 0x400000
13#define FAKE_MAP_LIBC 0x500000
14#define FAKE_MAP_KERNEL 0xf00000
15#define FAKE_MAP_LENGTH 0x100000
16
17#define FAKE_SYM_OFFSET1 700
18#define FAKE_SYM_OFFSET2 800
19#define FAKE_SYM_OFFSET3 900
20#define FAKE_SYM_LENGTH 100
21
22#define FAKE_IP_PERF_MAIN FAKE_MAP_PERF + FAKE_SYM_OFFSET1
23#define FAKE_IP_PERF_RUN_COMMAND FAKE_MAP_PERF + FAKE_SYM_OFFSET2
24#define FAKE_IP_PERF_CMD_RECORD FAKE_MAP_PERF + FAKE_SYM_OFFSET3
25#define FAKE_IP_BASH_MAIN FAKE_MAP_BASH + FAKE_SYM_OFFSET1
26#define FAKE_IP_BASH_XMALLOC FAKE_MAP_BASH + FAKE_SYM_OFFSET2
27#define FAKE_IP_BASH_XFREE FAKE_MAP_BASH + FAKE_SYM_OFFSET3
28#define FAKE_IP_LIBC_MALLOC FAKE_MAP_LIBC + FAKE_SYM_OFFSET1
29#define FAKE_IP_LIBC_FREE FAKE_MAP_LIBC + FAKE_SYM_OFFSET2
30#define FAKE_IP_LIBC_REALLOC FAKE_MAP_LIBC + FAKE_SYM_OFFSET3
31#define FAKE_IP_KERNEL_SCHEDULE FAKE_MAP_KERNEL + FAKE_SYM_OFFSET1
32#define FAKE_IP_KERNEL_PAGE_FAULT FAKE_MAP_KERNEL + FAKE_SYM_OFFSET2
33#define FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN FAKE_MAP_KERNEL + FAKE_SYM_OFFSET3
34
35/*
36 * The setup_fake_machine() provides a test environment which consists
37 * of 3 processes that have 3 mappings and in turn, have 3 symbols
38 * respectively. See below table:
39 *
40 * Command: Pid Shared Object Symbol
41 * ............. ............. ...................
42 * perf: 100 perf main
43 * perf: 100 perf run_command
44 * perf: 100 perf cmd_record
45 * perf: 100 libc malloc
46 * perf: 100 libc free
47 * perf: 100 libc realloc
48 * perf: 100 [kernel] schedule
49 * perf: 100 [kernel] page_fault
50 * perf: 100 [kernel] sys_perf_event_open
51 * perf: 200 perf main
52 * perf: 200 perf run_command
53 * perf: 200 perf cmd_record
54 * perf: 200 libc malloc
55 * perf: 200 libc free
56 * perf: 200 libc realloc
57 * perf: 200 [kernel] schedule
58 * perf: 200 [kernel] page_fault
59 * perf: 200 [kernel] sys_perf_event_open
60 * bash: 300 bash main
61 * bash: 300 bash xmalloc
62 * bash: 300 bash xfree
63 * bash: 300 libc malloc
64 * bash: 300 libc free
65 * bash: 300 libc realloc
66 * bash: 300 [kernel] schedule
67 * bash: 300 [kernel] page_fault
68 * bash: 300 [kernel] sys_perf_event_open
69 */
70struct machine *setup_fake_machine(struct machines *machines);
71
72void print_hists_in(struct hists *hists);
73void print_hists_out(struct hists *hists);
74
75#endif /* __PERF_TESTS__HISTS_COMMON_H__ */
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
new file mode 100644
index 000000000000..0ac240db2e24
--- /dev/null
+++ b/tools/perf/tests/hists_cumulate.c
@@ -0,0 +1,726 @@
1#include "perf.h"
2#include "util/debug.h"
3#include "util/symbol.h"
4#include "util/sort.h"
5#include "util/evsel.h"
6#include "util/evlist.h"
7#include "util/machine.h"
8#include "util/thread.h"
9#include "util/parse-events.h"
10#include "tests/tests.h"
11#include "tests/hists_common.h"
12
13struct sample {
14 u32 pid;
15 u64 ip;
16 struct thread *thread;
17 struct map *map;
18 struct symbol *sym;
19};
20
21/* For the numbers, see hists_common.c */
22static struct sample fake_samples[] = {
23 /* perf [kernel] schedule() */
24 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
25 /* perf [perf] main() */
26 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
27 /* perf [perf] cmd_record() */
28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
29 /* perf [libc] malloc() */
30 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
31 /* perf [libc] free() */
32 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
33 /* perf [perf] main() */
34 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
35 /* perf [kernel] page_fault() */
36 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
37 /* bash [bash] main() */
38 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
39 /* bash [bash] xmalloc() */
40 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
41 /* bash [kernel] page_fault() */
42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
43};
44
45/*
46 * Will be casted to struct ip_callchain which has all 64 bit entries
47 * of nr and ips[].
48 */
49static u64 fake_callchains[][10] = {
50 /* schedule => run_command => main */
51 { 3, FAKE_IP_KERNEL_SCHEDULE, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
52 /* main */
53 { 1, FAKE_IP_PERF_MAIN, },
54 /* cmd_record => run_command => main */
55 { 3, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
56 /* malloc => cmd_record => run_command => main */
57 { 4, FAKE_IP_LIBC_MALLOC, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
58 FAKE_IP_PERF_MAIN, },
59 /* free => cmd_record => run_command => main */
60 { 4, FAKE_IP_LIBC_FREE, FAKE_IP_PERF_CMD_RECORD, FAKE_IP_PERF_RUN_COMMAND,
61 FAKE_IP_PERF_MAIN, },
62 /* main */
63 { 1, FAKE_IP_PERF_MAIN, },
64 /* page_fault => sys_perf_event_open => run_command => main */
65 { 4, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN,
66 FAKE_IP_PERF_RUN_COMMAND, FAKE_IP_PERF_MAIN, },
67 /* main */
68 { 1, FAKE_IP_BASH_MAIN, },
69 /* xmalloc => malloc => xmalloc => malloc => xmalloc => main */
70 { 6, FAKE_IP_BASH_XMALLOC, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC,
71 FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_XMALLOC, FAKE_IP_BASH_MAIN, },
72 /* page_fault => malloc => main */
73 { 3, FAKE_IP_KERNEL_PAGE_FAULT, FAKE_IP_LIBC_MALLOC, FAKE_IP_BASH_MAIN, },
74};
75
76static int add_hist_entries(struct hists *hists, struct machine *machine)
77{
78 struct addr_location al;
79 struct perf_evsel *evsel = hists_to_evsel(hists);
80 struct perf_sample sample = { .period = 1000, };
81 size_t i;
82
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 = {
90 .hide_unresolved = false,
91 };
92
93 if (symbol_conf.cumulate_callchain)
94 iter.ops = &hist_iter_cumulative;
95 else
96 iter.ops = &hist_iter_normal;
97
98 sample.pid = fake_samples[i].pid;
99 sample.tid = fake_samples[i].pid;
100 sample.ip = fake_samples[i].ip;
101 sample.callchain = (struct ip_callchain *)fake_callchains[i];
102
103 if (perf_event__preprocess_sample(&event, machine, &al,
104 &sample) < 0)
105 goto out;
106
107 if (hist_entry_iter__add(&iter, &al, evsel, &sample,
108 PERF_MAX_STACK_DEPTH, NULL) < 0)
109 goto out;
110
111 fake_samples[i].thread = al.thread;
112 fake_samples[i].map = al.map;
113 fake_samples[i].sym = al.sym;
114 }
115
116 return TEST_OK;
117
118out:
119 pr_debug("Not enough memory for adding a hist entry\n");
120 return TEST_FAIL;
121}
122
123static void del_hist_entries(struct hists *hists)
124{
125 struct hist_entry *he;
126 struct rb_root *root_in;
127 struct rb_root *root_out;
128 struct rb_node *node;
129
130 if (sort__need_collapse)
131 root_in = &hists->entries_collapsed;
132 else
133 root_in = hists->entries_in;
134
135 root_out = &hists->entries;
136
137 while (!RB_EMPTY_ROOT(root_out)) {
138 node = rb_first(root_out);
139
140 he = rb_entry(node, struct hist_entry, rb_node);
141 rb_erase(node, root_out);
142 rb_erase(&he->rb_node_in, root_in);
143 hist_entry__free(he);
144 }
145}
146
147typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
148
149#define COMM(he) (thread__comm_str(he->thread))
150#define DSO(he) (he->ms.map->dso->short_name)
151#define SYM(he) (he->ms.sym->name)
152#define CPU(he) (he->cpu)
153#define PID(he) (he->thread->tid)
154#define DEPTH(he) (he->callchain->max_depth)
155#define CDSO(cl) (cl->ms.map->dso->short_name)
156#define CSYM(cl) (cl->ms.sym->name)
157
158struct result {
159 u64 children;
160 u64 self;
161 const char *comm;
162 const char *dso;
163 const char *sym;
164};
165
166struct callchain_result {
167 u64 nr;
168 struct {
169 const char *dso;
170 const char *sym;
171 } node[10];
172};
173
174static int do_test(struct hists *hists, struct result *expected, size_t nr_expected,
175 struct callchain_result *expected_callchain, size_t nr_callchain)
176{
177 char buf[32];
178 size_t i, c;
179 struct hist_entry *he;
180 struct rb_root *root;
181 struct rb_node *node;
182 struct callchain_node *cnode;
183 struct callchain_list *clist;
184
185 /*
186 * adding and deleting hist entries must be done outside of this
187 * function since TEST_ASSERT_VAL() returns in case of failure.
188 */
189 hists__collapse_resort(hists, NULL);
190 hists__output_resort(hists);
191
192 if (verbose > 2) {
193 pr_info("use callchain: %d, cumulate callchain: %d\n",
194 symbol_conf.use_callchain,
195 symbol_conf.cumulate_callchain);
196 print_hists_out(hists);
197 }
198
199 root = &hists->entries;
200 for (node = rb_first(root), i = 0;
201 node && (he = rb_entry(node, struct hist_entry, rb_node));
202 node = rb_next(node), i++) {
203 scnprintf(buf, sizeof(buf), "Invalid hist entry #%zd", i);
204
205 TEST_ASSERT_VAL("Incorrect number of hist entry",
206 i < nr_expected);
207 TEST_ASSERT_VAL(buf, he->stat.period == expected[i].self &&
208 !strcmp(COMM(he), expected[i].comm) &&
209 !strcmp(DSO(he), expected[i].dso) &&
210 !strcmp(SYM(he), expected[i].sym));
211
212 if (symbol_conf.cumulate_callchain)
213 TEST_ASSERT_VAL(buf, he->stat_acc->period == expected[i].children);
214
215 if (!symbol_conf.use_callchain)
216 continue;
217
218 /* check callchain entries */
219 root = &he->callchain->node.rb_root;
220 cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
221
222 c = 0;
223 list_for_each_entry(clist, &cnode->val, list) {
224 scnprintf(buf, sizeof(buf), "Invalid callchain entry #%zd/%zd", i, c);
225
226 TEST_ASSERT_VAL("Incorrect number of callchain entry",
227 c < expected_callchain[i].nr);
228 TEST_ASSERT_VAL(buf,
229 !strcmp(CDSO(clist), expected_callchain[i].node[c].dso) &&
230 !strcmp(CSYM(clist), expected_callchain[i].node[c].sym));
231 c++;
232 }
233 /* TODO: handle multiple child nodes properly */
234 TEST_ASSERT_VAL("Incorrect number of callchain entry",
235 c <= expected_callchain[i].nr);
236 }
237 TEST_ASSERT_VAL("Incorrect number of hist entry",
238 i == nr_expected);
239 TEST_ASSERT_VAL("Incorrect number of callchain entry",
240 !symbol_conf.use_callchain || nr_expected == nr_callchain);
241 return 0;
242}
243
244/* NO callchain + NO children */
245static int test1(struct perf_evsel *evsel, struct machine *machine)
246{
247 int err;
248 struct hists *hists = &evsel->hists;
249 /*
250 * expected output:
251 *
252 * Overhead Command Shared Object Symbol
253 * ======== ======= ============= ==============
254 * 20.00% perf perf [.] main
255 * 10.00% bash [kernel] [k] page_fault
256 * 10.00% bash bash [.] main
257 * 10.00% bash bash [.] xmalloc
258 * 10.00% perf [kernel] [k] page_fault
259 * 10.00% perf [kernel] [k] schedule
260 * 10.00% perf libc [.] free
261 * 10.00% perf libc [.] malloc
262 * 10.00% perf perf [.] cmd_record
263 */
264 struct result expected[] = {
265 { 0, 2000, "perf", "perf", "main" },
266 { 0, 1000, "bash", "[kernel]", "page_fault" },
267 { 0, 1000, "bash", "bash", "main" },
268 { 0, 1000, "bash", "bash", "xmalloc" },
269 { 0, 1000, "perf", "[kernel]", "page_fault" },
270 { 0, 1000, "perf", "[kernel]", "schedule" },
271 { 0, 1000, "perf", "libc", "free" },
272 { 0, 1000, "perf", "libc", "malloc" },
273 { 0, 1000, "perf", "perf", "cmd_record" },
274 };
275
276 symbol_conf.use_callchain = false;
277 symbol_conf.cumulate_callchain = false;
278
279 setup_sorting();
280 callchain_register_param(&callchain_param);
281
282 err = add_hist_entries(hists, machine);
283 if (err < 0)
284 goto out;
285
286 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
287
288out:
289 del_hist_entries(hists);
290 reset_output_field();
291 return err;
292}
293
294/* callcain + NO children */
295static int test2(struct perf_evsel *evsel, struct machine *machine)
296{
297 int err;
298 struct hists *hists = &evsel->hists;
299 /*
300 * expected output:
301 *
302 * Overhead Command Shared Object Symbol
303 * ======== ======= ============= ==============
304 * 20.00% perf perf [.] main
305 * |
306 * --- main
307 *
308 * 10.00% bash [kernel] [k] page_fault
309 * |
310 * --- page_fault
311 * malloc
312 * main
313 *
314 * 10.00% bash bash [.] main
315 * |
316 * --- main
317 *
318 * 10.00% bash bash [.] xmalloc
319 * |
320 * --- xmalloc
321 * malloc
322 * xmalloc <--- NOTE: there's a cycle
323 * malloc
324 * xmalloc
325 * main
326 *
327 * 10.00% perf [kernel] [k] page_fault
328 * |
329 * --- page_fault
330 * sys_perf_event_open
331 * run_command
332 * main
333 *
334 * 10.00% perf [kernel] [k] schedule
335 * |
336 * --- schedule
337 * run_command
338 * main
339 *
340 * 10.00% perf libc [.] free
341 * |
342 * --- free
343 * cmd_record
344 * run_command
345 * main
346 *
347 * 10.00% perf libc [.] malloc
348 * |
349 * --- malloc
350 * cmd_record
351 * run_command
352 * main
353 *
354 * 10.00% perf perf [.] cmd_record
355 * |
356 * --- cmd_record
357 * run_command
358 * main
359 *
360 */
361 struct result expected[] = {
362 { 0, 2000, "perf", "perf", "main" },
363 { 0, 1000, "bash", "[kernel]", "page_fault" },
364 { 0, 1000, "bash", "bash", "main" },
365 { 0, 1000, "bash", "bash", "xmalloc" },
366 { 0, 1000, "perf", "[kernel]", "page_fault" },
367 { 0, 1000, "perf", "[kernel]", "schedule" },
368 { 0, 1000, "perf", "libc", "free" },
369 { 0, 1000, "perf", "libc", "malloc" },
370 { 0, 1000, "perf", "perf", "cmd_record" },
371 };
372 struct callchain_result expected_callchain[] = {
373 {
374 1, { { "perf", "main" }, },
375 },
376 {
377 3, { { "[kernel]", "page_fault" },
378 { "libc", "malloc" },
379 { "bash", "main" }, },
380 },
381 {
382 1, { { "bash", "main" }, },
383 },
384 {
385 6, { { "bash", "xmalloc" },
386 { "libc", "malloc" },
387 { "bash", "xmalloc" },
388 { "libc", "malloc" },
389 { "bash", "xmalloc" },
390 { "bash", "main" }, },
391 },
392 {
393 4, { { "[kernel]", "page_fault" },
394 { "[kernel]", "sys_perf_event_open" },
395 { "perf", "run_command" },
396 { "perf", "main" }, },
397 },
398 {
399 3, { { "[kernel]", "schedule" },
400 { "perf", "run_command" },
401 { "perf", "main" }, },
402 },
403 {
404 4, { { "libc", "free" },
405 { "perf", "cmd_record" },
406 { "perf", "run_command" },
407 { "perf", "main" }, },
408 },
409 {
410 4, { { "libc", "malloc" },
411 { "perf", "cmd_record" },
412 { "perf", "run_command" },
413 { "perf", "main" }, },
414 },
415 {
416 3, { { "perf", "cmd_record" },
417 { "perf", "run_command" },
418 { "perf", "main" }, },
419 },
420 };
421
422 symbol_conf.use_callchain = true;
423 symbol_conf.cumulate_callchain = false;
424
425 setup_sorting();
426 callchain_register_param(&callchain_param);
427
428 err = add_hist_entries(hists, machine);
429 if (err < 0)
430 goto out;
431
432 err = do_test(hists, expected, ARRAY_SIZE(expected),
433 expected_callchain, ARRAY_SIZE(expected_callchain));
434
435out:
436 del_hist_entries(hists);
437 reset_output_field();
438 return err;
439}
440
441/* NO callchain + children */
442static int test3(struct perf_evsel *evsel, struct machine *machine)
443{
444 int err;
445 struct hists *hists = &evsel->hists;
446 /*
447 * expected output:
448 *
449 * Children Self Command Shared Object Symbol
450 * ======== ======== ======= ============= =======================
451 * 70.00% 20.00% perf perf [.] main
452 * 50.00% 0.00% perf perf [.] run_command
453 * 30.00% 10.00% bash bash [.] main
454 * 30.00% 10.00% perf perf [.] cmd_record
455 * 20.00% 0.00% bash libc [.] malloc
456 * 10.00% 10.00% bash [kernel] [k] page_fault
457 * 10.00% 10.00% perf [kernel] [k] schedule
458 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open
459 * 10.00% 10.00% perf [kernel] [k] page_fault
460 * 10.00% 10.00% perf libc [.] free
461 * 10.00% 10.00% perf libc [.] malloc
462 * 10.00% 10.00% bash bash [.] xmalloc
463 */
464 struct result expected[] = {
465 { 7000, 2000, "perf", "perf", "main" },
466 { 5000, 0, "perf", "perf", "run_command" },
467 { 3000, 1000, "bash", "bash", "main" },
468 { 3000, 1000, "perf", "perf", "cmd_record" },
469 { 2000, 0, "bash", "libc", "malloc" },
470 { 1000, 1000, "bash", "[kernel]", "page_fault" },
471 { 1000, 1000, "perf", "[kernel]", "schedule" },
472 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
473 { 1000, 1000, "perf", "[kernel]", "page_fault" },
474 { 1000, 1000, "perf", "libc", "free" },
475 { 1000, 1000, "perf", "libc", "malloc" },
476 { 1000, 1000, "bash", "bash", "xmalloc" },
477 };
478
479 symbol_conf.use_callchain = false;
480 symbol_conf.cumulate_callchain = true;
481
482 setup_sorting();
483 callchain_register_param(&callchain_param);
484
485 err = add_hist_entries(hists, machine);
486 if (err < 0)
487 goto out;
488
489 err = do_test(hists, expected, ARRAY_SIZE(expected), NULL, 0);
490
491out:
492 del_hist_entries(hists);
493 reset_output_field();
494 return err;
495}
496
497/* callchain + children */
498static int test4(struct perf_evsel *evsel, struct machine *machine)
499{
500 int err;
501 struct hists *hists = &evsel->hists;
502 /*
503 * expected output:
504 *
505 * Children Self Command Shared Object Symbol
506 * ======== ======== ======= ============= =======================
507 * 70.00% 20.00% perf perf [.] main
508 * |
509 * --- main
510 *
511 * 50.00% 0.00% perf perf [.] run_command
512 * |
513 * --- run_command
514 * main
515 *
516 * 30.00% 10.00% bash bash [.] main
517 * |
518 * --- main
519 *
520 * 30.00% 10.00% perf perf [.] cmd_record
521 * |
522 * --- cmd_record
523 * run_command
524 * main
525 *
526 * 20.00% 0.00% bash libc [.] malloc
527 * |
528 * --- malloc
529 * |
530 * |--50.00%-- xmalloc
531 * | main
532 * --50.00%-- main
533 *
534 * 10.00% 10.00% bash [kernel] [k] page_fault
535 * |
536 * --- page_fault
537 * malloc
538 * main
539 *
540 * 10.00% 10.00% perf [kernel] [k] schedule
541 * |
542 * --- schedule
543 * run_command
544 * main
545 *
546 * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open
547 * |
548 * --- sys_perf_event_open
549 * run_command
550 * main
551 *
552 * 10.00% 10.00% perf [kernel] [k] page_fault
553 * |
554 * --- page_fault
555 * sys_perf_event_open
556 * run_command
557 * main
558 *
559 * 10.00% 10.00% perf libc [.] free
560 * |
561 * --- free
562 * cmd_record
563 * run_command
564 * main
565 *
566 * 10.00% 10.00% perf libc [.] malloc
567 * |
568 * --- malloc
569 * cmd_record
570 * run_command
571 * main
572 *
573 * 10.00% 10.00% bash bash [.] xmalloc
574 * |
575 * --- xmalloc
576 * malloc
577 * xmalloc <--- NOTE: there's a cycle
578 * malloc
579 * xmalloc
580 * main
581 *
582 */
583 struct result expected[] = {
584 { 7000, 2000, "perf", "perf", "main" },
585 { 5000, 0, "perf", "perf", "run_command" },
586 { 3000, 1000, "bash", "bash", "main" },
587 { 3000, 1000, "perf", "perf", "cmd_record" },
588 { 2000, 0, "bash", "libc", "malloc" },
589 { 1000, 1000, "bash", "[kernel]", "page_fault" },
590 { 1000, 1000, "perf", "[kernel]", "schedule" },
591 { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" },
592 { 1000, 1000, "perf", "[kernel]", "page_fault" },
593 { 1000, 1000, "perf", "libc", "free" },
594 { 1000, 1000, "perf", "libc", "malloc" },
595 { 1000, 1000, "bash", "bash", "xmalloc" },
596 };
597 struct callchain_result expected_callchain[] = {
598 {
599 1, { { "perf", "main" }, },
600 },
601 {
602 2, { { "perf", "run_command" },
603 { "perf", "main" }, },
604 },
605 {
606 1, { { "bash", "main" }, },
607 },
608 {
609 3, { { "perf", "cmd_record" },
610 { "perf", "run_command" },
611 { "perf", "main" }, },
612 },
613 {
614 4, { { "libc", "malloc" },
615 { "bash", "xmalloc" },
616 { "bash", "main" },
617 { "bash", "main" }, },
618 },
619 {
620 3, { { "[kernel]", "page_fault" },
621 { "libc", "malloc" },
622 { "bash", "main" }, },
623 },
624 {
625 3, { { "[kernel]", "schedule" },
626 { "perf", "run_command" },
627 { "perf", "main" }, },
628 },
629 {
630 3, { { "[kernel]", "sys_perf_event_open" },
631 { "perf", "run_command" },
632 { "perf", "main" }, },
633 },
634 {
635 4, { { "[kernel]", "page_fault" },
636 { "[kernel]", "sys_perf_event_open" },
637 { "perf", "run_command" },
638 { "perf", "main" }, },
639 },
640 {
641 4, { { "libc", "free" },
642 { "perf", "cmd_record" },
643 { "perf", "run_command" },
644 { "perf", "main" }, },
645 },
646 {
647 4, { { "libc", "malloc" },
648 { "perf", "cmd_record" },
649 { "perf", "run_command" },
650 { "perf", "main" }, },
651 },
652 {
653 6, { { "bash", "xmalloc" },
654 { "libc", "malloc" },
655 { "bash", "xmalloc" },
656 { "libc", "malloc" },
657 { "bash", "xmalloc" },
658 { "bash", "main" }, },
659 },
660 };
661
662 symbol_conf.use_callchain = true;
663 symbol_conf.cumulate_callchain = true;
664
665 setup_sorting();
666 callchain_register_param(&callchain_param);
667
668 err = add_hist_entries(hists, machine);
669 if (err < 0)
670 goto out;
671
672 err = do_test(hists, expected, ARRAY_SIZE(expected),
673 expected_callchain, ARRAY_SIZE(expected_callchain));
674
675out:
676 del_hist_entries(hists);
677 reset_output_field();
678 return err;
679}
680
681int test__hists_cumulate(void)
682{
683 int err = TEST_FAIL;
684 struct machines machines;
685 struct machine *machine;
686 struct perf_evsel *evsel;
687 struct perf_evlist *evlist = perf_evlist__new();
688 size_t i;
689 test_fn_t testcases[] = {
690 test1,
691 test2,
692 test3,
693 test4,
694 };
695
696 TEST_ASSERT_VAL("No memory", evlist);
697
698 err = parse_events(evlist, "cpu-clock");
699 if (err)
700 goto out;
701
702 machines__init(&machines);
703
704 /* setup threads/dso/map/symbols also */
705 machine = setup_fake_machine(&machines);
706 if (!machine)
707 goto out;
708
709 if (verbose > 1)
710 machine__fprintf(machine, stderr);
711
712 evsel = perf_evlist__first(evlist);
713
714 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
715 err = testcases[i](evsel, machine);
716 if (err < 0)
717 break;
718 }
719
720out:
721 /* tear down everything */
722 perf_evlist__delete(evlist);
723 machines__exit(&machines);
724
725 return err;
726}
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
new file mode 100644
index 000000000000..821f581fd930
--- /dev/null
+++ b/tools/perf/tests/hists_filter.c
@@ -0,0 +1,289 @@
1#include "perf.h"
2#include "util/debug.h"
3#include "util/symbol.h"
4#include "util/sort.h"
5#include "util/evsel.h"
6#include "util/evlist.h"
7#include "util/machine.h"
8#include "util/thread.h"
9#include "util/parse-events.h"
10#include "tests/tests.h"
11#include "tests/hists_common.h"
12
13struct sample {
14 u32 pid;
15 u64 ip;
16 struct thread *thread;
17 struct map *map;
18 struct symbol *sym;
19};
20
21/* For the numbers, see hists_common.c */
22static struct sample fake_samples[] = {
23 /* perf [kernel] schedule() */
24 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
25 /* perf [perf] main() */
26 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
27 /* perf [libc] malloc() */
28 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
29 /* perf [perf] main() */
30 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, }, /* will be merged */
31 /* perf [perf] cmd_record() */
32 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
33 /* perf [kernel] page_fault() */
34 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
35 /* bash [bash] main() */
36 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
37 /* bash [bash] xmalloc() */
38 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
39 /* bash [libc] malloc() */
40 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, },
41 /* bash [kernel] page_fault() */
42 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
43};
44
45static int add_hist_entries(struct perf_evlist *evlist,
46 struct machine *machine __maybe_unused)
47{
48 struct perf_evsel *evsel;
49 struct addr_location al;
50 struct perf_sample sample = { .period = 100, };
51 size_t i;
52
53 /*
54 * each evsel will have 10 samples but the 4th sample
55 * (perf [perf] main) will be collapsed to an existing entry
56 * so total 9 entries will be in the tree.
57 */
58 evlist__for_each(evlist, evsel) {
59 for (i = 0; i < ARRAY_SIZE(fake_samples); i++) {
60 const union perf_event event = {
61 .header = {
62 .misc = PERF_RECORD_MISC_USER,
63 },
64 };
65 struct hist_entry_iter iter = {
66 .ops = &hist_iter_normal,
67 .hide_unresolved = false,
68 };
69
70 /* make sure it has no filter at first */
71 evsel->hists.thread_filter = NULL;
72 evsel->hists.dso_filter = NULL;
73 evsel->hists.symbol_filter_str = NULL;
74
75 sample.pid = fake_samples[i].pid;
76 sample.tid = fake_samples[i].pid;
77 sample.ip = fake_samples[i].ip;
78
79 if (perf_event__preprocess_sample(&event, machine, &al,
80 &sample) < 0)
81 goto out;
82
83 if (hist_entry_iter__add(&iter, &al, evsel, &sample,
84 PERF_MAX_STACK_DEPTH, NULL) < 0)
85 goto out;
86
87 fake_samples[i].thread = al.thread;
88 fake_samples[i].map = al.map;
89 fake_samples[i].sym = al.sym;
90 }
91 }
92
93 return 0;
94
95out:
96 pr_debug("Not enough memory for adding a hist entry\n");
97 return TEST_FAIL;
98}
99
100int test__hists_filter(void)
101{
102 int err = TEST_FAIL;
103 struct machines machines;
104 struct machine *machine;
105 struct perf_evsel *evsel;
106 struct perf_evlist *evlist = perf_evlist__new();
107
108 TEST_ASSERT_VAL("No memory", evlist);
109
110 err = parse_events(evlist, "cpu-clock");
111 if (err)
112 goto out;
113 err = parse_events(evlist, "task-clock");
114 if (err)
115 goto out;
116
117 /* default sort order (comm,dso,sym) will be used */
118 if (setup_sorting() < 0)
119 goto out;
120
121 machines__init(&machines);
122
123 /* setup threads/dso/map/symbols also */
124 machine = setup_fake_machine(&machines);
125 if (!machine)
126 goto out;
127
128 if (verbose > 1)
129 machine__fprintf(machine, stderr);
130
131 /* process sample events */
132 err = add_hist_entries(evlist, machine);
133 if (err < 0)
134 goto out;
135
136 evlist__for_each(evlist, evsel) {
137 struct hists *hists = &evsel->hists;
138
139 hists__collapse_resort(hists, NULL);
140 hists__output_resort(hists);
141
142 if (verbose > 2) {
143 pr_info("Normal histogram\n");
144 print_hists_out(hists);
145 }
146
147 TEST_ASSERT_VAL("Invalid nr samples",
148 hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
149 TEST_ASSERT_VAL("Invalid nr hist entries",
150 hists->nr_entries == 9);
151 TEST_ASSERT_VAL("Invalid total period",
152 hists->stats.total_period == 1000);
153 TEST_ASSERT_VAL("Unmatched nr samples",
154 hists->stats.nr_events[PERF_RECORD_SAMPLE] ==
155 hists->stats.nr_non_filtered_samples);
156 TEST_ASSERT_VAL("Unmatched nr hist entries",
157 hists->nr_entries == hists->nr_non_filtered_entries);
158 TEST_ASSERT_VAL("Unmatched total period",
159 hists->stats.total_period ==
160 hists->stats.total_non_filtered_period);
161
162 /* now applying thread filter for 'bash' */
163 evsel->hists.thread_filter = fake_samples[9].thread;
164 hists__filter_by_thread(hists);
165
166 if (verbose > 2) {
167 pr_info("Histogram for thread filter\n");
168 print_hists_out(hists);
169 }
170
171 /* normal stats should be invariant */
172 TEST_ASSERT_VAL("Invalid nr samples",
173 hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
174 TEST_ASSERT_VAL("Invalid nr hist entries",
175 hists->nr_entries == 9);
176 TEST_ASSERT_VAL("Invalid total period",
177 hists->stats.total_period == 1000);
178
179 /* but filter stats are changed */
180 TEST_ASSERT_VAL("Unmatched nr samples for thread filter",
181 hists->stats.nr_non_filtered_samples == 4);
182 TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter",
183 hists->nr_non_filtered_entries == 4);
184 TEST_ASSERT_VAL("Unmatched total period for thread filter",
185 hists->stats.total_non_filtered_period == 400);
186
187 /* remove thread filter first */
188 evsel->hists.thread_filter = NULL;
189 hists__filter_by_thread(hists);
190
191 /* now applying dso filter for 'kernel' */
192 evsel->hists.dso_filter = fake_samples[0].map->dso;
193 hists__filter_by_dso(hists);
194
195 if (verbose > 2) {
196 pr_info("Histogram for dso filter\n");
197 print_hists_out(hists);
198 }
199
200 /* normal stats should be invariant */
201 TEST_ASSERT_VAL("Invalid nr samples",
202 hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
203 TEST_ASSERT_VAL("Invalid nr hist entries",
204 hists->nr_entries == 9);
205 TEST_ASSERT_VAL("Invalid total period",
206 hists->stats.total_period == 1000);
207
208 /* but filter stats are changed */
209 TEST_ASSERT_VAL("Unmatched nr samples for dso filter",
210 hists->stats.nr_non_filtered_samples == 3);
211 TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter",
212 hists->nr_non_filtered_entries == 3);
213 TEST_ASSERT_VAL("Unmatched total period for dso filter",
214 hists->stats.total_non_filtered_period == 300);
215
216 /* remove dso filter first */
217 evsel->hists.dso_filter = NULL;
218 hists__filter_by_dso(hists);
219
220 /*
221 * now applying symbol filter for 'main'. Also note that
222 * there's 3 samples that have 'main' symbol but the 4th
223 * entry of fake_samples was collapsed already so it won't
224 * be counted as a separate entry but the sample count and
225 * total period will be remained.
226 */
227 evsel->hists.symbol_filter_str = "main";
228 hists__filter_by_symbol(hists);
229
230 if (verbose > 2) {
231 pr_info("Histogram for symbol filter\n");
232 print_hists_out(hists);
233 }
234
235 /* normal stats should be invariant */
236 TEST_ASSERT_VAL("Invalid nr samples",
237 hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
238 TEST_ASSERT_VAL("Invalid nr hist entries",
239 hists->nr_entries == 9);
240 TEST_ASSERT_VAL("Invalid total period",
241 hists->stats.total_period == 1000);
242
243 /* but filter stats are changed */
244 TEST_ASSERT_VAL("Unmatched nr samples for symbol filter",
245 hists->stats.nr_non_filtered_samples == 3);
246 TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter",
247 hists->nr_non_filtered_entries == 2);
248 TEST_ASSERT_VAL("Unmatched total period for symbol filter",
249 hists->stats.total_non_filtered_period == 300);
250
251 /* now applying all filters at once. */
252 evsel->hists.thread_filter = fake_samples[1].thread;
253 evsel->hists.dso_filter = fake_samples[1].map->dso;
254 hists__filter_by_thread(hists);
255 hists__filter_by_dso(hists);
256
257 if (verbose > 2) {
258 pr_info("Histogram for all filters\n");
259 print_hists_out(hists);
260 }
261
262 /* normal stats should be invariant */
263 TEST_ASSERT_VAL("Invalid nr samples",
264 hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10);
265 TEST_ASSERT_VAL("Invalid nr hist entries",
266 hists->nr_entries == 9);
267 TEST_ASSERT_VAL("Invalid total period",
268 hists->stats.total_period == 1000);
269
270 /* but filter stats are changed */
271 TEST_ASSERT_VAL("Unmatched nr samples for all filter",
272 hists->stats.nr_non_filtered_samples == 2);
273 TEST_ASSERT_VAL("Unmatched nr hist entries for all filter",
274 hists->nr_non_filtered_entries == 1);
275 TEST_ASSERT_VAL("Unmatched total period for all filter",
276 hists->stats.total_non_filtered_period == 200);
277 }
278
279
280 err = TEST_OK;
281
282out:
283 /* tear down everything */
284 perf_evlist__delete(evlist);
285 reset_output_field();
286 machines__exit(&machines);
287
288 return err;
289}
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index 7ccbc7b6ae77..d4b34b0f50a2 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -8,145 +8,7 @@
8#include "machine.h" 8#include "machine.h"
9#include "thread.h" 9#include "thread.h"
10#include "parse-events.h" 10#include "parse-events.h"
11 11#include "hists_common.h"
12static struct {
13 u32 pid;
14 const char *comm;
15} fake_threads[] = {
16 { 100, "perf" },
17 { 200, "perf" },
18 { 300, "bash" },
19};
20
21static struct {
22 u32 pid;
23 u64 start;
24 const char *filename;
25} fake_mmap_info[] = {
26 { 100, 0x40000, "perf" },
27 { 100, 0x50000, "libc" },
28 { 100, 0xf0000, "[kernel]" },
29 { 200, 0x40000, "perf" },
30 { 200, 0x50000, "libc" },
31 { 200, 0xf0000, "[kernel]" },
32 { 300, 0x40000, "bash" },
33 { 300, 0x50000, "libc" },
34 { 300, 0xf0000, "[kernel]" },
35};
36
37struct fake_sym {
38 u64 start;
39 u64 length;
40 const char *name;
41};
42
43static struct fake_sym perf_syms[] = {
44 { 700, 100, "main" },
45 { 800, 100, "run_command" },
46 { 900, 100, "cmd_record" },
47};
48
49static struct fake_sym bash_syms[] = {
50 { 700, 100, "main" },
51 { 800, 100, "xmalloc" },
52 { 900, 100, "xfree" },
53};
54
55static struct fake_sym libc_syms[] = {
56 { 700, 100, "malloc" },
57 { 800, 100, "free" },
58 { 900, 100, "realloc" },
59};
60
61static struct fake_sym kernel_syms[] = {
62 { 700, 100, "schedule" },
63 { 800, 100, "page_fault" },
64 { 900, 100, "sys_perf_event_open" },
65};
66
67static struct {
68 const char *dso_name;
69 struct fake_sym *syms;
70 size_t nr_syms;
71} fake_symbols[] = {
72 { "perf", perf_syms, ARRAY_SIZE(perf_syms) },
73 { "bash", bash_syms, ARRAY_SIZE(bash_syms) },
74 { "libc", libc_syms, ARRAY_SIZE(libc_syms) },
75 { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) },
76};
77
78static struct machine *setup_fake_machine(struct machines *machines)
79{
80 struct machine *machine = machines__find(machines, HOST_KERNEL_ID);
81 size_t i;
82
83 if (machine == NULL) {
84 pr_debug("Not enough memory for machine setup\n");
85 return NULL;
86 }
87
88 for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
89 struct thread *thread;
90
91 thread = machine__findnew_thread(machine, fake_threads[i].pid,
92 fake_threads[i].pid);
93 if (thread == NULL)
94 goto out;
95
96 thread__set_comm(thread, fake_threads[i].comm, 0);
97 }
98
99 for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
100 union perf_event fake_mmap_event = {
101 .mmap = {
102 .header = { .misc = PERF_RECORD_MISC_USER, },
103 .pid = fake_mmap_info[i].pid,
104 .tid = fake_mmap_info[i].pid,
105 .start = fake_mmap_info[i].start,
106 .len = 0x1000ULL,
107 .pgoff = 0ULL,
108 },
109 };
110
111 strcpy(fake_mmap_event.mmap.filename,
112 fake_mmap_info[i].filename);
113
114 machine__process_mmap_event(machine, &fake_mmap_event, NULL);
115 }
116
117 for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
118 size_t k;
119 struct dso *dso;
120
121 dso = __dsos__findnew(&machine->user_dsos,
122 fake_symbols[i].dso_name);
123 if (dso == NULL)
124 goto out;
125
126 /* emulate dso__load() */
127 dso__set_loaded(dso, MAP__FUNCTION);
128
129 for (k = 0; k < fake_symbols[i].nr_syms; k++) {
130 struct symbol *sym;
131 struct fake_sym *fsym = &fake_symbols[i].syms[k];
132
133 sym = symbol__new(fsym->start, fsym->length,
134 STB_GLOBAL, fsym->name);
135 if (sym == NULL)
136 goto out;
137
138 symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
139 }
140 }
141
142 return machine;
143
144out:
145 pr_debug("Not enough memory for machine setup\n");
146 machine__delete_threads(machine);
147 machine__delete(machine);
148 return NULL;
149}
150 12
151struct sample { 13struct sample {
152 u32 pid; 14 u32 pid;
@@ -156,43 +18,44 @@ struct sample {
156 struct symbol *sym; 18 struct symbol *sym;
157}; 19};
158 20
21/* For the numbers, see hists_common.c */
159static struct sample fake_common_samples[] = { 22static struct sample fake_common_samples[] = {
160 /* perf [kernel] schedule() */ 23 /* perf [kernel] schedule() */
161 { .pid = 100, .ip = 0xf0000 + 700, }, 24 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
162 /* perf [perf] main() */ 25 /* perf [perf] main() */
163 { .pid = 200, .ip = 0x40000 + 700, }, 26 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
164 /* perf [perf] cmd_record() */ 27 /* perf [perf] cmd_record() */
165 { .pid = 200, .ip = 0x40000 + 900, }, 28 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
166 /* bash [bash] xmalloc() */ 29 /* bash [bash] xmalloc() */
167 { .pid = 300, .ip = 0x40000 + 800, }, 30 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
168 /* bash [libc] malloc() */ 31 /* bash [libc] malloc() */
169 { .pid = 300, .ip = 0x50000 + 700, }, 32 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, },
170}; 33};
171 34
172static struct sample fake_samples[][5] = { 35static struct sample fake_samples[][5] = {
173 { 36 {
174 /* perf [perf] run_command() */ 37 /* perf [perf] run_command() */
175 { .pid = 100, .ip = 0x40000 + 800, }, 38 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_RUN_COMMAND, },
176 /* perf [libc] malloc() */ 39 /* perf [libc] malloc() */
177 { .pid = 100, .ip = 0x50000 + 700, }, 40 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
178 /* perf [kernel] page_fault() */ 41 /* perf [kernel] page_fault() */
179 { .pid = 100, .ip = 0xf0000 + 800, }, 42 { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
180 /* perf [kernel] sys_perf_event_open() */ 43 /* perf [kernel] sys_perf_event_open() */
181 { .pid = 200, .ip = 0xf0000 + 900, }, 44 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN, },
182 /* bash [libc] free() */ 45 /* bash [libc] free() */
183 { .pid = 300, .ip = 0x50000 + 800, }, 46 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_FREE, },
184 }, 47 },
185 { 48 {
186 /* perf [libc] free() */ 49 /* perf [libc] free() */
187 { .pid = 200, .ip = 0x50000 + 800, }, 50 { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, },
188 /* bash [libc] malloc() */ 51 /* bash [libc] malloc() */
189 { .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */ 52 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */
190 /* bash [bash] xfee() */ 53 /* bash [bash] xfee() */
191 { .pid = 300, .ip = 0x40000 + 900, }, 54 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XFREE, },
192 /* bash [libc] realloc() */ 55 /* bash [libc] realloc() */
193 { .pid = 300, .ip = 0x50000 + 900, }, 56 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_REALLOC, },
194 /* bash [kernel] page_fault() */ 57 /* bash [kernel] page_fault() */
195 { .pid = 300, .ip = 0xf0000 + 800, }, 58 { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
196 }, 59 },
197}; 60};
198 61
@@ -201,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
201 struct perf_evsel *evsel; 64 struct perf_evsel *evsel;
202 struct addr_location al; 65 struct addr_location al;
203 struct hist_entry *he; 66 struct hist_entry *he;
204 struct perf_sample sample = { .cpu = 0, }; 67 struct perf_sample sample = { .period = 1, };
205 size_t i = 0, k; 68 size_t i = 0, k;
206 69
207 /* 70 /*
@@ -218,13 +81,14 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
218 }; 81 };
219 82
220 sample.pid = fake_common_samples[k].pid; 83 sample.pid = fake_common_samples[k].pid;
84 sample.tid = fake_common_samples[k].pid;
221 sample.ip = fake_common_samples[k].ip; 85 sample.ip = fake_common_samples[k].ip;
222 if (perf_event__preprocess_sample(&event, machine, &al, 86 if (perf_event__preprocess_sample(&event, machine, &al,
223 &sample) < 0) 87 &sample) < 0)
224 goto out; 88 goto out;
225 89
226 he = __hists__add_entry(&evsel->hists, &al, NULL, 90 he = __hists__add_entry(&evsel->hists, &al, NULL,
227 NULL, NULL, 1, 1, 0); 91 NULL, NULL, 1, 1, 0, true);
228 if (he == NULL) 92 if (he == NULL)
229 goto out; 93 goto out;
230 94
@@ -241,13 +105,14 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
241 }; 105 };
242 106
243 sample.pid = fake_samples[i][k].pid; 107 sample.pid = fake_samples[i][k].pid;
108 sample.tid = fake_samples[i][k].pid;
244 sample.ip = fake_samples[i][k].ip; 109 sample.ip = fake_samples[i][k].ip;
245 if (perf_event__preprocess_sample(&event, machine, &al, 110 if (perf_event__preprocess_sample(&event, machine, &al,
246 &sample) < 0) 111 &sample) < 0)
247 goto out; 112 goto out;
248 113
249 he = __hists__add_entry(&evsel->hists, &al, NULL, 114 he = __hists__add_entry(&evsel->hists, &al, NULL,
250 NULL, NULL, 1, 1, 0); 115 NULL, NULL, 1, 1, 0, true);
251 if (he == NULL) 116 if (he == NULL)
252 goto out; 117 goto out;
253 118
@@ -403,33 +268,6 @@ static int validate_link(struct hists *leader, struct hists *other)
403 return __validate_link(leader, 0) || __validate_link(other, 1); 268 return __validate_link(leader, 0) || __validate_link(other, 1);
404} 269}
405 270
406static void print_hists(struct hists *hists)
407{
408 int i = 0;
409 struct rb_root *root;
410 struct rb_node *node;
411
412 if (sort__need_collapse)
413 root = &hists->entries_collapsed;
414 else
415 root = hists->entries_in;
416
417 pr_info("----- %s --------\n", __func__);
418 node = rb_first(root);
419 while (node) {
420 struct hist_entry *he;
421
422 he = rb_entry(node, struct hist_entry, rb_node_in);
423
424 pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
425 i, thread__comm_str(he->thread), he->ms.map->dso->short_name,
426 he->ms.sym->name, he->stat.period);
427
428 i++;
429 node = rb_next(node);
430 }
431}
432
433int test__hists_link(void) 271int test__hists_link(void)
434{ 272{
435 int err = -1; 273 int err = -1;
@@ -471,7 +309,7 @@ int test__hists_link(void)
471 hists__collapse_resort(&evsel->hists, NULL); 309 hists__collapse_resort(&evsel->hists, NULL);
472 310
473 if (verbose > 2) 311 if (verbose > 2)
474 print_hists(&evsel->hists); 312 print_hists_in(&evsel->hists);
475 } 313 }
476 314
477 first = perf_evlist__first(evlist); 315 first = perf_evlist__first(evlist);
@@ -494,6 +332,7 @@ int test__hists_link(void)
494out: 332out:
495 /* tear down everything */ 333 /* tear down everything */
496 perf_evlist__delete(evlist); 334 perf_evlist__delete(evlist);
335 reset_output_field();
497 machines__exit(&machines); 336 machines__exit(&machines);
498 337
499 return err; 338 return err;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
new file mode 100644
index 000000000000..e3bbd6c54c1b
--- /dev/null
+++ b/tools/perf/tests/hists_output.c
@@ -0,0 +1,621 @@
1#include "perf.h"
2#include "util/debug.h"
3#include "util/symbol.h"
4#include "util/sort.h"
5#include "util/evsel.h"
6#include "util/evlist.h"
7#include "util/machine.h"
8#include "util/thread.h"
9#include "util/parse-events.h"
10#include "tests/tests.h"
11#include "tests/hists_common.h"
12
13struct sample {
14 u32 cpu;
15 u32 pid;
16 u64 ip;
17 struct thread *thread;
18 struct map *map;
19 struct symbol *sym;
20};
21
22/* For the numbers, see hists_common.c */
23static struct sample fake_samples[] = {
24 /* perf [kernel] schedule() */
25 { .cpu = 0, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_SCHEDULE, },
26 /* perf [perf] main() */
27 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_MAIN, },
28 /* perf [perf] cmd_record() */
29 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_PERF_CMD_RECORD, },
30 /* perf [libc] malloc() */
31 { .cpu = 1, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
32 /* perf [libc] free() */
33 { .cpu = 2, .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_FREE, },
34 /* perf [perf] main() */
35 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
36 /* perf [kernel] page_fault() */
37 { .cpu = 2, .pid = FAKE_PID_PERF2, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
38 /* bash [bash] main() */
39 { .cpu = 3, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_MAIN, },
40 /* bash [bash] xmalloc() */
41 { .cpu = 0, .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
42 /* bash [kernel] page_fault() */
43 { .cpu = 1, .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
44};
45
46static int add_hist_entries(struct hists *hists, struct machine *machine)
47{
48 struct addr_location al;
49 struct perf_evsel *evsel = hists_to_evsel(hists);
50 struct perf_sample sample = { .period = 100, };
51 size_t i;
52
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 = {
60 .ops = &hist_iter_normal,
61 .hide_unresolved = false,
62 };
63
64 sample.cpu = fake_samples[i].cpu;
65 sample.pid = fake_samples[i].pid;
66 sample.tid = fake_samples[i].pid;
67 sample.ip = fake_samples[i].ip;
68
69 if (perf_event__preprocess_sample(&event, machine, &al,
70 &sample) < 0)
71 goto out;
72
73 if (hist_entry_iter__add(&iter, &al, evsel, &sample,
74 PERF_MAX_STACK_DEPTH, NULL) < 0)
75 goto out;
76
77 fake_samples[i].thread = al.thread;
78 fake_samples[i].map = al.map;
79 fake_samples[i].sym = al.sym;
80 }
81
82 return TEST_OK;
83
84out:
85 pr_debug("Not enough memory for adding a hist entry\n");
86 return TEST_FAIL;
87}
88
89static void del_hist_entries(struct hists *hists)
90{
91 struct hist_entry *he;
92 struct rb_root *root_in;
93 struct rb_root *root_out;
94 struct rb_node *node;
95
96 if (sort__need_collapse)
97 root_in = &hists->entries_collapsed;
98 else
99 root_in = hists->entries_in;
100
101 root_out = &hists->entries;
102
103 while (!RB_EMPTY_ROOT(root_out)) {
104 node = rb_first(root_out);
105
106 he = rb_entry(node, struct hist_entry, rb_node);
107 rb_erase(node, root_out);
108 rb_erase(&he->rb_node_in, root_in);
109 hist_entry__free(he);
110 }
111}
112
113typedef int (*test_fn_t)(struct perf_evsel *, struct machine *);
114
115#define COMM(he) (thread__comm_str(he->thread))
116#define DSO(he) (he->ms.map->dso->short_name)
117#define SYM(he) (he->ms.sym->name)
118#define CPU(he) (he->cpu)
119#define PID(he) (he->thread->tid)
120
121/* default sort keys (no field) */
122static int test1(struct perf_evsel *evsel, struct machine *machine)
123{
124 int err;
125 struct hists *hists = &evsel->hists;
126 struct hist_entry *he;
127 struct rb_root *root;
128 struct rb_node *node;
129
130 field_order = NULL;
131 sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */
132
133 setup_sorting();
134
135 /*
136 * expected output:
137 *
138 * Overhead Command Shared Object Symbol
139 * ======== ======= ============= ==============
140 * 20.00% perf perf [.] main
141 * 10.00% bash [kernel] [k] page_fault
142 * 10.00% bash bash [.] main
143 * 10.00% bash bash [.] xmalloc
144 * 10.00% perf [kernel] [k] page_fault
145 * 10.00% perf [kernel] [k] schedule
146 * 10.00% perf libc [.] free
147 * 10.00% perf libc [.] malloc
148 * 10.00% perf perf [.] cmd_record
149 */
150 err = add_hist_entries(hists, machine);
151 if (err < 0)
152 goto out;
153
154 hists__collapse_resort(hists, NULL);
155 hists__output_resort(hists);
156
157 if (verbose > 2) {
158 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
159 print_hists_out(hists);
160 }
161
162 root = &evsel->hists.entries;
163 node = rb_first(root);
164 he = rb_entry(node, struct hist_entry, rb_node);
165 TEST_ASSERT_VAL("Invalid hist entry",
166 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
167 !strcmp(SYM(he), "main") && he->stat.period == 200);
168
169 node = rb_next(node);
170 he = rb_entry(node, struct hist_entry, rb_node);
171 TEST_ASSERT_VAL("Invalid hist entry",
172 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
173 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
174
175 node = rb_next(node);
176 he = rb_entry(node, struct hist_entry, rb_node);
177 TEST_ASSERT_VAL("Invalid hist entry",
178 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
179 !strcmp(SYM(he), "main") && he->stat.period == 100);
180
181 node = rb_next(node);
182 he = rb_entry(node, struct hist_entry, rb_node);
183 TEST_ASSERT_VAL("Invalid hist entry",
184 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
185 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
186
187 node = rb_next(node);
188 he = rb_entry(node, struct hist_entry, rb_node);
189 TEST_ASSERT_VAL("Invalid hist entry",
190 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
191 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
192
193 node = rb_next(node);
194 he = rb_entry(node, struct hist_entry, rb_node);
195 TEST_ASSERT_VAL("Invalid hist entry",
196 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
197 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
198
199 node = rb_next(node);
200 he = rb_entry(node, struct hist_entry, rb_node);
201 TEST_ASSERT_VAL("Invalid hist entry",
202 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
203 !strcmp(SYM(he), "free") && he->stat.period == 100);
204
205 node = rb_next(node);
206 he = rb_entry(node, struct hist_entry, rb_node);
207 TEST_ASSERT_VAL("Invalid hist entry",
208 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
209 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
210
211 node = rb_next(node);
212 he = rb_entry(node, struct hist_entry, rb_node);
213 TEST_ASSERT_VAL("Invalid hist entry",
214 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
215 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
216
217out:
218 del_hist_entries(hists);
219 reset_output_field();
220 return err;
221}
222
223/* mixed fields and sort keys */
224static int test2(struct perf_evsel *evsel, struct machine *machine)
225{
226 int err;
227 struct hists *hists = &evsel->hists;
228 struct hist_entry *he;
229 struct rb_root *root;
230 struct rb_node *node;
231
232 field_order = "overhead,cpu";
233 sort_order = "pid";
234
235 setup_sorting();
236
237 /*
238 * expected output:
239 *
240 * Overhead CPU Command: Pid
241 * ======== === =============
242 * 30.00% 1 perf : 100
243 * 10.00% 0 perf : 100
244 * 10.00% 2 perf : 100
245 * 20.00% 2 perf : 200
246 * 10.00% 0 bash : 300
247 * 10.00% 1 bash : 300
248 * 10.00% 3 bash : 300
249 */
250 err = add_hist_entries(hists, machine);
251 if (err < 0)
252 goto out;
253
254 hists__collapse_resort(hists, NULL);
255 hists__output_resort(hists);
256
257 if (verbose > 2) {
258 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
259 print_hists_out(hists);
260 }
261
262 root = &evsel->hists.entries;
263 node = rb_first(root);
264 he = rb_entry(node, struct hist_entry, rb_node);
265 TEST_ASSERT_VAL("Invalid hist entry",
266 CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300);
267
268 node = rb_next(node);
269 he = rb_entry(node, struct hist_entry, rb_node);
270 TEST_ASSERT_VAL("Invalid hist entry",
271 CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100);
272
273out:
274 del_hist_entries(hists);
275 reset_output_field();
276 return err;
277}
278
279/* fields only (no sort key) */
280static int test3(struct perf_evsel *evsel, struct machine *machine)
281{
282 int err;
283 struct hists *hists = &evsel->hists;
284 struct hist_entry *he;
285 struct rb_root *root;
286 struct rb_node *node;
287
288 field_order = "comm,overhead,dso";
289 sort_order = NULL;
290
291 setup_sorting();
292
293 /*
294 * expected output:
295 *
296 * Command Overhead Shared Object
297 * ======= ======== =============
298 * bash 20.00% bash
299 * bash 10.00% [kernel]
300 * perf 30.00% perf
301 * perf 20.00% [kernel]
302 * perf 20.00% libc
303 */
304 err = add_hist_entries(hists, machine);
305 if (err < 0)
306 goto out;
307
308 hists__collapse_resort(hists, NULL);
309 hists__output_resort(hists);
310
311 if (verbose > 2) {
312 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
313 print_hists_out(hists);
314 }
315
316 root = &evsel->hists.entries;
317 node = rb_first(root);
318 he = rb_entry(node, struct hist_entry, rb_node);
319 TEST_ASSERT_VAL("Invalid hist entry",
320 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
321 he->stat.period == 200);
322
323 node = rb_next(node);
324 he = rb_entry(node, struct hist_entry, rb_node);
325 TEST_ASSERT_VAL("Invalid hist entry",
326 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
327 he->stat.period == 100);
328
329 node = rb_next(node);
330 he = rb_entry(node, struct hist_entry, rb_node);
331 TEST_ASSERT_VAL("Invalid hist entry",
332 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
333 he->stat.period == 300);
334
335 node = rb_next(node);
336 he = rb_entry(node, struct hist_entry, rb_node);
337 TEST_ASSERT_VAL("Invalid hist entry",
338 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
339 he->stat.period == 200);
340
341 node = rb_next(node);
342 he = rb_entry(node, struct hist_entry, rb_node);
343 TEST_ASSERT_VAL("Invalid hist entry",
344 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
345 he->stat.period == 200);
346
347out:
348 del_hist_entries(hists);
349 reset_output_field();
350 return err;
351}
352
353/* handle duplicate 'dso' field */
354static int test4(struct perf_evsel *evsel, struct machine *machine)
355{
356 int err;
357 struct hists *hists = &evsel->hists;
358 struct hist_entry *he;
359 struct rb_root *root;
360 struct rb_node *node;
361
362 field_order = "dso,sym,comm,overhead,dso";
363 sort_order = "sym";
364
365 setup_sorting();
366
367 /*
368 * expected output:
369 *
370 * Shared Object Symbol Command Overhead
371 * ============= ============== ======= ========
372 * perf [.] cmd_record perf 10.00%
373 * libc [.] free perf 10.00%
374 * bash [.] main bash 10.00%
375 * perf [.] main perf 20.00%
376 * libc [.] malloc perf 10.00%
377 * [kernel] [k] page_fault bash 10.00%
378 * [kernel] [k] page_fault perf 10.00%
379 * [kernel] [k] schedule perf 10.00%
380 * bash [.] xmalloc bash 10.00%
381 */
382 err = add_hist_entries(hists, machine);
383 if (err < 0)
384 goto out;
385
386 hists__collapse_resort(hists, NULL);
387 hists__output_resort(hists);
388
389 if (verbose > 2) {
390 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
391 print_hists_out(hists);
392 }
393
394 root = &evsel->hists.entries;
395 node = rb_first(root);
396 he = rb_entry(node, struct hist_entry, rb_node);
397 TEST_ASSERT_VAL("Invalid hist entry",
398 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") &&
399 !strcmp(COMM(he), "perf") && he->stat.period == 100);
400
401 node = rb_next(node);
402 he = rb_entry(node, struct hist_entry, rb_node);
403 TEST_ASSERT_VAL("Invalid hist entry",
404 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") &&
405 !strcmp(COMM(he), "perf") && he->stat.period == 100);
406
407 node = rb_next(node);
408 he = rb_entry(node, struct hist_entry, rb_node);
409 TEST_ASSERT_VAL("Invalid hist entry",
410 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") &&
411 !strcmp(COMM(he), "bash") && he->stat.period == 100);
412
413 node = rb_next(node);
414 he = rb_entry(node, struct hist_entry, rb_node);
415 TEST_ASSERT_VAL("Invalid hist entry",
416 !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") &&
417 !strcmp(COMM(he), "perf") && he->stat.period == 200);
418
419 node = rb_next(node);
420 he = rb_entry(node, struct hist_entry, rb_node);
421 TEST_ASSERT_VAL("Invalid hist entry",
422 !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") &&
423 !strcmp(COMM(he), "perf") && he->stat.period == 100);
424
425 node = rb_next(node);
426 he = rb_entry(node, struct hist_entry, rb_node);
427 TEST_ASSERT_VAL("Invalid hist entry",
428 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
429 !strcmp(COMM(he), "bash") && he->stat.period == 100);
430
431 node = rb_next(node);
432 he = rb_entry(node, struct hist_entry, rb_node);
433 TEST_ASSERT_VAL("Invalid hist entry",
434 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") &&
435 !strcmp(COMM(he), "perf") && he->stat.period == 100);
436
437 node = rb_next(node);
438 he = rb_entry(node, struct hist_entry, rb_node);
439 TEST_ASSERT_VAL("Invalid hist entry",
440 !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") &&
441 !strcmp(COMM(he), "perf") && he->stat.period == 100);
442
443 node = rb_next(node);
444 he = rb_entry(node, struct hist_entry, rb_node);
445 TEST_ASSERT_VAL("Invalid hist entry",
446 !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") &&
447 !strcmp(COMM(he), "bash") && he->stat.period == 100);
448
449out:
450 del_hist_entries(hists);
451 reset_output_field();
452 return err;
453}
454
455/* full sort keys w/o overhead field */
456static int test5(struct perf_evsel *evsel, struct machine *machine)
457{
458 int err;
459 struct hists *hists = &evsel->hists;
460 struct hist_entry *he;
461 struct rb_root *root;
462 struct rb_node *node;
463
464 field_order = "cpu,pid,comm,dso,sym";
465 sort_order = "dso,pid";
466
467 setup_sorting();
468
469 /*
470 * expected output:
471 *
472 * CPU Command: Pid Command Shared Object Symbol
473 * === ============= ======= ============= ==============
474 * 0 perf: 100 perf [kernel] [k] schedule
475 * 2 perf: 200 perf [kernel] [k] page_fault
476 * 1 bash: 300 bash [kernel] [k] page_fault
477 * 0 bash: 300 bash bash [.] xmalloc
478 * 3 bash: 300 bash bash [.] main
479 * 1 perf: 100 perf libc [.] malloc
480 * 2 perf: 100 perf libc [.] free
481 * 1 perf: 100 perf perf [.] cmd_record
482 * 1 perf: 100 perf perf [.] main
483 * 2 perf: 200 perf perf [.] main
484 */
485 err = add_hist_entries(hists, machine);
486 if (err < 0)
487 goto out;
488
489 hists__collapse_resort(hists, NULL);
490 hists__output_resort(hists);
491
492 if (verbose > 2) {
493 pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
494 print_hists_out(hists);
495 }
496
497 root = &evsel->hists.entries;
498 node = rb_first(root);
499 he = rb_entry(node, struct hist_entry, rb_node);
500
501 TEST_ASSERT_VAL("Invalid hist entry",
502 CPU(he) == 0 && PID(he) == 100 &&
503 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
504 !strcmp(SYM(he), "schedule") && he->stat.period == 100);
505
506 node = rb_next(node);
507 he = rb_entry(node, struct hist_entry, rb_node);
508 TEST_ASSERT_VAL("Invalid hist entry",
509 CPU(he) == 2 && PID(he) == 200 &&
510 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") &&
511 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
512
513 node = rb_next(node);
514 he = rb_entry(node, struct hist_entry, rb_node);
515 TEST_ASSERT_VAL("Invalid hist entry",
516 CPU(he) == 1 && PID(he) == 300 &&
517 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") &&
518 !strcmp(SYM(he), "page_fault") && he->stat.period == 100);
519
520 node = rb_next(node);
521 he = rb_entry(node, struct hist_entry, rb_node);
522 TEST_ASSERT_VAL("Invalid hist entry",
523 CPU(he) == 0 && PID(he) == 300 &&
524 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
525 !strcmp(SYM(he), "xmalloc") && he->stat.period == 100);
526
527 node = rb_next(node);
528 he = rb_entry(node, struct hist_entry, rb_node);
529 TEST_ASSERT_VAL("Invalid hist entry",
530 CPU(he) == 3 && PID(he) == 300 &&
531 !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") &&
532 !strcmp(SYM(he), "main") && he->stat.period == 100);
533
534 node = rb_next(node);
535 he = rb_entry(node, struct hist_entry, rb_node);
536 TEST_ASSERT_VAL("Invalid hist entry",
537 CPU(he) == 1 && PID(he) == 100 &&
538 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
539 !strcmp(SYM(he), "malloc") && he->stat.period == 100);
540
541 node = rb_next(node);
542 he = rb_entry(node, struct hist_entry, rb_node);
543 TEST_ASSERT_VAL("Invalid hist entry",
544 CPU(he) == 2 && PID(he) == 100 &&
545 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") &&
546 !strcmp(SYM(he), "free") && he->stat.period == 100);
547
548 node = rb_next(node);
549 he = rb_entry(node, struct hist_entry, rb_node);
550 TEST_ASSERT_VAL("Invalid hist entry",
551 CPU(he) == 1 && PID(he) == 100 &&
552 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
553 !strcmp(SYM(he), "cmd_record") && he->stat.period == 100);
554
555 node = rb_next(node);
556 he = rb_entry(node, struct hist_entry, rb_node);
557 TEST_ASSERT_VAL("Invalid hist entry",
558 CPU(he) == 1 && PID(he) == 100 &&
559 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
560 !strcmp(SYM(he), "main") && he->stat.period == 100);
561
562 node = rb_next(node);
563 he = rb_entry(node, struct hist_entry, rb_node);
564 TEST_ASSERT_VAL("Invalid hist entry",
565 CPU(he) == 2 && PID(he) == 200 &&
566 !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") &&
567 !strcmp(SYM(he), "main") && he->stat.period == 100);
568
569out:
570 del_hist_entries(hists);
571 reset_output_field();
572 return err;
573}
574
575int test__hists_output(void)
576{
577 int err = TEST_FAIL;
578 struct machines machines;
579 struct machine *machine;
580 struct perf_evsel *evsel;
581 struct perf_evlist *evlist = perf_evlist__new();
582 size_t i;
583 test_fn_t testcases[] = {
584 test1,
585 test2,
586 test3,
587 test4,
588 test5,
589 };
590
591 TEST_ASSERT_VAL("No memory", evlist);
592
593 err = parse_events(evlist, "cpu-clock");
594 if (err)
595 goto out;
596
597 machines__init(&machines);
598
599 /* setup threads/dso/map/symbols also */
600 machine = setup_fake_machine(&machines);
601 if (!machine)
602 goto out;
603
604 if (verbose > 1)
605 machine__fprintf(machine, stderr);
606
607 evsel = perf_evlist__first(evlist);
608
609 for (i = 0; i < ARRAY_SIZE(testcases); i++) {
610 err = testcases[i](evsel, machine);
611 if (err < 0)
612 break;
613 }
614
615out:
616 /* tear down everything */
617 perf_evlist__delete(evlist);
618 machines__exit(&machines);
619
620 return err;
621}
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c
index 497957f269d8..7a5ab7b0b8f6 100644
--- a/tools/perf/tests/keep-tracking.c
+++ b/tools/perf/tests/keep-tracking.c
@@ -1,4 +1,4 @@
1#include <sys/types.h> 1#include <linux/types.h>
2#include <unistd.h> 2#include <unistd.h>
3#include <sys/prctl.h> 3#include <sys/prctl.h>
4 4
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
new file mode 100644
index 000000000000..4a456fef66ca
--- /dev/null
+++ b/tools/perf/tests/mmap-thread-lookup.c
@@ -0,0 +1,233 @@
1#include <unistd.h>
2#include <sys/syscall.h>
3#include <sys/types.h>
4#include <sys/mman.h>
5#include <pthread.h>
6#include <stdlib.h>
7#include <stdio.h>
8#include "debug.h"
9#include "tests.h"
10#include "machine.h"
11#include "thread_map.h"
12#include "symbol.h"
13#include "thread.h"
14
15#define THREADS 4
16
17static int go_away;
18
19struct thread_data {
20 pthread_t pt;
21 pid_t tid;
22 void *map;
23 int ready[2];
24};
25
26static struct thread_data threads[THREADS];
27
28static int thread_init(struct thread_data *td)
29{
30 void *map;
31
32 map = mmap(NULL, page_size,
33 PROT_READ|PROT_WRITE|PROT_EXEC,
34 MAP_SHARED|MAP_ANONYMOUS, -1, 0);
35
36 if (map == MAP_FAILED) {
37 perror("mmap failed");
38 return -1;
39 }
40
41 td->map = map;
42 td->tid = syscall(SYS_gettid);
43
44 pr_debug("tid = %d, map = %p\n", td->tid, map);
45 return 0;
46}
47
48static void *thread_fn(void *arg)
49{
50 struct thread_data *td = arg;
51 ssize_t ret;
52 int go;
53
54 if (thread_init(td))
55 return NULL;
56
57 /* Signal thread_create thread is initialized. */
58 ret = write(td->ready[1], &go, sizeof(int));
59 if (ret != sizeof(int)) {
60 pr_err("failed to notify\n");
61 return NULL;
62 }
63
64 while (!go_away) {
65 /* Waiting for main thread to kill us. */
66 usleep(100);
67 }
68
69 munmap(td->map, page_size);
70 return NULL;
71}
72
73static int thread_create(int i)
74{
75 struct thread_data *td = &threads[i];
76 int err, go;
77
78 if (pipe(td->ready))
79 return -1;
80
81 err = pthread_create(&td->pt, NULL, thread_fn, td);
82 if (!err) {
83 /* Wait for thread initialization. */
84 ssize_t ret = read(td->ready[0], &go, sizeof(int));
85 err = ret != sizeof(int);
86 }
87
88 close(td->ready[0]);
89 close(td->ready[1]);
90 return err;
91}
92
93static int threads_create(void)
94{
95 struct thread_data *td0 = &threads[0];
96 int i, err = 0;
97
98 go_away = 0;
99
100 /* 0 is main thread */
101 if (thread_init(td0))
102 return -1;
103
104 for (i = 1; !err && i < THREADS; i++)
105 err = thread_create(i);
106
107 return err;
108}
109
110static int threads_destroy(void)
111{
112 struct thread_data *td0 = &threads[0];
113 int i, err = 0;
114
115 /* cleanup the main thread */
116 munmap(td0->map, page_size);
117
118 go_away = 1;
119
120 for (i = 1; !err && i < THREADS; i++)
121 err = pthread_join(threads[i].pt, NULL);
122
123 return err;
124}
125
126typedef int (*synth_cb)(struct machine *machine);
127
128static int synth_all(struct machine *machine)
129{
130 return perf_event__synthesize_threads(NULL,
131 perf_event__process,
132 machine, 0);
133}
134
135static int synth_process(struct machine *machine)
136{
137 struct thread_map *map;
138 int err;
139
140 map = thread_map__new_by_pid(getpid());
141
142 err = perf_event__synthesize_thread_map(NULL, map,
143 perf_event__process,
144 machine, 0);
145
146 thread_map__delete(map);
147 return err;
148}
149
150static int mmap_events(synth_cb synth)
151{
152 struct machines machines;
153 struct machine *machine;
154 int err, i;
155
156 /*
157 * The threads_create will not return before all threads
158 * are spawned and all created memory map.
159 *
160 * They will loop until threads_destroy is called, so we
161 * can safely run synthesizing function.
162 */
163 TEST_ASSERT_VAL("failed to create threads", !threads_create());
164
165 machines__init(&machines);
166 machine = &machines.host;
167
168 dump_trace = verbose > 1 ? 1 : 0;
169
170 err = synth(machine);
171
172 dump_trace = 0;
173
174 TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy());
175 TEST_ASSERT_VAL("failed to synthesize maps", !err);
176
177 /*
178 * All data is synthesized, try to find map for each
179 * thread object.
180 */
181 for (i = 0; i < THREADS; i++) {
182 struct thread_data *td = &threads[i];
183 struct addr_location al;
184 struct thread *thread;
185
186 thread = machine__findnew_thread(machine, getpid(), td->tid);
187
188 pr_debug("looking for map %p\n", td->map);
189
190 thread__find_addr_map(thread, machine,
191 PERF_RECORD_MISC_USER, MAP__FUNCTION,
192 (unsigned long) (td->map + 1), &al);
193
194 if (!al.map) {
195 pr_debug("failed, couldn't find map\n");
196 err = -1;
197 break;
198 }
199
200 pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start);
201 }
202
203 machine__delete_threads(machine);
204 machines__exit(&machines);
205 return err;
206}
207
208/*
209 * This test creates 'THREADS' number of threads (including
210 * main thread) and each thread creates memory map.
211 *
212 * When threads are created, we synthesize them with both
213 * (separate tests):
214 * perf_event__synthesize_thread_map (process based)
215 * perf_event__synthesize_threads (global)
216 *
217 * We test we can find all memory maps via:
218 * thread__find_addr_map
219 *
220 * by using all thread objects.
221 */
222int test__mmap_thread_lookup(void)
223{
224 /* perf_event__synthesize_threads synthesize */
225 TEST_ASSERT_VAL("failed with sythesizing all",
226 !mmap_events(synth_all));
227
228 /* perf_event__synthesize_thread_map synthesize */
229 TEST_ASSERT_VAL("failed with sythesizing process",
230 !mmap_events(synth_process));
231
232 return 0;
233}
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 8605ff5572ae..deba66955f8c 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -1174,188 +1174,240 @@ static int test__all_tracepoints(struct perf_evlist *evlist)
1174struct evlist_test { 1174struct evlist_test {
1175 const char *name; 1175 const char *name;
1176 __u32 type; 1176 __u32 type;
1177 const int id;
1177 int (*check)(struct perf_evlist *evlist); 1178 int (*check)(struct perf_evlist *evlist);
1178}; 1179};
1179 1180
1180static struct evlist_test test__events[] = { 1181static struct evlist_test test__events[] = {
1181 [0] = { 1182 {
1182 .name = "syscalls:sys_enter_open", 1183 .name = "syscalls:sys_enter_open",
1183 .check = test__checkevent_tracepoint, 1184 .check = test__checkevent_tracepoint,
1185 .id = 0,
1184 }, 1186 },
1185 [1] = { 1187 {
1186 .name = "syscalls:*", 1188 .name = "syscalls:*",
1187 .check = test__checkevent_tracepoint_multi, 1189 .check = test__checkevent_tracepoint_multi,
1190 .id = 1,
1188 }, 1191 },
1189 [2] = { 1192 {
1190 .name = "r1a", 1193 .name = "r1a",
1191 .check = test__checkevent_raw, 1194 .check = test__checkevent_raw,
1195 .id = 2,
1192 }, 1196 },
1193 [3] = { 1197 {
1194 .name = "1:1", 1198 .name = "1:1",
1195 .check = test__checkevent_numeric, 1199 .check = test__checkevent_numeric,
1200 .id = 3,
1196 }, 1201 },
1197 [4] = { 1202 {
1198 .name = "instructions", 1203 .name = "instructions",
1199 .check = test__checkevent_symbolic_name, 1204 .check = test__checkevent_symbolic_name,
1205 .id = 4,
1200 }, 1206 },
1201 [5] = { 1207 {
1202 .name = "cycles/period=100000,config2/", 1208 .name = "cycles/period=100000,config2/",
1203 .check = test__checkevent_symbolic_name_config, 1209 .check = test__checkevent_symbolic_name_config,
1210 .id = 5,
1204 }, 1211 },
1205 [6] = { 1212 {
1206 .name = "faults", 1213 .name = "faults",
1207 .check = test__checkevent_symbolic_alias, 1214 .check = test__checkevent_symbolic_alias,
1215 .id = 6,
1208 }, 1216 },
1209 [7] = { 1217 {
1210 .name = "L1-dcache-load-miss", 1218 .name = "L1-dcache-load-miss",
1211 .check = test__checkevent_genhw, 1219 .check = test__checkevent_genhw,
1220 .id = 7,
1212 }, 1221 },
1213 [8] = { 1222 {
1214 .name = "mem:0", 1223 .name = "mem:0",
1215 .check = test__checkevent_breakpoint, 1224 .check = test__checkevent_breakpoint,
1225 .id = 8,
1216 }, 1226 },
1217 [9] = { 1227 {
1218 .name = "mem:0:x", 1228 .name = "mem:0:x",
1219 .check = test__checkevent_breakpoint_x, 1229 .check = test__checkevent_breakpoint_x,
1230 .id = 9,
1220 }, 1231 },
1221 [10] = { 1232 {
1222 .name = "mem:0:r", 1233 .name = "mem:0:r",
1223 .check = test__checkevent_breakpoint_r, 1234 .check = test__checkevent_breakpoint_r,
1235 .id = 10,
1224 }, 1236 },
1225 [11] = { 1237 {
1226 .name = "mem:0:w", 1238 .name = "mem:0:w",
1227 .check = test__checkevent_breakpoint_w, 1239 .check = test__checkevent_breakpoint_w,
1240 .id = 11,
1228 }, 1241 },
1229 [12] = { 1242 {
1230 .name = "syscalls:sys_enter_open:k", 1243 .name = "syscalls:sys_enter_open:k",
1231 .check = test__checkevent_tracepoint_modifier, 1244 .check = test__checkevent_tracepoint_modifier,
1245 .id = 12,
1232 }, 1246 },
1233 [13] = { 1247 {
1234 .name = "syscalls:*:u", 1248 .name = "syscalls:*:u",
1235 .check = test__checkevent_tracepoint_multi_modifier, 1249 .check = test__checkevent_tracepoint_multi_modifier,
1250 .id = 13,
1236 }, 1251 },
1237 [14] = { 1252 {
1238 .name = "r1a:kp", 1253 .name = "r1a:kp",
1239 .check = test__checkevent_raw_modifier, 1254 .check = test__checkevent_raw_modifier,
1255 .id = 14,
1240 }, 1256 },
1241 [15] = { 1257 {
1242 .name = "1:1:hp", 1258 .name = "1:1:hp",
1243 .check = test__checkevent_numeric_modifier, 1259 .check = test__checkevent_numeric_modifier,
1260 .id = 15,
1244 }, 1261 },
1245 [16] = { 1262 {
1246 .name = "instructions:h", 1263 .name = "instructions:h",
1247 .check = test__checkevent_symbolic_name_modifier, 1264 .check = test__checkevent_symbolic_name_modifier,
1265 .id = 16,
1248 }, 1266 },
1249 [17] = { 1267 {
1250 .name = "faults:u", 1268 .name = "faults:u",
1251 .check = test__checkevent_symbolic_alias_modifier, 1269 .check = test__checkevent_symbolic_alias_modifier,
1270 .id = 17,
1252 }, 1271 },
1253 [18] = { 1272 {
1254 .name = "L1-dcache-load-miss:kp", 1273 .name = "L1-dcache-load-miss:kp",
1255 .check = test__checkevent_genhw_modifier, 1274 .check = test__checkevent_genhw_modifier,
1275 .id = 18,
1256 }, 1276 },
1257 [19] = { 1277 {
1258 .name = "mem:0:u", 1278 .name = "mem:0:u",
1259 .check = test__checkevent_breakpoint_modifier, 1279 .check = test__checkevent_breakpoint_modifier,
1280 .id = 19,
1260 }, 1281 },
1261 [20] = { 1282 {
1262 .name = "mem:0:x:k", 1283 .name = "mem:0:x:k",
1263 .check = test__checkevent_breakpoint_x_modifier, 1284 .check = test__checkevent_breakpoint_x_modifier,
1285 .id = 20,
1264 }, 1286 },
1265 [21] = { 1287 {
1266 .name = "mem:0:r:hp", 1288 .name = "mem:0:r:hp",
1267 .check = test__checkevent_breakpoint_r_modifier, 1289 .check = test__checkevent_breakpoint_r_modifier,
1290 .id = 21,
1268 }, 1291 },
1269 [22] = { 1292 {
1270 .name = "mem:0:w:up", 1293 .name = "mem:0:w:up",
1271 .check = test__checkevent_breakpoint_w_modifier, 1294 .check = test__checkevent_breakpoint_w_modifier,
1295 .id = 22,
1272 }, 1296 },
1273 [23] = { 1297 {
1274 .name = "r1,syscalls:sys_enter_open:k,1:1:hp", 1298 .name = "r1,syscalls:sys_enter_open:k,1:1:hp",
1275 .check = test__checkevent_list, 1299 .check = test__checkevent_list,
1300 .id = 23,
1276 }, 1301 },
1277 [24] = { 1302 {
1278 .name = "instructions:G", 1303 .name = "instructions:G",
1279 .check = test__checkevent_exclude_host_modifier, 1304 .check = test__checkevent_exclude_host_modifier,
1305 .id = 24,
1280 }, 1306 },
1281 [25] = { 1307 {
1282 .name = "instructions:H", 1308 .name = "instructions:H",
1283 .check = test__checkevent_exclude_guest_modifier, 1309 .check = test__checkevent_exclude_guest_modifier,
1310 .id = 25,
1284 }, 1311 },
1285 [26] = { 1312 {
1286 .name = "mem:0:rw", 1313 .name = "mem:0:rw",
1287 .check = test__checkevent_breakpoint_rw, 1314 .check = test__checkevent_breakpoint_rw,
1315 .id = 26,
1288 }, 1316 },
1289 [27] = { 1317 {
1290 .name = "mem:0:rw:kp", 1318 .name = "mem:0:rw:kp",
1291 .check = test__checkevent_breakpoint_rw_modifier, 1319 .check = test__checkevent_breakpoint_rw_modifier,
1320 .id = 27,
1292 }, 1321 },
1293 [28] = { 1322 {
1294 .name = "{instructions:k,cycles:upp}", 1323 .name = "{instructions:k,cycles:upp}",
1295 .check = test__group1, 1324 .check = test__group1,
1325 .id = 28,
1296 }, 1326 },
1297 [29] = { 1327 {
1298 .name = "{faults:k,cache-references}:u,cycles:k", 1328 .name = "{faults:k,cache-references}:u,cycles:k",
1299 .check = test__group2, 1329 .check = test__group2,
1330 .id = 29,
1300 }, 1331 },
1301 [30] = { 1332 {
1302 .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", 1333 .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u",
1303 .check = test__group3, 1334 .check = test__group3,
1335 .id = 30,
1304 }, 1336 },
1305 [31] = { 1337 {
1306 .name = "{cycles:u,instructions:kp}:p", 1338 .name = "{cycles:u,instructions:kp}:p",
1307 .check = test__group4, 1339 .check = test__group4,
1340 .id = 31,
1308 }, 1341 },
1309 [32] = { 1342 {
1310 .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", 1343 .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",
1311 .check = test__group5, 1344 .check = test__group5,
1345 .id = 32,
1312 }, 1346 },
1313 [33] = { 1347 {
1314 .name = "*:*", 1348 .name = "*:*",
1315 .check = test__all_tracepoints, 1349 .check = test__all_tracepoints,
1350 .id = 33,
1316 }, 1351 },
1317 [34] = { 1352 {
1318 .name = "{cycles,cache-misses:G}:H", 1353 .name = "{cycles,cache-misses:G}:H",
1319 .check = test__group_gh1, 1354 .check = test__group_gh1,
1355 .id = 34,
1320 }, 1356 },
1321 [35] = { 1357 {
1322 .name = "{cycles,cache-misses:H}:G", 1358 .name = "{cycles,cache-misses:H}:G",
1323 .check = test__group_gh2, 1359 .check = test__group_gh2,
1360 .id = 35,
1324 }, 1361 },
1325 [36] = { 1362 {
1326 .name = "{cycles:G,cache-misses:H}:u", 1363 .name = "{cycles:G,cache-misses:H}:u",
1327 .check = test__group_gh3, 1364 .check = test__group_gh3,
1365 .id = 36,
1328 }, 1366 },
1329 [37] = { 1367 {
1330 .name = "{cycles:G,cache-misses:H}:uG", 1368 .name = "{cycles:G,cache-misses:H}:uG",
1331 .check = test__group_gh4, 1369 .check = test__group_gh4,
1370 .id = 37,
1332 }, 1371 },
1333 [38] = { 1372 {
1334 .name = "{cycles,cache-misses,branch-misses}:S", 1373 .name = "{cycles,cache-misses,branch-misses}:S",
1335 .check = test__leader_sample1, 1374 .check = test__leader_sample1,
1375 .id = 38,
1336 }, 1376 },
1337 [39] = { 1377 {
1338 .name = "{instructions,branch-misses}:Su", 1378 .name = "{instructions,branch-misses}:Su",
1339 .check = test__leader_sample2, 1379 .check = test__leader_sample2,
1380 .id = 39,
1340 }, 1381 },
1341 [40] = { 1382 {
1342 .name = "instructions:uDp", 1383 .name = "instructions:uDp",
1343 .check = test__checkevent_pinned_modifier, 1384 .check = test__checkevent_pinned_modifier,
1385 .id = 40,
1344 }, 1386 },
1345 [41] = { 1387 {
1346 .name = "{cycles,cache-misses,branch-misses}:D", 1388 .name = "{cycles,cache-misses,branch-misses}:D",
1347 .check = test__pinned_group, 1389 .check = test__pinned_group,
1390 .id = 41,
1391 },
1392#if defined(__s390x__)
1393 {
1394 .name = "kvm-s390:kvm_s390_create_vm",
1395 .check = test__checkevent_tracepoint,
1396 .id = 100,
1348 }, 1397 },
1398#endif
1349}; 1399};
1350 1400
1351static struct evlist_test test__events_pmu[] = { 1401static struct evlist_test test__events_pmu[] = {
1352 [0] = { 1402 {
1353 .name = "cpu/config=10,config1,config2=3,period=1000/u", 1403 .name = "cpu/config=10,config1,config2=3,period=1000/u",
1354 .check = test__checkevent_pmu, 1404 .check = test__checkevent_pmu,
1405 .id = 0,
1355 }, 1406 },
1356 [1] = { 1407 {
1357 .name = "cpu/config=1,name=krava/u,cpu/config=2/u", 1408 .name = "cpu/config=1,name=krava/u,cpu/config=2/u",
1358 .check = test__checkevent_pmu_name, 1409 .check = test__checkevent_pmu_name,
1410 .id = 1,
1359 }, 1411 },
1360}; 1412};
1361 1413
@@ -1402,7 +1454,7 @@ static int test_events(struct evlist_test *events, unsigned cnt)
1402 for (i = 0; i < cnt; i++) { 1454 for (i = 0; i < cnt; i++) {
1403 struct evlist_test *e = &events[i]; 1455 struct evlist_test *e = &events[i];
1404 1456
1405 pr_debug("running test %d '%s'\n", i, e->name); 1457 pr_debug("running test %d '%s'\n", e->id, e->name);
1406 ret1 = test_event(e); 1458 ret1 = test_event(e);
1407 if (ret1) 1459 if (ret1)
1408 ret2 = ret1; 1460 ret2 = ret1;
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c
index e117b6c6a248..905019f9b740 100644
--- a/tools/perf/tests/parse-no-sample-id-all.c
+++ b/tools/perf/tests/parse-no-sample-id-all.c
@@ -1,4 +1,4 @@
1#include <sys/types.h> 1#include <linux/types.h>
2#include <stddef.h> 2#include <stddef.h>
3 3
4#include "tests.h" 4#include "tests.h"
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c
index 47146d388dbf..3b7cd4d32dcb 100644
--- a/tools/perf/tests/perf-time-to-tsc.c
+++ b/tools/perf/tests/perf-time-to-tsc.c
@@ -1,7 +1,6 @@
1#include <stdio.h> 1#include <stdio.h>
2#include <sys/types.h>
3#include <unistd.h> 2#include <unistd.h>
4#include <inttypes.h> 3#include <linux/types.h>
5#include <sys/prctl.h> 4#include <sys/prctl.h>
6 5
7#include "parse-events.h" 6#include "parse-events.h"
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c
index 46649c25fa5e..e59143fd9e71 100644
--- a/tools/perf/tests/rdpmc.c
+++ b/tools/perf/tests/rdpmc.c
@@ -2,7 +2,7 @@
2#include <stdlib.h> 2#include <stdlib.h>
3#include <signal.h> 3#include <signal.h>
4#include <sys/mman.h> 4#include <sys/mman.h>
5#include "types.h" 5#include <linux/types.h>
6#include "perf.h" 6#include "perf.h"
7#include "debug.h" 7#include "debug.h"
8#include "tests.h" 8#include "tests.h"
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 0014d3c8c21c..7ae8d17db3d9 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -1,5 +1,5 @@
1#include <stdbool.h> 1#include <stdbool.h>
2#include <inttypes.h> 2#include <linux/types.h>
3 3
4#include "util.h" 4#include "util.h"
5#include "event.h" 5#include "event.h"
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index a24795ca002d..022bb68fd9c7 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -41,8 +41,13 @@ int test__sample_parsing(void);
41int test__keep_tracking(void); 41int test__keep_tracking(void);
42int test__parse_no_sample_id_all(void); 42int test__parse_no_sample_id_all(void);
43int test__dwarf_unwind(void); 43int test__dwarf_unwind(void);
44int test__hists_filter(void);
45int test__mmap_thread_lookup(void);
46int test__thread_mg_share(void);
47int test__hists_output(void);
48int test__hists_cumulate(void);
44 49
45#if defined(__x86_64__) || defined(__i386__) 50#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
46#ifdef HAVE_DWARF_UNWIND_SUPPORT 51#ifdef HAVE_DWARF_UNWIND_SUPPORT
47struct thread; 52struct thread;
48struct perf_sample; 53struct perf_sample;
diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c
new file mode 100644
index 000000000000..2b2e0dbe114f
--- /dev/null
+++ b/tools/perf/tests/thread-mg-share.c
@@ -0,0 +1,90 @@
1#include "tests.h"
2#include "machine.h"
3#include "thread.h"
4#include "map.h"
5
6int test__thread_mg_share(void)
7{
8 struct machines machines;
9 struct machine *machine;
10
11 /* thread group */
12 struct thread *leader;
13 struct thread *t1, *t2, *t3;
14 struct map_groups *mg;
15
16 /* other process */
17 struct thread *other, *other_leader;
18 struct map_groups *other_mg;
19
20 /*
21 * This test create 2 processes abstractions (struct thread)
22 * with several threads and checks they properly share and
23 * maintain map groups info (struct map_groups).
24 *
25 * thread group (pid: 0, tids: 0, 1, 2, 3)
26 * other group (pid: 4, tids: 4, 5)
27 */
28
29 machines__init(&machines);
30 machine = &machines.host;
31
32 /* create process with 4 threads */
33 leader = machine__findnew_thread(machine, 0, 0);
34 t1 = machine__findnew_thread(machine, 0, 1);
35 t2 = machine__findnew_thread(machine, 0, 2);
36 t3 = machine__findnew_thread(machine, 0, 3);
37
38 /* and create 1 separated process, without thread leader */
39 other = machine__findnew_thread(machine, 4, 5);
40
41 TEST_ASSERT_VAL("failed to create threads",
42 leader && t1 && t2 && t3 && other);
43
44 mg = leader->mg;
45 TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4);
46
47 /* test the map groups pointer is shared */
48 TEST_ASSERT_VAL("map groups don't match", mg == t1->mg);
49 TEST_ASSERT_VAL("map groups don't match", mg == t2->mg);
50 TEST_ASSERT_VAL("map groups don't match", mg == t3->mg);
51
52 /*
53 * Verify the other leader was created by previous call.
54 * It should have shared map groups with no change in
55 * refcnt.
56 */
57 other_leader = machine__find_thread(machine, 4, 4);
58 TEST_ASSERT_VAL("failed to find other leader", other_leader);
59
60 other_mg = other->mg;
61 TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2);
62
63 TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg);
64
65 /* release thread group */
66 thread__delete(leader);
67 TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3);
68
69 thread__delete(t1);
70 TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2);
71
72 thread__delete(t2);
73 TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1);
74
75 thread__delete(t3);
76
77 /* release other group */
78 thread__delete(other_leader);
79 TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1);
80
81 thread__delete(other);
82
83 /*
84 * Cannot call machine__delete_threads(machine) now,
85 * because we've already released all the threads.
86 */
87
88 machines__exit(&machines);
89 return 0;
90}
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index d11541d4d7d7..3ccf6e14f89b 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -194,7 +194,7 @@ int ui_browser__warning(struct ui_browser *browser, int timeout,
194 ui_helpline__vpush(format, args); 194 ui_helpline__vpush(format, args);
195 va_end(args); 195 va_end(args);
196 } else { 196 } else {
197 while ((key == ui__question_window("Warning!", text, 197 while ((key = ui__question_window("Warning!", text,
198 "Press any key...", 198 "Press any key...",
199 timeout)) == K_RESIZE) 199 timeout)) == K_RESIZE)
200 ui_browser__handle_resize(browser); 200 ui_browser__handle_resize(browser);
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h
index 118cca29dd26..03d4d6295f10 100644
--- a/tools/perf/ui/browser.h
+++ b/tools/perf/ui/browser.h
@@ -1,9 +1,7 @@
1#ifndef _PERF_UI_BROWSER_H_ 1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1 2#define _PERF_UI_BROWSER_H_ 1
3 3
4#include <stdbool.h> 4#include <linux/types.h>
5#include <sys/types.h>
6#include "../types.h"
7 5
8#define HE_COLORSET_TOP 50 6#define HE_COLORSET_TOP 50
9#define HE_COLORSET_MEDIUM 51 7#define HE_COLORSET_MEDIUM 51
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 7ec871af3f6f..52c03fbbba17 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -26,13 +26,35 @@ struct hist_browser {
26 int print_seq; 26 int print_seq;
27 bool show_dso; 27 bool show_dso;
28 float min_pcnt; 28 float min_pcnt;
29 u64 nr_pcnt_entries; 29 u64 nr_non_filtered_entries;
30 u64 nr_callchain_rows;
30}; 31};
31 32
32extern void hist_browser__init_hpp(void); 33extern void hist_browser__init_hpp(void);
33 34
34static int hists__browser_title(struct hists *hists, char *bf, size_t size, 35static int hists__browser_title(struct hists *hists, char *bf, size_t size,
35 const char *ev_name); 36 const char *ev_name);
37static void hist_browser__update_nr_entries(struct hist_browser *hb);
38
39static struct rb_node *hists__filter_entries(struct rb_node *nd,
40 float min_pcnt);
41
42static bool hist_browser__has_filter(struct hist_browser *hb)
43{
44 return hists__has_filter(hb->hists) || hb->min_pcnt;
45}
46
47static u32 hist_browser__nr_entries(struct hist_browser *hb)
48{
49 u32 nr_entries;
50
51 if (hist_browser__has_filter(hb))
52 nr_entries = hb->nr_non_filtered_entries;
53 else
54 nr_entries = hb->hists->nr_entries;
55
56 return nr_entries + hb->nr_callchain_rows;
57}
36 58
37static void hist_browser__refresh_dimensions(struct hist_browser *browser) 59static void hist_browser__refresh_dimensions(struct hist_browser *browser)
38{ 60{
@@ -43,7 +65,14 @@ static void hist_browser__refresh_dimensions(struct hist_browser *browser)
43 65
44static void hist_browser__reset(struct hist_browser *browser) 66static void hist_browser__reset(struct hist_browser *browser)
45{ 67{
46 browser->b.nr_entries = browser->hists->nr_entries; 68 /*
69 * The hists__remove_entry_filter() already folds non-filtered
70 * entries so we can assume it has 0 callchain rows.
71 */
72 browser->nr_callchain_rows = 0;
73
74 hist_browser__update_nr_entries(browser);
75 browser->b.nr_entries = hist_browser__nr_entries(browser);
47 hist_browser__refresh_dimensions(browser); 76 hist_browser__refresh_dimensions(browser);
48 ui_browser__reset_index(&browser->b); 77 ui_browser__reset_index(&browser->b);
49} 78}
@@ -198,14 +227,16 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
198 struct hist_entry *he = browser->he_selection; 227 struct hist_entry *he = browser->he_selection;
199 228
200 hist_entry__init_have_children(he); 229 hist_entry__init_have_children(he);
201 browser->hists->nr_entries -= he->nr_rows; 230 browser->b.nr_entries -= he->nr_rows;
231 browser->nr_callchain_rows -= he->nr_rows;
202 232
203 if (he->ms.unfolded) 233 if (he->ms.unfolded)
204 he->nr_rows = callchain__count_rows(&he->sorted_chain); 234 he->nr_rows = callchain__count_rows(&he->sorted_chain);
205 else 235 else
206 he->nr_rows = 0; 236 he->nr_rows = 0;
207 browser->hists->nr_entries += he->nr_rows; 237
208 browser->b.nr_entries = browser->hists->nr_entries; 238 browser->b.nr_entries += he->nr_rows;
239 browser->nr_callchain_rows += he->nr_rows;
209 240
210 return true; 241 return true;
211 } 242 }
@@ -280,23 +311,27 @@ static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
280 he->nr_rows = 0; 311 he->nr_rows = 0;
281} 312}
282 313
283static void hists__set_folding(struct hists *hists, bool unfold) 314static void
315__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
284{ 316{
285 struct rb_node *nd; 317 struct rb_node *nd;
318 struct hists *hists = browser->hists;
286 319
287 hists->nr_entries = 0; 320 for (nd = rb_first(&hists->entries);
288 321 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
289 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 322 nd = rb_next(nd)) {
290 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 323 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
291 hist_entry__set_folding(he, unfold); 324 hist_entry__set_folding(he, unfold);
292 hists->nr_entries += 1 + he->nr_rows; 325 browser->nr_callchain_rows += he->nr_rows;
293 } 326 }
294} 327}
295 328
296static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 329static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
297{ 330{
298 hists__set_folding(browser->hists, unfold); 331 browser->nr_callchain_rows = 0;
299 browser->b.nr_entries = browser->hists->nr_entries; 332 __hist_browser__set_folding(browser, unfold);
333
334 browser->b.nr_entries = hist_browser__nr_entries(browser);
300 /* Go to the start, we may be way after valid entries after a collapse */ 335 /* Go to the start, we may be way after valid entries after a collapse */
301 ui_browser__reset_index(&browser->b); 336 ui_browser__reset_index(&browser->b);
302} 337}
@@ -310,8 +345,6 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
310 "Or reduce the sampling frequency."); 345 "Or reduce the sampling frequency.");
311} 346}
312 347
313static void hist_browser__update_pcnt_entries(struct hist_browser *hb);
314
315static int hist_browser__run(struct hist_browser *browser, const char *ev_name, 348static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
316 struct hist_browser_timer *hbt) 349 struct hist_browser_timer *hbt)
317{ 350{
@@ -320,9 +353,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
320 int delay_secs = hbt ? hbt->refresh : 0; 353 int delay_secs = hbt ? hbt->refresh : 0;
321 354
322 browser->b.entries = &browser->hists->entries; 355 browser->b.entries = &browser->hists->entries;
323 browser->b.nr_entries = browser->hists->nr_entries; 356 browser->b.nr_entries = hist_browser__nr_entries(browser);
324 if (browser->min_pcnt)
325 browser->b.nr_entries = browser->nr_pcnt_entries;
326 357
327 hist_browser__refresh_dimensions(browser); 358 hist_browser__refresh_dimensions(browser);
328 hists__browser_title(browser->hists, title, sizeof(title), ev_name); 359 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
@@ -339,13 +370,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
339 u64 nr_entries; 370 u64 nr_entries;
340 hbt->timer(hbt->arg); 371 hbt->timer(hbt->arg);
341 372
342 if (browser->min_pcnt) { 373 if (hist_browser__has_filter(browser))
343 hist_browser__update_pcnt_entries(browser); 374 hist_browser__update_nr_entries(browser);
344 nr_entries = browser->nr_pcnt_entries;
345 } else {
346 nr_entries = browser->hists->nr_entries;
347 }
348 375
376 nr_entries = hist_browser__nr_entries(browser);
349 ui_browser__update_nr_entries(&browser->b, nr_entries); 377 ui_browser__update_nr_entries(&browser->b, nr_entries);
350 378
351 if (browser->hists->stats.nr_lost_warned != 379 if (browser->hists->stats.nr_lost_warned !=
@@ -587,35 +615,6 @@ struct hpp_arg {
587 bool current_entry; 615 bool current_entry;
588}; 616};
589 617
590static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
591{
592 struct hpp_arg *arg = hpp->ptr;
593
594 if (arg->current_entry && arg->b->navkeypressed)
595 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
596 else
597 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
598
599 if (front) {
600 if (!symbol_conf.use_callchain)
601 return 0;
602
603 slsmg_printf("%c ", arg->folded_sign);
604 return 2;
605 }
606
607 return 0;
608}
609
610static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
611{
612 struct hpp_arg *arg = hpp->ptr;
613
614 if (!arg->current_entry || !arg->b->navkeypressed)
615 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
616 return 0;
617}
618
619static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 618static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
620{ 619{
621 struct hpp_arg *arg = hpp->ptr; 620 struct hpp_arg *arg = hpp->ptr;
@@ -636,7 +635,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
636 return ret; 635 return ret;
637} 636}
638 637
639#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ 638#define __HPP_COLOR_PERCENT_FN(_type, _field) \
640static u64 __hpp_get_##_field(struct hist_entry *he) \ 639static u64 __hpp_get_##_field(struct hist_entry *he) \
641{ \ 640{ \
642 return he->stat._field; \ 641 return he->stat._field; \
@@ -647,22 +646,43 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
647 struct perf_hpp *hpp, \ 646 struct perf_hpp *hpp, \
648 struct hist_entry *he) \ 647 struct hist_entry *he) \
649{ \ 648{ \
650 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \ 649 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
651 __hpp__slsmg_color_printf, true); \ 650 __hpp__slsmg_color_printf, true); \
652} 651}
653 652
654__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback) 653#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
655__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback) 654static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
656__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback) 655{ \
657__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback) 656 return he->stat_acc->_field; \
658__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback) 657} \
658 \
659static int \
660hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
661 struct perf_hpp *hpp, \
662 struct hist_entry *he) \
663{ \
664 if (!symbol_conf.cumulate_callchain) { \
665 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
666 slsmg_printf("%s", hpp->buf); \
667 \
668 return ret; \
669 } \
670 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
671 __hpp__slsmg_color_printf, true); \
672}
673
674__HPP_COLOR_PERCENT_FN(overhead, period)
675__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
676__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
677__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
678__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
679__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
659 680
660#undef __HPP_COLOR_PERCENT_FN 681#undef __HPP_COLOR_PERCENT_FN
682#undef __HPP_COLOR_ACC_PERCENT_FN
661 683
662void hist_browser__init_hpp(void) 684void hist_browser__init_hpp(void)
663{ 685{
664 perf_hpp__init();
665
666 perf_hpp__format[PERF_HPP__OVERHEAD].color = 686 perf_hpp__format[PERF_HPP__OVERHEAD].color =
667 hist_browser__hpp_color_overhead; 687 hist_browser__hpp_color_overhead;
668 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 688 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
@@ -673,6 +693,8 @@ void hist_browser__init_hpp(void)
673 hist_browser__hpp_color_overhead_guest_sys; 693 hist_browser__hpp_color_overhead_guest_sys;
674 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 694 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
675 hist_browser__hpp_color_overhead_guest_us; 695 hist_browser__hpp_color_overhead_guest_us;
696 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
697 hist_browser__hpp_color_overhead_acc;
676} 698}
677 699
678static int hist_browser__show_entry(struct hist_browser *browser, 700static int hist_browser__show_entry(struct hist_browser *browser,
@@ -700,7 +722,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
700 722
701 if (row_offset == 0) { 723 if (row_offset == 0) {
702 struct hpp_arg arg = { 724 struct hpp_arg arg = {
703 .b = &browser->b, 725 .b = &browser->b,
704 .folded_sign = folded_sign, 726 .folded_sign = folded_sign,
705 .current_entry = current_entry, 727 .current_entry = current_entry,
706 }; 728 };
@@ -713,11 +735,27 @@ static int hist_browser__show_entry(struct hist_browser *browser,
713 ui_browser__gotorc(&browser->b, row, 0); 735 ui_browser__gotorc(&browser->b, row, 0);
714 736
715 perf_hpp__for_each_format(fmt) { 737 perf_hpp__for_each_format(fmt) {
716 if (!first) { 738 if (perf_hpp__should_skip(fmt))
739 continue;
740
741 if (current_entry && browser->b.navkeypressed) {
742 ui_browser__set_color(&browser->b,
743 HE_COLORSET_SELECTED);
744 } else {
745 ui_browser__set_color(&browser->b,
746 HE_COLORSET_NORMAL);
747 }
748
749 if (first) {
750 if (symbol_conf.use_callchain) {
751 slsmg_printf("%c ", folded_sign);
752 width -= 2;
753 }
754 first = false;
755 } else {
717 slsmg_printf(" "); 756 slsmg_printf(" ");
718 width -= 2; 757 width -= 2;
719 } 758 }
720 first = false;
721 759
722 if (fmt->color) { 760 if (fmt->color) {
723 width -= fmt->color(fmt, &hpp, entry); 761 width -= fmt->color(fmt, &hpp, entry);
@@ -731,8 +769,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
731 if (!browser->b.navkeypressed) 769 if (!browser->b.navkeypressed)
732 width += 1; 770 width += 1;
733 771
734 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists); 772 slsmg_write_nstring("", width);
735 slsmg_write_nstring(s, width); 773
736 ++row; 774 ++row;
737 ++printed; 775 ++printed;
738 } else 776 } else
@@ -769,12 +807,12 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
769 807
770 for (nd = browser->top; nd; nd = rb_next(nd)) { 808 for (nd = browser->top; nd; nd = rb_next(nd)) {
771 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 809 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
772 float percent = h->stat.period * 100.0 / 810 float percent;
773 hb->hists->stats.total_period;
774 811
775 if (h->filtered) 812 if (h->filtered)
776 continue; 813 continue;
777 814
815 percent = hist_entry__get_percent_limit(h);
778 if (percent < hb->min_pcnt) 816 if (percent < hb->min_pcnt)
779 continue; 817 continue;
780 818
@@ -787,18 +825,13 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
787} 825}
788 826
789static struct rb_node *hists__filter_entries(struct rb_node *nd, 827static struct rb_node *hists__filter_entries(struct rb_node *nd,
790 struct hists *hists,
791 float min_pcnt) 828 float min_pcnt)
792{ 829{
793 while (nd != NULL) { 830 while (nd != NULL) {
794 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 831 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
795 float percent = h->stat.period * 100.0 / 832 float percent = hist_entry__get_percent_limit(h);
796 hists->stats.total_period;
797
798 if (percent < min_pcnt)
799 return NULL;
800 833
801 if (!h->filtered) 834 if (!h->filtered && percent >= min_pcnt)
802 return nd; 835 return nd;
803 836
804 nd = rb_next(nd); 837 nd = rb_next(nd);
@@ -808,13 +841,11 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
808} 841}
809 842
810static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 843static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
811 struct hists *hists,
812 float min_pcnt) 844 float min_pcnt)
813{ 845{
814 while (nd != NULL) { 846 while (nd != NULL) {
815 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 847 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
816 float percent = h->stat.period * 100.0 / 848 float percent = hist_entry__get_percent_limit(h);
817 hists->stats.total_period;
818 849
819 if (!h->filtered && percent >= min_pcnt) 850 if (!h->filtered && percent >= min_pcnt)
820 return nd; 851 return nd;
@@ -843,14 +874,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
843 switch (whence) { 874 switch (whence) {
844 case SEEK_SET: 875 case SEEK_SET:
845 nd = hists__filter_entries(rb_first(browser->entries), 876 nd = hists__filter_entries(rb_first(browser->entries),
846 hb->hists, hb->min_pcnt); 877 hb->min_pcnt);
847 break; 878 break;
848 case SEEK_CUR: 879 case SEEK_CUR:
849 nd = browser->top; 880 nd = browser->top;
850 goto do_offset; 881 goto do_offset;
851 case SEEK_END: 882 case SEEK_END:
852 nd = hists__filter_prev_entries(rb_last(browser->entries), 883 nd = hists__filter_prev_entries(rb_last(browser->entries),
853 hb->hists, hb->min_pcnt); 884 hb->min_pcnt);
854 first = false; 885 first = false;
855 break; 886 break;
856 default: 887 default:
@@ -893,8 +924,7 @@ do_offset:
893 break; 924 break;
894 } 925 }
895 } 926 }
896 nd = hists__filter_entries(rb_next(nd), hb->hists, 927 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
897 hb->min_pcnt);
898 if (nd == NULL) 928 if (nd == NULL)
899 break; 929 break;
900 --offset; 930 --offset;
@@ -927,7 +957,7 @@ do_offset:
927 } 957 }
928 } 958 }
929 959
930 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists, 960 nd = hists__filter_prev_entries(rb_prev(nd),
931 hb->min_pcnt); 961 hb->min_pcnt);
932 if (nd == NULL) 962 if (nd == NULL)
933 break; 963 break;
@@ -1066,27 +1096,35 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
1066 struct hist_entry *he, FILE *fp) 1096 struct hist_entry *he, FILE *fp)
1067{ 1097{
1068 char s[8192]; 1098 char s[8192];
1069 double percent;
1070 int printed = 0; 1099 int printed = 0;
1071 char folded_sign = ' '; 1100 char folded_sign = ' ';
1101 struct perf_hpp hpp = {
1102 .buf = s,
1103 .size = sizeof(s),
1104 };
1105 struct perf_hpp_fmt *fmt;
1106 bool first = true;
1107 int ret;
1072 1108
1073 if (symbol_conf.use_callchain) 1109 if (symbol_conf.use_callchain)
1074 folded_sign = hist_entry__folded(he); 1110 folded_sign = hist_entry__folded(he);
1075 1111
1076 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1077 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1078
1079 if (symbol_conf.use_callchain) 1112 if (symbol_conf.use_callchain)
1080 printed += fprintf(fp, "%c ", folded_sign); 1113 printed += fprintf(fp, "%c ", folded_sign);
1081 1114
1082 printed += fprintf(fp, " %5.2f%%", percent); 1115 perf_hpp__for_each_format(fmt) {
1083 1116 if (perf_hpp__should_skip(fmt))
1084 if (symbol_conf.show_nr_samples) 1117 continue;
1085 printed += fprintf(fp, " %11u", he->stat.nr_events);
1086 1118
1087 if (symbol_conf.show_total_period) 1119 if (!first) {
1088 printed += fprintf(fp, " %12" PRIu64, he->stat.period); 1120 ret = scnprintf(hpp.buf, hpp.size, " ");
1121 advance_hpp(&hpp, ret);
1122 } else
1123 first = false;
1089 1124
1125 ret = fmt->entry(fmt, &hpp, he);
1126 advance_hpp(&hpp, ret);
1127 }
1090 printed += fprintf(fp, "%s\n", rtrim(s)); 1128 printed += fprintf(fp, "%s\n", rtrim(s));
1091 1129
1092 if (folded_sign == '-') 1130 if (folded_sign == '-')
@@ -1098,7 +1136,6 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
1098static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 1136static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1099{ 1137{
1100 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 1138 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1101 browser->hists,
1102 browser->min_pcnt); 1139 browser->min_pcnt);
1103 int printed = 0; 1140 int printed = 0;
1104 1141
@@ -1106,8 +1143,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1106 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1143 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1107 1144
1108 printed += hist_browser__fprintf_entry(browser, h, fp); 1145 printed += hist_browser__fprintf_entry(browser, h, fp);
1109 nd = hists__filter_entries(rb_next(nd), browser->hists, 1146 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1110 browser->min_pcnt);
1111 } 1147 }
1112 1148
1113 return printed; 1149 return printed;
@@ -1189,6 +1225,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1189 char buf[512]; 1225 char buf[512];
1190 size_t buflen = sizeof(buf); 1226 size_t buflen = sizeof(buf);
1191 1227
1228 if (symbol_conf.filter_relative) {
1229 nr_samples = hists->stats.nr_non_filtered_samples;
1230 nr_events = hists->stats.total_non_filtered_period;
1231 }
1232
1192 if (perf_evsel__is_group_event(evsel)) { 1233 if (perf_evsel__is_group_event(evsel)) {
1193 struct perf_evsel *pos; 1234 struct perf_evsel *pos;
1194 1235
@@ -1196,8 +1237,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1196 ev_name = buf; 1237 ev_name = buf;
1197 1238
1198 for_each_group_member(pos, evsel) { 1239 for_each_group_member(pos, evsel) {
1199 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; 1240 if (symbol_conf.filter_relative) {
1200 nr_events += pos->hists.stats.total_period; 1241 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1242 nr_events += pos->hists.stats.total_non_filtered_period;
1243 } else {
1244 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1245 nr_events += pos->hists.stats.total_period;
1246 }
1201 } 1247 }
1202 } 1248 }
1203 1249
@@ -1324,18 +1370,22 @@ close_file_and_continue:
1324 return ret; 1370 return ret;
1325} 1371}
1326 1372
1327static void hist_browser__update_pcnt_entries(struct hist_browser *hb) 1373static void hist_browser__update_nr_entries(struct hist_browser *hb)
1328{ 1374{
1329 u64 nr_entries = 0; 1375 u64 nr_entries = 0;
1330 struct rb_node *nd = rb_first(&hb->hists->entries); 1376 struct rb_node *nd = rb_first(&hb->hists->entries);
1331 1377
1332 while (nd) { 1378 if (hb->min_pcnt == 0) {
1379 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1380 return;
1381 }
1382
1383 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1333 nr_entries++; 1384 nr_entries++;
1334 nd = hists__filter_entries(rb_next(nd), hb->hists, 1385 nd = rb_next(nd);
1335 hb->min_pcnt);
1336 } 1386 }
1337 1387
1338 hb->nr_pcnt_entries = nr_entries; 1388 hb->nr_non_filtered_entries = nr_entries;
1339} 1389}
1340 1390
1341static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 1391static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
@@ -1370,6 +1420,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1370 "C Collapse all callchains\n" \ 1420 "C Collapse all callchains\n" \
1371 "d Zoom into current DSO\n" \ 1421 "d Zoom into current DSO\n" \
1372 "E Expand all callchains\n" \ 1422 "E Expand all callchains\n" \
1423 "F Toggle percentage of filtered entries\n" \
1373 1424
1374 /* help messages are sorted by lexical order of the hotkey */ 1425 /* help messages are sorted by lexical order of the hotkey */
1375 const char report_help[] = HIST_BROWSER_HELP_COMMON 1426 const char report_help[] = HIST_BROWSER_HELP_COMMON
@@ -1391,7 +1442,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1391 1442
1392 if (min_pcnt) { 1443 if (min_pcnt) {
1393 browser->min_pcnt = min_pcnt; 1444 browser->min_pcnt = min_pcnt;
1394 hist_browser__update_pcnt_entries(browser); 1445 hist_browser__update_nr_entries(browser);
1395 } 1446 }
1396 1447
1397 fstack = pstack__new(2); 1448 fstack = pstack__new(2);
@@ -1475,6 +1526,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1475 if (env->arch) 1526 if (env->arch)
1476 tui__header_window(env); 1527 tui__header_window(env);
1477 continue; 1528 continue;
1529 case 'F':
1530 symbol_conf.filter_relative ^= 1;
1531 continue;
1478 case K_F1: 1532 case K_F1:
1479 case 'h': 1533 case 'h':
1480 case '?': 1534 case '?':
@@ -1652,14 +1706,14 @@ zoom_dso:
1652zoom_out_dso: 1706zoom_out_dso:
1653 ui_helpline__pop(); 1707 ui_helpline__pop();
1654 browser->hists->dso_filter = NULL; 1708 browser->hists->dso_filter = NULL;
1655 sort_dso.elide = false; 1709 perf_hpp__set_elide(HISTC_DSO, false);
1656 } else { 1710 } else {
1657 if (dso == NULL) 1711 if (dso == NULL)
1658 continue; 1712 continue;
1659 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1713 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1660 dso->kernel ? "the Kernel" : dso->short_name); 1714 dso->kernel ? "the Kernel" : dso->short_name);
1661 browser->hists->dso_filter = dso; 1715 browser->hists->dso_filter = dso;
1662 sort_dso.elide = true; 1716 perf_hpp__set_elide(HISTC_DSO, true);
1663 pstack__push(fstack, &browser->hists->dso_filter); 1717 pstack__push(fstack, &browser->hists->dso_filter);
1664 } 1718 }
1665 hists__filter_by_dso(hists); 1719 hists__filter_by_dso(hists);
@@ -1671,13 +1725,13 @@ zoom_thread:
1671zoom_out_thread: 1725zoom_out_thread:
1672 ui_helpline__pop(); 1726 ui_helpline__pop();
1673 browser->hists->thread_filter = NULL; 1727 browser->hists->thread_filter = NULL;
1674 sort_thread.elide = false; 1728 perf_hpp__set_elide(HISTC_THREAD, false);
1675 } else { 1729 } else {
1676 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1730 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1677 thread->comm_set ? thread__comm_str(thread) : "", 1731 thread->comm_set ? thread__comm_str(thread) : "",
1678 thread->tid); 1732 thread->tid);
1679 browser->hists->thread_filter = thread; 1733 browser->hists->thread_filter = thread;
1680 sort_thread.elide = true; 1734 perf_hpp__set_elide(HISTC_THREAD, false);
1681 pstack__push(fstack, &browser->hists->thread_filter); 1735 pstack__push(fstack, &browser->hists->thread_filter);
1682 } 1736 }
1683 hists__filter_by_thread(hists); 1737 hists__filter_by_thread(hists);
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index e395ef9b0ae0..6ca60e482cdc 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -43,23 +43,36 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,
43 struct perf_hpp *hpp, \ 43 struct perf_hpp *hpp, \
44 struct hist_entry *he) \ 44 struct hist_entry *he) \
45{ \ 45{ \
46 return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ 46 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
47 __percent_color_snprintf, true); \ 47 __percent_color_snprintf, true); \
48} 48}
49 49
50#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
51static u64 he_get_acc_##_field(struct hist_entry *he) \
52{ \
53 return he->stat_acc->_field; \
54} \
55 \
56static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
57 struct perf_hpp *hpp, \
58 struct hist_entry *he) \
59{ \
60 return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
61 __percent_color_snprintf, true); \
62}
63
50__HPP_COLOR_PERCENT_FN(overhead, period) 64__HPP_COLOR_PERCENT_FN(overhead, period)
51__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 65__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
52__HPP_COLOR_PERCENT_FN(overhead_us, period_us) 66__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
53__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 67__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
54__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 68__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
69__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
55 70
56#undef __HPP_COLOR_PERCENT_FN 71#undef __HPP_COLOR_PERCENT_FN
57 72
58 73
59void perf_gtk__init_hpp(void) 74void perf_gtk__init_hpp(void)
60{ 75{
61 perf_hpp__init();
62
63 perf_hpp__format[PERF_HPP__OVERHEAD].color = 76 perf_hpp__format[PERF_HPP__OVERHEAD].color =
64 perf_gtk__hpp_color_overhead; 77 perf_gtk__hpp_color_overhead;
65 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 78 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
@@ -70,6 +83,8 @@ void perf_gtk__init_hpp(void)
70 perf_gtk__hpp_color_overhead_guest_sys; 83 perf_gtk__hpp_color_overhead_guest_sys;
71 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 84 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
72 perf_gtk__hpp_color_overhead_guest_us; 85 perf_gtk__hpp_color_overhead_guest_us;
86 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
87 perf_gtk__hpp_color_overhead_acc;
73} 88}
74 89
75static void callchain_list__sym_name(struct callchain_list *cl, 90static void callchain_list__sym_name(struct callchain_list *cl,
@@ -153,7 +168,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
153 struct perf_hpp_fmt *fmt; 168 struct perf_hpp_fmt *fmt;
154 GType col_types[MAX_COLUMNS]; 169 GType col_types[MAX_COLUMNS];
155 GtkCellRenderer *renderer; 170 GtkCellRenderer *renderer;
156 struct sort_entry *se;
157 GtkTreeStore *store; 171 GtkTreeStore *store;
158 struct rb_node *nd; 172 struct rb_node *nd;
159 GtkWidget *view; 173 GtkWidget *view;
@@ -172,16 +186,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
172 perf_hpp__for_each_format(fmt) 186 perf_hpp__for_each_format(fmt)
173 col_types[nr_cols++] = G_TYPE_STRING; 187 col_types[nr_cols++] = G_TYPE_STRING;
174 188
175 list_for_each_entry(se, &hist_entry__sort_list, list) {
176 if (se->elide)
177 continue;
178
179 if (se == &sort_sym)
180 sym_col = nr_cols;
181
182 col_types[nr_cols++] = G_TYPE_STRING;
183 }
184
185 store = gtk_tree_store_newv(nr_cols, col_types); 189 store = gtk_tree_store_newv(nr_cols, col_types);
186 190
187 view = gtk_tree_view_new(); 191 view = gtk_tree_view_new();
@@ -191,6 +195,16 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
191 col_idx = 0; 195 col_idx = 0;
192 196
193 perf_hpp__for_each_format(fmt) { 197 perf_hpp__for_each_format(fmt) {
198 if (perf_hpp__should_skip(fmt))
199 continue;
200
201 /*
202 * XXX no way to determine where symcol column is..
203 * Just use last column for now.
204 */
205 if (perf_hpp__is_sort_entry(fmt))
206 sym_col = col_idx;
207
194 fmt->header(fmt, &hpp, hists_to_evsel(hists)); 208 fmt->header(fmt, &hpp, hists_to_evsel(hists));
195 209
196 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 210 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
@@ -199,16 +213,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
199 col_idx++, NULL); 213 col_idx++, NULL);
200 } 214 }
201 215
202 list_for_each_entry(se, &hist_entry__sort_list, list) {
203 if (se->elide)
204 continue;
205
206 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
207 -1, se->se_header,
208 renderer, "text",
209 col_idx++, NULL);
210 }
211
212 for (col_idx = 0; col_idx < nr_cols; col_idx++) { 216 for (col_idx = 0; col_idx < nr_cols; col_idx++) {
213 GtkTreeViewColumn *column; 217 GtkTreeViewColumn *column;
214 218
@@ -228,12 +232,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
228 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 232 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
229 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 233 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
230 GtkTreeIter iter; 234 GtkTreeIter iter;
231 float percent = h->stat.period * 100.0 / 235 u64 total = hists__total_period(h->hists);
232 hists->stats.total_period; 236 float percent;
233 237
234 if (h->filtered) 238 if (h->filtered)
235 continue; 239 continue;
236 240
241 percent = hist_entry__get_percent_limit(h);
237 if (percent < min_pcnt) 242 if (percent < min_pcnt)
238 continue; 243 continue;
239 244
@@ -242,6 +247,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
242 col_idx = 0; 247 col_idx = 0;
243 248
244 perf_hpp__for_each_format(fmt) { 249 perf_hpp__for_each_format(fmt) {
250 if (perf_hpp__should_skip(fmt))
251 continue;
252
245 if (fmt->color) 253 if (fmt->color)
246 fmt->color(fmt, &hpp, h); 254 fmt->color(fmt, &hpp, h);
247 else 255 else
@@ -250,23 +258,10 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
250 gtk_tree_store_set(store, &iter, col_idx++, s, -1); 258 gtk_tree_store_set(store, &iter, col_idx++, s, -1);
251 } 259 }
252 260
253 list_for_each_entry(se, &hist_entry__sort_list, list) {
254 if (se->elide)
255 continue;
256
257 se->se_snprintf(h, s, ARRAY_SIZE(s),
258 hists__col_len(hists, se->se_width_idx));
259
260 gtk_tree_store_set(store, &iter, col_idx++, s, -1);
261 }
262
263 if (symbol_conf.use_callchain && sort__has_sym) { 261 if (symbol_conf.use_callchain && sort__has_sym) {
264 u64 total;
265
266 if (callchain_param.mode == CHAIN_GRAPH_REL) 262 if (callchain_param.mode == CHAIN_GRAPH_REL)
267 total = h->stat.period; 263 total = symbol_conf.cumulate_callchain ?
268 else 264 h->stat_acc->period : h->stat.period;
269 total = hists->stats.total_period;
270 265
271 perf_gtk__add_callchain(&h->sorted_chain, store, &iter, 266 perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
272 sym_col, total); 267 sym_col, total);
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 0f403b83e9d1..498adb23c02e 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -16,30 +16,25 @@
16}) 16})
17 17
18int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 18int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
19 hpp_field_fn get_field, hpp_callback_fn callback, 19 hpp_field_fn get_field, const char *fmt,
20 const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent) 20 hpp_snprint_fn print_fn, bool fmt_percent)
21{ 21{
22 int ret = 0; 22 int ret;
23 struct hists *hists = he->hists; 23 struct hists *hists = he->hists;
24 struct perf_evsel *evsel = hists_to_evsel(hists); 24 struct perf_evsel *evsel = hists_to_evsel(hists);
25 char *buf = hpp->buf; 25 char *buf = hpp->buf;
26 size_t size = hpp->size; 26 size_t size = hpp->size;
27 27
28 if (callback) {
29 ret = callback(hpp, true);
30 advance_hpp(hpp, ret);
31 }
32
33 if (fmt_percent) { 28 if (fmt_percent) {
34 double percent = 0.0; 29 double percent = 0.0;
30 u64 total = hists__total_period(hists);
35 31
36 if (hists->stats.total_period) 32 if (total)
37 percent = 100.0 * get_field(he) / 33 percent = 100.0 * get_field(he) / total;
38 hists->stats.total_period;
39 34
40 ret += hpp__call_print_fn(hpp, print_fn, fmt, percent); 35 ret = hpp__call_print_fn(hpp, print_fn, fmt, percent);
41 } else 36 } else
42 ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); 37 ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
43 38
44 if (perf_evsel__is_group_event(evsel)) { 39 if (perf_evsel__is_group_event(evsel)) {
45 int prev_idx, idx_delta; 40 int prev_idx, idx_delta;
@@ -50,7 +45,7 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
50 45
51 list_for_each_entry(pair, &he->pairs.head, pairs.node) { 46 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
52 u64 period = get_field(pair); 47 u64 period = get_field(pair);
53 u64 total = pair->hists->stats.total_period; 48 u64 total = hists__total_period(pair->hists);
54 49
55 if (!total) 50 if (!total)
56 continue; 51 continue;
@@ -99,13 +94,6 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
99 } 94 }
100 } 95 }
101 96
102 if (callback) {
103 int __ret = callback(hpp, false);
104
105 advance_hpp(hpp, __ret);
106 ret += __ret;
107 }
108
109 /* 97 /*
110 * Restore original buf and size as it's where caller expects 98 * Restore original buf and size as it's where caller expects
111 * the result will be saved. 99 * the result will be saved.
@@ -116,6 +104,92 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
116 return ret; 104 return ret;
117} 105}
118 106
107int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
108 hpp_field_fn get_field, const char *fmt,
109 hpp_snprint_fn print_fn, bool fmt_percent)
110{
111 if (!symbol_conf.cumulate_callchain) {
112 return snprintf(hpp->buf, hpp->size, "%*s",
113 fmt_percent ? 8 : 12, "N/A");
114 }
115
116 return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent);
117}
118
119static int field_cmp(u64 field_a, u64 field_b)
120{
121 if (field_a > field_b)
122 return 1;
123 if (field_a < field_b)
124 return -1;
125 return 0;
126}
127
128static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
129 hpp_field_fn get_field)
130{
131 s64 ret;
132 int i, nr_members;
133 struct perf_evsel *evsel;
134 struct hist_entry *pair;
135 u64 *fields_a, *fields_b;
136
137 ret = field_cmp(get_field(a), get_field(b));
138 if (ret || !symbol_conf.event_group)
139 return ret;
140
141 evsel = hists_to_evsel(a->hists);
142 if (!perf_evsel__is_group_event(evsel))
143 return ret;
144
145 nr_members = evsel->nr_members;
146 fields_a = calloc(sizeof(*fields_a), nr_members);
147 fields_b = calloc(sizeof(*fields_b), nr_members);
148
149 if (!fields_a || !fields_b)
150 goto out;
151
152 list_for_each_entry(pair, &a->pairs.head, pairs.node) {
153 evsel = hists_to_evsel(pair->hists);
154 fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
155 }
156
157 list_for_each_entry(pair, &b->pairs.head, pairs.node) {
158 evsel = hists_to_evsel(pair->hists);
159 fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
160 }
161
162 for (i = 1; i < nr_members; i++) {
163 ret = field_cmp(fields_a[i], fields_b[i]);
164 if (ret)
165 break;
166 }
167
168out:
169 free(fields_a);
170 free(fields_b);
171
172 return ret;
173}
174
175static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
176 hpp_field_fn get_field)
177{
178 s64 ret = 0;
179
180 if (symbol_conf.cumulate_callchain) {
181 /*
182 * Put caller above callee when they have equal period.
183 */
184 ret = field_cmp(get_field(a), get_field(b));
185 if (ret)
186 return ret;
187
188 ret = b->callchain->max_depth - a->callchain->max_depth;
189 }
190 return ret;
191}
192
119#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 193#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
120static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 194static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
121 struct perf_hpp *hpp, \ 195 struct perf_hpp *hpp, \
@@ -179,7 +253,7 @@ static u64 he_get_##_field(struct hist_entry *he) \
179static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ 253static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
180 struct perf_hpp *hpp, struct hist_entry *he) \ 254 struct perf_hpp *hpp, struct hist_entry *he) \
181{ \ 255{ \
182 return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ 256 return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
183 hpp_color_scnprintf, true); \ 257 hpp_color_scnprintf, true); \
184} 258}
185 259
@@ -188,10 +262,44 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
188 struct perf_hpp *hpp, struct hist_entry *he) \ 262 struct perf_hpp *hpp, struct hist_entry *he) \
189{ \ 263{ \
190 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ 264 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
191 return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt, \ 265 return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
192 hpp_entry_scnprintf, true); \ 266 hpp_entry_scnprintf, true); \
193} 267}
194 268
269#define __HPP_SORT_FN(_type, _field) \
270static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
271{ \
272 return __hpp__sort(a, b, he_get_##_field); \
273}
274
275#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
276static u64 he_get_acc_##_field(struct hist_entry *he) \
277{ \
278 return he->stat_acc->_field; \
279} \
280 \
281static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
282 struct perf_hpp *hpp, struct hist_entry *he) \
283{ \
284 return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \
285 hpp_color_scnprintf, true); \
286}
287
288#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
289static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
290 struct perf_hpp *hpp, struct hist_entry *he) \
291{ \
292 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
293 return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \
294 hpp_entry_scnprintf, true); \
295}
296
297#define __HPP_SORT_ACC_FN(_type, _field) \
298static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
299{ \
300 return __hpp__sort_acc(a, b, he_get_acc_##_field); \
301}
302
195#define __HPP_ENTRY_RAW_FN(_type, _field) \ 303#define __HPP_ENTRY_RAW_FN(_type, _field) \
196static u64 he_get_raw_##_field(struct hist_entry *he) \ 304static u64 he_get_raw_##_field(struct hist_entry *he) \
197{ \ 305{ \
@@ -202,44 +310,85 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
202 struct perf_hpp *hpp, struct hist_entry *he) \ 310 struct perf_hpp *hpp, struct hist_entry *he) \
203{ \ 311{ \
204 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ 312 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
205 return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt, \ 313 return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \
206 hpp_entry_scnprintf, false); \ 314 hpp_entry_scnprintf, false); \
207} 315}
208 316
317#define __HPP_SORT_RAW_FN(_type, _field) \
318static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \
319{ \
320 return __hpp__sort(a, b, he_get_raw_##_field); \
321}
322
323
209#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ 324#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
210__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 325__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
211__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 326__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
212__HPP_COLOR_PERCENT_FN(_type, _field) \ 327__HPP_COLOR_PERCENT_FN(_type, _field) \
213__HPP_ENTRY_PERCENT_FN(_type, _field) 328__HPP_ENTRY_PERCENT_FN(_type, _field) \
329__HPP_SORT_FN(_type, _field)
330
331#define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\
332__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
333__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
334__HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
335__HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \
336__HPP_SORT_ACC_FN(_type, _field)
214 337
215#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ 338#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
216__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ 339__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
217__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ 340__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
218__HPP_ENTRY_RAW_FN(_type, _field) 341__HPP_ENTRY_RAW_FN(_type, _field) \
342__HPP_SORT_RAW_FN(_type, _field)
219 343
344__HPP_HEADER_FN(overhead_self, "Self", 8, 8)
220 345
221HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) 346HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
222HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) 347HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
223HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) 348HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
224HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) 349HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
225HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) 350HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
351HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8)
226 352
227HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) 353HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
228HPP_RAW_FNS(period, "Period", period, 12, 12) 354HPP_RAW_FNS(period, "Period", period, 12, 12)
229 355
356static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
357 struct hist_entry *b __maybe_unused)
358{
359 return 0;
360}
361
230#define HPP__COLOR_PRINT_FNS(_name) \ 362#define HPP__COLOR_PRINT_FNS(_name) \
231 { \ 363 { \
232 .header = hpp__header_ ## _name, \ 364 .header = hpp__header_ ## _name, \
233 .width = hpp__width_ ## _name, \ 365 .width = hpp__width_ ## _name, \
234 .color = hpp__color_ ## _name, \ 366 .color = hpp__color_ ## _name, \
235 .entry = hpp__entry_ ## _name \ 367 .entry = hpp__entry_ ## _name, \
368 .cmp = hpp__nop_cmp, \
369 .collapse = hpp__nop_cmp, \
370 .sort = hpp__sort_ ## _name, \
371 }
372
373#define HPP__COLOR_ACC_PRINT_FNS(_name) \
374 { \
375 .header = hpp__header_ ## _name, \
376 .width = hpp__width_ ## _name, \
377 .color = hpp__color_ ## _name, \
378 .entry = hpp__entry_ ## _name, \
379 .cmp = hpp__nop_cmp, \
380 .collapse = hpp__nop_cmp, \
381 .sort = hpp__sort_ ## _name, \
236 } 382 }
237 383
238#define HPP__PRINT_FNS(_name) \ 384#define HPP__PRINT_FNS(_name) \
239 { \ 385 { \
240 .header = hpp__header_ ## _name, \ 386 .header = hpp__header_ ## _name, \
241 .width = hpp__width_ ## _name, \ 387 .width = hpp__width_ ## _name, \
242 .entry = hpp__entry_ ## _name \ 388 .entry = hpp__entry_ ## _name, \
389 .cmp = hpp__nop_cmp, \
390 .collapse = hpp__nop_cmp, \
391 .sort = hpp__sort_ ## _name, \
243 } 392 }
244 393
245struct perf_hpp_fmt perf_hpp__format[] = { 394struct perf_hpp_fmt perf_hpp__format[] = {
@@ -248,28 +397,63 @@ struct perf_hpp_fmt perf_hpp__format[] = {
248 HPP__COLOR_PRINT_FNS(overhead_us), 397 HPP__COLOR_PRINT_FNS(overhead_us),
249 HPP__COLOR_PRINT_FNS(overhead_guest_sys), 398 HPP__COLOR_PRINT_FNS(overhead_guest_sys),
250 HPP__COLOR_PRINT_FNS(overhead_guest_us), 399 HPP__COLOR_PRINT_FNS(overhead_guest_us),
400 HPP__COLOR_ACC_PRINT_FNS(overhead_acc),
251 HPP__PRINT_FNS(samples), 401 HPP__PRINT_FNS(samples),
252 HPP__PRINT_FNS(period) 402 HPP__PRINT_FNS(period)
253}; 403};
254 404
255LIST_HEAD(perf_hpp__list); 405LIST_HEAD(perf_hpp__list);
406LIST_HEAD(perf_hpp__sort_list);
256 407
257 408
258#undef HPP__COLOR_PRINT_FNS 409#undef HPP__COLOR_PRINT_FNS
410#undef HPP__COLOR_ACC_PRINT_FNS
259#undef HPP__PRINT_FNS 411#undef HPP__PRINT_FNS
260 412
261#undef HPP_PERCENT_FNS 413#undef HPP_PERCENT_FNS
414#undef HPP_PERCENT_ACC_FNS
262#undef HPP_RAW_FNS 415#undef HPP_RAW_FNS
263 416
264#undef __HPP_HEADER_FN 417#undef __HPP_HEADER_FN
265#undef __HPP_WIDTH_FN 418#undef __HPP_WIDTH_FN
266#undef __HPP_COLOR_PERCENT_FN 419#undef __HPP_COLOR_PERCENT_FN
267#undef __HPP_ENTRY_PERCENT_FN 420#undef __HPP_ENTRY_PERCENT_FN
421#undef __HPP_COLOR_ACC_PERCENT_FN
422#undef __HPP_ENTRY_ACC_PERCENT_FN
268#undef __HPP_ENTRY_RAW_FN 423#undef __HPP_ENTRY_RAW_FN
424#undef __HPP_SORT_FN
425#undef __HPP_SORT_ACC_FN
426#undef __HPP_SORT_RAW_FN
269 427
270 428
271void perf_hpp__init(void) 429void perf_hpp__init(void)
272{ 430{
431 struct list_head *list;
432 int i;
433
434 for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
435 struct perf_hpp_fmt *fmt = &perf_hpp__format[i];
436
437 INIT_LIST_HEAD(&fmt->list);
438
439 /* sort_list may be linked by setup_sorting() */
440 if (fmt->sort_list.next == NULL)
441 INIT_LIST_HEAD(&fmt->sort_list);
442 }
443
444 /*
445 * If user specified field order, no need to setup default fields.
446 */
447 if (field_order)
448 return;
449
450 if (symbol_conf.cumulate_callchain) {
451 perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC);
452
453 perf_hpp__format[PERF_HPP__OVERHEAD].header =
454 hpp__header_overhead_self;
455 }
456
273 perf_hpp__column_enable(PERF_HPP__OVERHEAD); 457 perf_hpp__column_enable(PERF_HPP__OVERHEAD);
274 458
275 if (symbol_conf.show_cpu_utilization) { 459 if (symbol_conf.show_cpu_utilization) {
@@ -287,6 +471,17 @@ void perf_hpp__init(void)
287 471
288 if (symbol_conf.show_total_period) 472 if (symbol_conf.show_total_period)
289 perf_hpp__column_enable(PERF_HPP__PERIOD); 473 perf_hpp__column_enable(PERF_HPP__PERIOD);
474
475 /* prepend overhead field for backward compatiblity. */
476 list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
477 if (list_empty(list))
478 list_add(list, &perf_hpp__sort_list);
479
480 if (symbol_conf.cumulate_callchain) {
481 list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list;
482 if (list_empty(list))
483 list_add(list, &perf_hpp__sort_list);
484 }
290} 485}
291 486
292void perf_hpp__column_register(struct perf_hpp_fmt *format) 487void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -294,29 +489,110 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format)
294 list_add_tail(&format->list, &perf_hpp__list); 489 list_add_tail(&format->list, &perf_hpp__list);
295} 490}
296 491
492void perf_hpp__column_unregister(struct perf_hpp_fmt *format)
493{
494 list_del(&format->list);
495}
496
497void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
498{
499 list_add_tail(&format->sort_list, &perf_hpp__sort_list);
500}
501
297void perf_hpp__column_enable(unsigned col) 502void perf_hpp__column_enable(unsigned col)
298{ 503{
299 BUG_ON(col >= PERF_HPP__MAX_INDEX); 504 BUG_ON(col >= PERF_HPP__MAX_INDEX);
300 perf_hpp__column_register(&perf_hpp__format[col]); 505 perf_hpp__column_register(&perf_hpp__format[col]);
301} 506}
302 507
303int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, 508void perf_hpp__column_disable(unsigned col)
304 struct hists *hists)
305{ 509{
306 const char *sep = symbol_conf.field_sep; 510 BUG_ON(col >= PERF_HPP__MAX_INDEX);
307 struct sort_entry *se; 511 perf_hpp__column_unregister(&perf_hpp__format[col]);
308 int ret = 0; 512}
513
514void perf_hpp__cancel_cumulate(void)
515{
516 if (field_order)
517 return;
309 518
310 list_for_each_entry(se, &hist_entry__sort_list, list) { 519 perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
311 if (se->elide) 520 perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead;
521}
522
523void perf_hpp__setup_output_field(void)
524{
525 struct perf_hpp_fmt *fmt;
526
527 /* append sort keys to output field */
528 perf_hpp__for_each_sort_list(fmt) {
529 if (!list_empty(&fmt->list))
312 continue; 530 continue;
313 531
314 ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); 532 /*
315 ret += se->se_snprintf(he, s + ret, size - ret, 533 * sort entry fields are dynamically created,
316 hists__col_len(hists, se->se_width_idx)); 534 * so they can share a same sort key even though
535 * the list is empty.
536 */
537 if (perf_hpp__is_sort_entry(fmt)) {
538 struct perf_hpp_fmt *pos;
539
540 perf_hpp__for_each_format(pos) {
541 if (perf_hpp__same_sort_entry(pos, fmt))
542 goto next;
543 }
544 }
545
546 perf_hpp__column_register(fmt);
547next:
548 continue;
317 } 549 }
550}
318 551
319 return ret; 552void perf_hpp__append_sort_keys(void)
553{
554 struct perf_hpp_fmt *fmt;
555
556 /* append output fields to sort keys */
557 perf_hpp__for_each_format(fmt) {
558 if (!list_empty(&fmt->sort_list))
559 continue;
560
561 /*
562 * sort entry fields are dynamically created,
563 * so they can share a same sort key even though
564 * the list is empty.
565 */
566 if (perf_hpp__is_sort_entry(fmt)) {
567 struct perf_hpp_fmt *pos;
568
569 perf_hpp__for_each_sort_list(pos) {
570 if (perf_hpp__same_sort_entry(pos, fmt))
571 goto next;
572 }
573 }
574
575 perf_hpp__register_sort_field(fmt);
576next:
577 continue;
578 }
579}
580
581void perf_hpp__reset_output_field(void)
582{
583 struct perf_hpp_fmt *fmt, *tmp;
584
585 /* reset output fields */
586 perf_hpp__for_each_format_safe(fmt, tmp) {
587 list_del_init(&fmt->list);
588 list_del_init(&fmt->sort_list);
589 }
590
591 /* reset sort keys */
592 perf_hpp__for_each_sort_list_safe(fmt, tmp) {
593 list_del_init(&fmt->list);
594 list_del_init(&fmt->sort_list);
595 }
320} 596}
321 597
322/* 598/*
@@ -325,22 +601,23 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
325unsigned int hists__sort_list_width(struct hists *hists) 601unsigned int hists__sort_list_width(struct hists *hists)
326{ 602{
327 struct perf_hpp_fmt *fmt; 603 struct perf_hpp_fmt *fmt;
328 struct sort_entry *se; 604 int ret = 0;
329 int i = 0, ret = 0; 605 bool first = true;
330 struct perf_hpp dummy_hpp; 606 struct perf_hpp dummy_hpp;
331 607
332 perf_hpp__for_each_format(fmt) { 608 perf_hpp__for_each_format(fmt) {
333 if (i) 609 if (perf_hpp__should_skip(fmt))
610 continue;
611
612 if (first)
613 first = false;
614 else
334 ret += 2; 615 ret += 2;
335 616
336 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); 617 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
337 } 618 }
338 619
339 list_for_each_entry(se, &hist_entry__sort_list, list) 620 if (verbose && sort__has_sym) /* Addr + origin */
340 if (!se->elide)
341 ret += 2 + hists__col_len(hists, se->se_width_idx);
342
343 if (verbose) /* Addr + origin */
344 ret += 3 + BITS_PER_LONG / 4; 621 ret += 3 + BITS_PER_LONG / 4;
345 622
346 return ret; 623 return ret;
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h
index 29ec8efffefb..f34f89eb607c 100644
--- a/tools/perf/ui/progress.h
+++ b/tools/perf/ui/progress.h
@@ -1,7 +1,7 @@
1#ifndef _PERF_UI_PROGRESS_H_ 1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1 2#define _PERF_UI_PROGRESS_H_ 1
3 3
4#include <../types.h> 4#include <linux/types.h>
5 5
6void ui_progress__finish(void); 6void ui_progress__finish(void);
7 7
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index 5df5140a9f29..ba51fa8a1176 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -86,8 +86,6 @@ void setup_browser(bool fallback_to_pager)
86 use_browser = 0; 86 use_browser = 0;
87 if (fallback_to_pager) 87 if (fallback_to_pager)
88 setup_pager(); 88 setup_pager();
89
90 perf_hpp__init();
91 break; 89 break;
92 } 90 }
93} 91}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index d59893edf031..90122abd3721 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
183 * the symbol. No need to print it otherwise it appears as 183 * the symbol. No need to print it otherwise it appears as
184 * displayed twice. 184 * displayed twice.
185 */ 185 */
186 if (!i++ && sort__first_dimension == SORT_SYM) 186 if (!i++ && field_order == NULL &&
187 sort_order && !prefixcmp(sort_order, "sym"))
187 continue; 188 continue;
188 if (!printed) { 189 if (!printed) {
189 ret += callchain__fprintf_left_margin(fp, left_margin); 190 ret += callchain__fprintf_left_margin(fp, left_margin);
@@ -270,7 +271,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
270{ 271{
271 switch (callchain_param.mode) { 272 switch (callchain_param.mode) {
272 case CHAIN_GRAPH_REL: 273 case CHAIN_GRAPH_REL:
273 return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period, 274 return callchain__fprintf_graph(fp, &he->sorted_chain,
275 symbol_conf.cumulate_callchain ?
276 he->stat_acc->period : he->stat.period,
274 left_margin); 277 left_margin);
275 break; 278 break;
276 case CHAIN_GRAPH_ABS: 279 case CHAIN_GRAPH_ABS:
@@ -296,18 +299,24 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
296 int left_margin = 0; 299 int left_margin = 0;
297 u64 total_period = hists->stats.total_period; 300 u64 total_period = hists->stats.total_period;
298 301
299 if (sort__first_dimension == SORT_COMM) { 302 if (field_order == NULL && (sort_order == NULL ||
300 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 303 !prefixcmp(sort_order, "comm"))) {
301 typeof(*se), list); 304 struct perf_hpp_fmt *fmt;
302 left_margin = hists__col_len(hists, se->se_width_idx); 305
303 left_margin -= thread__comm_len(he->thread); 306 perf_hpp__for_each_format(fmt) {
304 } 307 if (!perf_hpp__is_sort_entry(fmt))
308 continue;
305 309
310 /* must be 'comm' sort entry */
311 left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists));
312 left_margin -= thread__comm_len(he->thread);
313 break;
314 }
315 }
306 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); 316 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
307} 317}
308 318
309static int hist_entry__period_snprintf(struct perf_hpp *hpp, 319static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
310 struct hist_entry *he)
311{ 320{
312 const char *sep = symbol_conf.field_sep; 321 const char *sep = symbol_conf.field_sep;
313 struct perf_hpp_fmt *fmt; 322 struct perf_hpp_fmt *fmt;
@@ -319,6 +328,9 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp,
319 return 0; 328 return 0;
320 329
321 perf_hpp__for_each_format(fmt) { 330 perf_hpp__for_each_format(fmt) {
331 if (perf_hpp__should_skip(fmt))
332 continue;
333
322 /* 334 /*
323 * If there's no field_sep, we still need 335 * If there's no field_sep, we still need
324 * to display initial ' '. 336 * to display initial ' '.
@@ -353,8 +365,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
353 if (size == 0 || size > bfsz) 365 if (size == 0 || size > bfsz)
354 size = hpp.size = bfsz; 366 size = hpp.size = bfsz;
355 367
356 ret = hist_entry__period_snprintf(&hpp, he); 368 hist_entry__snprintf(he, &hpp);
357 hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
358 369
359 ret = fprintf(fp, "%s\n", bf); 370 ret = fprintf(fp, "%s\n", bf);
360 371
@@ -368,12 +379,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
368 int max_cols, float min_pcnt, FILE *fp) 379 int max_cols, float min_pcnt, FILE *fp)
369{ 380{
370 struct perf_hpp_fmt *fmt; 381 struct perf_hpp_fmt *fmt;
371 struct sort_entry *se;
372 struct rb_node *nd; 382 struct rb_node *nd;
373 size_t ret = 0; 383 size_t ret = 0;
374 unsigned int width; 384 unsigned int width;
375 const char *sep = symbol_conf.field_sep; 385 const char *sep = symbol_conf.field_sep;
376 const char *col_width = symbol_conf.col_width_list_str;
377 int nr_rows = 0; 386 int nr_rows = 0;
378 char bf[96]; 387 char bf[96];
379 struct perf_hpp dummy_hpp = { 388 struct perf_hpp dummy_hpp = {
@@ -386,12 +395,19 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
386 395
387 init_rem_hits(); 396 init_rem_hits();
388 397
398
399 perf_hpp__for_each_format(fmt)
400 perf_hpp__reset_width(fmt, hists);
401
389 if (!show_header) 402 if (!show_header)
390 goto print_entries; 403 goto print_entries;
391 404
392 fprintf(fp, "# "); 405 fprintf(fp, "# ");
393 406
394 perf_hpp__for_each_format(fmt) { 407 perf_hpp__for_each_format(fmt) {
408 if (perf_hpp__should_skip(fmt))
409 continue;
410
395 if (!first) 411 if (!first)
396 fprintf(fp, "%s", sep ?: " "); 412 fprintf(fp, "%s", sep ?: " ");
397 else 413 else
@@ -401,28 +417,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
401 fprintf(fp, "%s", bf); 417 fprintf(fp, "%s", bf);
402 } 418 }
403 419
404 list_for_each_entry(se, &hist_entry__sort_list, list) {
405 if (se->elide)
406 continue;
407 if (sep) {
408 fprintf(fp, "%c%s", *sep, se->se_header);
409 continue;
410 }
411 width = strlen(se->se_header);
412 if (symbol_conf.col_width_list_str) {
413 if (col_width) {
414 hists__set_col_len(hists, se->se_width_idx,
415 atoi(col_width));
416 col_width = strchr(col_width, ',');
417 if (col_width)
418 ++col_width;
419 }
420 }
421 if (!hists__new_col_len(hists, se->se_width_idx, width))
422 width = hists__col_len(hists, se->se_width_idx);
423 fprintf(fp, " %*s", width, se->se_header);
424 }
425
426 fprintf(fp, "\n"); 420 fprintf(fp, "\n");
427 if (max_rows && ++nr_rows >= max_rows) 421 if (max_rows && ++nr_rows >= max_rows)
428 goto out; 422 goto out;
@@ -437,6 +431,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
437 perf_hpp__for_each_format(fmt) { 431 perf_hpp__for_each_format(fmt) {
438 unsigned int i; 432 unsigned int i;
439 433
434 if (perf_hpp__should_skip(fmt))
435 continue;
436
440 if (!first) 437 if (!first)
441 fprintf(fp, "%s", sep ?: " "); 438 fprintf(fp, "%s", sep ?: " ");
442 else 439 else
@@ -447,20 +444,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
447 fprintf(fp, "."); 444 fprintf(fp, ".");
448 } 445 }
449 446
450 list_for_each_entry(se, &hist_entry__sort_list, list) {
451 unsigned int i;
452
453 if (se->elide)
454 continue;
455
456 fprintf(fp, " ");
457 width = hists__col_len(hists, se->se_width_idx);
458 if (width == 0)
459 width = strlen(se->se_header);
460 for (i = 0; i < width; i++)
461 fprintf(fp, ".");
462 }
463
464 fprintf(fp, "\n"); 447 fprintf(fp, "\n");
465 if (max_rows && ++nr_rows >= max_rows) 448 if (max_rows && ++nr_rows >= max_rows)
466 goto out; 449 goto out;
@@ -480,12 +463,12 @@ print_entries:
480 463
481 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 464 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
482 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 465 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
483 float percent = h->stat.period * 100.0 / 466 float percent;
484 hists->stats.total_period;
485 467
486 if (h->filtered) 468 if (h->filtered)
487 continue; 469 continue;
488 470
471 percent = hist_entry__get_percent_limit(h);
489 if (percent < min_pcnt) 472 if (percent < min_pcnt)
490 continue; 473 continue;
491 474
@@ -495,7 +478,7 @@ print_entries:
495 break; 478 break;
496 479
497 if (h->ms.map == NULL && verbose > 1) { 480 if (h->ms.map == NULL && verbose > 1) {
498 __map_groups__fprintf_maps(&h->thread->mg, 481 __map_groups__fprintf_maps(h->thread->mg,
499 MAP__FUNCTION, verbose, fp); 482 MAP__FUNCTION, verbose, fp);
500 fprintf(fp, "%.10s end\n", graph_dotted_line); 483 fprintf(fp, "%.10s end\n", graph_dotted_line);
501 } 484 }
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 56ad4f5287de..112d6e268150 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -3,7 +3,7 @@
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <stdint.h> 5#include <stdint.h>
6#include "types.h" 6#include <linux/types.h>
7#include "symbol.h" 7#include "symbol.h"
8#include "hist.h" 8#include "hist.h"
9#include "sort.h" 9#include "sort.h"
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 6baabe63182b..a904a4cfe7d3 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
25 struct addr_location al; 25 struct addr_location al;
26 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 26 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
27 struct thread *thread = machine__findnew_thread(machine, sample->pid, 27 struct thread *thread = machine__findnew_thread(machine, sample->pid,
28 sample->pid); 28 sample->tid);
29 29
30 if (thread == NULL) { 30 if (thread == NULL) {
31 pr_err("problem processing %d event, skipping it.\n", 31 pr_err("problem processing %d event, skipping it.\n",
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 845ef865eced..ae392561470b 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -4,7 +4,7 @@
4#define BUILD_ID_SIZE 20 4#define BUILD_ID_SIZE 20
5 5
6#include "tool.h" 6#include "tool.h"
7#include "types.h" 7#include <linux/types.h>
8 8
9extern struct perf_tool build_id__mark_dso_hit_ops; 9extern struct perf_tool build_id__mark_dso_hit_ops;
10struct dso; 10struct dso;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 8d9db454f1a9..48b6d3f50012 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -25,6 +25,84 @@
25 25
26__thread struct callchain_cursor callchain_cursor; 26__thread struct callchain_cursor callchain_cursor;
27 27
28int
29parse_callchain_report_opt(const char *arg)
30{
31 char *tok, *tok2;
32 char *endptr;
33
34 symbol_conf.use_callchain = true;
35
36 if (!arg)
37 return 0;
38
39 tok = strtok((char *)arg, ",");
40 if (!tok)
41 return -1;
42
43 /* get the output mode */
44 if (!strncmp(tok, "graph", strlen(arg))) {
45 callchain_param.mode = CHAIN_GRAPH_ABS;
46
47 } else if (!strncmp(tok, "flat", strlen(arg))) {
48 callchain_param.mode = CHAIN_FLAT;
49 } else if (!strncmp(tok, "fractal", strlen(arg))) {
50 callchain_param.mode = CHAIN_GRAPH_REL;
51 } else if (!strncmp(tok, "none", strlen(arg))) {
52 callchain_param.mode = CHAIN_NONE;
53 symbol_conf.use_callchain = false;
54 return 0;
55 } else {
56 return -1;
57 }
58
59 /* get the min percentage */
60 tok = strtok(NULL, ",");
61 if (!tok)
62 goto setup;
63
64 callchain_param.min_percent = strtod(tok, &endptr);
65 if (tok == endptr)
66 return -1;
67
68 /* get the print limit */
69 tok2 = strtok(NULL, ",");
70 if (!tok2)
71 goto setup;
72
73 if (tok2[0] != 'c') {
74 callchain_param.print_limit = strtoul(tok2, &endptr, 0);
75 tok2 = strtok(NULL, ",");
76 if (!tok2)
77 goto setup;
78 }
79
80 /* get the call chain order */
81 if (!strncmp(tok2, "caller", strlen("caller")))
82 callchain_param.order = ORDER_CALLER;
83 else if (!strncmp(tok2, "callee", strlen("callee")))
84 callchain_param.order = ORDER_CALLEE;
85 else
86 return -1;
87
88 /* Get the sort key */
89 tok2 = strtok(NULL, ",");
90 if (!tok2)
91 goto setup;
92 if (!strncmp(tok2, "function", strlen("function")))
93 callchain_param.key = CCKEY_FUNCTION;
94 else if (!strncmp(tok2, "address", strlen("address")))
95 callchain_param.key = CCKEY_ADDRESS;
96 else
97 return -1;
98setup:
99 if (callchain_register_param(&callchain_param) < 0) {
100 pr_err("Can't register callchain params\n");
101 return -1;
102 }
103 return 0;
104}
105
28static void 106static void
29rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 107rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
30 enum chain_mode mode) 108 enum chain_mode mode)
@@ -538,7 +616,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
538 if (sample->callchain == NULL) 616 if (sample->callchain == NULL)
539 return 0; 617 return 0;
540 618
541 if (symbol_conf.use_callchain || sort__has_parent) { 619 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
620 sort__has_parent) {
542 return machine__resolve_callchain(al->machine, evsel, al->thread, 621 return machine__resolve_callchain(al->machine, evsel, al->thread,
543 sample, parent, al, max_stack); 622 sample, parent, al, max_stack);
544 } 623 }
@@ -551,3 +630,45 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
551 return 0; 630 return 0;
552 return callchain_append(he->callchain, &callchain_cursor, sample->period); 631 return callchain_append(he->callchain, &callchain_cursor, sample->period);
553} 632}
633
634int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
635 bool hide_unresolved)
636{
637 al->map = node->map;
638 al->sym = node->sym;
639 if (node->map)
640 al->addr = node->map->map_ip(node->map, node->ip);
641 else
642 al->addr = node->ip;
643
644 if (al->sym == NULL) {
645 if (hide_unresolved)
646 return 0;
647 if (al->map == NULL)
648 goto out;
649 }
650
651 if (al->map->groups == &al->machine->kmaps) {
652 if (machine__is_host(al->machine)) {
653 al->cpumode = PERF_RECORD_MISC_KERNEL;
654 al->level = 'k';
655 } else {
656 al->cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
657 al->level = 'g';
658 }
659 } else {
660 if (machine__is_host(al->machine)) {
661 al->cpumode = PERF_RECORD_MISC_USER;
662 al->level = '.';
663 } else if (perf_guest) {
664 al->cpumode = PERF_RECORD_MISC_GUEST_USER;
665 al->level = 'u';
666 } else {
667 al->cpumode = PERF_RECORD_MISC_HYPERVISOR;
668 al->level = 'H';
669 }
670 }
671
672out:
673 return 1;
674}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 8ad97e9b119f..8f84423a75da 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -7,6 +7,13 @@
7#include "event.h" 7#include "event.h"
8#include "symbol.h" 8#include "symbol.h"
9 9
10enum perf_call_graph_mode {
11 CALLCHAIN_NONE,
12 CALLCHAIN_FP,
13 CALLCHAIN_DWARF,
14 CALLCHAIN_MAX
15};
16
10enum chain_mode { 17enum chain_mode {
11 CHAIN_NONE, 18 CHAIN_NONE,
12 CHAIN_FLAT, 19 CHAIN_FLAT,
@@ -155,6 +162,18 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
155 struct perf_evsel *evsel, struct addr_location *al, 162 struct perf_evsel *evsel, struct addr_location *al,
156 int max_stack); 163 int max_stack);
157int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); 164int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
165int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
166 bool hide_unresolved);
158 167
159extern const char record_callchain_help[]; 168extern const char record_callchain_help[];
169int parse_callchain_report_opt(const char *arg);
170
171static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
172 struct callchain_cursor *src)
173{
174 *dest = *src;
175
176 dest->first = src->curr;
177 dest->nr -= src->pos;
178}
160#endif /* __PERF_CALLCHAIN_H */ 179#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 3e0fdd369ccb..24519e14ac56 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,6 +11,7 @@
11#include "util.h" 11#include "util.h"
12#include "cache.h" 12#include "cache.h"
13#include "exec_cmd.h" 13#include "exec_cmd.h"
14#include "util/hist.h" /* perf_hist_config */
14 15
15#define MAXNAME (256) 16#define MAXNAME (256)
16 17
@@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value,
355 if (!prefixcmp(var, "core.")) 356 if (!prefixcmp(var, "core."))
356 return perf_default_core_config(var, value); 357 return perf_default_core_config(var, value);
357 358
359 if (!prefixcmp(var, "hist."))
360 return perf_hist_config(var, value);
361
358 /* Add other config variables here. */ 362 /* Add other config variables here. */
359 return 0; 363 return 0;
360} 364}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 7fe4994eeb63..c4e55b71010c 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep)
317{ 317{
318 return cpu_map__build_map(cpus, corep, cpu_map__get_core); 318 return cpu_map__build_map(cpus, corep, cpu_map__get_core);
319} 319}
320
321/* setup simple routines to easily access node numbers given a cpu number */
322static int get_max_num(char *path, int *max)
323{
324 size_t num;
325 char *buf;
326 int err = 0;
327
328 if (filename__read_str(path, &buf, &num))
329 return -1;
330
331 buf[num] = '\0';
332
333 /* start on the right, to find highest node num */
334 while (--num) {
335 if ((buf[num] == ',') || (buf[num] == '-')) {
336 num++;
337 break;
338 }
339 }
340 if (sscanf(&buf[num], "%d", max) < 1) {
341 err = -1;
342 goto out;
343 }
344
345 /* convert from 0-based to 1-based */
346 (*max)++;
347
348out:
349 free(buf);
350 return err;
351}
352
353/* Determine highest possible cpu in the system for sparse allocation */
354static void set_max_cpu_num(void)
355{
356 const char *mnt;
357 char path[PATH_MAX];
358 int ret = -1;
359
360 /* set up default */
361 max_cpu_num = 4096;
362
363 mnt = sysfs__mountpoint();
364 if (!mnt)
365 goto out;
366
367 /* get the highest possible cpu number for a sparse allocation */
368 ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt);
369 if (ret == PATH_MAX) {
370 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
371 goto out;
372 }
373
374 ret = get_max_num(path, &max_cpu_num);
375
376out:
377 if (ret)
378 pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num);
379}
380
381/* Determine highest possible node in the system for sparse allocation */
382static void set_max_node_num(void)
383{
384 const char *mnt;
385 char path[PATH_MAX];
386 int ret = -1;
387
388 /* set up default */
389 max_node_num = 8;
390
391 mnt = sysfs__mountpoint();
392 if (!mnt)
393 goto out;
394
395 /* get the highest possible cpu number for a sparse allocation */
396 ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt);
397 if (ret == PATH_MAX) {
398 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
399 goto out;
400 }
401
402 ret = get_max_num(path, &max_node_num);
403
404out:
405 if (ret)
406 pr_err("Failed to read max nodes, using default of %d\n", max_node_num);
407}
408
409static int init_cpunode_map(void)
410{
411 int i;
412
413 set_max_cpu_num();
414 set_max_node_num();
415
416 cpunode_map = calloc(max_cpu_num, sizeof(int));
417 if (!cpunode_map) {
418 pr_err("%s: calloc failed\n", __func__);
419 return -1;
420 }
421
422 for (i = 0; i < max_cpu_num; i++)
423 cpunode_map[i] = -1;
424
425 return 0;
426}
427
428int cpu__setup_cpunode_map(void)
429{
430 struct dirent *dent1, *dent2;
431 DIR *dir1, *dir2;
432 unsigned int cpu, mem;
433 char buf[PATH_MAX];
434 char path[PATH_MAX];
435 const char *mnt;
436 int n;
437
438 /* initialize globals */
439 if (init_cpunode_map())
440 return -1;
441
442 mnt = sysfs__mountpoint();
443 if (!mnt)
444 return 0;
445
446 n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt);
447 if (n == PATH_MAX) {
448 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
449 return -1;
450 }
451
452 dir1 = opendir(path);
453 if (!dir1)
454 return 0;
455
456 /* walk tree and setup map */
457 while ((dent1 = readdir(dir1)) != NULL) {
458 if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1)
459 continue;
460
461 n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name);
462 if (n == PATH_MAX) {
463 pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX);
464 continue;
465 }
466
467 dir2 = opendir(buf);
468 if (!dir2)
469 continue;
470 while ((dent2 = readdir(dir2)) != NULL) {
471 if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1)
472 continue;
473 cpunode_map[cpu] = mem;
474 }
475 closedir(dir2);
476 }
477 closedir(dir1);
478 return 0;
479}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index b123bb9d6f55..61a654849002 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -4,6 +4,9 @@
4#include <stdio.h> 4#include <stdio.h>
5#include <stdbool.h> 5#include <stdbool.h>
6 6
7#include "perf.h"
8#include "util/debug.h"
9
7struct cpu_map { 10struct cpu_map {
8 int nr; 11 int nr;
9 int map[]; 12 int map[];
@@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map)
46 return map ? map->map[0] == -1 : true; 49 return map ? map->map[0] == -1 : true;
47} 50}
48 51
52int max_cpu_num;
53int max_node_num;
54int *cpunode_map;
55
56int cpu__setup_cpunode_map(void);
57
58static inline int cpu__max_node(void)
59{
60 if (unlikely(!max_node_num))
61 pr_debug("cpu_map not initialized\n");
62
63 return max_node_num;
64}
65
66static inline int cpu__max_cpu(void)
67{
68 if (unlikely(!max_cpu_num))
69 pr_debug("cpu_map not initialized\n");
70
71 return max_cpu_num;
72}
73
74static inline int cpu__get_node(int cpu)
75{
76 if (unlikely(cpunode_map == NULL)) {
77 pr_debug("cpu_map not initialized\n");
78 return -1;
79 }
80
81 return cpunode_map[cpu];
82}
83
49#endif /* __PERF_CPUMAP_H */ 84#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index ab06f1c03655..38efe95a7fdd 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -4,7 +4,7 @@
4#include <linux/types.h> 4#include <linux/types.h>
5#include <linux/rbtree.h> 5#include <linux/rbtree.h>
6#include <stdbool.h> 6#include <stdbool.h>
7#include "types.h" 7#include <linux/types.h>
8#include "map.h" 8#include "map.h"
9#include "build-id.h" 9#include "build-id.h"
10 10
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 7defd77105d0..cc66c4049e09 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -747,14 +747,17 @@ struct __find_variable_param {
747static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 747static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
748{ 748{
749 struct __find_variable_param *fvp = data; 749 struct __find_variable_param *fvp = data;
750 Dwarf_Attribute attr;
750 int tag; 751 int tag;
751 752
752 tag = dwarf_tag(die_mem); 753 tag = dwarf_tag(die_mem);
753 if ((tag == DW_TAG_formal_parameter || 754 if ((tag == DW_TAG_formal_parameter ||
754 tag == DW_TAG_variable) && 755 tag == DW_TAG_variable) &&
755 die_compare_name(die_mem, fvp->name)) 756 die_compare_name(die_mem, fvp->name) &&
757 /* Does the DIE have location information or external instance? */
758 (dwarf_attr(die_mem, DW_AT_external, &attr) ||
759 dwarf_attr(die_mem, DW_AT_location, &attr)))
756 return DIE_FIND_CB_END; 760 return DIE_FIND_CB_END;
757
758 if (dwarf_haspc(die_mem, fvp->addr)) 761 if (dwarf_haspc(die_mem, fvp->addr))
759 return DIE_FIND_CB_CONTINUE; 762 return DIE_FIND_CB_CONTINUE;
760 else 763 else
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 9d12aa6dd485..65795b835b39 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -699,7 +699,7 @@ void thread__find_addr_map(struct thread *thread,
699 enum map_type type, u64 addr, 699 enum map_type type, u64 addr,
700 struct addr_location *al) 700 struct addr_location *al)
701{ 701{
702 struct map_groups *mg = &thread->mg; 702 struct map_groups *mg = thread->mg;
703 bool load_map = false; 703 bool load_map = false;
704 704
705 al->machine = machine; 705 al->machine = machine;
@@ -788,7 +788,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
788{ 788{
789 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 789 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
790 struct thread *thread = machine__findnew_thread(machine, sample->pid, 790 struct thread *thread = machine__findnew_thread(machine, sample->pid,
791 sample->pid); 791 sample->tid);
792 792
793 if (thread == NULL) 793 if (thread == NULL)
794 return -1; 794 return -1;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 38457d447a13..d970232cb270 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -112,6 +112,30 @@ struct sample_read {
112 }; 112 };
113}; 113};
114 114
115struct ip_callchain {
116 u64 nr;
117 u64 ips[0];
118};
119
120struct branch_flags {
121 u64 mispred:1;
122 u64 predicted:1;
123 u64 in_tx:1;
124 u64 abort:1;
125 u64 reserved:60;
126};
127
128struct branch_entry {
129 u64 from;
130 u64 to;
131 struct branch_flags flags;
132};
133
134struct branch_stack {
135 u64 nr;
136 struct branch_entry entries[0];
137};
138
115struct perf_sample { 139struct perf_sample {
116 u64 ip; 140 u64 ip;
117 u32 pid, tid; 141 u32 pid, tid;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 0c9926cfb292..a52e9a5bb2d0 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -5,12 +5,12 @@
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stddef.h> 6#include <stddef.h>
7#include <linux/perf_event.h> 7#include <linux/perf_event.h>
8#include "types.h" 8#include <linux/types.h>
9#include "xyarray.h" 9#include "xyarray.h"
10#include "cgroup.h" 10#include "cgroup.h"
11#include "hist.h" 11#include "hist.h"
12#include "symbol.h" 12#include "symbol.h"
13 13
14struct perf_counts_values { 14struct perf_counts_values {
15 union { 15 union {
16 struct { 16 struct {
@@ -91,6 +91,11 @@ struct perf_evsel {
91 char *group_name; 91 char *group_name;
92}; 92};
93 93
94union u64_swap {
95 u64 val64;
96 u32 val32[2];
97};
98
94#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) 99#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
95 100
96struct cpu_map; 101struct cpu_map;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index a2d047bdf4ef..d08cfe499404 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -4,10 +4,10 @@
4#include <linux/perf_event.h> 4#include <linux/perf_event.h>
5#include <sys/types.h> 5#include <sys/types.h>
6#include <stdbool.h> 6#include <stdbool.h>
7#include "types.h" 7#include <linux/bitmap.h>
8#include <linux/types.h>
8#include "event.h" 9#include "event.h"
9 10
10#include <linux/bitmap.h>
11 11
12enum { 12enum {
13 HEADER_RESERVED = 0, /* always cleared */ 13 HEADER_RESERVED = 0, /* always cleared */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f38590d7561b..5a0a4b2cadc4 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -4,6 +4,7 @@
4#include "session.h" 4#include "session.h"
5#include "sort.h" 5#include "sort.h"
6#include "evsel.h" 6#include "evsel.h"
7#include "annotate.h"
7#include <math.h> 8#include <math.h>
8 9
9static bool hists__filter_entry_by_dso(struct hists *hists, 10static bool hists__filter_entry_by_dso(struct hists *hists,
@@ -225,14 +226,20 @@ static void he_stat__decay(struct he_stat *he_stat)
225static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) 226static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
226{ 227{
227 u64 prev_period = he->stat.period; 228 u64 prev_period = he->stat.period;
229 u64 diff;
228 230
229 if (prev_period == 0) 231 if (prev_period == 0)
230 return true; 232 return true;
231 233
232 he_stat__decay(&he->stat); 234 he_stat__decay(&he->stat);
235 if (symbol_conf.cumulate_callchain)
236 he_stat__decay(he->stat_acc);
233 237
238 diff = prev_period - he->stat.period;
239
240 hists->stats.total_period -= diff;
234 if (!he->filtered) 241 if (!he->filtered)
235 hists->stats.total_period -= prev_period - he->stat.period; 242 hists->stats.total_non_filtered_period -= diff;
236 243
237 return he->stat.period == 0; 244 return he->stat.period == 0;
238} 245}
@@ -259,8 +266,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
259 if (sort__need_collapse) 266 if (sort__need_collapse)
260 rb_erase(&n->rb_node_in, &hists->entries_collapsed); 267 rb_erase(&n->rb_node_in, &hists->entries_collapsed);
261 268
262 hist_entry__free(n);
263 --hists->nr_entries; 269 --hists->nr_entries;
270 if (!n->filtered)
271 --hists->nr_non_filtered_entries;
272
273 hist_entry__free(n);
264 } 274 }
265 } 275 }
266} 276}
@@ -269,14 +279,31 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
269 * histogram, sorted on item, collects periods 279 * histogram, sorted on item, collects periods
270 */ 280 */
271 281
272static struct hist_entry *hist_entry__new(struct hist_entry *template) 282static struct hist_entry *hist_entry__new(struct hist_entry *template,
283 bool sample_self)
273{ 284{
274 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; 285 size_t callchain_size = 0;
275 struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); 286 struct hist_entry *he;
287
288 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain)
289 callchain_size = sizeof(struct callchain_root);
290
291 he = zalloc(sizeof(*he) + callchain_size);
276 292
277 if (he != NULL) { 293 if (he != NULL) {
278 *he = *template; 294 *he = *template;
279 295
296 if (symbol_conf.cumulate_callchain) {
297 he->stat_acc = malloc(sizeof(he->stat));
298 if (he->stat_acc == NULL) {
299 free(he);
300 return NULL;
301 }
302 memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
303 if (!sample_self)
304 memset(&he->stat, 0, sizeof(he->stat));
305 }
306
280 if (he->ms.map) 307 if (he->ms.map)
281 he->ms.map->referenced = true; 308 he->ms.map->referenced = true;
282 309
@@ -288,6 +315,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
288 */ 315 */
289 he->branch_info = malloc(sizeof(*he->branch_info)); 316 he->branch_info = malloc(sizeof(*he->branch_info));
290 if (he->branch_info == NULL) { 317 if (he->branch_info == NULL) {
318 free(he->stat_acc);
291 free(he); 319 free(he);
292 return NULL; 320 return NULL;
293 } 321 }
@@ -317,15 +345,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
317 return he; 345 return he;
318} 346}
319 347
320void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
321{
322 if (!h->filtered) {
323 hists__calc_col_len(hists, h);
324 ++hists->nr_entries;
325 hists->stats.total_period += h->stat.period;
326 }
327}
328
329static u8 symbol__parent_filter(const struct symbol *parent) 348static u8 symbol__parent_filter(const struct symbol *parent)
330{ 349{
331 if (symbol_conf.exclude_other && parent == NULL) 350 if (symbol_conf.exclude_other && parent == NULL)
@@ -335,7 +354,8 @@ static u8 symbol__parent_filter(const struct symbol *parent)
335 354
336static struct hist_entry *add_hist_entry(struct hists *hists, 355static struct hist_entry *add_hist_entry(struct hists *hists,
337 struct hist_entry *entry, 356 struct hist_entry *entry,
338 struct addr_location *al) 357 struct addr_location *al,
358 bool sample_self)
339{ 359{
340 struct rb_node **p; 360 struct rb_node **p;
341 struct rb_node *parent = NULL; 361 struct rb_node *parent = NULL;
@@ -359,7 +379,10 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
359 cmp = hist_entry__cmp(he, entry); 379 cmp = hist_entry__cmp(he, entry);
360 380
361 if (!cmp) { 381 if (!cmp) {
362 he_stat__add_period(&he->stat, period, weight); 382 if (sample_self)
383 he_stat__add_period(&he->stat, period, weight);
384 if (symbol_conf.cumulate_callchain)
385 he_stat__add_period(he->stat_acc, period, weight);
363 386
364 /* 387 /*
365 * This mem info was allocated from sample__resolve_mem 388 * This mem info was allocated from sample__resolve_mem
@@ -387,15 +410,17 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
387 p = &(*p)->rb_right; 410 p = &(*p)->rb_right;
388 } 411 }
389 412
390 he = hist_entry__new(entry); 413 he = hist_entry__new(entry, sample_self);
391 if (!he) 414 if (!he)
392 return NULL; 415 return NULL;
393 416
394 hists->nr_entries++;
395 rb_link_node(&he->rb_node_in, parent, p); 417 rb_link_node(&he->rb_node_in, parent, p);
396 rb_insert_color(&he->rb_node_in, hists->entries_in); 418 rb_insert_color(&he->rb_node_in, hists->entries_in);
397out: 419out:
398 he_stat__add_cpumode_period(&he->stat, al->cpumode, period); 420 if (sample_self)
421 he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
422 if (symbol_conf.cumulate_callchain)
423 he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);
399 return he; 424 return he;
400} 425}
401 426
@@ -404,7 +429,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
404 struct symbol *sym_parent, 429 struct symbol *sym_parent,
405 struct branch_info *bi, 430 struct branch_info *bi,
406 struct mem_info *mi, 431 struct mem_info *mi,
407 u64 period, u64 weight, u64 transaction) 432 u64 period, u64 weight, u64 transaction,
433 bool sample_self)
408{ 434{
409 struct hist_entry entry = { 435 struct hist_entry entry = {
410 .thread = al->thread, 436 .thread = al->thread,
@@ -429,17 +455,442 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
429 .transaction = transaction, 455 .transaction = transaction,
430 }; 456 };
431 457
432 return add_hist_entry(hists, &entry, al); 458 return add_hist_entry(hists, &entry, al, sample_self);
459}
460
461static int
462iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
463 struct addr_location *al __maybe_unused)
464{
465 return 0;
466}
467
468static int
469iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
470 struct addr_location *al __maybe_unused)
471{
472 return 0;
473}
474
475static int
476iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
477{
478 struct perf_sample *sample = iter->sample;
479 struct mem_info *mi;
480
481 mi = sample__resolve_mem(sample, al);
482 if (mi == NULL)
483 return -ENOMEM;
484
485 iter->priv = mi;
486 return 0;
487}
488
489static int
490iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
491{
492 u64 cost;
493 struct mem_info *mi = iter->priv;
494 struct hist_entry *he;
495
496 if (mi == NULL)
497 return -EINVAL;
498
499 cost = iter->sample->weight;
500 if (!cost)
501 cost = 1;
502
503 /*
504 * must pass period=weight in order to get the correct
505 * sorting from hists__collapse_resort() which is solely
506 * based on periods. We want sorting be done on nr_events * weight
507 * and this is indirectly achieved by passing period=weight here
508 * and the he_stat__add_period() function.
509 */
510 he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi,
511 cost, cost, 0, true);
512 if (!he)
513 return -ENOMEM;
514
515 iter->he = he;
516 return 0;
517}
518
519static int
520iter_finish_mem_entry(struct hist_entry_iter *iter,
521 struct addr_location *al __maybe_unused)
522{
523 struct perf_evsel *evsel = iter->evsel;
524 struct hist_entry *he = iter->he;
525 int err = -EINVAL;
526
527 if (he == NULL)
528 goto out;
529
530 hists__inc_nr_samples(&evsel->hists, he->filtered);
531
532 err = hist_entry__append_callchain(he, iter->sample);
533
534out:
535 /*
536 * We don't need to free iter->priv (mem_info) here since
537 * the mem info was either already freed in add_hist_entry() or
538 * passed to a new hist entry by hist_entry__new().
539 */
540 iter->priv = NULL;
541
542 iter->he = NULL;
543 return err;
544}
545
546static int
547iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
548{
549 struct branch_info *bi;
550 struct perf_sample *sample = iter->sample;
551
552 bi = sample__resolve_bstack(sample, al);
553 if (!bi)
554 return -ENOMEM;
555
556 iter->curr = 0;
557 iter->total = sample->branch_stack->nr;
558
559 iter->priv = bi;
560 return 0;
561}
562
563static int
564iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
565 struct addr_location *al __maybe_unused)
566{
567 /* to avoid calling callback function */
568 iter->he = NULL;
569
570 return 0;
571}
572
573static int
574iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
575{
576 struct branch_info *bi = iter->priv;
577 int i = iter->curr;
578
579 if (bi == NULL)
580 return 0;
581
582 if (iter->curr >= iter->total)
583 return 0;
584
585 al->map = bi[i].to.map;
586 al->sym = bi[i].to.sym;
587 al->addr = bi[i].to.addr;
588 return 1;
589}
590
591static int
592iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
593{
594 struct branch_info *bi;
595 struct perf_evsel *evsel = iter->evsel;
596 struct hist_entry *he = NULL;
597 int i = iter->curr;
598 int err = 0;
599
600 bi = iter->priv;
601
602 if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
603 goto out;
604
605 /*
606 * The report shows the percentage of total branches captured
607 * and not events sampled. Thus we use a pseudo period of 1.
608 */
609 he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL,
610 1, 1, 0, true);
611 if (he == NULL)
612 return -ENOMEM;
613
614 hists__inc_nr_samples(&evsel->hists, he->filtered);
615
616out:
617 iter->he = he;
618 iter->curr++;
619 return err;
620}
621
622static int
623iter_finish_branch_entry(struct hist_entry_iter *iter,
624 struct addr_location *al __maybe_unused)
625{
626 zfree(&iter->priv);
627 iter->he = NULL;
628
629 return iter->curr >= iter->total ? 0 : -1;
630}
631
632static int
633iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
634 struct addr_location *al __maybe_unused)
635{
636 return 0;
637}
638
639static int
640iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
641{
642 struct perf_evsel *evsel = iter->evsel;
643 struct perf_sample *sample = iter->sample;
644 struct hist_entry *he;
645
646 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
647 sample->period, sample->weight,
648 sample->transaction, true);
649 if (he == NULL)
650 return -ENOMEM;
651
652 iter->he = he;
653 return 0;
654}
655
656static int
657iter_finish_normal_entry(struct hist_entry_iter *iter,
658 struct addr_location *al __maybe_unused)
659{
660 struct hist_entry *he = iter->he;
661 struct perf_evsel *evsel = iter->evsel;
662 struct perf_sample *sample = iter->sample;
663
664 if (he == NULL)
665 return 0;
666
667 iter->he = NULL;
668
669 hists__inc_nr_samples(&evsel->hists, he->filtered);
670
671 return hist_entry__append_callchain(he, sample);
672}
673
674static int
675iter_prepare_cumulative_entry(struct hist_entry_iter *iter __maybe_unused,
676 struct addr_location *al __maybe_unused)
677{
678 struct hist_entry **he_cache;
679
680 callchain_cursor_commit(&callchain_cursor);
681
682 /*
683 * This is for detecting cycles or recursions so that they're
684 * cumulated only one time to prevent entries more than 100%
685 * overhead.
686 */
687 he_cache = malloc(sizeof(*he_cache) * (PERF_MAX_STACK_DEPTH + 1));
688 if (he_cache == NULL)
689 return -ENOMEM;
690
691 iter->priv = he_cache;
692 iter->curr = 0;
693
694 return 0;
695}
696
697static int
698iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
699 struct addr_location *al)
700{
701 struct perf_evsel *evsel = iter->evsel;
702 struct perf_sample *sample = iter->sample;
703 struct hist_entry **he_cache = iter->priv;
704 struct hist_entry *he;
705 int err = 0;
706
707 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
708 sample->period, sample->weight,
709 sample->transaction, true);
710 if (he == NULL)
711 return -ENOMEM;
712
713 iter->he = he;
714 he_cache[iter->curr++] = he;
715
716 callchain_append(he->callchain, &callchain_cursor, sample->period);
717
718 /*
719 * We need to re-initialize the cursor since callchain_append()
720 * advanced the cursor to the end.
721 */
722 callchain_cursor_commit(&callchain_cursor);
723
724 hists__inc_nr_samples(&evsel->hists, he->filtered);
725
726 return err;
727}
728
729static int
730iter_next_cumulative_entry(struct hist_entry_iter *iter,
731 struct addr_location *al)
732{
733 struct callchain_cursor_node *node;
734
735 node = callchain_cursor_current(&callchain_cursor);
736 if (node == NULL)
737 return 0;
738
739 return fill_callchain_info(al, node, iter->hide_unresolved);
740}
741
742static int
743iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
744 struct addr_location *al)
745{
746 struct perf_evsel *evsel = iter->evsel;
747 struct perf_sample *sample = iter->sample;
748 struct hist_entry **he_cache = iter->priv;
749 struct hist_entry *he;
750 struct hist_entry he_tmp = {
751 .cpu = al->cpu,
752 .thread = al->thread,
753 .comm = thread__comm(al->thread),
754 .ip = al->addr,
755 .ms = {
756 .map = al->map,
757 .sym = al->sym,
758 },
759 .parent = iter->parent,
760 };
761 int i;
762 struct callchain_cursor cursor;
763
764 callchain_cursor_snapshot(&cursor, &callchain_cursor);
765
766 callchain_cursor_advance(&callchain_cursor);
767
768 /*
769 * Check if there's duplicate entries in the callchain.
770 * It's possible that it has cycles or recursive calls.
771 */
772 for (i = 0; i < iter->curr; i++) {
773 if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
774 /* to avoid calling callback function */
775 iter->he = NULL;
776 return 0;
777 }
778 }
779
780 he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL,
781 sample->period, sample->weight,
782 sample->transaction, false);
783 if (he == NULL)
784 return -ENOMEM;
785
786 iter->he = he;
787 he_cache[iter->curr++] = he;
788
789 callchain_append(he->callchain, &cursor, sample->period);
790 return 0;
791}
792
793static int
794iter_finish_cumulative_entry(struct hist_entry_iter *iter,
795 struct addr_location *al __maybe_unused)
796{
797 zfree(&iter->priv);
798 iter->he = NULL;
799
800 return 0;
801}
802
803const struct hist_iter_ops hist_iter_mem = {
804 .prepare_entry = iter_prepare_mem_entry,
805 .add_single_entry = iter_add_single_mem_entry,
806 .next_entry = iter_next_nop_entry,
807 .add_next_entry = iter_add_next_nop_entry,
808 .finish_entry = iter_finish_mem_entry,
809};
810
811const struct hist_iter_ops hist_iter_branch = {
812 .prepare_entry = iter_prepare_branch_entry,
813 .add_single_entry = iter_add_single_branch_entry,
814 .next_entry = iter_next_branch_entry,
815 .add_next_entry = iter_add_next_branch_entry,
816 .finish_entry = iter_finish_branch_entry,
817};
818
819const struct hist_iter_ops hist_iter_normal = {
820 .prepare_entry = iter_prepare_normal_entry,
821 .add_single_entry = iter_add_single_normal_entry,
822 .next_entry = iter_next_nop_entry,
823 .add_next_entry = iter_add_next_nop_entry,
824 .finish_entry = iter_finish_normal_entry,
825};
826
827const struct hist_iter_ops hist_iter_cumulative = {
828 .prepare_entry = iter_prepare_cumulative_entry,
829 .add_single_entry = iter_add_single_cumulative_entry,
830 .next_entry = iter_next_cumulative_entry,
831 .add_next_entry = iter_add_next_cumulative_entry,
832 .finish_entry = iter_finish_cumulative_entry,
833};
834
835int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
836 struct perf_evsel *evsel, struct perf_sample *sample,
837 int max_stack_depth, void *arg)
838{
839 int err, err2;
840
841 err = sample__resolve_callchain(sample, &iter->parent, evsel, al,
842 max_stack_depth);
843 if (err)
844 return err;
845
846 iter->evsel = evsel;
847 iter->sample = sample;
848
849 err = iter->ops->prepare_entry(iter, al);
850 if (err)
851 goto out;
852
853 err = iter->ops->add_single_entry(iter, al);
854 if (err)
855 goto out;
856
857 if (iter->he && iter->add_entry_cb) {
858 err = iter->add_entry_cb(iter, al, true, arg);
859 if (err)
860 goto out;
861 }
862
863 while (iter->ops->next_entry(iter, al)) {
864 err = iter->ops->add_next_entry(iter, al);
865 if (err)
866 break;
867
868 if (iter->he && iter->add_entry_cb) {
869 err = iter->add_entry_cb(iter, al, false, arg);
870 if (err)
871 goto out;
872 }
873 }
874
875out:
876 err2 = iter->ops->finish_entry(iter, al);
877 if (!err)
878 err = err2;
879
880 return err;
433} 881}
434 882
435int64_t 883int64_t
436hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 884hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
437{ 885{
438 struct sort_entry *se; 886 struct perf_hpp_fmt *fmt;
439 int64_t cmp = 0; 887 int64_t cmp = 0;
440 888
441 list_for_each_entry(se, &hist_entry__sort_list, list) { 889 perf_hpp__for_each_sort_list(fmt) {
442 cmp = se->se_cmp(left, right); 890 if (perf_hpp__should_skip(fmt))
891 continue;
892
893 cmp = fmt->cmp(left, right);
443 if (cmp) 894 if (cmp)
444 break; 895 break;
445 } 896 }
@@ -450,15 +901,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
450int64_t 901int64_t
451hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) 902hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
452{ 903{
453 struct sort_entry *se; 904 struct perf_hpp_fmt *fmt;
454 int64_t cmp = 0; 905 int64_t cmp = 0;
455 906
456 list_for_each_entry(se, &hist_entry__sort_list, list) { 907 perf_hpp__for_each_sort_list(fmt) {
457 int64_t (*f)(struct hist_entry *, struct hist_entry *); 908 if (perf_hpp__should_skip(fmt))
458 909 continue;
459 f = se->se_collapse ?: se->se_cmp;
460 910
461 cmp = f(left, right); 911 cmp = fmt->collapse(left, right);
462 if (cmp) 912 if (cmp)
463 break; 913 break;
464 } 914 }
@@ -470,6 +920,7 @@ void hist_entry__free(struct hist_entry *he)
470{ 920{
471 zfree(&he->branch_info); 921 zfree(&he->branch_info);
472 zfree(&he->mem_info); 922 zfree(&he->mem_info);
923 zfree(&he->stat_acc);
473 free_srcline(he->srcline); 924 free_srcline(he->srcline);
474 free(he); 925 free(he);
475} 926}
@@ -495,6 +946,8 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
495 946
496 if (!cmp) { 947 if (!cmp) {
497 he_stat__add_stat(&iter->stat, &he->stat); 948 he_stat__add_stat(&iter->stat, &he->stat);
949 if (symbol_conf.cumulate_callchain)
950 he_stat__add_stat(iter->stat_acc, he->stat_acc);
498 951
499 if (symbol_conf.use_callchain) { 952 if (symbol_conf.use_callchain) {
500 callchain_cursor_reset(&callchain_cursor); 953 callchain_cursor_reset(&callchain_cursor);
@@ -571,64 +1024,50 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
571 } 1024 }
572} 1025}
573 1026
574/* 1027static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
575 * reverse the map, sort on period.
576 */
577
578static int period_cmp(u64 period_a, u64 period_b)
579{
580 if (period_a > period_b)
581 return 1;
582 if (period_a < period_b)
583 return -1;
584 return 0;
585}
586
587static int hist_entry__sort_on_period(struct hist_entry *a,
588 struct hist_entry *b)
589{ 1028{
590 int ret; 1029 struct perf_hpp_fmt *fmt;
591 int i, nr_members; 1030 int64_t cmp = 0;
592 struct perf_evsel *evsel;
593 struct hist_entry *pair;
594 u64 *periods_a, *periods_b;
595 1031
596 ret = period_cmp(a->stat.period, b->stat.period); 1032 perf_hpp__for_each_sort_list(fmt) {
597 if (ret || !symbol_conf.event_group) 1033 if (perf_hpp__should_skip(fmt))
598 return ret; 1034 continue;
599 1035
600 evsel = hists_to_evsel(a->hists); 1036 cmp = fmt->sort(a, b);
601 nr_members = evsel->nr_members; 1037 if (cmp)
602 if (nr_members <= 1) 1038 break;
603 return ret; 1039 }
604 1040
605 periods_a = zalloc(sizeof(periods_a) * nr_members); 1041 return cmp;
606 periods_b = zalloc(sizeof(periods_b) * nr_members); 1042}
607 1043
608 if (!periods_a || !periods_b) 1044static void hists__reset_filter_stats(struct hists *hists)
609 goto out; 1045{
1046 hists->nr_non_filtered_entries = 0;
1047 hists->stats.total_non_filtered_period = 0;
1048}
610 1049
611 list_for_each_entry(pair, &a->pairs.head, pairs.node) { 1050void hists__reset_stats(struct hists *hists)
612 evsel = hists_to_evsel(pair->hists); 1051{
613 periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; 1052 hists->nr_entries = 0;
614 } 1053 hists->stats.total_period = 0;
615 1054
616 list_for_each_entry(pair, &b->pairs.head, pairs.node) { 1055 hists__reset_filter_stats(hists);
617 evsel = hists_to_evsel(pair->hists); 1056}
618 periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
619 }
620 1057
621 for (i = 1; i < nr_members; i++) { 1058static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h)
622 ret = period_cmp(periods_a[i], periods_b[i]); 1059{
623 if (ret) 1060 hists->nr_non_filtered_entries++;
624 break; 1061 hists->stats.total_non_filtered_period += h->stat.period;
625 } 1062}
626 1063
627out: 1064void hists__inc_stats(struct hists *hists, struct hist_entry *h)
628 free(periods_a); 1065{
629 free(periods_b); 1066 if (!h->filtered)
1067 hists__inc_filter_stats(hists, h);
630 1068
631 return ret; 1069 hists->nr_entries++;
1070 hists->stats.total_period += h->stat.period;
632} 1071}
633 1072
634static void __hists__insert_output_entry(struct rb_root *entries, 1073static void __hists__insert_output_entry(struct rb_root *entries,
@@ -647,7 +1086,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
647 parent = *p; 1086 parent = *p;
648 iter = rb_entry(parent, struct hist_entry, rb_node); 1087 iter = rb_entry(parent, struct hist_entry, rb_node);
649 1088
650 if (hist_entry__sort_on_period(he, iter) > 0) 1089 if (hist_entry__sort(he, iter) > 0)
651 p = &(*p)->rb_left; 1090 p = &(*p)->rb_left;
652 else 1091 else
653 p = &(*p)->rb_right; 1092 p = &(*p)->rb_right;
@@ -674,8 +1113,7 @@ void hists__output_resort(struct hists *hists)
674 next = rb_first(root); 1113 next = rb_first(root);
675 hists->entries = RB_ROOT; 1114 hists->entries = RB_ROOT;
676 1115
677 hists->nr_entries = 0; 1116 hists__reset_stats(hists);
678 hists->stats.total_period = 0;
679 hists__reset_col_len(hists); 1117 hists__reset_col_len(hists);
680 1118
681 while (next) { 1119 while (next) {
@@ -683,7 +1121,10 @@ void hists__output_resort(struct hists *hists)
683 next = rb_next(&n->rb_node_in); 1121 next = rb_next(&n->rb_node_in);
684 1122
685 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); 1123 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
686 hists__inc_nr_entries(hists, n); 1124 hists__inc_stats(hists, n);
1125
1126 if (!n->filtered)
1127 hists__calc_col_len(hists, n);
687 } 1128 }
688} 1129}
689 1130
@@ -694,13 +1135,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
694 if (h->filtered) 1135 if (h->filtered)
695 return; 1136 return;
696 1137
697 ++hists->nr_entries; 1138 /* force fold unfiltered entry for simplicity */
698 if (h->ms.unfolded) 1139 h->ms.unfolded = false;
699 hists->nr_entries += h->nr_rows;
700 h->row_offset = 0; 1140 h->row_offset = 0;
701 hists->stats.total_period += h->stat.period;
702 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events;
703 1141
1142 hists->stats.nr_non_filtered_samples += h->stat.nr_events;
1143
1144 hists__inc_filter_stats(hists, h);
704 hists__calc_col_len(hists, h); 1145 hists__calc_col_len(hists, h);
705} 1146}
706 1147
@@ -721,8 +1162,9 @@ void hists__filter_by_dso(struct hists *hists)
721{ 1162{
722 struct rb_node *nd; 1163 struct rb_node *nd;
723 1164
724 hists->nr_entries = hists->stats.total_period = 0; 1165 hists->stats.nr_non_filtered_samples = 0;
725 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1166
1167 hists__reset_filter_stats(hists);
726 hists__reset_col_len(hists); 1168 hists__reset_col_len(hists);
727 1169
728 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1170 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -754,8 +1196,9 @@ void hists__filter_by_thread(struct hists *hists)
754{ 1196{
755 struct rb_node *nd; 1197 struct rb_node *nd;
756 1198
757 hists->nr_entries = hists->stats.total_period = 0; 1199 hists->stats.nr_non_filtered_samples = 0;
758 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1200
1201 hists__reset_filter_stats(hists);
759 hists__reset_col_len(hists); 1202 hists__reset_col_len(hists);
760 1203
761 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1204 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -785,8 +1228,9 @@ void hists__filter_by_symbol(struct hists *hists)
785{ 1228{
786 struct rb_node *nd; 1229 struct rb_node *nd;
787 1230
788 hists->nr_entries = hists->stats.total_period = 0; 1231 hists->stats.nr_non_filtered_samples = 0;
789 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1232
1233 hists__reset_filter_stats(hists);
790 hists__reset_col_len(hists); 1234 hists__reset_col_len(hists);
791 1235
792 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { 1236 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
@@ -810,6 +1254,13 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
810 events_stats__inc(&hists->stats, type); 1254 events_stats__inc(&hists->stats, type);
811} 1255}
812 1256
1257void hists__inc_nr_samples(struct hists *hists, bool filtered)
1258{
1259 events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE);
1260 if (!filtered)
1261 hists->stats.nr_non_filtered_samples++;
1262}
1263
813static struct hist_entry *hists__add_dummy_entry(struct hists *hists, 1264static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
814 struct hist_entry *pair) 1265 struct hist_entry *pair)
815{ 1266{
@@ -841,13 +1292,13 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
841 p = &(*p)->rb_right; 1292 p = &(*p)->rb_right;
842 } 1293 }
843 1294
844 he = hist_entry__new(pair); 1295 he = hist_entry__new(pair, true);
845 if (he) { 1296 if (he) {
846 memset(&he->stat, 0, sizeof(he->stat)); 1297 memset(&he->stat, 0, sizeof(he->stat));
847 he->hists = hists; 1298 he->hists = hists;
848 rb_link_node(&he->rb_node_in, parent, p); 1299 rb_link_node(&he->rb_node_in, parent, p);
849 rb_insert_color(&he->rb_node_in, root); 1300 rb_insert_color(&he->rb_node_in, root);
850 hists__inc_nr_entries(hists, he); 1301 hists__inc_stats(hists, he);
851 he->dummy = true; 1302 he->dummy = true;
852 } 1303 }
853out: 1304out:
@@ -931,3 +1382,30 @@ int hists__link(struct hists *leader, struct hists *other)
931 1382
932 return 0; 1383 return 0;
933} 1384}
1385
1386u64 hists__total_period(struct hists *hists)
1387{
1388 return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
1389 hists->stats.total_period;
1390}
1391
1392int parse_filter_percentage(const struct option *opt __maybe_unused,
1393 const char *arg, int unset __maybe_unused)
1394{
1395 if (!strcmp(arg, "relative"))
1396 symbol_conf.filter_relative = true;
1397 else if (!strcmp(arg, "absolute"))
1398 symbol_conf.filter_relative = false;
1399 else
1400 return -1;
1401
1402 return 0;
1403}
1404
1405int perf_hist_config(const char *var, const char *value)
1406{
1407 if (!strcmp(var, "hist.percentage"))
1408 return parse_filter_percentage(NULL, value, 0);
1409
1410 return 0;
1411}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 1f1f513dfe7f..d2bf03575d5f 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -37,9 +37,11 @@ enum hist_filter {
37 */ 37 */
38struct events_stats { 38struct events_stats {
39 u64 total_period; 39 u64 total_period;
40 u64 total_non_filtered_period;
40 u64 total_lost; 41 u64 total_lost;
41 u64 total_invalid_chains; 42 u64 total_invalid_chains;
42 u32 nr_events[PERF_RECORD_HEADER_MAX]; 43 u32 nr_events[PERF_RECORD_HEADER_MAX];
44 u32 nr_non_filtered_samples;
43 u32 nr_lost_warned; 45 u32 nr_lost_warned;
44 u32 nr_unknown_events; 46 u32 nr_unknown_events;
45 u32 nr_invalid_chains; 47 u32 nr_invalid_chains;
@@ -83,6 +85,7 @@ struct hists {
83 struct rb_root entries; 85 struct rb_root entries;
84 struct rb_root entries_collapsed; 86 struct rb_root entries_collapsed;
85 u64 nr_entries; 87 u64 nr_entries;
88 u64 nr_non_filtered_entries;
86 const struct thread *thread_filter; 89 const struct thread *thread_filter;
87 const struct dso *dso_filter; 90 const struct dso *dso_filter;
88 const char *uid_filter_str; 91 const char *uid_filter_str;
@@ -93,12 +96,50 @@ struct hists {
93 u16 col_len[HISTC_NR_COLS]; 96 u16 col_len[HISTC_NR_COLS];
94}; 97};
95 98
99struct hist_entry_iter;
100
101struct hist_iter_ops {
102 int (*prepare_entry)(struct hist_entry_iter *, struct addr_location *);
103 int (*add_single_entry)(struct hist_entry_iter *, struct addr_location *);
104 int (*next_entry)(struct hist_entry_iter *, struct addr_location *);
105 int (*add_next_entry)(struct hist_entry_iter *, struct addr_location *);
106 int (*finish_entry)(struct hist_entry_iter *, struct addr_location *);
107};
108
109struct hist_entry_iter {
110 int total;
111 int curr;
112
113 bool hide_unresolved;
114
115 struct perf_evsel *evsel;
116 struct perf_sample *sample;
117 struct hist_entry *he;
118 struct symbol *parent;
119 void *priv;
120
121 const struct hist_iter_ops *ops;
122 /* user-defined callback function (optional) */
123 int (*add_entry_cb)(struct hist_entry_iter *iter,
124 struct addr_location *al, bool single, void *arg);
125};
126
127extern const struct hist_iter_ops hist_iter_normal;
128extern const struct hist_iter_ops hist_iter_branch;
129extern const struct hist_iter_ops hist_iter_mem;
130extern const struct hist_iter_ops hist_iter_cumulative;
131
96struct hist_entry *__hists__add_entry(struct hists *hists, 132struct hist_entry *__hists__add_entry(struct hists *hists,
97 struct addr_location *al, 133 struct addr_location *al,
98 struct symbol *parent, 134 struct symbol *parent,
99 struct branch_info *bi, 135 struct branch_info *bi,
100 struct mem_info *mi, u64 period, 136 struct mem_info *mi, u64 period,
101 u64 weight, u64 transaction); 137 u64 weight, u64 transaction,
138 bool sample_self);
139int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
140 struct perf_evsel *evsel, struct perf_sample *sample,
141 int max_stack_depth, void *arg);
142
102int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); 143int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
103int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); 144int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
104int hist_entry__transaction_len(void); 145int hist_entry__transaction_len(void);
@@ -112,8 +153,11 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
112void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); 153void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
113void hists__output_recalc_col_len(struct hists *hists, int max_rows); 154void hists__output_recalc_col_len(struct hists *hists, int max_rows);
114 155
115void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); 156u64 hists__total_period(struct hists *hists);
157void hists__reset_stats(struct hists *hists);
158void hists__inc_stats(struct hists *hists, struct hist_entry *h);
116void hists__inc_nr_events(struct hists *hists, u32 type); 159void hists__inc_nr_events(struct hists *hists, u32 type);
160void hists__inc_nr_samples(struct hists *hists, bool filtered);
117void events_stats__inc(struct events_stats *stats, u32 type); 161void events_stats__inc(struct events_stats *stats, u32 type);
118size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); 162size_t events_stats__fprintf(struct events_stats *stats, FILE *fp);
119 163
@@ -124,6 +168,12 @@ void hists__filter_by_dso(struct hists *hists);
124void hists__filter_by_thread(struct hists *hists); 168void hists__filter_by_thread(struct hists *hists);
125void hists__filter_by_symbol(struct hists *hists); 169void hists__filter_by_symbol(struct hists *hists);
126 170
171static inline bool hists__has_filter(struct hists *hists)
172{
173 return hists->thread_filter || hists->dso_filter ||
174 hists->symbol_filter_str;
175}
176
127u16 hists__col_len(struct hists *hists, enum hist_column col); 177u16 hists__col_len(struct hists *hists, enum hist_column col);
128void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); 178void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len);
129bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); 179bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len);
@@ -149,15 +199,30 @@ struct perf_hpp_fmt {
149 struct hist_entry *he); 199 struct hist_entry *he);
150 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, 200 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
151 struct hist_entry *he); 201 struct hist_entry *he);
202 int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b);
203 int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b);
204 int64_t (*sort)(struct hist_entry *a, struct hist_entry *b);
152 205
153 struct list_head list; 206 struct list_head list;
207 struct list_head sort_list;
208 bool elide;
154}; 209};
155 210
156extern struct list_head perf_hpp__list; 211extern struct list_head perf_hpp__list;
212extern struct list_head perf_hpp__sort_list;
157 213
158#define perf_hpp__for_each_format(format) \ 214#define perf_hpp__for_each_format(format) \
159 list_for_each_entry(format, &perf_hpp__list, list) 215 list_for_each_entry(format, &perf_hpp__list, list)
160 216
217#define perf_hpp__for_each_format_safe(format, tmp) \
218 list_for_each_entry_safe(format, tmp, &perf_hpp__list, list)
219
220#define perf_hpp__for_each_sort_list(format) \
221 list_for_each_entry(format, &perf_hpp__sort_list, sort_list)
222
223#define perf_hpp__for_each_sort_list_safe(format, tmp) \
224 list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list)
225
161extern struct perf_hpp_fmt perf_hpp__format[]; 226extern struct perf_hpp_fmt perf_hpp__format[];
162 227
163enum { 228enum {
@@ -167,6 +232,7 @@ enum {
167 PERF_HPP__OVERHEAD_US, 232 PERF_HPP__OVERHEAD_US,
168 PERF_HPP__OVERHEAD_GUEST_SYS, 233 PERF_HPP__OVERHEAD_GUEST_SYS,
169 PERF_HPP__OVERHEAD_GUEST_US, 234 PERF_HPP__OVERHEAD_GUEST_US,
235 PERF_HPP__OVERHEAD_ACC,
170 PERF_HPP__SAMPLES, 236 PERF_HPP__SAMPLES,
171 PERF_HPP__PERIOD, 237 PERF_HPP__PERIOD,
172 238
@@ -175,15 +241,36 @@ enum {
175 241
176void perf_hpp__init(void); 242void perf_hpp__init(void);
177void perf_hpp__column_register(struct perf_hpp_fmt *format); 243void perf_hpp__column_register(struct perf_hpp_fmt *format);
244void perf_hpp__column_unregister(struct perf_hpp_fmt *format);
178void perf_hpp__column_enable(unsigned col); 245void perf_hpp__column_enable(unsigned col);
246void perf_hpp__column_disable(unsigned col);
247void perf_hpp__cancel_cumulate(void);
248
249void perf_hpp__register_sort_field(struct perf_hpp_fmt *format);
250void perf_hpp__setup_output_field(void);
251void perf_hpp__reset_output_field(void);
252void perf_hpp__append_sort_keys(void);
253
254bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
255bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
256
257static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
258{
259 return format->elide;
260}
261
262void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
179 263
180typedef u64 (*hpp_field_fn)(struct hist_entry *he); 264typedef u64 (*hpp_field_fn)(struct hist_entry *he);
181typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); 265typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front);
182typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); 266typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...);
183 267
184int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, 268int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
185 hpp_field_fn get_field, hpp_callback_fn callback, 269 hpp_field_fn get_field, const char *fmt,
186 const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); 270 hpp_snprint_fn print_fn, bool fmt_percent);
271int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he,
272 hpp_field_fn get_field, const char *fmt,
273 hpp_snprint_fn print_fn, bool fmt_percent);
187 274
188static inline void advance_hpp(struct perf_hpp *hpp, int inc) 275static inline void advance_hpp(struct perf_hpp *hpp, int inc)
189{ 276{
@@ -250,4 +337,10 @@ static inline int script_browse(const char *script_opt __maybe_unused)
250#endif 337#endif
251 338
252unsigned int hists__sort_list_width(struct hists *hists); 339unsigned int hists__sort_list_width(struct hists *hists);
340
341struct option;
342int parse_filter_percentage(const struct option *opt __maybe_unused,
343 const char *arg, int unset __maybe_unused);
344int perf_hist_config(const char *var, const char *value);
345
253#endif /* __PERF_HIST_H */ 346#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h
index bb162e40c76c..01ffd12dc791 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/perf/util/include/linux/bitmap.h
@@ -4,6 +4,9 @@
4#include <string.h> 4#include <string.h>
5#include <linux/bitops.h> 5#include <linux/bitops.h>
6 6
7#define DECLARE_BITMAP(name,bits) \
8 unsigned long name[BITS_TO_LONGS(bits)]
9
7int __bitmap_weight(const unsigned long *bitmap, int bits); 10int __bitmap_weight(const unsigned long *bitmap, int bits);
8void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, 11void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
9 const unsigned long *bitmap2, int bits); 12 const unsigned long *bitmap2, int bits);
diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h
deleted file mode 100644
index b43e2dc21e04..000000000000
--- a/tools/perf/util/include/linux/export.h
+++ /dev/null
@@ -1,6 +0,0 @@
1#ifndef PERF_LINUX_MODULE_H
2#define PERF_LINUX_MODULE_H
3
4#define EXPORT_SYMBOL(name)
5
6#endif
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index bfe0a2afd0d2..76ddbc726343 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -1,4 +1,5 @@
1#include <linux/kernel.h> 1#include <linux/kernel.h>
2#include <linux/types.h>
2 3
3#include "../../../../include/linux/list.h" 4#include "../../../../include/linux/list.h"
4 5
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
deleted file mode 100644
index eb464786c084..000000000000
--- a/tools/perf/util/include/linux/types.h
+++ /dev/null
@@ -1,29 +0,0 @@
1#ifndef _PERF_LINUX_TYPES_H_
2#define _PERF_LINUX_TYPES_H_
3
4#include <asm/types.h>
5
6#ifndef __bitwise
7#define __bitwise
8#endif
9
10#ifndef __le32
11typedef __u32 __bitwise __le32;
12#endif
13
14#define DECLARE_BITMAP(name,bits) \
15 unsigned long name[BITS_TO_LONGS(bits)]
16
17struct list_head {
18 struct list_head *next, *prev;
19};
20
21struct hlist_head {
22 struct hlist_node *first;
23};
24
25struct hlist_node {
26 struct hlist_node *next, **pprev;
27};
28
29#endif
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 27c2a5efe450..7409ac8de51c 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -316,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
316 rb_link_node(&th->rb_node, parent, p); 316 rb_link_node(&th->rb_node, parent, p);
317 rb_insert_color(&th->rb_node, &machine->threads); 317 rb_insert_color(&th->rb_node, &machine->threads);
318 machine->last_match = th; 318 machine->last_match = th;
319
320 /*
321 * We have to initialize map_groups separately
322 * after rb tree is updated.
323 *
324 * The reason is that we call machine__findnew_thread
325 * within thread__init_map_groups to find the thread
326 * leader and that would screwed the rb tree.
327 */
328 if (thread__init_map_groups(th, machine))
329 return NULL;
319 } 330 }
320 331
321 return th; 332 return th;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 39cd2d0faff6..8ccbb32eda25 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename)
32 !strcmp(filename, "[heap]"); 32 !strcmp(filename, "[heap]");
33} 33}
34 34
35static inline int is_android_lib(const char *filename)
36{
37 return !strncmp(filename, "/data/app-lib", 13) ||
38 !strncmp(filename, "/system/lib", 11);
39}
40
41static inline bool replace_android_lib(const char *filename, char *newfilename)
42{
43 const char *libname;
44 char *app_abi;
45 size_t app_abi_length, new_length;
46 size_t lib_length = 0;
47
48 libname = strrchr(filename, '/');
49 if (libname)
50 lib_length = strlen(libname);
51
52 app_abi = getenv("APP_ABI");
53 if (!app_abi)
54 return false;
55
56 app_abi_length = strlen(app_abi);
57
58 if (!strncmp(filename, "/data/app-lib", 13)) {
59 char *apk_path;
60
61 if (!app_abi_length)
62 return false;
63
64 new_length = 7 + app_abi_length + lib_length;
65
66 apk_path = getenv("APK_PATH");
67 if (apk_path) {
68 new_length += strlen(apk_path) + 1;
69 if (new_length > PATH_MAX)
70 return false;
71 snprintf(newfilename, new_length,
72 "%s/libs/%s/%s", apk_path, app_abi, libname);
73 } else {
74 if (new_length > PATH_MAX)
75 return false;
76 snprintf(newfilename, new_length,
77 "libs/%s/%s", app_abi, libname);
78 }
79 return true;
80 }
81
82 if (!strncmp(filename, "/system/lib/", 11)) {
83 char *ndk, *app;
84 const char *arch;
85 size_t ndk_length;
86 size_t app_length;
87
88 ndk = getenv("NDK_ROOT");
89 app = getenv("APP_PLATFORM");
90
91 if (!(ndk && app))
92 return false;
93
94 ndk_length = strlen(ndk);
95 app_length = strlen(app);
96
97 if (!(ndk_length && app_length && app_abi_length))
98 return false;
99
100 arch = !strncmp(app_abi, "arm", 3) ? "arm" :
101 !strncmp(app_abi, "mips", 4) ? "mips" :
102 !strncmp(app_abi, "x86", 3) ? "x86" : NULL;
103
104 if (!arch)
105 return false;
106
107 new_length = 27 + ndk_length +
108 app_length + lib_length
109 + strlen(arch);
110
111 if (new_length > PATH_MAX)
112 return false;
113 snprintf(newfilename, new_length,
114 "%s/platforms/%s/arch-%s/usr/lib/%s",
115 ndk, app, arch, libname);
116
117 return true;
118 }
119 return false;
120}
121
35void map__init(struct map *map, enum map_type type, 122void map__init(struct map *map, enum map_type type,
36 u64 start, u64 end, u64 pgoff, struct dso *dso) 123 u64 start, u64 end, u64 pgoff, struct dso *dso)
37{ 124{
@@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
59 if (map != NULL) { 146 if (map != NULL) {
60 char newfilename[PATH_MAX]; 147 char newfilename[PATH_MAX];
61 struct dso *dso; 148 struct dso *dso;
62 int anon, no_dso, vdso; 149 int anon, no_dso, vdso, android;
63 150
151 android = is_android_lib(filename);
64 anon = is_anon_memory(filename); 152 anon = is_anon_memory(filename);
65 vdso = is_vdso_map(filename); 153 vdso = is_vdso_map(filename);
66 no_dso = is_no_dso_memory(filename); 154 no_dso = is_no_dso_memory(filename);
@@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
75 filename = newfilename; 163 filename = newfilename;
76 } 164 }
77 165
166 if (android) {
167 if (replace_android_lib(filename, newfilename))
168 filename = newfilename;
169 }
170
78 if (vdso) { 171 if (vdso) {
79 pgoff = 0; 172 pgoff = 0;
80 dso = vdso__dso_findnew(dsos__list); 173 dso = vdso__dso_findnew(dsos__list);
@@ -323,6 +416,7 @@ void map_groups__init(struct map_groups *mg)
323 INIT_LIST_HEAD(&mg->removed_maps[i]); 416 INIT_LIST_HEAD(&mg->removed_maps[i]);
324 } 417 }
325 mg->machine = NULL; 418 mg->machine = NULL;
419 mg->refcnt = 1;
326} 420}
327 421
328static void maps__delete(struct rb_root *maps) 422static void maps__delete(struct rb_root *maps)
@@ -358,6 +452,28 @@ void map_groups__exit(struct map_groups *mg)
358 } 452 }
359} 453}
360 454
455struct map_groups *map_groups__new(void)
456{
457 struct map_groups *mg = malloc(sizeof(*mg));
458
459 if (mg != NULL)
460 map_groups__init(mg);
461
462 return mg;
463}
464
465void map_groups__delete(struct map_groups *mg)
466{
467 map_groups__exit(mg);
468 free(mg);
469}
470
471void map_groups__put(struct map_groups *mg)
472{
473 if (--mg->refcnt == 0)
474 map_groups__delete(mg);
475}
476
361void map_groups__flush(struct map_groups *mg) 477void map_groups__flush(struct map_groups *mg)
362{ 478{
363 int type; 479 int type;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f00f058afb3b..ae2d45110588 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -6,7 +6,7 @@
6#include <linux/rbtree.h> 6#include <linux/rbtree.h>
7#include <stdio.h> 7#include <stdio.h>
8#include <stdbool.h> 8#include <stdbool.h>
9#include "types.h" 9#include <linux/types.h>
10 10
11enum map_type { 11enum map_type {
12 MAP__FUNCTION = 0, 12 MAP__FUNCTION = 0,
@@ -59,8 +59,20 @@ struct map_groups {
59 struct rb_root maps[MAP__NR_TYPES]; 59 struct rb_root maps[MAP__NR_TYPES];
60 struct list_head removed_maps[MAP__NR_TYPES]; 60 struct list_head removed_maps[MAP__NR_TYPES];
61 struct machine *machine; 61 struct machine *machine;
62 int refcnt;
62}; 63};
63 64
65struct map_groups *map_groups__new(void);
66void map_groups__delete(struct map_groups *mg);
67
68static inline struct map_groups *map_groups__get(struct map_groups *mg)
69{
70 ++mg->refcnt;
71 return mg;
72}
73
74void map_groups__put(struct map_groups *mg);
75
64static inline struct kmap *map__kmap(struct map *map) 76static inline struct kmap *map__kmap(struct map *map)
65{ 77{
66 return (struct kmap *)(map + 1); 78 return (struct kmap *)(map + 1);
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c
index 3322b8446e89..31ee02d4e988 100644
--- a/tools/perf/util/pager.c
+++ b/tools/perf/util/pager.c
@@ -57,13 +57,13 @@ void setup_pager(void)
57 } 57 }
58 if (!pager) 58 if (!pager)
59 pager = getenv("PAGER"); 59 pager = getenv("PAGER");
60 if (!pager) { 60 if (!(pager || access("/usr/bin/pager", X_OK)))
61 if (!access("/usr/bin/pager", X_OK)) 61 pager = "/usr/bin/pager";
62 pager = "/usr/bin/pager"; 62 if (!(pager || access("/usr/bin/less", X_OK)))
63 } 63 pager = "/usr/bin/less";
64 if (!pager) 64 if (!pager)
65 pager = "less"; 65 pager = "cat";
66 else if (!*pager || !strcmp(pager, "cat")) 66 if (!*pager || !strcmp(pager, "cat"))
67 return; 67 return;
68 68
69 spawned_pager = 1; /* means we are emitting to terminal */ 69 spawned_pager = 1; /* means we are emitting to terminal */
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index f1cb4c4b3c70..df094b4ed5ed 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -6,9 +6,8 @@
6 6
7#include <linux/list.h> 7#include <linux/list.h>
8#include <stdbool.h> 8#include <stdbool.h>
9#include "types.h" 9#include <linux/types.h>
10#include <linux/perf_event.h> 10#include <linux/perf_event.h>
11#include "types.h"
12 11
13struct list_head; 12struct list_head;
14struct perf_evsel; 13struct perf_evsel;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 4eb67ec333f1..0bc87ba46bf3 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -9,7 +9,7 @@
9 9
10#include <linux/compiler.h> 10#include <linux/compiler.h>
11#include <linux/list.h> 11#include <linux/list.h>
12#include "types.h" 12#include <linux/types.h>
13#include "util.h" 13#include "util.h"
14#include "parse-events.h" 14#include "parse-events.h"
15#include "parse-events-bison.h" 15#include "parse-events-bison.h"
@@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc
299} 299}
300 300
301event_legacy_tracepoint: 301event_legacy_tracepoint:
302PE_NAME '-' PE_NAME ':' PE_NAME
303{
304 struct parse_events_evlist *data = _data;
305 struct list_head *list;
306 char sys_name[128];
307 snprintf(&sys_name, 128, "%s-%s", $1, $3);
308
309 ALLOC_LIST(list);
310 ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5));
311 $$ = list;
312}
313|
302PE_NAME ':' PE_NAME 314PE_NAME ':' PE_NAME
303{ 315{
304 struct parse_events_evlist *data = _data; 316 struct parse_events_evlist *data = _data;
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index d6e8b6a8d7f3..79c78f74e0cf 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_REGS_H 1#ifndef __PERF_REGS_H
2#define __PERF_REGS_H 2#define __PERF_REGS_H
3 3
4#include "types.h" 4#include <linux/types.h>
5#include "event.h" 5#include "event.h"
6 6
7#ifdef HAVE_PERF_REGS_SUPPORT 7#ifdef HAVE_PERF_REGS_SUPPORT
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 00a7dcb2f55c..7a811eb61f75 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -284,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head)
284static int pmu_alias_terms(struct perf_pmu_alias *alias, 284static int pmu_alias_terms(struct perf_pmu_alias *alias,
285 struct list_head *terms) 285 struct list_head *terms)
286{ 286{
287 struct parse_events_term *term, *clone; 287 struct parse_events_term *term, *cloned;
288 LIST_HEAD(list); 288 LIST_HEAD(list);
289 int ret; 289 int ret;
290 290
291 list_for_each_entry(term, &alias->terms, list) { 291 list_for_each_entry(term, &alias->terms, list) {
292 ret = parse_events_term__clone(&clone, term); 292 ret = parse_events_term__clone(&cloned, term);
293 if (ret) { 293 if (ret) {
294 parse_events__free_terms(&list); 294 parse_events__free_terms(&list);
295 return ret; 295 return ret;
296 } 296 }
297 list_add_tail(&clone->list, &list); 297 list_add_tail(&cloned->list, &list);
298 } 298 }
299 list_splice(&list, terms); 299 list_splice(&list, terms);
300 return 0; 300 return 0;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 8b64125a9281..c14a543ce1f3 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -1,7 +1,7 @@
1#ifndef __PMU_H 1#ifndef __PMU_H
2#define __PMU_H 2#define __PMU_H
3 3
4#include <linux/bitops.h> 4#include <linux/bitmap.h>
5#include <linux/perf_event.h> 5#include <linux/perf_event.h>
6#include <stdbool.h> 6#include <stdbool.h>
7 7
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 562762117639..9d8eb26f0533 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -511,12 +511,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
511 511
512 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, 512 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
513 &pf->sp_die, pf->tvar); 513 &pf->sp_die, pf->tvar);
514 if (ret == -ENOENT) 514 if (ret == -ENOENT || ret == -EINVAL)
515 pr_err("Failed to find the location of %s at this address.\n" 515 pr_err("Failed to find the location of %s at this address.\n"
516 " Perhaps, it has been optimized out.\n", pf->pvar->var); 516 " Perhaps, it has been optimized out.\n", pf->pvar->var);
517 else if (ret == -ENOTSUP) 517 else if (ret == -ENOTSUP)
518 pr_err("Sorry, we don't support this variable location yet.\n"); 518 pr_err("Sorry, we don't support this variable location yet.\n");
519 else if (pf->pvar->field) { 519 else if (ret == 0 && pf->pvar->field) {
520 ret = convert_variable_fields(vr_die, pf->pvar->var, 520 ret = convert_variable_fields(vr_die, pf->pvar->var,
521 pf->pvar->field, &pf->tvar->ref, 521 pf->pvar->field, &pf->tvar->ref,
522 &die_mem); 522 &die_mem);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 55960f22233c..64a186edc7be 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1625,13 +1625,14 @@ out_delete_map:
1625void perf_session__fprintf_info(struct perf_session *session, FILE *fp, 1625void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
1626 bool full) 1626 bool full)
1627{ 1627{
1628 int fd = perf_data_file__fd(session->file);
1629 struct stat st; 1628 struct stat st;
1630 int ret; 1629 int fd, ret;
1631 1630
1632 if (session == NULL || fp == NULL) 1631 if (session == NULL || fp == NULL)
1633 return; 1632 return;
1634 1633
1634 fd = perf_data_file__fd(session->file);
1635
1635 ret = fstat(fd, &st); 1636 ret = fstat(fd, &st);
1636 if (ret == -1) 1637 if (ret == -1)
1637 return; 1638 return;
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 635cd8f8b22e..45512baaab67 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2,12 +2,18 @@
2#include "hist.h" 2#include "hist.h"
3#include "comm.h" 3#include "comm.h"
4#include "symbol.h" 4#include "symbol.h"
5#include "evsel.h"
5 6
6regex_t parent_regex; 7regex_t parent_regex;
7const char default_parent_pattern[] = "^sys_|^do_page_fault"; 8const char default_parent_pattern[] = "^sys_|^do_page_fault";
8const char *parent_pattern = default_parent_pattern; 9const char *parent_pattern = default_parent_pattern;
9const char default_sort_order[] = "comm,dso,symbol"; 10const char default_sort_order[] = "comm,dso,symbol";
10const char *sort_order = default_sort_order; 11const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to";
12const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
13const char default_top_sort_order[] = "dso,symbol";
14const char default_diff_sort_order[] = "dso,symbol";
15const char *sort_order;
16const char *field_order;
11regex_t ignore_callees_regex; 17regex_t ignore_callees_regex;
12int have_ignore_callees = 0; 18int have_ignore_callees = 0;
13int sort__need_collapse = 0; 19int sort__need_collapse = 0;
@@ -16,9 +22,6 @@ int sort__has_sym = 0;
16int sort__has_dso = 0; 22int sort__has_dso = 0;
17enum sort_mode sort__mode = SORT_MODE__NORMAL; 23enum sort_mode sort__mode = SORT_MODE__NORMAL;
18 24
19enum sort_type sort__first_dimension;
20
21LIST_HEAD(hist_entry__sort_list);
22 25
23static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) 26static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
24{ 27{
@@ -93,6 +96,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
93 return comm__str(right->comm) - comm__str(left->comm); 96 return comm__str(right->comm) - comm__str(left->comm);
94} 97}
95 98
99static int64_t
100sort__comm_sort(struct hist_entry *left, struct hist_entry *right)
101{
102 return strcmp(comm__str(right->comm), comm__str(left->comm));
103}
104
96static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, 105static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf,
97 size_t size, unsigned int width) 106 size_t size, unsigned int width)
98{ 107{
@@ -103,6 +112,7 @@ struct sort_entry sort_comm = {
103 .se_header = "Command", 112 .se_header = "Command",
104 .se_cmp = sort__comm_cmp, 113 .se_cmp = sort__comm_cmp,
105 .se_collapse = sort__comm_collapse, 114 .se_collapse = sort__comm_collapse,
115 .se_sort = sort__comm_sort,
106 .se_snprintf = hist_entry__comm_snprintf, 116 .se_snprintf = hist_entry__comm_snprintf,
107 .se_width_idx = HISTC_COMM, 117 .se_width_idx = HISTC_COMM,
108}; 118};
@@ -116,7 +126,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
116 const char *dso_name_l, *dso_name_r; 126 const char *dso_name_l, *dso_name_r;
117 127
118 if (!dso_l || !dso_r) 128 if (!dso_l || !dso_r)
119 return cmp_null(dso_l, dso_r); 129 return cmp_null(dso_r, dso_l);
120 130
121 if (verbose) { 131 if (verbose) {
122 dso_name_l = dso_l->long_name; 132 dso_name_l = dso_l->long_name;
@@ -132,7 +142,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
132static int64_t 142static int64_t
133sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 143sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
134{ 144{
135 return _sort__dso_cmp(left->ms.map, right->ms.map); 145 return _sort__dso_cmp(right->ms.map, left->ms.map);
136} 146}
137 147
138static int _hist_entry__dso_snprintf(struct map *map, char *bf, 148static int _hist_entry__dso_snprintf(struct map *map, char *bf,
@@ -204,6 +214,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
204 return _sort__sym_cmp(left->ms.sym, right->ms.sym); 214 return _sort__sym_cmp(left->ms.sym, right->ms.sym);
205} 215}
206 216
217static int64_t
218sort__sym_sort(struct hist_entry *left, struct hist_entry *right)
219{
220 if (!left->ms.sym || !right->ms.sym)
221 return cmp_null(left->ms.sym, right->ms.sym);
222
223 return strcmp(right->ms.sym->name, left->ms.sym->name);
224}
225
207static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, 226static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
208 u64 ip, char level, char *bf, size_t size, 227 u64 ip, char level, char *bf, size_t size,
209 unsigned int width) 228 unsigned int width)
@@ -250,6 +269,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf,
250struct sort_entry sort_sym = { 269struct sort_entry sort_sym = {
251 .se_header = "Symbol", 270 .se_header = "Symbol",
252 .se_cmp = sort__sym_cmp, 271 .se_cmp = sort__sym_cmp,
272 .se_sort = sort__sym_sort,
253 .se_snprintf = hist_entry__sym_snprintf, 273 .se_snprintf = hist_entry__sym_snprintf,
254 .se_width_idx = HISTC_SYMBOL, 274 .se_width_idx = HISTC_SYMBOL,
255}; 275};
@@ -277,7 +297,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
277 map__rip_2objdump(map, right->ip)); 297 map__rip_2objdump(map, right->ip));
278 } 298 }
279 } 299 }
280 return strcmp(left->srcline, right->srcline); 300 return strcmp(right->srcline, left->srcline);
281} 301}
282 302
283static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, 303static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
@@ -305,7 +325,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)
305 if (!sym_l || !sym_r) 325 if (!sym_l || !sym_r)
306 return cmp_null(sym_l, sym_r); 326 return cmp_null(sym_l, sym_r);
307 327
308 return strcmp(sym_l->name, sym_r->name); 328 return strcmp(sym_r->name, sym_l->name);
309} 329}
310 330
311static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, 331static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf,
@@ -1027,19 +1047,194 @@ static struct sort_dimension memory_sort_dimensions[] = {
1027 1047
1028#undef DIM 1048#undef DIM
1029 1049
1030static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) 1050struct hpp_dimension {
1051 const char *name;
1052 struct perf_hpp_fmt *fmt;
1053 int taken;
1054};
1055
1056#define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], }
1057
1058static struct hpp_dimension hpp_sort_dimensions[] = {
1059 DIM(PERF_HPP__OVERHEAD, "overhead"),
1060 DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"),
1061 DIM(PERF_HPP__OVERHEAD_US, "overhead_us"),
1062 DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"),
1063 DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"),
1064 DIM(PERF_HPP__OVERHEAD_ACC, "overhead_children"),
1065 DIM(PERF_HPP__SAMPLES, "sample"),
1066 DIM(PERF_HPP__PERIOD, "period"),
1067};
1068
1069#undef DIM
1070
1071struct hpp_sort_entry {
1072 struct perf_hpp_fmt hpp;
1073 struct sort_entry *se;
1074};
1075
1076bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1031{ 1077{
1032 if (sd->taken) 1078 struct hpp_sort_entry *hse_a;
1079 struct hpp_sort_entry *hse_b;
1080
1081 if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b))
1082 return false;
1083
1084 hse_a = container_of(a, struct hpp_sort_entry, hpp);
1085 hse_b = container_of(b, struct hpp_sort_entry, hpp);
1086
1087 return hse_a->se == hse_b->se;
1088}
1089
1090void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
1091{
1092 struct hpp_sort_entry *hse;
1093
1094 if (!perf_hpp__is_sort_entry(fmt))
1033 return; 1095 return;
1034 1096
1097 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1098 hists__new_col_len(hists, hse->se->se_width_idx,
1099 strlen(hse->se->se_header));
1100}
1101
1102static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1103 struct perf_evsel *evsel)
1104{
1105 struct hpp_sort_entry *hse;
1106 size_t len;
1107
1108 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1109 len = hists__col_len(&evsel->hists, hse->se->se_width_idx);
1110
1111 return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header);
1112}
1113
1114static int __sort__hpp_width(struct perf_hpp_fmt *fmt,
1115 struct perf_hpp *hpp __maybe_unused,
1116 struct perf_evsel *evsel)
1117{
1118 struct hpp_sort_entry *hse;
1119
1120 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1121
1122 return hists__col_len(&evsel->hists, hse->se->se_width_idx);
1123}
1124
1125static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1126 struct hist_entry *he)
1127{
1128 struct hpp_sort_entry *hse;
1129 size_t len;
1130
1131 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1132 len = hists__col_len(he->hists, hse->se->se_width_idx);
1133
1134 return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
1135}
1136
1137static struct hpp_sort_entry *
1138__sort_dimension__alloc_hpp(struct sort_dimension *sd)
1139{
1140 struct hpp_sort_entry *hse;
1141
1142 hse = malloc(sizeof(*hse));
1143 if (hse == NULL) {
1144 pr_err("Memory allocation failed\n");
1145 return NULL;
1146 }
1147
1148 hse->se = sd->entry;
1149 hse->hpp.header = __sort__hpp_header;
1150 hse->hpp.width = __sort__hpp_width;
1151 hse->hpp.entry = __sort__hpp_entry;
1152 hse->hpp.color = NULL;
1153
1154 hse->hpp.cmp = sd->entry->se_cmp;
1155 hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp;
1156 hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse;
1157
1158 INIT_LIST_HEAD(&hse->hpp.list);
1159 INIT_LIST_HEAD(&hse->hpp.sort_list);
1160 hse->hpp.elide = false;
1161
1162 return hse;
1163}
1164
1165bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format)
1166{
1167 return format->header == __sort__hpp_header;
1168}
1169
1170static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd)
1171{
1172 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1173
1174 if (hse == NULL)
1175 return -1;
1176
1177 perf_hpp__register_sort_field(&hse->hpp);
1178 return 0;
1179}
1180
1181static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
1182{
1183 struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd);
1184
1185 if (hse == NULL)
1186 return -1;
1187
1188 perf_hpp__column_register(&hse->hpp);
1189 return 0;
1190}
1191
1192static int __sort_dimension__add(struct sort_dimension *sd)
1193{
1194 if (sd->taken)
1195 return 0;
1196
1197 if (__sort_dimension__add_hpp_sort(sd) < 0)
1198 return -1;
1199
1035 if (sd->entry->se_collapse) 1200 if (sd->entry->se_collapse)
1036 sort__need_collapse = 1; 1201 sort__need_collapse = 1;
1037 1202
1038 if (list_empty(&hist_entry__sort_list)) 1203 sd->taken = 1;
1039 sort__first_dimension = idx; 1204
1205 return 0;
1206}
1207
1208static int __hpp_dimension__add(struct hpp_dimension *hd)
1209{
1210 if (!hd->taken) {
1211 hd->taken = 1;
1212
1213 perf_hpp__register_sort_field(hd->fmt);
1214 }
1215 return 0;
1216}
1217
1218static int __sort_dimension__add_output(struct sort_dimension *sd)
1219{
1220 if (sd->taken)
1221 return 0;
1222
1223 if (__sort_dimension__add_hpp_output(sd) < 0)
1224 return -1;
1040 1225
1041 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
1042 sd->taken = 1; 1226 sd->taken = 1;
1227 return 0;
1228}
1229
1230static int __hpp_dimension__add_output(struct hpp_dimension *hd)
1231{
1232 if (!hd->taken) {
1233 hd->taken = 1;
1234
1235 perf_hpp__column_register(hd->fmt);
1236 }
1237 return 0;
1043} 1238}
1044 1239
1045int sort_dimension__add(const char *tok) 1240int sort_dimension__add(const char *tok)
@@ -1068,8 +1263,16 @@ int sort_dimension__add(const char *tok)
1068 sort__has_dso = 1; 1263 sort__has_dso = 1;
1069 } 1264 }
1070 1265
1071 __sort_dimension__add(sd, i); 1266 return __sort_dimension__add(sd);
1072 return 0; 1267 }
1268
1269 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1270 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1271
1272 if (strncasecmp(tok, hd->name, strlen(tok)))
1273 continue;
1274
1275 return __hpp_dimension__add(hd);
1073 } 1276 }
1074 1277
1075 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { 1278 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
@@ -1084,7 +1287,7 @@ int sort_dimension__add(const char *tok)
1084 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 1287 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
1085 sort__has_sym = 1; 1288 sort__has_sym = 1;
1086 1289
1087 __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); 1290 __sort_dimension__add(sd);
1088 return 0; 1291 return 0;
1089 } 1292 }
1090 1293
@@ -1100,18 +1303,47 @@ int sort_dimension__add(const char *tok)
1100 if (sd->entry == &sort_mem_daddr_sym) 1303 if (sd->entry == &sort_mem_daddr_sym)
1101 sort__has_sym = 1; 1304 sort__has_sym = 1;
1102 1305
1103 __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); 1306 __sort_dimension__add(sd);
1104 return 0; 1307 return 0;
1105 } 1308 }
1106 1309
1107 return -ESRCH; 1310 return -ESRCH;
1108} 1311}
1109 1312
1110int setup_sorting(void) 1313static const char *get_default_sort_order(void)
1111{ 1314{
1112 char *tmp, *tok, *str = strdup(sort_order); 1315 const char *default_sort_orders[] = {
1316 default_sort_order,
1317 default_branch_sort_order,
1318 default_mem_sort_order,
1319 default_top_sort_order,
1320 default_diff_sort_order,
1321 };
1322
1323 BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
1324
1325 return default_sort_orders[sort__mode];
1326}
1327
1328static int __setup_sorting(void)
1329{
1330 char *tmp, *tok, *str;
1331 const char *sort_keys = sort_order;
1113 int ret = 0; 1332 int ret = 0;
1114 1333
1334 if (sort_keys == NULL) {
1335 if (field_order) {
1336 /*
1337 * If user specified field order but no sort order,
1338 * we'll honor it and not add default sort orders.
1339 */
1340 return 0;
1341 }
1342
1343 sort_keys = get_default_sort_order();
1344 }
1345
1346 str = strdup(sort_keys);
1115 if (str == NULL) { 1347 if (str == NULL) {
1116 error("Not enough memory to setup sort keys"); 1348 error("Not enough memory to setup sort keys");
1117 return -ENOMEM; 1349 return -ENOMEM;
@@ -1133,66 +1365,235 @@ int setup_sorting(void)
1133 return ret; 1365 return ret;
1134} 1366}
1135 1367
1136static void sort_entry__setup_elide(struct sort_entry *se, 1368void perf_hpp__set_elide(int idx, bool elide)
1137 struct strlist *list, 1369{
1138 const char *list_name, FILE *fp) 1370 struct perf_hpp_fmt *fmt;
1371 struct hpp_sort_entry *hse;
1372
1373 perf_hpp__for_each_format(fmt) {
1374 if (!perf_hpp__is_sort_entry(fmt))
1375 continue;
1376
1377 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1378 if (hse->se->se_width_idx == idx) {
1379 fmt->elide = elide;
1380 break;
1381 }
1382 }
1383}
1384
1385static bool __get_elide(struct strlist *list, const char *list_name, FILE *fp)
1139{ 1386{
1140 if (list && strlist__nr_entries(list) == 1) { 1387 if (list && strlist__nr_entries(list) == 1) {
1141 if (fp != NULL) 1388 if (fp != NULL)
1142 fprintf(fp, "# %s: %s\n", list_name, 1389 fprintf(fp, "# %s: %s\n", list_name,
1143 strlist__entry(list, 0)->s); 1390 strlist__entry(list, 0)->s);
1144 se->elide = true; 1391 return true;
1392 }
1393 return false;
1394}
1395
1396static bool get_elide(int idx, FILE *output)
1397{
1398 switch (idx) {
1399 case HISTC_SYMBOL:
1400 return __get_elide(symbol_conf.sym_list, "symbol", output);
1401 case HISTC_DSO:
1402 return __get_elide(symbol_conf.dso_list, "dso", output);
1403 case HISTC_COMM:
1404 return __get_elide(symbol_conf.comm_list, "comm", output);
1405 default:
1406 break;
1145 } 1407 }
1408
1409 if (sort__mode != SORT_MODE__BRANCH)
1410 return false;
1411
1412 switch (idx) {
1413 case HISTC_SYMBOL_FROM:
1414 return __get_elide(symbol_conf.sym_from_list, "sym_from", output);
1415 case HISTC_SYMBOL_TO:
1416 return __get_elide(symbol_conf.sym_to_list, "sym_to", output);
1417 case HISTC_DSO_FROM:
1418 return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
1419 case HISTC_DSO_TO:
1420 return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
1421 default:
1422 break;
1423 }
1424
1425 return false;
1146} 1426}
1147 1427
1148void sort__setup_elide(FILE *output) 1428void sort__setup_elide(FILE *output)
1149{ 1429{
1150 struct sort_entry *se; 1430 struct perf_hpp_fmt *fmt;
1431 struct hpp_sort_entry *hse;
1151 1432
1152 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, 1433 perf_hpp__for_each_format(fmt) {
1153 "dso", output); 1434 if (!perf_hpp__is_sort_entry(fmt))
1154 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, 1435 continue;
1155 "comm", output); 1436
1156 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, 1437 hse = container_of(fmt, struct hpp_sort_entry, hpp);
1157 "symbol", output); 1438 fmt->elide = get_elide(hse->se->se_width_idx, output);
1158
1159 if (sort__mode == SORT_MODE__BRANCH) {
1160 sort_entry__setup_elide(&sort_dso_from,
1161 symbol_conf.dso_from_list,
1162 "dso_from", output);
1163 sort_entry__setup_elide(&sort_dso_to,
1164 symbol_conf.dso_to_list,
1165 "dso_to", output);
1166 sort_entry__setup_elide(&sort_sym_from,
1167 symbol_conf.sym_from_list,
1168 "sym_from", output);
1169 sort_entry__setup_elide(&sort_sym_to,
1170 symbol_conf.sym_to_list,
1171 "sym_to", output);
1172 } else if (sort__mode == SORT_MODE__MEMORY) {
1173 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1174 "symbol_daddr", output);
1175 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1176 "dso_daddr", output);
1177 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1178 "mem", output);
1179 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1180 "local_weight", output);
1181 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1182 "tlb", output);
1183 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list,
1184 "snoop", output);
1185 } 1439 }
1186 1440
1187 /* 1441 /*
1188 * It makes no sense to elide all of sort entries. 1442 * It makes no sense to elide all of sort entries.
1189 * Just revert them to show up again. 1443 * Just revert them to show up again.
1190 */ 1444 */
1191 list_for_each_entry(se, &hist_entry__sort_list, list) { 1445 perf_hpp__for_each_format(fmt) {
1192 if (!se->elide) 1446 if (!perf_hpp__is_sort_entry(fmt))
1447 continue;
1448
1449 if (!fmt->elide)
1193 return; 1450 return;
1194 } 1451 }
1195 1452
1196 list_for_each_entry(se, &hist_entry__sort_list, list) 1453 perf_hpp__for_each_format(fmt) {
1197 se->elide = false; 1454 if (!perf_hpp__is_sort_entry(fmt))
1455 continue;
1456
1457 fmt->elide = false;
1458 }
1459}
1460
1461static int output_field_add(char *tok)
1462{
1463 unsigned int i;
1464
1465 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) {
1466 struct sort_dimension *sd = &common_sort_dimensions[i];
1467
1468 if (strncasecmp(tok, sd->name, strlen(tok)))
1469 continue;
1470
1471 return __sort_dimension__add_output(sd);
1472 }
1473
1474 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) {
1475 struct hpp_dimension *hd = &hpp_sort_dimensions[i];
1476
1477 if (strncasecmp(tok, hd->name, strlen(tok)))
1478 continue;
1479
1480 return __hpp_dimension__add_output(hd);
1481 }
1482
1483 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) {
1484 struct sort_dimension *sd = &bstack_sort_dimensions[i];
1485
1486 if (strncasecmp(tok, sd->name, strlen(tok)))
1487 continue;
1488
1489 return __sort_dimension__add_output(sd);
1490 }
1491
1492 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) {
1493 struct sort_dimension *sd = &memory_sort_dimensions[i];
1494
1495 if (strncasecmp(tok, sd->name, strlen(tok)))
1496 continue;
1497
1498 return __sort_dimension__add_output(sd);
1499 }
1500
1501 return -ESRCH;
1502}
1503
1504static void reset_dimensions(void)
1505{
1506 unsigned int i;
1507
1508 for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++)
1509 common_sort_dimensions[i].taken = 0;
1510
1511 for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++)
1512 hpp_sort_dimensions[i].taken = 0;
1513
1514 for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++)
1515 bstack_sort_dimensions[i].taken = 0;
1516
1517 for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++)
1518 memory_sort_dimensions[i].taken = 0;
1519}
1520
1521static int __setup_output_field(void)
1522{
1523 char *tmp, *tok, *str;
1524 int ret = 0;
1525
1526 if (field_order == NULL)
1527 return 0;
1528
1529 reset_dimensions();
1530
1531 str = strdup(field_order);
1532 if (str == NULL) {
1533 error("Not enough memory to setup output fields");
1534 return -ENOMEM;
1535 }
1536
1537 for (tok = strtok_r(str, ", ", &tmp);
1538 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1539 ret = output_field_add(tok);
1540 if (ret == -EINVAL) {
1541 error("Invalid --fields key: `%s'", tok);
1542 break;
1543 } else if (ret == -ESRCH) {
1544 error("Unknown --fields key: `%s'", tok);
1545 break;
1546 }
1547 }
1548
1549 free(str);
1550 return ret;
1551}
1552
1553int setup_sorting(void)
1554{
1555 int err;
1556
1557 err = __setup_sorting();
1558 if (err < 0)
1559 return err;
1560
1561 if (parent_pattern != default_parent_pattern) {
1562 err = sort_dimension__add("parent");
1563 if (err < 0)
1564 return err;
1565 }
1566
1567 reset_dimensions();
1568
1569 /*
1570 * perf diff doesn't use default hpp output fields.
1571 */
1572 if (sort__mode != SORT_MODE__DIFF)
1573 perf_hpp__init();
1574
1575 err = __setup_output_field();
1576 if (err < 0)
1577 return err;
1578
1579 /* copy sort keys to output fields */
1580 perf_hpp__setup_output_field();
1581 /* and then copy output fields to sort keys */
1582 perf_hpp__append_sort_keys();
1583
1584 return 0;
1585}
1586
1587void reset_output_field(void)
1588{
1589 sort__need_collapse = 0;
1590 sort__has_parent = 0;
1591 sort__has_sym = 0;
1592 sort__has_dso = 0;
1593
1594 field_order = NULL;
1595 sort_order = NULL;
1596
1597 reset_dimensions();
1598 perf_hpp__reset_output_field();
1198} 1599}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 43e5ff42a609..5bf0098d6b06 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -20,11 +20,12 @@
20 20
21#include "parse-options.h" 21#include "parse-options.h"
22#include "parse-events.h" 22#include "parse-events.h"
23 23#include "hist.h"
24#include "thread.h" 24#include "thread.h"
25 25
26extern regex_t parent_regex; 26extern regex_t parent_regex;
27extern const char *sort_order; 27extern const char *sort_order;
28extern const char *field_order;
28extern const char default_parent_pattern[]; 29extern const char default_parent_pattern[];
29extern const char *parent_pattern; 30extern const char *parent_pattern;
30extern const char default_sort_order[]; 31extern const char default_sort_order[];
@@ -81,6 +82,7 @@ struct hist_entry {
81 struct list_head head; 82 struct list_head head;
82 } pairs; 83 } pairs;
83 struct he_stat stat; 84 struct he_stat stat;
85 struct he_stat *stat_acc;
84 struct map_symbol ms; 86 struct map_symbol ms;
85 struct thread *thread; 87 struct thread *thread;
86 struct comm *comm; 88 struct comm *comm;
@@ -129,10 +131,27 @@ static inline void hist_entry__add_pair(struct hist_entry *pair,
129 list_add_tail(&pair->pairs.node, &he->pairs.head); 131 list_add_tail(&pair->pairs.node, &he->pairs.head);
130} 132}
131 133
134static inline float hist_entry__get_percent_limit(struct hist_entry *he)
135{
136 u64 period = he->stat.period;
137 u64 total_period = hists__total_period(he->hists);
138
139 if (unlikely(total_period == 0))
140 return 0;
141
142 if (symbol_conf.cumulate_callchain)
143 period = he->stat_acc->period;
144
145 return period * 100.0 / total_period;
146}
147
148
132enum sort_mode { 149enum sort_mode {
133 SORT_MODE__NORMAL, 150 SORT_MODE__NORMAL,
134 SORT_MODE__BRANCH, 151 SORT_MODE__BRANCH,
135 SORT_MODE__MEMORY, 152 SORT_MODE__MEMORY,
153 SORT_MODE__TOP,
154 SORT_MODE__DIFF,
136}; 155};
137 156
138enum sort_type { 157enum sort_type {
@@ -179,18 +198,21 @@ struct sort_entry {
179 198
180 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); 199 int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
181 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); 200 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
201 int64_t (*se_sort)(struct hist_entry *, struct hist_entry *);
182 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, 202 int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size,
183 unsigned int width); 203 unsigned int width);
184 u8 se_width_idx; 204 u8 se_width_idx;
185 bool elide;
186}; 205};
187 206
188extern struct sort_entry sort_thread; 207extern struct sort_entry sort_thread;
189extern struct list_head hist_entry__sort_list; 208extern struct list_head hist_entry__sort_list;
190 209
191int setup_sorting(void); 210int setup_sorting(void);
211int setup_output_field(void);
212void reset_output_field(void);
192extern int sort_dimension__add(const char *); 213extern int sort_dimension__add(const char *);
193void sort__setup_elide(FILE *fp); 214void sort__setup_elide(FILE *fp);
215void perf_hpp__set_elide(int idx, bool elide);
194 216
195int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); 217int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
196 218
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index ae8ccd7227cf..5667fc3e39cf 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_STATS_H 1#ifndef __PERF_STATS_H
2#define __PERF_STATS_H 2#define __PERF_STATS_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6struct stats 6struct stats
7{ 7{
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index 43262b83c541..6a0a13d07a28 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -17,7 +17,7 @@
17#include <stdlib.h> 17#include <stdlib.h>
18#include <unistd.h> 18#include <unistd.h>
19#include <string.h> 19#include <string.h>
20#include <linux/bitops.h> 20#include <linux/bitmap.h>
21 21
22#include "perf.h" 22#include "perf.h"
23#include "svghelper.h" 23#include "svghelper.h"
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h
index f7b4d6e699ea..e3aff5332e30 100644
--- a/tools/perf/util/svghelper.h
+++ b/tools/perf/util/svghelper.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_SVGHELPER_H 1#ifndef __PERF_SVGHELPER_H
2#define __PERF_SVGHELPER_H 2#define __PERF_SVGHELPER_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); 6extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end);
7extern void svg_box(int Yslot, u64 start, u64 end, const char *type); 7extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 95e249779931..7b9096f29cdb 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -29,11 +29,12 @@ int vmlinux_path__nr_entries;
29char **vmlinux_path; 29char **vmlinux_path;
30 30
31struct symbol_conf symbol_conf = { 31struct symbol_conf symbol_conf = {
32 .use_modules = true, 32 .use_modules = true,
33 .try_vmlinux_path = true, 33 .try_vmlinux_path = true,
34 .annotate_src = true, 34 .annotate_src = true,
35 .demangle = true, 35 .demangle = true,
36 .symfs = "", 36 .cumulate_callchain = true,
37 .symfs = "",
37}; 38};
38 39
39static enum dso_binary_type binary_type_symtab[] = { 40static enum dso_binary_type binary_type_symtab[] = {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 501e4e722e8e..615c752dd767 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -12,6 +12,7 @@
12#include <byteswap.h> 12#include <byteswap.h>
13#include <libgen.h> 13#include <libgen.h>
14#include "build-id.h" 14#include "build-id.h"
15#include "event.h"
15 16
16#ifdef HAVE_LIBELF_SUPPORT 17#ifdef HAVE_LIBELF_SUPPORT
17#include <libelf.h> 18#include <libelf.h>
@@ -108,6 +109,7 @@ struct symbol_conf {
108 show_nr_samples, 109 show_nr_samples,
109 show_total_period, 110 show_total_period,
110 use_callchain, 111 use_callchain,
112 cumulate_callchain,
111 exclude_other, 113 exclude_other,
112 show_cpu_utilization, 114 show_cpu_utilization,
113 initialized, 115 initialized,
@@ -115,7 +117,8 @@ struct symbol_conf {
115 annotate_asm_raw, 117 annotate_asm_raw,
116 annotate_src, 118 annotate_src,
117 event_group, 119 event_group,
118 demangle; 120 demangle,
121 filter_relative;
119 const char *vmlinux_name, 122 const char *vmlinux_name,
120 *kallsyms_name, 123 *kallsyms_name,
121 *source_prefix, 124 *source_prefix,
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 3ce0498bdae6..2fde0d5e40b5 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -8,6 +8,22 @@
8#include "debug.h" 8#include "debug.h"
9#include "comm.h" 9#include "comm.h"
10 10
11int thread__init_map_groups(struct thread *thread, struct machine *machine)
12{
13 struct thread *leader;
14 pid_t pid = thread->pid_;
15
16 if (pid == thread->tid) {
17 thread->mg = map_groups__new();
18 } else {
19 leader = machine__findnew_thread(machine, pid, pid);
20 if (leader)
21 thread->mg = map_groups__get(leader->mg);
22 }
23
24 return thread->mg ? 0 : -1;
25}
26
11struct thread *thread__new(pid_t pid, pid_t tid) 27struct thread *thread__new(pid_t pid, pid_t tid)
12{ 28{
13 char *comm_str; 29 char *comm_str;
@@ -15,7 +31,6 @@ struct thread *thread__new(pid_t pid, pid_t tid)
15 struct thread *thread = zalloc(sizeof(*thread)); 31 struct thread *thread = zalloc(sizeof(*thread));
16 32
17 if (thread != NULL) { 33 if (thread != NULL) {
18 map_groups__init(&thread->mg);
19 thread->pid_ = pid; 34 thread->pid_ = pid;
20 thread->tid = tid; 35 thread->tid = tid;
21 thread->ppid = -1; 36 thread->ppid = -1;
@@ -45,7 +60,8 @@ void thread__delete(struct thread *thread)
45{ 60{
46 struct comm *comm, *tmp; 61 struct comm *comm, *tmp;
47 62
48 map_groups__exit(&thread->mg); 63 map_groups__put(thread->mg);
64 thread->mg = NULL;
49 list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { 65 list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) {
50 list_del(&comm->list); 66 list_del(&comm->list);
51 comm__free(comm); 67 comm__free(comm);
@@ -111,18 +127,35 @@ int thread__comm_len(struct thread *thread)
111size_t thread__fprintf(struct thread *thread, FILE *fp) 127size_t thread__fprintf(struct thread *thread, FILE *fp)
112{ 128{
113 return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + 129 return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) +
114 map_groups__fprintf(&thread->mg, verbose, fp); 130 map_groups__fprintf(thread->mg, verbose, fp);
115} 131}
116 132
117void thread__insert_map(struct thread *thread, struct map *map) 133void thread__insert_map(struct thread *thread, struct map *map)
118{ 134{
119 map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); 135 map_groups__fixup_overlappings(thread->mg, map, verbose, stderr);
120 map_groups__insert(&thread->mg, map); 136 map_groups__insert(thread->mg, map);
137}
138
139static int thread__clone_map_groups(struct thread *thread,
140 struct thread *parent)
141{
142 int i;
143
144 /* This is new thread, we share map groups for process. */
145 if (thread->pid_ == parent->pid_)
146 return 0;
147
148 /* But this one is new process, copy maps. */
149 for (i = 0; i < MAP__NR_TYPES; ++i)
150 if (map_groups__clone(thread->mg, parent->mg, i) < 0)
151 return -ENOMEM;
152
153 return 0;
121} 154}
122 155
123int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) 156int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
124{ 157{
125 int i, err; 158 int err;
126 159
127 if (parent->comm_set) { 160 if (parent->comm_set) {
128 const char *comm = thread__comm_str(parent); 161 const char *comm = thread__comm_str(parent);
@@ -134,13 +167,8 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
134 thread->comm_set = true; 167 thread->comm_set = true;
135 } 168 }
136 169
137 for (i = 0; i < MAP__NR_TYPES; ++i)
138 if (map_groups__clone(&thread->mg, &parent->mg, i) < 0)
139 return -ENOMEM;
140
141 thread->ppid = parent->tid; 170 thread->ppid = parent->tid;
142 171 return thread__clone_map_groups(thread, parent);
143 return 0;
144} 172}
145 173
146void thread__find_cpumode_addr_location(struct thread *thread, 174void thread__find_cpumode_addr_location(struct thread *thread,
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 9b29f085aede..3c0c2724f82c 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -13,7 +13,7 @@ struct thread {
13 struct rb_node rb_node; 13 struct rb_node rb_node;
14 struct list_head node; 14 struct list_head node;
15 }; 15 };
16 struct map_groups mg; 16 struct map_groups *mg;
17 pid_t pid_; /* Not all tools update this */ 17 pid_t pid_; /* Not all tools update this */
18 pid_t tid; 18 pid_t tid;
19 pid_t ppid; 19 pid_t ppid;
@@ -30,6 +30,7 @@ struct machine;
30struct comm; 30struct comm;
31 31
32struct thread *thread__new(pid_t pid, pid_t tid); 32struct thread *thread__new(pid_t pid, pid_t tid);
33int thread__init_map_groups(struct thread *thread, struct machine *machine);
33void thread__delete(struct thread *thread); 34void thread__delete(struct thread *thread);
34static inline void thread__exited(struct thread *thread) 35static inline void thread__exited(struct thread *thread)
35{ 36{
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index dab14d0ad3d0..f92c37abb0a8 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -2,7 +2,7 @@
2#define __PERF_TOP_H 1 2#define __PERF_TOP_H 1
3 3
4#include "tool.h" 4#include "tool.h"
5#include "types.h" 5#include <linux/types.h>
6#include <stddef.h> 6#include <stddef.h>
7#include <stdbool.h> 7#include <stdbool.h>
8#include <termios.h> 8#include <termios.h>
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h
deleted file mode 100644
index c51fa6b70a28..000000000000
--- a/tools/perf/util/types.h
+++ /dev/null
@@ -1,24 +0,0 @@
1#ifndef __PERF_TYPES_H
2#define __PERF_TYPES_H
3
4#include <stdint.h>
5
6/*
7 * We define u64 as uint64_t for every architecture
8 * so that we can print it with "%"PRIx64 without getting warnings.
9 */
10typedef uint64_t u64;
11typedef int64_t s64;
12typedef unsigned int u32;
13typedef signed int s32;
14typedef unsigned short u16;
15typedef signed short s16;
16typedef unsigned char u8;
17typedef signed char s8;
18
19union u64_swap {
20 u64 val64;
21 u32 val32[2];
22};
23
24#endif /* __PERF_TYPES_H */
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 67db73ec3dab..5ec80a575b50 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -7,7 +7,7 @@
7#include "unwind-libdw.h" 7#include "unwind-libdw.h"
8#include "machine.h" 8#include "machine.h"
9#include "thread.h" 9#include "thread.h"
10#include "types.h" 10#include <linux/types.h>
11#include "event.h" 11#include "event.h"
12#include "perf_regs.h" 12#include "perf_regs.h"
13 13
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index b031316f221a..f03061260b4e 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -1,7 +1,7 @@
1#ifndef __UNWIND_H 1#ifndef __UNWIND_H
2#define __UNWIND_H 2#define __UNWIND_H
3 3
4#include "types.h" 4#include <linux/types.h>
5#include "event.h" 5#include "event.h"
6#include "symbol.h" 6#include "symbol.h"
7 7
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 9f66549562bd..7fff6be07f07 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -166,6 +166,8 @@ static ssize_t ion(bool is_read, int fd, void *buf, size_t n)
166 ssize_t ret = is_read ? read(fd, buf, left) : 166 ssize_t ret = is_read ? read(fd, buf, left) :
167 write(fd, buf, left); 167 write(fd, buf, left);
168 168
169 if (ret < 0 && errno == EINTR)
170 continue;
169 if (ret <= 0) 171 if (ret <= 0)
170 return ret; 172 return ret;
171 173
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 6995d66f225c..b03da44e94e4 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -69,7 +69,7 @@
69#include <sys/ioctl.h> 69#include <sys/ioctl.h>
70#include <inttypes.h> 70#include <inttypes.h>
71#include <linux/magic.h> 71#include <linux/magic.h>
72#include "types.h" 72#include <linux/types.h>
73#include <sys/ttydefaults.h> 73#include <sys/ttydefaults.h>
74#include <api/fs/debugfs.h> 74#include <api/fs/debugfs.h>
75#include <termios.h> 75#include <termios.h>
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h
index 2fa967e1a88a..b21a80c6cf8d 100644
--- a/tools/perf/util/values.h
+++ b/tools/perf/util/values.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_VALUES_H 1#ifndef __PERF_VALUES_H
2#define __PERF_VALUES_H 2#define __PERF_VALUES_H
3 3
4#include "types.h" 4#include <linux/types.h>
5 5
6struct perf_read_values { 6struct perf_read_values {
7 int threads; 7 int threads;