diff options
Diffstat (limited to 'tools/perf')
169 files changed, 11173 insertions, 4871 deletions
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index ef6d22e879eb..eb30044a922a 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -222,10 +222,14 @@ install-pdf: pdf | |||
222 | #install-html: html | 222 | #install-html: html |
223 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) | 223 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) |
224 | 224 | ||
225 | ifneq ($(MAKECMDGOALS),clean) | ||
226 | ifneq ($(MAKECMDGOALS),tags) | ||
225 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 227 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
226 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE | 228 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE |
227 | 229 | ||
228 | -include $(OUTPUT)PERF-VERSION-FILE | 230 | -include $(OUTPUT)PERF-VERSION-FILE |
231 | endif | ||
232 | endif | ||
229 | 233 | ||
230 | # | 234 | # |
231 | # Determine "include::" file references in asciidoc files. | 235 | # Determine "include::" file references in asciidoc files. |
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index c8ffd9fd5c6a..e9cd39a92dc2 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -61,11 +61,13 @@ OPTIONS | |||
61 | 61 | ||
62 | --stdio:: Use the stdio interface. | 62 | --stdio:: Use the stdio interface. |
63 | 63 | ||
64 | --tui:: Use the TUI interface Use of --tui requires a tty, if one is not | 64 | --tui:: Use the TUI interface. Use of --tui requires a tty, if one is not |
65 | present, as when piping to other commands, the stdio interface is | 65 | present, as when piping to other commands, the stdio interface is |
66 | used. This interfaces starts by centering on the line with more | 66 | used. This interfaces starts by centering on the line with more |
67 | samples, TAB/UNTAB cycles through the lines with more samples. | 67 | samples, TAB/UNTAB cycles through the lines with more samples. |
68 | 68 | ||
69 | --gtk:: Use the GTK interface. | ||
70 | |||
69 | -C:: | 71 | -C:: |
70 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | 72 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can |
71 | be provided as a comma-separated list with no space: 0,1. Ranges of | 73 | be provided as a comma-separated list with no space: 0,1. Ranges of |
@@ -88,6 +90,12 @@ OPTIONS | |||
88 | --objdump=<path>:: | 90 | --objdump=<path>:: |
89 | Path to objdump binary. | 91 | Path to objdump binary. |
90 | 92 | ||
93 | --skip-missing:: | ||
94 | Skip symbols that cannot be annotated. | ||
95 | |||
96 | --group:: | ||
97 | Show event group information together | ||
98 | |||
91 | SEE ALSO | 99 | SEE ALSO |
92 | -------- | 100 | -------- |
93 | linkperf:perf-record[1], linkperf:perf-report[1] | 101 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt index fae174dc7d01..5032a142853e 100644 --- a/tools/perf/Documentation/perf-archive.txt +++ b/tools/perf/Documentation/perf-archive.txt | |||
@@ -13,7 +13,7 @@ SYNOPSIS | |||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command runs runs perf-buildid-list --with-hits, and collects the files | 15 | This command runs runs perf-buildid-list --with-hits, and collects the files |
16 | with the buildids found so that analisys of perf.data contents can be possible | 16 | with the buildids found so that analysis of perf.data contents can be possible |
17 | on another machine. | 17 | on another machine. |
18 | 18 | ||
19 | 19 | ||
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index c1057701a7dc..e9a8349a7172 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -24,6 +24,13 @@ OPTIONS | |||
24 | -r:: | 24 | -r:: |
25 | --remove=:: | 25 | --remove=:: |
26 | Remove specified file from the cache. | 26 | Remove specified file from the cache. |
27 | -M:: | ||
28 | --missing=:: | ||
29 | List missing build ids in the cache for the specified file. | ||
30 | -u:: | ||
31 | --update:: | ||
32 | Update specified file of the cache. It can be used to update kallsyms | ||
33 | kernel dso to vmlinux in order to support annotation. | ||
27 | -v:: | 34 | -v:: |
28 | --verbose:: | 35 | --verbose:: |
29 | Be more verbose. | 36 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 194f37d635df..5b3123d5721f 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -22,10 +22,6 @@ specified perf.data files. | |||
22 | 22 | ||
23 | OPTIONS | 23 | OPTIONS |
24 | ------- | 24 | ------- |
25 | -M:: | ||
26 | --displacement:: | ||
27 | Show position displacement relative to baseline. | ||
28 | |||
29 | -D:: | 25 | -D:: |
30 | --dump-raw-trace:: | 26 | --dump-raw-trace:: |
31 | Dump raw trace in ASCII. | 27 | Dump raw trace in ASCII. |
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt index 15217345c2fa..1ceb3700ffbb 100644 --- a/tools/perf/Documentation/perf-evlist.txt +++ b/tools/perf/Documentation/perf-evlist.txt | |||
@@ -28,6 +28,10 @@ OPTIONS | |||
28 | --verbose=:: | 28 | --verbose=:: |
29 | Show all fields. | 29 | Show all fields. |
30 | 30 | ||
31 | -g:: | ||
32 | --group:: | ||
33 | Show event group information. | ||
34 | |||
31 | SEE ALSO | 35 | SEE ALSO |
32 | -------- | 36 | -------- |
33 | linkperf:perf-record[1], linkperf:perf-list[1], | 37 | linkperf:perf-record[1], linkperf:perf-list[1], |
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt new file mode 100644 index 000000000000..888d51137fbe --- /dev/null +++ b/tools/perf/Documentation/perf-mem.txt | |||
@@ -0,0 +1,48 @@ | |||
1 | perf-mem(1) | ||
2 | =========== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-mem - Profile memory accesses | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf mem' [<options>] (record [<command>] | report) | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | "perf mem -t <TYPE> record" runs a command and gathers memory operation data | ||
16 | from it, into perf.data. Perf record options are accepted and are passed through. | ||
17 | |||
18 | "perf mem -t <TYPE> report" displays the result. It invokes perf report with the | ||
19 | right set of options to display a memory access profile. | ||
20 | |||
21 | OPTIONS | ||
22 | ------- | ||
23 | <command>...:: | ||
24 | Any command you can specify in a shell. | ||
25 | |||
26 | -t:: | ||
27 | --type=:: | ||
28 | Select the memory operation type: load or store (default: load) | ||
29 | |||
30 | -D:: | ||
31 | --dump-raw-samples=:: | ||
32 | Dump the raw decoded samples on the screen in a format that is easy to parse with | ||
33 | one sample per line. | ||
34 | |||
35 | -x:: | ||
36 | --field-separator:: | ||
37 | Specify the field separator used when dump raw samples (-D option). By default, | ||
38 | The separator is the space character. | ||
39 | |||
40 | -C:: | ||
41 | --cpu-list:: | ||
42 | Restrict dump of raw samples to those provided via this option. Note that the same | ||
43 | option can be passed in record mode. It will be interpreted the same way as perf | ||
44 | record. | ||
45 | |||
46 | SEE ALSO | ||
47 | -------- | ||
48 | linkperf:perf-record[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 938e8904f64d..d4da111ef53d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -182,6 +182,12 @@ is enabled for all the sampling events. The sampled branch type is the same for | |||
182 | The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k | 182 | The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k |
183 | Note that this feature may not be available on all processors. | 183 | Note that this feature may not be available on all processors. |
184 | 184 | ||
185 | -W:: | ||
186 | --weight:: | ||
187 | Enable weightened sampling. An additional weight is recorded per sample and can be | ||
188 | displayed with the weight and local_weight sort keys. This currently works for TSX | ||
189 | abort events and some memory events in precise mode on modern Intel CPUs. | ||
190 | |||
185 | SEE ALSO | 191 | SEE ALSO |
186 | -------- | 192 | -------- |
187 | linkperf:perf-stat[1], linkperf:perf-list[1] | 193 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index f4d91bebd59d..66dab7410c1d 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -57,11 +57,44 @@ OPTIONS | |||
57 | 57 | ||
58 | -s:: | 58 | -s:: |
59 | --sort=:: | 59 | --sort=:: |
60 | Sort by key(s): pid, comm, dso, symbol, parent, srcline. | 60 | Sort histogram entries by given key(s) - multiple keys can be specified |
61 | in CSV format. Following sort keys are available: | ||
62 | pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight. | ||
63 | |||
64 | Each key has following meaning: | ||
65 | |||
66 | - comm: command (name) of the task which can be read via /proc/<pid>/comm | ||
67 | - pid: command and tid of the task | ||
68 | - dso: name of library or module executed at the time of sample | ||
69 | - symbol: name of function executed at the time of sample | ||
70 | - parent: name of function matched to the parent regex filter. Unmatched | ||
71 | entries are displayed as "[other]". | ||
72 | - cpu: cpu number the task ran at the time of sample | ||
73 | - srcline: filename and line number executed at the time of sample. The | ||
74 | DWARF debuggin info must be provided. | ||
75 | |||
76 | By default, comm, dso and symbol keys are used. | ||
77 | (i.e. --sort comm,dso,symbol) | ||
78 | |||
79 | If --branch-stack option is used, following sort keys are also | ||
80 | available: | ||
81 | dso_from, dso_to, symbol_from, symbol_to, mispredict. | ||
82 | |||
83 | - dso_from: name of library or module branched from | ||
84 | - dso_to: name of library or module branched to | ||
85 | - symbol_from: name of function branched from | ||
86 | - symbol_to: name of function branched to | ||
87 | - mispredict: "N" for predicted branch, "Y" for mispredicted branch | ||
88 | |||
89 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to | ||
90 | and symbol_to, see '--branch-stack'. | ||
61 | 91 | ||
62 | -p:: | 92 | -p:: |
63 | --parent=<regex>:: | 93 | --parent=<regex>:: |
64 | regex filter to identify parent, see: '--sort parent' | 94 | A regex filter to identify parent. The parent is a caller of this |
95 | function and searched through the callchain, thus it requires callchain | ||
96 | information recorded. The pattern is in the exteneded regex format and | ||
97 | defaults to "\^sys_|^do_page_fault", see '--sort parent'. | ||
65 | 98 | ||
66 | -x:: | 99 | -x:: |
67 | --exclude-other:: | 100 | --exclude-other:: |
@@ -74,7 +107,6 @@ OPTIONS | |||
74 | 107 | ||
75 | -t:: | 108 | -t:: |
76 | --field-separator=:: | 109 | --field-separator=:: |
77 | |||
78 | Use a special separator character and don't pad with spaces, replacing | 110 | Use a special separator character and don't pad with spaces, replacing |
79 | all occurrences of this separator in symbol names (and other output) | 111 | all occurrences of this separator in symbol names (and other output) |
80 | with a '.' character, that thus it's the only non valid separator. | 112 | with a '.' character, that thus it's the only non valid separator. |
@@ -171,6 +203,17 @@ OPTIONS | |||
171 | --objdump=<path>:: | 203 | --objdump=<path>:: |
172 | Path to objdump binary. | 204 | Path to objdump binary. |
173 | 205 | ||
206 | --group:: | ||
207 | Show event group information together. | ||
208 | |||
209 | --demangle:: | ||
210 | Demangle symbol names to human readable form. It's enabled by default, | ||
211 | disable with --no-demangle. | ||
212 | |||
213 | --percent-limit:: | ||
214 | Do not show entries which have an overhead under that percent. | ||
215 | (Default: 0). | ||
216 | |||
174 | SEE ALSO | 217 | SEE ALSO |
175 | -------- | 218 | -------- |
176 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 219 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt index a4027f221a53..9f1f054b8432 100644 --- a/tools/perf/Documentation/perf-script-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt | |||
@@ -336,7 +336,6 @@ scripts listed by the 'perf script -l' command e.g.: | |||
336 | ---- | 336 | ---- |
337 | root@tropicana:~# perf script -l | 337 | root@tropicana:~# perf script -l |
338 | List of available trace scripts: | 338 | List of available trace scripts: |
339 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
340 | wakeup-latency system-wide min/max/avg wakeup latency | 339 | wakeup-latency system-wide min/max/avg wakeup latency |
341 | rw-by-file <comm> r/w activity for a program, by file | 340 | rw-by-file <comm> r/w activity for a program, by file |
342 | rw-by-pid system-wide r/w activity | 341 | rw-by-pid system-wide r/w activity |
@@ -402,7 +401,6 @@ should show a new entry for your script: | |||
402 | ---- | 401 | ---- |
403 | root@tropicana:~# perf script -l | 402 | root@tropicana:~# perf script -l |
404 | List of available trace scripts: | 403 | List of available trace scripts: |
405 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
406 | wakeup-latency system-wide min/max/avg wakeup latency | 404 | wakeup-latency system-wide min/max/avg wakeup latency |
407 | rw-by-file <comm> r/w activity for a program, by file | 405 | rw-by-file <comm> r/w activity for a program, by file |
408 | rw-by-pid system-wide r/w activity | 406 | rw-by-pid system-wide r/w activity |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index cf0c3107e06e..2fe87fb558f0 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -52,7 +52,7 @@ OPTIONS | |||
52 | 52 | ||
53 | -r:: | 53 | -r:: |
54 | --repeat=<n>:: | 54 | --repeat=<n>:: |
55 | repeat command and print average + stddev (max: 100) | 55 | repeat command and print average + stddev (max: 100). 0 means forever. |
56 | 56 | ||
57 | -B:: | 57 | -B:: |
58 | --big-num:: | 58 | --big-num:: |
@@ -114,6 +114,23 @@ with it. --append may be used here. Examples: | |||
114 | 114 | ||
115 | perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage | 115 | perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage |
116 | 116 | ||
117 | -I msecs:: | ||
118 | --interval-print msecs:: | ||
119 | Print count deltas every N milliseconds (minimum: 100ms) | ||
120 | example: perf stat -I 1000 -e cycles -a sleep 5 | ||
121 | |||
122 | --per-socket:: | ||
123 | Aggregate counts per processor socket for system-wide mode measurements. This | ||
124 | is a useful mode to detect imbalance between sockets. To enable this mode, | ||
125 | use --per-socket in addition to -a. (system-wide). The output includes the | ||
126 | socket number and the number of online processors on that socket. This is | ||
127 | useful to gauge the amount of aggregation. | ||
128 | |||
129 | --per-core:: | ||
130 | Aggregate counts per physical processor for system-wide mode measurements. This | ||
131 | is a useful mode to detect imbalance between physical cores. To enable this mode, | ||
132 | use --per-core in addition to -a. (system-wide). The output includes the | ||
133 | core number and the number of online logical processors on that physical processor. | ||
117 | 134 | ||
118 | EXAMPLES | 135 | EXAMPLES |
119 | -------- | 136 | -------- |
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index b24ac40fcd58..d1d3e5121f89 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt | |||
@@ -23,6 +23,10 @@ from 'perf test list'. | |||
23 | 23 | ||
24 | OPTIONS | 24 | OPTIONS |
25 | ------- | 25 | ------- |
26 | -s:: | ||
27 | --skip:: | ||
28 | Tests to skip (comma separater numeric list). | ||
29 | |||
26 | -v:: | 30 | -v:: |
27 | --verbose:: | 31 | --verbose:: |
28 | Be more verbose. | 32 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 5b80d84d6b4a..7fdd1909e376 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -60,7 +60,7 @@ Default is to monitor all CPUS. | |||
60 | 60 | ||
61 | -i:: | 61 | -i:: |
62 | --inherit:: | 62 | --inherit:: |
63 | Child tasks inherit counters, only makes sens with -p option. | 63 | Child tasks do not inherit counters. |
64 | 64 | ||
65 | -k <path>:: | 65 | -k <path>:: |
66 | --vmlinux=<path>:: | 66 | --vmlinux=<path>:: |
@@ -112,7 +112,7 @@ Default is to monitor all CPUS. | |||
112 | 112 | ||
113 | -s:: | 113 | -s:: |
114 | --sort:: | 114 | --sort:: |
115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline. | 115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight. |
116 | 116 | ||
117 | -n:: | 117 | -n:: |
118 | --show-nr-samples:: | 118 | --show-nr-samples:: |
@@ -155,6 +155,10 @@ Default is to monitor all CPUS. | |||
155 | 155 | ||
156 | Default: fractal,0.5,callee. | 156 | Default: fractal,0.5,callee. |
157 | 157 | ||
158 | --percent-limit:: | ||
159 | Do not show entries which have an overhead under that percent. | ||
160 | (Default: 0). | ||
161 | |||
158 | INTERACTIVE PROMPTING KEYS | 162 | INTERACTIVE PROMPTING KEYS |
159 | -------------------------- | 163 | -------------------------- |
160 | 164 | ||
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 39d41068484f..025de796067c 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -1,6 +1,7 @@ | |||
1 | tools/perf | 1 | tools/perf |
2 | tools/scripts | 2 | tools/scripts |
3 | tools/lib/traceevent | 3 | tools/lib/traceevent |
4 | tools/lib/lk | ||
4 | include/linux/const.h | 5 | include/linux/const.h |
5 | include/linux/perf_event.h | 6 | include/linux/perf_event.h |
6 | include/linux/rbtree.h | 7 | include/linux/rbtree.h |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 8ab05e543ef4..203cb0eecff2 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -35,7 +35,9 @@ include config/utilities.mak | |||
35 | # | 35 | # |
36 | # Define WERROR=0 to disable treating any warnings as errors. | 36 | # Define WERROR=0 to disable treating any warnings as errors. |
37 | # | 37 | # |
38 | # Define NO_NEWT if you do not want TUI support. | 38 | # Define NO_NEWT if you do not want TUI support. (deprecated) |
39 | # | ||
40 | # Define NO_SLANG if you do not want TUI support. | ||
39 | # | 41 | # |
40 | # Define NO_GTK2 if you do not want GTK+ GUI support. | 42 | # Define NO_GTK2 if you do not want GTK+ GUI support. |
41 | # | 43 | # |
@@ -47,165 +49,65 @@ include config/utilities.mak | |||
47 | # backtrace post unwind. | 49 | # backtrace post unwind. |
48 | # | 50 | # |
49 | # Define NO_BACKTRACE if you do not want stack backtrace debug feature | 51 | # Define NO_BACKTRACE if you do not want stack backtrace debug feature |
52 | # | ||
53 | # Define NO_LIBNUMA if you do not want numa perf benchmark | ||
54 | # | ||
55 | # Define NO_LIBAUDIT if you do not want libaudit support | ||
56 | # | ||
57 | # Define NO_LIBBIONIC if you do not want bionic support | ||
50 | 58 | ||
51 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 59 | ifeq ($(srctree),) |
52 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 60 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) |
53 | -include $(OUTPUT)PERF-VERSION-FILE | 61 | srctree := $(patsubst %/,%,$(dir $(srctree))) |
54 | 62 | #$(info Determined 'srctree' to be $(srctree)) | |
55 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
56 | |||
57 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ | ||
58 | -e s/arm.*/arm/ -e s/sa110/arm/ \ | ||
59 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ | ||
60 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ | ||
61 | -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) | ||
62 | NO_PERF_REGS := 1 | ||
63 | |||
64 | CC = $(CROSS_COMPILE)gcc | ||
65 | AR = $(CROSS_COMPILE)ar | ||
66 | |||
67 | # Additional ARCH settings for x86 | ||
68 | ifeq ($(ARCH),i386) | ||
69 | override ARCH := x86 | ||
70 | NO_PERF_REGS := 0 | ||
71 | LIBUNWIND_LIBS = -lunwind -lunwind-x86 | ||
72 | endif | ||
73 | ifeq ($(ARCH),x86_64) | ||
74 | override ARCH := x86 | ||
75 | IS_X86_64 := 0 | ||
76 | ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) | ||
77 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) | ||
78 | endif | ||
79 | ifeq (${IS_X86_64}, 1) | ||
80 | RAW_ARCH := x86_64 | ||
81 | ARCH_CFLAGS := -DARCH_X86_64 | ||
82 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S | ||
83 | endif | ||
84 | NO_PERF_REGS := 0 | ||
85 | LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 | ||
86 | endif | ||
87 | |||
88 | # Treat warnings as errors unless directed not to | ||
89 | ifneq ($(WERROR),0) | ||
90 | CFLAGS_WERROR := -Werror | ||
91 | endif | 63 | endif |
92 | 64 | ||
93 | ifeq ("$(origin DEBUG)", "command line") | 65 | ifneq ($(objtree),) |
94 | PERF_DEBUG = $(DEBUG) | 66 | #$(info Determined 'objtree' to be $(objtree)) |
95 | endif | ||
96 | ifndef PERF_DEBUG | ||
97 | CFLAGS_OPTIMIZE = -O6 -D_FORTIFY_SOURCE=2 | ||
98 | endif | 67 | endif |
99 | 68 | ||
100 | ifdef PARSER_DEBUG | 69 | ifneq ($(OUTPUT),) |
101 | PARSER_DEBUG_BISON := -t | 70 | #$(info Determined 'OUTPUT' to be $(OUTPUT)) |
102 | PARSER_DEBUG_FLEX := -d | ||
103 | PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG | ||
104 | endif | 71 | endif |
105 | 72 | ||
106 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) | 73 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
107 | EXTLIBS = -lpthread -lrt -lelf -lm | 74 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
108 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | ||
109 | ALL_LDFLAGS = $(LDFLAGS) | ||
110 | STRIP ?= strip | ||
111 | |||
112 | # Among the variables below, these: | ||
113 | # perfexecdir | ||
114 | # template_dir | ||
115 | # mandir | ||
116 | # infodir | ||
117 | # htmldir | ||
118 | # ETC_PERFCONFIG (but not sysconfdir) | ||
119 | # can be specified as a relative path some/where/else; | ||
120 | # this is interpreted as relative to $(prefix) and "perf" at | ||
121 | # runtime figures out where they are based on the path to the executable. | ||
122 | # This can help installing the suite in a relocatable way. | ||
123 | |||
124 | # Make the path relative to DESTDIR, not to prefix | ||
125 | ifndef DESTDIR | ||
126 | prefix = $(HOME) | ||
127 | endif | ||
128 | bindir_relative = bin | ||
129 | bindir = $(prefix)/$(bindir_relative) | ||
130 | mandir = share/man | ||
131 | infodir = share/info | ||
132 | perfexecdir = libexec/perf-core | ||
133 | sharedir = $(prefix)/share | ||
134 | template_dir = share/perf-core/templates | ||
135 | htmldir = share/doc/perf-doc | ||
136 | ifeq ($(prefix),/usr) | ||
137 | sysconfdir = /etc | ||
138 | ETC_PERFCONFIG = $(sysconfdir)/perfconfig | ||
139 | else | ||
140 | sysconfdir = $(prefix)/etc | ||
141 | ETC_PERFCONFIG = etc/perfconfig | ||
142 | endif | ||
143 | lib = lib | ||
144 | 75 | ||
145 | export prefix bindir sharedir sysconfdir | 76 | CC = $(CROSS_COMPILE)gcc |
77 | AR = $(CROSS_COMPILE)ar | ||
146 | 78 | ||
147 | RM = rm -f | 79 | RM = rm -f |
148 | MKDIR = mkdir | 80 | MKDIR = mkdir |
149 | FIND = find | 81 | FIND = find |
150 | INSTALL = install | 82 | INSTALL = install |
83 | FLEX = flex | ||
84 | BISON = bison | ||
85 | STRIP = strip | ||
151 | 86 | ||
152 | # sparse is architecture-neutral, which means that we need to tell it | 87 | LK_DIR = $(srctree)/tools/lib/lk/ |
153 | # explicitly what architecture to check for. Fix this up for yours.. | 88 | TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ |
154 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | ||
155 | |||
156 | -include config/feature-tests.mak | ||
157 | 89 | ||
158 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) | 90 | # include config/Makefile by default and rule out |
159 | CFLAGS := $(CFLAGS) -fstack-protector-all | 91 | # non-config cases |
160 | endif | 92 | config := 1 |
161 | 93 | ||
162 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) | 94 | NON_CONFIG_TARGETS := clean TAGS tags cscope help |
163 | CFLAGS := $(CFLAGS) -Wstack-protector | ||
164 | endif | ||
165 | 95 | ||
166 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) | 96 | ifdef MAKECMDGOALS |
167 | CFLAGS := $(CFLAGS) -Wvolatile-register-var | 97 | ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),) |
98 | config := 0 | ||
168 | endif | 99 | endif |
169 | |||
170 | ### --- END CONFIGURATION SECTION --- | ||
171 | |||
172 | ifeq ($(srctree),) | ||
173 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | ||
174 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
175 | #$(info Determined 'srctree' to be $(srctree)) | ||
176 | endif | 100 | endif |
177 | 101 | ||
178 | ifneq ($(objtree),) | 102 | ifeq ($(config),1) |
179 | #$(info Determined 'objtree' to be $(objtree)) | 103 | include config/Makefile |
180 | endif | 104 | endif |
181 | 105 | ||
182 | ifneq ($(OUTPUT),) | 106 | export prefix bindir sharedir sysconfdir |
183 | #$(info Determined 'OUTPUT' to be $(OUTPUT)) | ||
184 | endif | ||
185 | 107 | ||
186 | BASIC_CFLAGS = \ | 108 | # sparse is architecture-neutral, which means that we need to tell it |
187 | -Iutil/include \ | 109 | # explicitly what architecture to check for. Fix this up for yours.. |
188 | -Iarch/$(ARCH)/include \ | 110 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
189 | $(if $(objtree),-I$(objtree)/arch/$(ARCH)/include/generated/uapi) \ | ||
190 | -I$(srctree)/arch/$(ARCH)/include/uapi \ | ||
191 | -I$(srctree)/arch/$(ARCH)/include \ | ||
192 | $(if $(objtree),-I$(objtree)/include/generated/uapi) \ | ||
193 | -I$(srctree)/include/uapi \ | ||
194 | -I$(srctree)/include \ | ||
195 | -I$(OUTPUT)util \ | ||
196 | -Iutil \ | ||
197 | -I. \ | ||
198 | -I$(TRACE_EVENT_DIR) \ | ||
199 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | ||
200 | |||
201 | BASIC_LDFLAGS = | ||
202 | |||
203 | ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) | ||
204 | BIONIC := 1 | ||
205 | EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) | ||
206 | EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) | ||
207 | BASIC_CFLAGS += -I. | ||
208 | endif | ||
209 | 111 | ||
210 | # Guard against environment variables | 112 | # Guard against environment variables |
211 | BUILTIN_OBJS = | 113 | BUILTIN_OBJS = |
@@ -219,24 +121,38 @@ SCRIPT_SH += perf-archive.sh | |||
219 | grep-libs = $(filter -l%,$(1)) | 121 | grep-libs = $(filter -l%,$(1)) |
220 | strip-libs = $(filter-out -l%,$(1)) | 122 | strip-libs = $(filter-out -l%,$(1)) |
221 | 123 | ||
222 | TRACE_EVENT_DIR = ../lib/traceevent/ | 124 | LK_PATH=$(LK_DIR) |
223 | 125 | ||
224 | ifneq ($(OUTPUT),) | 126 | ifneq ($(OUTPUT),) |
225 | TE_PATH=$(OUTPUT) | 127 | TE_PATH=$(OUTPUT) |
128 | ifneq ($(subdir),) | ||
129 | LK_PATH=$(OUTPUT)$(LK_DIR) | ||
226 | else | 130 | else |
227 | TE_PATH=$(TRACE_EVENT_DIR) | 131 | LK_PATH=$(OUTPUT) |
132 | endif | ||
133 | else | ||
134 | TE_PATH=$(TRACE_EVENT_DIR) | ||
228 | endif | 135 | endif |
229 | 136 | ||
230 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a | 137 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a |
231 | TE_LIB := -L$(TE_PATH) -ltraceevent | 138 | export LIBTRACEEVENT |
232 | 139 | ||
233 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) | 140 | LIBLK = $(LK_PATH)liblk.a |
234 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py | 141 | export LIBLK |
235 | 142 | ||
236 | export LIBTRACEEVENT | 143 | # python extension build directories |
144 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ | ||
145 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ | ||
146 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
147 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
148 | |||
149 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
150 | |||
151 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) | ||
152 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) | ||
237 | 153 | ||
238 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | 154 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) |
239 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 155 | $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
240 | --quiet build_ext; \ | 156 | --quiet build_ext; \ |
241 | mkdir -p $(OUTPUT)python && \ | 157 | mkdir -p $(OUTPUT)python && \ |
242 | cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ | 158 | cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ |
@@ -251,8 +167,6 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) | |||
251 | # | 167 | # |
252 | PROGRAMS += $(OUTPUT)perf | 168 | PROGRAMS += $(OUTPUT)perf |
253 | 169 | ||
254 | LANG_BINDINGS = | ||
255 | |||
256 | # what 'all' will build and 'install' will install, in perfexecdir | 170 | # what 'all' will build and 'install' will install, in perfexecdir |
257 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 171 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
258 | 172 | ||
@@ -261,28 +175,25 @@ OTHER_PROGRAMS = $(OUTPUT)perf | |||
261 | 175 | ||
262 | # Set paths to tools early so that they can be used for version tests. | 176 | # Set paths to tools early so that they can be used for version tests. |
263 | ifndef SHELL_PATH | 177 | ifndef SHELL_PATH |
264 | SHELL_PATH = /bin/sh | 178 | SHELL_PATH = /bin/sh |
265 | endif | 179 | endif |
266 | ifndef PERL_PATH | 180 | ifndef PERL_PATH |
267 | PERL_PATH = /usr/bin/perl | 181 | PERL_PATH = /usr/bin/perl |
268 | endif | 182 | endif |
269 | 183 | ||
270 | export PERL_PATH | 184 | export PERL_PATH |
271 | 185 | ||
272 | FLEX = flex | ||
273 | BISON= bison | ||
274 | |||
275 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c | 186 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c |
276 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c | 187 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c |
277 | 188 | ||
278 | $(OUTPUT)util/parse-events-bison.c: util/parse-events.y | 189 | $(OUTPUT)util/parse-events-bison.c: util/parse-events.y |
279 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c | 190 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_ |
280 | 191 | ||
281 | $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c | 192 | $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c |
282 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c | 193 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c |
283 | 194 | ||
284 | $(OUTPUT)util/pmu-bison.c: util/pmu.y | 195 | $(OUTPUT)util/pmu-bison.c: util/pmu.y |
285 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c | 196 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_ |
286 | 197 | ||
287 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c | 198 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c |
288 | $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c | 199 | $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c |
@@ -329,7 +240,6 @@ LIB_H += util/cache.h | |||
329 | LIB_H += util/callchain.h | 240 | LIB_H += util/callchain.h |
330 | LIB_H += util/build-id.h | 241 | LIB_H += util/build-id.h |
331 | LIB_H += util/debug.h | 242 | LIB_H += util/debug.h |
332 | LIB_H += util/debugfs.h | ||
333 | LIB_H += util/sysfs.h | 243 | LIB_H += util/sysfs.h |
334 | LIB_H += util/pmu.h | 244 | LIB_H += util/pmu.h |
335 | LIB_H += util/event.h | 245 | LIB_H += util/event.h |
@@ -378,8 +288,11 @@ LIB_H += util/rblist.h | |||
378 | LIB_H += util/intlist.h | 288 | LIB_H += util/intlist.h |
379 | LIB_H += util/perf_regs.h | 289 | LIB_H += util/perf_regs.h |
380 | LIB_H += util/unwind.h | 290 | LIB_H += util/unwind.h |
381 | LIB_H += ui/helpline.h | ||
382 | LIB_H += util/vdso.h | 291 | LIB_H += util/vdso.h |
292 | LIB_H += ui/helpline.h | ||
293 | LIB_H += ui/progress.h | ||
294 | LIB_H += ui/util.h | ||
295 | LIB_H += ui/ui.h | ||
383 | 296 | ||
384 | LIB_OBJS += $(OUTPUT)util/abspath.o | 297 | LIB_OBJS += $(OUTPUT)util/abspath.o |
385 | LIB_OBJS += $(OUTPUT)util/alias.o | 298 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -387,7 +300,6 @@ LIB_OBJS += $(OUTPUT)util/annotate.o | |||
387 | LIB_OBJS += $(OUTPUT)util/build-id.o | 300 | LIB_OBJS += $(OUTPUT)util/build-id.o |
388 | LIB_OBJS += $(OUTPUT)util/config.o | 301 | LIB_OBJS += $(OUTPUT)util/config.o |
389 | LIB_OBJS += $(OUTPUT)util/ctype.o | 302 | LIB_OBJS += $(OUTPUT)util/ctype.o |
390 | LIB_OBJS += $(OUTPUT)util/debugfs.o | ||
391 | LIB_OBJS += $(OUTPUT)util/sysfs.o | 303 | LIB_OBJS += $(OUTPUT)util/sysfs.o |
392 | LIB_OBJS += $(OUTPUT)util/pmu.o | 304 | LIB_OBJS += $(OUTPUT)util/pmu.o |
393 | LIB_OBJS += $(OUTPUT)util/environment.o | 305 | LIB_OBJS += $(OUTPUT)util/environment.o |
@@ -453,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/stat.o | |||
453 | LIB_OBJS += $(OUTPUT)ui/setup.o | 365 | LIB_OBJS += $(OUTPUT)ui/setup.o |
454 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 366 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
455 | LIB_OBJS += $(OUTPUT)ui/progress.o | 367 | LIB_OBJS += $(OUTPUT)ui/progress.o |
368 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
456 | LIB_OBJS += $(OUTPUT)ui/hist.o | 369 | LIB_OBJS += $(OUTPUT)ui/hist.o |
457 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o | 370 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o |
458 | 371 | ||
@@ -471,7 +384,12 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o | |||
471 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | 384 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o |
472 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | 385 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o |
473 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 386 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
474 | LIB_OBJS += $(OUTPUT)tests/util.o | 387 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
388 | LIB_OBJS += $(OUTPUT)tests/python-use.o | ||
389 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o | ||
390 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o | ||
391 | LIB_OBJS += $(OUTPUT)tests/task-exit.o | ||
392 | LIB_OBJS += $(OUTPUT)tests/sw-clock.o | ||
475 | 393 | ||
476 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 394 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
477 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 395 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
@@ -504,78 +422,18 @@ BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | |||
504 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | 422 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o |
505 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | 423 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o |
506 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o | 424 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o |
425 | BUILTIN_OBJS += $(OUTPUT)builtin-mem.o | ||
507 | 426 | ||
508 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) | 427 | PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) |
509 | |||
510 | # | ||
511 | # Platform specific tweaks | ||
512 | # | ||
513 | 428 | ||
514 | # We choose to avoid "if .. else if .. else .. endif endif" | 429 | # We choose to avoid "if .. else if .. else .. endif endif" |
515 | # because maintaining the nesting to match is a pain. If | 430 | # because maintaining the nesting to match is a pain. If |
516 | # we had "elif" things would have been much nicer... | 431 | # we had "elif" things would have been much nicer... |
517 | 432 | ||
518 | -include config.mak.autogen | ||
519 | -include config.mak | ||
520 | |||
521 | ifdef NO_LIBELF | ||
522 | NO_DWARF := 1 | ||
523 | NO_DEMANGLE := 1 | ||
524 | NO_LIBUNWIND := 1 | ||
525 | else | ||
526 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | ||
527 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) | ||
528 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) | ||
529 | ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) | ||
530 | LIBC_SUPPORT := 1 | ||
531 | endif | ||
532 | ifeq ($(BIONIC),1) | ||
533 | LIBC_SUPPORT := 1 | ||
534 | endif | ||
535 | ifeq ($(LIBC_SUPPORT),1) | ||
536 | msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); | ||
537 | |||
538 | NO_LIBELF := 1 | ||
539 | NO_DWARF := 1 | ||
540 | NO_DEMANGLE := 1 | ||
541 | else | ||
542 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
543 | endif | ||
544 | else | ||
545 | # for linking with debug library, run like: | ||
546 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ | ||
547 | ifdef LIBDW_DIR | ||
548 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include | ||
549 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib | ||
550 | endif | ||
551 | |||
552 | FLAGS_DWARF=$(ALL_CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | ||
553 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) | ||
554 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | ||
555 | NO_DWARF := 1 | ||
556 | endif # Dwarf support | ||
557 | endif # SOURCE_LIBELF | ||
558 | endif # NO_LIBELF | ||
559 | |||
560 | ifndef NO_LIBUNWIND | ||
561 | # for linking with debug library, run like: | ||
562 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | ||
563 | ifdef LIBUNWIND_DIR | ||
564 | LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include | ||
565 | LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib | ||
566 | endif | ||
567 | |||
568 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) | ||
569 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) | ||
570 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); | ||
571 | NO_LIBUNWIND := 1 | ||
572 | endif # Libunwind support | ||
573 | endif # NO_LIBUNWIND | ||
574 | |||
575 | -include arch/$(ARCH)/Makefile | 433 | -include arch/$(ARCH)/Makefile |
576 | 434 | ||
577 | ifneq ($(OUTPUT),) | 435 | ifneq ($(OUTPUT),) |
578 | BASIC_CFLAGS += -I$(OUTPUT) | 436 | CFLAGS += -I$(OUTPUT) |
579 | endif | 437 | endif |
580 | 438 | ||
581 | ifdef NO_LIBELF | 439 | ifdef NO_LIBELF |
@@ -593,282 +451,74 @@ BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS)) | |||
593 | LIB_OBJS += $(OUTPUT)util/symbol-minimal.o | 451 | LIB_OBJS += $(OUTPUT)util/symbol-minimal.o |
594 | 452 | ||
595 | else # NO_LIBELF | 453 | else # NO_LIBELF |
596 | BASIC_CFLAGS += -DLIBELF_SUPPORT | ||
597 | |||
598 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | ||
599 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | ||
600 | BASIC_CFLAGS += -DLIBELF_MMAP | ||
601 | endif | ||
602 | |||
603 | ifndef NO_DWARF | 454 | ifndef NO_DWARF |
604 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | 455 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
605 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | 456 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o |
606 | else | ||
607 | BASIC_CFLAGS := -DDWARF_SUPPORT $(LIBDW_CFLAGS) $(BASIC_CFLAGS) | ||
608 | BASIC_LDFLAGS := $(LIBDW_LDFLAGS) $(BASIC_LDFLAGS) | ||
609 | EXTLIBS += -lelf -ldw | ||
610 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | ||
611 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | ||
612 | endif # PERF_HAVE_DWARF_REGS | ||
613 | endif # NO_DWARF | 457 | endif # NO_DWARF |
614 | endif # NO_LIBELF | 458 | endif # NO_LIBELF |
615 | 459 | ||
616 | ifndef NO_LIBUNWIND | 460 | ifndef NO_LIBUNWIND |
617 | BASIC_CFLAGS += -DLIBUNWIND_SUPPORT | 461 | LIB_OBJS += $(OUTPUT)util/unwind.o |
618 | EXTLIBS += $(LIBUNWIND_LIBS) | ||
619 | BASIC_CFLAGS := $(LIBUNWIND_CFLAGS) $(BASIC_CFLAGS) | ||
620 | BASIC_LDFLAGS := $(LIBUNWIND_LDFLAGS) $(BASIC_LDFLAGS) | ||
621 | LIB_OBJS += $(OUTPUT)util/unwind.o | ||
622 | endif | 462 | endif |
623 | 463 | ||
624 | ifndef NO_LIBAUDIT | 464 | ifndef NO_LIBAUDIT |
625 | FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit | 465 | BUILTIN_OBJS += $(OUTPUT)builtin-trace.o |
626 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) | ||
627 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); | ||
628 | else | ||
629 | BASIC_CFLAGS += -DLIBAUDIT_SUPPORT | ||
630 | BUILTIN_OBJS += $(OUTPUT)builtin-trace.o | ||
631 | EXTLIBS += -laudit | ||
632 | endif | ||
633 | endif | 466 | endif |
634 | 467 | ||
635 | ifndef NO_NEWT | 468 | ifndef NO_SLANG |
636 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt | 469 | LIB_OBJS += $(OUTPUT)ui/browser.o |
637 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y) | 470 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o |
638 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 471 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o |
639 | else | 472 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o |
640 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 473 | LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o |
641 | BASIC_CFLAGS += -I/usr/include/slang | 474 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o |
642 | BASIC_CFLAGS += -DNEWT_SUPPORT | 475 | LIB_OBJS += $(OUTPUT)ui/tui/util.o |
643 | EXTLIBS += -lnewt -lslang | 476 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o |
644 | LIB_OBJS += $(OUTPUT)ui/browser.o | 477 | LIB_OBJS += $(OUTPUT)ui/tui/progress.o |
645 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o | 478 | LIB_H += ui/browser.h |
646 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o | 479 | LIB_H += ui/browsers/map.h |
647 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o | 480 | LIB_H += ui/keysyms.h |
648 | LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o | 481 | LIB_H += ui/libslang.h |
649 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
650 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o | ||
651 | LIB_OBJS += $(OUTPUT)ui/tui/util.o | ||
652 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o | ||
653 | LIB_OBJS += $(OUTPUT)ui/tui/progress.o | ||
654 | LIB_H += ui/browser.h | ||
655 | LIB_H += ui/browsers/map.h | ||
656 | LIB_H += ui/keysyms.h | ||
657 | LIB_H += ui/libslang.h | ||
658 | LIB_H += ui/progress.h | ||
659 | LIB_H += ui/util.h | ||
660 | LIB_H += ui/ui.h | ||
661 | endif | ||
662 | endif | 482 | endif |
663 | 483 | ||
664 | ifndef NO_GTK2 | 484 | ifndef NO_GTK2 |
665 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) | 485 | LIB_OBJS += $(OUTPUT)ui/gtk/browser.o |
666 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) | 486 | LIB_OBJS += $(OUTPUT)ui/gtk/hists.o |
667 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | 487 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o |
668 | else | 488 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o |
669 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) | 489 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o |
670 | BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR | 490 | LIB_OBJS += $(OUTPUT)ui/gtk/progress.o |
671 | endif | 491 | LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o |
672 | BASIC_CFLAGS += -DGTK2_SUPPORT | ||
673 | BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) | ||
674 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) | ||
675 | LIB_OBJS += $(OUTPUT)ui/gtk/browser.o | ||
676 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o | ||
677 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o | ||
678 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o | ||
679 | LIB_OBJS += $(OUTPUT)ui/gtk/progress.o | ||
680 | # Make sure that it'd be included only once. | ||
681 | ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),) | ||
682 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
683 | endif | ||
684 | endif | ||
685 | endif | ||
686 | |||
687 | ifdef NO_LIBPERL | ||
688 | BASIC_CFLAGS += -DNO_LIBPERL | ||
689 | else | ||
690 | PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) | ||
691 | PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) | ||
692 | PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) | ||
693 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | ||
694 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | ||
695 | |||
696 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) | ||
697 | BASIC_CFLAGS += -DNO_LIBPERL | ||
698 | else | ||
699 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) | ||
700 | EXTLIBS += $(PERL_EMBED_LIBADD) | ||
701 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o | ||
702 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o | ||
703 | endif | ||
704 | endif | 492 | endif |
705 | 493 | ||
706 | disable-python = $(eval $(disable-python_code)) | 494 | ifndef NO_LIBPERL |
707 | define disable-python_code | 495 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o |
708 | BASIC_CFLAGS += -DNO_LIBPYTHON | 496 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o |
709 | $(if $(1),$(warning No $(1) was found)) | ||
710 | $(warning Python support won't be built) | ||
711 | endef | ||
712 | |||
713 | override PYTHON := \ | ||
714 | $(call get-executable-or-default,PYTHON,python) | ||
715 | |||
716 | ifndef PYTHON | ||
717 | $(call disable-python,python interpreter) | ||
718 | python-clean := | ||
719 | else | ||
720 | |||
721 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | ||
722 | |||
723 | # python extension build directories | ||
724 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ | ||
725 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ | ||
726 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
727 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
728 | |||
729 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
730 | |||
731 | ifdef NO_LIBPYTHON | ||
732 | $(call disable-python) | ||
733 | else | ||
734 | |||
735 | override PYTHON_CONFIG := \ | ||
736 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) | ||
737 | |||
738 | ifndef PYTHON_CONFIG | ||
739 | $(call disable-python,python-config tool) | ||
740 | else | ||
741 | |||
742 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | ||
743 | |||
744 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
745 | PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) | ||
746 | PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) | ||
747 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
748 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | ||
749 | |||
750 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) | ||
751 | $(call disable-python,Python.h (for Python 2.x)) | ||
752 | else | ||
753 | |||
754 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) | ||
755 | $(warning Python 3 is not yet supported; please set) | ||
756 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) | ||
757 | $(warning If you also have Python 2 installed, then) | ||
758 | $(warning try something like:) | ||
759 | $(warning $(and ,)) | ||
760 | $(warning $(and ,) make PYTHON=python2) | ||
761 | $(warning $(and ,)) | ||
762 | $(warning Otherwise, disable Python support entirely:) | ||
763 | $(warning $(and ,)) | ||
764 | $(warning $(and ,) make NO_LIBPYTHON=1) | ||
765 | $(warning $(and ,)) | ||
766 | $(error $(and ,)) | ||
767 | else | ||
768 | ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) | ||
769 | EXTLIBS += $(PYTHON_EMBED_LIBADD) | ||
770 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o | ||
771 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o | ||
772 | LANG_BINDINGS += $(OUTPUT)python/perf.so | ||
773 | endif | ||
774 | |||
775 | endif | ||
776 | endif | ||
777 | endif | ||
778 | endif | 497 | endif |
779 | 498 | ||
780 | ifdef NO_DEMANGLE | 499 | ifndef NO_LIBPYTHON |
781 | BASIC_CFLAGS += -DNO_DEMANGLE | 500 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o |
782 | else | 501 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o |
783 | ifdef HAVE_CPLUS_DEMANGLE | ||
784 | EXTLIBS += -liberty | ||
785 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
786 | else | ||
787 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd | ||
788 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) | ||
789 | ifeq ($(has_bfd),y) | ||
790 | EXTLIBS += -lbfd | ||
791 | else | ||
792 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | ||
793 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) | ||
794 | ifeq ($(has_bfd_iberty),y) | ||
795 | EXTLIBS += -lbfd -liberty | ||
796 | else | ||
797 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | ||
798 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) | ||
799 | ifeq ($(has_bfd_iberty_z),y) | ||
800 | EXTLIBS += -lbfd -liberty -lz | ||
801 | else | ||
802 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty | ||
803 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) | ||
804 | ifeq ($(has_cplus_demangle),y) | ||
805 | EXTLIBS += -liberty | ||
806 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
807 | else | ||
808 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) | ||
809 | BASIC_CFLAGS += -DNO_DEMANGLE | ||
810 | endif | ||
811 | endif | ||
812 | endif | ||
813 | endif | ||
814 | endif | ||
815 | endif | 502 | endif |
816 | 503 | ||
817 | ifeq ($(NO_PERF_REGS),0) | 504 | ifeq ($(NO_PERF_REGS),0) |
818 | ifeq ($(ARCH),x86) | 505 | ifeq ($(ARCH),x86) |
819 | LIB_H += arch/x86/include/perf_regs.h | 506 | LIB_H += arch/x86/include/perf_regs.h |
820 | endif | 507 | endif |
821 | BASIC_CFLAGS += -DHAVE_PERF_REGS | ||
822 | endif | ||
823 | |||
824 | ifndef NO_STRLCPY | ||
825 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) | ||
826 | BASIC_CFLAGS += -DHAVE_STRLCPY | ||
827 | endif | ||
828 | endif | ||
829 | |||
830 | ifndef NO_ON_EXIT | ||
831 | ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) | ||
832 | BASIC_CFLAGS += -DHAVE_ON_EXIT | ||
833 | endif | ||
834 | endif | 508 | endif |
835 | 509 | ||
836 | ifndef NO_BACKTRACE | 510 | ifndef NO_LIBNUMA |
837 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) | 511 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o |
838 | BASIC_CFLAGS += -DBACKTRACE_SUPPORT | ||
839 | endif | ||
840 | endif | 512 | endif |
841 | 513 | ||
842 | ifdef ASCIIDOC8 | 514 | ifdef ASCIIDOC8 |
843 | export ASCIIDOC8 | 515 | export ASCIIDOC8 |
844 | endif | 516 | endif |
845 | 517 | ||
846 | # Shell quote (do not use $(call) to accommodate ancient setups); | ||
847 | |||
848 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) | ||
849 | |||
850 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) | ||
851 | bindir_SQ = $(subst ','\'',$(bindir)) | ||
852 | bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) | ||
853 | mandir_SQ = $(subst ','\'',$(mandir)) | ||
854 | infodir_SQ = $(subst ','\'',$(infodir)) | ||
855 | perfexecdir_SQ = $(subst ','\'',$(perfexecdir)) | ||
856 | template_dir_SQ = $(subst ','\'',$(template_dir)) | ||
857 | htmldir_SQ = $(subst ','\'',$(htmldir)) | ||
858 | prefix_SQ = $(subst ','\'',$(prefix)) | ||
859 | sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) | ||
860 | |||
861 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) | ||
862 | |||
863 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group | 518 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group |
864 | 519 | ||
865 | ALL_CFLAGS += $(BASIC_CFLAGS) | ||
866 | ALL_CFLAGS += $(ARCH_CFLAGS) | ||
867 | ALL_LDFLAGS += $(BASIC_LDFLAGS) | ||
868 | |||
869 | export INSTALL SHELL_PATH | 520 | export INSTALL SHELL_PATH |
870 | 521 | ||
871 | |||
872 | ### Build rules | 522 | ### Build rules |
873 | 523 | ||
874 | SHELL = $(SHELL_PATH) | 524 | SHELL = $(SHELL_PATH) |
@@ -884,22 +534,22 @@ strip: $(PROGRAMS) $(OUTPUT)perf | |||
884 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf | 534 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf |
885 | 535 | ||
886 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 536 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
887 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ | 537 | $(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \ |
888 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 538 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
889 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ | 539 | $(CFLAGS) -c $(filter %.c,$^) -o $@ |
890 | 540 | ||
891 | $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) | 541 | $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) |
892 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \ | 542 | $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(OUTPUT)perf.o \ |
893 | $(BUILTIN_OBJS) $(LIBS) -o $@ | 543 | $(BUILTIN_OBJS) $(LIBS) -o $@ |
894 | 544 | ||
895 | $(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 545 | $(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
896 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 546 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ |
897 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 547 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
898 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ | 548 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ |
899 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< | 549 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< |
900 | 550 | ||
901 | $(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 551 | $(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
902 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 552 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ |
903 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 553 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
904 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ | 554 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ |
905 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< | 555 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< |
@@ -924,71 +574,77 @@ $(OUTPUT)perf.o perf.spec \ | |||
924 | # over the general rule for .o | 574 | # over the general rule for .o |
925 | 575 | ||
926 | $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS | 576 | $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS |
927 | $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(ALL_CFLAGS) -w $< | 577 | $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -w $< |
928 | 578 | ||
929 | $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS | 579 | $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS |
930 | $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $< | 580 | $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $< |
931 | 581 | ||
932 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS | 582 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS |
933 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 583 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< |
934 | $(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS | 584 | $(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS |
935 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | 585 | $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< |
936 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS | 586 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS |
937 | $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $< | 587 | $(QUIET_CC)$(CC) -o $@ -S $(CFLAGS) $< |
938 | $(OUTPUT)%.o: %.S | 588 | $(OUTPUT)%.o: %.S |
939 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 589 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $< |
940 | $(OUTPUT)%.s: %.S | 590 | $(OUTPUT)%.s: %.S |
941 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | 591 | $(QUIET_CC)$(CC) -o $@ -E $(CFLAGS) $< |
942 | 592 | ||
943 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 593 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
944 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 594 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ |
945 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ | 595 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ |
946 | '-DPREFIX="$(prefix_SQ)"' \ | 596 | '-DPREFIX="$(prefix_SQ)"' \ |
947 | $< | 597 | $< |
948 | 598 | ||
949 | $(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS | 599 | $(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS |
950 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 600 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ |
951 | '-DBINDIR="$(bindir_SQ)"' \ | 601 | '-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \ |
602 | $< | ||
603 | |||
604 | $(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS | ||
605 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) \ | ||
606 | -DPYTHONPATH='"$(OUTPUT)python"' \ | ||
607 | -DPYTHON='"$(PYTHON_WORD)"' \ | ||
952 | $< | 608 | $< |
953 | 609 | ||
954 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 610 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
955 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 611 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
956 | 612 | ||
957 | $(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS | 613 | $(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS |
958 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 614 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< |
959 | 615 | ||
960 | $(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS | 616 | $(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS |
961 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 617 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< |
962 | 618 | ||
963 | $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | 619 | $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS |
964 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 620 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< |
965 | 621 | ||
966 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS | 622 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS |
967 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 623 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< |
968 | 624 | ||
969 | $(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS | 625 | $(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS |
970 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 626 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -DENABLE_SLFUTURE_CONST $< |
971 | 627 | ||
972 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 628 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
973 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 629 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
974 | 630 | ||
975 | $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS | 631 | $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS |
976 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls $< | 632 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< |
977 | 633 | ||
978 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS | 634 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS |
979 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 635 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< |
980 | 636 | ||
981 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 637 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
982 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 638 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< |
983 | 639 | ||
984 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS | 640 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS |
985 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 641 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< |
986 | 642 | ||
987 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 643 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
988 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 644 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< |
989 | 645 | ||
990 | $(OUTPUT)perf-%: %.o $(PERFLIBS) | 646 | $(OUTPUT)perf-%: %.o $(PERFLIBS) |
991 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | 647 | $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) |
992 | 648 | ||
993 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) | 649 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) |
994 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) | 650 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) |
@@ -1011,6 +667,18 @@ $(LIBTRACEEVENT): | |||
1011 | $(LIBTRACEEVENT)-clean: | 667 | $(LIBTRACEEVENT)-clean: |
1012 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean | 668 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean |
1013 | 669 | ||
670 | # if subdir is set, we've been called from above so target has been built | ||
671 | # already | ||
672 | $(LIBLK): | ||
673 | ifeq ($(subdir),) | ||
674 | $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a | ||
675 | endif | ||
676 | |||
677 | $(LIBLK)-clean: | ||
678 | ifeq ($(subdir),) | ||
679 | $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean | ||
680 | endif | ||
681 | |||
1014 | help: | 682 | help: |
1015 | @echo 'Perf make targets:' | 683 | @echo 'Perf make targets:' |
1016 | @echo ' doc - make *all* documentation (see below)' | 684 | @echo ' doc - make *all* documentation (see below)' |
@@ -1063,7 +731,7 @@ cscope: | |||
1063 | $(FIND) . -name '*.[hcS]' -print | xargs cscope -b | 731 | $(FIND) . -name '*.[hcS]' -print | xargs cscope -b |
1064 | 732 | ||
1065 | ### Detect prefix changes | 733 | ### Detect prefix changes |
1066 | TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ | 734 | TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):\ |
1067 | $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) | 735 | $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) |
1068 | 736 | ||
1069 | $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS | 737 | $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS |
@@ -1084,7 +752,7 @@ check: $(OUTPUT)common-cmds.h | |||
1084 | then \ | 752 | then \ |
1085 | for i in *.c */*.c; \ | 753 | for i in *.c */*.c; \ |
1086 | do \ | 754 | do \ |
1087 | sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ | 755 | sparse $(CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ |
1088 | done; \ | 756 | done; \ |
1089 | else \ | 757 | else \ |
1090 | exit 1; \ | 758 | exit 1; \ |
@@ -1092,14 +760,7 @@ check: $(OUTPUT)common-cmds.h | |||
1092 | 760 | ||
1093 | ### Installation rules | 761 | ### Installation rules |
1094 | 762 | ||
1095 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) | 763 | install-bin: all |
1096 | perfexec_instdir = $(perfexecdir) | ||
1097 | else | ||
1098 | perfexec_instdir = $(prefix)/$(perfexecdir) | ||
1099 | endif | ||
1100 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | ||
1101 | |||
1102 | install: all try-install-man | ||
1103 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 764 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
1104 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 765 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
1105 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 766 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
@@ -1120,6 +781,8 @@ install: all try-install-man | |||
1120 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | 781 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' |
1121 | $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | 782 | $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' |
1122 | 783 | ||
784 | install: install-bin try-install-man | ||
785 | |||
1123 | install-python_ext: | 786 | install-python_ext: |
1124 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' | 787 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' |
1125 | 788 | ||
@@ -1129,7 +792,7 @@ $(INSTALL_DOC_TARGETS): | |||
1129 | 792 | ||
1130 | ### Cleaning rules | 793 | ### Cleaning rules |
1131 | 794 | ||
1132 | clean: $(LIBTRACEEVENT)-clean | 795 | clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean |
1133 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) | 796 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
1134 | $(RM) $(ALL_PROGRAMS) perf | 797 | $(RM) $(ALL_PROGRAMS) perf |
1135 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 798 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
@@ -1139,6 +802,6 @@ clean: $(LIBTRACEEVENT)-clean | |||
1139 | $(RM) $(OUTPUT)util/*-flex* | 802 | $(RM) $(OUTPUT)util/*-flex* |
1140 | $(python-clean) | 803 | $(python-clean) |
1141 | 804 | ||
1142 | .PHONY: all install clean strip $(LIBTRACEEVENT) | 805 | .PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) |
1143 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell | 806 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell |
1144 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 807 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index e8d5c551c69c..33ec5b339da8 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c | |||
@@ -8,10 +8,7 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <stdlib.h> | 11 | #include <stddef.h> |
12 | #ifndef __UCLIBC__ | ||
13 | #include <libio.h> | ||
14 | #endif | ||
15 | #include <dwarf-regs.h> | 12 | #include <dwarf-regs.h> |
16 | 13 | ||
17 | struct pt_regs_dwarfnum { | 14 | struct pt_regs_dwarfnum { |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 3e975cb6232e..aacef07ebf31 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c | |||
@@ -155,6 +155,7 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | |||
155 | if (lookup_path(buf)) | 155 | if (lookup_path(buf)) |
156 | goto out; | 156 | goto out; |
157 | free(buf); | 157 | free(buf); |
158 | buf = NULL; | ||
158 | } | 159 | } |
159 | 160 | ||
160 | if (!strcmp(arch, "arm")) | 161 | if (!strcmp(arch, "arm")) |
diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 7cdd61d0e27c..733151cdf46e 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c | |||
@@ -9,10 +9,7 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <stdlib.h> | 12 | #include <stddef.h> |
13 | #ifndef __UCLIBC__ | ||
14 | #include <libio.h> | ||
15 | #endif | ||
16 | #include <dwarf-regs.h> | 13 | #include <dwarf-regs.h> |
17 | 14 | ||
18 | 15 | ||
diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c index e19653e025fa..0469df02ee62 100644 --- a/tools/perf/arch/s390/util/dwarf-regs.c +++ b/tools/perf/arch/s390/util/dwarf-regs.c | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <libio.h> | 9 | #include <stddef.h> |
10 | #include <dwarf-regs.h> | 10 | #include <dwarf-regs.h> |
11 | 11 | ||
12 | #define NUM_GPRS 16 | 12 | #define NUM_GPRS 16 |
diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c index a11edb007a6c..0d0897f57a10 100644 --- a/tools/perf/arch/sh/util/dwarf-regs.c +++ b/tools/perf/arch/sh/util/dwarf-regs.c | |||
@@ -19,7 +19,7 @@ | |||
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <libio.h> | 22 | #include <stddef.h> |
23 | #include <dwarf-regs.h> | 23 | #include <dwarf-regs.h> |
24 | 24 | ||
25 | /* | 25 | /* |
diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c index 0ab88483720c..92eda412fed3 100644 --- a/tools/perf/arch/sparc/util/dwarf-regs.c +++ b/tools/perf/arch/sparc/util/dwarf-regs.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <libio.h> | 12 | #include <stddef.h> |
13 | #include <dwarf-regs.h> | 13 | #include <dwarf-regs.h> |
14 | 14 | ||
15 | #define SPARC_MAX_REGS 96 | 15 | #define SPARC_MAX_REGS 96 |
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index a794d3081928..be22dd463232 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c | |||
@@ -20,7 +20,7 @@ | |||
20 | * | 20 | * |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <libio.h> | 23 | #include <stddef.h> |
24 | #include <dwarf-regs.h> | 24 | #include <dwarf-regs.h> |
25 | 25 | ||
26 | /* | 26 | /* |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index 8f89998eeaf4..0fdc85269c4d 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h | |||
@@ -1,6 +1,31 @@ | |||
1 | #ifndef BENCH_H | 1 | #ifndef BENCH_H |
2 | #define BENCH_H | 2 | #define BENCH_H |
3 | 3 | ||
4 | /* | ||
5 | * The madvise transparent hugepage constants were added in glibc | ||
6 | * 2.13. For compatibility with older versions of glibc, define these | ||
7 | * tokens if they are not already defined. | ||
8 | * | ||
9 | * PA-RISC uses different madvise values from other architectures and | ||
10 | * needs to be special-cased. | ||
11 | */ | ||
12 | #ifdef __hppa__ | ||
13 | # ifndef MADV_HUGEPAGE | ||
14 | # define MADV_HUGEPAGE 67 | ||
15 | # endif | ||
16 | # ifndef MADV_NOHUGEPAGE | ||
17 | # define MADV_NOHUGEPAGE 68 | ||
18 | # endif | ||
19 | #else | ||
20 | # ifndef MADV_HUGEPAGE | ||
21 | # define MADV_HUGEPAGE 14 | ||
22 | # endif | ||
23 | # ifndef MADV_NOHUGEPAGE | ||
24 | # define MADV_NOHUGEPAGE 15 | ||
25 | # endif | ||
26 | #endif | ||
27 | |||
28 | extern int bench_numa(int argc, const char **argv, const char *prefix); | ||
4 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); | 29 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); |
5 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); | 30 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); |
6 | extern int bench_mem_memcpy(int argc, const char **argv, | 31 | extern int bench_mem_memcpy(int argc, const char **argv, |
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c new file mode 100644 index 000000000000..30d1c3225b46 --- /dev/null +++ b/tools/perf/bench/numa.c | |||
@@ -0,0 +1,1731 @@ | |||
1 | /* | ||
2 | * numa.c | ||
3 | * | ||
4 | * numa: Simulate NUMA-sensitive workload and measure their NUMA performance | ||
5 | */ | ||
6 | |||
7 | #include "../perf.h" | ||
8 | #include "../builtin.h" | ||
9 | #include "../util/util.h" | ||
10 | #include "../util/parse-options.h" | ||
11 | |||
12 | #include "bench.h" | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <sched.h> | ||
16 | #include <stdio.h> | ||
17 | #include <assert.h> | ||
18 | #include <malloc.h> | ||
19 | #include <signal.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | #include <unistd.h> | ||
23 | #include <pthread.h> | ||
24 | #include <sys/mman.h> | ||
25 | #include <sys/time.h> | ||
26 | #include <sys/wait.h> | ||
27 | #include <sys/prctl.h> | ||
28 | #include <sys/types.h> | ||
29 | |||
30 | #include <numa.h> | ||
31 | #include <numaif.h> | ||
32 | |||
33 | /* | ||
34 | * Regular printout to the terminal, supressed if -q is specified: | ||
35 | */ | ||
36 | #define tprintf(x...) do { if (g && g->p.show_details >= 0) printf(x); } while (0) | ||
37 | |||
38 | /* | ||
39 | * Debug printf: | ||
40 | */ | ||
41 | #define dprintf(x...) do { if (g && g->p.show_details >= 1) printf(x); } while (0) | ||
42 | |||
43 | struct thread_data { | ||
44 | int curr_cpu; | ||
45 | cpu_set_t bind_cpumask; | ||
46 | int bind_node; | ||
47 | u8 *process_data; | ||
48 | int process_nr; | ||
49 | int thread_nr; | ||
50 | int task_nr; | ||
51 | unsigned int loops_done; | ||
52 | u64 val; | ||
53 | u64 runtime_ns; | ||
54 | pthread_mutex_t *process_lock; | ||
55 | }; | ||
56 | |||
57 | /* Parameters set by options: */ | ||
58 | |||
59 | struct params { | ||
60 | /* Startup synchronization: */ | ||
61 | bool serialize_startup; | ||
62 | |||
63 | /* Task hierarchy: */ | ||
64 | int nr_proc; | ||
65 | int nr_threads; | ||
66 | |||
67 | /* Working set sizes: */ | ||
68 | const char *mb_global_str; | ||
69 | const char *mb_proc_str; | ||
70 | const char *mb_proc_locked_str; | ||
71 | const char *mb_thread_str; | ||
72 | |||
73 | double mb_global; | ||
74 | double mb_proc; | ||
75 | double mb_proc_locked; | ||
76 | double mb_thread; | ||
77 | |||
78 | /* Access patterns to the working set: */ | ||
79 | bool data_reads; | ||
80 | bool data_writes; | ||
81 | bool data_backwards; | ||
82 | bool data_zero_memset; | ||
83 | bool data_rand_walk; | ||
84 | u32 nr_loops; | ||
85 | u32 nr_secs; | ||
86 | u32 sleep_usecs; | ||
87 | |||
88 | /* Working set initialization: */ | ||
89 | bool init_zero; | ||
90 | bool init_random; | ||
91 | bool init_cpu0; | ||
92 | |||
93 | /* Misc options: */ | ||
94 | int show_details; | ||
95 | int run_all; | ||
96 | int thp; | ||
97 | |||
98 | long bytes_global; | ||
99 | long bytes_process; | ||
100 | long bytes_process_locked; | ||
101 | long bytes_thread; | ||
102 | |||
103 | int nr_tasks; | ||
104 | bool show_quiet; | ||
105 | |||
106 | bool show_convergence; | ||
107 | bool measure_convergence; | ||
108 | |||
109 | int perturb_secs; | ||
110 | int nr_cpus; | ||
111 | int nr_nodes; | ||
112 | |||
113 | /* Affinity options -C and -N: */ | ||
114 | char *cpu_list_str; | ||
115 | char *node_list_str; | ||
116 | }; | ||
117 | |||
118 | |||
119 | /* Global, read-writable area, accessible to all processes and threads: */ | ||
120 | |||
121 | struct global_info { | ||
122 | u8 *data; | ||
123 | |||
124 | pthread_mutex_t startup_mutex; | ||
125 | int nr_tasks_started; | ||
126 | |||
127 | pthread_mutex_t startup_done_mutex; | ||
128 | |||
129 | pthread_mutex_t start_work_mutex; | ||
130 | int nr_tasks_working; | ||
131 | |||
132 | pthread_mutex_t stop_work_mutex; | ||
133 | u64 bytes_done; | ||
134 | |||
135 | struct thread_data *threads; | ||
136 | |||
137 | /* Convergence latency measurement: */ | ||
138 | bool all_converged; | ||
139 | bool stop_work; | ||
140 | |||
141 | int print_once; | ||
142 | |||
143 | struct params p; | ||
144 | }; | ||
145 | |||
146 | static struct global_info *g = NULL; | ||
147 | |||
148 | static int parse_cpus_opt(const struct option *opt, const char *arg, int unset); | ||
149 | static int parse_nodes_opt(const struct option *opt, const char *arg, int unset); | ||
150 | |||
151 | struct params p0; | ||
152 | |||
153 | static const struct option options[] = { | ||
154 | OPT_INTEGER('p', "nr_proc" , &p0.nr_proc, "number of processes"), | ||
155 | OPT_INTEGER('t', "nr_threads" , &p0.nr_threads, "number of threads per process"), | ||
156 | |||
157 | OPT_STRING('G', "mb_global" , &p0.mb_global_str, "MB", "global memory (MBs)"), | ||
158 | OPT_STRING('P', "mb_proc" , &p0.mb_proc_str, "MB", "process memory (MBs)"), | ||
159 | OPT_STRING('L', "mb_proc_locked", &p0.mb_proc_locked_str,"MB", "process serialized/locked memory access (MBs), <= process_memory"), | ||
160 | OPT_STRING('T', "mb_thread" , &p0.mb_thread_str, "MB", "thread memory (MBs)"), | ||
161 | |||
162 | OPT_UINTEGER('l', "nr_loops" , &p0.nr_loops, "max number of loops to run"), | ||
163 | OPT_UINTEGER('s', "nr_secs" , &p0.nr_secs, "max number of seconds to run"), | ||
164 | OPT_UINTEGER('u', "usleep" , &p0.sleep_usecs, "usecs to sleep per loop iteration"), | ||
165 | |||
166 | OPT_BOOLEAN('R', "data_reads" , &p0.data_reads, "access the data via writes (can be mixed with -W)"), | ||
167 | OPT_BOOLEAN('W', "data_writes" , &p0.data_writes, "access the data via writes (can be mixed with -R)"), | ||
168 | OPT_BOOLEAN('B', "data_backwards", &p0.data_backwards, "access the data backwards as well"), | ||
169 | OPT_BOOLEAN('Z', "data_zero_memset", &p0.data_zero_memset,"access the data via glibc bzero only"), | ||
170 | OPT_BOOLEAN('r', "data_rand_walk", &p0.data_rand_walk, "access the data with random (32bit LFSR) walk"), | ||
171 | |||
172 | |||
173 | OPT_BOOLEAN('z', "init_zero" , &p0.init_zero, "bzero the initial allocations"), | ||
174 | OPT_BOOLEAN('I', "init_random" , &p0.init_random, "randomize the contents of the initial allocations"), | ||
175 | OPT_BOOLEAN('0', "init_cpu0" , &p0.init_cpu0, "do the initial allocations on CPU#0"), | ||
176 | OPT_INTEGER('x', "perturb_secs", &p0.perturb_secs, "perturb thread 0/0 every X secs, to test convergence stability"), | ||
177 | |||
178 | OPT_INCR ('d', "show_details" , &p0.show_details, "Show details"), | ||
179 | OPT_INCR ('a', "all" , &p0.run_all, "Run all tests in the suite"), | ||
180 | OPT_INTEGER('H', "thp" , &p0.thp, "MADV_NOHUGEPAGE < 0 < MADV_HUGEPAGE"), | ||
181 | OPT_BOOLEAN('c', "show_convergence", &p0.show_convergence, "show convergence details"), | ||
182 | OPT_BOOLEAN('m', "measure_convergence", &p0.measure_convergence, "measure convergence latency"), | ||
183 | OPT_BOOLEAN('q', "quiet" , &p0.show_quiet, "bzero the initial allocations"), | ||
184 | OPT_BOOLEAN('S', "serialize-startup", &p0.serialize_startup,"serialize thread startup"), | ||
185 | |||
186 | /* Special option string parsing callbacks: */ | ||
187 | OPT_CALLBACK('C', "cpus", NULL, "cpu[,cpu2,...cpuN]", | ||
188 | "bind the first N tasks to these specific cpus (the rest is unbound)", | ||
189 | parse_cpus_opt), | ||
190 | OPT_CALLBACK('M', "memnodes", NULL, "node[,node2,...nodeN]", | ||
191 | "bind the first N tasks to these specific memory nodes (the rest is unbound)", | ||
192 | parse_nodes_opt), | ||
193 | OPT_END() | ||
194 | }; | ||
195 | |||
196 | static const char * const bench_numa_usage[] = { | ||
197 | "perf bench numa <options>", | ||
198 | NULL | ||
199 | }; | ||
200 | |||
201 | static const char * const numa_usage[] = { | ||
202 | "perf bench numa mem [<options>]", | ||
203 | NULL | ||
204 | }; | ||
205 | |||
206 | static cpu_set_t bind_to_cpu(int target_cpu) | ||
207 | { | ||
208 | cpu_set_t orig_mask, mask; | ||
209 | int ret; | ||
210 | |||
211 | ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask); | ||
212 | BUG_ON(ret); | ||
213 | |||
214 | CPU_ZERO(&mask); | ||
215 | |||
216 | if (target_cpu == -1) { | ||
217 | int cpu; | ||
218 | |||
219 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
220 | CPU_SET(cpu, &mask); | ||
221 | } else { | ||
222 | BUG_ON(target_cpu < 0 || target_cpu >= g->p.nr_cpus); | ||
223 | CPU_SET(target_cpu, &mask); | ||
224 | } | ||
225 | |||
226 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
227 | BUG_ON(ret); | ||
228 | |||
229 | return orig_mask; | ||
230 | } | ||
231 | |||
232 | static cpu_set_t bind_to_node(int target_node) | ||
233 | { | ||
234 | int cpus_per_node = g->p.nr_cpus/g->p.nr_nodes; | ||
235 | cpu_set_t orig_mask, mask; | ||
236 | int cpu; | ||
237 | int ret; | ||
238 | |||
239 | BUG_ON(cpus_per_node*g->p.nr_nodes != g->p.nr_cpus); | ||
240 | BUG_ON(!cpus_per_node); | ||
241 | |||
242 | ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask); | ||
243 | BUG_ON(ret); | ||
244 | |||
245 | CPU_ZERO(&mask); | ||
246 | |||
247 | if (target_node == -1) { | ||
248 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
249 | CPU_SET(cpu, &mask); | ||
250 | } else { | ||
251 | int cpu_start = (target_node + 0) * cpus_per_node; | ||
252 | int cpu_stop = (target_node + 1) * cpus_per_node; | ||
253 | |||
254 | BUG_ON(cpu_stop > g->p.nr_cpus); | ||
255 | |||
256 | for (cpu = cpu_start; cpu < cpu_stop; cpu++) | ||
257 | CPU_SET(cpu, &mask); | ||
258 | } | ||
259 | |||
260 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
261 | BUG_ON(ret); | ||
262 | |||
263 | return orig_mask; | ||
264 | } | ||
265 | |||
266 | static void bind_to_cpumask(cpu_set_t mask) | ||
267 | { | ||
268 | int ret; | ||
269 | |||
270 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
271 | BUG_ON(ret); | ||
272 | } | ||
273 | |||
274 | static void mempol_restore(void) | ||
275 | { | ||
276 | int ret; | ||
277 | |||
278 | ret = set_mempolicy(MPOL_DEFAULT, NULL, g->p.nr_nodes-1); | ||
279 | |||
280 | BUG_ON(ret); | ||
281 | } | ||
282 | |||
283 | static void bind_to_memnode(int node) | ||
284 | { | ||
285 | unsigned long nodemask; | ||
286 | int ret; | ||
287 | |||
288 | if (node == -1) | ||
289 | return; | ||
290 | |||
291 | BUG_ON(g->p.nr_nodes > (int)sizeof(nodemask)); | ||
292 | nodemask = 1L << node; | ||
293 | |||
294 | ret = set_mempolicy(MPOL_BIND, &nodemask, sizeof(nodemask)*8); | ||
295 | dprintf("binding to node %d, mask: %016lx => %d\n", node, nodemask, ret); | ||
296 | |||
297 | BUG_ON(ret); | ||
298 | } | ||
299 | |||
300 | #define HPSIZE (2*1024*1024) | ||
301 | |||
302 | #define set_taskname(fmt...) \ | ||
303 | do { \ | ||
304 | char name[20]; \ | ||
305 | \ | ||
306 | snprintf(name, 20, fmt); \ | ||
307 | prctl(PR_SET_NAME, name); \ | ||
308 | } while (0) | ||
309 | |||
310 | static u8 *alloc_data(ssize_t bytes0, int map_flags, | ||
311 | int init_zero, int init_cpu0, int thp, int init_random) | ||
312 | { | ||
313 | cpu_set_t orig_mask; | ||
314 | ssize_t bytes; | ||
315 | u8 *buf; | ||
316 | int ret; | ||
317 | |||
318 | if (!bytes0) | ||
319 | return NULL; | ||
320 | |||
321 | /* Allocate and initialize all memory on CPU#0: */ | ||
322 | if (init_cpu0) { | ||
323 | orig_mask = bind_to_node(0); | ||
324 | bind_to_memnode(0); | ||
325 | } | ||
326 | |||
327 | bytes = bytes0 + HPSIZE; | ||
328 | |||
329 | buf = (void *)mmap(0, bytes, PROT_READ|PROT_WRITE, MAP_ANON|map_flags, -1, 0); | ||
330 | BUG_ON(buf == (void *)-1); | ||
331 | |||
332 | if (map_flags == MAP_PRIVATE) { | ||
333 | if (thp > 0) { | ||
334 | ret = madvise(buf, bytes, MADV_HUGEPAGE); | ||
335 | if (ret && !g->print_once) { | ||
336 | g->print_once = 1; | ||
337 | printf("WARNING: Could not enable THP - do: 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled'\n"); | ||
338 | } | ||
339 | } | ||
340 | if (thp < 0) { | ||
341 | ret = madvise(buf, bytes, MADV_NOHUGEPAGE); | ||
342 | if (ret && !g->print_once) { | ||
343 | g->print_once = 1; | ||
344 | printf("WARNING: Could not disable THP: run a CONFIG_TRANSPARENT_HUGEPAGE kernel?\n"); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | if (init_zero) { | ||
350 | bzero(buf, bytes); | ||
351 | } else { | ||
352 | /* Initialize random contents, different in each word: */ | ||
353 | if (init_random) { | ||
354 | u64 *wbuf = (void *)buf; | ||
355 | long off = rand(); | ||
356 | long i; | ||
357 | |||
358 | for (i = 0; i < bytes/8; i++) | ||
359 | wbuf[i] = i + off; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /* Align to 2MB boundary: */ | ||
364 | buf = (void *)(((unsigned long)buf + HPSIZE-1) & ~(HPSIZE-1)); | ||
365 | |||
366 | /* Restore affinity: */ | ||
367 | if (init_cpu0) { | ||
368 | bind_to_cpumask(orig_mask); | ||
369 | mempol_restore(); | ||
370 | } | ||
371 | |||
372 | return buf; | ||
373 | } | ||
374 | |||
375 | static void free_data(void *data, ssize_t bytes) | ||
376 | { | ||
377 | int ret; | ||
378 | |||
379 | if (!data) | ||
380 | return; | ||
381 | |||
382 | ret = munmap(data, bytes); | ||
383 | BUG_ON(ret); | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Create a shared memory buffer that can be shared between processes, zeroed: | ||
388 | */ | ||
389 | static void * zalloc_shared_data(ssize_t bytes) | ||
390 | { | ||
391 | return alloc_data(bytes, MAP_SHARED, 1, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * Create a shared memory buffer that can be shared between processes: | ||
396 | */ | ||
397 | static void * setup_shared_data(ssize_t bytes) | ||
398 | { | ||
399 | return alloc_data(bytes, MAP_SHARED, 0, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * Allocate process-local memory - this will either be shared between | ||
404 | * threads of this process, or only be accessed by this thread: | ||
405 | */ | ||
406 | static void * setup_private_data(ssize_t bytes) | ||
407 | { | ||
408 | return alloc_data(bytes, MAP_PRIVATE, 0, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * Return a process-shared (global) mutex: | ||
413 | */ | ||
414 | static void init_global_mutex(pthread_mutex_t *mutex) | ||
415 | { | ||
416 | pthread_mutexattr_t attr; | ||
417 | |||
418 | pthread_mutexattr_init(&attr); | ||
419 | pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); | ||
420 | pthread_mutex_init(mutex, &attr); | ||
421 | } | ||
422 | |||
423 | static int parse_cpu_list(const char *arg) | ||
424 | { | ||
425 | p0.cpu_list_str = strdup(arg); | ||
426 | |||
427 | dprintf("got CPU list: {%s}\n", p0.cpu_list_str); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static void parse_setup_cpu_list(void) | ||
433 | { | ||
434 | struct thread_data *td; | ||
435 | char *str0, *str; | ||
436 | int t; | ||
437 | |||
438 | if (!g->p.cpu_list_str) | ||
439 | return; | ||
440 | |||
441 | dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); | ||
442 | |||
443 | str0 = str = strdup(g->p.cpu_list_str); | ||
444 | t = 0; | ||
445 | |||
446 | BUG_ON(!str); | ||
447 | |||
448 | tprintf("# binding tasks to CPUs:\n"); | ||
449 | tprintf("# "); | ||
450 | |||
451 | while (true) { | ||
452 | int bind_cpu, bind_cpu_0, bind_cpu_1; | ||
453 | char *tok, *tok_end, *tok_step, *tok_len, *tok_mul; | ||
454 | int bind_len; | ||
455 | int step; | ||
456 | int mul; | ||
457 | |||
458 | tok = strsep(&str, ","); | ||
459 | if (!tok) | ||
460 | break; | ||
461 | |||
462 | tok_end = strstr(tok, "-"); | ||
463 | |||
464 | dprintf("\ntoken: {%s}, end: {%s}\n", tok, tok_end); | ||
465 | if (!tok_end) { | ||
466 | /* Single CPU specified: */ | ||
467 | bind_cpu_0 = bind_cpu_1 = atol(tok); | ||
468 | } else { | ||
469 | /* CPU range specified (for example: "5-11"): */ | ||
470 | bind_cpu_0 = atol(tok); | ||
471 | bind_cpu_1 = atol(tok_end + 1); | ||
472 | } | ||
473 | |||
474 | step = 1; | ||
475 | tok_step = strstr(tok, "#"); | ||
476 | if (tok_step) { | ||
477 | step = atol(tok_step + 1); | ||
478 | BUG_ON(step <= 0 || step >= g->p.nr_cpus); | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * Mask length. | ||
483 | * Eg: "--cpus 8_4-16#4" means: '--cpus 8_4,12_4,16_4', | ||
484 | * where the _4 means the next 4 CPUs are allowed. | ||
485 | */ | ||
486 | bind_len = 1; | ||
487 | tok_len = strstr(tok, "_"); | ||
488 | if (tok_len) { | ||
489 | bind_len = atol(tok_len + 1); | ||
490 | BUG_ON(bind_len <= 0 || bind_len > g->p.nr_cpus); | ||
491 | } | ||
492 | |||
493 | /* Multiplicator shortcut, "0x8" is a shortcut for: "0,0,0,0,0,0,0,0" */ | ||
494 | mul = 1; | ||
495 | tok_mul = strstr(tok, "x"); | ||
496 | if (tok_mul) { | ||
497 | mul = atol(tok_mul + 1); | ||
498 | BUG_ON(mul <= 0); | ||
499 | } | ||
500 | |||
501 | dprintf("CPUs: %d_%d-%d#%dx%d\n", bind_cpu_0, bind_len, bind_cpu_1, step, mul); | ||
502 | |||
503 | BUG_ON(bind_cpu_0 < 0 || bind_cpu_0 >= g->p.nr_cpus); | ||
504 | BUG_ON(bind_cpu_1 < 0 || bind_cpu_1 >= g->p.nr_cpus); | ||
505 | BUG_ON(bind_cpu_0 > bind_cpu_1); | ||
506 | |||
507 | for (bind_cpu = bind_cpu_0; bind_cpu <= bind_cpu_1; bind_cpu += step) { | ||
508 | int i; | ||
509 | |||
510 | for (i = 0; i < mul; i++) { | ||
511 | int cpu; | ||
512 | |||
513 | if (t >= g->p.nr_tasks) { | ||
514 | printf("\n# NOTE: ignoring bind CPUs starting at CPU#%d\n #", bind_cpu); | ||
515 | goto out; | ||
516 | } | ||
517 | td = g->threads + t; | ||
518 | |||
519 | if (t) | ||
520 | tprintf(","); | ||
521 | if (bind_len > 1) { | ||
522 | tprintf("%2d/%d", bind_cpu, bind_len); | ||
523 | } else { | ||
524 | tprintf("%2d", bind_cpu); | ||
525 | } | ||
526 | |||
527 | CPU_ZERO(&td->bind_cpumask); | ||
528 | for (cpu = bind_cpu; cpu < bind_cpu+bind_len; cpu++) { | ||
529 | BUG_ON(cpu < 0 || cpu >= g->p.nr_cpus); | ||
530 | CPU_SET(cpu, &td->bind_cpumask); | ||
531 | } | ||
532 | t++; | ||
533 | } | ||
534 | } | ||
535 | } | ||
536 | out: | ||
537 | |||
538 | tprintf("\n"); | ||
539 | |||
540 | if (t < g->p.nr_tasks) | ||
541 | printf("# NOTE: %d tasks bound, %d tasks unbound\n", t, g->p.nr_tasks - t); | ||
542 | |||
543 | free(str0); | ||
544 | } | ||
545 | |||
546 | static int parse_cpus_opt(const struct option *opt __maybe_unused, | ||
547 | const char *arg, int unset __maybe_unused) | ||
548 | { | ||
549 | if (!arg) | ||
550 | return -1; | ||
551 | |||
552 | return parse_cpu_list(arg); | ||
553 | } | ||
554 | |||
555 | static int parse_node_list(const char *arg) | ||
556 | { | ||
557 | p0.node_list_str = strdup(arg); | ||
558 | |||
559 | dprintf("got NODE list: {%s}\n", p0.node_list_str); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static void parse_setup_node_list(void) | ||
565 | { | ||
566 | struct thread_data *td; | ||
567 | char *str0, *str; | ||
568 | int t; | ||
569 | |||
570 | if (!g->p.node_list_str) | ||
571 | return; | ||
572 | |||
573 | dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); | ||
574 | |||
575 | str0 = str = strdup(g->p.node_list_str); | ||
576 | t = 0; | ||
577 | |||
578 | BUG_ON(!str); | ||
579 | |||
580 | tprintf("# binding tasks to NODEs:\n"); | ||
581 | tprintf("# "); | ||
582 | |||
583 | while (true) { | ||
584 | int bind_node, bind_node_0, bind_node_1; | ||
585 | char *tok, *tok_end, *tok_step, *tok_mul; | ||
586 | int step; | ||
587 | int mul; | ||
588 | |||
589 | tok = strsep(&str, ","); | ||
590 | if (!tok) | ||
591 | break; | ||
592 | |||
593 | tok_end = strstr(tok, "-"); | ||
594 | |||
595 | dprintf("\ntoken: {%s}, end: {%s}\n", tok, tok_end); | ||
596 | if (!tok_end) { | ||
597 | /* Single NODE specified: */ | ||
598 | bind_node_0 = bind_node_1 = atol(tok); | ||
599 | } else { | ||
600 | /* NODE range specified (for example: "5-11"): */ | ||
601 | bind_node_0 = atol(tok); | ||
602 | bind_node_1 = atol(tok_end + 1); | ||
603 | } | ||
604 | |||
605 | step = 1; | ||
606 | tok_step = strstr(tok, "#"); | ||
607 | if (tok_step) { | ||
608 | step = atol(tok_step + 1); | ||
609 | BUG_ON(step <= 0 || step >= g->p.nr_nodes); | ||
610 | } | ||
611 | |||
612 | /* Multiplicator shortcut, "0x8" is a shortcut for: "0,0,0,0,0,0,0,0" */ | ||
613 | mul = 1; | ||
614 | tok_mul = strstr(tok, "x"); | ||
615 | if (tok_mul) { | ||
616 | mul = atol(tok_mul + 1); | ||
617 | BUG_ON(mul <= 0); | ||
618 | } | ||
619 | |||
620 | dprintf("NODEs: %d-%d #%d\n", bind_node_0, bind_node_1, step); | ||
621 | |||
622 | BUG_ON(bind_node_0 < 0 || bind_node_0 >= g->p.nr_nodes); | ||
623 | BUG_ON(bind_node_1 < 0 || bind_node_1 >= g->p.nr_nodes); | ||
624 | BUG_ON(bind_node_0 > bind_node_1); | ||
625 | |||
626 | for (bind_node = bind_node_0; bind_node <= bind_node_1; bind_node += step) { | ||
627 | int i; | ||
628 | |||
629 | for (i = 0; i < mul; i++) { | ||
630 | if (t >= g->p.nr_tasks) { | ||
631 | printf("\n# NOTE: ignoring bind NODEs starting at NODE#%d\n", bind_node); | ||
632 | goto out; | ||
633 | } | ||
634 | td = g->threads + t; | ||
635 | |||
636 | if (!t) | ||
637 | tprintf(" %2d", bind_node); | ||
638 | else | ||
639 | tprintf(",%2d", bind_node); | ||
640 | |||
641 | td->bind_node = bind_node; | ||
642 | t++; | ||
643 | } | ||
644 | } | ||
645 | } | ||
646 | out: | ||
647 | |||
648 | tprintf("\n"); | ||
649 | |||
650 | if (t < g->p.nr_tasks) | ||
651 | printf("# NOTE: %d tasks mem-bound, %d tasks unbound\n", t, g->p.nr_tasks - t); | ||
652 | |||
653 | free(str0); | ||
654 | } | ||
655 | |||
656 | static int parse_nodes_opt(const struct option *opt __maybe_unused, | ||
657 | const char *arg, int unset __maybe_unused) | ||
658 | { | ||
659 | if (!arg) | ||
660 | return -1; | ||
661 | |||
662 | return parse_node_list(arg); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | #define BIT(x) (1ul << x) | ||
668 | |||
669 | static inline uint32_t lfsr_32(uint32_t lfsr) | ||
670 | { | ||
671 | const uint32_t taps = BIT(1) | BIT(5) | BIT(6) | BIT(31); | ||
672 | return (lfsr>>1) ^ ((0x0u - (lfsr & 0x1u)) & taps); | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | * Make sure there's real data dependency to RAM (when read | ||
677 | * accesses are enabled), so the compiler, the CPU and the | ||
678 | * kernel (KSM, zero page, etc.) cannot optimize away RAM | ||
679 | * accesses: | ||
680 | */ | ||
681 | static inline u64 access_data(u64 *data __attribute__((unused)), u64 val) | ||
682 | { | ||
683 | if (g->p.data_reads) | ||
684 | val += *data; | ||
685 | if (g->p.data_writes) | ||
686 | *data = val + 1; | ||
687 | return val; | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | * The worker process does two types of work, a forwards going | ||
692 | * loop and a backwards going loop. | ||
693 | * | ||
694 | * We do this so that on multiprocessor systems we do not create | ||
695 | * a 'train' of processing, with highly synchronized processes, | ||
696 | * skewing the whole benchmark. | ||
697 | */ | ||
698 | static u64 do_work(u8 *__data, long bytes, int nr, int nr_max, int loop, u64 val) | ||
699 | { | ||
700 | long words = bytes/sizeof(u64); | ||
701 | u64 *data = (void *)__data; | ||
702 | long chunk_0, chunk_1; | ||
703 | u64 *d0, *d, *d1; | ||
704 | long off; | ||
705 | long i; | ||
706 | |||
707 | BUG_ON(!data && words); | ||
708 | BUG_ON(data && !words); | ||
709 | |||
710 | if (!data) | ||
711 | return val; | ||
712 | |||
713 | /* Very simple memset() work variant: */ | ||
714 | if (g->p.data_zero_memset && !g->p.data_rand_walk) { | ||
715 | bzero(data, bytes); | ||
716 | return val; | ||
717 | } | ||
718 | |||
719 | /* Spread out by PID/TID nr and by loop nr: */ | ||
720 | chunk_0 = words/nr_max; | ||
721 | chunk_1 = words/g->p.nr_loops; | ||
722 | off = nr*chunk_0 + loop*chunk_1; | ||
723 | |||
724 | while (off >= words) | ||
725 | off -= words; | ||
726 | |||
727 | if (g->p.data_rand_walk) { | ||
728 | u32 lfsr = nr + loop + val; | ||
729 | int j; | ||
730 | |||
731 | for (i = 0; i < words/1024; i++) { | ||
732 | long start, end; | ||
733 | |||
734 | lfsr = lfsr_32(lfsr); | ||
735 | |||
736 | start = lfsr % words; | ||
737 | end = min(start + 1024, words-1); | ||
738 | |||
739 | if (g->p.data_zero_memset) { | ||
740 | bzero(data + start, (end-start) * sizeof(u64)); | ||
741 | } else { | ||
742 | for (j = start; j < end; j++) | ||
743 | val = access_data(data + j, val); | ||
744 | } | ||
745 | } | ||
746 | } else if (!g->p.data_backwards || (nr + loop) & 1) { | ||
747 | |||
748 | d0 = data + off; | ||
749 | d = data + off + 1; | ||
750 | d1 = data + words; | ||
751 | |||
752 | /* Process data forwards: */ | ||
753 | for (;;) { | ||
754 | if (unlikely(d >= d1)) | ||
755 | d = data; | ||
756 | if (unlikely(d == d0)) | ||
757 | break; | ||
758 | |||
759 | val = access_data(d, val); | ||
760 | |||
761 | d++; | ||
762 | } | ||
763 | } else { | ||
764 | /* Process data backwards: */ | ||
765 | |||
766 | d0 = data + off; | ||
767 | d = data + off - 1; | ||
768 | d1 = data + words; | ||
769 | |||
770 | /* Process data forwards: */ | ||
771 | for (;;) { | ||
772 | if (unlikely(d < data)) | ||
773 | d = data + words-1; | ||
774 | if (unlikely(d == d0)) | ||
775 | break; | ||
776 | |||
777 | val = access_data(d, val); | ||
778 | |||
779 | d--; | ||
780 | } | ||
781 | } | ||
782 | |||
783 | return val; | ||
784 | } | ||
785 | |||
786 | static void update_curr_cpu(int task_nr, unsigned long bytes_worked) | ||
787 | { | ||
788 | unsigned int cpu; | ||
789 | |||
790 | cpu = sched_getcpu(); | ||
791 | |||
792 | g->threads[task_nr].curr_cpu = cpu; | ||
793 | prctl(0, bytes_worked); | ||
794 | } | ||
795 | |||
796 | #define MAX_NR_NODES 64 | ||
797 | |||
798 | /* | ||
799 | * Count the number of nodes a process's threads | ||
800 | * are spread out on. | ||
801 | * | ||
802 | * A count of 1 means that the process is compressed | ||
803 | * to a single node. A count of g->p.nr_nodes means it's | ||
804 | * spread out on the whole system. | ||
805 | */ | ||
806 | static int count_process_nodes(int process_nr) | ||
807 | { | ||
808 | char node_present[MAX_NR_NODES] = { 0, }; | ||
809 | int nodes; | ||
810 | int n, t; | ||
811 | |||
812 | for (t = 0; t < g->p.nr_threads; t++) { | ||
813 | struct thread_data *td; | ||
814 | int task_nr; | ||
815 | int node; | ||
816 | |||
817 | task_nr = process_nr*g->p.nr_threads + t; | ||
818 | td = g->threads + task_nr; | ||
819 | |||
820 | node = numa_node_of_cpu(td->curr_cpu); | ||
821 | node_present[node] = 1; | ||
822 | } | ||
823 | |||
824 | nodes = 0; | ||
825 | |||
826 | for (n = 0; n < MAX_NR_NODES; n++) | ||
827 | nodes += node_present[n]; | ||
828 | |||
829 | return nodes; | ||
830 | } | ||
831 | |||
832 | /* | ||
833 | * Count the number of distinct process-threads a node contains. | ||
834 | * | ||
835 | * A count of 1 means that the node contains only a single | ||
836 | * process. If all nodes on the system contain at most one | ||
837 | * process then we are well-converged. | ||
838 | */ | ||
839 | static int count_node_processes(int node) | ||
840 | { | ||
841 | int processes = 0; | ||
842 | int t, p; | ||
843 | |||
844 | for (p = 0; p < g->p.nr_proc; p++) { | ||
845 | for (t = 0; t < g->p.nr_threads; t++) { | ||
846 | struct thread_data *td; | ||
847 | int task_nr; | ||
848 | int n; | ||
849 | |||
850 | task_nr = p*g->p.nr_threads + t; | ||
851 | td = g->threads + task_nr; | ||
852 | |||
853 | n = numa_node_of_cpu(td->curr_cpu); | ||
854 | if (n == node) { | ||
855 | processes++; | ||
856 | break; | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | |||
861 | return processes; | ||
862 | } | ||
863 | |||
864 | static void calc_convergence_compression(int *strong) | ||
865 | { | ||
866 | unsigned int nodes_min, nodes_max; | ||
867 | int p; | ||
868 | |||
869 | nodes_min = -1; | ||
870 | nodes_max = 0; | ||
871 | |||
872 | for (p = 0; p < g->p.nr_proc; p++) { | ||
873 | unsigned int nodes = count_process_nodes(p); | ||
874 | |||
875 | nodes_min = min(nodes, nodes_min); | ||
876 | nodes_max = max(nodes, nodes_max); | ||
877 | } | ||
878 | |||
879 | /* Strong convergence: all threads compress on a single node: */ | ||
880 | if (nodes_min == 1 && nodes_max == 1) { | ||
881 | *strong = 1; | ||
882 | } else { | ||
883 | *strong = 0; | ||
884 | tprintf(" {%d-%d}", nodes_min, nodes_max); | ||
885 | } | ||
886 | } | ||
887 | |||
888 | static void calc_convergence(double runtime_ns_max, double *convergence) | ||
889 | { | ||
890 | unsigned int loops_done_min, loops_done_max; | ||
891 | int process_groups; | ||
892 | int nodes[MAX_NR_NODES]; | ||
893 | int distance; | ||
894 | int nr_min; | ||
895 | int nr_max; | ||
896 | int strong; | ||
897 | int sum; | ||
898 | int nr; | ||
899 | int node; | ||
900 | int cpu; | ||
901 | int t; | ||
902 | |||
903 | if (!g->p.show_convergence && !g->p.measure_convergence) | ||
904 | return; | ||
905 | |||
906 | for (node = 0; node < g->p.nr_nodes; node++) | ||
907 | nodes[node] = 0; | ||
908 | |||
909 | loops_done_min = -1; | ||
910 | loops_done_max = 0; | ||
911 | |||
912 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
913 | struct thread_data *td = g->threads + t; | ||
914 | unsigned int loops_done; | ||
915 | |||
916 | cpu = td->curr_cpu; | ||
917 | |||
918 | /* Not all threads have written it yet: */ | ||
919 | if (cpu < 0) | ||
920 | continue; | ||
921 | |||
922 | node = numa_node_of_cpu(cpu); | ||
923 | |||
924 | nodes[node]++; | ||
925 | |||
926 | loops_done = td->loops_done; | ||
927 | loops_done_min = min(loops_done, loops_done_min); | ||
928 | loops_done_max = max(loops_done, loops_done_max); | ||
929 | } | ||
930 | |||
931 | nr_max = 0; | ||
932 | nr_min = g->p.nr_tasks; | ||
933 | sum = 0; | ||
934 | |||
935 | for (node = 0; node < g->p.nr_nodes; node++) { | ||
936 | nr = nodes[node]; | ||
937 | nr_min = min(nr, nr_min); | ||
938 | nr_max = max(nr, nr_max); | ||
939 | sum += nr; | ||
940 | } | ||
941 | BUG_ON(nr_min > nr_max); | ||
942 | |||
943 | BUG_ON(sum > g->p.nr_tasks); | ||
944 | |||
945 | if (0 && (sum < g->p.nr_tasks)) | ||
946 | return; | ||
947 | |||
948 | /* | ||
949 | * Count the number of distinct process groups present | ||
950 | * on nodes - when we are converged this will decrease | ||
951 | * to g->p.nr_proc: | ||
952 | */ | ||
953 | process_groups = 0; | ||
954 | |||
955 | for (node = 0; node < g->p.nr_nodes; node++) { | ||
956 | int processes = count_node_processes(node); | ||
957 | |||
958 | nr = nodes[node]; | ||
959 | tprintf(" %2d/%-2d", nr, processes); | ||
960 | |||
961 | process_groups += processes; | ||
962 | } | ||
963 | |||
964 | distance = nr_max - nr_min; | ||
965 | |||
966 | tprintf(" [%2d/%-2d]", distance, process_groups); | ||
967 | |||
968 | tprintf(" l:%3d-%-3d (%3d)", | ||
969 | loops_done_min, loops_done_max, loops_done_max-loops_done_min); | ||
970 | |||
971 | if (loops_done_min && loops_done_max) { | ||
972 | double skew = 1.0 - (double)loops_done_min/loops_done_max; | ||
973 | |||
974 | tprintf(" [%4.1f%%]", skew * 100.0); | ||
975 | } | ||
976 | |||
977 | calc_convergence_compression(&strong); | ||
978 | |||
979 | if (strong && process_groups == g->p.nr_proc) { | ||
980 | if (!*convergence) { | ||
981 | *convergence = runtime_ns_max; | ||
982 | tprintf(" (%6.1fs converged)\n", *convergence/1e9); | ||
983 | if (g->p.measure_convergence) { | ||
984 | g->all_converged = true; | ||
985 | g->stop_work = true; | ||
986 | } | ||
987 | } | ||
988 | } else { | ||
989 | if (*convergence) { | ||
990 | tprintf(" (%6.1fs de-converged)", runtime_ns_max/1e9); | ||
991 | *convergence = 0; | ||
992 | } | ||
993 | tprintf("\n"); | ||
994 | } | ||
995 | } | ||
996 | |||
997 | static void show_summary(double runtime_ns_max, int l, double *convergence) | ||
998 | { | ||
999 | tprintf("\r # %5.1f%% [%.1f mins]", | ||
1000 | (double)(l+1)/g->p.nr_loops*100.0, runtime_ns_max/1e9 / 60.0); | ||
1001 | |||
1002 | calc_convergence(runtime_ns_max, convergence); | ||
1003 | |||
1004 | if (g->p.show_details >= 0) | ||
1005 | fflush(stdout); | ||
1006 | } | ||
1007 | |||
1008 | static void *worker_thread(void *__tdata) | ||
1009 | { | ||
1010 | struct thread_data *td = __tdata; | ||
1011 | struct timeval start0, start, stop, diff; | ||
1012 | int process_nr = td->process_nr; | ||
1013 | int thread_nr = td->thread_nr; | ||
1014 | unsigned long last_perturbance; | ||
1015 | int task_nr = td->task_nr; | ||
1016 | int details = g->p.show_details; | ||
1017 | int first_task, last_task; | ||
1018 | double convergence = 0; | ||
1019 | u64 val = td->val; | ||
1020 | double runtime_ns_max; | ||
1021 | u8 *global_data; | ||
1022 | u8 *process_data; | ||
1023 | u8 *thread_data; | ||
1024 | u64 bytes_done; | ||
1025 | long work_done; | ||
1026 | u32 l; | ||
1027 | |||
1028 | bind_to_cpumask(td->bind_cpumask); | ||
1029 | bind_to_memnode(td->bind_node); | ||
1030 | |||
1031 | set_taskname("thread %d/%d", process_nr, thread_nr); | ||
1032 | |||
1033 | global_data = g->data; | ||
1034 | process_data = td->process_data; | ||
1035 | thread_data = setup_private_data(g->p.bytes_thread); | ||
1036 | |||
1037 | bytes_done = 0; | ||
1038 | |||
1039 | last_task = 0; | ||
1040 | if (process_nr == g->p.nr_proc-1 && thread_nr == g->p.nr_threads-1) | ||
1041 | last_task = 1; | ||
1042 | |||
1043 | first_task = 0; | ||
1044 | if (process_nr == 0 && thread_nr == 0) | ||
1045 | first_task = 1; | ||
1046 | |||
1047 | if (details >= 2) { | ||
1048 | printf("# thread %2d / %2d global mem: %p, process mem: %p, thread mem: %p\n", | ||
1049 | process_nr, thread_nr, global_data, process_data, thread_data); | ||
1050 | } | ||
1051 | |||
1052 | if (g->p.serialize_startup) { | ||
1053 | pthread_mutex_lock(&g->startup_mutex); | ||
1054 | g->nr_tasks_started++; | ||
1055 | pthread_mutex_unlock(&g->startup_mutex); | ||
1056 | |||
1057 | /* Here we will wait for the main process to start us all at once: */ | ||
1058 | pthread_mutex_lock(&g->start_work_mutex); | ||
1059 | g->nr_tasks_working++; | ||
1060 | |||
1061 | /* Last one wake the main process: */ | ||
1062 | if (g->nr_tasks_working == g->p.nr_tasks) | ||
1063 | pthread_mutex_unlock(&g->startup_done_mutex); | ||
1064 | |||
1065 | pthread_mutex_unlock(&g->start_work_mutex); | ||
1066 | } | ||
1067 | |||
1068 | gettimeofday(&start0, NULL); | ||
1069 | |||
1070 | start = stop = start0; | ||
1071 | last_perturbance = start.tv_sec; | ||
1072 | |||
1073 | for (l = 0; l < g->p.nr_loops; l++) { | ||
1074 | start = stop; | ||
1075 | |||
1076 | if (g->stop_work) | ||
1077 | break; | ||
1078 | |||
1079 | val += do_work(global_data, g->p.bytes_global, process_nr, g->p.nr_proc, l, val); | ||
1080 | val += do_work(process_data, g->p.bytes_process, thread_nr, g->p.nr_threads, l, val); | ||
1081 | val += do_work(thread_data, g->p.bytes_thread, 0, 1, l, val); | ||
1082 | |||
1083 | if (g->p.sleep_usecs) { | ||
1084 | pthread_mutex_lock(td->process_lock); | ||
1085 | usleep(g->p.sleep_usecs); | ||
1086 | pthread_mutex_unlock(td->process_lock); | ||
1087 | } | ||
1088 | /* | ||
1089 | * Amount of work to be done under a process-global lock: | ||
1090 | */ | ||
1091 | if (g->p.bytes_process_locked) { | ||
1092 | pthread_mutex_lock(td->process_lock); | ||
1093 | val += do_work(process_data, g->p.bytes_process_locked, thread_nr, g->p.nr_threads, l, val); | ||
1094 | pthread_mutex_unlock(td->process_lock); | ||
1095 | } | ||
1096 | |||
1097 | work_done = g->p.bytes_global + g->p.bytes_process + | ||
1098 | g->p.bytes_process_locked + g->p.bytes_thread; | ||
1099 | |||
1100 | update_curr_cpu(task_nr, work_done); | ||
1101 | bytes_done += work_done; | ||
1102 | |||
1103 | if (details < 0 && !g->p.perturb_secs && !g->p.measure_convergence && !g->p.nr_secs) | ||
1104 | continue; | ||
1105 | |||
1106 | td->loops_done = l; | ||
1107 | |||
1108 | gettimeofday(&stop, NULL); | ||
1109 | |||
1110 | /* Check whether our max runtime timed out: */ | ||
1111 | if (g->p.nr_secs) { | ||
1112 | timersub(&stop, &start0, &diff); | ||
1113 | if (diff.tv_sec >= g->p.nr_secs) { | ||
1114 | g->stop_work = true; | ||
1115 | break; | ||
1116 | } | ||
1117 | } | ||
1118 | |||
1119 | /* Update the summary at most once per second: */ | ||
1120 | if (start.tv_sec == stop.tv_sec) | ||
1121 | continue; | ||
1122 | |||
1123 | /* | ||
1124 | * Perturb the first task's equilibrium every g->p.perturb_secs seconds, | ||
1125 | * by migrating to CPU#0: | ||
1126 | */ | ||
1127 | if (first_task && g->p.perturb_secs && (int)(stop.tv_sec - last_perturbance) >= g->p.perturb_secs) { | ||
1128 | cpu_set_t orig_mask; | ||
1129 | int target_cpu; | ||
1130 | int this_cpu; | ||
1131 | |||
1132 | last_perturbance = stop.tv_sec; | ||
1133 | |||
1134 | /* | ||
1135 | * Depending on where we are running, move into | ||
1136 | * the other half of the system, to create some | ||
1137 | * real disturbance: | ||
1138 | */ | ||
1139 | this_cpu = g->threads[task_nr].curr_cpu; | ||
1140 | if (this_cpu < g->p.nr_cpus/2) | ||
1141 | target_cpu = g->p.nr_cpus-1; | ||
1142 | else | ||
1143 | target_cpu = 0; | ||
1144 | |||
1145 | orig_mask = bind_to_cpu(target_cpu); | ||
1146 | |||
1147 | /* Here we are running on the target CPU already */ | ||
1148 | if (details >= 1) | ||
1149 | printf(" (injecting perturbalance, moved to CPU#%d)\n", target_cpu); | ||
1150 | |||
1151 | bind_to_cpumask(orig_mask); | ||
1152 | } | ||
1153 | |||
1154 | if (details >= 3) { | ||
1155 | timersub(&stop, &start, &diff); | ||
1156 | runtime_ns_max = diff.tv_sec * 1000000000; | ||
1157 | runtime_ns_max += diff.tv_usec * 1000; | ||
1158 | |||
1159 | if (details >= 0) { | ||
1160 | printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", | ||
1161 | process_nr, thread_nr, runtime_ns_max / bytes_done, val); | ||
1162 | } | ||
1163 | fflush(stdout); | ||
1164 | } | ||
1165 | if (!last_task) | ||
1166 | continue; | ||
1167 | |||
1168 | timersub(&stop, &start0, &diff); | ||
1169 | runtime_ns_max = diff.tv_sec * 1000000000ULL; | ||
1170 | runtime_ns_max += diff.tv_usec * 1000ULL; | ||
1171 | |||
1172 | show_summary(runtime_ns_max, l, &convergence); | ||
1173 | } | ||
1174 | |||
1175 | gettimeofday(&stop, NULL); | ||
1176 | timersub(&stop, &start0, &diff); | ||
1177 | td->runtime_ns = diff.tv_sec * 1000000000ULL; | ||
1178 | td->runtime_ns += diff.tv_usec * 1000ULL; | ||
1179 | |||
1180 | free_data(thread_data, g->p.bytes_thread); | ||
1181 | |||
1182 | pthread_mutex_lock(&g->stop_work_mutex); | ||
1183 | g->bytes_done += bytes_done; | ||
1184 | pthread_mutex_unlock(&g->stop_work_mutex); | ||
1185 | |||
1186 | return NULL; | ||
1187 | } | ||
1188 | |||
1189 | /* | ||
1190 | * A worker process starts a couple of threads: | ||
1191 | */ | ||
1192 | static void worker_process(int process_nr) | ||
1193 | { | ||
1194 | pthread_mutex_t process_lock; | ||
1195 | struct thread_data *td; | ||
1196 | pthread_t *pthreads; | ||
1197 | u8 *process_data; | ||
1198 | int task_nr; | ||
1199 | int ret; | ||
1200 | int t; | ||
1201 | |||
1202 | pthread_mutex_init(&process_lock, NULL); | ||
1203 | set_taskname("process %d", process_nr); | ||
1204 | |||
1205 | /* | ||
1206 | * Pick up the memory policy and the CPU binding of our first thread, | ||
1207 | * so that we initialize memory accordingly: | ||
1208 | */ | ||
1209 | task_nr = process_nr*g->p.nr_threads; | ||
1210 | td = g->threads + task_nr; | ||
1211 | |||
1212 | bind_to_memnode(td->bind_node); | ||
1213 | bind_to_cpumask(td->bind_cpumask); | ||
1214 | |||
1215 | pthreads = zalloc(g->p.nr_threads * sizeof(pthread_t)); | ||
1216 | process_data = setup_private_data(g->p.bytes_process); | ||
1217 | |||
1218 | if (g->p.show_details >= 3) { | ||
1219 | printf(" # process %2d global mem: %p, process mem: %p\n", | ||
1220 | process_nr, g->data, process_data); | ||
1221 | } | ||
1222 | |||
1223 | for (t = 0; t < g->p.nr_threads; t++) { | ||
1224 | task_nr = process_nr*g->p.nr_threads + t; | ||
1225 | td = g->threads + task_nr; | ||
1226 | |||
1227 | td->process_data = process_data; | ||
1228 | td->process_nr = process_nr; | ||
1229 | td->thread_nr = t; | ||
1230 | td->task_nr = task_nr; | ||
1231 | td->val = rand(); | ||
1232 | td->curr_cpu = -1; | ||
1233 | td->process_lock = &process_lock; | ||
1234 | |||
1235 | ret = pthread_create(pthreads + t, NULL, worker_thread, td); | ||
1236 | BUG_ON(ret); | ||
1237 | } | ||
1238 | |||
1239 | for (t = 0; t < g->p.nr_threads; t++) { | ||
1240 | ret = pthread_join(pthreads[t], NULL); | ||
1241 | BUG_ON(ret); | ||
1242 | } | ||
1243 | |||
1244 | free_data(process_data, g->p.bytes_process); | ||
1245 | free(pthreads); | ||
1246 | } | ||
1247 | |||
1248 | static void print_summary(void) | ||
1249 | { | ||
1250 | if (g->p.show_details < 0) | ||
1251 | return; | ||
1252 | |||
1253 | printf("\n ###\n"); | ||
1254 | printf(" # %d %s will execute (on %d nodes, %d CPUs):\n", | ||
1255 | g->p.nr_tasks, g->p.nr_tasks == 1 ? "task" : "tasks", g->p.nr_nodes, g->p.nr_cpus); | ||
1256 | printf(" # %5dx %5ldMB global shared mem operations\n", | ||
1257 | g->p.nr_loops, g->p.bytes_global/1024/1024); | ||
1258 | printf(" # %5dx %5ldMB process shared mem operations\n", | ||
1259 | g->p.nr_loops, g->p.bytes_process/1024/1024); | ||
1260 | printf(" # %5dx %5ldMB thread local mem operations\n", | ||
1261 | g->p.nr_loops, g->p.bytes_thread/1024/1024); | ||
1262 | |||
1263 | printf(" ###\n"); | ||
1264 | |||
1265 | printf("\n ###\n"); fflush(stdout); | ||
1266 | } | ||
1267 | |||
1268 | static void init_thread_data(void) | ||
1269 | { | ||
1270 | ssize_t size = sizeof(*g->threads)*g->p.nr_tasks; | ||
1271 | int t; | ||
1272 | |||
1273 | g->threads = zalloc_shared_data(size); | ||
1274 | |||
1275 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
1276 | struct thread_data *td = g->threads + t; | ||
1277 | int cpu; | ||
1278 | |||
1279 | /* Allow all nodes by default: */ | ||
1280 | td->bind_node = -1; | ||
1281 | |||
1282 | /* Allow all CPUs by default: */ | ||
1283 | CPU_ZERO(&td->bind_cpumask); | ||
1284 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
1285 | CPU_SET(cpu, &td->bind_cpumask); | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | static void deinit_thread_data(void) | ||
1290 | { | ||
1291 | ssize_t size = sizeof(*g->threads)*g->p.nr_tasks; | ||
1292 | |||
1293 | free_data(g->threads, size); | ||
1294 | } | ||
1295 | |||
1296 | static int init(void) | ||
1297 | { | ||
1298 | g = (void *)alloc_data(sizeof(*g), MAP_SHARED, 1, 0, 0 /* THP */, 0); | ||
1299 | |||
1300 | /* Copy over options: */ | ||
1301 | g->p = p0; | ||
1302 | |||
1303 | g->p.nr_cpus = numa_num_configured_cpus(); | ||
1304 | |||
1305 | g->p.nr_nodes = numa_max_node() + 1; | ||
1306 | |||
1307 | /* char array in count_process_nodes(): */ | ||
1308 | BUG_ON(g->p.nr_nodes > MAX_NR_NODES || g->p.nr_nodes < 0); | ||
1309 | |||
1310 | if (g->p.show_quiet && !g->p.show_details) | ||
1311 | g->p.show_details = -1; | ||
1312 | |||
1313 | /* Some memory should be specified: */ | ||
1314 | if (!g->p.mb_global_str && !g->p.mb_proc_str && !g->p.mb_thread_str) | ||
1315 | return -1; | ||
1316 | |||
1317 | if (g->p.mb_global_str) { | ||
1318 | g->p.mb_global = atof(g->p.mb_global_str); | ||
1319 | BUG_ON(g->p.mb_global < 0); | ||
1320 | } | ||
1321 | |||
1322 | if (g->p.mb_proc_str) { | ||
1323 | g->p.mb_proc = atof(g->p.mb_proc_str); | ||
1324 | BUG_ON(g->p.mb_proc < 0); | ||
1325 | } | ||
1326 | |||
1327 | if (g->p.mb_proc_locked_str) { | ||
1328 | g->p.mb_proc_locked = atof(g->p.mb_proc_locked_str); | ||
1329 | BUG_ON(g->p.mb_proc_locked < 0); | ||
1330 | BUG_ON(g->p.mb_proc_locked > g->p.mb_proc); | ||
1331 | } | ||
1332 | |||
1333 | if (g->p.mb_thread_str) { | ||
1334 | g->p.mb_thread = atof(g->p.mb_thread_str); | ||
1335 | BUG_ON(g->p.mb_thread < 0); | ||
1336 | } | ||
1337 | |||
1338 | BUG_ON(g->p.nr_threads <= 0); | ||
1339 | BUG_ON(g->p.nr_proc <= 0); | ||
1340 | |||
1341 | g->p.nr_tasks = g->p.nr_proc*g->p.nr_threads; | ||
1342 | |||
1343 | g->p.bytes_global = g->p.mb_global *1024L*1024L; | ||
1344 | g->p.bytes_process = g->p.mb_proc *1024L*1024L; | ||
1345 | g->p.bytes_process_locked = g->p.mb_proc_locked *1024L*1024L; | ||
1346 | g->p.bytes_thread = g->p.mb_thread *1024L*1024L; | ||
1347 | |||
1348 | g->data = setup_shared_data(g->p.bytes_global); | ||
1349 | |||
1350 | /* Startup serialization: */ | ||
1351 | init_global_mutex(&g->start_work_mutex); | ||
1352 | init_global_mutex(&g->startup_mutex); | ||
1353 | init_global_mutex(&g->startup_done_mutex); | ||
1354 | init_global_mutex(&g->stop_work_mutex); | ||
1355 | |||
1356 | init_thread_data(); | ||
1357 | |||
1358 | tprintf("#\n"); | ||
1359 | parse_setup_cpu_list(); | ||
1360 | parse_setup_node_list(); | ||
1361 | tprintf("#\n"); | ||
1362 | |||
1363 | print_summary(); | ||
1364 | |||
1365 | return 0; | ||
1366 | } | ||
1367 | |||
1368 | static void deinit(void) | ||
1369 | { | ||
1370 | free_data(g->data, g->p.bytes_global); | ||
1371 | g->data = NULL; | ||
1372 | |||
1373 | deinit_thread_data(); | ||
1374 | |||
1375 | free_data(g, sizeof(*g)); | ||
1376 | g = NULL; | ||
1377 | } | ||
1378 | |||
1379 | /* | ||
1380 | * Print a short or long result, depending on the verbosity setting: | ||
1381 | */ | ||
1382 | static void print_res(const char *name, double val, | ||
1383 | const char *txt_unit, const char *txt_short, const char *txt_long) | ||
1384 | { | ||
1385 | if (!name) | ||
1386 | name = "main,"; | ||
1387 | |||
1388 | if (g->p.show_quiet) | ||
1389 | printf(" %-30s %15.3f, %-15s %s\n", name, val, txt_unit, txt_short); | ||
1390 | else | ||
1391 | printf(" %14.3f %s\n", val, txt_long); | ||
1392 | } | ||
1393 | |||
1394 | static int __bench_numa(const char *name) | ||
1395 | { | ||
1396 | struct timeval start, stop, diff; | ||
1397 | u64 runtime_ns_min, runtime_ns_sum; | ||
1398 | pid_t *pids, pid, wpid; | ||
1399 | double delta_runtime; | ||
1400 | double runtime_avg; | ||
1401 | double runtime_sec_max; | ||
1402 | double runtime_sec_min; | ||
1403 | int wait_stat; | ||
1404 | double bytes; | ||
1405 | int i, t; | ||
1406 | |||
1407 | if (init()) | ||
1408 | return -1; | ||
1409 | |||
1410 | pids = zalloc(g->p.nr_proc * sizeof(*pids)); | ||
1411 | pid = -1; | ||
1412 | |||
1413 | /* All threads try to acquire it, this way we can wait for them to start up: */ | ||
1414 | pthread_mutex_lock(&g->start_work_mutex); | ||
1415 | |||
1416 | if (g->p.serialize_startup) { | ||
1417 | tprintf(" #\n"); | ||
1418 | tprintf(" # Startup synchronization: ..."); fflush(stdout); | ||
1419 | } | ||
1420 | |||
1421 | gettimeofday(&start, NULL); | ||
1422 | |||
1423 | for (i = 0; i < g->p.nr_proc; i++) { | ||
1424 | pid = fork(); | ||
1425 | dprintf(" # process %2d: PID %d\n", i, pid); | ||
1426 | |||
1427 | BUG_ON(pid < 0); | ||
1428 | if (!pid) { | ||
1429 | /* Child process: */ | ||
1430 | worker_process(i); | ||
1431 | |||
1432 | exit(0); | ||
1433 | } | ||
1434 | pids[i] = pid; | ||
1435 | |||
1436 | } | ||
1437 | /* Wait for all the threads to start up: */ | ||
1438 | while (g->nr_tasks_started != g->p.nr_tasks) | ||
1439 | usleep(1000); | ||
1440 | |||
1441 | BUG_ON(g->nr_tasks_started != g->p.nr_tasks); | ||
1442 | |||
1443 | if (g->p.serialize_startup) { | ||
1444 | double startup_sec; | ||
1445 | |||
1446 | pthread_mutex_lock(&g->startup_done_mutex); | ||
1447 | |||
1448 | /* This will start all threads: */ | ||
1449 | pthread_mutex_unlock(&g->start_work_mutex); | ||
1450 | |||
1451 | /* This mutex is locked - the last started thread will wake us: */ | ||
1452 | pthread_mutex_lock(&g->startup_done_mutex); | ||
1453 | |||
1454 | gettimeofday(&stop, NULL); | ||
1455 | |||
1456 | timersub(&stop, &start, &diff); | ||
1457 | |||
1458 | startup_sec = diff.tv_sec * 1000000000.0; | ||
1459 | startup_sec += diff.tv_usec * 1000.0; | ||
1460 | startup_sec /= 1e9; | ||
1461 | |||
1462 | tprintf(" threads initialized in %.6f seconds.\n", startup_sec); | ||
1463 | tprintf(" #\n"); | ||
1464 | |||
1465 | start = stop; | ||
1466 | pthread_mutex_unlock(&g->startup_done_mutex); | ||
1467 | } else { | ||
1468 | gettimeofday(&start, NULL); | ||
1469 | } | ||
1470 | |||
1471 | /* Parent process: */ | ||
1472 | |||
1473 | |||
1474 | for (i = 0; i < g->p.nr_proc; i++) { | ||
1475 | wpid = waitpid(pids[i], &wait_stat, 0); | ||
1476 | BUG_ON(wpid < 0); | ||
1477 | BUG_ON(!WIFEXITED(wait_stat)); | ||
1478 | |||
1479 | } | ||
1480 | |||
1481 | runtime_ns_sum = 0; | ||
1482 | runtime_ns_min = -1LL; | ||
1483 | |||
1484 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
1485 | u64 thread_runtime_ns = g->threads[t].runtime_ns; | ||
1486 | |||
1487 | runtime_ns_sum += thread_runtime_ns; | ||
1488 | runtime_ns_min = min(thread_runtime_ns, runtime_ns_min); | ||
1489 | } | ||
1490 | |||
1491 | gettimeofday(&stop, NULL); | ||
1492 | timersub(&stop, &start, &diff); | ||
1493 | |||
1494 | BUG_ON(bench_format != BENCH_FORMAT_DEFAULT); | ||
1495 | |||
1496 | tprintf("\n ###\n"); | ||
1497 | tprintf("\n"); | ||
1498 | |||
1499 | runtime_sec_max = diff.tv_sec * 1000000000.0; | ||
1500 | runtime_sec_max += diff.tv_usec * 1000.0; | ||
1501 | runtime_sec_max /= 1e9; | ||
1502 | |||
1503 | runtime_sec_min = runtime_ns_min/1e9; | ||
1504 | |||
1505 | bytes = g->bytes_done; | ||
1506 | runtime_avg = (double)runtime_ns_sum / g->p.nr_tasks / 1e9; | ||
1507 | |||
1508 | if (g->p.measure_convergence) { | ||
1509 | print_res(name, runtime_sec_max, | ||
1510 | "secs,", "NUMA-convergence-latency", "secs latency to NUMA-converge"); | ||
1511 | } | ||
1512 | |||
1513 | print_res(name, runtime_sec_max, | ||
1514 | "secs,", "runtime-max/thread", "secs slowest (max) thread-runtime"); | ||
1515 | |||
1516 | print_res(name, runtime_sec_min, | ||
1517 | "secs,", "runtime-min/thread", "secs fastest (min) thread-runtime"); | ||
1518 | |||
1519 | print_res(name, runtime_avg, | ||
1520 | "secs,", "runtime-avg/thread", "secs average thread-runtime"); | ||
1521 | |||
1522 | delta_runtime = (runtime_sec_max - runtime_sec_min)/2.0; | ||
1523 | print_res(name, delta_runtime / runtime_sec_max * 100.0, | ||
1524 | "%,", "spread-runtime/thread", "% difference between max/avg runtime"); | ||
1525 | |||
1526 | print_res(name, bytes / g->p.nr_tasks / 1e9, | ||
1527 | "GB,", "data/thread", "GB data processed, per thread"); | ||
1528 | |||
1529 | print_res(name, bytes / 1e9, | ||
1530 | "GB,", "data-total", "GB data processed, total"); | ||
1531 | |||
1532 | print_res(name, runtime_sec_max * 1e9 / (bytes / g->p.nr_tasks), | ||
1533 | "nsecs,", "runtime/byte/thread","nsecs/byte/thread runtime"); | ||
1534 | |||
1535 | print_res(name, bytes / g->p.nr_tasks / 1e9 / runtime_sec_max, | ||
1536 | "GB/sec,", "thread-speed", "GB/sec/thread speed"); | ||
1537 | |||
1538 | print_res(name, bytes / runtime_sec_max / 1e9, | ||
1539 | "GB/sec,", "total-speed", "GB/sec total speed"); | ||
1540 | |||
1541 | free(pids); | ||
1542 | |||
1543 | deinit(); | ||
1544 | |||
1545 | return 0; | ||
1546 | } | ||
1547 | |||
1548 | #define MAX_ARGS 50 | ||
1549 | |||
1550 | static int command_size(const char **argv) | ||
1551 | { | ||
1552 | int size = 0; | ||
1553 | |||
1554 | while (*argv) { | ||
1555 | size++; | ||
1556 | argv++; | ||
1557 | } | ||
1558 | |||
1559 | BUG_ON(size >= MAX_ARGS); | ||
1560 | |||
1561 | return size; | ||
1562 | } | ||
1563 | |||
1564 | static void init_params(struct params *p, const char *name, int argc, const char **argv) | ||
1565 | { | ||
1566 | int i; | ||
1567 | |||
1568 | printf("\n # Running %s \"perf bench numa", name); | ||
1569 | |||
1570 | for (i = 0; i < argc; i++) | ||
1571 | printf(" %s", argv[i]); | ||
1572 | |||
1573 | printf("\"\n"); | ||
1574 | |||
1575 | memset(p, 0, sizeof(*p)); | ||
1576 | |||
1577 | /* Initialize nonzero defaults: */ | ||
1578 | |||
1579 | p->serialize_startup = 1; | ||
1580 | p->data_reads = true; | ||
1581 | p->data_writes = true; | ||
1582 | p->data_backwards = true; | ||
1583 | p->data_rand_walk = true; | ||
1584 | p->nr_loops = -1; | ||
1585 | p->init_random = true; | ||
1586 | } | ||
1587 | |||
1588 | static int run_bench_numa(const char *name, const char **argv) | ||
1589 | { | ||
1590 | int argc = command_size(argv); | ||
1591 | |||
1592 | init_params(&p0, name, argc, argv); | ||
1593 | argc = parse_options(argc, argv, options, bench_numa_usage, 0); | ||
1594 | if (argc) | ||
1595 | goto err; | ||
1596 | |||
1597 | if (__bench_numa(name)) | ||
1598 | goto err; | ||
1599 | |||
1600 | return 0; | ||
1601 | |||
1602 | err: | ||
1603 | usage_with_options(numa_usage, options); | ||
1604 | return -1; | ||
1605 | } | ||
1606 | |||
1607 | #define OPT_BW_RAM "-s", "20", "-zZq", "--thp", " 1", "--no-data_rand_walk" | ||
1608 | #define OPT_BW_RAM_NOTHP OPT_BW_RAM, "--thp", "-1" | ||
1609 | |||
1610 | #define OPT_CONV "-s", "100", "-zZ0qcm", "--thp", " 1" | ||
1611 | #define OPT_CONV_NOTHP OPT_CONV, "--thp", "-1" | ||
1612 | |||
1613 | #define OPT_BW "-s", "20", "-zZ0q", "--thp", " 1" | ||
1614 | #define OPT_BW_NOTHP OPT_BW, "--thp", "-1" | ||
1615 | |||
1616 | /* | ||
1617 | * The built-in test-suite executed by "perf bench numa -a". | ||
1618 | * | ||
1619 | * (A minimum of 4 nodes and 16 GB of RAM is recommended.) | ||
1620 | */ | ||
1621 | static const char *tests[][MAX_ARGS] = { | ||
1622 | /* Basic single-stream NUMA bandwidth measurements: */ | ||
1623 | { "RAM-bw-local,", "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1624 | "-C" , "0", "-M", "0", OPT_BW_RAM }, | ||
1625 | { "RAM-bw-local-NOTHP,", | ||
1626 | "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1627 | "-C" , "0", "-M", "0", OPT_BW_RAM_NOTHP }, | ||
1628 | { "RAM-bw-remote,", "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1629 | "-C" , "0", "-M", "1", OPT_BW_RAM }, | ||
1630 | |||
1631 | /* 2-stream NUMA bandwidth measurements: */ | ||
1632 | { "RAM-bw-local-2x,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1633 | "-C", "0,2", "-M", "0x2", OPT_BW_RAM }, | ||
1634 | { "RAM-bw-remote-2x,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1635 | "-C", "0,2", "-M", "1x2", OPT_BW_RAM }, | ||
1636 | |||
1637 | /* Cross-stream NUMA bandwidth measurement: */ | ||
1638 | { "RAM-bw-cross,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1639 | "-C", "0,8", "-M", "1,0", OPT_BW_RAM }, | ||
1640 | |||
1641 | /* Convergence latency measurements: */ | ||
1642 | { " 1x3-convergence,", "mem", "-p", "1", "-t", "3", "-P", "512", OPT_CONV }, | ||
1643 | { " 1x4-convergence,", "mem", "-p", "1", "-t", "4", "-P", "512", OPT_CONV }, | ||
1644 | { " 1x6-convergence,", "mem", "-p", "1", "-t", "6", "-P", "1020", OPT_CONV }, | ||
1645 | { " 2x3-convergence,", "mem", "-p", "3", "-t", "3", "-P", "1020", OPT_CONV }, | ||
1646 | { " 3x3-convergence,", "mem", "-p", "3", "-t", "3", "-P", "1020", OPT_CONV }, | ||
1647 | { " 4x4-convergence,", "mem", "-p", "4", "-t", "4", "-P", "512", OPT_CONV }, | ||
1648 | { " 4x4-convergence-NOTHP,", | ||
1649 | "mem", "-p", "4", "-t", "4", "-P", "512", OPT_CONV_NOTHP }, | ||
1650 | { " 4x6-convergence,", "mem", "-p", "4", "-t", "6", "-P", "1020", OPT_CONV }, | ||
1651 | { " 4x8-convergence,", "mem", "-p", "4", "-t", "8", "-P", "512", OPT_CONV }, | ||
1652 | { " 8x4-convergence,", "mem", "-p", "8", "-t", "4", "-P", "512", OPT_CONV }, | ||
1653 | { " 8x4-convergence-NOTHP,", | ||
1654 | "mem", "-p", "8", "-t", "4", "-P", "512", OPT_CONV_NOTHP }, | ||
1655 | { " 3x1-convergence,", "mem", "-p", "3", "-t", "1", "-P", "512", OPT_CONV }, | ||
1656 | { " 4x1-convergence,", "mem", "-p", "4", "-t", "1", "-P", "512", OPT_CONV }, | ||
1657 | { " 8x1-convergence,", "mem", "-p", "8", "-t", "1", "-P", "512", OPT_CONV }, | ||
1658 | { "16x1-convergence,", "mem", "-p", "16", "-t", "1", "-P", "256", OPT_CONV }, | ||
1659 | { "32x1-convergence,", "mem", "-p", "32", "-t", "1", "-P", "128", OPT_CONV }, | ||
1660 | |||
1661 | /* Various NUMA process/thread layout bandwidth measurements: */ | ||
1662 | { " 2x1-bw-process,", "mem", "-p", "2", "-t", "1", "-P", "1024", OPT_BW }, | ||
1663 | { " 3x1-bw-process,", "mem", "-p", "3", "-t", "1", "-P", "1024", OPT_BW }, | ||
1664 | { " 4x1-bw-process,", "mem", "-p", "4", "-t", "1", "-P", "1024", OPT_BW }, | ||
1665 | { " 8x1-bw-process,", "mem", "-p", "8", "-t", "1", "-P", " 512", OPT_BW }, | ||
1666 | { " 8x1-bw-process-NOTHP,", | ||
1667 | "mem", "-p", "8", "-t", "1", "-P", " 512", OPT_BW_NOTHP }, | ||
1668 | { "16x1-bw-process,", "mem", "-p", "16", "-t", "1", "-P", "256", OPT_BW }, | ||
1669 | |||
1670 | { " 4x1-bw-thread,", "mem", "-p", "1", "-t", "4", "-T", "256", OPT_BW }, | ||
1671 | { " 8x1-bw-thread,", "mem", "-p", "1", "-t", "8", "-T", "256", OPT_BW }, | ||
1672 | { "16x1-bw-thread,", "mem", "-p", "1", "-t", "16", "-T", "128", OPT_BW }, | ||
1673 | { "32x1-bw-thread,", "mem", "-p", "1", "-t", "32", "-T", "64", OPT_BW }, | ||
1674 | |||
1675 | { " 2x3-bw-thread,", "mem", "-p", "2", "-t", "3", "-P", "512", OPT_BW }, | ||
1676 | { " 4x4-bw-thread,", "mem", "-p", "4", "-t", "4", "-P", "512", OPT_BW }, | ||
1677 | { " 4x6-bw-thread,", "mem", "-p", "4", "-t", "6", "-P", "512", OPT_BW }, | ||
1678 | { " 4x8-bw-thread,", "mem", "-p", "4", "-t", "8", "-P", "512", OPT_BW }, | ||
1679 | { " 4x8-bw-thread-NOTHP,", | ||
1680 | "mem", "-p", "4", "-t", "8", "-P", "512", OPT_BW_NOTHP }, | ||
1681 | { " 3x3-bw-thread,", "mem", "-p", "3", "-t", "3", "-P", "512", OPT_BW }, | ||
1682 | { " 5x5-bw-thread,", "mem", "-p", "5", "-t", "5", "-P", "512", OPT_BW }, | ||
1683 | |||
1684 | { "2x16-bw-thread,", "mem", "-p", "2", "-t", "16", "-P", "512", OPT_BW }, | ||
1685 | { "1x32-bw-thread,", "mem", "-p", "1", "-t", "32", "-P", "2048", OPT_BW }, | ||
1686 | |||
1687 | { "numa02-bw,", "mem", "-p", "1", "-t", "32", "-T", "32", OPT_BW }, | ||
1688 | { "numa02-bw-NOTHP,", "mem", "-p", "1", "-t", "32", "-T", "32", OPT_BW_NOTHP }, | ||
1689 | { "numa01-bw-thread,", "mem", "-p", "2", "-t", "16", "-T", "192", OPT_BW }, | ||
1690 | { "numa01-bw-thread-NOTHP,", | ||
1691 | "mem", "-p", "2", "-t", "16", "-T", "192", OPT_BW_NOTHP }, | ||
1692 | }; | ||
1693 | |||
1694 | static int bench_all(void) | ||
1695 | { | ||
1696 | int nr = ARRAY_SIZE(tests); | ||
1697 | int ret; | ||
1698 | int i; | ||
1699 | |||
1700 | ret = system("echo ' #'; echo ' # Running test on: '$(uname -a); echo ' #'"); | ||
1701 | BUG_ON(ret < 0); | ||
1702 | |||
1703 | for (i = 0; i < nr; i++) { | ||
1704 | if (run_bench_numa(tests[i][0], tests[i] + 1)) | ||
1705 | return -1; | ||
1706 | } | ||
1707 | |||
1708 | printf("\n"); | ||
1709 | |||
1710 | return 0; | ||
1711 | } | ||
1712 | |||
1713 | int bench_numa(int argc, const char **argv, const char *prefix __maybe_unused) | ||
1714 | { | ||
1715 | init_params(&p0, "main,", argc, argv); | ||
1716 | argc = parse_options(argc, argv, options, bench_numa_usage, 0); | ||
1717 | if (argc) | ||
1718 | goto err; | ||
1719 | |||
1720 | if (p0.run_all) | ||
1721 | return bench_all(); | ||
1722 | |||
1723 | if (__bench_numa(NULL)) | ||
1724 | goto err; | ||
1725 | |||
1726 | return 0; | ||
1727 | |||
1728 | err: | ||
1729 | usage_with_options(numa_usage, options); | ||
1730 | return -1; | ||
1731 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index dc870cf31b79..db491e9a812b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -34,9 +34,10 @@ | |||
34 | 34 | ||
35 | struct perf_annotate { | 35 | struct perf_annotate { |
36 | struct perf_tool tool; | 36 | struct perf_tool tool; |
37 | bool force, use_tui, use_stdio; | 37 | bool force, use_tui, use_stdio, use_gtk; |
38 | bool full_paths; | 38 | bool full_paths; |
39 | bool print_line; | 39 | bool print_line; |
40 | bool skip_missing; | ||
40 | const char *sym_hist_filter; | 41 | const char *sym_hist_filter; |
41 | const char *cpu_list; | 42 | const char *cpu_list; |
42 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 43 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
@@ -62,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
62 | return 0; | 63 | return 0; |
63 | } | 64 | } |
64 | 65 | ||
65 | he = __hists__add_entry(&evsel->hists, al, NULL, 1); | 66 | he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1); |
66 | if (he == NULL) | 67 | if (he == NULL) |
67 | return -ENOMEM; | 68 | return -ENOMEM; |
68 | 69 | ||
@@ -108,14 +109,16 @@ static int process_sample_event(struct perf_tool *tool, | |||
108 | return 0; | 109 | return 0; |
109 | } | 110 | } |
110 | 111 | ||
111 | static int hist_entry__tty_annotate(struct hist_entry *he, int evidx, | 112 | static int hist_entry__tty_annotate(struct hist_entry *he, |
113 | struct perf_evsel *evsel, | ||
112 | struct perf_annotate *ann) | 114 | struct perf_annotate *ann) |
113 | { | 115 | { |
114 | return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, | 116 | return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, |
115 | ann->print_line, ann->full_paths, 0, 0); | 117 | ann->print_line, ann->full_paths, 0, 0); |
116 | } | 118 | } |
117 | 119 | ||
118 | static void hists__find_annotations(struct hists *self, int evidx, | 120 | static void hists__find_annotations(struct hists *self, |
121 | struct perf_evsel *evsel, | ||
119 | struct perf_annotate *ann) | 122 | struct perf_annotate *ann) |
120 | { | 123 | { |
121 | struct rb_node *nd = rb_first(&self->entries), *next; | 124 | struct rb_node *nd = rb_first(&self->entries), *next; |
@@ -138,9 +141,22 @@ find_next: | |||
138 | continue; | 141 | continue; |
139 | } | 142 | } |
140 | 143 | ||
141 | if (use_browser > 0) { | 144 | if (use_browser == 2) { |
142 | key = hist_entry__tui_annotate(he, evidx, NULL); | 145 | int ret; |
146 | |||
147 | ret = hist_entry__gtk_annotate(he, evsel, NULL); | ||
148 | if (!ret || !ann->skip_missing) | ||
149 | return; | ||
150 | |||
151 | /* skip missing symbols */ | ||
152 | nd = rb_next(nd); | ||
153 | } else if (use_browser == 1) { | ||
154 | key = hist_entry__tui_annotate(he, evsel, NULL); | ||
143 | switch (key) { | 155 | switch (key) { |
156 | case -1: | ||
157 | if (!ann->skip_missing) | ||
158 | return; | ||
159 | /* fall through */ | ||
144 | case K_RIGHT: | 160 | case K_RIGHT: |
145 | next = rb_next(nd); | 161 | next = rb_next(nd); |
146 | break; | 162 | break; |
@@ -154,7 +170,7 @@ find_next: | |||
154 | if (next != NULL) | 170 | if (next != NULL) |
155 | nd = next; | 171 | nd = next; |
156 | } else { | 172 | } else { |
157 | hist_entry__tty_annotate(he, evidx, ann); | 173 | hist_entry__tty_annotate(he, evsel, ann); |
158 | nd = rb_next(nd); | 174 | nd = rb_next(nd); |
159 | /* | 175 | /* |
160 | * Since we have a hist_entry per IP for the same | 176 | * Since we have a hist_entry per IP for the same |
@@ -216,7 +232,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
216 | total_nr_samples += nr_samples; | 232 | total_nr_samples += nr_samples; |
217 | hists__collapse_resort(hists); | 233 | hists__collapse_resort(hists); |
218 | hists__output_resort(hists); | 234 | hists__output_resort(hists); |
219 | hists__find_annotations(hists, pos->idx, ann); | 235 | |
236 | if (symbol_conf.event_group && | ||
237 | !perf_evsel__is_group_leader(pos)) | ||
238 | continue; | ||
239 | |||
240 | hists__find_annotations(hists, pos, ann); | ||
220 | } | 241 | } |
221 | } | 242 | } |
222 | 243 | ||
@@ -224,6 +245,10 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
224 | ui__error("The %s file has no samples!\n", session->filename); | 245 | ui__error("The %s file has no samples!\n", session->filename); |
225 | goto out_delete; | 246 | goto out_delete; |
226 | } | 247 | } |
248 | |||
249 | if (use_browser == 2) | ||
250 | perf_gtk__show_annotations(); | ||
251 | |||
227 | out_delete: | 252 | out_delete: |
228 | /* | 253 | /* |
229 | * Speed up the exit process, for large files this can | 254 | * Speed up the exit process, for large files this can |
@@ -270,6 +295,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
270 | "be more verbose (show symbol address, etc)"), | 295 | "be more verbose (show symbol address, etc)"), |
271 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 296 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
272 | "dump raw trace in ASCII"), | 297 | "dump raw trace in ASCII"), |
298 | OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"), | ||
273 | OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), | 299 | OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), |
274 | OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"), | 300 | OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"), |
275 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 301 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
@@ -280,6 +306,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
280 | "print matching source lines (may be slow)"), | 306 | "print matching source lines (may be slow)"), |
281 | OPT_BOOLEAN('P', "full-paths", &annotate.full_paths, | 307 | OPT_BOOLEAN('P', "full-paths", &annotate.full_paths, |
282 | "Don't shorten the displayed pathnames"), | 308 | "Don't shorten the displayed pathnames"), |
309 | OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing, | ||
310 | "Skip symbols that cannot be annotated"), | ||
283 | OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), | 311 | OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), |
284 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 312 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
285 | "Look for files with symbols relative to this directory"), | 313 | "Look for files with symbols relative to this directory"), |
@@ -291,6 +319,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
291 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 319 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
292 | OPT_STRING(0, "objdump", &objdump_path, "path", | 320 | OPT_STRING(0, "objdump", &objdump_path, "path", |
293 | "objdump binary to use for disassembly and annotations"), | 321 | "objdump binary to use for disassembly and annotations"), |
322 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, | ||
323 | "Show event group information together"), | ||
294 | OPT_END() | 324 | OPT_END() |
295 | }; | 325 | }; |
296 | 326 | ||
@@ -300,6 +330,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
300 | use_browser = 0; | 330 | use_browser = 0; |
301 | else if (annotate.use_tui) | 331 | else if (annotate.use_tui) |
302 | use_browser = 1; | 332 | use_browser = 1; |
333 | else if (annotate.use_gtk) | ||
334 | use_browser = 2; | ||
303 | 335 | ||
304 | setup_browser(true); | 336 | setup_browser(true); |
305 | 337 | ||
@@ -309,7 +341,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
309 | if (symbol__init() < 0) | 341 | if (symbol__init() < 0) |
310 | return -1; | 342 | return -1; |
311 | 343 | ||
312 | setup_sorting(annotate_usage, options); | 344 | if (setup_sorting() < 0) |
345 | usage_with_options(annotate_usage, options); | ||
313 | 346 | ||
314 | if (argc) { | 347 | if (argc) { |
315 | /* | 348 | /* |
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index cae9a5fd2ecf..77298bf892b8 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c | |||
@@ -35,6 +35,18 @@ struct bench_suite { | |||
35 | /* sentinel: easy for help */ | 35 | /* sentinel: easy for help */ |
36 | #define suite_all { "all", "Test all benchmark suites", NULL } | 36 | #define suite_all { "all", "Test all benchmark suites", NULL } |
37 | 37 | ||
38 | #ifdef LIBNUMA_SUPPORT | ||
39 | static struct bench_suite numa_suites[] = { | ||
40 | { "mem", | ||
41 | "Benchmark for NUMA workloads", | ||
42 | bench_numa }, | ||
43 | suite_all, | ||
44 | { NULL, | ||
45 | NULL, | ||
46 | NULL } | ||
47 | }; | ||
48 | #endif | ||
49 | |||
38 | static struct bench_suite sched_suites[] = { | 50 | static struct bench_suite sched_suites[] = { |
39 | { "messaging", | 51 | { "messaging", |
40 | "Benchmark for scheduler and IPC mechanisms", | 52 | "Benchmark for scheduler and IPC mechanisms", |
@@ -68,6 +80,11 @@ struct bench_subsys { | |||
68 | }; | 80 | }; |
69 | 81 | ||
70 | static struct bench_subsys subsystems[] = { | 82 | static struct bench_subsys subsystems[] = { |
83 | #ifdef LIBNUMA_SUPPORT | ||
84 | { "numa", | ||
85 | "NUMA scheduling and MM behavior", | ||
86 | numa_suites }, | ||
87 | #endif | ||
71 | { "sched", | 88 | { "sched", |
72 | "scheduler and IPC mechanism", | 89 | "scheduler and IPC mechanism", |
73 | sched_suites }, | 90 | sched_suites }, |
@@ -159,6 +176,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */ | |||
159 | printf("# Running %s/%s benchmark...\n", | 176 | printf("# Running %s/%s benchmark...\n", |
160 | subsys->name, | 177 | subsys->name, |
161 | suites[i].name); | 178 | suites[i].name); |
179 | fflush(stdout); | ||
162 | 180 | ||
163 | argv[1] = suites[i].name; | 181 | argv[1] = suites[i].name; |
164 | suites[i].fn(1, argv, NULL); | 182 | suites[i].fn(1, argv, NULL); |
@@ -225,6 +243,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) | |||
225 | printf("# Running %s/%s benchmark...\n", | 243 | printf("# Running %s/%s benchmark...\n", |
226 | subsystems[i].name, | 244 | subsystems[i].name, |
227 | subsystems[i].suites[j].name); | 245 | subsystems[i].suites[j].name); |
246 | fflush(stdout); | ||
228 | status = subsystems[i].suites[j].fn(argc - 1, | 247 | status = subsystems[i].suites[j].fn(argc - 1, |
229 | argv + 1, prefix); | 248 | argv + 1, prefix); |
230 | goto end; | 249 | goto end; |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index fae8b250b2ca..c96c8fa38243 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
15 | #include "util/strlist.h" | 15 | #include "util/strlist.h" |
16 | #include "util/build-id.h" | 16 | #include "util/build-id.h" |
17 | #include "util/session.h" | ||
17 | #include "util/symbol.h" | 18 | #include "util/symbol.h" |
18 | 19 | ||
19 | static int build_id_cache__add_file(const char *filename, const char *debugdir) | 20 | static int build_id_cache__add_file(const char *filename, const char *debugdir) |
@@ -58,19 +59,89 @@ static int build_id_cache__remove_file(const char *filename, | |||
58 | return err; | 59 | return err; |
59 | } | 60 | } |
60 | 61 | ||
62 | static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | ||
63 | { | ||
64 | char filename[PATH_MAX]; | ||
65 | u8 build_id[BUILD_ID_SIZE]; | ||
66 | |||
67 | if (dso__build_id_filename(dso, filename, sizeof(filename)) && | ||
68 | filename__read_build_id(filename, build_id, | ||
69 | sizeof(build_id)) != sizeof(build_id)) { | ||
70 | if (errno == ENOENT) | ||
71 | return false; | ||
72 | |||
73 | pr_warning("Problems with %s file, consider removing it from the cache\n", | ||
74 | filename); | ||
75 | } else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) { | ||
76 | pr_warning("Problems with %s file, consider removing it from the cache\n", | ||
77 | filename); | ||
78 | } | ||
79 | |||
80 | return true; | ||
81 | } | ||
82 | |||
83 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) | ||
84 | { | ||
85 | struct perf_session *session = perf_session__new(filename, O_RDONLY, | ||
86 | force, false, NULL); | ||
87 | if (session == NULL) | ||
88 | return -1; | ||
89 | |||
90 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); | ||
91 | perf_session__delete(session); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int build_id_cache__update_file(const char *filename, | ||
97 | const char *debugdir) | ||
98 | { | ||
99 | u8 build_id[BUILD_ID_SIZE]; | ||
100 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
101 | |||
102 | int err; | ||
103 | |||
104 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
105 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
106 | return -1; | ||
107 | } | ||
108 | |||
109 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
110 | err = build_id_cache__remove_s(sbuild_id, debugdir); | ||
111 | if (!err) { | ||
112 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, | ||
113 | false, false); | ||
114 | } | ||
115 | if (verbose) | ||
116 | pr_info("Updating %s %s: %s\n", sbuild_id, filename, | ||
117 | err ? "FAIL" : "Ok"); | ||
118 | |||
119 | return err; | ||
120 | } | ||
121 | |||
61 | int cmd_buildid_cache(int argc, const char **argv, | 122 | int cmd_buildid_cache(int argc, const char **argv, |
62 | const char *prefix __maybe_unused) | 123 | const char *prefix __maybe_unused) |
63 | { | 124 | { |
64 | struct strlist *list; | 125 | struct strlist *list; |
65 | struct str_node *pos; | 126 | struct str_node *pos; |
127 | int ret = 0; | ||
128 | bool force = false; | ||
66 | char debugdir[PATH_MAX]; | 129 | char debugdir[PATH_MAX]; |
67 | char const *add_name_list_str = NULL, | 130 | char const *add_name_list_str = NULL, |
68 | *remove_name_list_str = NULL; | 131 | *remove_name_list_str = NULL, |
132 | *missing_filename = NULL, | ||
133 | *update_name_list_str = NULL; | ||
134 | |||
69 | const struct option buildid_cache_options[] = { | 135 | const struct option buildid_cache_options[] = { |
70 | OPT_STRING('a', "add", &add_name_list_str, | 136 | OPT_STRING('a', "add", &add_name_list_str, |
71 | "file list", "file(s) to add"), | 137 | "file list", "file(s) to add"), |
72 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", | 138 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", |
73 | "file(s) to remove"), | 139 | "file(s) to remove"), |
140 | OPT_STRING('M', "missing", &missing_filename, "file", | ||
141 | "to find missing build ids in the cache"), | ||
142 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
143 | OPT_STRING('u', "update", &update_name_list_str, "file list", | ||
144 | "file(s) to update"), | ||
74 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 145 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
75 | OPT_END() | 146 | OPT_END() |
76 | }; | 147 | }; |
@@ -125,5 +196,26 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
125 | } | 196 | } |
126 | } | 197 | } |
127 | 198 | ||
128 | return 0; | 199 | if (missing_filename) |
200 | ret = build_id_cache__fprintf_missing(missing_filename, force, stdout); | ||
201 | |||
202 | if (update_name_list_str) { | ||
203 | list = strlist__new(true, update_name_list_str); | ||
204 | if (list) { | ||
205 | strlist__for_each(pos, list) | ||
206 | if (build_id_cache__update_file(pos->s, debugdir)) { | ||
207 | if (errno == ENOENT) { | ||
208 | pr_debug("%s wasn't in the cache\n", | ||
209 | pos->s); | ||
210 | continue; | ||
211 | } | ||
212 | pr_warning("Couldn't update %s: %s\n", | ||
213 | pos->s, strerror(errno)); | ||
214 | } | ||
215 | |||
216 | strlist__delete(list); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return ret; | ||
129 | } | 221 | } |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a82d99fec83e..e74366a13218 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -44,23 +44,26 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) | |||
44 | return fprintf(fp, "%s\n", sbuild_id); | 44 | return fprintf(fp, "%s\n", sbuild_id); |
45 | } | 45 | } |
46 | 46 | ||
47 | static bool dso__skip_buildid(struct dso *dso, int with_hits) | ||
48 | { | ||
49 | return with_hits && !dso->hit; | ||
50 | } | ||
51 | |||
47 | static int perf_session__list_build_ids(bool force, bool with_hits) | 52 | static int perf_session__list_build_ids(bool force, bool with_hits) |
48 | { | 53 | { |
49 | struct perf_session *session; | 54 | struct perf_session *session; |
50 | 55 | ||
51 | symbol__elf_init(); | 56 | symbol__elf_init(); |
52 | |||
53 | session = perf_session__new(input_name, O_RDONLY, force, false, | ||
54 | &build_id__mark_dso_hit_ops); | ||
55 | if (session == NULL) | ||
56 | return -1; | ||
57 | |||
58 | /* | 57 | /* |
59 | * See if this is an ELF file first: | 58 | * See if this is an ELF file first: |
60 | */ | 59 | */ |
61 | if (filename__fprintf_build_id(session->filename, stdout)) | 60 | if (filename__fprintf_build_id(input_name, stdout)) |
62 | goto out; | 61 | goto out; |
63 | 62 | ||
63 | session = perf_session__new(input_name, O_RDONLY, force, false, | ||
64 | &build_id__mark_dso_hit_ops); | ||
65 | if (session == NULL) | ||
66 | return -1; | ||
64 | /* | 67 | /* |
65 | * in pipe-mode, the only way to get the buildids is to parse | 68 | * in pipe-mode, the only way to get the buildids is to parse |
66 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID | 69 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID |
@@ -68,9 +71,9 @@ static int perf_session__list_build_ids(bool force, bool with_hits) | |||
68 | if (with_hits || session->fd_pipe) | 71 | if (with_hits || session->fd_pipe) |
69 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 72 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
70 | 73 | ||
71 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); | 74 | perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); |
72 | out: | ||
73 | perf_session__delete(session); | 75 | perf_session__delete(session); |
76 | out: | ||
74 | return 0; | 77 | return 0; |
75 | } | 78 | } |
76 | 79 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 93b852f8a5d5..da8f8eb383a0 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -23,7 +23,6 @@ static char const *input_old = "perf.data.old", | |||
23 | *input_new = "perf.data"; | 23 | *input_new = "perf.data"; |
24 | static char diff__default_sort_order[] = "dso,symbol"; | 24 | static char diff__default_sort_order[] = "dso,symbol"; |
25 | static bool force; | 25 | static bool force; |
26 | static bool show_displacement; | ||
27 | static bool show_period; | 26 | static bool show_period; |
28 | static bool show_formula; | 27 | static bool show_formula; |
29 | static bool show_baseline_only; | 28 | static bool show_baseline_only; |
@@ -146,58 +145,47 @@ static int setup_compute(const struct option *opt, const char *str, | |||
146 | return -EINVAL; | 145 | return -EINVAL; |
147 | } | 146 | } |
148 | 147 | ||
149 | static double get_period_percent(struct hist_entry *he, u64 period) | 148 | double perf_diff__period_percent(struct hist_entry *he, u64 period) |
150 | { | 149 | { |
151 | u64 total = he->hists->stats.total_period; | 150 | u64 total = he->hists->stats.total_period; |
152 | return (period * 100.0) / total; | 151 | return (period * 100.0) / total; |
153 | } | 152 | } |
154 | 153 | ||
155 | double perf_diff__compute_delta(struct hist_entry *he) | 154 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair) |
156 | { | 155 | { |
157 | struct hist_entry *pair = hist_entry__next_pair(he); | 156 | double new_percent = perf_diff__period_percent(he, he->stat.period); |
158 | double new_percent = get_period_percent(he, he->stat.period); | 157 | double old_percent = perf_diff__period_percent(pair, pair->stat.period); |
159 | double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; | ||
160 | 158 | ||
161 | he->diff.period_ratio_delta = new_percent - old_percent; | 159 | he->diff.period_ratio_delta = new_percent - old_percent; |
162 | he->diff.computed = true; | 160 | he->diff.computed = true; |
163 | return he->diff.period_ratio_delta; | 161 | return he->diff.period_ratio_delta; |
164 | } | 162 | } |
165 | 163 | ||
166 | double perf_diff__compute_ratio(struct hist_entry *he) | 164 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair) |
167 | { | 165 | { |
168 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
169 | double new_period = he->stat.period; | 166 | double new_period = he->stat.period; |
170 | double old_period = pair ? pair->stat.period : 0; | 167 | double old_period = pair->stat.period; |
171 | 168 | ||
172 | he->diff.computed = true; | 169 | he->diff.computed = true; |
173 | he->diff.period_ratio = pair ? (new_period / old_period) : 0; | 170 | he->diff.period_ratio = new_period / old_period; |
174 | return he->diff.period_ratio; | 171 | return he->diff.period_ratio; |
175 | } | 172 | } |
176 | 173 | ||
177 | s64 perf_diff__compute_wdiff(struct hist_entry *he) | 174 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair) |
178 | { | 175 | { |
179 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
180 | u64 new_period = he->stat.period; | 176 | u64 new_period = he->stat.period; |
181 | u64 old_period = pair ? pair->stat.period : 0; | 177 | u64 old_period = pair->stat.period; |
182 | 178 | ||
183 | he->diff.computed = true; | 179 | he->diff.computed = true; |
184 | 180 | he->diff.wdiff = new_period * compute_wdiff_w2 - | |
185 | if (!pair) | 181 | old_period * compute_wdiff_w1; |
186 | he->diff.wdiff = 0; | ||
187 | else | ||
188 | he->diff.wdiff = new_period * compute_wdiff_w2 - | ||
189 | old_period * compute_wdiff_w1; | ||
190 | 182 | ||
191 | return he->diff.wdiff; | 183 | return he->diff.wdiff; |
192 | } | 184 | } |
193 | 185 | ||
194 | static int formula_delta(struct hist_entry *he, char *buf, size_t size) | 186 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, |
187 | char *buf, size_t size) | ||
195 | { | 188 | { |
196 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
197 | |||
198 | if (!pair) | ||
199 | return -1; | ||
200 | |||
201 | return scnprintf(buf, size, | 189 | return scnprintf(buf, size, |
202 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | 190 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " |
203 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | 191 | "(%" PRIu64 " * 100 / %" PRIu64 ")", |
@@ -205,41 +193,36 @@ static int formula_delta(struct hist_entry *he, char *buf, size_t size) | |||
205 | pair->stat.period, pair->hists->stats.total_period); | 193 | pair->stat.period, pair->hists->stats.total_period); |
206 | } | 194 | } |
207 | 195 | ||
208 | static int formula_ratio(struct hist_entry *he, char *buf, size_t size) | 196 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, |
197 | char *buf, size_t size) | ||
209 | { | 198 | { |
210 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
211 | double new_period = he->stat.period; | 199 | double new_period = he->stat.period; |
212 | double old_period = pair ? pair->stat.period : 0; | 200 | double old_period = pair->stat.period; |
213 | |||
214 | if (!pair) | ||
215 | return -1; | ||
216 | 201 | ||
217 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); | 202 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); |
218 | } | 203 | } |
219 | 204 | ||
220 | static int formula_wdiff(struct hist_entry *he, char *buf, size_t size) | 205 | static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, |
206 | char *buf, size_t size) | ||
221 | { | 207 | { |
222 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
223 | u64 new_period = he->stat.period; | 208 | u64 new_period = he->stat.period; |
224 | u64 old_period = pair ? pair->stat.period : 0; | 209 | u64 old_period = pair->stat.period; |
225 | |||
226 | if (!pair) | ||
227 | return -1; | ||
228 | 210 | ||
229 | return scnprintf(buf, size, | 211 | return scnprintf(buf, size, |
230 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", | 212 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", |
231 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); | 213 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); |
232 | } | 214 | } |
233 | 215 | ||
234 | int perf_diff__formula(char *buf, size_t size, struct hist_entry *he) | 216 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, |
217 | char *buf, size_t size) | ||
235 | { | 218 | { |
236 | switch (compute) { | 219 | switch (compute) { |
237 | case COMPUTE_DELTA: | 220 | case COMPUTE_DELTA: |
238 | return formula_delta(he, buf, size); | 221 | return formula_delta(he, pair, buf, size); |
239 | case COMPUTE_RATIO: | 222 | case COMPUTE_RATIO: |
240 | return formula_ratio(he, buf, size); | 223 | return formula_ratio(he, pair, buf, size); |
241 | case COMPUTE_WEIGHTED_DIFF: | 224 | case COMPUTE_WEIGHTED_DIFF: |
242 | return formula_wdiff(he, buf, size); | 225 | return formula_wdiff(he, pair, buf, size); |
243 | default: | 226 | default: |
244 | BUG_ON(1); | 227 | BUG_ON(1); |
245 | } | 228 | } |
@@ -248,9 +231,10 @@ int perf_diff__formula(char *buf, size_t size, struct hist_entry *he) | |||
248 | } | 231 | } |
249 | 232 | ||
250 | static int hists__add_entry(struct hists *self, | 233 | static int hists__add_entry(struct hists *self, |
251 | struct addr_location *al, u64 period) | 234 | struct addr_location *al, u64 period, |
235 | u64 weight) | ||
252 | { | 236 | { |
253 | if (__hists__add_entry(self, al, NULL, period) != NULL) | 237 | if (__hists__add_entry(self, al, NULL, period, weight) != NULL) |
254 | return 0; | 238 | return 0; |
255 | return -ENOMEM; | 239 | return -ENOMEM; |
256 | } | 240 | } |
@@ -272,7 +256,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
272 | if (al.filtered) | 256 | if (al.filtered) |
273 | return 0; | 257 | return 0; |
274 | 258 | ||
275 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 259 | if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) { |
276 | pr_warning("problem incrementing symbol period, skipping event\n"); | 260 | pr_warning("problem incrementing symbol period, skipping event\n"); |
277 | return -1; | 261 | return -1; |
278 | } | 262 | } |
@@ -292,48 +276,6 @@ static struct perf_tool tool = { | |||
292 | .ordering_requires_timestamps = true, | 276 | .ordering_requires_timestamps = true, |
293 | }; | 277 | }; |
294 | 278 | ||
295 | static void insert_hist_entry_by_name(struct rb_root *root, | ||
296 | struct hist_entry *he) | ||
297 | { | ||
298 | struct rb_node **p = &root->rb_node; | ||
299 | struct rb_node *parent = NULL; | ||
300 | struct hist_entry *iter; | ||
301 | |||
302 | while (*p != NULL) { | ||
303 | parent = *p; | ||
304 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
305 | if (hist_entry__cmp(he, iter) < 0) | ||
306 | p = &(*p)->rb_left; | ||
307 | else | ||
308 | p = &(*p)->rb_right; | ||
309 | } | ||
310 | |||
311 | rb_link_node(&he->rb_node, parent, p); | ||
312 | rb_insert_color(&he->rb_node, root); | ||
313 | } | ||
314 | |||
315 | static void hists__name_resort(struct hists *self, bool sort) | ||
316 | { | ||
317 | unsigned long position = 1; | ||
318 | struct rb_root tmp = RB_ROOT; | ||
319 | struct rb_node *next = rb_first(&self->entries); | ||
320 | |||
321 | while (next != NULL) { | ||
322 | struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); | ||
323 | |||
324 | next = rb_next(&n->rb_node); | ||
325 | n->position = position++; | ||
326 | |||
327 | if (sort) { | ||
328 | rb_erase(&n->rb_node, &self->entries); | ||
329 | insert_hist_entry_by_name(&tmp, n); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | if (sort) | ||
334 | self->entries = tmp; | ||
335 | } | ||
336 | |||
337 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | 279 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, |
338 | struct perf_evlist *evlist) | 280 | struct perf_evlist *evlist) |
339 | { | 281 | { |
@@ -346,34 +288,34 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | |||
346 | return NULL; | 288 | return NULL; |
347 | } | 289 | } |
348 | 290 | ||
349 | static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) | 291 | static void perf_evlist__collapse_resort(struct perf_evlist *evlist) |
350 | { | 292 | { |
351 | struct perf_evsel *evsel; | 293 | struct perf_evsel *evsel; |
352 | 294 | ||
353 | list_for_each_entry(evsel, &evlist->entries, node) { | 295 | list_for_each_entry(evsel, &evlist->entries, node) { |
354 | struct hists *hists = &evsel->hists; | 296 | struct hists *hists = &evsel->hists; |
355 | 297 | ||
356 | hists__output_resort(hists); | 298 | hists__collapse_resort(hists); |
357 | |||
358 | /* | ||
359 | * The hists__name_resort only sets possition | ||
360 | * if name is false. | ||
361 | */ | ||
362 | if (name || ((!name) && show_displacement)) | ||
363 | hists__name_resort(hists, name); | ||
364 | } | 299 | } |
365 | } | 300 | } |
366 | 301 | ||
367 | static void hists__baseline_only(struct hists *hists) | 302 | static void hists__baseline_only(struct hists *hists) |
368 | { | 303 | { |
369 | struct rb_node *next = rb_first(&hists->entries); | 304 | struct rb_root *root; |
305 | struct rb_node *next; | ||
306 | |||
307 | if (sort__need_collapse) | ||
308 | root = &hists->entries_collapsed; | ||
309 | else | ||
310 | root = hists->entries_in; | ||
370 | 311 | ||
312 | next = rb_first(root); | ||
371 | while (next != NULL) { | 313 | while (next != NULL) { |
372 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 314 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); |
373 | 315 | ||
374 | next = rb_next(&he->rb_node); | 316 | next = rb_next(&he->rb_node_in); |
375 | if (!hist_entry__next_pair(he)) { | 317 | if (!hist_entry__next_pair(he)) { |
376 | rb_erase(&he->rb_node, &hists->entries); | 318 | rb_erase(&he->rb_node_in, root); |
377 | hist_entry__free(he); | 319 | hist_entry__free(he); |
378 | } | 320 | } |
379 | } | 321 | } |
@@ -381,22 +323,32 @@ static void hists__baseline_only(struct hists *hists) | |||
381 | 323 | ||
382 | static void hists__precompute(struct hists *hists) | 324 | static void hists__precompute(struct hists *hists) |
383 | { | 325 | { |
384 | struct rb_node *next = rb_first(&hists->entries); | 326 | struct rb_root *root; |
327 | struct rb_node *next; | ||
328 | |||
329 | if (sort__need_collapse) | ||
330 | root = &hists->entries_collapsed; | ||
331 | else | ||
332 | root = hists->entries_in; | ||
385 | 333 | ||
334 | next = rb_first(root); | ||
386 | while (next != NULL) { | 335 | while (next != NULL) { |
387 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 336 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); |
337 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
388 | 338 | ||
389 | next = rb_next(&he->rb_node); | 339 | next = rb_next(&he->rb_node_in); |
340 | if (!pair) | ||
341 | continue; | ||
390 | 342 | ||
391 | switch (compute) { | 343 | switch (compute) { |
392 | case COMPUTE_DELTA: | 344 | case COMPUTE_DELTA: |
393 | perf_diff__compute_delta(he); | 345 | perf_diff__compute_delta(he, pair); |
394 | break; | 346 | break; |
395 | case COMPUTE_RATIO: | 347 | case COMPUTE_RATIO: |
396 | perf_diff__compute_ratio(he); | 348 | perf_diff__compute_ratio(he, pair); |
397 | break; | 349 | break; |
398 | case COMPUTE_WEIGHTED_DIFF: | 350 | case COMPUTE_WEIGHTED_DIFF: |
399 | perf_diff__compute_wdiff(he); | 351 | perf_diff__compute_wdiff(he, pair); |
400 | break; | 352 | break; |
401 | default: | 353 | default: |
402 | BUG_ON(1); | 354 | BUG_ON(1); |
@@ -470,19 +422,30 @@ static void insert_hist_entry_by_compute(struct rb_root *root, | |||
470 | 422 | ||
471 | static void hists__compute_resort(struct hists *hists) | 423 | static void hists__compute_resort(struct hists *hists) |
472 | { | 424 | { |
473 | struct rb_root tmp = RB_ROOT; | 425 | struct rb_root *root; |
474 | struct rb_node *next = rb_first(&hists->entries); | 426 | struct rb_node *next; |
427 | |||
428 | if (sort__need_collapse) | ||
429 | root = &hists->entries_collapsed; | ||
430 | else | ||
431 | root = hists->entries_in; | ||
432 | |||
433 | hists->entries = RB_ROOT; | ||
434 | next = rb_first(root); | ||
435 | |||
436 | hists->nr_entries = 0; | ||
437 | hists->stats.total_period = 0; | ||
438 | hists__reset_col_len(hists); | ||
475 | 439 | ||
476 | while (next != NULL) { | 440 | while (next != NULL) { |
477 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | 441 | struct hist_entry *he; |
478 | 442 | ||
479 | next = rb_next(&he->rb_node); | 443 | he = rb_entry(next, struct hist_entry, rb_node_in); |
444 | next = rb_next(&he->rb_node_in); | ||
480 | 445 | ||
481 | rb_erase(&he->rb_node, &hists->entries); | 446 | insert_hist_entry_by_compute(&hists->entries, he, compute); |
482 | insert_hist_entry_by_compute(&tmp, he, compute); | 447 | hists__inc_nr_entries(hists, he); |
483 | } | 448 | } |
484 | |||
485 | hists->entries = tmp; | ||
486 | } | 449 | } |
487 | 450 | ||
488 | static void hists__process(struct hists *old, struct hists *new) | 451 | static void hists__process(struct hists *old, struct hists *new) |
@@ -497,9 +460,11 @@ static void hists__process(struct hists *old, struct hists *new) | |||
497 | if (sort_compute) { | 460 | if (sort_compute) { |
498 | hists__precompute(new); | 461 | hists__precompute(new); |
499 | hists__compute_resort(new); | 462 | hists__compute_resort(new); |
463 | } else { | ||
464 | hists__output_resort(new); | ||
500 | } | 465 | } |
501 | 466 | ||
502 | hists__fprintf(new, true, 0, 0, stdout); | 467 | hists__fprintf(new, true, 0, 0, 0, stdout); |
503 | } | 468 | } |
504 | 469 | ||
505 | static int __cmd_diff(void) | 470 | static int __cmd_diff(void) |
@@ -528,8 +493,8 @@ static int __cmd_diff(void) | |||
528 | evlist_old = older->evlist; | 493 | evlist_old = older->evlist; |
529 | evlist_new = newer->evlist; | 494 | evlist_new = newer->evlist; |
530 | 495 | ||
531 | perf_evlist__resort_hists(evlist_old, true); | 496 | perf_evlist__collapse_resort(evlist_old); |
532 | perf_evlist__resort_hists(evlist_new, false); | 497 | perf_evlist__collapse_resort(evlist_new); |
533 | 498 | ||
534 | list_for_each_entry(evsel, &evlist_new->entries, node) { | 499 | list_for_each_entry(evsel, &evlist_new->entries, node) { |
535 | struct perf_evsel *evsel_old; | 500 | struct perf_evsel *evsel_old; |
@@ -562,8 +527,6 @@ static const char * const diff_usage[] = { | |||
562 | static const struct option options[] = { | 527 | static const struct option options[] = { |
563 | OPT_INCR('v', "verbose", &verbose, | 528 | OPT_INCR('v', "verbose", &verbose, |
564 | "be more verbose (show symbol address, etc)"), | 529 | "be more verbose (show symbol address, etc)"), |
565 | OPT_BOOLEAN('M', "displacement", &show_displacement, | ||
566 | "Show position displacement relative to baseline"), | ||
567 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, | 530 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, |
568 | "Show only items with match in baseline"), | 531 | "Show only items with match in baseline"), |
569 | OPT_CALLBACK('c', "compute", &compute, | 532 | OPT_CALLBACK('c', "compute", &compute, |
@@ -597,40 +560,32 @@ static const struct option options[] = { | |||
597 | 560 | ||
598 | static void ui_init(void) | 561 | static void ui_init(void) |
599 | { | 562 | { |
600 | perf_hpp__init(); | ||
601 | |||
602 | /* No overhead column. */ | ||
603 | perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); | ||
604 | |||
605 | /* | 563 | /* |
606 | * Display baseline/delta/ratio/displacement/ | 564 | * Display baseline/delta/ratio |
607 | * formula/periods columns. | 565 | * formula/periods columns. |
608 | */ | 566 | */ |
609 | perf_hpp__column_enable(PERF_HPP__BASELINE, true); | 567 | perf_hpp__column_enable(PERF_HPP__BASELINE); |
610 | 568 | ||
611 | switch (compute) { | 569 | switch (compute) { |
612 | case COMPUTE_DELTA: | 570 | case COMPUTE_DELTA: |
613 | perf_hpp__column_enable(PERF_HPP__DELTA, true); | 571 | perf_hpp__column_enable(PERF_HPP__DELTA); |
614 | break; | 572 | break; |
615 | case COMPUTE_RATIO: | 573 | case COMPUTE_RATIO: |
616 | perf_hpp__column_enable(PERF_HPP__RATIO, true); | 574 | perf_hpp__column_enable(PERF_HPP__RATIO); |
617 | break; | 575 | break; |
618 | case COMPUTE_WEIGHTED_DIFF: | 576 | case COMPUTE_WEIGHTED_DIFF: |
619 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true); | 577 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); |
620 | break; | 578 | break; |
621 | default: | 579 | default: |
622 | BUG_ON(1); | 580 | BUG_ON(1); |
623 | }; | 581 | }; |
624 | 582 | ||
625 | if (show_displacement) | ||
626 | perf_hpp__column_enable(PERF_HPP__DISPL, true); | ||
627 | |||
628 | if (show_formula) | 583 | if (show_formula) |
629 | perf_hpp__column_enable(PERF_HPP__FORMULA, true); | 584 | perf_hpp__column_enable(PERF_HPP__FORMULA); |
630 | 585 | ||
631 | if (show_period) { | 586 | if (show_period) { |
632 | perf_hpp__column_enable(PERF_HPP__PERIOD, true); | 587 | perf_hpp__column_enable(PERF_HPP__PERIOD); |
633 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true); | 588 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); |
634 | } | 589 | } |
635 | } | 590 | } |
636 | 591 | ||
@@ -658,12 +613,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
658 | 613 | ||
659 | ui_init(); | 614 | ui_init(); |
660 | 615 | ||
661 | setup_sorting(diff_usage, options); | 616 | if (setup_sorting() < 0) |
617 | usage_with_options(diff_usage, options); | ||
618 | |||
662 | setup_pager(); | 619 | setup_pager(); |
663 | 620 | ||
664 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); | 621 | sort__setup_elide(NULL); |
665 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL); | ||
666 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL); | ||
667 | 622 | ||
668 | return __cmd_diff(); | 623 | return __cmd_diff(); |
669 | } | 624 | } |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index c20f1dcfb7e2..05bd9dfe875c 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
@@ -15,39 +15,6 @@ | |||
15 | #include "util/parse-options.h" | 15 | #include "util/parse-options.h" |
16 | #include "util/session.h" | 16 | #include "util/session.h" |
17 | 17 | ||
18 | struct perf_attr_details { | ||
19 | bool freq; | ||
20 | bool verbose; | ||
21 | }; | ||
22 | |||
23 | static int comma_printf(bool *first, const char *fmt, ...) | ||
24 | { | ||
25 | va_list args; | ||
26 | int ret = 0; | ||
27 | |||
28 | if (!*first) { | ||
29 | ret += printf(","); | ||
30 | } else { | ||
31 | ret += printf(":"); | ||
32 | *first = false; | ||
33 | } | ||
34 | |||
35 | va_start(args, fmt); | ||
36 | ret += vprintf(fmt, args); | ||
37 | va_end(args); | ||
38 | return ret; | ||
39 | } | ||
40 | |||
41 | static int __if_print(bool *first, const char *field, u64 value) | ||
42 | { | ||
43 | if (value == 0) | ||
44 | return 0; | ||
45 | |||
46 | return comma_printf(first, " %s: %" PRIu64, field, value); | ||
47 | } | ||
48 | |||
49 | #define if_print(field) __if_print(&first, #field, pos->attr.field) | ||
50 | |||
51 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) | 18 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) |
52 | { | 19 | { |
53 | struct perf_session *session; | 20 | struct perf_session *session; |
@@ -57,52 +24,8 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details | |||
57 | if (session == NULL) | 24 | if (session == NULL) |
58 | return -ENOMEM; | 25 | return -ENOMEM; |
59 | 26 | ||
60 | list_for_each_entry(pos, &session->evlist->entries, node) { | 27 | list_for_each_entry(pos, &session->evlist->entries, node) |
61 | bool first = true; | 28 | perf_evsel__fprintf(pos, details, stdout); |
62 | |||
63 | printf("%s", perf_evsel__name(pos)); | ||
64 | |||
65 | if (details->verbose || details->freq) { | ||
66 | comma_printf(&first, " sample_freq=%" PRIu64, | ||
67 | (u64)pos->attr.sample_freq); | ||
68 | } | ||
69 | |||
70 | if (details->verbose) { | ||
71 | if_print(type); | ||
72 | if_print(config); | ||
73 | if_print(config1); | ||
74 | if_print(config2); | ||
75 | if_print(size); | ||
76 | if_print(sample_type); | ||
77 | if_print(read_format); | ||
78 | if_print(disabled); | ||
79 | if_print(inherit); | ||
80 | if_print(pinned); | ||
81 | if_print(exclusive); | ||
82 | if_print(exclude_user); | ||
83 | if_print(exclude_kernel); | ||
84 | if_print(exclude_hv); | ||
85 | if_print(exclude_idle); | ||
86 | if_print(mmap); | ||
87 | if_print(comm); | ||
88 | if_print(freq); | ||
89 | if_print(inherit_stat); | ||
90 | if_print(enable_on_exec); | ||
91 | if_print(task); | ||
92 | if_print(watermark); | ||
93 | if_print(precise_ip); | ||
94 | if_print(mmap_data); | ||
95 | if_print(sample_id_all); | ||
96 | if_print(exclude_host); | ||
97 | if_print(exclude_guest); | ||
98 | if_print(__reserved_1); | ||
99 | if_print(wakeup_events); | ||
100 | if_print(bp_type); | ||
101 | if_print(branch_sample_type); | ||
102 | } | ||
103 | |||
104 | putchar('\n'); | ||
105 | } | ||
106 | 29 | ||
107 | perf_session__delete(session); | 30 | perf_session__delete(session); |
108 | return 0; | 31 | return 0; |
@@ -116,6 +39,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) | |||
116 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), | 39 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), |
117 | OPT_BOOLEAN('v', "verbose", &details.verbose, | 40 | OPT_BOOLEAN('v', "verbose", &details.verbose, |
118 | "Show all event attr details"), | 41 | "Show all event attr details"), |
42 | OPT_BOOLEAN('g', "group", &details.event_group, | ||
43 | "Show event group information"), | ||
119 | OPT_END() | 44 | OPT_END() |
120 | }; | 45 | }; |
121 | const char * const evlist_usage[] = { | 46 | const char * const evlist_usage[] = { |
@@ -127,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) | |||
127 | if (argc) | 52 | if (argc) |
128 | usage_with_options(evlist_usage, options); | 53 | usage_with_options(evlist_usage, options); |
129 | 54 | ||
55 | if (details.event_group && (details.verbose || details.freq)) { | ||
56 | pr_err("--group option is not compatible with other options\n"); | ||
57 | usage_with_options(evlist_usage, options); | ||
58 | } | ||
59 | |||
130 | return __cmd_evlist(input_name, &details); | 60 | return __cmd_evlist(input_name, &details); |
131 | } | 61 | } |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 0b4b796167be..46878daca5cc 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/debug.h" | 17 | #include "util/debug.h" |
18 | 18 | ||
19 | #include <linux/rbtree.h> | 19 | #include <linux/rbtree.h> |
20 | #include <linux/string.h> | ||
20 | 21 | ||
21 | struct alloc_stat; | 22 | struct alloc_stat; |
22 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | 23 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); |
@@ -340,7 +341,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
340 | int n_lines, int is_caller) | 341 | int n_lines, int is_caller) |
341 | { | 342 | { |
342 | struct rb_node *next; | 343 | struct rb_node *next; |
343 | struct machine *machine; | 344 | struct machine *machine = &session->machines.host; |
344 | 345 | ||
345 | printf("%.102s\n", graph_dotted_line); | 346 | printf("%.102s\n", graph_dotted_line); |
346 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); | 347 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); |
@@ -349,11 +350,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
349 | 350 | ||
350 | next = rb_first(root); | 351 | next = rb_first(root); |
351 | 352 | ||
352 | machine = perf_session__find_host_machine(session); | ||
353 | if (!machine) { | ||
354 | pr_err("__print_result: couldn't find kernel information\n"); | ||
355 | return; | ||
356 | } | ||
357 | while (next && n_lines--) { | 353 | while (next && n_lines--) { |
358 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, | 354 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, |
359 | node); | 355 | node); |
@@ -614,8 +610,7 @@ static struct sort_dimension *avail_sorts[] = { | |||
614 | &pingpong_sort_dimension, | 610 | &pingpong_sort_dimension, |
615 | }; | 611 | }; |
616 | 612 | ||
617 | #define NUM_AVAIL_SORTS \ | 613 | #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts)) |
618 | (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *)) | ||
619 | 614 | ||
620 | static int sort_dimension__add(const char *tok, struct list_head *list) | 615 | static int sort_dimension__add(const char *tok, struct list_head *list) |
621 | { | 616 | { |
@@ -624,12 +619,11 @@ static int sort_dimension__add(const char *tok, struct list_head *list) | |||
624 | 619 | ||
625 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { | 620 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { |
626 | if (!strcmp(avail_sorts[i]->name, tok)) { | 621 | if (!strcmp(avail_sorts[i]->name, tok)) { |
627 | sort = malloc(sizeof(*sort)); | 622 | sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i])); |
628 | if (!sort) { | 623 | if (!sort) { |
629 | pr_err("%s: malloc failed\n", __func__); | 624 | pr_err("%s: memdup failed\n", __func__); |
630 | return -1; | 625 | return -1; |
631 | } | 626 | } |
632 | memcpy(sort, avail_sorts[i], sizeof(*sort)); | ||
633 | list_add_tail(&sort->list, list); | 627 | list_add_tail(&sort->list, list); |
634 | return 0; | 628 | return 0; |
635 | } | 629 | } |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index ca3f80ebc100..24b78aecc928 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include "util/parse-options.h" | 12 | #include "util/parse-options.h" |
13 | #include "util/trace-event.h" | 13 | #include "util/trace-event.h" |
14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
15 | #include "util/debugfs.h" | 15 | #include <lk/debugfs.h> |
16 | #include "util/tool.h" | 16 | #include "util/tool.h" |
17 | #include "util/stat.h" | 17 | #include "util/stat.h" |
18 | 18 | ||
@@ -328,6 +328,7 @@ static int kvm_events_hash_fn(u64 key) | |||
328 | static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) | 328 | static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) |
329 | { | 329 | { |
330 | int old_max_vcpu = event->max_vcpu; | 330 | int old_max_vcpu = event->max_vcpu; |
331 | void *prev; | ||
331 | 332 | ||
332 | if (vcpu_id < event->max_vcpu) | 333 | if (vcpu_id < event->max_vcpu) |
333 | return true; | 334 | return true; |
@@ -335,9 +336,11 @@ static bool kvm_event_expand(struct kvm_event *event, int vcpu_id) | |||
335 | while (event->max_vcpu <= vcpu_id) | 336 | while (event->max_vcpu <= vcpu_id) |
336 | event->max_vcpu += DEFAULT_VCPU_NUM; | 337 | event->max_vcpu += DEFAULT_VCPU_NUM; |
337 | 338 | ||
339 | prev = event->vcpu; | ||
338 | event->vcpu = realloc(event->vcpu, | 340 | event->vcpu = realloc(event->vcpu, |
339 | event->max_vcpu * sizeof(*event->vcpu)); | 341 | event->max_vcpu * sizeof(*event->vcpu)); |
340 | if (!event->vcpu) { | 342 | if (!event->vcpu) { |
343 | free(prev); | ||
341 | pr_err("Not enough memory\n"); | 344 | pr_err("Not enough memory\n"); |
342 | return false; | 345 | return false; |
343 | } | 346 | } |
@@ -973,8 +976,7 @@ __cmd_buildid_list(const char *file_name, int argc, const char **argv) | |||
973 | 976 | ||
974 | int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) | 977 | int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) |
975 | { | 978 | { |
976 | const char *file_name; | 979 | const char *file_name = NULL; |
977 | |||
978 | const struct option kvm_options[] = { | 980 | const struct option kvm_options[] = { |
979 | OPT_STRING('i', "input", &file_name, "file", | 981 | OPT_STRING('i', "input", &file_name, "file", |
980 | "Input file name"), | 982 | "Input file name"), |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c new file mode 100644 index 000000000000..a8ff6d264e50 --- /dev/null +++ b/tools/perf/builtin-mem.c | |||
@@ -0,0 +1,242 @@ | |||
1 | #include "builtin.h" | ||
2 | #include "perf.h" | ||
3 | |||
4 | #include "util/parse-options.h" | ||
5 | #include "util/trace-event.h" | ||
6 | #include "util/tool.h" | ||
7 | #include "util/session.h" | ||
8 | |||
9 | #define MEM_OPERATION_LOAD "load" | ||
10 | #define MEM_OPERATION_STORE "store" | ||
11 | |||
12 | static const char *mem_operation = MEM_OPERATION_LOAD; | ||
13 | |||
14 | struct perf_mem { | ||
15 | struct perf_tool tool; | ||
16 | char const *input_name; | ||
17 | symbol_filter_t annotate_init; | ||
18 | bool hide_unresolved; | ||
19 | bool dump_raw; | ||
20 | const char *cpu_list; | ||
21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
22 | }; | ||
23 | |||
24 | static const char * const mem_usage[] = { | ||
25 | "perf mem [<options>] {record <command> |report}", | ||
26 | NULL | ||
27 | }; | ||
28 | |||
29 | static int __cmd_record(int argc, const char **argv) | ||
30 | { | ||
31 | int rec_argc, i = 0, j; | ||
32 | const char **rec_argv; | ||
33 | char event[64]; | ||
34 | int ret; | ||
35 | |||
36 | rec_argc = argc + 4; | ||
37 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
38 | if (!rec_argv) | ||
39 | return -1; | ||
40 | |||
41 | rec_argv[i++] = strdup("record"); | ||
42 | if (!strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
43 | rec_argv[i++] = strdup("-W"); | ||
44 | rec_argv[i++] = strdup("-d"); | ||
45 | rec_argv[i++] = strdup("-e"); | ||
46 | |||
47 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
48 | sprintf(event, "cpu/mem-stores/pp"); | ||
49 | else | ||
50 | sprintf(event, "cpu/mem-loads/pp"); | ||
51 | |||
52 | rec_argv[i++] = strdup(event); | ||
53 | for (j = 1; j < argc; j++, i++) | ||
54 | rec_argv[i] = argv[j]; | ||
55 | |||
56 | ret = cmd_record(i, rec_argv, NULL); | ||
57 | free(rec_argv); | ||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | dump_raw_samples(struct perf_tool *tool, | ||
63 | union perf_event *event, | ||
64 | struct perf_sample *sample, | ||
65 | struct perf_evsel *evsel __maybe_unused, | ||
66 | struct machine *machine) | ||
67 | { | ||
68 | struct perf_mem *mem = container_of(tool, struct perf_mem, tool); | ||
69 | struct addr_location al; | ||
70 | const char *fmt; | ||
71 | |||
72 | if (perf_event__preprocess_sample(event, machine, &al, sample, | ||
73 | mem->annotate_init) < 0) { | ||
74 | fprintf(stderr, "problem processing %d event, skipping it.\n", | ||
75 | event->header.type); | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | if (al.filtered || (mem->hide_unresolved && al.sym == NULL)) | ||
80 | return 0; | ||
81 | |||
82 | if (al.map != NULL) | ||
83 | al.map->dso->hit = 1; | ||
84 | |||
85 | if (symbol_conf.field_sep) { | ||
86 | fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64 | ||
87 | "%s0x%"PRIx64"%s%s:%s\n"; | ||
88 | } else { | ||
89 | fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64 | ||
90 | "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n"; | ||
91 | symbol_conf.field_sep = " "; | ||
92 | } | ||
93 | |||
94 | printf(fmt, | ||
95 | sample->pid, | ||
96 | symbol_conf.field_sep, | ||
97 | sample->tid, | ||
98 | symbol_conf.field_sep, | ||
99 | event->ip.ip, | ||
100 | symbol_conf.field_sep, | ||
101 | sample->addr, | ||
102 | symbol_conf.field_sep, | ||
103 | sample->weight, | ||
104 | symbol_conf.field_sep, | ||
105 | sample->data_src, | ||
106 | symbol_conf.field_sep, | ||
107 | al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???", | ||
108 | al.sym ? al.sym->name : "???"); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int process_sample_event(struct perf_tool *tool, | ||
114 | union perf_event *event, | ||
115 | struct perf_sample *sample, | ||
116 | struct perf_evsel *evsel, | ||
117 | struct machine *machine) | ||
118 | { | ||
119 | return dump_raw_samples(tool, event, sample, evsel, machine); | ||
120 | } | ||
121 | |||
122 | static int report_raw_events(struct perf_mem *mem) | ||
123 | { | ||
124 | int err = -EINVAL; | ||
125 | int ret; | ||
126 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | ||
127 | 0, false, &mem->tool); | ||
128 | |||
129 | if (session == NULL) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | if (mem->cpu_list) { | ||
133 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, | ||
134 | mem->cpu_bitmap); | ||
135 | if (ret) | ||
136 | goto out_delete; | ||
137 | } | ||
138 | |||
139 | if (symbol__init() < 0) | ||
140 | return -1; | ||
141 | |||
142 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); | ||
143 | |||
144 | err = perf_session__process_events(session, &mem->tool); | ||
145 | if (err) | ||
146 | return err; | ||
147 | |||
148 | return 0; | ||
149 | |||
150 | out_delete: | ||
151 | perf_session__delete(session); | ||
152 | return err; | ||
153 | } | ||
154 | |||
155 | static int report_events(int argc, const char **argv, struct perf_mem *mem) | ||
156 | { | ||
157 | const char **rep_argv; | ||
158 | int ret, i = 0, j, rep_argc; | ||
159 | |||
160 | if (mem->dump_raw) | ||
161 | return report_raw_events(mem); | ||
162 | |||
163 | rep_argc = argc + 3; | ||
164 | rep_argv = calloc(rep_argc + 1, sizeof(char *)); | ||
165 | if (!rep_argv) | ||
166 | return -1; | ||
167 | |||
168 | rep_argv[i++] = strdup("report"); | ||
169 | rep_argv[i++] = strdup("--mem-mode"); | ||
170 | rep_argv[i++] = strdup("-n"); /* display number of samples */ | ||
171 | |||
172 | /* | ||
173 | * there is no weight (cost) associated with stores, so don't print | ||
174 | * the column | ||
175 | */ | ||
176 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
177 | rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," | ||
178 | "dso_daddr,tlb,locked"); | ||
179 | |||
180 | for (j = 1; j < argc; j++, i++) | ||
181 | rep_argv[i] = argv[j]; | ||
182 | |||
183 | ret = cmd_report(i, rep_argv, NULL); | ||
184 | free(rep_argv); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | ||
189 | { | ||
190 | struct stat st; | ||
191 | struct perf_mem mem = { | ||
192 | .tool = { | ||
193 | .sample = process_sample_event, | ||
194 | .mmap = perf_event__process_mmap, | ||
195 | .comm = perf_event__process_comm, | ||
196 | .lost = perf_event__process_lost, | ||
197 | .fork = perf_event__process_fork, | ||
198 | .build_id = perf_event__process_build_id, | ||
199 | .ordered_samples = true, | ||
200 | }, | ||
201 | .input_name = "perf.data", | ||
202 | }; | ||
203 | const struct option mem_options[] = { | ||
204 | OPT_STRING('t', "type", &mem_operation, | ||
205 | "type", "memory operations(load/store)"), | ||
206 | OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, | ||
207 | "dump raw samples in ASCII"), | ||
208 | OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, | ||
209 | "Only display entries resolved to a symbol"), | ||
210 | OPT_STRING('i', "input", &input_name, "file", | ||
211 | "input file name"), | ||
212 | OPT_STRING('C', "cpu", &mem.cpu_list, "cpu", | ||
213 | "list of cpus to profile"), | ||
214 | OPT_STRING('x', "field-separator", &symbol_conf.field_sep, | ||
215 | "separator", | ||
216 | "separator for columns, no spaces will be added" | ||
217 | " between columns '.' is reserved."), | ||
218 | OPT_END() | ||
219 | }; | ||
220 | |||
221 | argc = parse_options(argc, argv, mem_options, mem_usage, | ||
222 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
223 | |||
224 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) | ||
225 | usage_with_options(mem_usage, mem_options); | ||
226 | |||
227 | if (!mem.input_name || !strlen(mem.input_name)) { | ||
228 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
229 | mem.input_name = "-"; | ||
230 | else | ||
231 | mem.input_name = "perf.data"; | ||
232 | } | ||
233 | |||
234 | if (!strncmp(argv[0], "rec", 3)) | ||
235 | return __cmd_record(argc, argv); | ||
236 | else if (!strncmp(argv[0], "rep", 3)) | ||
237 | return report_events(argc, argv, &mem); | ||
238 | else | ||
239 | usage_with_options(mem_usage, mem_options); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index de38a034b129..e8a66f9a6715 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -37,7 +37,7 @@ | |||
37 | #include "util/strfilter.h" | 37 | #include "util/strfilter.h" |
38 | #include "util/symbol.h" | 38 | #include "util/symbol.h" |
39 | #include "util/debug.h" | 39 | #include "util/debug.h" |
40 | #include "util/debugfs.h" | 40 | #include <lk/debugfs.h> |
41 | #include "util/parse-options.h" | 41 | #include "util/parse-options.h" |
42 | #include "util/probe-finder.h" | 42 | #include "util/probe-finder.h" |
43 | #include "util/probe-event.h" | 43 | #include "util/probe-event.h" |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f3151d3c70ce..fff985cf3852 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -5,8 +5,6 @@ | |||
5 | * (or a CPU, or a PID) into the perf.data output file - for | 5 | * (or a CPU, or a PID) into the perf.data output file - for |
6 | * later analysis via perf report. | 6 | * later analysis via perf report. |
7 | */ | 7 | */ |
8 | #define _FILE_OFFSET_BITS 64 | ||
9 | |||
10 | #include "builtin.h" | 8 | #include "builtin.h" |
11 | 9 | ||
12 | #include "perf.h" | 10 | #include "perf.h" |
@@ -200,7 +198,6 @@ static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg) | |||
200 | return; | 198 | return; |
201 | 199 | ||
202 | signal(signr, SIG_DFL); | 200 | signal(signr, SIG_DFL); |
203 | kill(getpid(), signr); | ||
204 | } | 201 | } |
205 | 202 | ||
206 | static bool perf_evlist__equal(struct perf_evlist *evlist, | 203 | static bool perf_evlist__equal(struct perf_evlist *evlist, |
@@ -224,130 +221,28 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, | |||
224 | 221 | ||
225 | static int perf_record__open(struct perf_record *rec) | 222 | static int perf_record__open(struct perf_record *rec) |
226 | { | 223 | { |
224 | char msg[512]; | ||
227 | struct perf_evsel *pos; | 225 | struct perf_evsel *pos; |
228 | struct perf_evlist *evlist = rec->evlist; | 226 | struct perf_evlist *evlist = rec->evlist; |
229 | struct perf_session *session = rec->session; | 227 | struct perf_session *session = rec->session; |
230 | struct perf_record_opts *opts = &rec->opts; | 228 | struct perf_record_opts *opts = &rec->opts; |
231 | int rc = 0; | 229 | int rc = 0; |
232 | 230 | ||
233 | /* | 231 | perf_evlist__config(evlist, opts); |
234 | * Set the evsel leader links before we configure attributes, | ||
235 | * since some might depend on this info. | ||
236 | */ | ||
237 | if (opts->group) | ||
238 | perf_evlist__set_leader(evlist); | ||
239 | |||
240 | perf_evlist__config_attrs(evlist, opts); | ||
241 | 232 | ||
242 | list_for_each_entry(pos, &evlist->entries, node) { | 233 | list_for_each_entry(pos, &evlist->entries, node) { |
243 | struct perf_event_attr *attr = &pos->attr; | ||
244 | /* | ||
245 | * Check if parse_single_tracepoint_event has already asked for | ||
246 | * PERF_SAMPLE_TIME. | ||
247 | * | ||
248 | * XXX this is kludgy but short term fix for problems introduced by | ||
249 | * eac23d1c that broke 'perf script' by having different sample_types | ||
250 | * when using multiple tracepoint events when we use a perf binary | ||
251 | * that tries to use sample_id_all on an older kernel. | ||
252 | * | ||
253 | * We need to move counter creation to perf_session, support | ||
254 | * different sample_types, etc. | ||
255 | */ | ||
256 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
257 | |||
258 | fallback_missing_features: | ||
259 | if (opts->exclude_guest_missing) | ||
260 | attr->exclude_guest = attr->exclude_host = 0; | ||
261 | retry_sample_id: | ||
262 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | ||
263 | try_again: | 234 | try_again: |
264 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { | 235 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { |
265 | int err = errno; | 236 | if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) { |
266 | |||
267 | if (err == EPERM || err == EACCES) { | ||
268 | ui__error_paranoid(); | ||
269 | rc = -err; | ||
270 | goto out; | ||
271 | } else if (err == ENODEV && opts->target.cpu_list) { | ||
272 | pr_err("No such device - did you specify" | ||
273 | " an out-of-range profile CPU?\n"); | ||
274 | rc = -err; | ||
275 | goto out; | ||
276 | } else if (err == EINVAL) { | ||
277 | if (!opts->exclude_guest_missing && | ||
278 | (attr->exclude_guest || attr->exclude_host)) { | ||
279 | pr_debug("Old kernel, cannot exclude " | ||
280 | "guest or host samples.\n"); | ||
281 | opts->exclude_guest_missing = true; | ||
282 | goto fallback_missing_features; | ||
283 | } else if (!opts->sample_id_all_missing) { | ||
284 | /* | ||
285 | * Old kernel, no attr->sample_id_type_all field | ||
286 | */ | ||
287 | opts->sample_id_all_missing = true; | ||
288 | if (!opts->sample_time && !opts->raw_samples && !time_needed) | ||
289 | attr->sample_type &= ~PERF_SAMPLE_TIME; | ||
290 | |||
291 | goto retry_sample_id; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * If it's cycles then fall back to hrtimer | ||
297 | * based cpu-clock-tick sw counter, which | ||
298 | * is always available even if no PMU support. | ||
299 | * | ||
300 | * PPC returns ENXIO until 2.6.37 (behavior changed | ||
301 | * with commit b0a873e). | ||
302 | */ | ||
303 | if ((err == ENOENT || err == ENXIO) | ||
304 | && attr->type == PERF_TYPE_HARDWARE | ||
305 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | ||
306 | |||
307 | if (verbose) | 237 | if (verbose) |
308 | ui__warning("The cycles event is not supported, " | 238 | ui__warning("%s\n", msg); |
309 | "trying to fall back to cpu-clock-ticks\n"); | ||
310 | attr->type = PERF_TYPE_SOFTWARE; | ||
311 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | ||
312 | if (pos->name) { | ||
313 | free(pos->name); | ||
314 | pos->name = NULL; | ||
315 | } | ||
316 | goto try_again; | 239 | goto try_again; |
317 | } | 240 | } |
318 | 241 | ||
319 | if (err == ENOENT) { | 242 | rc = -errno; |
320 | ui__error("The %s event is not supported.\n", | 243 | perf_evsel__open_strerror(pos, &opts->target, |
321 | perf_evsel__name(pos)); | 244 | errno, msg, sizeof(msg)); |
322 | rc = -err; | 245 | ui__error("%s\n", msg); |
323 | goto out; | ||
324 | } else if ((err == EOPNOTSUPP) && (attr->precise_ip)) { | ||
325 | ui__error("\'precise\' request may not be supported. " | ||
326 | "Try removing 'p' modifier\n"); | ||
327 | rc = -err; | ||
328 | goto out; | ||
329 | } | ||
330 | |||
331 | printf("\n"); | ||
332 | error("sys_perf_event_open() syscall returned with %d " | ||
333 | "(%s) for event %s. /bin/dmesg may provide " | ||
334 | "additional information.\n", | ||
335 | err, strerror(err), perf_evsel__name(pos)); | ||
336 | |||
337 | #if defined(__i386__) || defined(__x86_64__) | ||
338 | if (attr->type == PERF_TYPE_HARDWARE && | ||
339 | err == EOPNOTSUPP) { | ||
340 | pr_err("No hardware sampling interrupt available." | ||
341 | " No APIC? If so then you can boot the kernel" | ||
342 | " with the \"lapic\" boot parameter to" | ||
343 | " force-enable it.\n"); | ||
344 | rc = -err; | ||
345 | goto out; | ||
346 | } | ||
347 | #endif | ||
348 | |||
349 | pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
350 | rc = -err; | ||
351 | goto out; | 246 | goto out; |
352 | } | 247 | } |
353 | } | 248 | } |
@@ -430,10 +325,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data) | |||
430 | { | 325 | { |
431 | int err; | 326 | int err; |
432 | struct perf_tool *tool = data; | 327 | struct perf_tool *tool = data; |
433 | |||
434 | if (machine__is_host(machine)) | ||
435 | return; | ||
436 | |||
437 | /* | 328 | /* |
438 | *As for guest kernel when processing subcommand record&report, | 329 | *As for guest kernel when processing subcommand record&report, |
439 | *we arrange module mmap prior to guest kernel mmap and trigger | 330 | *we arrange module mmap prior to guest kernel mmap and trigger |
@@ -512,6 +403,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
512 | signal(SIGCHLD, sig_handler); | 403 | signal(SIGCHLD, sig_handler); |
513 | signal(SIGINT, sig_handler); | 404 | signal(SIGINT, sig_handler); |
514 | signal(SIGUSR1, sig_handler); | 405 | signal(SIGUSR1, sig_handler); |
406 | signal(SIGTERM, sig_handler); | ||
515 | 407 | ||
516 | if (!output_name) { | 408 | if (!output_name) { |
517 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | 409 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
@@ -580,7 +472,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
580 | } | 472 | } |
581 | 473 | ||
582 | if (forks) { | 474 | if (forks) { |
583 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); | 475 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, |
476 | argv, opts->pipe_output, | ||
477 | true); | ||
584 | if (err < 0) { | 478 | if (err < 0) { |
585 | pr_err("Couldn't run the workload!\n"); | 479 | pr_err("Couldn't run the workload!\n"); |
586 | goto out_delete_session; | 480 | goto out_delete_session; |
@@ -592,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
592 | goto out_delete_session; | 486 | goto out_delete_session; |
593 | } | 487 | } |
594 | 488 | ||
489 | if (!evsel_list->nr_groups) | ||
490 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | ||
491 | |||
595 | /* | 492 | /* |
596 | * perf_session__delete(session) will be called at perf_record__exit() | 493 | * perf_session__delete(session) will be called at perf_record__exit() |
597 | */ | 494 | */ |
@@ -618,12 +515,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
618 | 515 | ||
619 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); | 516 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); |
620 | 517 | ||
621 | machine = perf_session__find_host_machine(session); | 518 | machine = &session->machines.host; |
622 | if (!machine) { | ||
623 | pr_err("Couldn't find native kernel information.\n"); | ||
624 | err = -1; | ||
625 | goto out_delete_session; | ||
626 | } | ||
627 | 519 | ||
628 | if (opts->pipe_output) { | 520 | if (opts->pipe_output) { |
629 | err = perf_event__synthesize_attrs(tool, session, | 521 | err = perf_event__synthesize_attrs(tool, session, |
@@ -676,17 +568,20 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
676 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 568 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
677 | "Check /proc/modules permission or run as root.\n"); | 569 | "Check /proc/modules permission or run as root.\n"); |
678 | 570 | ||
679 | if (perf_guest) | 571 | if (perf_guest) { |
680 | perf_session__process_machines(session, tool, | 572 | machines__process_guests(&session->machines, |
681 | perf_event__synthesize_guest_os); | 573 | perf_event__synthesize_guest_os, tool); |
574 | } | ||
682 | 575 | ||
683 | if (!opts->target.system_wide) | 576 | if (perf_target__has_task(&opts->target)) |
684 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, | 577 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, |
685 | process_synthesized_event, | 578 | process_synthesized_event, |
686 | machine); | 579 | machine); |
687 | else | 580 | else if (perf_target__has_cpu(&opts->target)) |
688 | err = perf_event__synthesize_threads(tool, process_synthesized_event, | 581 | err = perf_event__synthesize_threads(tool, process_synthesized_event, |
689 | machine); | 582 | machine); |
583 | else /* command specified */ | ||
584 | err = 0; | ||
690 | 585 | ||
691 | if (err != 0) | 586 | if (err != 0) |
692 | goto out_delete_session; | 587 | goto out_delete_session; |
@@ -875,11 +770,10 @@ static int get_stack_size(char *str, unsigned long *_size) | |||
875 | } | 770 | } |
876 | #endif /* LIBUNWIND_SUPPORT */ | 771 | #endif /* LIBUNWIND_SUPPORT */ |
877 | 772 | ||
878 | static int | 773 | int record_parse_callchain_opt(const struct option *opt, |
879 | parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | 774 | const char *arg, int unset) |
880 | int unset) | ||
881 | { | 775 | { |
882 | struct perf_record *rec = (struct perf_record *)opt->value; | 776 | struct perf_record_opts *opts = opt->value; |
883 | char *tok, *name, *saveptr = NULL; | 777 | char *tok, *name, *saveptr = NULL; |
884 | char *buf; | 778 | char *buf; |
885 | int ret = -1; | 779 | int ret = -1; |
@@ -905,7 +799,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
905 | /* Framepointer style */ | 799 | /* Framepointer style */ |
906 | if (!strncmp(name, "fp", sizeof("fp"))) { | 800 | if (!strncmp(name, "fp", sizeof("fp"))) { |
907 | if (!strtok_r(NULL, ",", &saveptr)) { | 801 | if (!strtok_r(NULL, ",", &saveptr)) { |
908 | rec->opts.call_graph = CALLCHAIN_FP; | 802 | opts->call_graph = CALLCHAIN_FP; |
909 | ret = 0; | 803 | ret = 0; |
910 | } else | 804 | } else |
911 | pr_err("callchain: No more arguments " | 805 | pr_err("callchain: No more arguments " |
@@ -918,20 +812,20 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
918 | const unsigned long default_stack_dump_size = 8192; | 812 | const unsigned long default_stack_dump_size = 8192; |
919 | 813 | ||
920 | ret = 0; | 814 | ret = 0; |
921 | rec->opts.call_graph = CALLCHAIN_DWARF; | 815 | opts->call_graph = CALLCHAIN_DWARF; |
922 | rec->opts.stack_dump_size = default_stack_dump_size; | 816 | opts->stack_dump_size = default_stack_dump_size; |
923 | 817 | ||
924 | tok = strtok_r(NULL, ",", &saveptr); | 818 | tok = strtok_r(NULL, ",", &saveptr); |
925 | if (tok) { | 819 | if (tok) { |
926 | unsigned long size = 0; | 820 | unsigned long size = 0; |
927 | 821 | ||
928 | ret = get_stack_size(tok, &size); | 822 | ret = get_stack_size(tok, &size); |
929 | rec->opts.stack_dump_size = size; | 823 | opts->stack_dump_size = size; |
930 | } | 824 | } |
931 | 825 | ||
932 | if (!ret) | 826 | if (!ret) |
933 | pr_debug("callchain: stack dump size %d\n", | 827 | pr_debug("callchain: stack dump size %d\n", |
934 | rec->opts.stack_dump_size); | 828 | opts->stack_dump_size); |
935 | #endif /* LIBUNWIND_SUPPORT */ | 829 | #endif /* LIBUNWIND_SUPPORT */ |
936 | } else { | 830 | } else { |
937 | pr_err("callchain: Unknown -g option " | 831 | pr_err("callchain: Unknown -g option " |
@@ -944,7 +838,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
944 | free(buf); | 838 | free(buf); |
945 | 839 | ||
946 | if (!ret) | 840 | if (!ret) |
947 | pr_debug("callchain: type %d\n", rec->opts.call_graph); | 841 | pr_debug("callchain: type %d\n", opts->call_graph); |
948 | 842 | ||
949 | return ret; | 843 | return ret; |
950 | } | 844 | } |
@@ -982,9 +876,9 @@ static struct perf_record record = { | |||
982 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " | 876 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " |
983 | 877 | ||
984 | #ifdef LIBUNWIND_SUPPORT | 878 | #ifdef LIBUNWIND_SUPPORT |
985 | static const char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | 879 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; |
986 | #else | 880 | #else |
987 | static const char callchain_help[] = CALLCHAIN_HELP "[fp]"; | 881 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; |
988 | #endif | 882 | #endif |
989 | 883 | ||
990 | /* | 884 | /* |
@@ -1028,9 +922,9 @@ const struct option record_options[] = { | |||
1028 | "number of mmap data pages"), | 922 | "number of mmap data pages"), |
1029 | OPT_BOOLEAN(0, "group", &record.opts.group, | 923 | OPT_BOOLEAN(0, "group", &record.opts.group, |
1030 | "put the counters into a counter group"), | 924 | "put the counters into a counter group"), |
1031 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]", | 925 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, |
1032 | callchain_help, &parse_callchain_opt, | 926 | "mode[,dump_size]", record_callchain_help, |
1033 | "fp"), | 927 | &record_parse_callchain_opt, "fp"), |
1034 | OPT_INCR('v', "verbose", &verbose, | 928 | OPT_INCR('v', "verbose", &verbose, |
1035 | "be more verbose (show counter open errors, etc)"), | 929 | "be more verbose (show counter open errors, etc)"), |
1036 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 930 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
@@ -1059,6 +953,8 @@ const struct option record_options[] = { | |||
1059 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, | 953 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, |
1060 | "branch filter mask", "branch stack filter modes", | 954 | "branch filter mask", "branch stack filter modes", |
1061 | parse_branch_stack), | 955 | parse_branch_stack), |
956 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, | ||
957 | "sample by weight (on special events only)"), | ||
1062 | OPT_END() | 958 | OPT_END() |
1063 | }; | 959 | }; |
1064 | 960 | ||
@@ -1070,7 +966,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1070 | struct perf_record *rec = &record; | 966 | struct perf_record *rec = &record; |
1071 | char errbuf[BUFSIZ]; | 967 | char errbuf[BUFSIZ]; |
1072 | 968 | ||
1073 | evsel_list = perf_evlist__new(NULL, NULL); | 969 | evsel_list = perf_evlist__new(); |
1074 | if (evsel_list == NULL) | 970 | if (evsel_list == NULL) |
1075 | return -ENOMEM; | 971 | return -ENOMEM; |
1076 | 972 | ||
@@ -1132,7 +1028,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1132 | ui__error("%s", errbuf); | 1028 | ui__error("%s", errbuf); |
1133 | 1029 | ||
1134 | err = -saved_errno; | 1030 | err = -saved_errno; |
1135 | goto out_free_fd; | 1031 | goto out_symbol_exit; |
1136 | } | 1032 | } |
1137 | 1033 | ||
1138 | err = -ENOMEM; | 1034 | err = -ENOMEM; |
@@ -1163,6 +1059,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1163 | } | 1059 | } |
1164 | 1060 | ||
1165 | err = __cmd_record(&record, argc, argv); | 1061 | err = __cmd_record(&record, argc, argv); |
1062 | |||
1063 | perf_evlist__munmap(evsel_list); | ||
1064 | perf_evlist__close(evsel_list); | ||
1166 | out_free_fd: | 1065 | out_free_fd: |
1167 | perf_evlist__delete_maps(evsel_list); | 1066 | perf_evlist__delete_maps(evsel_list); |
1168 | out_symbol_exit: | 1067 | out_symbol_exit: |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index fc251005dd3d..ca98d34cd58b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -8,11 +8,11 @@ | |||
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | #include "util/cache.h" | ||
11 | 12 | ||
12 | #include "util/annotate.h" | 13 | #include "util/annotate.h" |
13 | #include "util/color.h" | 14 | #include "util/color.h" |
14 | #include <linux/list.h> | 15 | #include <linux/list.h> |
15 | #include "util/cache.h" | ||
16 | #include <linux/rbtree.h> | 16 | #include <linux/rbtree.h> |
17 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
18 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
@@ -46,14 +46,124 @@ struct perf_report { | |||
46 | bool show_full_info; | 46 | bool show_full_info; |
47 | bool show_threads; | 47 | bool show_threads; |
48 | bool inverted_callchain; | 48 | bool inverted_callchain; |
49 | bool mem_mode; | ||
49 | struct perf_read_values show_threads_values; | 50 | struct perf_read_values show_threads_values; |
50 | const char *pretty_printing_style; | 51 | const char *pretty_printing_style; |
51 | symbol_filter_t annotate_init; | 52 | symbol_filter_t annotate_init; |
52 | const char *cpu_list; | 53 | const char *cpu_list; |
53 | const char *symbol_filter_str; | 54 | const char *symbol_filter_str; |
55 | float min_percent; | ||
54 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 56 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
55 | }; | 57 | }; |
56 | 58 | ||
59 | static int perf_report_config(const char *var, const char *value, void *cb) | ||
60 | { | ||
61 | if (!strcmp(var, "report.group")) { | ||
62 | symbol_conf.event_group = perf_config_bool(var, value); | ||
63 | return 0; | ||
64 | } | ||
65 | if (!strcmp(var, "report.percent-limit")) { | ||
66 | struct perf_report *rep = cb; | ||
67 | rep->min_percent = strtof(value, NULL); | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | return perf_default_config(var, value, cb); | ||
72 | } | ||
73 | |||
74 | static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | ||
75 | struct addr_location *al, | ||
76 | struct perf_sample *sample, | ||
77 | struct perf_evsel *evsel, | ||
78 | struct machine *machine, | ||
79 | union perf_event *event) | ||
80 | { | ||
81 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | ||
82 | struct symbol *parent = NULL; | ||
83 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
84 | int err = 0; | ||
85 | struct hist_entry *he; | ||
86 | struct mem_info *mi, *mx; | ||
87 | uint64_t cost; | ||
88 | |||
89 | if ((sort__has_parent || symbol_conf.use_callchain) && | ||
90 | sample->callchain) { | ||
91 | err = machine__resolve_callchain(machine, evsel, al->thread, | ||
92 | sample, &parent); | ||
93 | if (err) | ||
94 | return err; | ||
95 | } | ||
96 | |||
97 | mi = machine__resolve_mem(machine, al->thread, sample, cpumode); | ||
98 | if (!mi) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | if (rep->hide_unresolved && !al->sym) | ||
102 | return 0; | ||
103 | |||
104 | cost = sample->weight; | ||
105 | if (!cost) | ||
106 | cost = 1; | ||
107 | |||
108 | /* | ||
109 | * must pass period=weight in order to get the correct | ||
110 | * sorting from hists__collapse_resort() which is solely | ||
111 | * based on periods. We want sorting be done on nr_events * weight | ||
112 | * and this is indirectly achieved by passing period=weight here | ||
113 | * and the he_stat__add_period() function. | ||
114 | */ | ||
115 | he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); | ||
116 | if (!he) | ||
117 | return -ENOMEM; | ||
118 | |||
119 | /* | ||
120 | * In the TUI browser, we are doing integrated annotation, | ||
121 | * so we don't allocate the extra space needed because the stdio | ||
122 | * code will not use it. | ||
123 | */ | ||
124 | if (sort__has_sym && he->ms.sym && use_browser > 0) { | ||
125 | struct annotation *notes = symbol__annotation(he->ms.sym); | ||
126 | |||
127 | assert(evsel != NULL); | ||
128 | |||
129 | if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) | ||
130 | goto out; | ||
131 | |||
132 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
133 | if (err) | ||
134 | goto out; | ||
135 | } | ||
136 | |||
137 | if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { | ||
138 | struct annotation *notes; | ||
139 | |||
140 | mx = he->mem_info; | ||
141 | |||
142 | notes = symbol__annotation(mx->daddr.sym); | ||
143 | if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) | ||
144 | goto out; | ||
145 | |||
146 | err = symbol__inc_addr_samples(mx->daddr.sym, | ||
147 | mx->daddr.map, | ||
148 | evsel->idx, | ||
149 | mx->daddr.al_addr); | ||
150 | if (err) | ||
151 | goto out; | ||
152 | } | ||
153 | |||
154 | evsel->hists.stats.total_period += cost; | ||
155 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
156 | err = 0; | ||
157 | |||
158 | if (symbol_conf.use_callchain) { | ||
159 | err = callchain_append(he->callchain, | ||
160 | &callchain_cursor, | ||
161 | sample->period); | ||
162 | } | ||
163 | out: | ||
164 | return err; | ||
165 | } | ||
166 | |||
57 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | 167 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, |
58 | struct addr_location *al, | 168 | struct addr_location *al, |
59 | struct perf_sample *sample, | 169 | struct perf_sample *sample, |
@@ -83,15 +193,17 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
83 | for (i = 0; i < sample->branch_stack->nr; i++) { | 193 | for (i = 0; i < sample->branch_stack->nr; i++) { |
84 | if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) | 194 | if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) |
85 | continue; | 195 | continue; |
196 | |||
197 | err = -ENOMEM; | ||
198 | |||
86 | /* | 199 | /* |
87 | * The report shows the percentage of total branches captured | 200 | * The report shows the percentage of total branches captured |
88 | * and not events sampled. Thus we use a pseudo period of 1. | 201 | * and not events sampled. Thus we use a pseudo period of 1. |
89 | */ | 202 | */ |
90 | he = __hists__add_branch_entry(&evsel->hists, al, parent, | 203 | he = __hists__add_branch_entry(&evsel->hists, al, parent, |
91 | &bi[i], 1); | 204 | &bi[i], 1, 1); |
92 | if (he) { | 205 | if (he) { |
93 | struct annotation *notes; | 206 | struct annotation *notes; |
94 | err = -ENOMEM; | ||
95 | bx = he->branch_info; | 207 | bx = he->branch_info; |
96 | if (bx->from.sym && use_browser == 1 && sort__has_sym) { | 208 | if (bx->from.sym && use_browser == 1 && sort__has_sym) { |
97 | notes = symbol__annotation(bx->from.sym); | 209 | notes = symbol__annotation(bx->from.sym); |
@@ -122,11 +234,12 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
122 | } | 234 | } |
123 | evsel->hists.stats.total_period += 1; | 235 | evsel->hists.stats.total_period += 1; |
124 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 236 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
125 | err = 0; | ||
126 | } else | 237 | } else |
127 | return -ENOMEM; | 238 | goto out; |
128 | } | 239 | } |
240 | err = 0; | ||
129 | out: | 241 | out: |
242 | free(bi); | ||
130 | return err; | 243 | return err; |
131 | } | 244 | } |
132 | 245 | ||
@@ -146,7 +259,8 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
146 | return err; | 259 | return err; |
147 | } | 260 | } |
148 | 261 | ||
149 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | 262 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period, |
263 | sample->weight); | ||
150 | if (he == NULL) | 264 | if (he == NULL) |
151 | return -ENOMEM; | 265 | return -ENOMEM; |
152 | 266 | ||
@@ -158,7 +272,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
158 | return err; | 272 | return err; |
159 | } | 273 | } |
160 | /* | 274 | /* |
161 | * Only in the newt browser we are doing integrated annotation, | 275 | * Only in the TUI browser we are doing integrated annotation, |
162 | * so we don't allocated the extra space needed because the stdio | 276 | * so we don't allocated the extra space needed because the stdio |
163 | * code will not use it. | 277 | * code will not use it. |
164 | */ | 278 | */ |
@@ -189,6 +303,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
189 | { | 303 | { |
190 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 304 | struct perf_report *rep = container_of(tool, struct perf_report, tool); |
191 | struct addr_location al; | 305 | struct addr_location al; |
306 | int ret; | ||
192 | 307 | ||
193 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 308 | if (perf_event__preprocess_sample(event, machine, &al, sample, |
194 | rep->annotate_init) < 0) { | 309 | rep->annotate_init) < 0) { |
@@ -203,22 +318,25 @@ static int process_sample_event(struct perf_tool *tool, | |||
203 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) | 318 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) |
204 | return 0; | 319 | return 0; |
205 | 320 | ||
206 | if (sort__branch_mode == 1) { | 321 | if (sort__mode == SORT_MODE__BRANCH) { |
207 | if (perf_report__add_branch_hist_entry(tool, &al, sample, | 322 | ret = perf_report__add_branch_hist_entry(tool, &al, sample, |
208 | evsel, machine)) { | 323 | evsel, machine); |
324 | if (ret < 0) | ||
209 | pr_debug("problem adding lbr entry, skipping event\n"); | 325 | pr_debug("problem adding lbr entry, skipping event\n"); |
210 | return -1; | 326 | } else if (rep->mem_mode == 1) { |
211 | } | 327 | ret = perf_report__add_mem_hist_entry(tool, &al, sample, |
328 | evsel, machine, event); | ||
329 | if (ret < 0) | ||
330 | pr_debug("problem adding mem entry, skipping event\n"); | ||
212 | } else { | 331 | } else { |
213 | if (al.map != NULL) | 332 | if (al.map != NULL) |
214 | al.map->dso->hit = 1; | 333 | al.map->dso->hit = 1; |
215 | 334 | ||
216 | if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) { | 335 | ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); |
336 | if (ret < 0) | ||
217 | pr_debug("problem incrementing symbol period, skipping event\n"); | 337 | pr_debug("problem incrementing symbol period, skipping event\n"); |
218 | return -1; | ||
219 | } | ||
220 | } | 338 | } |
221 | return 0; | 339 | return ret; |
222 | } | 340 | } |
223 | 341 | ||
224 | static int process_read_event(struct perf_tool *tool, | 342 | static int process_read_event(struct perf_tool *tool, |
@@ -273,7 +391,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
273 | } | 391 | } |
274 | } | 392 | } |
275 | 393 | ||
276 | if (sort__branch_mode == 1) { | 394 | if (sort__mode == SORT_MODE__BRANCH) { |
277 | if (!self->fd_pipe && | 395 | if (!self->fd_pipe && |
278 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { | 396 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { |
279 | ui__error("Selected -b but no branch data. " | 397 | ui__error("Selected -b but no branch data. " |
@@ -292,20 +410,40 @@ static void sig_handler(int sig __maybe_unused) | |||
292 | session_done = 1; | 410 | session_done = 1; |
293 | } | 411 | } |
294 | 412 | ||
295 | static size_t hists__fprintf_nr_sample_events(struct hists *self, | 413 | static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, |
414 | struct hists *self, | ||
296 | const char *evname, FILE *fp) | 415 | const char *evname, FILE *fp) |
297 | { | 416 | { |
298 | size_t ret; | 417 | size_t ret; |
299 | char unit; | 418 | char unit; |
300 | unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 419 | unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
301 | u64 nr_events = self->stats.total_period; | 420 | u64 nr_events = self->stats.total_period; |
421 | struct perf_evsel *evsel = hists_to_evsel(self); | ||
422 | char buf[512]; | ||
423 | size_t size = sizeof(buf); | ||
424 | |||
425 | if (perf_evsel__is_group_event(evsel)) { | ||
426 | struct perf_evsel *pos; | ||
427 | |||
428 | perf_evsel__group_desc(evsel, buf, size); | ||
429 | evname = buf; | ||
430 | |||
431 | for_each_group_member(pos, evsel) { | ||
432 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
433 | nr_events += pos->hists.stats.total_period; | ||
434 | } | ||
435 | } | ||
302 | 436 | ||
303 | nr_samples = convert_unit(nr_samples, &unit); | 437 | nr_samples = convert_unit(nr_samples, &unit); |
304 | ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); | 438 | ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); |
305 | if (evname != NULL) | 439 | if (evname != NULL) |
306 | ret += fprintf(fp, " of event '%s'", evname); | 440 | ret += fprintf(fp, " of event '%s'", evname); |
307 | 441 | ||
308 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); | 442 | if (rep->mem_mode) { |
443 | ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); | ||
444 | ret += fprintf(fp, "\n# Sort order : %s", sort_order); | ||
445 | } else | ||
446 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); | ||
309 | return ret + fprintf(fp, "\n#\n"); | 447 | return ret + fprintf(fp, "\n#\n"); |
310 | } | 448 | } |
311 | 449 | ||
@@ -319,8 +457,12 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
319 | struct hists *hists = &pos->hists; | 457 | struct hists *hists = &pos->hists; |
320 | const char *evname = perf_evsel__name(pos); | 458 | const char *evname = perf_evsel__name(pos); |
321 | 459 | ||
322 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 460 | if (symbol_conf.event_group && |
323 | hists__fprintf(hists, true, 0, 0, stdout); | 461 | !perf_evsel__is_group_leader(pos)) |
462 | continue; | ||
463 | |||
464 | hists__fprintf_nr_sample_events(rep, hists, evname, stdout); | ||
465 | hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout); | ||
324 | fprintf(stdout, "\n\n"); | 466 | fprintf(stdout, "\n\n"); |
325 | } | 467 | } |
326 | 468 | ||
@@ -372,7 +514,7 @@ static int __cmd_report(struct perf_report *rep) | |||
372 | if (ret) | 514 | if (ret) |
373 | goto out_delete; | 515 | goto out_delete; |
374 | 516 | ||
375 | kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION]; | 517 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; |
376 | kernel_kmap = map__kmap(kernel_map); | 518 | kernel_kmap = map__kmap(kernel_map); |
377 | if (kernel_map == NULL || | 519 | if (kernel_map == NULL || |
378 | (kernel_map->dso->hit && | 520 | (kernel_map->dso->hit && |
@@ -416,8 +558,16 @@ static int __cmd_report(struct perf_report *rep) | |||
416 | hists->symbol_filter_str = rep->symbol_filter_str; | 558 | hists->symbol_filter_str = rep->symbol_filter_str; |
417 | 559 | ||
418 | hists__collapse_resort(hists); | 560 | hists__collapse_resort(hists); |
419 | hists__output_resort(hists); | ||
420 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 561 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
562 | |||
563 | /* Non-group events are considered as leader */ | ||
564 | if (symbol_conf.event_group && | ||
565 | !perf_evsel__is_group_leader(pos)) { | ||
566 | struct hists *leader_hists = &pos->leader->hists; | ||
567 | |||
568 | hists__match(leader_hists, hists); | ||
569 | hists__link(leader_hists, hists); | ||
570 | } | ||
421 | } | 571 | } |
422 | 572 | ||
423 | if (nr_samples == 0) { | 573 | if (nr_samples == 0) { |
@@ -425,14 +575,25 @@ static int __cmd_report(struct perf_report *rep) | |||
425 | goto out_delete; | 575 | goto out_delete; |
426 | } | 576 | } |
427 | 577 | ||
578 | list_for_each_entry(pos, &session->evlist->entries, node) | ||
579 | hists__output_resort(&pos->hists); | ||
580 | |||
428 | if (use_browser > 0) { | 581 | if (use_browser > 0) { |
429 | if (use_browser == 1) { | 582 | if (use_browser == 1) { |
430 | perf_evlist__tui_browse_hists(session->evlist, help, | 583 | ret = perf_evlist__tui_browse_hists(session->evlist, |
431 | NULL, | 584 | help, NULL, |
432 | &session->header.env); | 585 | rep->min_percent, |
586 | &session->header.env); | ||
587 | /* | ||
588 | * Usually "ret" is the last pressed key, and we only | ||
589 | * care if the key notifies us to switch data file. | ||
590 | */ | ||
591 | if (ret != K_SWITCH_INPUT_DATA) | ||
592 | ret = 0; | ||
593 | |||
433 | } else if (use_browser == 2) { | 594 | } else if (use_browser == 2) { |
434 | perf_evlist__gtk_browse_hists(session->evlist, help, | 595 | perf_evlist__gtk_browse_hists(session->evlist, help, |
435 | NULL); | 596 | NULL, rep->min_percent); |
436 | } | 597 | } |
437 | } else | 598 | } else |
438 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 599 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
@@ -537,7 +698,19 @@ static int | |||
537 | parse_branch_mode(const struct option *opt __maybe_unused, | 698 | parse_branch_mode(const struct option *opt __maybe_unused, |
538 | const char *str __maybe_unused, int unset) | 699 | const char *str __maybe_unused, int unset) |
539 | { | 700 | { |
540 | sort__branch_mode = !unset; | 701 | int *branch_mode = opt->value; |
702 | |||
703 | *branch_mode = !unset; | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static int | ||
708 | parse_percent_limit(const struct option *opt, const char *str, | ||
709 | int unset __maybe_unused) | ||
710 | { | ||
711 | struct perf_report *rep = opt->value; | ||
712 | |||
713 | rep->min_percent = strtof(str, NULL); | ||
541 | return 0; | 714 | return 0; |
542 | } | 715 | } |
543 | 716 | ||
@@ -546,6 +719,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
546 | struct perf_session *session; | 719 | struct perf_session *session; |
547 | struct stat st; | 720 | struct stat st; |
548 | bool has_br_stack = false; | 721 | bool has_br_stack = false; |
722 | int branch_mode = -1; | ||
549 | int ret = -1; | 723 | int ret = -1; |
550 | char callchain_default_opt[] = "fractal,0.5,callee"; | 724 | char callchain_default_opt[] = "fractal,0.5,callee"; |
551 | const char * const report_usage[] = { | 725 | const char * const report_usage[] = { |
@@ -595,8 +769,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
595 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, | 769 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, |
596 | "Use the stdio interface"), | 770 | "Use the stdio interface"), |
597 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 771 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
598 | "sort by key(s): pid, comm, dso, symbol, parent, dso_to," | 772 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," |
599 | " dso_from, symbol_to, symbol_from, mispredict"), | 773 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," |
774 | " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " | ||
775 | "snoop, locked"), | ||
600 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 776 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
601 | "Show sample percentage for different cpu modes"), | 777 | "Show sample percentage for different cpu modes"), |
602 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 778 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -638,13 +814,22 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
638 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 814 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
639 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 815 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
640 | "Show a column with the sum of periods"), | 816 | "Show a column with the sum of periods"), |
641 | OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", | 817 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, |
818 | "Show event group information together"), | ||
819 | OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "", | ||
642 | "use branch records for histogram filling", parse_branch_mode), | 820 | "use branch records for histogram filling", parse_branch_mode), |
643 | OPT_STRING(0, "objdump", &objdump_path, "path", | 821 | OPT_STRING(0, "objdump", &objdump_path, "path", |
644 | "objdump binary to use for disassembly and annotations"), | 822 | "objdump binary to use for disassembly and annotations"), |
823 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | ||
824 | "Disable symbol demangling"), | ||
825 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | ||
826 | OPT_CALLBACK(0, "percent-limit", &report, "percent", | ||
827 | "Don't show entries under that percent", parse_percent_limit), | ||
645 | OPT_END() | 828 | OPT_END() |
646 | }; | 829 | }; |
647 | 830 | ||
831 | perf_config(perf_report_config, &report); | ||
832 | |||
648 | argc = parse_options(argc, argv, options, report_usage, 0); | 833 | argc = parse_options(argc, argv, options, report_usage, 0); |
649 | 834 | ||
650 | if (report.use_stdio) | 835 | if (report.use_stdio) |
@@ -663,6 +848,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
663 | else | 848 | else |
664 | input_name = "perf.data"; | 849 | input_name = "perf.data"; |
665 | } | 850 | } |
851 | |||
852 | if (strcmp(input_name, "-") != 0) | ||
853 | setup_browser(true); | ||
854 | else { | ||
855 | use_browser = 0; | ||
856 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
857 | perf_hpp__init(); | ||
858 | } | ||
859 | |||
860 | repeat: | ||
666 | session = perf_session__new(input_name, O_RDONLY, | 861 | session = perf_session__new(input_name, O_RDONLY, |
667 | report.force, false, &report.tool); | 862 | report.force, false, &report.tool); |
668 | if (session == NULL) | 863 | if (session == NULL) |
@@ -673,11 +868,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
673 | has_br_stack = perf_header__has_feat(&session->header, | 868 | has_br_stack = perf_header__has_feat(&session->header, |
674 | HEADER_BRANCH_STACK); | 869 | HEADER_BRANCH_STACK); |
675 | 870 | ||
676 | if (sort__branch_mode == -1 && has_br_stack) | 871 | if (branch_mode == -1 && has_br_stack) |
677 | sort__branch_mode = 1; | 872 | sort__mode = SORT_MODE__BRANCH; |
678 | 873 | ||
679 | /* sort__branch_mode could be 0 if --no-branch-stack */ | 874 | /* sort__mode could be NORMAL if --no-branch-stack */ |
680 | if (sort__branch_mode == 1) { | 875 | if (sort__mode == SORT_MODE__BRANCH) { |
681 | /* | 876 | /* |
682 | * if no sort_order is provided, then specify | 877 | * if no sort_order is provided, then specify |
683 | * branch-mode specific order | 878 | * branch-mode specific order |
@@ -687,18 +882,26 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
687 | "dso_to,symbol_to"; | 882 | "dso_to,symbol_to"; |
688 | 883 | ||
689 | } | 884 | } |
885 | if (report.mem_mode) { | ||
886 | if (sort__mode == SORT_MODE__BRANCH) { | ||
887 | fprintf(stderr, "branch and mem mode incompatible\n"); | ||
888 | goto error; | ||
889 | } | ||
890 | sort__mode = SORT_MODE__MEMORY; | ||
690 | 891 | ||
691 | if (strcmp(input_name, "-") != 0) | 892 | /* |
692 | setup_browser(true); | 893 | * if no sort_order is provided, then specify |
693 | else { | 894 | * branch-mode specific order |
694 | use_browser = 0; | 895 | */ |
695 | perf_hpp__init(); | 896 | if (sort_order == default_sort_order) |
897 | sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | ||
696 | } | 898 | } |
697 | 899 | ||
698 | setup_sorting(report_usage, options); | 900 | if (setup_sorting() < 0) |
901 | usage_with_options(report_usage, options); | ||
699 | 902 | ||
700 | /* | 903 | /* |
701 | * Only in the newt browser we are doing integrated annotation, | 904 | * Only in the TUI browser we are doing integrated annotation, |
702 | * so don't allocate extra space that won't be used in the stdio | 905 | * so don't allocate extra space that won't be used in the stdio |
703 | * implementation. | 906 | * implementation. |
704 | */ | 907 | */ |
@@ -750,19 +953,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
750 | report.symbol_filter_str = argv[0]; | 953 | report.symbol_filter_str = argv[0]; |
751 | } | 954 | } |
752 | 955 | ||
753 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | 956 | sort__setup_elide(stdout); |
754 | |||
755 | if (sort__branch_mode == 1) { | ||
756 | sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout); | ||
757 | sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout); | ||
758 | sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout); | ||
759 | sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout); | ||
760 | } else { | ||
761 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
762 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
763 | } | ||
764 | 957 | ||
765 | ret = __cmd_report(&report); | 958 | ret = __cmd_report(&report); |
959 | if (ret == K_SWITCH_INPUT_DATA) { | ||
960 | perf_session__delete(session); | ||
961 | goto repeat; | ||
962 | } else | ||
963 | ret = 0; | ||
964 | |||
766 | error: | 965 | error: |
767 | perf_session__delete(session); | 966 | perf_session__delete(session); |
768 | return ret; | 967 | return ret; |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index cc28b85dabd5..2da2a6ca22bf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1475,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1475 | goto out_delete; | 1475 | goto out_delete; |
1476 | } | 1476 | } |
1477 | 1477 | ||
1478 | sched->nr_events = session->hists.stats.nr_events[0]; | 1478 | sched->nr_events = session->stats.nr_events[0]; |
1479 | sched->nr_lost_events = session->hists.stats.total_lost; | 1479 | sched->nr_lost_events = session->stats.total_lost; |
1480 | sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; | 1480 | sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST]; |
1481 | } | 1481 | } |
1482 | 1482 | ||
1483 | if (destroy) | 1483 | if (destroy) |
@@ -1671,7 +1671,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1671 | .sample = perf_sched__process_tracepoint_sample, | 1671 | .sample = perf_sched__process_tracepoint_sample, |
1672 | .comm = perf_event__process_comm, | 1672 | .comm = perf_event__process_comm, |
1673 | .lost = perf_event__process_lost, | 1673 | .lost = perf_event__process_lost, |
1674 | .exit = perf_event__process_exit, | ||
1675 | .fork = perf_event__process_fork, | 1674 | .fork = perf_event__process_fork, |
1676 | .ordered_samples = true, | 1675 | .ordered_samples = true, |
1677 | }, | 1676 | }, |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b363e7b292b2..92d4658f56fb 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused, | |||
692 | const char *arg, int unset __maybe_unused) | 692 | const char *arg, int unset __maybe_unused) |
693 | { | 693 | { |
694 | char *tok; | 694 | char *tok; |
695 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | 695 | int i, imax = ARRAY_SIZE(all_output_options); |
696 | int j; | 696 | int j; |
697 | int rc = 0; | 697 | int rc = 0; |
698 | char *str = strdup(arg); | 698 | char *str = strdup(arg); |
@@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix) | |||
909 | return NULL; | 909 | return NULL; |
910 | } | 910 | } |
911 | 911 | ||
912 | static char *ltrim(char *str) | ||
913 | { | ||
914 | int len = strlen(str); | ||
915 | |||
916 | while (len && isspace(*str)) { | ||
917 | len--; | ||
918 | str++; | ||
919 | } | ||
920 | |||
921 | return str; | ||
922 | } | ||
923 | |||
924 | static int read_script_info(struct script_desc *desc, const char *filename) | 912 | static int read_script_info(struct script_desc *desc, const char *filename) |
925 | { | 913 | { |
926 | char line[BUFSIZ], *p; | 914 | char line[BUFSIZ], *p; |
@@ -1487,7 +1475,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1487 | return -1; | 1475 | return -1; |
1488 | } | 1476 | } |
1489 | 1477 | ||
1490 | perf_session__fprintf_info(session, stdout, show_full_info); | 1478 | if (!script_name && !generate_script_lang) |
1479 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
1491 | 1480 | ||
1492 | if (!no_callchain) | 1481 | if (!no_callchain) |
1493 | symbol_conf.use_callchain = true; | 1482 | symbol_conf.use_callchain = true; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c247faca7127..7e910bab1097 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -65,16 +65,28 @@ | |||
65 | #define CNTR_NOT_SUPPORTED "<not supported>" | 65 | #define CNTR_NOT_SUPPORTED "<not supported>" |
66 | #define CNTR_NOT_COUNTED "<not counted>" | 66 | #define CNTR_NOT_COUNTED "<not counted>" |
67 | 67 | ||
68 | static void print_stat(int argc, const char **argv); | ||
69 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); | ||
70 | static void print_counter(struct perf_evsel *counter, char *prefix); | ||
71 | static void print_aggr(char *prefix); | ||
72 | |||
68 | static struct perf_evlist *evsel_list; | 73 | static struct perf_evlist *evsel_list; |
69 | 74 | ||
70 | static struct perf_target target = { | 75 | static struct perf_target target = { |
71 | .uid = UINT_MAX, | 76 | .uid = UINT_MAX, |
72 | }; | 77 | }; |
73 | 78 | ||
79 | enum aggr_mode { | ||
80 | AGGR_NONE, | ||
81 | AGGR_GLOBAL, | ||
82 | AGGR_SOCKET, | ||
83 | AGGR_CORE, | ||
84 | }; | ||
85 | |||
74 | static int run_count = 1; | 86 | static int run_count = 1; |
75 | static bool no_inherit = false; | 87 | static bool no_inherit = false; |
76 | static bool scale = true; | 88 | static bool scale = true; |
77 | static bool no_aggr = false; | 89 | static enum aggr_mode aggr_mode = AGGR_GLOBAL; |
78 | static pid_t child_pid = -1; | 90 | static pid_t child_pid = -1; |
79 | static bool null_run = false; | 91 | static bool null_run = false; |
80 | static int detailed_run = 0; | 92 | static int detailed_run = 0; |
@@ -87,6 +99,11 @@ static FILE *output = NULL; | |||
87 | static const char *pre_cmd = NULL; | 99 | static const char *pre_cmd = NULL; |
88 | static const char *post_cmd = NULL; | 100 | static const char *post_cmd = NULL; |
89 | static bool sync_run = false; | 101 | static bool sync_run = false; |
102 | static unsigned int interval = 0; | ||
103 | static bool forever = false; | ||
104 | static struct timespec ref_time; | ||
105 | static struct cpu_map *aggr_map; | ||
106 | static int (*aggr_get_id)(struct cpu_map *m, int cpu); | ||
90 | 107 | ||
91 | static volatile int done = 0; | 108 | static volatile int done = 0; |
92 | 109 | ||
@@ -94,6 +111,33 @@ struct perf_stat { | |||
94 | struct stats res_stats[3]; | 111 | struct stats res_stats[3]; |
95 | }; | 112 | }; |
96 | 113 | ||
114 | static inline void diff_timespec(struct timespec *r, struct timespec *a, | ||
115 | struct timespec *b) | ||
116 | { | ||
117 | r->tv_sec = a->tv_sec - b->tv_sec; | ||
118 | if (a->tv_nsec < b->tv_nsec) { | ||
119 | r->tv_nsec = a->tv_nsec + 1000000000L - b->tv_nsec; | ||
120 | r->tv_sec--; | ||
121 | } else { | ||
122 | r->tv_nsec = a->tv_nsec - b->tv_nsec ; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | ||
127 | { | ||
128 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | ||
129 | } | ||
130 | |||
131 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | ||
132 | { | ||
133 | return perf_evsel__cpus(evsel)->nr; | ||
134 | } | ||
135 | |||
136 | static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | ||
137 | { | ||
138 | memset(evsel->priv, 0, sizeof(struct perf_stat)); | ||
139 | } | ||
140 | |||
97 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | 141 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) |
98 | { | 142 | { |
99 | evsel->priv = zalloc(sizeof(struct perf_stat)); | 143 | evsel->priv = zalloc(sizeof(struct perf_stat)); |
@@ -106,14 +150,56 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | |||
106 | evsel->priv = NULL; | 150 | evsel->priv = NULL; |
107 | } | 151 | } |
108 | 152 | ||
109 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | 153 | static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) |
110 | { | 154 | { |
111 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | 155 | void *addr; |
156 | size_t sz; | ||
157 | |||
158 | sz = sizeof(*evsel->counts) + | ||
159 | (perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values)); | ||
160 | |||
161 | addr = zalloc(sz); | ||
162 | if (!addr) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | evsel->prev_raw_counts = addr; | ||
166 | |||
167 | return 0; | ||
112 | } | 168 | } |
113 | 169 | ||
114 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | 170 | static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) |
115 | { | 171 | { |
116 | return perf_evsel__cpus(evsel)->nr; | 172 | free(evsel->prev_raw_counts); |
173 | evsel->prev_raw_counts = NULL; | ||
174 | } | ||
175 | |||
176 | static void perf_evlist__free_stats(struct perf_evlist *evlist) | ||
177 | { | ||
178 | struct perf_evsel *evsel; | ||
179 | |||
180 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
181 | perf_evsel__free_stat_priv(evsel); | ||
182 | perf_evsel__free_counts(evsel); | ||
183 | perf_evsel__free_prev_raw_counts(evsel); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) | ||
188 | { | ||
189 | struct perf_evsel *evsel; | ||
190 | |||
191 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
192 | if (perf_evsel__alloc_stat_priv(evsel) < 0 || | ||
193 | perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 || | ||
194 | (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) | ||
195 | goto out_free; | ||
196 | } | ||
197 | |||
198 | return 0; | ||
199 | |||
200 | out_free: | ||
201 | perf_evlist__free_stats(evlist); | ||
202 | return -1; | ||
117 | } | 203 | } |
118 | 204 | ||
119 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 205 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
@@ -129,11 +215,32 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | |||
129 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 215 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; |
130 | static struct stats walltime_nsecs_stats; | 216 | static struct stats walltime_nsecs_stats; |
131 | 217 | ||
218 | static void perf_stat__reset_stats(struct perf_evlist *evlist) | ||
219 | { | ||
220 | struct perf_evsel *evsel; | ||
221 | |||
222 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
223 | perf_evsel__reset_stat_priv(evsel); | ||
224 | perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel)); | ||
225 | } | ||
226 | |||
227 | memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats)); | ||
228 | memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats)); | ||
229 | memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats)); | ||
230 | memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats)); | ||
231 | memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats)); | ||
232 | memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats)); | ||
233 | memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats)); | ||
234 | memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats)); | ||
235 | memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats)); | ||
236 | memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats)); | ||
237 | memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats)); | ||
238 | memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); | ||
239 | } | ||
240 | |||
132 | static int create_perf_stat_counter(struct perf_evsel *evsel) | 241 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
133 | { | 242 | { |
134 | struct perf_event_attr *attr = &evsel->attr; | 243 | struct perf_event_attr *attr = &evsel->attr; |
135 | bool exclude_guest_missing = false; | ||
136 | int ret; | ||
137 | 244 | ||
138 | if (scale) | 245 | if (scale) |
139 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 246 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
@@ -141,38 +248,16 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
141 | 248 | ||
142 | attr->inherit = !no_inherit; | 249 | attr->inherit = !no_inherit; |
143 | 250 | ||
144 | retry: | 251 | if (perf_target__has_cpu(&target)) |
145 | if (exclude_guest_missing) | 252 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
146 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | ||
147 | |||
148 | if (perf_target__has_cpu(&target)) { | ||
149 | ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
150 | if (ret) | ||
151 | goto check_ret; | ||
152 | return 0; | ||
153 | } | ||
154 | 253 | ||
155 | if (!perf_target__has_task(&target) && | 254 | if (!perf_target__has_task(&target) && |
156 | !perf_evsel__is_group_member(evsel)) { | 255 | perf_evsel__is_group_leader(evsel)) { |
157 | attr->disabled = 1; | 256 | attr->disabled = 1; |
158 | attr->enable_on_exec = 1; | 257 | attr->enable_on_exec = 1; |
159 | } | 258 | } |
160 | 259 | ||
161 | ret = perf_evsel__open_per_thread(evsel, evsel_list->threads); | 260 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
162 | if (!ret) | ||
163 | return 0; | ||
164 | /* fall through */ | ||
165 | check_ret: | ||
166 | if (ret && errno == EINVAL) { | ||
167 | if (!exclude_guest_missing && | ||
168 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
169 | pr_debug("Old kernel, cannot exclude " | ||
170 | "guest or host samples.\n"); | ||
171 | exclude_guest_missing = true; | ||
172 | goto retry; | ||
173 | } | ||
174 | } | ||
175 | return ret; | ||
176 | } | 261 | } |
177 | 262 | ||
178 | /* | 263 | /* |
@@ -229,7 +314,7 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
229 | int i; | 314 | int i; |
230 | 315 | ||
231 | if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), | 316 | if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), |
232 | evsel_list->threads->nr, scale) < 0) | 317 | thread_map__nr(evsel_list->threads), scale) < 0) |
233 | return -1; | 318 | return -1; |
234 | 319 | ||
235 | for (i = 0; i < 3; i++) | 320 | for (i = 0; i < 3; i++) |
@@ -269,64 +354,91 @@ static int read_counter(struct perf_evsel *counter) | |||
269 | return 0; | 354 | return 0; |
270 | } | 355 | } |
271 | 356 | ||
272 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) | 357 | static void print_interval(void) |
273 | { | 358 | { |
274 | unsigned long long t0, t1; | 359 | static int num_print_interval; |
275 | struct perf_evsel *counter; | 360 | struct perf_evsel *counter; |
276 | int status = 0; | 361 | struct perf_stat *ps; |
277 | int child_ready_pipe[2], go_pipe[2]; | 362 | struct timespec ts, rs; |
278 | const bool forks = (argc > 0); | 363 | char prefix[64]; |
279 | char buf; | ||
280 | 364 | ||
281 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | 365 | if (aggr_mode == AGGR_GLOBAL) { |
282 | perror("failed to create pipes"); | 366 | list_for_each_entry(counter, &evsel_list->entries, node) { |
283 | return -1; | 367 | ps = counter->priv; |
368 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
369 | read_counter_aggr(counter); | ||
370 | } | ||
371 | } else { | ||
372 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
373 | ps = counter->priv; | ||
374 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
375 | read_counter(counter); | ||
376 | } | ||
284 | } | 377 | } |
285 | 378 | ||
286 | if (forks) { | 379 | clock_gettime(CLOCK_MONOTONIC, &ts); |
287 | if ((child_pid = fork()) < 0) | 380 | diff_timespec(&rs, &ts, &ref_time); |
288 | perror("failed to fork"); | 381 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); |
382 | |||
383 | if (num_print_interval == 0 && !csv_output) { | ||
384 | switch (aggr_mode) { | ||
385 | case AGGR_SOCKET: | ||
386 | fprintf(output, "# time socket cpus counts events\n"); | ||
387 | break; | ||
388 | case AGGR_CORE: | ||
389 | fprintf(output, "# time core cpus counts events\n"); | ||
390 | break; | ||
391 | case AGGR_NONE: | ||
392 | fprintf(output, "# time CPU counts events\n"); | ||
393 | break; | ||
394 | case AGGR_GLOBAL: | ||
395 | default: | ||
396 | fprintf(output, "# time counts events\n"); | ||
397 | } | ||
398 | } | ||
289 | 399 | ||
290 | if (!child_pid) { | 400 | if (++num_print_interval == 25) |
291 | close(child_ready_pipe[0]); | 401 | num_print_interval = 0; |
292 | close(go_pipe[1]); | ||
293 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
294 | 402 | ||
295 | /* | 403 | switch (aggr_mode) { |
296 | * Do a dummy execvp to get the PLT entry resolved, | 404 | case AGGR_CORE: |
297 | * so we avoid the resolver overhead on the real | 405 | case AGGR_SOCKET: |
298 | * execvp call. | 406 | print_aggr(prefix); |
299 | */ | 407 | break; |
300 | execvp("", (char **)argv); | 408 | case AGGR_NONE: |
301 | 409 | list_for_each_entry(counter, &evsel_list->entries, node) | |
302 | /* | 410 | print_counter(counter, prefix); |
303 | * Tell the parent we're ready to go | 411 | break; |
304 | */ | 412 | case AGGR_GLOBAL: |
305 | close(child_ready_pipe[1]); | 413 | default: |
414 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
415 | print_counter_aggr(counter, prefix); | ||
416 | } | ||
417 | } | ||
306 | 418 | ||
307 | /* | 419 | static int __run_perf_stat(int argc, const char **argv) |
308 | * Wait until the parent tells us to go. | 420 | { |
309 | */ | 421 | char msg[512]; |
310 | if (read(go_pipe[0], &buf, 1) == -1) | 422 | unsigned long long t0, t1; |
311 | perror("unable to read pipe"); | 423 | struct perf_evsel *counter; |
424 | struct timespec ts; | ||
425 | int status = 0; | ||
426 | const bool forks = (argc > 0); | ||
312 | 427 | ||
313 | execvp(argv[0], (char **)argv); | 428 | if (interval) { |
429 | ts.tv_sec = interval / 1000; | ||
430 | ts.tv_nsec = (interval % 1000) * 1000000; | ||
431 | } else { | ||
432 | ts.tv_sec = 1; | ||
433 | ts.tv_nsec = 0; | ||
434 | } | ||
314 | 435 | ||
315 | perror(argv[0]); | 436 | if (forks) { |
316 | exit(-1); | 437 | if (perf_evlist__prepare_workload(evsel_list, &target, argv, |
438 | false, false) < 0) { | ||
439 | perror("failed to prepare workload"); | ||
440 | return -1; | ||
317 | } | 441 | } |
318 | |||
319 | if (perf_target__none(&target)) | ||
320 | evsel_list->threads->map[0] = child_pid; | ||
321 | |||
322 | /* | ||
323 | * Wait for the child to be ready to exec. | ||
324 | */ | ||
325 | close(child_ready_pipe[1]); | ||
326 | close(go_pipe[0]); | ||
327 | if (read(child_ready_pipe[0], &buf, 1) == -1) | ||
328 | perror("unable to read pipe"); | ||
329 | close(child_ready_pipe[0]); | ||
330 | } | 442 | } |
331 | 443 | ||
332 | if (group) | 444 | if (group) |
@@ -348,20 +460,13 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
348 | continue; | 460 | continue; |
349 | } | 461 | } |
350 | 462 | ||
351 | if (errno == EPERM || errno == EACCES) { | 463 | perf_evsel__open_strerror(counter, &target, |
352 | error("You may not have permission to collect %sstats.\n" | 464 | errno, msg, sizeof(msg)); |
353 | "\t Consider tweaking" | 465 | ui__error("%s\n", msg); |
354 | " /proc/sys/kernel/perf_event_paranoid or running as root.", | 466 | |
355 | target.system_wide ? "system-wide " : ""); | ||
356 | } else { | ||
357 | error("open_counter returned with %d (%s). " | ||
358 | "/bin/dmesg may provide additional information.\n", | ||
359 | errno, strerror(errno)); | ||
360 | } | ||
361 | if (child_pid != -1) | 467 | if (child_pid != -1) |
362 | kill(child_pid, SIGTERM); | 468 | kill(child_pid, SIGTERM); |
363 | 469 | ||
364 | pr_err("Not all events could be opened.\n"); | ||
365 | return -1; | 470 | return -1; |
366 | } | 471 | } |
367 | counter->supported = true; | 472 | counter->supported = true; |
@@ -377,30 +482,42 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
377 | * Enable counters and exec the command: | 482 | * Enable counters and exec the command: |
378 | */ | 483 | */ |
379 | t0 = rdclock(); | 484 | t0 = rdclock(); |
485 | clock_gettime(CLOCK_MONOTONIC, &ref_time); | ||
380 | 486 | ||
381 | if (forks) { | 487 | if (forks) { |
382 | close(go_pipe[1]); | 488 | perf_evlist__start_workload(evsel_list); |
489 | |||
490 | if (interval) { | ||
491 | while (!waitpid(child_pid, &status, WNOHANG)) { | ||
492 | nanosleep(&ts, NULL); | ||
493 | print_interval(); | ||
494 | } | ||
495 | } | ||
383 | wait(&status); | 496 | wait(&status); |
384 | if (WIFSIGNALED(status)) | 497 | if (WIFSIGNALED(status)) |
385 | psignal(WTERMSIG(status), argv[0]); | 498 | psignal(WTERMSIG(status), argv[0]); |
386 | } else { | 499 | } else { |
387 | while(!done) sleep(1); | 500 | while (!done) { |
501 | nanosleep(&ts, NULL); | ||
502 | if (interval) | ||
503 | print_interval(); | ||
504 | } | ||
388 | } | 505 | } |
389 | 506 | ||
390 | t1 = rdclock(); | 507 | t1 = rdclock(); |
391 | 508 | ||
392 | update_stats(&walltime_nsecs_stats, t1 - t0); | 509 | update_stats(&walltime_nsecs_stats, t1 - t0); |
393 | 510 | ||
394 | if (no_aggr) { | 511 | if (aggr_mode == AGGR_GLOBAL) { |
395 | list_for_each_entry(counter, &evsel_list->entries, node) { | 512 | list_for_each_entry(counter, &evsel_list->entries, node) { |
396 | read_counter(counter); | 513 | read_counter_aggr(counter); |
397 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); | 514 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), |
515 | thread_map__nr(evsel_list->threads)); | ||
398 | } | 516 | } |
399 | } else { | 517 | } else { |
400 | list_for_each_entry(counter, &evsel_list->entries, node) { | 518 | list_for_each_entry(counter, &evsel_list->entries, node) { |
401 | read_counter_aggr(counter); | 519 | read_counter(counter); |
402 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), | 520 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); |
403 | evsel_list->threads->nr); | ||
404 | } | 521 | } |
405 | } | 522 | } |
406 | 523 | ||
@@ -454,23 +571,52 @@ static void print_noise(struct perf_evsel *evsel, double avg) | |||
454 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | 571 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); |
455 | } | 572 | } |
456 | 573 | ||
457 | static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | 574 | static void aggr_printout(struct perf_evsel *evsel, int id, int nr) |
575 | { | ||
576 | switch (aggr_mode) { | ||
577 | case AGGR_CORE: | ||
578 | fprintf(output, "S%d-C%*d%s%*d%s", | ||
579 | cpu_map__id_to_socket(id), | ||
580 | csv_output ? 0 : -8, | ||
581 | cpu_map__id_to_cpu(id), | ||
582 | csv_sep, | ||
583 | csv_output ? 0 : 4, | ||
584 | nr, | ||
585 | csv_sep); | ||
586 | break; | ||
587 | case AGGR_SOCKET: | ||
588 | fprintf(output, "S%*d%s%*d%s", | ||
589 | csv_output ? 0 : -5, | ||
590 | id, | ||
591 | csv_sep, | ||
592 | csv_output ? 0 : 4, | ||
593 | nr, | ||
594 | csv_sep); | ||
595 | break; | ||
596 | case AGGR_NONE: | ||
597 | fprintf(output, "CPU%*d%s", | ||
598 | csv_output ? 0 : -4, | ||
599 | perf_evsel__cpus(evsel)->map[id], csv_sep); | ||
600 | break; | ||
601 | case AGGR_GLOBAL: | ||
602 | default: | ||
603 | break; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | ||
458 | { | 608 | { |
459 | double msecs = avg / 1e6; | 609 | double msecs = avg / 1e6; |
460 | char cpustr[16] = { '\0', }; | 610 | const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; |
461 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; | ||
462 | 611 | ||
463 | if (no_aggr) | 612 | aggr_printout(evsel, cpu, nr); |
464 | sprintf(cpustr, "CPU%*d%s", | ||
465 | csv_output ? 0 : -4, | ||
466 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | ||
467 | 613 | ||
468 | fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel)); | 614 | fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel)); |
469 | 615 | ||
470 | if (evsel->cgrp) | 616 | if (evsel->cgrp) |
471 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 617 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
472 | 618 | ||
473 | if (csv_output) | 619 | if (csv_output || interval) |
474 | return; | 620 | return; |
475 | 621 | ||
476 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 622 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
@@ -659,37 +805,33 @@ static void print_ll_cache_misses(int cpu, | |||
659 | fprintf(output, " of all LL-cache hits "); | 805 | fprintf(output, " of all LL-cache hits "); |
660 | } | 806 | } |
661 | 807 | ||
662 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | 808 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
663 | { | 809 | { |
664 | double total, ratio = 0.0; | 810 | double total, ratio = 0.0; |
665 | char cpustr[16] = { '\0', }; | ||
666 | const char *fmt; | 811 | const char *fmt; |
667 | 812 | ||
668 | if (csv_output) | 813 | if (csv_output) |
669 | fmt = "%s%.0f%s%s"; | 814 | fmt = "%.0f%s%s"; |
670 | else if (big_num) | 815 | else if (big_num) |
671 | fmt = "%s%'18.0f%s%-25s"; | 816 | fmt = "%'18.0f%s%-25s"; |
672 | else | 817 | else |
673 | fmt = "%s%18.0f%s%-25s"; | 818 | fmt = "%18.0f%s%-25s"; |
674 | 819 | ||
675 | if (no_aggr) | 820 | aggr_printout(evsel, cpu, nr); |
676 | sprintf(cpustr, "CPU%*d%s", | 821 | |
677 | csv_output ? 0 : -4, | 822 | if (aggr_mode == AGGR_GLOBAL) |
678 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | ||
679 | else | ||
680 | cpu = 0; | 823 | cpu = 0; |
681 | 824 | ||
682 | fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel)); | 825 | fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); |
683 | 826 | ||
684 | if (evsel->cgrp) | 827 | if (evsel->cgrp) |
685 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 828 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
686 | 829 | ||
687 | if (csv_output) | 830 | if (csv_output || interval) |
688 | return; | 831 | return; |
689 | 832 | ||
690 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | 833 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { |
691 | total = avg_stats(&runtime_cycles_stats[cpu]); | 834 | total = avg_stats(&runtime_cycles_stats[cpu]); |
692 | |||
693 | if (total) | 835 | if (total) |
694 | ratio = avg / total; | 836 | ratio = avg / total; |
695 | 837 | ||
@@ -779,16 +921,80 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
779 | } | 921 | } |
780 | } | 922 | } |
781 | 923 | ||
924 | static void print_aggr(char *prefix) | ||
925 | { | ||
926 | struct perf_evsel *counter; | ||
927 | int cpu, s, s2, id, nr; | ||
928 | u64 ena, run, val; | ||
929 | |||
930 | if (!(aggr_map || aggr_get_id)) | ||
931 | return; | ||
932 | |||
933 | for (s = 0; s < aggr_map->nr; s++) { | ||
934 | id = aggr_map->map[s]; | ||
935 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
936 | val = ena = run = 0; | ||
937 | nr = 0; | ||
938 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
939 | s2 = aggr_get_id(evsel_list->cpus, cpu); | ||
940 | if (s2 != id) | ||
941 | continue; | ||
942 | val += counter->counts->cpu[cpu].val; | ||
943 | ena += counter->counts->cpu[cpu].ena; | ||
944 | run += counter->counts->cpu[cpu].run; | ||
945 | nr++; | ||
946 | } | ||
947 | if (prefix) | ||
948 | fprintf(output, "%s", prefix); | ||
949 | |||
950 | if (run == 0 || ena == 0) { | ||
951 | aggr_printout(counter, cpu, nr); | ||
952 | |||
953 | fprintf(output, "%*s%s%*s", | ||
954 | csv_output ? 0 : 18, | ||
955 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
956 | csv_sep, | ||
957 | csv_output ? 0 : -24, | ||
958 | perf_evsel__name(counter)); | ||
959 | |||
960 | if (counter->cgrp) | ||
961 | fprintf(output, "%s%s", | ||
962 | csv_sep, counter->cgrp->name); | ||
963 | |||
964 | fputc('\n', output); | ||
965 | continue; | ||
966 | } | ||
967 | |||
968 | if (nsec_counter(counter)) | ||
969 | nsec_printout(id, nr, counter, val); | ||
970 | else | ||
971 | abs_printout(id, nr, counter, val); | ||
972 | |||
973 | if (!csv_output) { | ||
974 | print_noise(counter, 1.0); | ||
975 | |||
976 | if (run != ena) | ||
977 | fprintf(output, " (%.2f%%)", | ||
978 | 100.0 * run / ena); | ||
979 | } | ||
980 | fputc('\n', output); | ||
981 | } | ||
982 | } | ||
983 | } | ||
984 | |||
782 | /* | 985 | /* |
783 | * Print out the results of a single counter: | 986 | * Print out the results of a single counter: |
784 | * aggregated counts in system-wide mode | 987 | * aggregated counts in system-wide mode |
785 | */ | 988 | */ |
786 | static void print_counter_aggr(struct perf_evsel *counter) | 989 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) |
787 | { | 990 | { |
788 | struct perf_stat *ps = counter->priv; | 991 | struct perf_stat *ps = counter->priv; |
789 | double avg = avg_stats(&ps->res_stats[0]); | 992 | double avg = avg_stats(&ps->res_stats[0]); |
790 | int scaled = counter->counts->scaled; | 993 | int scaled = counter->counts->scaled; |
791 | 994 | ||
995 | if (prefix) | ||
996 | fprintf(output, "%s", prefix); | ||
997 | |||
792 | if (scaled == -1) { | 998 | if (scaled == -1) { |
793 | fprintf(output, "%*s%s%*s", | 999 | fprintf(output, "%*s%s%*s", |
794 | csv_output ? 0 : 18, | 1000 | csv_output ? 0 : 18, |
@@ -805,9 +1011,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
805 | } | 1011 | } |
806 | 1012 | ||
807 | if (nsec_counter(counter)) | 1013 | if (nsec_counter(counter)) |
808 | nsec_printout(-1, counter, avg); | 1014 | nsec_printout(-1, 0, counter, avg); |
809 | else | 1015 | else |
810 | abs_printout(-1, counter, avg); | 1016 | abs_printout(-1, 0, counter, avg); |
811 | 1017 | ||
812 | print_noise(counter, avg); | 1018 | print_noise(counter, avg); |
813 | 1019 | ||
@@ -831,7 +1037,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
831 | * Print out the results of a single counter: | 1037 | * Print out the results of a single counter: |
832 | * does not use aggregated count in system-wide | 1038 | * does not use aggregated count in system-wide |
833 | */ | 1039 | */ |
834 | static void print_counter(struct perf_evsel *counter) | 1040 | static void print_counter(struct perf_evsel *counter, char *prefix) |
835 | { | 1041 | { |
836 | u64 ena, run, val; | 1042 | u64 ena, run, val; |
837 | int cpu; | 1043 | int cpu; |
@@ -840,6 +1046,10 @@ static void print_counter(struct perf_evsel *counter) | |||
840 | val = counter->counts->cpu[cpu].val; | 1046 | val = counter->counts->cpu[cpu].val; |
841 | ena = counter->counts->cpu[cpu].ena; | 1047 | ena = counter->counts->cpu[cpu].ena; |
842 | run = counter->counts->cpu[cpu].run; | 1048 | run = counter->counts->cpu[cpu].run; |
1049 | |||
1050 | if (prefix) | ||
1051 | fprintf(output, "%s", prefix); | ||
1052 | |||
843 | if (run == 0 || ena == 0) { | 1053 | if (run == 0 || ena == 0) { |
844 | fprintf(output, "CPU%*d%s%*s%s%*s", | 1054 | fprintf(output, "CPU%*d%s%*s%s%*s", |
845 | csv_output ? 0 : -4, | 1055 | csv_output ? 0 : -4, |
@@ -859,9 +1069,9 @@ static void print_counter(struct perf_evsel *counter) | |||
859 | } | 1069 | } |
860 | 1070 | ||
861 | if (nsec_counter(counter)) | 1071 | if (nsec_counter(counter)) |
862 | nsec_printout(cpu, counter, val); | 1072 | nsec_printout(cpu, 0, counter, val); |
863 | else | 1073 | else |
864 | abs_printout(cpu, counter, val); | 1074 | abs_printout(cpu, 0, counter, val); |
865 | 1075 | ||
866 | if (!csv_output) { | 1076 | if (!csv_output) { |
867 | print_noise(counter, 1.0); | 1077 | print_noise(counter, 1.0); |
@@ -899,12 +1109,21 @@ static void print_stat(int argc, const char **argv) | |||
899 | fprintf(output, ":\n\n"); | 1109 | fprintf(output, ":\n\n"); |
900 | } | 1110 | } |
901 | 1111 | ||
902 | if (no_aggr) { | 1112 | switch (aggr_mode) { |
1113 | case AGGR_CORE: | ||
1114 | case AGGR_SOCKET: | ||
1115 | print_aggr(NULL); | ||
1116 | break; | ||
1117 | case AGGR_GLOBAL: | ||
903 | list_for_each_entry(counter, &evsel_list->entries, node) | 1118 | list_for_each_entry(counter, &evsel_list->entries, node) |
904 | print_counter(counter); | 1119 | print_counter_aggr(counter, NULL); |
905 | } else { | 1120 | break; |
1121 | case AGGR_NONE: | ||
906 | list_for_each_entry(counter, &evsel_list->entries, node) | 1122 | list_for_each_entry(counter, &evsel_list->entries, node) |
907 | print_counter_aggr(counter); | 1123 | print_counter(counter, NULL); |
1124 | break; | ||
1125 | default: | ||
1126 | break; | ||
908 | } | 1127 | } |
909 | 1128 | ||
910 | if (!csv_output) { | 1129 | if (!csv_output) { |
@@ -925,7 +1144,7 @@ static volatile int signr = -1; | |||
925 | 1144 | ||
926 | static void skip_signal(int signo) | 1145 | static void skip_signal(int signo) |
927 | { | 1146 | { |
928 | if(child_pid == -1) | 1147 | if ((child_pid == -1) || interval) |
929 | done = 1; | 1148 | done = 1; |
930 | 1149 | ||
931 | signr = signo; | 1150 | signr = signo; |
@@ -950,6 +1169,32 @@ static int stat__set_big_num(const struct option *opt __maybe_unused, | |||
950 | return 0; | 1169 | return 0; |
951 | } | 1170 | } |
952 | 1171 | ||
1172 | static int perf_stat_init_aggr_mode(void) | ||
1173 | { | ||
1174 | switch (aggr_mode) { | ||
1175 | case AGGR_SOCKET: | ||
1176 | if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { | ||
1177 | perror("cannot build socket map"); | ||
1178 | return -1; | ||
1179 | } | ||
1180 | aggr_get_id = cpu_map__get_socket; | ||
1181 | break; | ||
1182 | case AGGR_CORE: | ||
1183 | if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { | ||
1184 | perror("cannot build core map"); | ||
1185 | return -1; | ||
1186 | } | ||
1187 | aggr_get_id = cpu_map__get_core; | ||
1188 | break; | ||
1189 | case AGGR_NONE: | ||
1190 | case AGGR_GLOBAL: | ||
1191 | default: | ||
1192 | break; | ||
1193 | } | ||
1194 | return 0; | ||
1195 | } | ||
1196 | |||
1197 | |||
953 | /* | 1198 | /* |
954 | * Add default attributes, if there were no attributes specified or | 1199 | * Add default attributes, if there were no attributes specified or |
955 | * if -d/--detailed, -d -d or -d -d -d is used: | 1200 | * if -d/--detailed, -d -d or -d -d -d is used: |
@@ -1120,7 +1365,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1120 | OPT_INCR('v', "verbose", &verbose, | 1365 | OPT_INCR('v', "verbose", &verbose, |
1121 | "be more verbose (show counter open errors, etc)"), | 1366 | "be more verbose (show counter open errors, etc)"), |
1122 | OPT_INTEGER('r', "repeat", &run_count, | 1367 | OPT_INTEGER('r', "repeat", &run_count, |
1123 | "repeat command and print average + stddev (max: 100)"), | 1368 | "repeat command and print average + stddev (max: 100, forever: 0)"), |
1124 | OPT_BOOLEAN('n', "null", &null_run, | 1369 | OPT_BOOLEAN('n', "null", &null_run, |
1125 | "null run - dont start any counters"), | 1370 | "null run - dont start any counters"), |
1126 | OPT_INCR('d', "detailed", &detailed_run, | 1371 | OPT_INCR('d', "detailed", &detailed_run, |
@@ -1132,7 +1377,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1132 | stat__set_big_num), | 1377 | stat__set_big_num), |
1133 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", | 1378 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", |
1134 | "list of cpus to monitor in system-wide"), | 1379 | "list of cpus to monitor in system-wide"), |
1135 | OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"), | 1380 | OPT_SET_UINT('A', "no-aggr", &aggr_mode, |
1381 | "disable CPU count aggregation", AGGR_NONE), | ||
1136 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 1382 | OPT_STRING('x', "field-separator", &csv_sep, "separator", |
1137 | "print counts with custom separator"), | 1383 | "print counts with custom separator"), |
1138 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 1384 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
@@ -1145,19 +1391,24 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1145 | "command to run prior to the measured command"), | 1391 | "command to run prior to the measured command"), |
1146 | OPT_STRING(0, "post", &post_cmd, "command", | 1392 | OPT_STRING(0, "post", &post_cmd, "command", |
1147 | "command to run after to the measured command"), | 1393 | "command to run after to the measured command"), |
1394 | OPT_UINTEGER('I', "interval-print", &interval, | ||
1395 | "print counts at regular interval in ms (>= 100)"), | ||
1396 | OPT_SET_UINT(0, "per-socket", &aggr_mode, | ||
1397 | "aggregate counts per processor socket", AGGR_SOCKET), | ||
1398 | OPT_SET_UINT(0, "per-core", &aggr_mode, | ||
1399 | "aggregate counts per physical processor core", AGGR_CORE), | ||
1148 | OPT_END() | 1400 | OPT_END() |
1149 | }; | 1401 | }; |
1150 | const char * const stat_usage[] = { | 1402 | const char * const stat_usage[] = { |
1151 | "perf stat [<options>] [<command>]", | 1403 | "perf stat [<options>] [<command>]", |
1152 | NULL | 1404 | NULL |
1153 | }; | 1405 | }; |
1154 | struct perf_evsel *pos; | ||
1155 | int status = -ENOMEM, run_idx; | 1406 | int status = -ENOMEM, run_idx; |
1156 | const char *mode; | 1407 | const char *mode; |
1157 | 1408 | ||
1158 | setlocale(LC_ALL, ""); | 1409 | setlocale(LC_ALL, ""); |
1159 | 1410 | ||
1160 | evsel_list = perf_evlist__new(NULL, NULL); | 1411 | evsel_list = perf_evlist__new(); |
1161 | if (evsel_list == NULL) | 1412 | if (evsel_list == NULL) |
1162 | return -ENOMEM; | 1413 | return -ENOMEM; |
1163 | 1414 | ||
@@ -1220,15 +1471,21 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1220 | 1471 | ||
1221 | if (!argc && !perf_target__has_task(&target)) | 1472 | if (!argc && !perf_target__has_task(&target)) |
1222 | usage_with_options(stat_usage, options); | 1473 | usage_with_options(stat_usage, options); |
1223 | if (run_count <= 0) | 1474 | if (run_count < 0) { |
1224 | usage_with_options(stat_usage, options); | 1475 | usage_with_options(stat_usage, options); |
1476 | } else if (run_count == 0) { | ||
1477 | forever = true; | ||
1478 | run_count = 1; | ||
1479 | } | ||
1225 | 1480 | ||
1226 | /* no_aggr, cgroup are for system-wide only */ | 1481 | /* no_aggr, cgroup are for system-wide only */ |
1227 | if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { | 1482 | if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) |
1483 | && !perf_target__has_cpu(&target)) { | ||
1228 | fprintf(stderr, "both cgroup and no-aggregation " | 1484 | fprintf(stderr, "both cgroup and no-aggregation " |
1229 | "modes only available in system-wide mode\n"); | 1485 | "modes only available in system-wide mode\n"); |
1230 | 1486 | ||
1231 | usage_with_options(stat_usage, options); | 1487 | usage_with_options(stat_usage, options); |
1488 | return -1; | ||
1232 | } | 1489 | } |
1233 | 1490 | ||
1234 | if (add_default_attributes()) | 1491 | if (add_default_attributes()) |
@@ -1245,13 +1502,18 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1245 | usage_with_options(stat_usage, options); | 1502 | usage_with_options(stat_usage, options); |
1246 | return -1; | 1503 | return -1; |
1247 | } | 1504 | } |
1248 | 1505 | if (interval && interval < 100) { | |
1249 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1506 | pr_err("print interval must be >= 100ms\n"); |
1250 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 1507 | usage_with_options(stat_usage, options); |
1251 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) | 1508 | return -1; |
1252 | goto out_free_fd; | ||
1253 | } | 1509 | } |
1254 | 1510 | ||
1511 | if (perf_evlist__alloc_stats(evsel_list, interval)) | ||
1512 | goto out_free_maps; | ||
1513 | |||
1514 | if (perf_stat_init_aggr_mode()) | ||
1515 | goto out; | ||
1516 | |||
1255 | /* | 1517 | /* |
1256 | * We dont want to block the signals - that would cause | 1518 | * We dont want to block the signals - that would cause |
1257 | * child tasks to inherit that and Ctrl-C would not work. | 1519 | * child tasks to inherit that and Ctrl-C would not work. |
@@ -1259,24 +1521,30 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1259 | * task, but being ignored by perf stat itself: | 1521 | * task, but being ignored by perf stat itself: |
1260 | */ | 1522 | */ |
1261 | atexit(sig_atexit); | 1523 | atexit(sig_atexit); |
1262 | signal(SIGINT, skip_signal); | 1524 | if (!forever) |
1525 | signal(SIGINT, skip_signal); | ||
1526 | signal(SIGCHLD, skip_signal); | ||
1263 | signal(SIGALRM, skip_signal); | 1527 | signal(SIGALRM, skip_signal); |
1264 | signal(SIGABRT, skip_signal); | 1528 | signal(SIGABRT, skip_signal); |
1265 | 1529 | ||
1266 | status = 0; | 1530 | status = 0; |
1267 | for (run_idx = 0; run_idx < run_count; run_idx++) { | 1531 | for (run_idx = 0; forever || run_idx < run_count; run_idx++) { |
1268 | if (run_count != 1 && verbose) | 1532 | if (run_count != 1 && verbose) |
1269 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1533 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
1270 | run_idx + 1); | 1534 | run_idx + 1); |
1271 | 1535 | ||
1272 | status = run_perf_stat(argc, argv); | 1536 | status = run_perf_stat(argc, argv); |
1537 | if (forever && status != -1) { | ||
1538 | print_stat(argc, argv); | ||
1539 | perf_stat__reset_stats(evsel_list); | ||
1540 | } | ||
1273 | } | 1541 | } |
1274 | 1542 | ||
1275 | if (status != -1) | 1543 | if (!forever && status != -1 && !interval) |
1276 | print_stat(argc, argv); | 1544 | print_stat(argc, argv); |
1277 | out_free_fd: | 1545 | |
1278 | list_for_each_entry(pos, &evsel_list->entries, node) | 1546 | perf_evlist__free_stats(evsel_list); |
1279 | perf_evsel__free_stat_priv(pos); | 1547 | out_free_maps: |
1280 | perf_evlist__delete_maps(evsel_list); | 1548 | perf_evlist__delete_maps(evsel_list); |
1281 | out: | 1549 | out: |
1282 | perf_evlist__delete(evsel_list); | 1550 | perf_evlist__delete(evsel_list); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c9ff3950cd4b..f036af9b6f09 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -68,32 +68,13 @@ | |||
68 | #include <linux/unistd.h> | 68 | #include <linux/unistd.h> |
69 | #include <linux/types.h> | 69 | #include <linux/types.h> |
70 | 70 | ||
71 | void get_term_dimensions(struct winsize *ws) | 71 | static volatile int done; |
72 | { | 72 | |
73 | char *s = getenv("LINES"); | 73 | #define HEADER_LINE_NR 5 |
74 | |||
75 | if (s != NULL) { | ||
76 | ws->ws_row = atoi(s); | ||
77 | s = getenv("COLUMNS"); | ||
78 | if (s != NULL) { | ||
79 | ws->ws_col = atoi(s); | ||
80 | if (ws->ws_row && ws->ws_col) | ||
81 | return; | ||
82 | } | ||
83 | } | ||
84 | #ifdef TIOCGWINSZ | ||
85 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
86 | ws->ws_row && ws->ws_col) | ||
87 | return; | ||
88 | #endif | ||
89 | ws->ws_row = 25; | ||
90 | ws->ws_col = 80; | ||
91 | } | ||
92 | 74 | ||
93 | static void perf_top__update_print_entries(struct perf_top *top) | 75 | static void perf_top__update_print_entries(struct perf_top *top) |
94 | { | 76 | { |
95 | if (top->print_entries > 9) | 77 | top->print_entries = top->winsize.ws_row - HEADER_LINE_NR; |
96 | top->print_entries -= 9; | ||
97 | } | 78 | } |
98 | 79 | ||
99 | static void perf_top__sig_winch(int sig __maybe_unused, | 80 | static void perf_top__sig_winch(int sig __maybe_unused, |
@@ -102,13 +83,6 @@ static void perf_top__sig_winch(int sig __maybe_unused, | |||
102 | struct perf_top *top = arg; | 83 | struct perf_top *top = arg; |
103 | 84 | ||
104 | get_term_dimensions(&top->winsize); | 85 | get_term_dimensions(&top->winsize); |
105 | if (!top->print_entries | ||
106 | || (top->print_entries+4) > top->winsize.ws_row) { | ||
107 | top->print_entries = top->winsize.ws_row; | ||
108 | } else { | ||
109 | top->print_entries += 4; | ||
110 | top->winsize.ws_row = top->print_entries; | ||
111 | } | ||
112 | perf_top__update_print_entries(top); | 86 | perf_top__update_print_entries(top); |
113 | } | 87 | } |
114 | 88 | ||
@@ -251,7 +225,7 @@ static void perf_top__show_details(struct perf_top *top) | |||
251 | printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name); | 225 | printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name); |
252 | printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); | 226 | printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); |
253 | 227 | ||
254 | more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, | 228 | more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel, |
255 | 0, top->sym_pcnt_filter, top->print_entries, 4); | 229 | 0, top->sym_pcnt_filter, top->print_entries, 4); |
256 | if (top->zero) | 230 | if (top->zero) |
257 | symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); | 231 | symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); |
@@ -271,7 +245,11 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
271 | { | 245 | { |
272 | struct hist_entry *he; | 246 | struct hist_entry *he; |
273 | 247 | ||
274 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); | 248 | pthread_mutex_lock(&evsel->hists.lock); |
249 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, | ||
250 | sample->weight); | ||
251 | pthread_mutex_unlock(&evsel->hists.lock); | ||
252 | |||
275 | if (he == NULL) | 253 | if (he == NULL) |
276 | return NULL; | 254 | return NULL; |
277 | 255 | ||
@@ -309,16 +287,17 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
309 | return; | 287 | return; |
310 | } | 288 | } |
311 | 289 | ||
312 | hists__collapse_resort_threaded(&top->sym_evsel->hists); | 290 | hists__collapse_resort(&top->sym_evsel->hists); |
313 | hists__output_resort_threaded(&top->sym_evsel->hists); | 291 | hists__output_resort(&top->sym_evsel->hists); |
314 | hists__decay_entries_threaded(&top->sym_evsel->hists, | 292 | hists__decay_entries(&top->sym_evsel->hists, |
315 | top->hide_user_symbols, | 293 | top->hide_user_symbols, |
316 | top->hide_kernel_symbols); | 294 | top->hide_kernel_symbols); |
317 | hists__output_recalc_col_len(&top->sym_evsel->hists, | 295 | hists__output_recalc_col_len(&top->sym_evsel->hists, |
318 | top->winsize.ws_row - 3); | 296 | top->print_entries - printed); |
319 | putchar('\n'); | 297 | putchar('\n'); |
320 | hists__fprintf(&top->sym_evsel->hists, false, | 298 | hists__fprintf(&top->sym_evsel->hists, false, |
321 | top->winsize.ws_row - 4 - printed, win_width, stdout); | 299 | top->print_entries - printed, win_width, |
300 | top->min_percent, stdout); | ||
322 | } | 301 | } |
323 | 302 | ||
324 | static void prompt_integer(int *target, const char *msg) | 303 | static void prompt_integer(int *target, const char *msg) |
@@ -453,8 +432,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c) | |||
453 | return 0; | 432 | return 0; |
454 | } | 433 | } |
455 | 434 | ||
456 | static void perf_top__handle_keypress(struct perf_top *top, int c) | 435 | static bool perf_top__handle_keypress(struct perf_top *top, int c) |
457 | { | 436 | { |
437 | bool ret = true; | ||
438 | |||
458 | if (!perf_top__key_mapped(top, c)) { | 439 | if (!perf_top__key_mapped(top, c)) { |
459 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 440 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
460 | struct termios tc, save; | 441 | struct termios tc, save; |
@@ -475,7 +456,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
475 | 456 | ||
476 | tcsetattr(0, TCSAFLUSH, &save); | 457 | tcsetattr(0, TCSAFLUSH, &save); |
477 | if (!perf_top__key_mapped(top, c)) | 458 | if (!perf_top__key_mapped(top, c)) |
478 | return; | 459 | return ret; |
479 | } | 460 | } |
480 | 461 | ||
481 | switch (c) { | 462 | switch (c) { |
@@ -494,7 +475,6 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
494 | perf_top__sig_winch(SIGWINCH, NULL, top); | 475 | perf_top__sig_winch(SIGWINCH, NULL, top); |
495 | sigaction(SIGWINCH, &act, NULL); | 476 | sigaction(SIGWINCH, &act, NULL); |
496 | } else { | 477 | } else { |
497 | perf_top__sig_winch(SIGWINCH, NULL, top); | ||
498 | signal(SIGWINCH, SIG_DFL); | 478 | signal(SIGWINCH, SIG_DFL); |
499 | } | 479 | } |
500 | break; | 480 | break; |
@@ -537,7 +517,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
537 | printf("exiting.\n"); | 517 | printf("exiting.\n"); |
538 | if (top->dump_symtab) | 518 | if (top->dump_symtab) |
539 | perf_session__fprintf_dsos(top->session, stderr); | 519 | perf_session__fprintf_dsos(top->session, stderr); |
540 | exit(0); | 520 | ret = false; |
521 | break; | ||
541 | case 's': | 522 | case 's': |
542 | perf_top__prompt_symbol(top, "Enter details symbol"); | 523 | perf_top__prompt_symbol(top, "Enter details symbol"); |
543 | break; | 524 | break; |
@@ -560,6 +541,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
560 | default: | 541 | default: |
561 | break; | 542 | break; |
562 | } | 543 | } |
544 | |||
545 | return ret; | ||
563 | } | 546 | } |
564 | 547 | ||
565 | static void perf_top__sort_new_samples(void *arg) | 548 | static void perf_top__sort_new_samples(void *arg) |
@@ -570,11 +553,11 @@ static void perf_top__sort_new_samples(void *arg) | |||
570 | if (t->evlist->selected != NULL) | 553 | if (t->evlist->selected != NULL) |
571 | t->sym_evsel = t->evlist->selected; | 554 | t->sym_evsel = t->evlist->selected; |
572 | 555 | ||
573 | hists__collapse_resort_threaded(&t->sym_evsel->hists); | 556 | hists__collapse_resort(&t->sym_evsel->hists); |
574 | hists__output_resort_threaded(&t->sym_evsel->hists); | 557 | hists__output_resort(&t->sym_evsel->hists); |
575 | hists__decay_entries_threaded(&t->sym_evsel->hists, | 558 | hists__decay_entries(&t->sym_evsel->hists, |
576 | t->hide_user_symbols, | 559 | t->hide_user_symbols, |
577 | t->hide_kernel_symbols); | 560 | t->hide_kernel_symbols); |
578 | } | 561 | } |
579 | 562 | ||
580 | static void *display_thread_tui(void *arg) | 563 | static void *display_thread_tui(void *arg) |
@@ -596,13 +579,12 @@ static void *display_thread_tui(void *arg) | |||
596 | * via --uid. | 579 | * via --uid. |
597 | */ | 580 | */ |
598 | list_for_each_entry(pos, &top->evlist->entries, node) | 581 | list_for_each_entry(pos, &top->evlist->entries, node) |
599 | pos->hists.uid_filter_str = top->target.uid_str; | 582 | pos->hists.uid_filter_str = top->record_opts.target.uid_str; |
600 | 583 | ||
601 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, | 584 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, |
602 | &top->session->header.env); | 585 | &top->session->header.env); |
603 | 586 | ||
604 | exit_browser(0); | 587 | done = 1; |
605 | exit(0); | ||
606 | return NULL; | 588 | return NULL; |
607 | } | 589 | } |
608 | 590 | ||
@@ -626,7 +608,7 @@ repeat: | |||
626 | /* trash return*/ | 608 | /* trash return*/ |
627 | getc(stdin); | 609 | getc(stdin); |
628 | 610 | ||
629 | while (1) { | 611 | while (!done) { |
630 | perf_top__print_sym_table(top); | 612 | perf_top__print_sym_table(top); |
631 | /* | 613 | /* |
632 | * Either timeout expired or we got an EINTR due to SIGWINCH, | 614 | * Either timeout expired or we got an EINTR due to SIGWINCH, |
@@ -640,15 +622,14 @@ repeat: | |||
640 | continue; | 622 | continue; |
641 | /* Fall trhu */ | 623 | /* Fall trhu */ |
642 | default: | 624 | default: |
643 | goto process_hotkey; | 625 | c = getc(stdin); |
626 | tcsetattr(0, TCSAFLUSH, &save); | ||
627 | |||
628 | if (perf_top__handle_keypress(top, c)) | ||
629 | goto repeat; | ||
630 | done = 1; | ||
644 | } | 631 | } |
645 | } | 632 | } |
646 | process_hotkey: | ||
647 | c = getc(stdin); | ||
648 | tcsetattr(0, TCSAFLUSH, &save); | ||
649 | |||
650 | perf_top__handle_keypress(top, c); | ||
651 | goto repeat; | ||
652 | 633 | ||
653 | return NULL; | 634 | return NULL; |
654 | } | 635 | } |
@@ -716,7 +697,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
716 | static struct intlist *seen; | 697 | static struct intlist *seen; |
717 | 698 | ||
718 | if (!seen) | 699 | if (!seen) |
719 | seen = intlist__new(); | 700 | seen = intlist__new(NULL); |
720 | 701 | ||
721 | if (!intlist__has_entry(seen, event->ip.pid)) { | 702 | if (!intlist__has_entry(seen, event->ip.pid)) { |
722 | pr_err("Can't find guest [%d]'s kernel information\n", | 703 | pr_err("Can't find guest [%d]'s kernel information\n", |
@@ -727,8 +708,8 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
727 | } | 708 | } |
728 | 709 | ||
729 | if (!machine) { | 710 | if (!machine) { |
730 | pr_err("%u unprocessable samples recorded.", | 711 | pr_err("%u unprocessable samples recorded.\r", |
731 | top->session->hists.stats.nr_unprocessable_samples++); | 712 | top->session->stats.nr_unprocessable_samples++); |
732 | return; | 713 | return; |
733 | } | 714 | } |
734 | 715 | ||
@@ -810,7 +791,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
810 | return; | 791 | return; |
811 | } | 792 | } |
812 | 793 | ||
813 | if (top->sort_has_symbols) | 794 | if (sort__has_sym) |
814 | perf_top__record_precise_ip(top, he, evsel->idx, ip); | 795 | perf_top__record_precise_ip(top, he, evsel->idx, ip); |
815 | } | 796 | } |
816 | 797 | ||
@@ -847,13 +828,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
847 | ++top->us_samples; | 828 | ++top->us_samples; |
848 | if (top->hide_user_symbols) | 829 | if (top->hide_user_symbols) |
849 | continue; | 830 | continue; |
850 | machine = perf_session__find_host_machine(session); | 831 | machine = &session->machines.host; |
851 | break; | 832 | break; |
852 | case PERF_RECORD_MISC_KERNEL: | 833 | case PERF_RECORD_MISC_KERNEL: |
853 | ++top->kernel_samples; | 834 | ++top->kernel_samples; |
854 | if (top->hide_kernel_symbols) | 835 | if (top->hide_kernel_symbols) |
855 | continue; | 836 | continue; |
856 | machine = perf_session__find_host_machine(session); | 837 | machine = &session->machines.host; |
857 | break; | 838 | break; |
858 | case PERF_RECORD_MISC_GUEST_KERNEL: | 839 | case PERF_RECORD_MISC_GUEST_KERNEL: |
859 | ++top->guest_kernel_samples; | 840 | ++top->guest_kernel_samples; |
@@ -878,7 +859,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
878 | hists__inc_nr_events(&evsel->hists, event->header.type); | 859 | hists__inc_nr_events(&evsel->hists, event->header.type); |
879 | machine__process_event(machine, event); | 860 | machine__process_event(machine, event); |
880 | } else | 861 | } else |
881 | ++session->hists.stats.nr_unknown_events; | 862 | ++session->stats.nr_unknown_events; |
882 | } | 863 | } |
883 | } | 864 | } |
884 | 865 | ||
@@ -890,133 +871,52 @@ static void perf_top__mmap_read(struct perf_top *top) | |||
890 | perf_top__mmap_read_idx(top, i); | 871 | perf_top__mmap_read_idx(top, i); |
891 | } | 872 | } |
892 | 873 | ||
893 | static void perf_top__start_counters(struct perf_top *top) | 874 | static int perf_top__start_counters(struct perf_top *top) |
894 | { | 875 | { |
876 | char msg[512]; | ||
895 | struct perf_evsel *counter; | 877 | struct perf_evsel *counter; |
896 | struct perf_evlist *evlist = top->evlist; | 878 | struct perf_evlist *evlist = top->evlist; |
879 | struct perf_record_opts *opts = &top->record_opts; | ||
897 | 880 | ||
898 | if (top->group) | 881 | perf_evlist__config(evlist, opts); |
899 | perf_evlist__set_leader(evlist); | ||
900 | 882 | ||
901 | list_for_each_entry(counter, &evlist->entries, node) { | 883 | list_for_each_entry(counter, &evlist->entries, node) { |
902 | struct perf_event_attr *attr = &counter->attr; | ||
903 | |||
904 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | ||
905 | |||
906 | if (top->freq) { | ||
907 | attr->sample_type |= PERF_SAMPLE_PERIOD; | ||
908 | attr->freq = 1; | ||
909 | attr->sample_freq = top->freq; | ||
910 | } | ||
911 | |||
912 | if (evlist->nr_entries > 1) { | ||
913 | attr->sample_type |= PERF_SAMPLE_ID; | ||
914 | attr->read_format |= PERF_FORMAT_ID; | ||
915 | } | ||
916 | |||
917 | if (perf_target__has_cpu(&top->target)) | ||
918 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
919 | |||
920 | if (symbol_conf.use_callchain) | ||
921 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
922 | |||
923 | attr->mmap = 1; | ||
924 | attr->comm = 1; | ||
925 | attr->inherit = top->inherit; | ||
926 | fallback_missing_features: | ||
927 | if (top->exclude_guest_missing) | ||
928 | attr->exclude_guest = attr->exclude_host = 0; | ||
929 | retry_sample_id: | ||
930 | attr->sample_id_all = top->sample_id_all_missing ? 0 : 1; | ||
931 | try_again: | 884 | try_again: |
932 | if (perf_evsel__open(counter, top->evlist->cpus, | 885 | if (perf_evsel__open(counter, top->evlist->cpus, |
933 | top->evlist->threads) < 0) { | 886 | top->evlist->threads) < 0) { |
934 | int err = errno; | 887 | if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { |
935 | |||
936 | if (err == EPERM || err == EACCES) { | ||
937 | ui__error_paranoid(); | ||
938 | goto out_err; | ||
939 | } else if (err == EINVAL) { | ||
940 | if (!top->exclude_guest_missing && | ||
941 | (attr->exclude_guest || attr->exclude_host)) { | ||
942 | pr_debug("Old kernel, cannot exclude " | ||
943 | "guest or host samples.\n"); | ||
944 | top->exclude_guest_missing = true; | ||
945 | goto fallback_missing_features; | ||
946 | } else if (!top->sample_id_all_missing) { | ||
947 | /* | ||
948 | * Old kernel, no attr->sample_id_type_all field | ||
949 | */ | ||
950 | top->sample_id_all_missing = true; | ||
951 | goto retry_sample_id; | ||
952 | } | ||
953 | } | ||
954 | /* | ||
955 | * If it's cycles then fall back to hrtimer | ||
956 | * based cpu-clock-tick sw counter, which | ||
957 | * is always available even if no PMU support: | ||
958 | */ | ||
959 | if ((err == ENOENT || err == ENXIO) && | ||
960 | (attr->type == PERF_TYPE_HARDWARE) && | ||
961 | (attr->config == PERF_COUNT_HW_CPU_CYCLES)) { | ||
962 | |||
963 | if (verbose) | 888 | if (verbose) |
964 | ui__warning("Cycles event not supported,\n" | 889 | ui__warning("%s\n", msg); |
965 | "trying to fall back to cpu-clock-ticks\n"); | ||
966 | |||
967 | attr->type = PERF_TYPE_SOFTWARE; | ||
968 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | ||
969 | if (counter->name) { | ||
970 | free(counter->name); | ||
971 | counter->name = NULL; | ||
972 | } | ||
973 | goto try_again; | 890 | goto try_again; |
974 | } | 891 | } |
975 | 892 | ||
976 | if (err == ENOENT) { | 893 | perf_evsel__open_strerror(counter, &opts->target, |
977 | ui__error("The %s event is not supported.\n", | 894 | errno, msg, sizeof(msg)); |
978 | perf_evsel__name(counter)); | 895 | ui__error("%s\n", msg); |
979 | goto out_err; | ||
980 | } else if (err == EMFILE) { | ||
981 | ui__error("Too many events are opened.\n" | ||
982 | "Try again after reducing the number of events\n"); | ||
983 | goto out_err; | ||
984 | } else if ((err == EOPNOTSUPP) && (attr->precise_ip)) { | ||
985 | ui__error("\'precise\' request may not be supported. " | ||
986 | "Try removing 'p' modifier\n"); | ||
987 | goto out_err; | ||
988 | } | ||
989 | |||
990 | ui__error("The sys_perf_event_open() syscall " | ||
991 | "returned with %d (%s). /bin/dmesg " | ||
992 | "may provide additional information.\n" | ||
993 | "No CONFIG_PERF_EVENTS=y kernel support " | ||
994 | "configured?\n", err, strerror(err)); | ||
995 | goto out_err; | 896 | goto out_err; |
996 | } | 897 | } |
997 | } | 898 | } |
998 | 899 | ||
999 | if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) { | 900 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
1000 | ui__error("Failed to mmap with %d (%s)\n", | 901 | ui__error("Failed to mmap with %d (%s)\n", |
1001 | errno, strerror(errno)); | 902 | errno, strerror(errno)); |
1002 | goto out_err; | 903 | goto out_err; |
1003 | } | 904 | } |
1004 | 905 | ||
1005 | return; | 906 | return 0; |
1006 | 907 | ||
1007 | out_err: | 908 | out_err: |
1008 | exit_browser(0); | 909 | return -1; |
1009 | exit(0); | ||
1010 | } | 910 | } |
1011 | 911 | ||
1012 | static int perf_top__setup_sample_type(struct perf_top *top) | 912 | static int perf_top__setup_sample_type(struct perf_top *top __maybe_unused) |
1013 | { | 913 | { |
1014 | if (!top->sort_has_symbols) { | 914 | if (!sort__has_sym) { |
1015 | if (symbol_conf.use_callchain) { | 915 | if (symbol_conf.use_callchain) { |
1016 | ui__error("Selected -g but \"sym\" not present in --sort/-s."); | 916 | ui__error("Selected -g but \"sym\" not present in --sort/-s."); |
1017 | return -EINVAL; | 917 | return -EINVAL; |
1018 | } | 918 | } |
1019 | } else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | 919 | } else if (callchain_param.mode != CHAIN_NONE) { |
1020 | if (callchain_register_param(&callchain_param) < 0) { | 920 | if (callchain_register_param(&callchain_param) < 0) { |
1021 | ui__error("Can't register callchain params.\n"); | 921 | ui__error("Can't register callchain params.\n"); |
1022 | return -EINVAL; | 922 | return -EINVAL; |
@@ -1028,6 +928,7 @@ static int perf_top__setup_sample_type(struct perf_top *top) | |||
1028 | 928 | ||
1029 | static int __cmd_top(struct perf_top *top) | 929 | static int __cmd_top(struct perf_top *top) |
1030 | { | 930 | { |
931 | struct perf_record_opts *opts = &top->record_opts; | ||
1031 | pthread_t thread; | 932 | pthread_t thread; |
1032 | int ret; | 933 | int ret; |
1033 | /* | 934 | /* |
@@ -1042,26 +943,42 @@ static int __cmd_top(struct perf_top *top) | |||
1042 | if (ret) | 943 | if (ret) |
1043 | goto out_delete; | 944 | goto out_delete; |
1044 | 945 | ||
1045 | if (perf_target__has_task(&top->target)) | 946 | if (perf_target__has_task(&opts->target)) |
1046 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, | 947 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, |
1047 | perf_event__process, | 948 | perf_event__process, |
1048 | &top->session->host_machine); | 949 | &top->session->machines.host); |
1049 | else | 950 | else |
1050 | perf_event__synthesize_threads(&top->tool, perf_event__process, | 951 | perf_event__synthesize_threads(&top->tool, perf_event__process, |
1051 | &top->session->host_machine); | 952 | &top->session->machines.host); |
1052 | perf_top__start_counters(top); | 953 | |
954 | ret = perf_top__start_counters(top); | ||
955 | if (ret) | ||
956 | goto out_delete; | ||
957 | |||
1053 | top->session->evlist = top->evlist; | 958 | top->session->evlist = top->evlist; |
1054 | perf_session__set_id_hdr_size(top->session); | 959 | perf_session__set_id_hdr_size(top->session); |
1055 | 960 | ||
961 | /* | ||
962 | * When perf is starting the traced process, all the events (apart from | ||
963 | * group members) have enable_on_exec=1 set, so don't spoil it by | ||
964 | * prematurely enabling them. | ||
965 | * | ||
966 | * XXX 'top' still doesn't start workloads like record, trace, but should, | ||
967 | * so leave the check here. | ||
968 | */ | ||
969 | if (!perf_target__none(&opts->target)) | ||
970 | perf_evlist__enable(top->evlist); | ||
971 | |||
1056 | /* Wait for a minimal set of events before starting the snapshot */ | 972 | /* Wait for a minimal set of events before starting the snapshot */ |
1057 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 973 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
1058 | 974 | ||
1059 | perf_top__mmap_read(top); | 975 | perf_top__mmap_read(top); |
1060 | 976 | ||
977 | ret = -1; | ||
1061 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 978 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
1062 | display_thread), top)) { | 979 | display_thread), top)) { |
1063 | ui__error("Could not create display thread.\n"); | 980 | ui__error("Could not create display thread.\n"); |
1064 | exit(-1); | 981 | goto out_delete; |
1065 | } | 982 | } |
1066 | 983 | ||
1067 | if (top->realtime_prio) { | 984 | if (top->realtime_prio) { |
@@ -1070,11 +987,11 @@ static int __cmd_top(struct perf_top *top) | |||
1070 | param.sched_priority = top->realtime_prio; | 987 | param.sched_priority = top->realtime_prio; |
1071 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 988 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
1072 | ui__error("Could not set realtime priority.\n"); | 989 | ui__error("Could not set realtime priority.\n"); |
1073 | exit(-1); | 990 | goto out_delete; |
1074 | } | 991 | } |
1075 | } | 992 | } |
1076 | 993 | ||
1077 | while (1) { | 994 | while (!done) { |
1078 | u64 hits = top->samples; | 995 | u64 hits = top->samples; |
1079 | 996 | ||
1080 | perf_top__mmap_read(top); | 997 | perf_top__mmap_read(top); |
@@ -1083,126 +1000,77 @@ static int __cmd_top(struct perf_top *top) | |||
1083 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 1000 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
1084 | } | 1001 | } |
1085 | 1002 | ||
1003 | ret = 0; | ||
1086 | out_delete: | 1004 | out_delete: |
1087 | perf_session__delete(top->session); | 1005 | perf_session__delete(top->session); |
1088 | top->session = NULL; | 1006 | top->session = NULL; |
1089 | 1007 | ||
1090 | return 0; | 1008 | return ret; |
1091 | } | 1009 | } |
1092 | 1010 | ||
1093 | static int | 1011 | static int |
1094 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | 1012 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) |
1095 | { | 1013 | { |
1096 | struct perf_top *top = (struct perf_top *)opt->value; | ||
1097 | char *tok, *tok2; | ||
1098 | char *endptr; | ||
1099 | |||
1100 | /* | 1014 | /* |
1101 | * --no-call-graph | 1015 | * --no-call-graph |
1102 | */ | 1016 | */ |
1103 | if (unset) { | 1017 | if (unset) |
1104 | top->dont_use_callchains = true; | ||
1105 | return 0; | 1018 | return 0; |
1106 | } | ||
1107 | 1019 | ||
1108 | symbol_conf.use_callchain = true; | 1020 | symbol_conf.use_callchain = true; |
1109 | 1021 | ||
1110 | if (!arg) | 1022 | return record_parse_callchain_opt(opt, arg, unset); |
1111 | return 0; | 1023 | } |
1112 | |||
1113 | tok = strtok((char *)arg, ","); | ||
1114 | if (!tok) | ||
1115 | return -1; | ||
1116 | |||
1117 | /* get the output mode */ | ||
1118 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1119 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1120 | |||
1121 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1122 | callchain_param.mode = CHAIN_FLAT; | ||
1123 | |||
1124 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1125 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1126 | |||
1127 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
1128 | callchain_param.mode = CHAIN_NONE; | ||
1129 | symbol_conf.use_callchain = false; | ||
1130 | |||
1131 | return 0; | ||
1132 | } else | ||
1133 | return -1; | ||
1134 | |||
1135 | /* get the min percentage */ | ||
1136 | tok = strtok(NULL, ","); | ||
1137 | if (!tok) | ||
1138 | goto setup; | ||
1139 | |||
1140 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1141 | if (tok == endptr) | ||
1142 | return -1; | ||
1143 | |||
1144 | /* get the print limit */ | ||
1145 | tok2 = strtok(NULL, ","); | ||
1146 | if (!tok2) | ||
1147 | goto setup; | ||
1148 | 1024 | ||
1149 | if (tok2[0] != 'c') { | 1025 | static int |
1150 | callchain_param.print_limit = strtod(tok2, &endptr); | 1026 | parse_percent_limit(const struct option *opt, const char *arg, |
1151 | tok2 = strtok(NULL, ","); | 1027 | int unset __maybe_unused) |
1152 | if (!tok2) | 1028 | { |
1153 | goto setup; | 1029 | struct perf_top *top = opt->value; |
1154 | } | ||
1155 | 1030 | ||
1156 | /* get the call chain order */ | 1031 | top->min_percent = strtof(arg, NULL); |
1157 | if (!strcmp(tok2, "caller")) | ||
1158 | callchain_param.order = ORDER_CALLER; | ||
1159 | else if (!strcmp(tok2, "callee")) | ||
1160 | callchain_param.order = ORDER_CALLEE; | ||
1161 | else | ||
1162 | return -1; | ||
1163 | setup: | ||
1164 | if (callchain_register_param(&callchain_param) < 0) { | ||
1165 | fprintf(stderr, "Can't register callchain params\n"); | ||
1166 | return -1; | ||
1167 | } | ||
1168 | return 0; | 1032 | return 0; |
1169 | } | 1033 | } |
1170 | 1034 | ||
1171 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | 1035 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) |
1172 | { | 1036 | { |
1173 | struct perf_evsel *pos; | ||
1174 | int status; | 1037 | int status; |
1175 | char errbuf[BUFSIZ]; | 1038 | char errbuf[BUFSIZ]; |
1176 | struct perf_top top = { | 1039 | struct perf_top top = { |
1177 | .count_filter = 5, | 1040 | .count_filter = 5, |
1178 | .delay_secs = 2, | 1041 | .delay_secs = 2, |
1179 | .freq = 4000, /* 4 KHz */ | 1042 | .record_opts = { |
1180 | .mmap_pages = 128, | 1043 | .mmap_pages = UINT_MAX, |
1181 | .sym_pcnt_filter = 5, | 1044 | .user_freq = UINT_MAX, |
1182 | .target = { | 1045 | .user_interval = ULLONG_MAX, |
1183 | .uses_mmap = true, | 1046 | .freq = 4000, /* 4 KHz */ |
1047 | .target = { | ||
1048 | .uses_mmap = true, | ||
1049 | }, | ||
1184 | }, | 1050 | }, |
1051 | .sym_pcnt_filter = 5, | ||
1185 | }; | 1052 | }; |
1186 | char callchain_default_opt[] = "fractal,0.5,callee"; | 1053 | struct perf_record_opts *opts = &top.record_opts; |
1054 | struct perf_target *target = &opts->target; | ||
1187 | const struct option options[] = { | 1055 | const struct option options[] = { |
1188 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 1056 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1189 | "event selector. use 'perf list' to list available events", | 1057 | "event selector. use 'perf list' to list available events", |
1190 | parse_events_option), | 1058 | parse_events_option), |
1191 | OPT_INTEGER('c', "count", &top.default_interval, | 1059 | OPT_U64('c', "count", &opts->user_interval, "event period to sample"), |
1192 | "event period to sample"), | 1060 | OPT_STRING('p', "pid", &target->pid, "pid", |
1193 | OPT_STRING('p', "pid", &top.target.pid, "pid", | ||
1194 | "profile events on existing process id"), | 1061 | "profile events on existing process id"), |
1195 | OPT_STRING('t', "tid", &top.target.tid, "tid", | 1062 | OPT_STRING('t', "tid", &target->tid, "tid", |
1196 | "profile events on existing thread id"), | 1063 | "profile events on existing thread id"), |
1197 | OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide, | 1064 | OPT_BOOLEAN('a', "all-cpus", &target->system_wide, |
1198 | "system-wide collection from all CPUs"), | 1065 | "system-wide collection from all CPUs"), |
1199 | OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu", | 1066 | OPT_STRING('C', "cpu", &target->cpu_list, "cpu", |
1200 | "list of cpus to monitor"), | 1067 | "list of cpus to monitor"), |
1201 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 1068 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1202 | "file", "vmlinux pathname"), | 1069 | "file", "vmlinux pathname"), |
1203 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, | 1070 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, |
1204 | "hide kernel symbols"), | 1071 | "hide kernel symbols"), |
1205 | OPT_UINTEGER('m', "mmap-pages", &top.mmap_pages, "number of mmap data pages"), | 1072 | OPT_UINTEGER('m', "mmap-pages", &opts->mmap_pages, |
1073 | "number of mmap data pages"), | ||
1206 | OPT_INTEGER('r', "realtime", &top.realtime_prio, | 1074 | OPT_INTEGER('r', "realtime", &top.realtime_prio, |
1207 | "collect data with this RT SCHED_FIFO priority"), | 1075 | "collect data with this RT SCHED_FIFO priority"), |
1208 | OPT_INTEGER('d', "delay", &top.delay_secs, | 1076 | OPT_INTEGER('d', "delay", &top.delay_secs, |
@@ -1211,16 +1079,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1211 | "dump the symbol table used for profiling"), | 1079 | "dump the symbol table used for profiling"), |
1212 | OPT_INTEGER('f', "count-filter", &top.count_filter, | 1080 | OPT_INTEGER('f', "count-filter", &top.count_filter, |
1213 | "only display functions with more events than this"), | 1081 | "only display functions with more events than this"), |
1214 | OPT_BOOLEAN('g', "group", &top.group, | 1082 | OPT_BOOLEAN('g', "group", &opts->group, |
1215 | "put the counters into a counter group"), | 1083 | "put the counters into a counter group"), |
1216 | OPT_BOOLEAN('i', "inherit", &top.inherit, | 1084 | OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit, |
1217 | "child tasks inherit counters"), | 1085 | "child tasks do not inherit counters"), |
1218 | OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", | 1086 | OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", |
1219 | "symbol to annotate"), | 1087 | "symbol to annotate"), |
1220 | OPT_BOOLEAN('z', "zero", &top.zero, | 1088 | OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"), |
1221 | "zero history across updates"), | 1089 | OPT_UINTEGER('F', "freq", &opts->user_freq, "profile at this frequency"), |
1222 | OPT_INTEGER('F', "freq", &top.freq, | ||
1223 | "profile at this frequency"), | ||
1224 | OPT_INTEGER('E', "entries", &top.print_entries, | 1090 | OPT_INTEGER('E', "entries", &top.print_entries, |
1225 | "display this many functions"), | 1091 | "display this many functions"), |
1226 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, | 1092 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, |
@@ -1230,13 +1096,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1230 | OPT_INCR('v', "verbose", &verbose, | 1096 | OPT_INCR('v', "verbose", &verbose, |
1231 | "be more verbose (show counter open errors, etc)"), | 1097 | "be more verbose (show counter open errors, etc)"), |
1232 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1098 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1233 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1099 | "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), |
1234 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1100 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1235 | "Show a column with the number of samples"), | 1101 | "Show a column with the number of samples"), |
1236 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top, "output_type,min_percent, call_order", | 1102 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, |
1237 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | 1103 | "mode[,dump_size]", record_callchain_help, |
1238 | "Default: fractal,0.5,callee", &parse_callchain_opt, | 1104 | &parse_callchain_opt, "fp"), |
1239 | callchain_default_opt), | ||
1240 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 1105 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
1241 | "Show a column with the sum of periods"), | 1106 | "Show a column with the sum of periods"), |
1242 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 1107 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
@@ -1251,7 +1116,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1251 | "Display raw encoding of assembly instructions (default)"), | 1116 | "Display raw encoding of assembly instructions (default)"), |
1252 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1117 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
1253 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1118 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
1254 | OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"), | 1119 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), |
1120 | OPT_CALLBACK(0, "percent-limit", &top, "percent", | ||
1121 | "Don't show entries under that percent", parse_percent_limit), | ||
1255 | OPT_END() | 1122 | OPT_END() |
1256 | }; | 1123 | }; |
1257 | const char * const top_usage[] = { | 1124 | const char * const top_usage[] = { |
@@ -1259,7 +1126,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1259 | NULL | 1126 | NULL |
1260 | }; | 1127 | }; |
1261 | 1128 | ||
1262 | top.evlist = perf_evlist__new(NULL, NULL); | 1129 | top.evlist = perf_evlist__new(); |
1263 | if (top.evlist == NULL) | 1130 | if (top.evlist == NULL) |
1264 | return -ENOMEM; | 1131 | return -ENOMEM; |
1265 | 1132 | ||
@@ -1272,7 +1139,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1272 | if (sort_order == default_sort_order) | 1139 | if (sort_order == default_sort_order) |
1273 | sort_order = "dso,symbol"; | 1140 | sort_order = "dso,symbol"; |
1274 | 1141 | ||
1275 | setup_sorting(top_usage, options); | 1142 | if (setup_sorting() < 0) |
1143 | usage_with_options(top_usage, options); | ||
1144 | |||
1145 | /* display thread wants entries to be collapsed in a different tree */ | ||
1146 | sort__need_collapse = 1; | ||
1276 | 1147 | ||
1277 | if (top.use_stdio) | 1148 | if (top.use_stdio) |
1278 | use_browser = 0; | 1149 | use_browser = 0; |
@@ -1281,33 +1152,33 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1281 | 1152 | ||
1282 | setup_browser(false); | 1153 | setup_browser(false); |
1283 | 1154 | ||
1284 | status = perf_target__validate(&top.target); | 1155 | status = perf_target__validate(target); |
1285 | if (status) { | 1156 | if (status) { |
1286 | perf_target__strerror(&top.target, status, errbuf, BUFSIZ); | 1157 | perf_target__strerror(target, status, errbuf, BUFSIZ); |
1287 | ui__warning("%s", errbuf); | 1158 | ui__warning("%s", errbuf); |
1288 | } | 1159 | } |
1289 | 1160 | ||
1290 | status = perf_target__parse_uid(&top.target); | 1161 | status = perf_target__parse_uid(target); |
1291 | if (status) { | 1162 | if (status) { |
1292 | int saved_errno = errno; | 1163 | int saved_errno = errno; |
1293 | 1164 | ||
1294 | perf_target__strerror(&top.target, status, errbuf, BUFSIZ); | 1165 | perf_target__strerror(target, status, errbuf, BUFSIZ); |
1295 | ui__error("%s", errbuf); | 1166 | ui__error("%s", errbuf); |
1296 | 1167 | ||
1297 | status = -saved_errno; | 1168 | status = -saved_errno; |
1298 | goto out_delete_evlist; | 1169 | goto out_delete_evlist; |
1299 | } | 1170 | } |
1300 | 1171 | ||
1301 | if (perf_target__none(&top.target)) | 1172 | if (perf_target__none(target)) |
1302 | top.target.system_wide = true; | 1173 | target->system_wide = true; |
1303 | 1174 | ||
1304 | if (perf_evlist__create_maps(top.evlist, &top.target) < 0) | 1175 | if (perf_evlist__create_maps(top.evlist, target) < 0) |
1305 | usage_with_options(top_usage, options); | 1176 | usage_with_options(top_usage, options); |
1306 | 1177 | ||
1307 | if (!top.evlist->nr_entries && | 1178 | if (!top.evlist->nr_entries && |
1308 | perf_evlist__add_default(top.evlist) < 0) { | 1179 | perf_evlist__add_default(top.evlist) < 0) { |
1309 | ui__error("Not enough memory for event selector list\n"); | 1180 | ui__error("Not enough memory for event selector list\n"); |
1310 | return -ENOMEM; | 1181 | goto out_delete_maps; |
1311 | } | 1182 | } |
1312 | 1183 | ||
1313 | symbol_conf.nr_events = top.evlist->nr_entries; | 1184 | symbol_conf.nr_events = top.evlist->nr_entries; |
@@ -1315,24 +1186,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1315 | if (top.delay_secs < 1) | 1186 | if (top.delay_secs < 1) |
1316 | top.delay_secs = 1; | 1187 | top.delay_secs = 1; |
1317 | 1188 | ||
1189 | if (opts->user_interval != ULLONG_MAX) | ||
1190 | opts->default_interval = opts->user_interval; | ||
1191 | if (opts->user_freq != UINT_MAX) | ||
1192 | opts->freq = opts->user_freq; | ||
1193 | |||
1318 | /* | 1194 | /* |
1319 | * User specified count overrides default frequency. | 1195 | * User specified count overrides default frequency. |
1320 | */ | 1196 | */ |
1321 | if (top.default_interval) | 1197 | if (opts->default_interval) |
1322 | top.freq = 0; | 1198 | opts->freq = 0; |
1323 | else if (top.freq) { | 1199 | else if (opts->freq) { |
1324 | top.default_interval = top.freq; | 1200 | opts->default_interval = opts->freq; |
1325 | } else { | 1201 | } else { |
1326 | ui__error("frequency and count are zero, aborting\n"); | 1202 | ui__error("frequency and count are zero, aborting\n"); |
1327 | exit(EXIT_FAILURE); | 1203 | status = -EINVAL; |
1328 | } | 1204 | goto out_delete_maps; |
1329 | |||
1330 | list_for_each_entry(pos, &top.evlist->entries, node) { | ||
1331 | /* | ||
1332 | * Fill in the ones not specifically initialized via -c: | ||
1333 | */ | ||
1334 | if (!pos->attr.sample_period) | ||
1335 | pos->attr.sample_period = top.default_interval; | ||
1336 | } | 1205 | } |
1337 | 1206 | ||
1338 | top.sym_evsel = perf_evlist__first(top.evlist); | 1207 | top.sym_evsel = perf_evlist__first(top.evlist); |
@@ -1343,15 +1212,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1343 | if (symbol__init() < 0) | 1212 | if (symbol__init() < 0) |
1344 | return -1; | 1213 | return -1; |
1345 | 1214 | ||
1346 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | 1215 | sort__setup_elide(stdout); |
1347 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | ||
1348 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
1349 | |||
1350 | /* | ||
1351 | * Avoid annotation data structures overhead when symbols aren't on the | ||
1352 | * sort list. | ||
1353 | */ | ||
1354 | top.sort_has_symbols = sort_sym.list.next != NULL; | ||
1355 | 1216 | ||
1356 | get_term_dimensions(&top.winsize); | 1217 | get_term_dimensions(&top.winsize); |
1357 | if (top.print_entries == 0) { | 1218 | if (top.print_entries == 0) { |
@@ -1365,6 +1226,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1365 | 1226 | ||
1366 | status = __cmd_top(&top); | 1227 | status = __cmd_top(&top); |
1367 | 1228 | ||
1229 | out_delete_maps: | ||
1230 | perf_evlist__delete_maps(top.evlist); | ||
1368 | out_delete_evlist: | 1231 | out_delete_evlist: |
1369 | perf_evlist__delete(top.evlist); | 1232 | perf_evlist__delete(top.evlist); |
1370 | 1233 | ||
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7932ffa29889..ab3ed4af1466 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -419,7 +419,7 @@ out_dump: | |||
419 | 419 | ||
420 | static int trace__run(struct trace *trace, int argc, const char **argv) | 420 | static int trace__run(struct trace *trace, int argc, const char **argv) |
421 | { | 421 | { |
422 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 422 | struct perf_evlist *evlist = perf_evlist__new(); |
423 | struct perf_evsel *evsel; | 423 | struct perf_evsel *evsel; |
424 | int err = -1, i; | 424 | int err = -1, i; |
425 | unsigned long before; | 425 | unsigned long before; |
@@ -452,32 +452,33 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
452 | err = trace__symbols_init(trace, evlist); | 452 | err = trace__symbols_init(trace, evlist); |
453 | if (err < 0) { | 453 | if (err < 0) { |
454 | printf("Problems initializing symbol libraries!\n"); | 454 | printf("Problems initializing symbol libraries!\n"); |
455 | goto out_delete_evlist; | 455 | goto out_delete_maps; |
456 | } | 456 | } |
457 | 457 | ||
458 | perf_evlist__config_attrs(evlist, &trace->opts); | 458 | perf_evlist__config(evlist, &trace->opts); |
459 | 459 | ||
460 | signal(SIGCHLD, sig_handler); | 460 | signal(SIGCHLD, sig_handler); |
461 | signal(SIGINT, sig_handler); | 461 | signal(SIGINT, sig_handler); |
462 | 462 | ||
463 | if (forks) { | 463 | if (forks) { |
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); | 464 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
465 | argv, false, false); | ||
465 | if (err < 0) { | 466 | if (err < 0) { |
466 | printf("Couldn't run the workload!\n"); | 467 | printf("Couldn't run the workload!\n"); |
467 | goto out_delete_evlist; | 468 | goto out_delete_maps; |
468 | } | 469 | } |
469 | } | 470 | } |
470 | 471 | ||
471 | err = perf_evlist__open(evlist); | 472 | err = perf_evlist__open(evlist); |
472 | if (err < 0) { | 473 | if (err < 0) { |
473 | printf("Couldn't create the events: %s\n", strerror(errno)); | 474 | printf("Couldn't create the events: %s\n", strerror(errno)); |
474 | goto out_delete_evlist; | 475 | goto out_delete_maps; |
475 | } | 476 | } |
476 | 477 | ||
477 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 478 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
478 | if (err < 0) { | 479 | if (err < 0) { |
479 | printf("Couldn't mmap the events: %s\n", strerror(errno)); | 480 | printf("Couldn't mmap the events: %s\n", strerror(errno)); |
480 | goto out_delete_evlist; | 481 | goto out_close_evlist; |
481 | } | 482 | } |
482 | 483 | ||
483 | perf_evlist__enable(evlist); | 484 | perf_evlist__enable(evlist); |
@@ -526,13 +527,6 @@ again: | |||
526 | continue; | 527 | continue; |
527 | } | 528 | } |
528 | 529 | ||
529 | if (sample.raw_data == NULL) { | ||
530 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | ||
531 | perf_evsel__name(evsel), sample.tid, | ||
532 | sample.cpu, sample.raw_size); | ||
533 | continue; | ||
534 | } | ||
535 | |||
536 | handler = evsel->handler.func; | 530 | handler = evsel->handler.func; |
537 | handler(trace, evsel, &sample); | 531 | handler(trace, evsel, &sample); |
538 | } | 532 | } |
@@ -540,7 +534,7 @@ again: | |||
540 | 534 | ||
541 | if (trace->nr_events == before) { | 535 | if (trace->nr_events == before) { |
542 | if (done) | 536 | if (done) |
543 | goto out_delete_evlist; | 537 | goto out_unmap_evlist; |
544 | 538 | ||
545 | poll(evlist->pollfd, evlist->nr_fds, -1); | 539 | poll(evlist->pollfd, evlist->nr_fds, -1); |
546 | } | 540 | } |
@@ -550,6 +544,12 @@ again: | |||
550 | 544 | ||
551 | goto again; | 545 | goto again; |
552 | 546 | ||
547 | out_unmap_evlist: | ||
548 | perf_evlist__munmap(evlist); | ||
549 | out_close_evlist: | ||
550 | perf_evlist__close(evlist); | ||
551 | out_delete_maps: | ||
552 | perf_evlist__delete_maps(evlist); | ||
553 | out_delete_evlist: | 553 | out_delete_evlist: |
554 | perf_evlist__delete(evlist); | 554 | perf_evlist__delete(evlist); |
555 | out: | 555 | out: |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 08143bd854c7..b210d62907e4 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -36,6 +36,7 @@ extern int cmd_kvm(int argc, const char **argv, const char *prefix); | |||
36 | extern int cmd_test(int argc, const char **argv, const char *prefix); | 36 | extern int cmd_test(int argc, const char **argv, const char *prefix); |
37 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | 37 | extern int cmd_trace(int argc, const char **argv, const char *prefix); |
38 | extern int cmd_inject(int argc, const char **argv, const char *prefix); | 38 | extern int cmd_inject(int argc, const char **argv, const char *prefix); |
39 | extern int cmd_mem(int argc, const char **argv, const char *prefix); | ||
39 | 40 | ||
40 | extern int find_scripts(char **scripts_array, char **scripts_path_array); | 41 | extern int find_scripts(char **scripts_array, char **scripts_path_array); |
41 | #endif | 42 | #endif |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 3e86bbd8c2d5..0906fc401c52 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -10,17 +10,18 @@ perf-buildid-list mainporcelain common | |||
10 | perf-diff mainporcelain common | 10 | perf-diff mainporcelain common |
11 | perf-evlist mainporcelain common | 11 | perf-evlist mainporcelain common |
12 | perf-inject mainporcelain common | 12 | perf-inject mainporcelain common |
13 | perf-kmem mainporcelain common | ||
14 | perf-kvm mainporcelain common | ||
13 | perf-list mainporcelain common | 15 | perf-list mainporcelain common |
14 | perf-sched mainporcelain common | 16 | perf-lock mainporcelain common |
17 | perf-mem mainporcelain common | ||
18 | perf-probe mainporcelain full | ||
15 | perf-record mainporcelain common | 19 | perf-record mainporcelain common |
16 | perf-report mainporcelain common | 20 | perf-report mainporcelain common |
21 | perf-sched mainporcelain common | ||
22 | perf-script mainporcelain common | ||
17 | perf-stat mainporcelain common | 23 | perf-stat mainporcelain common |
24 | perf-test mainporcelain common | ||
18 | perf-timechart mainporcelain common | 25 | perf-timechart mainporcelain common |
19 | perf-top mainporcelain common | 26 | perf-top mainporcelain common |
20 | perf-trace mainporcelain common | 27 | perf-trace mainporcelain common |
21 | perf-script mainporcelain common | ||
22 | perf-probe mainporcelain full | ||
23 | perf-kmem mainporcelain common | ||
24 | perf-lock mainporcelain common | ||
25 | perf-kvm mainporcelain common | ||
26 | perf-test mainporcelain common | ||
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile new file mode 100644 index 000000000000..f139dcd2796e --- /dev/null +++ b/tools/perf/config/Makefile | |||
@@ -0,0 +1,477 @@ | |||
1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
2 | |||
3 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ | ||
4 | -e s/arm.*/arm/ -e s/sa110/arm/ \ | ||
5 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ | ||
6 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ | ||
7 | -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) | ||
8 | NO_PERF_REGS := 1 | ||
9 | CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS) | ||
10 | |||
11 | # Additional ARCH settings for x86 | ||
12 | ifeq ($(ARCH),i386) | ||
13 | override ARCH := x86 | ||
14 | NO_PERF_REGS := 0 | ||
15 | LIBUNWIND_LIBS = -lunwind -lunwind-x86 | ||
16 | endif | ||
17 | |||
18 | ifeq ($(ARCH),x86_64) | ||
19 | override ARCH := x86 | ||
20 | IS_X86_64 := 0 | ||
21 | ifeq (, $(findstring m32,$(CFLAGS))) | ||
22 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) | ||
23 | endif | ||
24 | ifeq (${IS_X86_64}, 1) | ||
25 | RAW_ARCH := x86_64 | ||
26 | CFLAGS += -DARCH_X86_64 | ||
27 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S | ||
28 | endif | ||
29 | NO_PERF_REGS := 0 | ||
30 | LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 | ||
31 | endif | ||
32 | |||
33 | ifeq ($(NO_PERF_REGS),0) | ||
34 | CFLAGS += -DHAVE_PERF_REGS | ||
35 | endif | ||
36 | |||
37 | ifeq ($(src-perf),) | ||
38 | src-perf := $(srctree)/tools/perf | ||
39 | endif | ||
40 | |||
41 | ifeq ($(obj-perf),) | ||
42 | obj-perf := $(objtree) | ||
43 | endif | ||
44 | |||
45 | ifneq ($(obj-perf),) | ||
46 | obj-perf := $(abspath $(obj-perf))/ | ||
47 | endif | ||
48 | |||
49 | # include ARCH specific config | ||
50 | -include $(src-perf)/arch/$(ARCH)/Makefile | ||
51 | |||
52 | include $(src-perf)/config/feature-tests.mak | ||
53 | include $(src-perf)/config/utilities.mak | ||
54 | |||
55 | ifeq ($(call get-executable,$(FLEX)),) | ||
56 | dummy := $(error Error: $(FLEX) is missing on this system, please install it) | ||
57 | endif | ||
58 | |||
59 | ifeq ($(call get-executable,$(BISON)),) | ||
60 | dummy := $(error Error: $(BISON) is missing on this system, please install it) | ||
61 | endif | ||
62 | |||
63 | # Treat warnings as errors unless directed not to | ||
64 | ifneq ($(WERROR),0) | ||
65 | CFLAGS += -Werror | ||
66 | endif | ||
67 | |||
68 | ifeq ("$(origin DEBUG)", "command line") | ||
69 | PERF_DEBUG = $(DEBUG) | ||
70 | endif | ||
71 | ifndef PERF_DEBUG | ||
72 | CFLAGS += -O6 | ||
73 | endif | ||
74 | |||
75 | ifdef PARSER_DEBUG | ||
76 | PARSER_DEBUG_BISON := -t | ||
77 | PARSER_DEBUG_FLEX := -d | ||
78 | CFLAGS += -DPARSER_DEBUG | ||
79 | endif | ||
80 | |||
81 | CFLAGS += -fno-omit-frame-pointer | ||
82 | CFLAGS += -ggdb3 | ||
83 | CFLAGS += -funwind-tables | ||
84 | CFLAGS += -Wall | ||
85 | CFLAGS += -Wextra | ||
86 | CFLAGS += -std=gnu99 | ||
87 | |||
88 | EXTLIBS = -lpthread -lrt -lelf -lm | ||
89 | |||
90 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) | ||
91 | CFLAGS += -fstack-protector-all | ||
92 | endif | ||
93 | |||
94 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) | ||
95 | CFLAGS += -Wstack-protector | ||
96 | endif | ||
97 | |||
98 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) | ||
99 | CFLAGS += -Wvolatile-register-var | ||
100 | endif | ||
101 | |||
102 | ifndef PERF_DEBUG | ||
103 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -D_FORTIFY_SOURCE=2,-D_FORTIFY_SOURCE=2),y) | ||
104 | CFLAGS += -D_FORTIFY_SOURCE=2 | ||
105 | endif | ||
106 | endif | ||
107 | |||
108 | CFLAGS += -I$(src-perf)/util/include | ||
109 | CFLAGS += -I$(src-perf)/arch/$(ARCH)/include | ||
110 | CFLAGS += -I$(srctree)/arch/$(ARCH)/include/uapi | ||
111 | CFLAGS += -I$(srctree)/arch/$(ARCH)/include | ||
112 | CFLAGS += -I$(srctree)/include/uapi | ||
113 | CFLAGS += -I$(srctree)/include | ||
114 | |||
115 | # $(obj-perf) for generated common-cmds.h | ||
116 | # $(obj-perf)/util for generated bison/flex headers | ||
117 | ifneq ($(OUTPUT),) | ||
118 | CFLAGS += -I$(obj-perf)/util | ||
119 | CFLAGS += -I$(obj-perf) | ||
120 | endif | ||
121 | |||
122 | CFLAGS += -I$(src-perf)/util | ||
123 | CFLAGS += -I$(src-perf) | ||
124 | CFLAGS += -I$(TRACE_EVENT_DIR) | ||
125 | CFLAGS += -I$(srctree)/tools/lib/ | ||
126 | |||
127 | CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | ||
128 | |||
129 | ifndef NO_BIONIC | ||
130 | ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) | ||
131 | BIONIC := 1 | ||
132 | EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) | ||
133 | EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) | ||
134 | endif | ||
135 | endif # NO_BIONIC | ||
136 | |||
137 | ifdef NO_LIBELF | ||
138 | NO_DWARF := 1 | ||
139 | NO_DEMANGLE := 1 | ||
140 | NO_LIBUNWIND := 1 | ||
141 | else | ||
142 | FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) | ||
143 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) | ||
144 | FLAGS_GLIBC=$(CFLAGS) $(LDFLAGS) | ||
145 | ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) | ||
146 | LIBC_SUPPORT := 1 | ||
147 | endif | ||
148 | ifeq ($(BIONIC),1) | ||
149 | LIBC_SUPPORT := 1 | ||
150 | endif | ||
151 | ifeq ($(LIBC_SUPPORT),1) | ||
152 | msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); | ||
153 | |||
154 | NO_LIBELF := 1 | ||
155 | NO_DWARF := 1 | ||
156 | NO_DEMANGLE := 1 | ||
157 | else | ||
158 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
159 | endif | ||
160 | else | ||
161 | # for linking with debug library, run like: | ||
162 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ | ||
163 | ifdef LIBDW_DIR | ||
164 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include | ||
165 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib | ||
166 | endif | ||
167 | |||
168 | FLAGS_DWARF=$(CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(LDFLAGS) $(EXTLIBS) | ||
169 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) | ||
170 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | ||
171 | NO_DWARF := 1 | ||
172 | endif # Dwarf support | ||
173 | endif # SOURCE_LIBELF | ||
174 | endif # NO_LIBELF | ||
175 | |||
176 | ifndef NO_LIBELF | ||
177 | CFLAGS += -DLIBELF_SUPPORT | ||
178 | FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) | ||
179 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | ||
180 | CFLAGS += -DLIBELF_MMAP | ||
181 | endif | ||
182 | |||
183 | # include ARCH specific config | ||
184 | -include $(src-perf)/arch/$(ARCH)/Makefile | ||
185 | |||
186 | ifndef NO_DWARF | ||
187 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | ||
188 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | ||
189 | NO_DWARF := 1 | ||
190 | else | ||
191 | CFLAGS += -DDWARF_SUPPORT $(LIBDW_CFLAGS) | ||
192 | LDFLAGS += $(LIBDW_LDFLAGS) | ||
193 | EXTLIBS += -lelf -ldw | ||
194 | endif # PERF_HAVE_DWARF_REGS | ||
195 | endif # NO_DWARF | ||
196 | |||
197 | endif # NO_LIBELF | ||
198 | |||
199 | ifndef NO_LIBELF | ||
200 | CFLAGS += -DLIBELF_SUPPORT | ||
201 | FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) | ||
202 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | ||
203 | CFLAGS += -DLIBELF_MMAP | ||
204 | endif # try-cc | ||
205 | endif # NO_LIBELF | ||
206 | |||
207 | # There's only x86 (both 32 and 64) support for CFI unwind so far | ||
208 | ifneq ($(ARCH),x86) | ||
209 | NO_LIBUNWIND := 1 | ||
210 | endif | ||
211 | |||
212 | ifndef NO_LIBUNWIND | ||
213 | # for linking with debug library, run like: | ||
214 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | ||
215 | ifdef LIBUNWIND_DIR | ||
216 | LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include | ||
217 | LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib | ||
218 | endif | ||
219 | |||
220 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(CFLAGS) $(LIBUNWIND_LDFLAGS) $(LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) | ||
221 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) | ||
222 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); | ||
223 | NO_LIBUNWIND := 1 | ||
224 | endif # Libunwind support | ||
225 | endif # NO_LIBUNWIND | ||
226 | |||
227 | ifndef NO_LIBUNWIND | ||
228 | CFLAGS += -DLIBUNWIND_SUPPORT | ||
229 | EXTLIBS += $(LIBUNWIND_LIBS) | ||
230 | CFLAGS += $(LIBUNWIND_CFLAGS) | ||
231 | LDFLAGS += $(LIBUNWIND_LDFLAGS) | ||
232 | endif # NO_LIBUNWIND | ||
233 | |||
234 | ifndef NO_LIBAUDIT | ||
235 | FLAGS_LIBAUDIT = $(CFLAGS) $(LDFLAGS) -laudit | ||
236 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) | ||
237 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); | ||
238 | NO_LIBAUDIT := 1 | ||
239 | else | ||
240 | CFLAGS += -DLIBAUDIT_SUPPORT | ||
241 | EXTLIBS += -laudit | ||
242 | endif | ||
243 | endif | ||
244 | |||
245 | ifdef NO_NEWT | ||
246 | NO_SLANG=1 | ||
247 | endif | ||
248 | |||
249 | ifndef NO_SLANG | ||
250 | FLAGS_SLANG=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang | ||
251 | ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) | ||
252 | msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev); | ||
253 | NO_SLANG := 1 | ||
254 | else | ||
255 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | ||
256 | CFLAGS += -I/usr/include/slang | ||
257 | CFLAGS += -DSLANG_SUPPORT | ||
258 | EXTLIBS += -lslang | ||
259 | endif | ||
260 | endif | ||
261 | |||
262 | ifndef NO_GTK2 | ||
263 | FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) | ||
264 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) | ||
265 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | ||
266 | NO_GTK2 := 1 | ||
267 | else | ||
268 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) | ||
269 | CFLAGS += -DHAVE_GTK_INFO_BAR | ||
270 | endif | ||
271 | CFLAGS += -DGTK2_SUPPORT | ||
272 | CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) | ||
273 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) | ||
274 | endif | ||
275 | endif | ||
276 | |||
277 | grep-libs = $(filter -l%,$(1)) | ||
278 | strip-libs = $(filter-out -l%,$(1)) | ||
279 | |||
280 | ifdef NO_LIBPERL | ||
281 | CFLAGS += -DNO_LIBPERL | ||
282 | else | ||
283 | PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) | ||
284 | PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) | ||
285 | PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) | ||
286 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | ||
287 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | ||
288 | |||
289 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) | ||
290 | CFLAGS += -DNO_LIBPERL | ||
291 | NO_LIBPERL := 1 | ||
292 | else | ||
293 | LDFLAGS += $(PERL_EMBED_LDFLAGS) | ||
294 | EXTLIBS += $(PERL_EMBED_LIBADD) | ||
295 | endif | ||
296 | endif | ||
297 | |||
298 | disable-python = $(eval $(disable-python_code)) | ||
299 | define disable-python_code | ||
300 | CFLAGS += -DNO_LIBPYTHON | ||
301 | $(if $(1),$(warning No $(1) was found)) | ||
302 | $(warning Python support will not be built) | ||
303 | NO_LIBPYTHON := 1 | ||
304 | endef | ||
305 | |||
306 | override PYTHON := \ | ||
307 | $(call get-executable-or-default,PYTHON,python) | ||
308 | |||
309 | ifndef PYTHON | ||
310 | $(call disable-python,python interpreter) | ||
311 | else | ||
312 | |||
313 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | ||
314 | |||
315 | ifdef NO_LIBPYTHON | ||
316 | $(call disable-python) | ||
317 | else | ||
318 | |||
319 | override PYTHON_CONFIG := \ | ||
320 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) | ||
321 | |||
322 | ifndef PYTHON_CONFIG | ||
323 | $(call disable-python,python-config tool) | ||
324 | else | ||
325 | |||
326 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | ||
327 | |||
328 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
329 | PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) | ||
330 | PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) | ||
331 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
332 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | ||
333 | |||
334 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) | ||
335 | $(call disable-python,Python.h (for Python 2.x)) | ||
336 | else | ||
337 | |||
338 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) | ||
339 | $(warning Python 3 is not yet supported; please set) | ||
340 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) | ||
341 | $(warning If you also have Python 2 installed, then) | ||
342 | $(warning try something like:) | ||
343 | $(warning $(and ,)) | ||
344 | $(warning $(and ,) make PYTHON=python2) | ||
345 | $(warning $(and ,)) | ||
346 | $(warning Otherwise, disable Python support entirely:) | ||
347 | $(warning $(and ,)) | ||
348 | $(warning $(and ,) make NO_LIBPYTHON=1) | ||
349 | $(warning $(and ,)) | ||
350 | $(error $(and ,)) | ||
351 | else | ||
352 | LDFLAGS += $(PYTHON_EMBED_LDFLAGS) | ||
353 | EXTLIBS += $(PYTHON_EMBED_LIBADD) | ||
354 | LANG_BINDINGS += $(obj-perf)python/perf.so | ||
355 | endif | ||
356 | endif | ||
357 | endif | ||
358 | endif | ||
359 | endif | ||
360 | |||
361 | ifdef NO_DEMANGLE | ||
362 | CFLAGS += -DNO_DEMANGLE | ||
363 | else | ||
364 | ifdef HAVE_CPLUS_DEMANGLE | ||
365 | EXTLIBS += -liberty | ||
366 | CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
367 | else | ||
368 | FLAGS_BFD=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd | ||
369 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) | ||
370 | ifeq ($(has_bfd),y) | ||
371 | EXTLIBS += -lbfd | ||
372 | else | ||
373 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | ||
374 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) | ||
375 | ifeq ($(has_bfd_iberty),y) | ||
376 | EXTLIBS += -lbfd -liberty | ||
377 | else | ||
378 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | ||
379 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) | ||
380 | ifeq ($(has_bfd_iberty_z),y) | ||
381 | EXTLIBS += -lbfd -liberty -lz | ||
382 | else | ||
383 | FLAGS_CPLUS_DEMANGLE=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) -liberty | ||
384 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) | ||
385 | ifeq ($(has_cplus_demangle),y) | ||
386 | EXTLIBS += -liberty | ||
387 | CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
388 | else | ||
389 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) | ||
390 | CFLAGS += -DNO_DEMANGLE | ||
391 | endif | ||
392 | endif | ||
393 | endif | ||
394 | endif | ||
395 | endif | ||
396 | endif | ||
397 | |||
398 | ifndef NO_STRLCPY | ||
399 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) | ||
400 | CFLAGS += -DHAVE_STRLCPY | ||
401 | endif | ||
402 | endif | ||
403 | |||
404 | ifndef NO_ON_EXIT | ||
405 | ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) | ||
406 | CFLAGS += -DHAVE_ON_EXIT | ||
407 | endif | ||
408 | endif | ||
409 | |||
410 | ifndef NO_BACKTRACE | ||
411 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) | ||
412 | CFLAGS += -DBACKTRACE_SUPPORT | ||
413 | endif | ||
414 | endif | ||
415 | |||
416 | ifndef NO_LIBNUMA | ||
417 | FLAGS_LIBNUMA = $(CFLAGS) $(LDFLAGS) -lnuma | ||
418 | ifneq ($(call try-cc,$(SOURCE_LIBNUMA),$(FLAGS_LIBNUMA),libnuma),y) | ||
419 | msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); | ||
420 | NO_LIBNUMA := 1 | ||
421 | else | ||
422 | CFLAGS += -DLIBNUMA_SUPPORT | ||
423 | EXTLIBS += -lnuma | ||
424 | endif | ||
425 | endif | ||
426 | |||
427 | # Among the variables below, these: | ||
428 | # perfexecdir | ||
429 | # template_dir | ||
430 | # mandir | ||
431 | # infodir | ||
432 | # htmldir | ||
433 | # ETC_PERFCONFIG (but not sysconfdir) | ||
434 | # can be specified as a relative path some/where/else; | ||
435 | # this is interpreted as relative to $(prefix) and "perf" at | ||
436 | # runtime figures out where they are based on the path to the executable. | ||
437 | # This can help installing the suite in a relocatable way. | ||
438 | |||
439 | # Make the path relative to DESTDIR, not to prefix | ||
440 | ifndef DESTDIR | ||
441 | prefix = $(HOME) | ||
442 | endif | ||
443 | bindir_relative = bin | ||
444 | bindir = $(prefix)/$(bindir_relative) | ||
445 | mandir = share/man | ||
446 | infodir = share/info | ||
447 | perfexecdir = libexec/perf-core | ||
448 | sharedir = $(prefix)/share | ||
449 | template_dir = share/perf-core/templates | ||
450 | htmldir = share/doc/perf-doc | ||
451 | ifeq ($(prefix),/usr) | ||
452 | sysconfdir = /etc | ||
453 | ETC_PERFCONFIG = $(sysconfdir)/perfconfig | ||
454 | else | ||
455 | sysconfdir = $(prefix)/etc | ||
456 | ETC_PERFCONFIG = etc/perfconfig | ||
457 | endif | ||
458 | lib = lib | ||
459 | |||
460 | # Shell quote (do not use $(call) to accommodate ancient setups); | ||
461 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) | ||
462 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) | ||
463 | bindir_SQ = $(subst ','\'',$(bindir)) | ||
464 | mandir_SQ = $(subst ','\'',$(mandir)) | ||
465 | infodir_SQ = $(subst ','\'',$(infodir)) | ||
466 | perfexecdir_SQ = $(subst ','\'',$(perfexecdir)) | ||
467 | template_dir_SQ = $(subst ','\'',$(template_dir)) | ||
468 | htmldir_SQ = $(subst ','\'',$(htmldir)) | ||
469 | prefix_SQ = $(subst ','\'',$(prefix)) | ||
470 | sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) | ||
471 | |||
472 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) | ||
473 | perfexec_instdir = $(perfexecdir) | ||
474 | else | ||
475 | perfexec_instdir = $(prefix)/$(perfexecdir) | ||
476 | endif | ||
477 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | ||
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index f5ac77485a4f..708fb8e9822a 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -61,15 +61,13 @@ int main(void) | |||
61 | } | 61 | } |
62 | endef | 62 | endef |
63 | 63 | ||
64 | ifndef NO_NEWT | 64 | ifndef NO_SLANG |
65 | define SOURCE_NEWT | 65 | define SOURCE_SLANG |
66 | #include <newt.h> | 66 | #include <slang.h> |
67 | 67 | ||
68 | int main(void) | 68 | int main(void) |
69 | { | 69 | { |
70 | newtInit(); | 70 | return SLsmg_init_smg(); |
71 | newtCls(); | ||
72 | return newtFinished(); | ||
73 | } | 71 | } |
74 | endef | 72 | endef |
75 | endif | 73 | endif |
@@ -225,3 +223,14 @@ int main(void) | |||
225 | return on_exit(NULL, NULL); | 223 | return on_exit(NULL, NULL); |
226 | } | 224 | } |
227 | endef | 225 | endef |
226 | |||
227 | define SOURCE_LIBNUMA | ||
228 | #include <numa.h> | ||
229 | #include <numaif.h> | ||
230 | |||
231 | int main(void) | ||
232 | { | ||
233 | numa_available(); | ||
234 | return 0; | ||
235 | } | ||
236 | endef | ||
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index e5413125e6bb..8ef3bd30a549 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak | |||
@@ -13,7 +13,7 @@ newline := $(newline) | |||
13 | # what should replace a newline when escaping | 13 | # what should replace a newline when escaping |
14 | # newlines; the default is a bizarre string. | 14 | # newlines; the default is a bizarre string. |
15 | # | 15 | # |
16 | nl-escape = $(or $(1),m822df3020w6a44id34bt574ctac44eb9f4n) | 16 | nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n) |
17 | 17 | ||
18 | # escape-nl | 18 | # escape-nl |
19 | # | 19 | # |
@@ -173,9 +173,9 @@ _ge-abspath = $(if $(is-executable),$(1)) | |||
173 | # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) | 173 | # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) |
174 | # | 174 | # |
175 | define get-executable-or-default | 175 | define get-executable-or-default |
176 | $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) | 176 | $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2),$(1))) |
177 | endef | 177 | endef |
178 | _ge_attempt = $(or $(get-executable),$(_gea_warn),$(call _gea_err,$(2))) | 178 | _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2))) |
179 | _gea_warn = $(warning The path '$(1)' is not executable.) | 179 | _gea_warn = $(warning The path '$(1)' is not executable.) |
180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) | 180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) |
181 | 181 | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 0f661fbce6a8..85e1aed95204 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -13,7 +13,7 @@ | |||
13 | #include "util/quote.h" | 13 | #include "util/quote.h" |
14 | #include "util/run-command.h" | 14 | #include "util/run-command.h" |
15 | #include "util/parse-events.h" | 15 | #include "util/parse-events.h" |
16 | #include "util/debugfs.h" | 16 | #include <lk/debugfs.h> |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | 18 | ||
19 | const char perf_usage_string[] = | 19 | const char perf_usage_string[] = |
@@ -60,6 +60,7 @@ static struct cmd_struct commands[] = { | |||
60 | { "trace", cmd_trace, 0 }, | 60 | { "trace", cmd_trace, 0 }, |
61 | #endif | 61 | #endif |
62 | { "inject", cmd_inject, 0 }, | 62 | { "inject", cmd_inject, 0 }, |
63 | { "mem", cmd_mem, 0 }, | ||
63 | }; | 64 | }; |
64 | 65 | ||
65 | struct pager_config { | 66 | struct pager_config { |
@@ -193,13 +194,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) | |||
193 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); | 194 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); |
194 | usage(perf_usage_string); | 195 | usage(perf_usage_string); |
195 | } | 196 | } |
196 | debugfs_set_path((*argv)[1]); | 197 | perf_debugfs_set_path((*argv)[1]); |
197 | if (envchanged) | 198 | if (envchanged) |
198 | *envchanged = 1; | 199 | *envchanged = 1; |
199 | (*argv)++; | 200 | (*argv)++; |
200 | (*argc)--; | 201 | (*argc)--; |
201 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { | 202 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { |
202 | debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); | 203 | perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); |
203 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); | 204 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); |
204 | if (envchanged) | 205 | if (envchanged) |
205 | *envchanged = 1; | 206 | *envchanged = 1; |
@@ -328,14 +329,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
328 | if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) | 329 | if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) |
329 | return 0; | 330 | return 0; |
330 | 331 | ||
332 | status = 1; | ||
331 | /* Check for ENOSPC and EIO errors.. */ | 333 | /* Check for ENOSPC and EIO errors.. */ |
332 | if (fflush(stdout)) | 334 | if (fflush(stdout)) { |
333 | die("write failure on standard output: %s", strerror(errno)); | 335 | fprintf(stderr, "write failure on standard output: %s", strerror(errno)); |
334 | if (ferror(stdout)) | 336 | goto out; |
335 | die("unknown write failure on standard output"); | 337 | } |
336 | if (fclose(stdout)) | 338 | if (ferror(stdout)) { |
337 | die("close failed on standard output: %s", strerror(errno)); | 339 | fprintf(stderr, "unknown write failure on standard output"); |
338 | return 0; | 340 | goto out; |
341 | } | ||
342 | if (fclose(stdout)) { | ||
343 | fprintf(stderr, "close failed on standard output: %s", strerror(errno)); | ||
344 | goto out; | ||
345 | } | ||
346 | status = 0; | ||
347 | out: | ||
348 | return status; | ||
339 | } | 349 | } |
340 | 350 | ||
341 | static void handle_internal_command(int argc, const char **argv) | 351 | static void handle_internal_command(int argc, const char **argv) |
@@ -452,7 +462,7 @@ int main(int argc, const char **argv) | |||
452 | if (!cmd) | 462 | if (!cmd) |
453 | cmd = "perf-help"; | 463 | cmd = "perf-help"; |
454 | /* get debugfs mount point from /proc/mounts */ | 464 | /* get debugfs mount point from /proc/mounts */ |
455 | debugfs_mount(NULL); | 465 | perf_debugfs_mount(NULL); |
456 | /* | 466 | /* |
457 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: | 467 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: |
458 | * | 468 | * |
@@ -467,7 +477,8 @@ int main(int argc, const char **argv) | |||
467 | cmd += 5; | 477 | cmd += 5; |
468 | argv[0] = cmd; | 478 | argv[0] = cmd; |
469 | handle_internal_command(argc, argv); | 479 | handle_internal_command(argc, argv); |
470 | die("cannot handle %s internally", cmd); | 480 | fprintf(stderr, "cannot handle %s internally", cmd); |
481 | goto out; | ||
471 | } | 482 | } |
472 | 483 | ||
473 | /* Look for flags.. */ | 484 | /* Look for flags.. */ |
@@ -485,7 +496,7 @@ int main(int argc, const char **argv) | |||
485 | printf("\n usage: %s\n\n", perf_usage_string); | 496 | printf("\n usage: %s\n\n", perf_usage_string); |
486 | list_common_cmds_help(); | 497 | list_common_cmds_help(); |
487 | printf("\n %s\n\n", perf_more_info_string); | 498 | printf("\n %s\n\n", perf_more_info_string); |
488 | exit(1); | 499 | goto out; |
489 | } | 500 | } |
490 | cmd = argv[0]; | 501 | cmd = argv[0]; |
491 | 502 | ||
@@ -507,9 +518,8 @@ int main(int argc, const char **argv) | |||
507 | 518 | ||
508 | while (1) { | 519 | while (1) { |
509 | static int done_help; | 520 | static int done_help; |
510 | static int was_alias; | 521 | int was_alias = run_argv(&argc, &argv); |
511 | 522 | ||
512 | was_alias = run_argv(&argc, &argv); | ||
513 | if (errno != ENOENT) | 523 | if (errno != ENOENT) |
514 | break; | 524 | break; |
515 | 525 | ||
@@ -517,7 +527,7 @@ int main(int argc, const char **argv) | |||
517 | fprintf(stderr, "Expansion of alias '%s' failed; " | 527 | fprintf(stderr, "Expansion of alias '%s' failed; " |
518 | "'%s' is not a perf-command\n", | 528 | "'%s' is not a perf-command\n", |
519 | cmd, argv[0]); | 529 | cmd, argv[0]); |
520 | exit(1); | 530 | goto out; |
521 | } | 531 | } |
522 | if (!done_help) { | 532 | if (!done_help) { |
523 | cmd = argv[0] = help_unknown_cmd(cmd); | 533 | cmd = argv[0] = help_unknown_cmd(cmd); |
@@ -528,6 +538,6 @@ int main(int argc, const char **argv) | |||
528 | 538 | ||
529 | fprintf(stderr, "Failed to run command '%s': %s\n", | 539 | fprintf(stderr, "Failed to run command '%s': %s\n", |
530 | cmd, strerror(errno)); | 540 | cmd, strerror(errno)); |
531 | 541 | out: | |
532 | return 1; | 542 | return 1; |
533 | } | 543 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 2c340e7da458..32bd102c32b6 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -1,10 +1,6 @@ | |||
1 | #ifndef _PERF_PERF_H | 1 | #ifndef _PERF_PERF_H |
2 | #define _PERF_PERF_H | 2 | #define _PERF_PERF_H |
3 | 3 | ||
4 | struct winsize; | ||
5 | |||
6 | void get_term_dimensions(struct winsize *ws); | ||
7 | |||
8 | #include <asm/unistd.h> | 4 | #include <asm/unistd.h> |
9 | 5 | ||
10 | #if defined(__i386__) | 6 | #if defined(__i386__) |
@@ -98,6 +94,18 @@ void get_term_dimensions(struct winsize *ws); | |||
98 | #define CPUINFO_PROC "cpu model" | 94 | #define CPUINFO_PROC "cpu model" |
99 | #endif | 95 | #endif |
100 | 96 | ||
97 | #ifdef __arc__ | ||
98 | #define rmb() asm volatile("" ::: "memory") | ||
99 | #define cpu_relax() rmb() | ||
100 | #define CPUINFO_PROC "Processor" | ||
101 | #endif | ||
102 | |||
103 | #ifdef __metag__ | ||
104 | #define rmb() asm volatile("" ::: "memory") | ||
105 | #define cpu_relax() asm volatile("" ::: "memory") | ||
106 | #define CPUINFO_PROC "CPU" | ||
107 | #endif | ||
108 | |||
101 | #include <time.h> | 109 | #include <time.h> |
102 | #include <unistd.h> | 110 | #include <unistd.h> |
103 | #include <sys/types.h> | 111 | #include <sys/types.h> |
@@ -107,32 +115,6 @@ void get_term_dimensions(struct winsize *ws); | |||
107 | #include "util/types.h" | 115 | #include "util/types.h" |
108 | #include <stdbool.h> | 116 | #include <stdbool.h> |
109 | 117 | ||
110 | struct perf_mmap { | ||
111 | void *base; | ||
112 | int mask; | ||
113 | unsigned int prev; | ||
114 | }; | ||
115 | |||
116 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
117 | { | ||
118 | struct perf_event_mmap_page *pc = mm->base; | ||
119 | int head = pc->data_head; | ||
120 | rmb(); | ||
121 | return head; | ||
122 | } | ||
123 | |||
124 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
125 | unsigned long tail) | ||
126 | { | ||
127 | struct perf_event_mmap_page *pc = md->base; | ||
128 | |||
129 | /* | ||
130 | * ensure all reads are done before we write the tail out. | ||
131 | */ | ||
132 | /* mb(); */ | ||
133 | pc->data_tail = tail; | ||
134 | } | ||
135 | |||
136 | /* | 118 | /* |
137 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 119 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all |
138 | * counters in the current task. | 120 | * counters in the current task. |
@@ -236,9 +218,8 @@ struct perf_record_opts { | |||
236 | bool pipe_output; | 218 | bool pipe_output; |
237 | bool raw_samples; | 219 | bool raw_samples; |
238 | bool sample_address; | 220 | bool sample_address; |
221 | bool sample_weight; | ||
239 | bool sample_time; | 222 | bool sample_time; |
240 | bool sample_id_all_missing; | ||
241 | bool exclude_guest_missing; | ||
242 | bool period; | 223 | bool period; |
243 | unsigned int freq; | 224 | unsigned int freq; |
244 | unsigned int mmap_pages; | 225 | unsigned int mmap_pages; |
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record deleted file mode 100644 index 8edda9078d5d..000000000000 --- a/tools/perf/scripts/perl/bin/workqueue-stats-record +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ | ||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report deleted file mode 100644 index 6d91411d248c..000000000000 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | # description: workqueue stats (ins/exe/create/destroy) | ||
3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl | ||
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl index 4bb3ecd33472..8b20787021c1 100644 --- a/tools/perf/scripts/perl/rwtop.pl +++ b/tools/perf/scripts/perl/rwtop.pl | |||
@@ -17,6 +17,7 @@ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | |||
17 | use lib "./Perf-Trace-Util/lib"; | 17 | use lib "./Perf-Trace-Util/lib"; |
18 | use Perf::Trace::Core; | 18 | use Perf::Trace::Core; |
19 | use Perf::Trace::Util; | 19 | use Perf::Trace::Util; |
20 | use POSIX qw/SIGALRM SA_RESTART/; | ||
20 | 21 | ||
21 | my $default_interval = 3; | 22 | my $default_interval = 3; |
22 | my $nlines = 20; | 23 | my $nlines = 20; |
@@ -90,7 +91,10 @@ sub syscalls::sys_enter_write | |||
90 | 91 | ||
91 | sub trace_begin | 92 | sub trace_begin |
92 | { | 93 | { |
93 | $SIG{ALRM} = \&set_print_pending; | 94 | my $sa = POSIX::SigAction->new(\&set_print_pending); |
95 | $sa->flags(SA_RESTART); | ||
96 | $sa->safe(1); | ||
97 | POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n"; | ||
94 | alarm 1; | 98 | alarm 1; |
95 | } | 99 | } |
96 | 100 | ||
diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl deleted file mode 100644 index a8eaff5119e0..000000000000 --- a/tools/perf/scripts/perl/workqueue-stats.pl +++ /dev/null | |||
@@ -1,129 +0,0 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Displays workqueue stats | ||
6 | # | ||
7 | # Usage: | ||
8 | # | ||
9 | # perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e | ||
10 | # workqueue:workqueue_destruction -e workqueue:workqueue_execution | ||
11 | # -e workqueue:workqueue_insertion | ||
12 | # | ||
13 | # perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl | ||
14 | |||
15 | use 5.010000; | ||
16 | use strict; | ||
17 | use warnings; | ||
18 | |||
19 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
20 | use lib "./Perf-Trace-Util/lib"; | ||
21 | use Perf::Trace::Core; | ||
22 | use Perf::Trace::Util; | ||
23 | |||
24 | my @cpus; | ||
25 | |||
26 | sub workqueue::workqueue_destruction | ||
27 | { | ||
28 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
29 | $common_pid, $common_comm, | ||
30 | $thread_comm, $thread_pid) = @_; | ||
31 | |||
32 | $cpus[$common_cpu]{$thread_pid}{destroyed}++; | ||
33 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
34 | } | ||
35 | |||
36 | sub workqueue::workqueue_creation | ||
37 | { | ||
38 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
39 | $common_pid, $common_comm, | ||
40 | $thread_comm, $thread_pid, $cpu) = @_; | ||
41 | |||
42 | $cpus[$common_cpu]{$thread_pid}{created}++; | ||
43 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
44 | } | ||
45 | |||
46 | sub workqueue::workqueue_execution | ||
47 | { | ||
48 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
49 | $common_pid, $common_comm, | ||
50 | $thread_comm, $thread_pid, $func) = @_; | ||
51 | |||
52 | $cpus[$common_cpu]{$thread_pid}{executed}++; | ||
53 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
54 | } | ||
55 | |||
56 | sub workqueue::workqueue_insertion | ||
57 | { | ||
58 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
59 | $common_pid, $common_comm, | ||
60 | $thread_comm, $thread_pid, $func) = @_; | ||
61 | |||
62 | $cpus[$common_cpu]{$thread_pid}{inserted}++; | ||
63 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
64 | } | ||
65 | |||
66 | sub trace_end | ||
67 | { | ||
68 | print "workqueue work stats:\n\n"; | ||
69 | my $cpu = 0; | ||
70 | printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name"); | ||
71 | printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----"); | ||
72 | foreach my $pidhash (@cpus) { | ||
73 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
74 | my $ins = $$wqhash{'inserted'} || 0; | ||
75 | my $exe = $$wqhash{'executed'} || 0; | ||
76 | my $comm = $$wqhash{'comm'} || ""; | ||
77 | if ($ins || $exe) { | ||
78 | printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm); | ||
79 | } | ||
80 | } | ||
81 | $cpu++; | ||
82 | } | ||
83 | |||
84 | $cpu = 0; | ||
85 | print "\nworkqueue lifecycle stats:\n\n"; | ||
86 | printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name"); | ||
87 | printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----"); | ||
88 | foreach my $pidhash (@cpus) { | ||
89 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
90 | my $created = $$wqhash{'created'} || 0; | ||
91 | my $destroyed = $$wqhash{'destroyed'} || 0; | ||
92 | my $comm = $$wqhash{'comm'} || ""; | ||
93 | if ($created || $destroyed) { | ||
94 | printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed, | ||
95 | $comm); | ||
96 | } | ||
97 | } | ||
98 | $cpu++; | ||
99 | } | ||
100 | |||
101 | print_unhandled(); | ||
102 | } | ||
103 | |||
104 | my %unhandled; | ||
105 | |||
106 | sub print_unhandled | ||
107 | { | ||
108 | if ((scalar keys %unhandled) == 0) { | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | print "\nunhandled events:\n\n"; | ||
113 | |||
114 | printf("%-40s %10s\n", "event", "count"); | ||
115 | printf("%-40s %10s\n", "----------------------------------------", | ||
116 | "-----------"); | ||
117 | |||
118 | foreach my $event_name (keys %unhandled) { | ||
119 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | sub trace_unhandled | ||
124 | { | ||
125 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
126 | $common_pid, $common_comm) = @_; | ||
127 | |||
128 | $unhandled{$event_name}++; | ||
129 | } | ||
diff --git a/tools/perf/scripts/python/net_dropmonitor.py b/tools/perf/scripts/python/net_dropmonitor.py index a4ffc9500023..b5740599aabd 100755 --- a/tools/perf/scripts/python/net_dropmonitor.py +++ b/tools/perf/scripts/python/net_dropmonitor.py | |||
@@ -15,35 +15,38 @@ kallsyms = [] | |||
15 | 15 | ||
16 | def get_kallsyms_table(): | 16 | def get_kallsyms_table(): |
17 | global kallsyms | 17 | global kallsyms |
18 | |||
18 | try: | 19 | try: |
19 | f = open("/proc/kallsyms", "r") | 20 | f = open("/proc/kallsyms", "r") |
20 | linecount = 0 | ||
21 | for line in f: | ||
22 | linecount = linecount+1 | ||
23 | f.seek(0) | ||
24 | except: | 21 | except: |
25 | return | 22 | return |
26 | 23 | ||
27 | |||
28 | j = 0 | ||
29 | for line in f: | 24 | for line in f: |
30 | loc = int(line.split()[0], 16) | 25 | loc = int(line.split()[0], 16) |
31 | name = line.split()[2] | 26 | name = line.split()[2] |
32 | j = j +1 | 27 | kallsyms.append((loc, name)) |
33 | if ((j % 100) == 0): | ||
34 | print "\r" + str(j) + "/" + str(linecount), | ||
35 | kallsyms.append({ 'loc': loc, 'name' : name}) | ||
36 | |||
37 | print "\r" + str(j) + "/" + str(linecount) | ||
38 | kallsyms.sort() | 28 | kallsyms.sort() |
39 | return | ||
40 | 29 | ||
41 | def get_sym(sloc): | 30 | def get_sym(sloc): |
42 | loc = int(sloc) | 31 | loc = int(sloc) |
43 | for i in kallsyms: | 32 | |
44 | if (i['loc'] >= loc): | 33 | # Invariant: kallsyms[i][0] <= loc for all 0 <= i <= start |
45 | return (i['name'], i['loc']-loc) | 34 | # kallsyms[i][0] > loc for all end <= i < len(kallsyms) |
46 | return (None, 0) | 35 | start, end = -1, len(kallsyms) |
36 | while end != start + 1: | ||
37 | pivot = (start + end) // 2 | ||
38 | if loc < kallsyms[pivot][0]: | ||
39 | end = pivot | ||
40 | else: | ||
41 | start = pivot | ||
42 | |||
43 | # Now (start == -1 or kallsyms[start][0] <= loc) | ||
44 | # and (start == len(kallsyms) - 1 or loc < kallsyms[start + 1][0]) | ||
45 | if start >= 0: | ||
46 | symloc, name = kallsyms[start] | ||
47 | return (name, loc - symloc) | ||
48 | else: | ||
49 | return (None, 0) | ||
47 | 50 | ||
48 | def print_drop_table(): | 51 | def print_drop_table(): |
49 | print "%25s %25s %25s" % ("LOCATION", "OFFSET", "COUNT") | 52 | print "%25s %25s %25s" % ("LOCATION", "OFFSET", "COUNT") |
@@ -64,7 +67,7 @@ def trace_end(): | |||
64 | 67 | ||
65 | # called from perf, when it finds a correspoinding event | 68 | # called from perf, when it finds a correspoinding event |
66 | def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, | 69 | def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, |
67 | skbaddr, protocol, location): | 70 | skbaddr, location, protocol): |
68 | slocation = str(location) | 71 | slocation = str(location) |
69 | try: | 72 | try: |
70 | drop_log[slocation] = drop_log[slocation] + 1 | 73 | drop_log[slocation] = drop_log[slocation] + 1 |
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 25638a986257..00218f503b2e 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c | |||
@@ -19,6 +19,11 @@ | |||
19 | * permissions. All the event text files are stored there. | 19 | * permissions. All the event text files are stored there. |
20 | */ | 20 | */ |
21 | 21 | ||
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__ | ||
22 | #include <stdlib.h> | 27 | #include <stdlib.h> |
23 | #include <stdio.h> | 28 | #include <stdio.h> |
24 | #include <inttypes.h> | 29 | #include <inttypes.h> |
@@ -33,8 +38,6 @@ | |||
33 | 38 | ||
34 | extern int verbose; | 39 | extern int verbose; |
35 | 40 | ||
36 | bool test_attr__enabled; | ||
37 | |||
38 | static char *dir; | 41 | static char *dir; |
39 | 42 | ||
40 | void test_attr__init(void) | 43 | void test_attr__init(void) |
@@ -144,10 +147,15 @@ void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | |||
144 | 147 | ||
145 | static int run_dir(const char *d, const char *perf) | 148 | static int run_dir(const char *d, const char *perf) |
146 | { | 149 | { |
150 | char v[] = "-vvvvv"; | ||
151 | int vcnt = min(verbose, (int) sizeof(v) - 1); | ||
147 | char cmd[3*PATH_MAX]; | 152 | char cmd[3*PATH_MAX]; |
148 | 153 | ||
149 | snprintf(cmd, 3*PATH_MAX, "python %s/attr.py -d %s/attr/ -p %s %s", | 154 | if (verbose) |
150 | d, d, perf, verbose ? "-v" : ""); | 155 | vcnt++; |
156 | |||
157 | snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", | ||
158 | d, d, perf, vcnt, v); | ||
151 | 159 | ||
152 | return system(cmd); | 160 | return system(cmd); |
153 | } | 161 | } |
@@ -170,6 +178,6 @@ int test__attr(void) | |||
170 | !lstat(path_perf, &st)) | 178 | !lstat(path_perf, &st)) |
171 | return run_dir(path_dir, path_perf); | 179 | return run_dir(path_dir, path_perf); |
172 | 180 | ||
173 | fprintf(stderr, " (ommitted)"); | 181 | fprintf(stderr, " (omitted)"); |
174 | return 0; | 182 | return 0; |
175 | } | 183 | } |
diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index e702b82dcb86..c9b4b6269b51 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py | |||
@@ -24,6 +24,7 @@ class Unsup(Exception): | |||
24 | 24 | ||
25 | class Event(dict): | 25 | class Event(dict): |
26 | terms = [ | 26 | terms = [ |
27 | 'cpu', | ||
27 | 'flags', | 28 | 'flags', |
28 | 'type', | 29 | 'type', |
29 | 'size', | 30 | 'size', |
@@ -68,7 +69,7 @@ class Event(dict): | |||
68 | self[key] = val | 69 | self[key] = val |
69 | 70 | ||
70 | def __init__(self, name, data, base): | 71 | def __init__(self, name, data, base): |
71 | log.info(" Event %s" % name); | 72 | log.debug(" Event %s" % name); |
72 | self.name = name; | 73 | self.name = name; |
73 | self.group = '' | 74 | self.group = '' |
74 | self.add(base) | 75 | self.add(base) |
@@ -97,6 +98,14 @@ class Event(dict): | |||
97 | return False | 98 | return False |
98 | return True | 99 | return True |
99 | 100 | ||
101 | def diff(self, other): | ||
102 | for t in Event.terms: | ||
103 | if not self.has_key(t) or not other.has_key(t): | ||
104 | continue | ||
105 | if not self.compare_data(self[t], other[t]): | ||
106 | log.warning("expected %s=%s, got %s" % (t, self[t], other[t])) | ||
107 | |||
108 | |||
100 | # Test file description needs to have following sections: | 109 | # Test file description needs to have following sections: |
101 | # [config] | 110 | # [config] |
102 | # - just single instance in file | 111 | # - just single instance in file |
@@ -128,7 +137,7 @@ class Test(object): | |||
128 | 137 | ||
129 | self.expect = {} | 138 | self.expect = {} |
130 | self.result = {} | 139 | self.result = {} |
131 | log.info(" loading expected events"); | 140 | log.debug(" loading expected events"); |
132 | self.load_events(path, self.expect) | 141 | self.load_events(path, self.expect) |
133 | 142 | ||
134 | def is_event(self, name): | 143 | def is_event(self, name): |
@@ -164,7 +173,7 @@ class Test(object): | |||
164 | self.perf, self.command, tempdir, self.args) | 173 | self.perf, self.command, tempdir, self.args) |
165 | ret = os.WEXITSTATUS(os.system(cmd)) | 174 | ret = os.WEXITSTATUS(os.system(cmd)) |
166 | 175 | ||
167 | log.info(" running '%s' ret %d " % (cmd, ret)) | 176 | log.info(" '%s' ret %d " % (cmd, ret)) |
168 | 177 | ||
169 | if ret != int(self.ret): | 178 | if ret != int(self.ret): |
170 | raise Unsup(self) | 179 | raise Unsup(self) |
@@ -172,7 +181,7 @@ class Test(object): | |||
172 | def compare(self, expect, result): | 181 | def compare(self, expect, result): |
173 | match = {} | 182 | match = {} |
174 | 183 | ||
175 | log.info(" compare"); | 184 | log.debug(" compare"); |
176 | 185 | ||
177 | # For each expected event find all matching | 186 | # For each expected event find all matching |
178 | # events in result. Fail if there's not any. | 187 | # events in result. Fail if there's not any. |
@@ -187,10 +196,11 @@ class Test(object): | |||
187 | else: | 196 | else: |
188 | log.debug(" ->FAIL"); | 197 | log.debug(" ->FAIL"); |
189 | 198 | ||
190 | log.info(" match: [%s] matches %s" % (exp_name, str(exp_list))) | 199 | log.debug(" match: [%s] matches %s" % (exp_name, str(exp_list))) |
191 | 200 | ||
192 | # we did not any matching event - fail | 201 | # we did not any matching event - fail |
193 | if (not exp_list): | 202 | if (not exp_list): |
203 | exp_event.diff(res_event) | ||
194 | raise Fail(self, 'match failure'); | 204 | raise Fail(self, 'match failure'); |
195 | 205 | ||
196 | match[exp_name] = exp_list | 206 | match[exp_name] = exp_list |
@@ -208,10 +218,10 @@ class Test(object): | |||
208 | if res_group not in match[group]: | 218 | if res_group not in match[group]: |
209 | raise Fail(self, 'group failure') | 219 | raise Fail(self, 'group failure') |
210 | 220 | ||
211 | log.info(" group: [%s] matches group leader %s" % | 221 | log.debug(" group: [%s] matches group leader %s" % |
212 | (exp_name, str(match[group]))) | 222 | (exp_name, str(match[group]))) |
213 | 223 | ||
214 | log.info(" matched") | 224 | log.debug(" matched") |
215 | 225 | ||
216 | def resolve_groups(self, events): | 226 | def resolve_groups(self, events): |
217 | for name, event in events.items(): | 227 | for name, event in events.items(): |
@@ -233,7 +243,7 @@ class Test(object): | |||
233 | self.run_cmd(tempdir); | 243 | self.run_cmd(tempdir); |
234 | 244 | ||
235 | # load events expectation for the test | 245 | # load events expectation for the test |
236 | log.info(" loading result events"); | 246 | log.debug(" loading result events"); |
237 | for f in glob.glob(tempdir + '/event*'): | 247 | for f in glob.glob(tempdir + '/event*'): |
238 | self.load_events(f, self.result); | 248 | self.load_events(f, self.result); |
239 | 249 | ||
diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record index f1485d8e6a0b..e9bd6391f2ae 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/attr/base-record | |||
@@ -2,12 +2,13 @@ | |||
2 | fd=1 | 2 | fd=1 |
3 | group_fd=-1 | 3 | group_fd=-1 |
4 | flags=0 | 4 | flags=0 |
5 | cpu=* | ||
5 | type=0|1 | 6 | type=0|1 |
6 | size=96 | 7 | size=96 |
7 | config=0 | 8 | config=0 |
8 | sample_period=4000 | 9 | sample_period=4000 |
9 | sample_type=263 | 10 | sample_type=263 |
10 | read_format=7 | 11 | read_format=0 |
11 | disabled=1 | 12 | disabled=1 |
12 | inherit=1 | 13 | inherit=1 |
13 | pinned=0 | 14 | pinned=0 |
@@ -26,8 +27,8 @@ watermark=0 | |||
26 | precise_ip=0 | 27 | precise_ip=0 |
27 | mmap_data=0 | 28 | mmap_data=0 |
28 | sample_id_all=1 | 29 | sample_id_all=1 |
29 | exclude_host=0 | 30 | exclude_host=0|1 |
30 | exclude_guest=1 | 31 | exclude_guest=0|1 |
31 | exclude_callchain_kernel=0 | 32 | exclude_callchain_kernel=0 |
32 | exclude_callchain_user=0 | 33 | exclude_callchain_user=0 |
33 | wakeup_events=0 | 34 | wakeup_events=0 |
diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat index 4bd79a82784f..91cd48b399f3 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/attr/base-stat | |||
@@ -2,6 +2,7 @@ | |||
2 | fd=1 | 2 | fd=1 |
3 | group_fd=-1 | 3 | group_fd=-1 |
4 | flags=0 | 4 | flags=0 |
5 | cpu=* | ||
5 | type=0 | 6 | type=0 |
6 | size=96 | 7 | size=96 |
7 | config=0 | 8 | config=0 |
@@ -26,8 +27,8 @@ watermark=0 | |||
26 | precise_ip=0 | 27 | precise_ip=0 |
27 | mmap_data=0 | 28 | mmap_data=0 |
28 | sample_id_all=0 | 29 | sample_id_all=0 |
29 | exclude_host=0 | 30 | exclude_host=0|1 |
30 | exclude_guest=1 | 31 | exclude_guest=0|1 |
31 | exclude_callchain_kernel=0 | 32 | exclude_callchain_kernel=0 |
32 | exclude_callchain_user=0 | 33 | exclude_callchain_user=0 |
33 | wakeup_events=0 | 34 | wakeup_events=0 |
diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/attr/test-record-C0 new file mode 100644 index 000000000000..d6a7e43f61b3 --- /dev/null +++ b/tools/perf/tests/attr/test-record-C0 | |||
@@ -0,0 +1,13 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -C 0 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | cpu=0 | ||
7 | |||
8 | # no enable on exec for CPU attached | ||
9 | enable_on_exec=0 | ||
10 | |||
11 | # PERF_SAMPLE_IP | PERF_SAMPLE_TID PERF_SAMPLE_TIME | # PERF_SAMPLE_PERIOD | ||
12 | # + PERF_SAMPLE_CPU added by -C 0 | ||
13 | sample_type=391 | ||
diff --git a/tools/perf/tests/attr/test-record-data b/tools/perf/tests/attr/test-record-data index 6627c3e7534a..716e143b5291 100644 --- a/tools/perf/tests/attr/test-record-data +++ b/tools/perf/tests/attr/test-record-data | |||
@@ -4,5 +4,8 @@ args = -d kill >/dev/null 2>&1 | |||
4 | 4 | ||
5 | [event:base-record] | 5 | [event:base-record] |
6 | sample_period=4000 | 6 | sample_period=4000 |
7 | sample_type=271 | 7 | |
8 | # sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | | ||
9 | # PERF_SAMPLE_ADDR | PERF_SAMPLE_PERIOD | PERF_SAMPLE_DATA_SRC | ||
10 | sample_type=33039 | ||
8 | mmap_data=1 | 11 | mmap_data=1 |
diff --git a/tools/perf/tests/attr/test-record-group b/tools/perf/tests/attr/test-record-group index a6599e9a19d3..57739cacdb2a 100644 --- a/tools/perf/tests/attr/test-record-group +++ b/tools/perf/tests/attr/test-record-group | |||
@@ -6,12 +6,14 @@ args = --group -e cycles,instructions kill >/dev/null 2>&1 | |||
6 | fd=1 | 6 | fd=1 |
7 | group_fd=-1 | 7 | group_fd=-1 |
8 | sample_type=327 | 8 | sample_type=327 |
9 | read_format=4 | ||
9 | 10 | ||
10 | [event-2:base-record] | 11 | [event-2:base-record] |
11 | fd=2 | 12 | fd=2 |
12 | group_fd=1 | 13 | group_fd=1 |
13 | config=1 | 14 | config=1 |
14 | sample_type=327 | 15 | sample_type=327 |
16 | read_format=4 | ||
15 | mmap=0 | 17 | mmap=0 |
16 | comm=0 | 18 | comm=0 |
17 | enable_on_exec=0 | 19 | enable_on_exec=0 |
diff --git a/tools/perf/tests/attr/test-record-group1 b/tools/perf/tests/attr/test-record-group1 index 5a8359da38af..c5548d054aff 100644 --- a/tools/perf/tests/attr/test-record-group1 +++ b/tools/perf/tests/attr/test-record-group1 | |||
@@ -1,11 +1,12 @@ | |||
1 | [config] | 1 | [config] |
2 | command = record | 2 | command = record |
3 | args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1 | 3 | args = -e '{cycles,instructions}' kill >/dev/null 2>&1 |
4 | 4 | ||
5 | [event-1:base-record] | 5 | [event-1:base-record] |
6 | fd=1 | 6 | fd=1 |
7 | group_fd=-1 | 7 | group_fd=-1 |
8 | sample_type=327 | 8 | sample_type=327 |
9 | read_format=4 | ||
9 | 10 | ||
10 | [event-2:base-record] | 11 | [event-2:base-record] |
11 | fd=2 | 12 | fd=2 |
@@ -13,6 +14,7 @@ group_fd=1 | |||
13 | type=0 | 14 | type=0 |
14 | config=1 | 15 | config=1 |
15 | sample_type=327 | 16 | sample_type=327 |
17 | read_format=4 | ||
16 | mmap=0 | 18 | mmap=0 |
17 | comm=0 | 19 | comm=0 |
18 | enable_on_exec=0 | 20 | enable_on_exec=0 |
diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/attr/test-stat-C0 new file mode 100644 index 000000000000..aa835950751f --- /dev/null +++ b/tools/perf/tests/attr/test-stat-C0 | |||
@@ -0,0 +1,9 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e cycles -C 0 kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
7 | # events are enabled by default when attached to cpu | ||
8 | disabled=0 | ||
9 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c new file mode 100644 index 000000000000..aba095489193 --- /dev/null +++ b/tools/perf/tests/bp_signal.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Inspired by breakpoint overflow test done by | ||
3 | * Vince Weaver <vincent.weaver@maine.edu> for perf_event_tests | ||
4 | * (git://github.com/deater/perf_event_tests) | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select | ||
9 | * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. | ||
10 | */ | ||
11 | #define __SANE_USERSPACE_TYPES__ | ||
12 | |||
13 | #include <stdlib.h> | ||
14 | #include <stdio.h> | ||
15 | #include <unistd.h> | ||
16 | #include <string.h> | ||
17 | #include <sys/ioctl.h> | ||
18 | #include <time.h> | ||
19 | #include <fcntl.h> | ||
20 | #include <signal.h> | ||
21 | #include <sys/mman.h> | ||
22 | #include <linux/compiler.h> | ||
23 | #include <linux/hw_breakpoint.h> | ||
24 | |||
25 | #include "tests.h" | ||
26 | #include "debug.h" | ||
27 | #include "perf.h" | ||
28 | |||
29 | static int fd1; | ||
30 | static int fd2; | ||
31 | static int overflows; | ||
32 | |||
33 | __attribute__ ((noinline)) | ||
34 | static int test_function(void) | ||
35 | { | ||
36 | return time(NULL); | ||
37 | } | ||
38 | |||
39 | static void sig_handler(int signum __maybe_unused, | ||
40 | siginfo_t *oh __maybe_unused, | ||
41 | void *uc __maybe_unused) | ||
42 | { | ||
43 | overflows++; | ||
44 | |||
45 | if (overflows > 10) { | ||
46 | /* | ||
47 | * This should be executed only once during | ||
48 | * this test, if we are here for the 10th | ||
49 | * time, consider this the recursive issue. | ||
50 | * | ||
51 | * We can get out of here by disable events, | ||
52 | * so no new SIGIO is delivered. | ||
53 | */ | ||
54 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | ||
55 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | static int bp_event(void *fn, int setup_signal) | ||
60 | { | ||
61 | struct perf_event_attr pe; | ||
62 | int fd; | ||
63 | |||
64 | memset(&pe, 0, sizeof(struct perf_event_attr)); | ||
65 | pe.type = PERF_TYPE_BREAKPOINT; | ||
66 | pe.size = sizeof(struct perf_event_attr); | ||
67 | |||
68 | pe.config = 0; | ||
69 | pe.bp_type = HW_BREAKPOINT_X; | ||
70 | pe.bp_addr = (unsigned long) fn; | ||
71 | pe.bp_len = sizeof(long); | ||
72 | |||
73 | pe.sample_period = 1; | ||
74 | pe.sample_type = PERF_SAMPLE_IP; | ||
75 | pe.wakeup_events = 1; | ||
76 | |||
77 | pe.disabled = 1; | ||
78 | pe.exclude_kernel = 1; | ||
79 | pe.exclude_hv = 1; | ||
80 | |||
81 | fd = sys_perf_event_open(&pe, 0, -1, -1, 0); | ||
82 | if (fd < 0) { | ||
83 | pr_debug("failed opening event %llx\n", pe.config); | ||
84 | return TEST_FAIL; | ||
85 | } | ||
86 | |||
87 | if (setup_signal) { | ||
88 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); | ||
89 | fcntl(fd, F_SETSIG, SIGIO); | ||
90 | fcntl(fd, F_SETOWN, getpid()); | ||
91 | } | ||
92 | |||
93 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); | ||
94 | |||
95 | return fd; | ||
96 | } | ||
97 | |||
98 | static long long bp_count(int fd) | ||
99 | { | ||
100 | long long count; | ||
101 | int ret; | ||
102 | |||
103 | ret = read(fd, &count, sizeof(long long)); | ||
104 | if (ret != sizeof(long long)) { | ||
105 | pr_debug("failed to read: %d\n", ret); | ||
106 | return TEST_FAIL; | ||
107 | } | ||
108 | |||
109 | return count; | ||
110 | } | ||
111 | |||
112 | int test__bp_signal(void) | ||
113 | { | ||
114 | struct sigaction sa; | ||
115 | long long count1, count2; | ||
116 | |||
117 | /* setup SIGIO signal handler */ | ||
118 | memset(&sa, 0, sizeof(struct sigaction)); | ||
119 | sa.sa_sigaction = (void *) sig_handler; | ||
120 | sa.sa_flags = SA_SIGINFO; | ||
121 | |||
122 | if (sigaction(SIGIO, &sa, NULL) < 0) { | ||
123 | pr_debug("failed setting up signal handler\n"); | ||
124 | return TEST_FAIL; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * We create following events: | ||
129 | * | ||
130 | * fd1 - breakpoint event on test_function with SIGIO | ||
131 | * signal configured. We should get signal | ||
132 | * notification each time the breakpoint is hit | ||
133 | * | ||
134 | * fd2 - breakpoint event on sig_handler without SIGIO | ||
135 | * configured. | ||
136 | * | ||
137 | * Following processing should happen: | ||
138 | * - execute test_function | ||
139 | * - fd1 event breakpoint hit -> count1 == 1 | ||
140 | * - SIGIO is delivered -> overflows == 1 | ||
141 | * - fd2 event breakpoint hit -> count2 == 1 | ||
142 | * | ||
143 | * The test case check following error conditions: | ||
144 | * - we get stuck in signal handler because of debug | ||
145 | * exception being triggered receursively due to | ||
146 | * the wrong RF EFLAG management | ||
147 | * | ||
148 | * - we never trigger the sig_handler breakpoint due | ||
149 | * to the rong RF EFLAG management | ||
150 | * | ||
151 | */ | ||
152 | |||
153 | fd1 = bp_event(test_function, 1); | ||
154 | fd2 = bp_event(sig_handler, 0); | ||
155 | |||
156 | ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0); | ||
157 | ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0); | ||
158 | |||
159 | /* | ||
160 | * Kick off the test by trigering 'fd1' | ||
161 | * breakpoint. | ||
162 | */ | ||
163 | test_function(); | ||
164 | |||
165 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | ||
166 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | ||
167 | |||
168 | count1 = bp_count(fd1); | ||
169 | count2 = bp_count(fd2); | ||
170 | |||
171 | close(fd1); | ||
172 | close(fd2); | ||
173 | |||
174 | pr_debug("count1 %lld, count2 %lld, overflow %d\n", | ||
175 | count1, count2, overflows); | ||
176 | |||
177 | if (count1 != 1) { | ||
178 | if (count1 == 11) | ||
179 | pr_debug("failed: RF EFLAG recursion issue detected\n"); | ||
180 | else | ||
181 | pr_debug("failed: wrong count for bp1%lld\n", count1); | ||
182 | } | ||
183 | |||
184 | if (overflows != 1) | ||
185 | pr_debug("failed: wrong overflow hit\n"); | ||
186 | |||
187 | if (count2 != 1) | ||
188 | pr_debug("failed: wrong count for bp2\n"); | ||
189 | |||
190 | return count1 == 1 && overflows == 1 && count2 == 1 ? | ||
191 | TEST_OK : TEST_FAIL; | ||
192 | } | ||
diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c new file mode 100644 index 000000000000..44ac82179708 --- /dev/null +++ b/tools/perf/tests/bp_signal_overflow.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Originally done by Vince Weaver <vincent.weaver@maine.edu> for | ||
3 | * perf_event_tests (git://github.com/deater/perf_event_tests) | ||
4 | */ | ||
5 | |||
6 | /* | ||
7 | * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select | ||
8 | * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. | ||
9 | */ | ||
10 | #define __SANE_USERSPACE_TYPES__ | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | #include <stdio.h> | ||
14 | #include <unistd.h> | ||
15 | #include <string.h> | ||
16 | #include <sys/ioctl.h> | ||
17 | #include <time.h> | ||
18 | #include <fcntl.h> | ||
19 | #include <signal.h> | ||
20 | #include <sys/mman.h> | ||
21 | #include <linux/compiler.h> | ||
22 | #include <linux/hw_breakpoint.h> | ||
23 | |||
24 | #include "tests.h" | ||
25 | #include "debug.h" | ||
26 | #include "perf.h" | ||
27 | |||
28 | static int overflows; | ||
29 | |||
30 | __attribute__ ((noinline)) | ||
31 | static int test_function(void) | ||
32 | { | ||
33 | return time(NULL); | ||
34 | } | ||
35 | |||
36 | static void sig_handler(int signum __maybe_unused, | ||
37 | siginfo_t *oh __maybe_unused, | ||
38 | void *uc __maybe_unused) | ||
39 | { | ||
40 | overflows++; | ||
41 | } | ||
42 | |||
43 | static long long bp_count(int fd) | ||
44 | { | ||
45 | long long count; | ||
46 | int ret; | ||
47 | |||
48 | ret = read(fd, &count, sizeof(long long)); | ||
49 | if (ret != sizeof(long long)) { | ||
50 | pr_debug("failed to read: %d\n", ret); | ||
51 | return TEST_FAIL; | ||
52 | } | ||
53 | |||
54 | return count; | ||
55 | } | ||
56 | |||
57 | #define EXECUTIONS 10000 | ||
58 | #define THRESHOLD 100 | ||
59 | |||
60 | int test__bp_signal_overflow(void) | ||
61 | { | ||
62 | struct perf_event_attr pe; | ||
63 | struct sigaction sa; | ||
64 | long long count; | ||
65 | int fd, i, fails = 0; | ||
66 | |||
67 | /* setup SIGIO signal handler */ | ||
68 | memset(&sa, 0, sizeof(struct sigaction)); | ||
69 | sa.sa_sigaction = (void *) sig_handler; | ||
70 | sa.sa_flags = SA_SIGINFO; | ||
71 | |||
72 | if (sigaction(SIGIO, &sa, NULL) < 0) { | ||
73 | pr_debug("failed setting up signal handler\n"); | ||
74 | return TEST_FAIL; | ||
75 | } | ||
76 | |||
77 | memset(&pe, 0, sizeof(struct perf_event_attr)); | ||
78 | pe.type = PERF_TYPE_BREAKPOINT; | ||
79 | pe.size = sizeof(struct perf_event_attr); | ||
80 | |||
81 | pe.config = 0; | ||
82 | pe.bp_type = HW_BREAKPOINT_X; | ||
83 | pe.bp_addr = (unsigned long) test_function; | ||
84 | pe.bp_len = sizeof(long); | ||
85 | |||
86 | pe.sample_period = THRESHOLD; | ||
87 | pe.sample_type = PERF_SAMPLE_IP; | ||
88 | pe.wakeup_events = 1; | ||
89 | |||
90 | pe.disabled = 1; | ||
91 | pe.exclude_kernel = 1; | ||
92 | pe.exclude_hv = 1; | ||
93 | |||
94 | fd = sys_perf_event_open(&pe, 0, -1, -1, 0); | ||
95 | if (fd < 0) { | ||
96 | pr_debug("failed opening event %llx\n", pe.config); | ||
97 | return TEST_FAIL; | ||
98 | } | ||
99 | |||
100 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); | ||
101 | fcntl(fd, F_SETSIG, SIGIO); | ||
102 | fcntl(fd, F_SETOWN, getpid()); | ||
103 | |||
104 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); | ||
105 | ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); | ||
106 | |||
107 | for (i = 0; i < EXECUTIONS; i++) | ||
108 | test_function(); | ||
109 | |||
110 | ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); | ||
111 | |||
112 | count = bp_count(fd); | ||
113 | |||
114 | close(fd); | ||
115 | |||
116 | pr_debug("count %lld, overflow %d\n", | ||
117 | count, overflows); | ||
118 | |||
119 | if (count != EXECUTIONS) { | ||
120 | pr_debug("\tWrong number of executions %lld != %d\n", | ||
121 | count, EXECUTIONS); | ||
122 | fails++; | ||
123 | } | ||
124 | |||
125 | if (overflows != EXECUTIONS / THRESHOLD) { | ||
126 | pr_debug("\tWrong number of overflows %d != %d\n", | ||
127 | overflows, EXECUTIONS / THRESHOLD); | ||
128 | fails++; | ||
129 | } | ||
130 | |||
131 | return fails ? TEST_FAIL : TEST_OK; | ||
132 | } | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 186f67535494..35b45f1466b5 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Builtin regression testing command: ever growing number of sanity tests | 4 | * Builtin regression testing command: ever growing number of sanity tests |
5 | */ | 5 | */ |
6 | #include "builtin.h" | 6 | #include "builtin.h" |
7 | #include "intlist.h" | ||
7 | #include "tests.h" | 8 | #include "tests.h" |
8 | #include "debug.h" | 9 | #include "debug.h" |
9 | #include "color.h" | 10 | #include "color.h" |
@@ -69,6 +70,30 @@ static struct test { | |||
69 | .func = test__attr, | 70 | .func = test__attr, |
70 | }, | 71 | }, |
71 | { | 72 | { |
73 | .desc = "Test matching and linking multiple hists", | ||
74 | .func = test__hists_link, | ||
75 | }, | ||
76 | { | ||
77 | .desc = "Try 'use perf' in python, checking link problems", | ||
78 | .func = test__python_use, | ||
79 | }, | ||
80 | { | ||
81 | .desc = "Test breakpoint overflow signal handler", | ||
82 | .func = test__bp_signal, | ||
83 | }, | ||
84 | { | ||
85 | .desc = "Test breakpoint overflow sampling", | ||
86 | .func = test__bp_signal_overflow, | ||
87 | }, | ||
88 | { | ||
89 | .desc = "Test number of exit event of a simple workload", | ||
90 | .func = test__task_exit, | ||
91 | }, | ||
92 | { | ||
93 | .desc = "Test software clock events have valid period values", | ||
94 | .func = test__sw_clock_freq, | ||
95 | }, | ||
96 | { | ||
72 | .func = NULL, | 97 | .func = NULL, |
73 | }, | 98 | }, |
74 | }; | 99 | }; |
@@ -97,7 +122,7 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
97 | return false; | 122 | return false; |
98 | } | 123 | } |
99 | 124 | ||
100 | static int __cmd_test(int argc, const char *argv[]) | 125 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) |
101 | { | 126 | { |
102 | int i = 0; | 127 | int i = 0; |
103 | int width = 0; | 128 | int width = 0; |
@@ -118,13 +143,28 @@ static int __cmd_test(int argc, const char *argv[]) | |||
118 | continue; | 143 | continue; |
119 | 144 | ||
120 | pr_info("%2d: %-*s:", i, width, tests[curr].desc); | 145 | pr_info("%2d: %-*s:", i, width, tests[curr].desc); |
146 | |||
147 | if (intlist__find(skiplist, i)) { | ||
148 | color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); | ||
149 | continue; | ||
150 | } | ||
151 | |||
121 | pr_debug("\n--- start ---\n"); | 152 | pr_debug("\n--- start ---\n"); |
122 | err = tests[curr].func(); | 153 | err = tests[curr].func(); |
123 | pr_debug("---- end ----\n%s:", tests[curr].desc); | 154 | pr_debug("---- end ----\n%s:", tests[curr].desc); |
124 | if (err) | 155 | |
125 | color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); | 156 | switch (err) { |
126 | else | 157 | case TEST_OK: |
127 | pr_info(" Ok\n"); | 158 | pr_info(" Ok\n"); |
159 | break; | ||
160 | case TEST_SKIP: | ||
161 | color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n"); | ||
162 | break; | ||
163 | case TEST_FAIL: | ||
164 | default: | ||
165 | color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); | ||
166 | break; | ||
167 | } | ||
128 | } | 168 | } |
129 | 169 | ||
130 | return 0; | 170 | return 0; |
@@ -152,11 +192,14 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
152 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | 192 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", |
153 | NULL, | 193 | NULL, |
154 | }; | 194 | }; |
195 | const char *skip = NULL; | ||
155 | const struct option test_options[] = { | 196 | const struct option test_options[] = { |
197 | OPT_STRING('s', "skip", &skip, "tests", "tests to skip"), | ||
156 | OPT_INCR('v', "verbose", &verbose, | 198 | OPT_INCR('v', "verbose", &verbose, |
157 | "be more verbose (show symbol address, etc)"), | 199 | "be more verbose (show symbol address, etc)"), |
158 | OPT_END() | 200 | OPT_END() |
159 | }; | 201 | }; |
202 | struct intlist *skiplist = NULL; | ||
160 | 203 | ||
161 | argc = parse_options(argc, argv, test_options, test_usage, 0); | 204 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
162 | if (argc >= 1 && !strcmp(argv[0], "list")) | 205 | if (argc >= 1 && !strcmp(argv[0], "list")) |
@@ -169,5 +212,8 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
169 | if (symbol__init() < 0) | 212 | if (symbol__init() < 0) |
170 | return -1; | 213 | return -1; |
171 | 214 | ||
172 | return __cmd_test(argc, argv); | 215 | if (skip != NULL) |
216 | skiplist = intlist__new(skip); | ||
217 | |||
218 | return __cmd_test(argc, argv, skiplist); | ||
173 | } | 219 | } |
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index e61fc828a158..0197bda9c461 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c | |||
@@ -8,7 +8,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) | |||
8 | char name[128]; | 8 | char name[128]; |
9 | int type, op, err = 0, ret = 0, i, idx; | 9 | int type, op, err = 0, ret = 0, i, idx; |
10 | struct perf_evsel *evsel; | 10 | struct perf_evsel *evsel; |
11 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 11 | struct perf_evlist *evlist = perf_evlist__new(); |
12 | 12 | ||
13 | if (evlist == NULL) | 13 | if (evlist == NULL) |
14 | return -ENOMEM; | 14 | return -ENOMEM; |
@@ -22,7 +22,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) | |||
22 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 22 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
23 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | 23 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, |
24 | name, sizeof(name)); | 24 | name, sizeof(name)); |
25 | err = parse_events(evlist, name, 0); | 25 | err = parse_events(evlist, name); |
26 | if (err) | 26 | if (err) |
27 | ret = err; | 27 | ret = err; |
28 | } | 28 | } |
@@ -64,13 +64,13 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) | |||
64 | { | 64 | { |
65 | int i, err; | 65 | int i, err; |
66 | struct perf_evsel *evsel; | 66 | struct perf_evsel *evsel; |
67 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 67 | struct perf_evlist *evlist = perf_evlist__new(); |
68 | 68 | ||
69 | if (evlist == NULL) | 69 | if (evlist == NULL) |
70 | return -ENOMEM; | 70 | return -ENOMEM; |
71 | 71 | ||
72 | for (i = 0; i < nr_names; ++i) { | 72 | for (i = 0; i < nr_names; ++i) { |
73 | err = parse_events(evlist, names[i], 0); | 73 | err = parse_events(evlist, names[i]); |
74 | if (err) { | 74 | if (err) { |
75 | pr_debug("failed to parse event '%s', err %d\n", | 75 | pr_debug("failed to parse event '%s', err %d\n", |
76 | names[i], err); | 76 | names[i], err); |
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c new file mode 100644 index 000000000000..89085a9615e2 --- /dev/null +++ b/tools/perf/tests/hists_link.c | |||
@@ -0,0 +1,500 @@ | |||
1 | #include "perf.h" | ||
2 | #include "tests.h" | ||
3 | #include "debug.h" | ||
4 | #include "symbol.h" | ||
5 | #include "sort.h" | ||
6 | #include "evsel.h" | ||
7 | #include "evlist.h" | ||
8 | #include "machine.h" | ||
9 | #include "thread.h" | ||
10 | #include "parse-events.h" | ||
11 | |||
12 | static struct { | ||
13 | u32 pid; | ||
14 | const char *comm; | ||
15 | } fake_threads[] = { | ||
16 | { 100, "perf" }, | ||
17 | { 200, "perf" }, | ||
18 | { 300, "bash" }, | ||
19 | }; | ||
20 | |||
21 | static 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 | |||
37 | struct fake_sym { | ||
38 | u64 start; | ||
39 | u64 length; | ||
40 | const char *name; | ||
41 | }; | ||
42 | |||
43 | static struct fake_sym perf_syms[] = { | ||
44 | { 700, 100, "main" }, | ||
45 | { 800, 100, "run_command" }, | ||
46 | { 900, 100, "cmd_record" }, | ||
47 | }; | ||
48 | |||
49 | static struct fake_sym bash_syms[] = { | ||
50 | { 700, 100, "main" }, | ||
51 | { 800, 100, "xmalloc" }, | ||
52 | { 900, 100, "xfree" }, | ||
53 | }; | ||
54 | |||
55 | static struct fake_sym libc_syms[] = { | ||
56 | { 700, 100, "malloc" }, | ||
57 | { 800, 100, "free" }, | ||
58 | { 900, 100, "realloc" }, | ||
59 | }; | ||
60 | |||
61 | static struct fake_sym kernel_syms[] = { | ||
62 | { 700, 100, "schedule" }, | ||
63 | { 800, 100, "page_fault" }, | ||
64 | { 900, 100, "sys_perf_event_open" }, | ||
65 | }; | ||
66 | |||
67 | static 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 | |||
78 | static 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 | if (thread == NULL) | ||
93 | goto out; | ||
94 | |||
95 | thread__set_comm(thread, fake_threads[i].comm); | ||
96 | } | ||
97 | |||
98 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
99 | union perf_event fake_mmap_event = { | ||
100 | .mmap = { | ||
101 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
102 | .pid = fake_mmap_info[i].pid, | ||
103 | .start = fake_mmap_info[i].start, | ||
104 | .len = 0x1000ULL, | ||
105 | .pgoff = 0ULL, | ||
106 | }, | ||
107 | }; | ||
108 | |||
109 | strcpy(fake_mmap_event.mmap.filename, | ||
110 | fake_mmap_info[i].filename); | ||
111 | |||
112 | machine__process_mmap_event(machine, &fake_mmap_event); | ||
113 | } | ||
114 | |||
115 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
116 | size_t k; | ||
117 | struct dso *dso; | ||
118 | |||
119 | dso = __dsos__findnew(&machine->user_dsos, | ||
120 | fake_symbols[i].dso_name); | ||
121 | if (dso == NULL) | ||
122 | goto out; | ||
123 | |||
124 | /* emulate dso__load() */ | ||
125 | dso__set_loaded(dso, MAP__FUNCTION); | ||
126 | |||
127 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
128 | struct symbol *sym; | ||
129 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
130 | |||
131 | sym = symbol__new(fsym->start, fsym->length, | ||
132 | STB_GLOBAL, fsym->name); | ||
133 | if (sym == NULL) | ||
134 | goto out; | ||
135 | |||
136 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | return machine; | ||
141 | |||
142 | out: | ||
143 | pr_debug("Not enough memory for machine setup\n"); | ||
144 | machine__delete_threads(machine); | ||
145 | machine__delete(machine); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | struct sample { | ||
150 | u32 pid; | ||
151 | u64 ip; | ||
152 | struct thread *thread; | ||
153 | struct map *map; | ||
154 | struct symbol *sym; | ||
155 | }; | ||
156 | |||
157 | static struct sample fake_common_samples[] = { | ||
158 | /* perf [kernel] schedule() */ | ||
159 | { .pid = 100, .ip = 0xf0000 + 700, }, | ||
160 | /* perf [perf] main() */ | ||
161 | { .pid = 200, .ip = 0x40000 + 700, }, | ||
162 | /* perf [perf] cmd_record() */ | ||
163 | { .pid = 200, .ip = 0x40000 + 900, }, | ||
164 | /* bash [bash] xmalloc() */ | ||
165 | { .pid = 300, .ip = 0x40000 + 800, }, | ||
166 | /* bash [libc] malloc() */ | ||
167 | { .pid = 300, .ip = 0x50000 + 700, }, | ||
168 | }; | ||
169 | |||
170 | static struct sample fake_samples[][5] = { | ||
171 | { | ||
172 | /* perf [perf] run_command() */ | ||
173 | { .pid = 100, .ip = 0x40000 + 800, }, | ||
174 | /* perf [libc] malloc() */ | ||
175 | { .pid = 100, .ip = 0x50000 + 700, }, | ||
176 | /* perf [kernel] page_fault() */ | ||
177 | { .pid = 100, .ip = 0xf0000 + 800, }, | ||
178 | /* perf [kernel] sys_perf_event_open() */ | ||
179 | { .pid = 200, .ip = 0xf0000 + 900, }, | ||
180 | /* bash [libc] free() */ | ||
181 | { .pid = 300, .ip = 0x50000 + 800, }, | ||
182 | }, | ||
183 | { | ||
184 | /* perf [libc] free() */ | ||
185 | { .pid = 200, .ip = 0x50000 + 800, }, | ||
186 | /* bash [libc] malloc() */ | ||
187 | { .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */ | ||
188 | /* bash [bash] xfee() */ | ||
189 | { .pid = 300, .ip = 0x40000 + 900, }, | ||
190 | /* bash [libc] realloc() */ | ||
191 | { .pid = 300, .ip = 0x50000 + 900, }, | ||
192 | /* bash [kernel] page_fault() */ | ||
193 | { .pid = 300, .ip = 0xf0000 + 800, }, | ||
194 | }, | ||
195 | }; | ||
196 | |||
197 | static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | ||
198 | { | ||
199 | struct perf_evsel *evsel; | ||
200 | struct addr_location al; | ||
201 | struct hist_entry *he; | ||
202 | struct perf_sample sample = { .cpu = 0, }; | ||
203 | size_t i = 0, k; | ||
204 | |||
205 | /* | ||
206 | * each evsel will have 10 samples - 5 common and 5 distinct. | ||
207 | * However the second evsel also has a collapsed entry for | ||
208 | * "bash [libc] malloc" so total 9 entries will be in the tree. | ||
209 | */ | ||
210 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
211 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | ||
212 | const union perf_event event = { | ||
213 | .ip = { | ||
214 | .header = { | ||
215 | .misc = PERF_RECORD_MISC_USER, | ||
216 | }, | ||
217 | .pid = fake_common_samples[k].pid, | ||
218 | .ip = fake_common_samples[k].ip, | ||
219 | }, | ||
220 | }; | ||
221 | |||
222 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
223 | &sample, 0) < 0) | ||
224 | goto out; | ||
225 | |||
226 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); | ||
227 | if (he == NULL) | ||
228 | goto out; | ||
229 | |||
230 | fake_common_samples[k].thread = al.thread; | ||
231 | fake_common_samples[k].map = al.map; | ||
232 | fake_common_samples[k].sym = al.sym; | ||
233 | } | ||
234 | |||
235 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { | ||
236 | const union perf_event event = { | ||
237 | .ip = { | ||
238 | .header = { | ||
239 | .misc = PERF_RECORD_MISC_USER, | ||
240 | }, | ||
241 | .pid = fake_samples[i][k].pid, | ||
242 | .ip = fake_samples[i][k].ip, | ||
243 | }, | ||
244 | }; | ||
245 | |||
246 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
247 | &sample, 0) < 0) | ||
248 | goto out; | ||
249 | |||
250 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); | ||
251 | if (he == NULL) | ||
252 | goto out; | ||
253 | |||
254 | fake_samples[i][k].thread = al.thread; | ||
255 | fake_samples[i][k].map = al.map; | ||
256 | fake_samples[i][k].sym = al.sym; | ||
257 | } | ||
258 | i++; | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | |||
263 | out: | ||
264 | pr_debug("Not enough memory for adding a hist entry\n"); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | static int find_sample(struct sample *samples, size_t nr_samples, | ||
269 | struct thread *t, struct map *m, struct symbol *s) | ||
270 | { | ||
271 | while (nr_samples--) { | ||
272 | if (samples->thread == t && samples->map == m && | ||
273 | samples->sym == s) | ||
274 | return 1; | ||
275 | samples++; | ||
276 | } | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int __validate_match(struct hists *hists) | ||
281 | { | ||
282 | size_t count = 0; | ||
283 | struct rb_root *root; | ||
284 | struct rb_node *node; | ||
285 | |||
286 | /* | ||
287 | * Only entries from fake_common_samples should have a pair. | ||
288 | */ | ||
289 | if (sort__need_collapse) | ||
290 | root = &hists->entries_collapsed; | ||
291 | else | ||
292 | root = hists->entries_in; | ||
293 | |||
294 | node = rb_first(root); | ||
295 | while (node) { | ||
296 | struct hist_entry *he; | ||
297 | |||
298 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
299 | |||
300 | if (hist_entry__has_pairs(he)) { | ||
301 | if (find_sample(fake_common_samples, | ||
302 | ARRAY_SIZE(fake_common_samples), | ||
303 | he->thread, he->ms.map, he->ms.sym)) { | ||
304 | count++; | ||
305 | } else { | ||
306 | pr_debug("Can't find the matched entry\n"); | ||
307 | return -1; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | node = rb_next(node); | ||
312 | } | ||
313 | |||
314 | if (count != ARRAY_SIZE(fake_common_samples)) { | ||
315 | pr_debug("Invalid count for matched entries: %zd of %zd\n", | ||
316 | count, ARRAY_SIZE(fake_common_samples)); | ||
317 | return -1; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int validate_match(struct hists *leader, struct hists *other) | ||
324 | { | ||
325 | return __validate_match(leader) || __validate_match(other); | ||
326 | } | ||
327 | |||
328 | static int __validate_link(struct hists *hists, int idx) | ||
329 | { | ||
330 | size_t count = 0; | ||
331 | size_t count_pair = 0; | ||
332 | size_t count_dummy = 0; | ||
333 | struct rb_root *root; | ||
334 | struct rb_node *node; | ||
335 | |||
336 | /* | ||
337 | * Leader hists (idx = 0) will have dummy entries from other, | ||
338 | * and some entries will have no pair. However every entry | ||
339 | * in other hists should have (dummy) pair. | ||
340 | */ | ||
341 | if (sort__need_collapse) | ||
342 | root = &hists->entries_collapsed; | ||
343 | else | ||
344 | root = hists->entries_in; | ||
345 | |||
346 | node = rb_first(root); | ||
347 | while (node) { | ||
348 | struct hist_entry *he; | ||
349 | |||
350 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
351 | |||
352 | if (hist_entry__has_pairs(he)) { | ||
353 | if (!find_sample(fake_common_samples, | ||
354 | ARRAY_SIZE(fake_common_samples), | ||
355 | he->thread, he->ms.map, he->ms.sym) && | ||
356 | !find_sample(fake_samples[idx], | ||
357 | ARRAY_SIZE(fake_samples[idx]), | ||
358 | he->thread, he->ms.map, he->ms.sym)) { | ||
359 | count_dummy++; | ||
360 | } | ||
361 | count_pair++; | ||
362 | } else if (idx) { | ||
363 | pr_debug("A entry from the other hists should have pair\n"); | ||
364 | return -1; | ||
365 | } | ||
366 | |||
367 | count++; | ||
368 | node = rb_next(node); | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * Note that we have a entry collapsed in the other (idx = 1) hists. | ||
373 | */ | ||
374 | if (idx == 0) { | ||
375 | if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) { | ||
376 | pr_debug("Invalid count of dummy entries: %zd of %zd\n", | ||
377 | count_dummy, ARRAY_SIZE(fake_samples[1]) - 1); | ||
378 | return -1; | ||
379 | } | ||
380 | if (count != count_pair + ARRAY_SIZE(fake_samples[0])) { | ||
381 | pr_debug("Invalid count of total leader entries: %zd of %zd\n", | ||
382 | count, count_pair + ARRAY_SIZE(fake_samples[0])); | ||
383 | return -1; | ||
384 | } | ||
385 | } else { | ||
386 | if (count != count_pair) { | ||
387 | pr_debug("Invalid count of total other entries: %zd of %zd\n", | ||
388 | count, count_pair); | ||
389 | return -1; | ||
390 | } | ||
391 | if (count_dummy > 0) { | ||
392 | pr_debug("Other hists should not have dummy entries: %zd\n", | ||
393 | count_dummy); | ||
394 | return -1; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int validate_link(struct hists *leader, struct hists *other) | ||
402 | { | ||
403 | return __validate_link(leader, 0) || __validate_link(other, 1); | ||
404 | } | ||
405 | |||
406 | static 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, he->thread->comm, 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 | |||
433 | int test__hists_link(void) | ||
434 | { | ||
435 | int err = -1; | ||
436 | struct machines machines; | ||
437 | struct machine *machine = NULL; | ||
438 | struct perf_evsel *evsel, *first; | ||
439 | struct perf_evlist *evlist = perf_evlist__new(); | ||
440 | |||
441 | if (evlist == NULL) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | err = parse_events(evlist, "cpu-clock"); | ||
445 | if (err) | ||
446 | goto out; | ||
447 | err = parse_events(evlist, "task-clock"); | ||
448 | if (err) | ||
449 | goto out; | ||
450 | |||
451 | /* default sort order (comm,dso,sym) will be used */ | ||
452 | if (setup_sorting() < 0) | ||
453 | goto out; | ||
454 | |||
455 | machines__init(&machines); | ||
456 | |||
457 | /* setup threads/dso/map/symbols also */ | ||
458 | machine = setup_fake_machine(&machines); | ||
459 | if (!machine) | ||
460 | goto out; | ||
461 | |||
462 | if (verbose > 1) | ||
463 | machine__fprintf(machine, stderr); | ||
464 | |||
465 | /* process sample events */ | ||
466 | err = add_hist_entries(evlist, machine); | ||
467 | if (err < 0) | ||
468 | goto out; | ||
469 | |||
470 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
471 | hists__collapse_resort(&evsel->hists); | ||
472 | |||
473 | if (verbose > 2) | ||
474 | print_hists(&evsel->hists); | ||
475 | } | ||
476 | |||
477 | first = perf_evlist__first(evlist); | ||
478 | evsel = perf_evlist__last(evlist); | ||
479 | |||
480 | /* match common entries */ | ||
481 | hists__match(&first->hists, &evsel->hists); | ||
482 | err = validate_match(&first->hists, &evsel->hists); | ||
483 | if (err) | ||
484 | goto out; | ||
485 | |||
486 | /* link common and/or dummy entries */ | ||
487 | hists__link(&first->hists, &evsel->hists); | ||
488 | err = validate_link(&first->hists, &evsel->hists); | ||
489 | if (err) | ||
490 | goto out; | ||
491 | |||
492 | err = 0; | ||
493 | |||
494 | out: | ||
495 | /* tear down everything */ | ||
496 | perf_evlist__delete(evlist); | ||
497 | machines__exit(&machines); | ||
498 | |||
499 | return err; | ||
500 | } | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make new file mode 100644 index 000000000000..c441a2875128 --- /dev/null +++ b/tools/perf/tests/make | |||
@@ -0,0 +1,138 @@ | |||
1 | PERF := . | ||
2 | MK := Makefile | ||
3 | |||
4 | # standard single make variable specified | ||
5 | make_clean_all := clean all | ||
6 | make_python_perf_so := python/perf.so | ||
7 | make_debug := DEBUG=1 | ||
8 | make_no_libperl := NO_LIBPERL=1 | ||
9 | make_no_libpython := NO_LIBPYTHON=1 | ||
10 | make_no_scripts := NO_LIBPYTHON=1 NO_LIBPERL=1 | ||
11 | make_no_newt := NO_NEWT=1 | ||
12 | make_no_slang := NO_SLANG=1 | ||
13 | make_no_gtk2 := NO_GTK2=1 | ||
14 | make_no_ui := NO_NEWT=1 NO_SLANG=1 NO_GTK2=1 | ||
15 | make_no_demangle := NO_DEMANGLE=1 | ||
16 | make_no_libelf := NO_LIBELF=1 | ||
17 | make_no_libunwind := NO_LIBUNWIND=1 | ||
18 | make_no_backtrace := NO_BACKTRACE=1 | ||
19 | make_no_libnuma := NO_LIBNUMA=1 | ||
20 | make_no_libaudit := NO_LIBAUDIT=1 | ||
21 | make_no_libbionic := NO_LIBBIONIC=1 | ||
22 | make_tags := tags | ||
23 | make_cscope := cscope | ||
24 | make_help := help | ||
25 | make_doc := doc | ||
26 | make_perf_o := perf.o | ||
27 | make_util_map_o := util/map.o | ||
28 | |||
29 | # all the NO_* variable combined | ||
30 | make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 | ||
31 | make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 | ||
32 | make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 | ||
33 | |||
34 | # $(run) contains all available tests | ||
35 | run := make_pure | ||
36 | run += make_clean_all | ||
37 | run += make_python_perf_so | ||
38 | run += make_debug | ||
39 | run += make_no_libperl | ||
40 | run += make_no_libpython | ||
41 | run += make_no_scripts | ||
42 | run += make_no_newt | ||
43 | run += make_no_slang | ||
44 | run += make_no_gtk2 | ||
45 | run += make_no_ui | ||
46 | run += make_no_demangle | ||
47 | run += make_no_libelf | ||
48 | run += make_no_libunwind | ||
49 | run += make_no_backtrace | ||
50 | run += make_no_libnuma | ||
51 | run += make_no_libaudit | ||
52 | run += make_no_libbionic | ||
53 | run += make_tags | ||
54 | run += make_cscope | ||
55 | run += make_help | ||
56 | run += make_doc | ||
57 | run += make_perf_o | ||
58 | run += make_util_map_o | ||
59 | run += make_minimal | ||
60 | |||
61 | # $(run_O) contains same portion of $(run) tests with '_O' attached | ||
62 | # to distinguish O=... tests | ||
63 | run_O := $(addsuffix _O,$(run)) | ||
64 | |||
65 | # disable some tests for O=... | ||
66 | run_O := $(filter-out make_python_perf_so_O,$(run_O)) | ||
67 | |||
68 | # define test for each compile as 'test_NAME' variable | ||
69 | # with the test itself as a value | ||
70 | test_make_tags = test -f tags | ||
71 | test_make_cscope = test -f cscope.out | ||
72 | |||
73 | test_make_tags_O := $(test_make_tags) | ||
74 | test_make_cscope_O := $(test_make_cscope) | ||
75 | |||
76 | test_ok := true | ||
77 | test_make_help := $(test_ok) | ||
78 | test_make_doc := $(test_ok) | ||
79 | test_make_help_O := $(test_ok) | ||
80 | test_make_doc_O := $(test_ok) | ||
81 | |||
82 | test_make_python_perf_so := test -f $(PERF)/python/perf.so | ||
83 | |||
84 | test_make_perf_o := test -f $(PERF)/perf.o | ||
85 | test_make_util_map_o := test -f $(PERF)/util/map.o | ||
86 | |||
87 | # Kbuild tests only | ||
88 | #test_make_python_perf_so_O := test -f $$TMP/tools/perf/python/perf.so | ||
89 | #test_make_perf_o_O := test -f $$TMP/tools/perf/perf.o | ||
90 | #test_make_util_map_o_O := test -f $$TMP/tools/perf/util/map.o | ||
91 | |||
92 | test_make_perf_o_O := true | ||
93 | test_make_util_map_o_O := true | ||
94 | |||
95 | test_default = test -x $(PERF)/perf | ||
96 | test = $(if $(test_$1),$(test_$1),$(test_default)) | ||
97 | |||
98 | test_default_O = test -x $$TMP/perf | ||
99 | test_O = $(if $(test_$1),$(test_$1),$(test_default_O)) | ||
100 | |||
101 | all: | ||
102 | |||
103 | ifdef DEBUG | ||
104 | d := $(info run $(run)) | ||
105 | d := $(info run_O $(run_O)) | ||
106 | endif | ||
107 | |||
108 | MAKEFLAGS := --no-print-directory | ||
109 | |||
110 | clean := @(cd $(PERF); make -s -f $(MK) clean >/dev/null) | ||
111 | |||
112 | $(run): | ||
113 | $(call clean) | ||
114 | @cmd="cd $(PERF) && make -f $(MK) $($@)"; \ | ||
115 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | ||
116 | ( eval $$cmd ) >> $@ 2>&1; \ | ||
117 | echo " test: $(call test,$@)"; \ | ||
118 | $(call test,$@) && \ | ||
119 | rm -f $@ | ||
120 | |||
121 | $(run_O): | ||
122 | $(call clean) | ||
123 | @TMP=$$(mktemp -d); \ | ||
124 | cmd="cd $(PERF) && make -f $(MK) $($(patsubst %_O,%,$@)) O=$$TMP"; \ | ||
125 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | ||
126 | ( eval $$cmd ) >> $@ 2>&1 && \ | ||
127 | echo " test: $(call test_O,$@)"; \ | ||
128 | $(call test_O,$@) && \ | ||
129 | rm -f $@ && \ | ||
130 | rm -rf $$TMP | ||
131 | |||
132 | all: $(run) $(run_O) | ||
133 | @echo OK | ||
134 | |||
135 | out: $(run_O) | ||
136 | @echo OK | ||
137 | |||
138 | .PHONY: all $(run) $(run_O) clean | ||
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index e1746811e14b..5b1b5aba722b 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -22,36 +22,16 @@ int test__basic_mmap(void) | |||
22 | struct thread_map *threads; | 22 | struct thread_map *threads; |
23 | struct cpu_map *cpus; | 23 | struct cpu_map *cpus; |
24 | struct perf_evlist *evlist; | 24 | struct perf_evlist *evlist; |
25 | struct perf_event_attr attr = { | ||
26 | .type = PERF_TYPE_TRACEPOINT, | ||
27 | .read_format = PERF_FORMAT_ID, | ||
28 | .sample_type = PERF_SAMPLE_ID, | ||
29 | .watermark = 0, | ||
30 | }; | ||
31 | cpu_set_t cpu_set; | 25 | cpu_set_t cpu_set; |
32 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | 26 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", |
33 | "getpgid", }; | 27 | "getpgid", }; |
34 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | 28 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, |
35 | (void*)getpgid }; | 29 | (void*)getpgid }; |
36 | #define nsyscalls ARRAY_SIZE(syscall_names) | 30 | #define nsyscalls ARRAY_SIZE(syscall_names) |
37 | int ids[nsyscalls]; | ||
38 | unsigned int nr_events[nsyscalls], | 31 | unsigned int nr_events[nsyscalls], |
39 | expected_nr_events[nsyscalls], i, j; | 32 | expected_nr_events[nsyscalls], i, j; |
40 | struct perf_evsel *evsels[nsyscalls], *evsel; | 33 | struct perf_evsel *evsels[nsyscalls], *evsel; |
41 | 34 | ||
42 | for (i = 0; i < nsyscalls; ++i) { | ||
43 | char name[64]; | ||
44 | |||
45 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
46 | ids[i] = trace_event__id(name); | ||
47 | if (ids[i] < 0) { | ||
48 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
49 | return -1; | ||
50 | } | ||
51 | nr_events[i] = 0; | ||
52 | expected_nr_events[i] = random() % 257; | ||
53 | } | ||
54 | |||
55 | threads = thread_map__new(-1, getpid(), UINT_MAX); | 35 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
56 | if (threads == NULL) { | 36 | if (threads == NULL) { |
57 | pr_debug("thread_map__new\n"); | 37 | pr_debug("thread_map__new\n"); |
@@ -73,24 +53,27 @@ int test__basic_mmap(void) | |||
73 | goto out_free_cpus; | 53 | goto out_free_cpus; |
74 | } | 54 | } |
75 | 55 | ||
76 | evlist = perf_evlist__new(cpus, threads); | 56 | evlist = perf_evlist__new(); |
77 | if (evlist == NULL) { | 57 | if (evlist == NULL) { |
78 | pr_debug("perf_evlist__new\n"); | 58 | pr_debug("perf_evlist__new\n"); |
79 | goto out_free_cpus; | 59 | goto out_free_cpus; |
80 | } | 60 | } |
81 | 61 | ||
82 | /* anonymous union fields, can't be initialized above */ | 62 | perf_evlist__set_maps(evlist, cpus, threads); |
83 | attr.wakeup_events = 1; | ||
84 | attr.sample_period = 1; | ||
85 | 63 | ||
86 | for (i = 0; i < nsyscalls; ++i) { | 64 | for (i = 0; i < nsyscalls; ++i) { |
87 | attr.config = ids[i]; | 65 | char name[64]; |
88 | evsels[i] = perf_evsel__new(&attr, i); | 66 | |
67 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
68 | evsels[i] = perf_evsel__newtp("syscalls", name, i); | ||
89 | if (evsels[i] == NULL) { | 69 | if (evsels[i] == NULL) { |
90 | pr_debug("perf_evsel__new\n"); | 70 | pr_debug("perf_evsel__new\n"); |
91 | goto out_free_evlist; | 71 | goto out_free_evlist; |
92 | } | 72 | } |
93 | 73 | ||
74 | evsels[i]->attr.wakeup_events = 1; | ||
75 | perf_evsel__set_sample_id(evsels[i]); | ||
76 | |||
94 | perf_evlist__add(evlist, evsels[i]); | 77 | perf_evlist__add(evlist, evsels[i]); |
95 | 78 | ||
96 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | 79 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { |
@@ -99,6 +82,9 @@ int test__basic_mmap(void) | |||
99 | strerror(errno)); | 82 | strerror(errno)); |
100 | goto out_close_fd; | 83 | goto out_close_fd; |
101 | } | 84 | } |
85 | |||
86 | nr_events[i] = 0; | ||
87 | expected_nr_events[i] = 1 + rand() % 127; | ||
102 | } | 88 | } |
103 | 89 | ||
104 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | 90 | if (perf_evlist__mmap(evlist, 128, true) < 0) { |
@@ -128,6 +114,7 @@ int test__basic_mmap(void) | |||
128 | goto out_munmap; | 114 | goto out_munmap; |
129 | } | 115 | } |
130 | 116 | ||
117 | err = -1; | ||
131 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 118 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
132 | if (evsel == NULL) { | 119 | if (evsel == NULL) { |
133 | pr_debug("event with id %" PRIu64 | 120 | pr_debug("event with id %" PRIu64 |
@@ -137,16 +124,17 @@ int test__basic_mmap(void) | |||
137 | nr_events[evsel->idx]++; | 124 | nr_events[evsel->idx]++; |
138 | } | 125 | } |
139 | 126 | ||
127 | err = 0; | ||
140 | list_for_each_entry(evsel, &evlist->entries, node) { | 128 | list_for_each_entry(evsel, &evlist->entries, node) { |
141 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | 129 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { |
142 | pr_debug("expected %d %s events, got %d\n", | 130 | pr_debug("expected %d %s events, got %d\n", |
143 | expected_nr_events[evsel->idx], | 131 | expected_nr_events[evsel->idx], |
144 | perf_evsel__name(evsel), nr_events[evsel->idx]); | 132 | perf_evsel__name(evsel), nr_events[evsel->idx]); |
133 | err = -1; | ||
145 | goto out_munmap; | 134 | goto out_munmap; |
146 | } | 135 | } |
147 | } | 136 | } |
148 | 137 | ||
149 | err = 0; | ||
150 | out_munmap: | 138 | out_munmap: |
151 | perf_evlist__munmap(evlist); | 139 | perf_evlist__munmap(evlist); |
152 | out_close_fd: | 140 | out_close_fd: |
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c index 31072aba0d54..b0657a9ccda6 100644 --- a/tools/perf/tests/open-syscall-all-cpus.c +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
@@ -7,20 +7,12 @@ | |||
7 | int test__open_syscall_event_on_all_cpus(void) | 7 | int test__open_syscall_event_on_all_cpus(void) |
8 | { | 8 | { |
9 | int err = -1, fd, cpu; | 9 | int err = -1, fd, cpu; |
10 | struct thread_map *threads; | ||
11 | struct cpu_map *cpus; | 10 | struct cpu_map *cpus; |
12 | struct perf_evsel *evsel; | 11 | struct perf_evsel *evsel; |
13 | struct perf_event_attr attr; | ||
14 | unsigned int nr_open_calls = 111, i; | 12 | unsigned int nr_open_calls = 111, i; |
15 | cpu_set_t cpu_set; | 13 | cpu_set_t cpu_set; |
16 | int id = trace_event__id("sys_enter_open"); | 14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
17 | 15 | ||
18 | if (id < 0) { | ||
19 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
20 | return -1; | ||
21 | } | ||
22 | |||
23 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
24 | if (threads == NULL) { | 16 | if (threads == NULL) { |
25 | pr_debug("thread_map__new\n"); | 17 | pr_debug("thread_map__new\n"); |
26 | return -1; | 18 | return -1; |
@@ -32,15 +24,11 @@ int test__open_syscall_event_on_all_cpus(void) | |||
32 | goto out_thread_map_delete; | 24 | goto out_thread_map_delete; |
33 | } | 25 | } |
34 | 26 | ||
35 | |||
36 | CPU_ZERO(&cpu_set); | 27 | CPU_ZERO(&cpu_set); |
37 | 28 | ||
38 | memset(&attr, 0, sizeof(attr)); | 29 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); |
39 | attr.type = PERF_TYPE_TRACEPOINT; | ||
40 | attr.config = id; | ||
41 | evsel = perf_evsel__new(&attr, 0); | ||
42 | if (evsel == NULL) { | 30 | if (evsel == NULL) { |
43 | pr_debug("perf_evsel__new\n"); | 31 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); |
44 | goto out_thread_map_delete; | 32 | goto out_thread_map_delete; |
45 | } | 33 | } |
46 | 34 | ||
@@ -110,6 +98,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
110 | } | 98 | } |
111 | } | 99 | } |
112 | 100 | ||
101 | perf_evsel__free_counts(evsel); | ||
113 | out_close_fd: | 102 | out_close_fd: |
114 | perf_evsel__close_fd(evsel, 1, threads->nr); | 103 | perf_evsel__close_fd(evsel, 1, threads->nr); |
115 | out_evsel_delete: | 104 | out_evsel_delete: |
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 1c52fdc1164e..fc5b9fca8b47 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -18,7 +18,7 @@ int test__syscall_open_tp_fields(void) | |||
18 | }; | 18 | }; |
19 | const char *filename = "/etc/passwd"; | 19 | const char *filename = "/etc/passwd"; |
20 | int flags = O_RDONLY | O_DIRECTORY; | 20 | int flags = O_RDONLY | O_DIRECTORY; |
21 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 21 | struct perf_evlist *evlist = perf_evlist__new(); |
22 | struct perf_evsel *evsel; | 22 | struct perf_evsel *evsel; |
23 | int err = -1, i, nr_events = 0, nr_polls = 0; | 23 | int err = -1, i, nr_events = 0, nr_polls = 0; |
24 | 24 | ||
@@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(void) | |||
48 | err = perf_evlist__open(evlist); | 48 | err = perf_evlist__open(evlist); |
49 | if (err < 0) { | 49 | if (err < 0) { |
50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); |
51 | goto out_delete_evlist; | 51 | goto out_delete_maps; |
52 | } | 52 | } |
53 | 53 | ||
54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
55 | if (err < 0) { | 55 | if (err < 0) { |
56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); |
57 | goto out_delete_evlist; | 57 | goto out_close_evlist; |
58 | } | 58 | } |
59 | 59 | ||
60 | perf_evlist__enable(evlist); | 60 | perf_evlist__enable(evlist); |
@@ -110,6 +110,10 @@ out_ok: | |||
110 | err = 0; | 110 | err = 0; |
111 | out_munmap: | 111 | out_munmap: |
112 | perf_evlist__munmap(evlist); | 112 | perf_evlist__munmap(evlist); |
113 | out_close_evlist: | ||
114 | perf_evlist__close(evlist); | ||
115 | out_delete_maps: | ||
116 | perf_evlist__delete_maps(evlist); | ||
113 | out_delete_evlist: | 117 | out_delete_evlist: |
114 | perf_evlist__delete(evlist); | 118 | perf_evlist__delete(evlist); |
115 | out: | 119 | out: |
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c index 98be8b518b4f..befc0671f95d 100644 --- a/tools/perf/tests/open-syscall.c +++ b/tools/perf/tests/open-syscall.c | |||
@@ -6,29 +6,18 @@ | |||
6 | int test__open_syscall_event(void) | 6 | int test__open_syscall_event(void) |
7 | { | 7 | { |
8 | int err = -1, fd; | 8 | int err = -1, fd; |
9 | struct thread_map *threads; | ||
10 | struct perf_evsel *evsel; | 9 | struct perf_evsel *evsel; |
11 | struct perf_event_attr attr; | ||
12 | unsigned int nr_open_calls = 111, i; | 10 | unsigned int nr_open_calls = 111, i; |
13 | int id = trace_event__id("sys_enter_open"); | 11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
14 | 12 | ||
15 | if (id < 0) { | ||
16 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
17 | return -1; | ||
18 | } | ||
19 | |||
20 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
21 | if (threads == NULL) { | 13 | if (threads == NULL) { |
22 | pr_debug("thread_map__new\n"); | 14 | pr_debug("thread_map__new\n"); |
23 | return -1; | 15 | return -1; |
24 | } | 16 | } |
25 | 17 | ||
26 | memset(&attr, 0, sizeof(attr)); | 18 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); |
27 | attr.type = PERF_TYPE_TRACEPOINT; | ||
28 | attr.config = id; | ||
29 | evsel = perf_evsel__new(&attr, 0); | ||
30 | if (evsel == NULL) { | 19 | if (evsel == NULL) { |
31 | pr_debug("perf_evsel__new\n"); | 20 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); |
32 | goto out_thread_map_delete; | 21 | goto out_thread_map_delete; |
33 | } | 22 | } |
34 | 23 | ||
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 32ee478905eb..0275bab4ea9e 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "evsel.h" | 3 | #include "evsel.h" |
4 | #include "evlist.h" | 4 | #include "evlist.h" |
5 | #include "sysfs.h" | 5 | #include "sysfs.h" |
6 | #include <lk/debugfs.h> | ||
6 | #include "tests.h" | 7 | #include "tests.h" |
7 | #include <linux/hw_breakpoint.h> | 8 | #include <linux/hw_breakpoint.h> |
8 | 9 | ||
@@ -22,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist) | |||
22 | struct perf_evsel *evsel = perf_evlist__first(evlist); | 23 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
23 | 24 | ||
24 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 25 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
26 | TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); | ||
25 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | 27 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); |
26 | TEST_ASSERT_VAL("wrong sample_type", | 28 | TEST_ASSERT_VAL("wrong sample_type", |
27 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); | 29 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); |
@@ -34,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) | |||
34 | struct perf_evsel *evsel; | 36 | struct perf_evsel *evsel; |
35 | 37 | ||
36 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); | 38 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); |
39 | TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); | ||
37 | 40 | ||
38 | list_for_each_entry(evsel, &evlist->entries, node) { | 41 | list_for_each_entry(evsel, &evlist->entries, node) { |
39 | TEST_ASSERT_VAL("wrong type", | 42 | TEST_ASSERT_VAL("wrong type", |
@@ -463,10 +466,10 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) | |||
463 | 466 | ||
464 | static int test__checkterms_simple(struct list_head *terms) | 467 | static int test__checkterms_simple(struct list_head *terms) |
465 | { | 468 | { |
466 | struct parse_events__term *term; | 469 | struct parse_events_term *term; |
467 | 470 | ||
468 | /* config=10 */ | 471 | /* config=10 */ |
469 | term = list_entry(terms->next, struct parse_events__term, list); | 472 | term = list_entry(terms->next, struct parse_events_term, list); |
470 | TEST_ASSERT_VAL("wrong type term", | 473 | TEST_ASSERT_VAL("wrong type term", |
471 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); | 474 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); |
472 | TEST_ASSERT_VAL("wrong type val", | 475 | TEST_ASSERT_VAL("wrong type val", |
@@ -475,7 +478,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
475 | TEST_ASSERT_VAL("wrong config", !term->config); | 478 | TEST_ASSERT_VAL("wrong config", !term->config); |
476 | 479 | ||
477 | /* config1 */ | 480 | /* config1 */ |
478 | term = list_entry(term->list.next, struct parse_events__term, list); | 481 | term = list_entry(term->list.next, struct parse_events_term, list); |
479 | TEST_ASSERT_VAL("wrong type term", | 482 | TEST_ASSERT_VAL("wrong type term", |
480 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); | 483 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); |
481 | TEST_ASSERT_VAL("wrong type val", | 484 | TEST_ASSERT_VAL("wrong type val", |
@@ -484,7 +487,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
484 | TEST_ASSERT_VAL("wrong config", !term->config); | 487 | TEST_ASSERT_VAL("wrong config", !term->config); |
485 | 488 | ||
486 | /* config2=3 */ | 489 | /* config2=3 */ |
487 | term = list_entry(term->list.next, struct parse_events__term, list); | 490 | term = list_entry(term->list.next, struct parse_events_term, list); |
488 | TEST_ASSERT_VAL("wrong type term", | 491 | TEST_ASSERT_VAL("wrong type term", |
489 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); | 492 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); |
490 | TEST_ASSERT_VAL("wrong type val", | 493 | TEST_ASSERT_VAL("wrong type val", |
@@ -493,7 +496,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
493 | TEST_ASSERT_VAL("wrong config", !term->config); | 496 | TEST_ASSERT_VAL("wrong config", !term->config); |
494 | 497 | ||
495 | /* umask=1*/ | 498 | /* umask=1*/ |
496 | term = list_entry(term->list.next, struct parse_events__term, list); | 499 | term = list_entry(term->list.next, struct parse_events_term, list); |
497 | TEST_ASSERT_VAL("wrong type term", | 500 | TEST_ASSERT_VAL("wrong type term", |
498 | term->type_term == PARSE_EVENTS__TERM_TYPE_USER); | 501 | term->type_term == PARSE_EVENTS__TERM_TYPE_USER); |
499 | TEST_ASSERT_VAL("wrong type val", | 502 | TEST_ASSERT_VAL("wrong type val", |
@@ -509,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
509 | struct perf_evsel *evsel, *leader; | 512 | struct perf_evsel *evsel, *leader; |
510 | 513 | ||
511 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 514 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
515 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
512 | 516 | ||
513 | /* instructions:k */ | 517 | /* instructions:k */ |
514 | evsel = leader = perf_evlist__first(evlist); | 518 | evsel = leader = perf_evlist__first(evlist); |
@@ -521,7 +525,9 @@ static int test__group1(struct perf_evlist *evlist) | |||
521 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 525 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
522 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 526 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
523 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 527 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
524 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 528 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
529 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
530 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
525 | 531 | ||
526 | /* cycles:upp */ | 532 | /* cycles:upp */ |
527 | evsel = perf_evsel__next(evsel); | 533 | evsel = perf_evsel__next(evsel); |
@@ -536,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
536 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 542 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
537 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 543 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
538 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 544 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
545 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
539 | 546 | ||
540 | return 0; | 547 | return 0; |
541 | } | 548 | } |
@@ -545,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
545 | struct perf_evsel *evsel, *leader; | 552 | struct perf_evsel *evsel, *leader; |
546 | 553 | ||
547 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | 554 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); |
555 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
548 | 556 | ||
549 | /* faults + :ku modifier */ | 557 | /* faults + :ku modifier */ |
550 | evsel = leader = perf_evlist__first(evlist); | 558 | evsel = leader = perf_evlist__first(evlist); |
@@ -557,7 +565,9 @@ static int test__group2(struct perf_evlist *evlist) | |||
557 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 565 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
558 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 566 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
559 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 567 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
560 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 568 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
569 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
570 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
561 | 571 | ||
562 | /* cache-references + :u modifier */ | 572 | /* cache-references + :u modifier */ |
563 | evsel = perf_evsel__next(evsel); | 573 | evsel = perf_evsel__next(evsel); |
@@ -567,10 +577,11 @@ static int test__group2(struct perf_evlist *evlist) | |||
567 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 577 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
568 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 578 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
569 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 579 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
570 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 580 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
571 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 581 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
572 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 582 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
573 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 583 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
584 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
574 | 585 | ||
575 | /* cycles:k */ | 586 | /* cycles:k */ |
576 | evsel = perf_evsel__next(evsel); | 587 | evsel = perf_evsel__next(evsel); |
@@ -583,7 +594,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
583 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 594 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
584 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 595 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
585 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 596 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
586 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 597 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
587 | 598 | ||
588 | return 0; | 599 | return 0; |
589 | } | 600 | } |
@@ -593,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
593 | struct perf_evsel *evsel, *leader; | 604 | struct perf_evsel *evsel, *leader; |
594 | 605 | ||
595 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | 606 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); |
607 | TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups); | ||
596 | 608 | ||
597 | /* group1 syscalls:sys_enter_open:H */ | 609 | /* group1 syscalls:sys_enter_open:H */ |
598 | evsel = leader = perf_evlist__first(evlist); | 610 | evsel = leader = perf_evlist__first(evlist); |
@@ -606,9 +618,11 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
606 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 618 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
607 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 619 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
608 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 620 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
609 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 621 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
610 | TEST_ASSERT_VAL("wrong group name", | 622 | TEST_ASSERT_VAL("wrong group name", |
611 | !strcmp(leader->group_name, "group1")); | 623 | !strcmp(leader->group_name, "group1")); |
624 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
625 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
612 | 626 | ||
613 | /* group1 cycles:kppp */ | 627 | /* group1 cycles:kppp */ |
614 | evsel = perf_evsel__next(evsel); | 628 | evsel = perf_evsel__next(evsel); |
@@ -624,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
624 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); | 638 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); |
625 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 639 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
626 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 640 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
641 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
627 | 642 | ||
628 | /* group2 cycles + G modifier */ | 643 | /* group2 cycles + G modifier */ |
629 | evsel = leader = perf_evsel__next(evsel); | 644 | evsel = leader = perf_evsel__next(evsel); |
@@ -636,9 +651,11 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
636 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 651 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
637 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 652 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
638 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 653 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
639 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 654 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
640 | TEST_ASSERT_VAL("wrong group name", | 655 | TEST_ASSERT_VAL("wrong group name", |
641 | !strcmp(leader->group_name, "group2")); | 656 | !strcmp(leader->group_name, "group2")); |
657 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
658 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
642 | 659 | ||
643 | /* group2 1:3 + G modifier */ | 660 | /* group2 1:3 + G modifier */ |
644 | evsel = perf_evsel__next(evsel); | 661 | evsel = perf_evsel__next(evsel); |
@@ -651,6 +668,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
651 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 668 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
652 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 669 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
653 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 670 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
671 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
654 | 672 | ||
655 | /* instructions:u */ | 673 | /* instructions:u */ |
656 | evsel = perf_evsel__next(evsel); | 674 | evsel = perf_evsel__next(evsel); |
@@ -663,7 +681,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
663 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 681 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
664 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 682 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
665 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 683 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
666 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 684 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
667 | 685 | ||
668 | return 0; | 686 | return 0; |
669 | } | 687 | } |
@@ -673,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
673 | struct perf_evsel *evsel, *leader; | 691 | struct perf_evsel *evsel, *leader; |
674 | 692 | ||
675 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 693 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
694 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
676 | 695 | ||
677 | /* cycles:u + p */ | 696 | /* cycles:u + p */ |
678 | evsel = leader = perf_evlist__first(evlist); | 697 | evsel = leader = perf_evlist__first(evlist); |
@@ -687,7 +706,9 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
687 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 706 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
688 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); | 707 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); |
689 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 708 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
690 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 709 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
710 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
711 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
691 | 712 | ||
692 | /* instructions:kp + p */ | 713 | /* instructions:kp + p */ |
693 | evsel = perf_evsel__next(evsel); | 714 | evsel = perf_evsel__next(evsel); |
@@ -702,6 +723,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
702 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 723 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
703 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 724 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
704 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 725 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
726 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
705 | 727 | ||
706 | return 0; | 728 | return 0; |
707 | } | 729 | } |
@@ -711,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
711 | struct perf_evsel *evsel, *leader; | 733 | struct perf_evsel *evsel, *leader; |
712 | 734 | ||
713 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | 735 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); |
736 | TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups); | ||
714 | 737 | ||
715 | /* cycles + G */ | 738 | /* cycles + G */ |
716 | evsel = leader = perf_evlist__first(evlist); | 739 | evsel = leader = perf_evlist__first(evlist); |
@@ -724,7 +747,9 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
724 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 747 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
725 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 748 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
726 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 749 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
727 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 750 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
751 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
752 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
728 | 753 | ||
729 | /* instructions + G */ | 754 | /* instructions + G */ |
730 | evsel = perf_evsel__next(evsel); | 755 | evsel = perf_evsel__next(evsel); |
@@ -738,6 +763,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
738 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 763 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
739 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 764 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
740 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 765 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
766 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
741 | 767 | ||
742 | /* cycles:G */ | 768 | /* cycles:G */ |
743 | evsel = leader = perf_evsel__next(evsel); | 769 | evsel = leader = perf_evsel__next(evsel); |
@@ -751,7 +777,9 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
751 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 777 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
752 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 778 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
753 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 779 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
754 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 780 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
781 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
782 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
755 | 783 | ||
756 | /* instructions:G */ | 784 | /* instructions:G */ |
757 | evsel = perf_evsel__next(evsel); | 785 | evsel = perf_evsel__next(evsel); |
@@ -765,6 +793,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
765 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 793 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
766 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 794 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
767 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 795 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
796 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
768 | 797 | ||
769 | /* cycles */ | 798 | /* cycles */ |
770 | evsel = perf_evsel__next(evsel); | 799 | evsel = perf_evsel__next(evsel); |
@@ -777,18 +806,235 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
777 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 806 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
778 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 807 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
779 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 808 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
780 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); | 809 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
810 | |||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int test__group_gh1(struct perf_evlist *evlist) | ||
815 | { | ||
816 | struct perf_evsel *evsel, *leader; | ||
817 | |||
818 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
819 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
820 | |||
821 | /* cycles + :H group modifier */ | ||
822 | evsel = leader = perf_evlist__first(evlist); | ||
823 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
824 | TEST_ASSERT_VAL("wrong config", | ||
825 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
826 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
827 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
828 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
829 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
830 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
831 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
832 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
833 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
834 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
835 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
836 | |||
837 | /* cache-misses:G + :H group modifier */ | ||
838 | evsel = perf_evsel__next(evsel); | ||
839 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
840 | TEST_ASSERT_VAL("wrong config", | ||
841 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
842 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
843 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
844 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
845 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
846 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
847 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
848 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
849 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
850 | |||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int test__group_gh2(struct perf_evlist *evlist) | ||
855 | { | ||
856 | struct perf_evsel *evsel, *leader; | ||
857 | |||
858 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
859 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
860 | |||
861 | /* cycles + :G group modifier */ | ||
862 | evsel = leader = perf_evlist__first(evlist); | ||
863 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
864 | TEST_ASSERT_VAL("wrong config", | ||
865 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
866 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
867 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
868 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
869 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
870 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
871 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
872 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
873 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
874 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
875 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
876 | |||
877 | /* cache-misses:H + :G group modifier */ | ||
878 | evsel = perf_evsel__next(evsel); | ||
879 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
880 | TEST_ASSERT_VAL("wrong config", | ||
881 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
882 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
883 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
884 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
885 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
886 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
887 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
888 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
889 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
890 | |||
891 | return 0; | ||
892 | } | ||
893 | |||
894 | static int test__group_gh3(struct perf_evlist *evlist) | ||
895 | { | ||
896 | struct perf_evsel *evsel, *leader; | ||
897 | |||
898 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
899 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
900 | |||
901 | /* cycles:G + :u group modifier */ | ||
902 | evsel = leader = perf_evlist__first(evlist); | ||
903 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
904 | TEST_ASSERT_VAL("wrong config", | ||
905 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
906 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
907 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
908 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
909 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
910 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
911 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
912 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
913 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
914 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
915 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
916 | |||
917 | /* cache-misses:H + :u group modifier */ | ||
918 | evsel = perf_evsel__next(evsel); | ||
919 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
920 | TEST_ASSERT_VAL("wrong config", | ||
921 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
922 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
923 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
924 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
925 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
926 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
927 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
928 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
929 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
930 | |||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | static int test__group_gh4(struct perf_evlist *evlist) | ||
935 | { | ||
936 | struct perf_evsel *evsel, *leader; | ||
937 | |||
938 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
939 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
940 | |||
941 | /* cycles:G + :uG group modifier */ | ||
942 | evsel = leader = perf_evlist__first(evlist); | ||
943 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
944 | TEST_ASSERT_VAL("wrong config", | ||
945 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
946 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
947 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
948 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
949 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
950 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
951 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
952 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
953 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
954 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
955 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
956 | |||
957 | /* cache-misses:H + :uG group modifier */ | ||
958 | evsel = perf_evsel__next(evsel); | ||
959 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
960 | TEST_ASSERT_VAL("wrong config", | ||
961 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
962 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
963 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
964 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
965 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
966 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
967 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
968 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
969 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
781 | 970 | ||
782 | return 0; | 971 | return 0; |
783 | } | 972 | } |
784 | 973 | ||
785 | struct test__event_st { | 974 | static int count_tracepoints(void) |
975 | { | ||
976 | char events_path[PATH_MAX]; | ||
977 | struct dirent *events_ent; | ||
978 | DIR *events_dir; | ||
979 | int cnt = 0; | ||
980 | |||
981 | scnprintf(events_path, PATH_MAX, "%s/tracing/events", | ||
982 | debugfs_find_mountpoint()); | ||
983 | |||
984 | events_dir = opendir(events_path); | ||
985 | |||
986 | TEST_ASSERT_VAL("Can't open events dir", events_dir); | ||
987 | |||
988 | while ((events_ent = readdir(events_dir))) { | ||
989 | char sys_path[PATH_MAX]; | ||
990 | struct dirent *sys_ent; | ||
991 | DIR *sys_dir; | ||
992 | |||
993 | if (!strcmp(events_ent->d_name, ".") | ||
994 | || !strcmp(events_ent->d_name, "..") | ||
995 | || !strcmp(events_ent->d_name, "enable") | ||
996 | || !strcmp(events_ent->d_name, "header_event") | ||
997 | || !strcmp(events_ent->d_name, "header_page")) | ||
998 | continue; | ||
999 | |||
1000 | scnprintf(sys_path, PATH_MAX, "%s/%s", | ||
1001 | events_path, events_ent->d_name); | ||
1002 | |||
1003 | sys_dir = opendir(sys_path); | ||
1004 | TEST_ASSERT_VAL("Can't open sys dir", sys_dir); | ||
1005 | |||
1006 | while ((sys_ent = readdir(sys_dir))) { | ||
1007 | if (!strcmp(sys_ent->d_name, ".") | ||
1008 | || !strcmp(sys_ent->d_name, "..") | ||
1009 | || !strcmp(sys_ent->d_name, "enable") | ||
1010 | || !strcmp(sys_ent->d_name, "filter")) | ||
1011 | continue; | ||
1012 | |||
1013 | cnt++; | ||
1014 | } | ||
1015 | |||
1016 | closedir(sys_dir); | ||
1017 | } | ||
1018 | |||
1019 | closedir(events_dir); | ||
1020 | return cnt; | ||
1021 | } | ||
1022 | |||
1023 | static int test__all_tracepoints(struct perf_evlist *evlist) | ||
1024 | { | ||
1025 | TEST_ASSERT_VAL("wrong events count", | ||
1026 | count_tracepoints() == evlist->nr_entries); | ||
1027 | |||
1028 | return test__checkevent_tracepoint_multi(evlist); | ||
1029 | } | ||
1030 | |||
1031 | struct evlist_test { | ||
786 | const char *name; | 1032 | const char *name; |
787 | __u32 type; | 1033 | __u32 type; |
788 | int (*check)(struct perf_evlist *evlist); | 1034 | int (*check)(struct perf_evlist *evlist); |
789 | }; | 1035 | }; |
790 | 1036 | ||
791 | static struct test__event_st test__events[] = { | 1037 | static struct evlist_test test__events[] = { |
792 | [0] = { | 1038 | [0] = { |
793 | .name = "syscalls:sys_enter_open", | 1039 | .name = "syscalls:sys_enter_open", |
794 | .check = test__checkevent_tracepoint, | 1040 | .check = test__checkevent_tracepoint, |
@@ -921,9 +1167,29 @@ static struct test__event_st test__events[] = { | |||
921 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", | 1167 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", |
922 | .check = test__group5, | 1168 | .check = test__group5, |
923 | }, | 1169 | }, |
1170 | [33] = { | ||
1171 | .name = "*:*", | ||
1172 | .check = test__all_tracepoints, | ||
1173 | }, | ||
1174 | [34] = { | ||
1175 | .name = "{cycles,cache-misses:G}:H", | ||
1176 | .check = test__group_gh1, | ||
1177 | }, | ||
1178 | [35] = { | ||
1179 | .name = "{cycles,cache-misses:H}:G", | ||
1180 | .check = test__group_gh2, | ||
1181 | }, | ||
1182 | [36] = { | ||
1183 | .name = "{cycles:G,cache-misses:H}:u", | ||
1184 | .check = test__group_gh3, | ||
1185 | }, | ||
1186 | [37] = { | ||
1187 | .name = "{cycles:G,cache-misses:H}:uG", | ||
1188 | .check = test__group_gh4, | ||
1189 | }, | ||
924 | }; | 1190 | }; |
925 | 1191 | ||
926 | static struct test__event_st test__events_pmu[] = { | 1192 | static struct evlist_test test__events_pmu[] = { |
927 | [0] = { | 1193 | [0] = { |
928 | .name = "cpu/config=10,config1,config2=3,period=1000/u", | 1194 | .name = "cpu/config=10,config1,config2=3,period=1000/u", |
929 | .check = test__checkevent_pmu, | 1195 | .check = test__checkevent_pmu, |
@@ -934,29 +1200,29 @@ static struct test__event_st test__events_pmu[] = { | |||
934 | }, | 1200 | }, |
935 | }; | 1201 | }; |
936 | 1202 | ||
937 | struct test__term { | 1203 | struct terms_test { |
938 | const char *str; | 1204 | const char *str; |
939 | __u32 type; | 1205 | __u32 type; |
940 | int (*check)(struct list_head *terms); | 1206 | int (*check)(struct list_head *terms); |
941 | }; | 1207 | }; |
942 | 1208 | ||
943 | static struct test__term test__terms[] = { | 1209 | static struct terms_test test__terms[] = { |
944 | [0] = { | 1210 | [0] = { |
945 | .str = "config=10,config1,config2=3,umask=1", | 1211 | .str = "config=10,config1,config2=3,umask=1", |
946 | .check = test__checkterms_simple, | 1212 | .check = test__checkterms_simple, |
947 | }, | 1213 | }, |
948 | }; | 1214 | }; |
949 | 1215 | ||
950 | static int test_event(struct test__event_st *e) | 1216 | static int test_event(struct evlist_test *e) |
951 | { | 1217 | { |
952 | struct perf_evlist *evlist; | 1218 | struct perf_evlist *evlist; |
953 | int ret; | 1219 | int ret; |
954 | 1220 | ||
955 | evlist = perf_evlist__new(NULL, NULL); | 1221 | evlist = perf_evlist__new(); |
956 | if (evlist == NULL) | 1222 | if (evlist == NULL) |
957 | return -ENOMEM; | 1223 | return -ENOMEM; |
958 | 1224 | ||
959 | ret = parse_events(evlist, e->name, 0); | 1225 | ret = parse_events(evlist, e->name); |
960 | if (ret) { | 1226 | if (ret) { |
961 | pr_debug("failed to parse event '%s', err %d\n", | 1227 | pr_debug("failed to parse event '%s', err %d\n", |
962 | e->name, ret); | 1228 | e->name, ret); |
@@ -969,13 +1235,13 @@ static int test_event(struct test__event_st *e) | |||
969 | return ret; | 1235 | return ret; |
970 | } | 1236 | } |
971 | 1237 | ||
972 | static int test_events(struct test__event_st *events, unsigned cnt) | 1238 | static int test_events(struct evlist_test *events, unsigned cnt) |
973 | { | 1239 | { |
974 | int ret1, ret2 = 0; | 1240 | int ret1, ret2 = 0; |
975 | unsigned i; | 1241 | unsigned i; |
976 | 1242 | ||
977 | for (i = 0; i < cnt; i++) { | 1243 | for (i = 0; i < cnt; i++) { |
978 | struct test__event_st *e = &events[i]; | 1244 | struct evlist_test *e = &events[i]; |
979 | 1245 | ||
980 | pr_debug("running test %d '%s'\n", i, e->name); | 1246 | pr_debug("running test %d '%s'\n", i, e->name); |
981 | ret1 = test_event(e); | 1247 | ret1 = test_event(e); |
@@ -986,7 +1252,7 @@ static int test_events(struct test__event_st *events, unsigned cnt) | |||
986 | return ret2; | 1252 | return ret2; |
987 | } | 1253 | } |
988 | 1254 | ||
989 | static int test_term(struct test__term *t) | 1255 | static int test_term(struct terms_test *t) |
990 | { | 1256 | { |
991 | struct list_head *terms; | 1257 | struct list_head *terms; |
992 | int ret; | 1258 | int ret; |
@@ -1010,13 +1276,13 @@ static int test_term(struct test__term *t) | |||
1010 | return ret; | 1276 | return ret; |
1011 | } | 1277 | } |
1012 | 1278 | ||
1013 | static int test_terms(struct test__term *terms, unsigned cnt) | 1279 | static int test_terms(struct terms_test *terms, unsigned cnt) |
1014 | { | 1280 | { |
1015 | int ret = 0; | 1281 | int ret = 0; |
1016 | unsigned i; | 1282 | unsigned i; |
1017 | 1283 | ||
1018 | for (i = 0; i < cnt; i++) { | 1284 | for (i = 0; i < cnt; i++) { |
1019 | struct test__term *t = &terms[i]; | 1285 | struct terms_test *t = &terms[i]; |
1020 | 1286 | ||
1021 | pr_debug("running test %d '%s'\n", i, t->str); | 1287 | pr_debug("running test %d '%s'\n", i, t->str); |
1022 | ret = test_term(t); | 1288 | ret = test_term(t); |
@@ -1055,7 +1321,7 @@ static int test_pmu_events(void) | |||
1055 | 1321 | ||
1056 | ret = stat(path, &st); | 1322 | ret = stat(path, &st); |
1057 | if (ret) { | 1323 | if (ret) { |
1058 | pr_debug("ommiting PMU cpu events tests\n"); | 1324 | pr_debug("omitting PMU cpu events tests\n"); |
1059 | return 0; | 1325 | return 0; |
1060 | } | 1326 | } |
1061 | 1327 | ||
@@ -1067,7 +1333,7 @@ static int test_pmu_events(void) | |||
1067 | 1333 | ||
1068 | while (!ret && (ent = readdir(dir))) { | 1334 | while (!ret && (ent = readdir(dir))) { |
1069 | #define MAX_NAME 100 | 1335 | #define MAX_NAME 100 |
1070 | struct test__event_st e; | 1336 | struct evlist_test e; |
1071 | char name[MAX_NAME]; | 1337 | char name[MAX_NAME]; |
1072 | 1338 | ||
1073 | if (!strcmp(ent->d_name, ".") || | 1339 | if (!strcmp(ent->d_name, ".") || |
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 70e0d4421df8..72d8881873b0 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c | |||
@@ -45,7 +45,7 @@ int test__PERF_RECORD(void) | |||
45 | }; | 45 | }; |
46 | cpu_set_t cpu_mask; | 46 | cpu_set_t cpu_mask; |
47 | size_t cpu_mask_size = sizeof(cpu_mask); | 47 | size_t cpu_mask_size = sizeof(cpu_mask); |
48 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 48 | struct perf_evlist *evlist = perf_evlist__new(); |
49 | struct perf_evsel *evsel; | 49 | struct perf_evsel *evsel; |
50 | struct perf_sample sample; | 50 | struct perf_sample sample; |
51 | const char *cmd = "sleep"; | 51 | const char *cmd = "sleep"; |
@@ -93,25 +93,26 @@ int test__PERF_RECORD(void) | |||
93 | * so that we have time to open the evlist (calling sys_perf_event_open | 93 | * so that we have time to open the evlist (calling sys_perf_event_open |
94 | * on all the fds) and then mmap them. | 94 | * on all the fds) and then mmap them. |
95 | */ | 95 | */ |
96 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | 96 | err = perf_evlist__prepare_workload(evlist, &opts.target, argv, |
97 | false, false); | ||
97 | if (err < 0) { | 98 | if (err < 0) { |
98 | pr_debug("Couldn't run the workload!\n"); | 99 | pr_debug("Couldn't run the workload!\n"); |
99 | goto out_delete_evlist; | 100 | goto out_delete_maps; |
100 | } | 101 | } |
101 | 102 | ||
102 | /* | 103 | /* |
103 | * Config the evsels, setting attr->comm on the first one, etc. | 104 | * Config the evsels, setting attr->comm on the first one, etc. |
104 | */ | 105 | */ |
105 | evsel = perf_evlist__first(evlist); | 106 | evsel = perf_evlist__first(evlist); |
106 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | 107 | perf_evsel__set_sample_bit(evsel, CPU); |
107 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | 108 | perf_evsel__set_sample_bit(evsel, TID); |
108 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | 109 | perf_evsel__set_sample_bit(evsel, TIME); |
109 | perf_evlist__config_attrs(evlist, &opts); | 110 | perf_evlist__config(evlist, &opts); |
110 | 111 | ||
111 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); | 112 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); |
112 | if (err < 0) { | 113 | if (err < 0) { |
113 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | 114 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); |
114 | goto out_delete_evlist; | 115 | goto out_delete_maps; |
115 | } | 116 | } |
116 | 117 | ||
117 | cpu = err; | 118 | cpu = err; |
@@ -121,7 +122,7 @@ int test__PERF_RECORD(void) | |||
121 | */ | 122 | */ |
122 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { | 123 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { |
123 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | 124 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); |
124 | goto out_delete_evlist; | 125 | goto out_delete_maps; |
125 | } | 126 | } |
126 | 127 | ||
127 | /* | 128 | /* |
@@ -131,7 +132,7 @@ int test__PERF_RECORD(void) | |||
131 | err = perf_evlist__open(evlist); | 132 | err = perf_evlist__open(evlist); |
132 | if (err < 0) { | 133 | if (err < 0) { |
133 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 134 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); |
134 | goto out_delete_evlist; | 135 | goto out_delete_maps; |
135 | } | 136 | } |
136 | 137 | ||
137 | /* | 138 | /* |
@@ -142,7 +143,7 @@ int test__PERF_RECORD(void) | |||
142 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | 143 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); |
143 | if (err < 0) { | 144 | if (err < 0) { |
144 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 145 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); |
145 | goto out_delete_evlist; | 146 | goto out_close_evlist; |
146 | } | 147 | } |
147 | 148 | ||
148 | /* | 149 | /* |
@@ -305,6 +306,10 @@ found_exit: | |||
305 | } | 306 | } |
306 | out_err: | 307 | out_err: |
307 | perf_evlist__munmap(evlist); | 308 | perf_evlist__munmap(evlist); |
309 | out_close_evlist: | ||
310 | perf_evlist__close(evlist); | ||
311 | out_delete_maps: | ||
312 | perf_evlist__delete_maps(evlist); | ||
308 | out_delete_evlist: | 313 | out_delete_evlist: |
309 | perf_evlist__delete(evlist); | 314 | perf_evlist__delete(evlist); |
310 | out: | 315 | out: |
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index a5f379863b8f..12b322fa3475 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c | |||
@@ -19,10 +19,8 @@ static struct test_format { | |||
19 | { "krava23", "config2:28-29,38\n", }, | 19 | { "krava23", "config2:28-29,38\n", }, |
20 | }; | 20 | }; |
21 | 21 | ||
22 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
23 | |||
24 | /* Simulated users input. */ | 22 | /* Simulated users input. */ |
25 | static struct parse_events__term test_terms[] = { | 23 | static struct parse_events_term test_terms[] = { |
26 | { | 24 | { |
27 | .config = (char *) "krava01", | 25 | .config = (char *) "krava01", |
28 | .val.num = 15, | 26 | .val.num = 15, |
@@ -78,7 +76,6 @@ static struct parse_events__term test_terms[] = { | |||
78 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | 76 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, |
79 | }, | 77 | }, |
80 | }; | 78 | }; |
81 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
82 | 79 | ||
83 | /* | 80 | /* |
84 | * Prepare format directory data, exported by kernel | 81 | * Prepare format directory data, exported by kernel |
@@ -93,7 +90,7 @@ static char *test_format_dir_get(void) | |||
93 | if (!mkdtemp(dir)) | 90 | if (!mkdtemp(dir)) |
94 | return NULL; | 91 | return NULL; |
95 | 92 | ||
96 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | 93 | for (i = 0; i < ARRAY_SIZE(test_formats); i++) { |
97 | static char name[PATH_MAX]; | 94 | static char name[PATH_MAX]; |
98 | struct test_format *format = &test_formats[i]; | 95 | struct test_format *format = &test_formats[i]; |
99 | FILE *file; | 96 | FILE *file; |
@@ -130,14 +127,12 @@ static struct list_head *test_terms_list(void) | |||
130 | static LIST_HEAD(terms); | 127 | static LIST_HEAD(terms); |
131 | unsigned int i; | 128 | unsigned int i; |
132 | 129 | ||
133 | for (i = 0; i < TERMS_CNT; i++) | 130 | for (i = 0; i < ARRAY_SIZE(test_terms); i++) |
134 | list_add_tail(&test_terms[i].list, &terms); | 131 | list_add_tail(&test_terms[i].list, &terms); |
135 | 132 | ||
136 | return &terms; | 133 | return &terms; |
137 | } | 134 | } |
138 | 135 | ||
139 | #undef TERMS_CNT | ||
140 | |||
141 | int test__pmu(void) | 136 | int test__pmu(void) |
142 | { | 137 | { |
143 | char *format = test_format_dir_get(); | 138 | char *format = test_format_dir_get(); |
diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c new file mode 100644 index 000000000000..7760277c6def --- /dev/null +++ b/tools/perf/tests/python-use.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Just test if we can load the python binding. | ||
3 | */ | ||
4 | |||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include "tests.h" | ||
8 | |||
9 | extern int verbose; | ||
10 | |||
11 | int test__python_use(void) | ||
12 | { | ||
13 | char *cmd; | ||
14 | int ret; | ||
15 | |||
16 | if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s", | ||
17 | PYTHONPATH, PYTHON, verbose ? "" : "2> /dev/null") < 0) | ||
18 | return -1; | ||
19 | |||
20 | ret = system(cmd) ? -1 : 0; | ||
21 | free(cmd); | ||
22 | return ret; | ||
23 | } | ||
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c new file mode 100644 index 000000000000..2e41e2d32ccc --- /dev/null +++ b/tools/perf/tests/sw-clock.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <signal.h> | ||
4 | #include <sys/mman.h> | ||
5 | |||
6 | #include "tests.h" | ||
7 | #include "util/evsel.h" | ||
8 | #include "util/evlist.h" | ||
9 | #include "util/cpumap.h" | ||
10 | #include "util/thread_map.h" | ||
11 | |||
12 | #define NR_LOOPS 1000000 | ||
13 | |||
14 | /* | ||
15 | * This test will open software clock events (cpu-clock, task-clock) | ||
16 | * then check their frequency -> period conversion has no artifact of | ||
17 | * setting period to 1 forcefully. | ||
18 | */ | ||
19 | static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | ||
20 | { | ||
21 | int i, err = -1; | ||
22 | volatile int tmp = 0; | ||
23 | u64 total_periods = 0; | ||
24 | int nr_samples = 0; | ||
25 | union perf_event *event; | ||
26 | struct perf_evsel *evsel; | ||
27 | struct perf_evlist *evlist; | ||
28 | struct perf_event_attr attr = { | ||
29 | .type = PERF_TYPE_SOFTWARE, | ||
30 | .config = clock_id, | ||
31 | .sample_type = PERF_SAMPLE_PERIOD, | ||
32 | .exclude_kernel = 1, | ||
33 | .disabled = 1, | ||
34 | .freq = 1, | ||
35 | }; | ||
36 | |||
37 | attr.sample_freq = 10000; | ||
38 | |||
39 | evlist = perf_evlist__new(); | ||
40 | if (evlist == NULL) { | ||
41 | pr_debug("perf_evlist__new\n"); | ||
42 | return -1; | ||
43 | } | ||
44 | |||
45 | evsel = perf_evsel__new(&attr, 0); | ||
46 | if (evsel == NULL) { | ||
47 | pr_debug("perf_evsel__new\n"); | ||
48 | goto out_free_evlist; | ||
49 | } | ||
50 | perf_evlist__add(evlist, evsel); | ||
51 | |||
52 | evlist->cpus = cpu_map__dummy_new(); | ||
53 | evlist->threads = thread_map__new_by_tid(getpid()); | ||
54 | if (!evlist->cpus || !evlist->threads) { | ||
55 | err = -ENOMEM; | ||
56 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
57 | goto out_delete_maps; | ||
58 | } | ||
59 | |||
60 | perf_evlist__open(evlist); | ||
61 | |||
62 | err = perf_evlist__mmap(evlist, 128, true); | ||
63 | if (err < 0) { | ||
64 | pr_debug("failed to mmap event: %d (%s)\n", errno, | ||
65 | strerror(errno)); | ||
66 | goto out_close_evlist; | ||
67 | } | ||
68 | |||
69 | perf_evlist__enable(evlist); | ||
70 | |||
71 | /* collect samples */ | ||
72 | for (i = 0; i < NR_LOOPS; i++) | ||
73 | tmp++; | ||
74 | |||
75 | perf_evlist__disable(evlist); | ||
76 | |||
77 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
78 | struct perf_sample sample; | ||
79 | |||
80 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
81 | continue; | ||
82 | |||
83 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
84 | if (err < 0) { | ||
85 | pr_debug("Error during parse sample\n"); | ||
86 | goto out_unmap_evlist; | ||
87 | } | ||
88 | |||
89 | total_periods += sample.period; | ||
90 | nr_samples++; | ||
91 | } | ||
92 | |||
93 | if ((u64) nr_samples == total_periods) { | ||
94 | pr_debug("All (%d) samples have period value of 1!\n", | ||
95 | nr_samples); | ||
96 | err = -1; | ||
97 | } | ||
98 | |||
99 | out_unmap_evlist: | ||
100 | perf_evlist__munmap(evlist); | ||
101 | out_close_evlist: | ||
102 | perf_evlist__close(evlist); | ||
103 | out_delete_maps: | ||
104 | perf_evlist__delete_maps(evlist); | ||
105 | out_free_evlist: | ||
106 | perf_evlist__delete(evlist); | ||
107 | return err; | ||
108 | } | ||
109 | |||
110 | int test__sw_clock_freq(void) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | ret = __test__sw_clock_freq(PERF_COUNT_SW_CPU_CLOCK); | ||
115 | if (!ret) | ||
116 | ret = __test__sw_clock_freq(PERF_COUNT_SW_TASK_CLOCK); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c new file mode 100644 index 000000000000..28fe5894b061 --- /dev/null +++ b/tools/perf/tests/task-exit.c | |||
@@ -0,0 +1,123 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | #include <signal.h> | ||
8 | |||
9 | static int exited; | ||
10 | static int nr_exit; | ||
11 | |||
12 | static void sig_handler(int sig) | ||
13 | { | ||
14 | exited = 1; | ||
15 | |||
16 | if (sig == SIGUSR1) | ||
17 | nr_exit = -1; | ||
18 | } | ||
19 | |||
20 | /* | ||
21 | * This test will start a workload that does nothing then it checks | ||
22 | * if the number of exit event reported by the kernel is 1 or not | ||
23 | * in order to check the kernel returns correct number of event. | ||
24 | */ | ||
25 | int test__task_exit(void) | ||
26 | { | ||
27 | int err = -1; | ||
28 | union perf_event *event; | ||
29 | struct perf_evsel *evsel; | ||
30 | struct perf_evlist *evlist; | ||
31 | struct perf_target target = { | ||
32 | .uid = UINT_MAX, | ||
33 | .uses_mmap = true, | ||
34 | }; | ||
35 | const char *argv[] = { "true", NULL }; | ||
36 | |||
37 | signal(SIGCHLD, sig_handler); | ||
38 | signal(SIGUSR1, sig_handler); | ||
39 | |||
40 | evlist = perf_evlist__new(); | ||
41 | if (evlist == NULL) { | ||
42 | pr_debug("perf_evlist__new\n"); | ||
43 | return -1; | ||
44 | } | ||
45 | /* | ||
46 | * We need at least one evsel in the evlist, use the default | ||
47 | * one: "cycles". | ||
48 | */ | ||
49 | err = perf_evlist__add_default(evlist); | ||
50 | if (err < 0) { | ||
51 | pr_debug("Not enough memory to create evsel\n"); | ||
52 | goto out_free_evlist; | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Create maps of threads and cpus to monitor. In this case | ||
57 | * we start with all threads and cpus (-1, -1) but then in | ||
58 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
59 | * we're monitoring, the one forked there. | ||
60 | */ | ||
61 | evlist->cpus = cpu_map__dummy_new(); | ||
62 | evlist->threads = thread_map__new_by_tid(-1); | ||
63 | if (!evlist->cpus || !evlist->threads) { | ||
64 | err = -ENOMEM; | ||
65 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
66 | goto out_delete_maps; | ||
67 | } | ||
68 | |||
69 | err = perf_evlist__prepare_workload(evlist, &target, argv, false, true); | ||
70 | if (err < 0) { | ||
71 | pr_debug("Couldn't run the workload!\n"); | ||
72 | goto out_delete_maps; | ||
73 | } | ||
74 | |||
75 | evsel = perf_evlist__first(evlist); | ||
76 | evsel->attr.task = 1; | ||
77 | evsel->attr.sample_freq = 0; | ||
78 | evsel->attr.inherit = 0; | ||
79 | evsel->attr.watermark = 0; | ||
80 | evsel->attr.wakeup_events = 1; | ||
81 | evsel->attr.exclude_kernel = 1; | ||
82 | |||
83 | err = perf_evlist__open(evlist); | ||
84 | if (err < 0) { | ||
85 | pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); | ||
86 | goto out_delete_maps; | ||
87 | } | ||
88 | |||
89 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
90 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
91 | strerror(errno)); | ||
92 | goto out_close_evlist; | ||
93 | } | ||
94 | |||
95 | perf_evlist__start_workload(evlist); | ||
96 | |||
97 | retry: | ||
98 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
99 | if (event->header.type != PERF_RECORD_EXIT) | ||
100 | continue; | ||
101 | |||
102 | nr_exit++; | ||
103 | } | ||
104 | |||
105 | if (!exited || !nr_exit) { | ||
106 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
107 | goto retry; | ||
108 | } | ||
109 | |||
110 | if (nr_exit != 1) { | ||
111 | pr_debug("received %d EXIT records\n", nr_exit); | ||
112 | err = -1; | ||
113 | } | ||
114 | |||
115 | perf_evlist__munmap(evlist); | ||
116 | out_close_evlist: | ||
117 | perf_evlist__close(evlist); | ||
118 | out_delete_maps: | ||
119 | perf_evlist__delete_maps(evlist); | ||
120 | out_free_evlist: | ||
121 | perf_evlist__delete(evlist); | ||
122 | return err; | ||
123 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index fc121edab016..dd7feae2d37b 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -1,6 +1,12 @@ | |||
1 | #ifndef TESTS_H | 1 | #ifndef TESTS_H |
2 | #define TESTS_H | 2 | #define TESTS_H |
3 | 3 | ||
4 | enum { | ||
5 | TEST_OK = 0, | ||
6 | TEST_FAIL = -1, | ||
7 | TEST_SKIP = -2, | ||
8 | }; | ||
9 | |||
4 | /* Tests */ | 10 | /* Tests */ |
5 | int test__vmlinux_matches_kallsyms(void); | 11 | int test__vmlinux_matches_kallsyms(void); |
6 | int test__open_syscall_event(void); | 12 | int test__open_syscall_event(void); |
@@ -15,8 +21,11 @@ int test__pmu(void); | |||
15 | int test__attr(void); | 21 | int test__attr(void); |
16 | int test__dso_data(void); | 22 | int test__dso_data(void); |
17 | int test__parse_events(void); | 23 | int test__parse_events(void); |
18 | 24 | int test__hists_link(void); | |
19 | /* Util */ | 25 | int test__python_use(void); |
20 | int trace_event__id(const char *evname); | 26 | int test__bp_signal(void); |
27 | int test__bp_signal_overflow(void); | ||
28 | int test__task_exit(void); | ||
29 | int test__sw_clock_freq(void); | ||
21 | 30 | ||
22 | #endif /* TESTS_H */ | 31 | #endif /* TESTS_H */ |
diff --git a/tools/perf/tests/util.c b/tools/perf/tests/util.c deleted file mode 100644 index 748f2e8f6961..000000000000 --- a/tools/perf/tests/util.c +++ /dev/null | |||
@@ -1,30 +0,0 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <sys/types.h> | ||
5 | #include <sys/stat.h> | ||
6 | #include <fcntl.h> | ||
7 | #include "tests.h" | ||
8 | #include "debugfs.h" | ||
9 | |||
10 | int trace_event__id(const char *evname) | ||
11 | { | ||
12 | char *filename; | ||
13 | int err = -1, fd; | ||
14 | |||
15 | if (asprintf(&filename, | ||
16 | "%s/syscalls/%s/id", | ||
17 | tracing_events_path, evname) < 0) | ||
18 | return -1; | ||
19 | |||
20 | fd = open(filename, O_RDONLY); | ||
21 | if (fd >= 0) { | ||
22 | char id[16]; | ||
23 | if (read(fd, id, sizeof(id)) > 0) | ||
24 | err = atoi(id); | ||
25 | close(fd); | ||
26 | } | ||
27 | |||
28 | free(filename); | ||
29 | return err; | ||
30 | } | ||
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 0d1cdbee2f59..7b4c4d26d1ba 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -44,7 +44,7 @@ int test__vmlinux_matches_kallsyms(void) | |||
44 | */ | 44 | */ |
45 | if (machine__create_kernel_maps(&kallsyms) < 0) { | 45 | if (machine__create_kernel_maps(&kallsyms) < 0) { |
46 | pr_debug("machine__create_kernel_maps "); | 46 | pr_debug("machine__create_kernel_maps "); |
47 | return -1; | 47 | goto out; |
48 | } | 48 | } |
49 | 49 | ||
50 | /* | 50 | /* |
@@ -101,7 +101,8 @@ int test__vmlinux_matches_kallsyms(void) | |||
101 | */ | 101 | */ |
102 | if (machine__load_vmlinux_path(&vmlinux, type, | 102 | if (machine__load_vmlinux_path(&vmlinux, type, |
103 | vmlinux_matches_kallsyms_filter) <= 0) { | 103 | vmlinux_matches_kallsyms_filter) <= 0) { |
104 | pr_debug("machine__load_vmlinux_path "); | 104 | pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n"); |
105 | err = TEST_SKIP; | ||
105 | goto out; | 106 | goto out; |
106 | } | 107 | } |
107 | 108 | ||
@@ -226,5 +227,7 @@ detour: | |||
226 | map__fprintf(pos, stderr); | 227 | map__fprintf(pos, stderr); |
227 | } | 228 | } |
228 | out: | 229 | out: |
230 | machine__exit(&kallsyms); | ||
231 | machine__exit(&vmlinux); | ||
229 | return err; | 232 | return err; |
230 | } | 233 | } |
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 4aeb7d5df939..bbc782e364b0 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c | |||
@@ -2,7 +2,6 @@ | |||
2 | #include "../cache.h" | 2 | #include "../cache.h" |
3 | #include "../../perf.h" | 3 | #include "../../perf.h" |
4 | #include "libslang.h" | 4 | #include "libslang.h" |
5 | #include <newt.h> | ||
6 | #include "ui.h" | 5 | #include "ui.h" |
7 | #include "util.h" | 6 | #include "util.h" |
8 | #include <linux/compiler.h> | 7 | #include <linux/compiler.h> |
@@ -234,7 +233,7 @@ void ui_browser__reset_index(struct ui_browser *browser) | |||
234 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | 233 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
235 | { | 234 | { |
236 | SLsmg_gotorc(0, 0); | 235 | SLsmg_gotorc(0, 0); |
237 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | 236 | ui_browser__set_color(browser, HE_COLORSET_ROOT); |
238 | slsmg_write_nstring(title, browser->width + 1); | 237 | slsmg_write_nstring(title, browser->width + 1); |
239 | } | 238 | } |
240 | 239 | ||
@@ -273,6 +272,8 @@ void ui_browser__hide(struct ui_browser *browser __maybe_unused) | |||
273 | { | 272 | { |
274 | pthread_mutex_lock(&ui__lock); | 273 | pthread_mutex_lock(&ui__lock); |
275 | ui_helpline__pop(); | 274 | ui_helpline__pop(); |
275 | free(browser->helpline); | ||
276 | browser->helpline = NULL; | ||
276 | pthread_mutex_unlock(&ui__lock); | 277 | pthread_mutex_unlock(&ui__lock); |
277 | } | 278 | } |
278 | 279 | ||
@@ -471,7 +472,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) | |||
471 | return row; | 472 | return row; |
472 | } | 473 | } |
473 | 474 | ||
474 | static struct ui_browser__colorset { | 475 | static struct ui_browser_colorset { |
475 | const char *name, *fg, *bg; | 476 | const char *name, *fg, *bg; |
476 | int colorset; | 477 | int colorset; |
477 | } ui_browser__colorsets[] = { | 478 | } ui_browser__colorsets[] = { |
@@ -512,6 +513,12 @@ static struct ui_browser__colorset { | |||
512 | .bg = "default", | 513 | .bg = "default", |
513 | }, | 514 | }, |
514 | { | 515 | { |
516 | .colorset = HE_COLORSET_ROOT, | ||
517 | .name = "root", | ||
518 | .fg = "white", | ||
519 | .bg = "blue", | ||
520 | }, | ||
521 | { | ||
515 | .name = NULL, | 522 | .name = NULL, |
516 | } | 523 | } |
517 | }; | 524 | }; |
@@ -706,7 +713,7 @@ void ui_browser__init(void) | |||
706 | perf_config(ui_browser__color_config, NULL); | 713 | perf_config(ui_browser__color_config, NULL); |
707 | 714 | ||
708 | while (ui_browser__colorsets[i].name) { | 715 | while (ui_browser__colorsets[i].name) { |
709 | struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; | 716 | struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; |
710 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); | 717 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); |
711 | } | 718 | } |
712 | 719 | ||
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index af70314605e5..404ff66a3e36 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define HE_COLORSET_SELECTED 53 | 11 | #define HE_COLORSET_SELECTED 53 |
12 | #define HE_COLORSET_CODE 54 | 12 | #define HE_COLORSET_CODE 54 |
13 | #define HE_COLORSET_ADDR 55 | 13 | #define HE_COLORSET_ADDR 55 |
14 | #define HE_COLORSET_ROOT 56 | ||
14 | 15 | ||
15 | struct ui_browser { | 16 | struct ui_browser { |
16 | u64 index, top_idx; | 17 | u64 index, top_idx; |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 5dab3ca96980..cc64d3f7fc36 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -8,15 +8,19 @@ | |||
8 | #include "../../util/hist.h" | 8 | #include "../../util/hist.h" |
9 | #include "../../util/sort.h" | 9 | #include "../../util/sort.h" |
10 | #include "../../util/symbol.h" | 10 | #include "../../util/symbol.h" |
11 | #include "../../util/evsel.h" | ||
11 | #include <pthread.h> | 12 | #include <pthread.h> |
12 | #include <newt.h> | ||
13 | 13 | ||
14 | struct browser_disasm_line { | 14 | struct browser_disasm_line { |
15 | struct rb_node rb_node; | 15 | struct rb_node rb_node; |
16 | double percent; | ||
17 | u32 idx; | 16 | u32 idx; |
18 | int idx_asm; | 17 | int idx_asm; |
19 | int jump_sources; | 18 | int jump_sources; |
19 | /* | ||
20 | * actual length of this array is saved on the nr_events field | ||
21 | * of the struct annotate_browser | ||
22 | */ | ||
23 | double percent[1]; | ||
20 | }; | 24 | }; |
21 | 25 | ||
22 | static struct annotate_browser_opt { | 26 | static struct annotate_browser_opt { |
@@ -33,8 +37,9 @@ struct annotate_browser { | |||
33 | struct ui_browser b; | 37 | struct ui_browser b; |
34 | struct rb_root entries; | 38 | struct rb_root entries; |
35 | struct rb_node *curr_hot; | 39 | struct rb_node *curr_hot; |
36 | struct disasm_line *selection; | 40 | struct disasm_line *selection; |
37 | struct disasm_line **offsets; | 41 | struct disasm_line **offsets; |
42 | int nr_events; | ||
38 | u64 start; | 43 | u64 start; |
39 | int nr_asm_entries; | 44 | int nr_asm_entries; |
40 | int nr_entries; | 45 | int nr_entries; |
@@ -94,14 +99,24 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
94 | (!current_entry || (browser->use_navkeypressed && | 99 | (!current_entry || (browser->use_navkeypressed && |
95 | !browser->navkeypressed))); | 100 | !browser->navkeypressed))); |
96 | int width = browser->width, printed; | 101 | int width = browser->width, printed; |
102 | int i, pcnt_width = 7 * ab->nr_events; | ||
103 | double percent_max = 0.0; | ||
97 | char bf[256]; | 104 | char bf[256]; |
98 | 105 | ||
99 | if (dl->offset != -1 && bdl->percent != 0.0) { | 106 | for (i = 0; i < ab->nr_events; i++) { |
100 | ui_browser__set_percent_color(browser, bdl->percent, current_entry); | 107 | if (bdl->percent[i] > percent_max) |
101 | slsmg_printf("%6.2f ", bdl->percent); | 108 | percent_max = bdl->percent[i]; |
109 | } | ||
110 | |||
111 | if (dl->offset != -1 && percent_max != 0.0) { | ||
112 | for (i = 0; i < ab->nr_events; i++) { | ||
113 | ui_browser__set_percent_color(browser, bdl->percent[i], | ||
114 | current_entry); | ||
115 | slsmg_printf("%6.2f ", bdl->percent[i]); | ||
116 | } | ||
102 | } else { | 117 | } else { |
103 | ui_browser__set_percent_color(browser, 0, current_entry); | 118 | ui_browser__set_percent_color(browser, 0, current_entry); |
104 | slsmg_write_nstring(" ", 7); | 119 | slsmg_write_nstring(" ", pcnt_width); |
105 | } | 120 | } |
106 | 121 | ||
107 | SLsmg_write_char(' '); | 122 | SLsmg_write_char(' '); |
@@ -111,12 +126,12 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
111 | width += 1; | 126 | width += 1; |
112 | 127 | ||
113 | if (!*dl->line) | 128 | if (!*dl->line) |
114 | slsmg_write_nstring(" ", width - 7); | 129 | slsmg_write_nstring(" ", width - pcnt_width); |
115 | else if (dl->offset == -1) { | 130 | else if (dl->offset == -1) { |
116 | printed = scnprintf(bf, sizeof(bf), "%*s ", | 131 | printed = scnprintf(bf, sizeof(bf), "%*s ", |
117 | ab->addr_width, " "); | 132 | ab->addr_width, " "); |
118 | slsmg_write_nstring(bf, printed); | 133 | slsmg_write_nstring(bf, printed); |
119 | slsmg_write_nstring(dl->line, width - printed - 6); | 134 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); |
120 | } else { | 135 | } else { |
121 | u64 addr = dl->offset; | 136 | u64 addr = dl->offset; |
122 | int color = -1; | 137 | int color = -1; |
@@ -175,13 +190,23 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
175 | } | 190 | } |
176 | 191 | ||
177 | disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); | 192 | disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); |
178 | slsmg_write_nstring(bf, width - 10 - printed); | 193 | slsmg_write_nstring(bf, width - pcnt_width - 3 - printed); |
179 | } | 194 | } |
180 | 195 | ||
181 | if (current_entry) | 196 | if (current_entry) |
182 | ab->selection = dl; | 197 | ab->selection = dl; |
183 | } | 198 | } |
184 | 199 | ||
200 | static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) | ||
201 | { | ||
202 | if (!dl || !dl->ins || !ins__is_jump(dl->ins) | ||
203 | || !disasm_line__has_offset(dl) | ||
204 | || dl->ops.target.offset >= symbol__size(sym)) | ||
205 | return false; | ||
206 | |||
207 | return true; | ||
208 | } | ||
209 | |||
185 | static void annotate_browser__draw_current_jump(struct ui_browser *browser) | 210 | static void annotate_browser__draw_current_jump(struct ui_browser *browser) |
186 | { | 211 | { |
187 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | 212 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); |
@@ -190,13 +215,13 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
190 | unsigned int from, to; | 215 | unsigned int from, to; |
191 | struct map_symbol *ms = ab->b.priv; | 216 | struct map_symbol *ms = ab->b.priv; |
192 | struct symbol *sym = ms->sym; | 217 | struct symbol *sym = ms->sym; |
218 | u8 pcnt_width = 7; | ||
193 | 219 | ||
194 | /* PLT symbols contain external offsets */ | 220 | /* PLT symbols contain external offsets */ |
195 | if (strstr(sym->name, "@plt")) | 221 | if (strstr(sym->name, "@plt")) |
196 | return; | 222 | return; |
197 | 223 | ||
198 | if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || | 224 | if (!disasm_line__is_valid_jump(cursor, sym)) |
199 | !disasm_line__has_offset(cursor)) | ||
200 | return; | 225 | return; |
201 | 226 | ||
202 | target = ab->offsets[cursor->ops.target.offset]; | 227 | target = ab->offsets[cursor->ops.target.offset]; |
@@ -214,57 +239,44 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
214 | to = (u64)btarget->idx; | 239 | to = (u64)btarget->idx; |
215 | } | 240 | } |
216 | 241 | ||
242 | pcnt_width *= ab->nr_events; | ||
243 | |||
217 | ui_browser__set_color(browser, HE_COLORSET_CODE); | 244 | ui_browser__set_color(browser, HE_COLORSET_CODE); |
218 | __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); | 245 | __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, |
246 | from, to); | ||
219 | } | 247 | } |
220 | 248 | ||
221 | static unsigned int annotate_browser__refresh(struct ui_browser *browser) | 249 | static unsigned int annotate_browser__refresh(struct ui_browser *browser) |
222 | { | 250 | { |
251 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
223 | int ret = ui_browser__list_head_refresh(browser); | 252 | int ret = ui_browser__list_head_refresh(browser); |
253 | int pcnt_width; | ||
254 | |||
255 | pcnt_width = 7 * ab->nr_events; | ||
224 | 256 | ||
225 | if (annotate_browser__opts.jump_arrows) | 257 | if (annotate_browser__opts.jump_arrows) |
226 | annotate_browser__draw_current_jump(browser); | 258 | annotate_browser__draw_current_jump(browser); |
227 | 259 | ||
228 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); | 260 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); |
229 | __ui_browser__vline(browser, 7, 0, browser->height - 1); | 261 | __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1); |
230 | return ret; | 262 | return ret; |
231 | } | 263 | } |
232 | 264 | ||
233 | static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) | 265 | static int disasm__cmp(struct browser_disasm_line *a, |
266 | struct browser_disasm_line *b, int nr_pcnt) | ||
234 | { | 267 | { |
235 | double percent = 0.0; | 268 | int i; |
236 | |||
237 | if (dl->offset != -1) { | ||
238 | int len = sym->end - sym->start; | ||
239 | unsigned int hits = 0; | ||
240 | struct annotation *notes = symbol__annotation(sym); | ||
241 | struct source_line *src_line = notes->src->lines; | ||
242 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
243 | s64 offset = dl->offset; | ||
244 | struct disasm_line *next; | ||
245 | |||
246 | next = disasm__get_next_ip_line(¬es->src->source, dl); | ||
247 | while (offset < (s64)len && | ||
248 | (next == NULL || offset < next->offset)) { | ||
249 | if (src_line) { | ||
250 | percent += src_line[offset].percent; | ||
251 | } else | ||
252 | hits += h->addr[offset]; | ||
253 | 269 | ||
254 | ++offset; | 270 | for (i = 0; i < nr_pcnt; i++) { |
255 | } | 271 | if (a->percent[i] == b->percent[i]) |
256 | /* | 272 | continue; |
257 | * If the percentage wasn't already calculated in | 273 | return a->percent[i] < b->percent[i]; |
258 | * symbol__get_source_line, do it now: | ||
259 | */ | ||
260 | if (src_line == NULL && h->sum) | ||
261 | percent = 100.0 * hits / h->sum; | ||
262 | } | 274 | } |
263 | 275 | return 0; | |
264 | return percent; | ||
265 | } | 276 | } |
266 | 277 | ||
267 | static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) | 278 | static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl, |
279 | int nr_events) | ||
268 | { | 280 | { |
269 | struct rb_node **p = &root->rb_node; | 281 | struct rb_node **p = &root->rb_node; |
270 | struct rb_node *parent = NULL; | 282 | struct rb_node *parent = NULL; |
@@ -273,7 +285,8 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l | |||
273 | while (*p != NULL) { | 285 | while (*p != NULL) { |
274 | parent = *p; | 286 | parent = *p; |
275 | l = rb_entry(parent, struct browser_disasm_line, rb_node); | 287 | l = rb_entry(parent, struct browser_disasm_line, rb_node); |
276 | if (bdl->percent < l->percent) | 288 | |
289 | if (disasm__cmp(bdl, l, nr_events)) | ||
277 | p = &(*p)->rb_left; | 290 | p = &(*p)->rb_left; |
278 | else | 291 | else |
279 | p = &(*p)->rb_right; | 292 | p = &(*p)->rb_right; |
@@ -322,12 +335,13 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser, | |||
322 | } | 335 | } |
323 | 336 | ||
324 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 337 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
325 | int evidx) | 338 | struct perf_evsel *evsel) |
326 | { | 339 | { |
327 | struct map_symbol *ms = browser->b.priv; | 340 | struct map_symbol *ms = browser->b.priv; |
328 | struct symbol *sym = ms->sym; | 341 | struct symbol *sym = ms->sym; |
329 | struct annotation *notes = symbol__annotation(sym); | 342 | struct annotation *notes = symbol__annotation(sym); |
330 | struct disasm_line *pos; | 343 | struct disasm_line *pos, *next; |
344 | s64 len = symbol__size(sym); | ||
331 | 345 | ||
332 | browser->entries = RB_ROOT; | 346 | browser->entries = RB_ROOT; |
333 | 347 | ||
@@ -335,12 +349,34 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
335 | 349 | ||
336 | list_for_each_entry(pos, ¬es->src->source, node) { | 350 | list_for_each_entry(pos, ¬es->src->source, node) { |
337 | struct browser_disasm_line *bpos = disasm_line__browser(pos); | 351 | struct browser_disasm_line *bpos = disasm_line__browser(pos); |
338 | bpos->percent = disasm_line__calc_percent(pos, sym, evidx); | 352 | const char *path = NULL; |
339 | if (bpos->percent < 0.01) { | 353 | double max_percent = 0.0; |
354 | int i; | ||
355 | |||
356 | if (pos->offset == -1) { | ||
340 | RB_CLEAR_NODE(&bpos->rb_node); | 357 | RB_CLEAR_NODE(&bpos->rb_node); |
341 | continue; | 358 | continue; |
342 | } | 359 | } |
343 | disasm_rb_tree__insert(&browser->entries, bpos); | 360 | |
361 | next = disasm__get_next_ip_line(¬es->src->source, pos); | ||
362 | |||
363 | for (i = 0; i < browser->nr_events; i++) { | ||
364 | bpos->percent[i] = disasm__calc_percent(notes, | ||
365 | evsel->idx + i, | ||
366 | pos->offset, | ||
367 | next ? next->offset : len, | ||
368 | &path); | ||
369 | |||
370 | if (max_percent < bpos->percent[i]) | ||
371 | max_percent = bpos->percent[i]; | ||
372 | } | ||
373 | |||
374 | if (max_percent < 0.01) { | ||
375 | RB_CLEAR_NODE(&bpos->rb_node); | ||
376 | continue; | ||
377 | } | ||
378 | disasm_rb_tree__insert(&browser->entries, bpos, | ||
379 | browser->nr_events); | ||
344 | } | 380 | } |
345 | pthread_mutex_unlock(¬es->lock); | 381 | pthread_mutex_unlock(¬es->lock); |
346 | 382 | ||
@@ -392,7 +428,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) | |||
392 | browser->b.nr_entries = browser->nr_asm_entries; | 428 | browser->b.nr_entries = browser->nr_asm_entries; |
393 | } | 429 | } |
394 | 430 | ||
395 | static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, | 431 | static bool annotate_browser__callq(struct annotate_browser *browser, |
432 | struct perf_evsel *evsel, | ||
396 | struct hist_browser_timer *hbt) | 433 | struct hist_browser_timer *hbt) |
397 | { | 434 | { |
398 | struct map_symbol *ms = browser->b.priv; | 435 | struct map_symbol *ms = browser->b.priv; |
@@ -423,7 +460,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, | |||
423 | } | 460 | } |
424 | 461 | ||
425 | pthread_mutex_unlock(¬es->lock); | 462 | pthread_mutex_unlock(¬es->lock); |
426 | symbol__tui_annotate(target, ms->map, evidx, hbt); | 463 | symbol__tui_annotate(target, ms->map, evsel, hbt); |
427 | ui_browser__show_title(&browser->b, sym->name); | 464 | ui_browser__show_title(&browser->b, sym->name); |
428 | return true; | 465 | return true; |
429 | } | 466 | } |
@@ -606,7 +643,8 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser | |||
606 | browser->addr_width += browser->jumps_width + 1; | 643 | browser->addr_width += browser->jumps_width + 1; |
607 | } | 644 | } |
608 | 645 | ||
609 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, | 646 | static int annotate_browser__run(struct annotate_browser *browser, |
647 | struct perf_evsel *evsel, | ||
610 | struct hist_browser_timer *hbt) | 648 | struct hist_browser_timer *hbt) |
611 | { | 649 | { |
612 | struct rb_node *nd = NULL; | 650 | struct rb_node *nd = NULL; |
@@ -619,7 +657,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
619 | if (ui_browser__show(&browser->b, sym->name, help) < 0) | 657 | if (ui_browser__show(&browser->b, sym->name, help) < 0) |
620 | return -1; | 658 | return -1; |
621 | 659 | ||
622 | annotate_browser__calc_percent(browser, evidx); | 660 | annotate_browser__calc_percent(browser, evsel); |
623 | 661 | ||
624 | if (browser->curr_hot) { | 662 | if (browser->curr_hot) { |
625 | annotate_browser__set_rb_top(browser, browser->curr_hot); | 663 | annotate_browser__set_rb_top(browser, browser->curr_hot); |
@@ -632,7 +670,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
632 | key = ui_browser__run(&browser->b, delay_secs); | 670 | key = ui_browser__run(&browser->b, delay_secs); |
633 | 671 | ||
634 | if (delay_secs != 0) { | 672 | if (delay_secs != 0) { |
635 | annotate_browser__calc_percent(browser, evidx); | 673 | annotate_browser__calc_percent(browser, evsel); |
636 | /* | 674 | /* |
637 | * Current line focus got out of the list of most active | 675 | * Current line focus got out of the list of most active |
638 | * lines, NULL it so that if TAB|UNTAB is pressed, we | 676 | * lines, NULL it so that if TAB|UNTAB is pressed, we |
@@ -648,7 +686,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
648 | hbt->timer(hbt->arg); | 686 | hbt->timer(hbt->arg); |
649 | 687 | ||
650 | if (delay_secs != 0) | 688 | if (delay_secs != 0) |
651 | symbol__annotate_decay_histogram(sym, evidx); | 689 | symbol__annotate_decay_histogram(sym, evsel->idx); |
652 | continue; | 690 | continue; |
653 | case K_TAB: | 691 | case K_TAB: |
654 | if (nd != NULL) { | 692 | if (nd != NULL) { |
@@ -745,7 +783,7 @@ show_help: | |||
745 | goto show_sup_ins; | 783 | goto show_sup_ins; |
746 | goto out; | 784 | goto out; |
747 | } else if (!(annotate_browser__jump(browser) || | 785 | } else if (!(annotate_browser__jump(browser) || |
748 | annotate_browser__callq(browser, evidx, hbt))) { | 786 | annotate_browser__callq(browser, evsel, hbt))) { |
749 | show_sup_ins: | 787 | show_sup_ins: |
750 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); | 788 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); |
751 | } | 789 | } |
@@ -767,10 +805,10 @@ out: | |||
767 | return key; | 805 | return key; |
768 | } | 806 | } |
769 | 807 | ||
770 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 808 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, |
771 | struct hist_browser_timer *hbt) | 809 | struct hist_browser_timer *hbt) |
772 | { | 810 | { |
773 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); | 811 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt); |
774 | } | 812 | } |
775 | 813 | ||
776 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, | 814 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, |
@@ -788,17 +826,9 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser | |||
788 | struct disasm_line *dl = browser->offsets[offset], *dlt; | 826 | struct disasm_line *dl = browser->offsets[offset], *dlt; |
789 | struct browser_disasm_line *bdlt; | 827 | struct browser_disasm_line *bdlt; |
790 | 828 | ||
791 | if (!dl || !dl->ins || !ins__is_jump(dl->ins) || | 829 | if (!disasm_line__is_valid_jump(dl, sym)) |
792 | !disasm_line__has_offset(dl)) | ||
793 | continue; | 830 | continue; |
794 | 831 | ||
795 | if (dl->ops.target.offset >= size) { | ||
796 | ui__error("jump to after symbol!\n" | ||
797 | "size: %zx, jump target: %" PRIx64, | ||
798 | size, dl->ops.target.offset); | ||
799 | continue; | ||
800 | } | ||
801 | |||
802 | dlt = browser->offsets[dl->ops.target.offset]; | 832 | dlt = browser->offsets[dl->ops.target.offset]; |
803 | /* | 833 | /* |
804 | * FIXME: Oops, no jump target? Buggy disassembler? Or do we | 834 | * FIXME: Oops, no jump target? Buggy disassembler? Or do we |
@@ -825,7 +855,8 @@ static inline int width_jumps(int n) | |||
825 | return 1; | 855 | return 1; |
826 | } | 856 | } |
827 | 857 | ||
828 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 858 | int symbol__tui_annotate(struct symbol *sym, struct map *map, |
859 | struct perf_evsel *evsel, | ||
829 | struct hist_browser_timer *hbt) | 860 | struct hist_browser_timer *hbt) |
830 | { | 861 | { |
831 | struct disasm_line *pos, *n; | 862 | struct disasm_line *pos, *n; |
@@ -846,6 +877,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
846 | }, | 877 | }, |
847 | }; | 878 | }; |
848 | int ret = -1; | 879 | int ret = -1; |
880 | int nr_pcnt = 1; | ||
881 | size_t sizeof_bdl = sizeof(struct browser_disasm_line); | ||
849 | 882 | ||
850 | if (sym == NULL) | 883 | if (sym == NULL) |
851 | return -1; | 884 | return -1; |
@@ -861,7 +894,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
861 | return -1; | 894 | return -1; |
862 | } | 895 | } |
863 | 896 | ||
864 | if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { | 897 | if (perf_evsel__is_group_event(evsel)) { |
898 | nr_pcnt = evsel->nr_members; | ||
899 | sizeof_bdl += sizeof(double) * (nr_pcnt - 1); | ||
900 | } | ||
901 | |||
902 | if (symbol__annotate(sym, map, sizeof_bdl) < 0) { | ||
865 | ui__error("%s", ui_helpline__last_msg); | 903 | ui__error("%s", ui_helpline__last_msg); |
866 | goto out_free_offsets; | 904 | goto out_free_offsets; |
867 | } | 905 | } |
@@ -899,6 +937,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
899 | browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); | 937 | browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); |
900 | browser.max_addr_width = hex_width(sym->end); | 938 | browser.max_addr_width = hex_width(sym->end); |
901 | browser.jumps_width = width_jumps(browser.max_jump_sources); | 939 | browser.jumps_width = width_jumps(browser.max_jump_sources); |
940 | browser.nr_events = nr_pcnt; | ||
902 | browser.b.nr_entries = browser.nr_entries; | 941 | browser.b.nr_entries = browser.nr_entries; |
903 | browser.b.entries = ¬es->src->source, | 942 | browser.b.entries = ¬es->src->source, |
904 | browser.b.width += 18; /* Percentage */ | 943 | browser.b.width += 18; /* Percentage */ |
@@ -908,7 +947,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
908 | 947 | ||
909 | annotate_browser__update_addr_width(&browser); | 948 | annotate_browser__update_addr_width(&browser); |
910 | 949 | ||
911 | ret = annotate_browser__run(&browser, evidx, hbt); | 950 | ret = annotate_browser__run(&browser, evsel, hbt); |
912 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 951 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
913 | list_del(&pos->node); | 952 | list_del(&pos->node); |
914 | disasm_line__free(pos); | 953 | disasm_line__free(pos); |
@@ -921,11 +960,11 @@ out_free_offsets: | |||
921 | 960 | ||
922 | #define ANNOTATE_CFG(n) \ | 961 | #define ANNOTATE_CFG(n) \ |
923 | { .name = #n, .value = &annotate_browser__opts.n, } | 962 | { .name = #n, .value = &annotate_browser__opts.n, } |
924 | 963 | ||
925 | /* | 964 | /* |
926 | * Keep the entries sorted, they are bsearch'ed | 965 | * Keep the entries sorted, they are bsearch'ed |
927 | */ | 966 | */ |
928 | static struct annotate__config { | 967 | static struct annotate_config { |
929 | const char *name; | 968 | const char *name; |
930 | bool *value; | 969 | bool *value; |
931 | } annotate__configs[] = { | 970 | } annotate__configs[] = { |
@@ -939,7 +978,7 @@ static struct annotate__config { | |||
939 | 978 | ||
940 | static int annotate_config__cmp(const void *name, const void *cfgp) | 979 | static int annotate_config__cmp(const void *name, const void *cfgp) |
941 | { | 980 | { |
942 | const struct annotate__config *cfg = cfgp; | 981 | const struct annotate_config *cfg = cfgp; |
943 | 982 | ||
944 | return strcmp(name, cfg->name); | 983 | return strcmp(name, cfg->name); |
945 | } | 984 | } |
@@ -947,7 +986,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp) | |||
947 | static int annotate__config(const char *var, const char *value, | 986 | static int annotate__config(const char *var, const char *value, |
948 | void *data __maybe_unused) | 987 | void *data __maybe_unused) |
949 | { | 988 | { |
950 | struct annotate__config *cfg; | 989 | struct annotate_config *cfg; |
951 | const char *name; | 990 | const char *name; |
952 | 991 | ||
953 | if (prefixcmp(var, "annotate.") != 0) | 992 | if (prefixcmp(var, "annotate.") != 0) |
@@ -955,7 +994,7 @@ static int annotate__config(const char *var, const char *value, | |||
955 | 994 | ||
956 | name = var + 9; | 995 | name = var + 9; |
957 | cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), | 996 | cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), |
958 | sizeof(struct annotate__config), annotate_config__cmp); | 997 | sizeof(struct annotate_config), annotate_config__cmp); |
959 | 998 | ||
960 | if (cfg == NULL) | 999 | if (cfg == NULL) |
961 | return -1; | 1000 | return -1; |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ccc4bd161420..fc0bd3843d34 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -2,7 +2,6 @@ | |||
2 | #include "../libslang.h" | 2 | #include "../libslang.h" |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <newt.h> | ||
6 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
7 | 6 | ||
8 | #include "../../util/evsel.h" | 7 | #include "../../util/evsel.h" |
@@ -26,7 +25,8 @@ struct hist_browser { | |||
26 | struct map_symbol *selection; | 25 | struct map_symbol *selection; |
27 | int print_seq; | 26 | int print_seq; |
28 | bool show_dso; | 27 | bool show_dso; |
29 | bool has_symbols; | 28 | float min_pcnt; |
29 | u64 nr_pcnt_entries; | ||
30 | }; | 30 | }; |
31 | 31 | ||
32 | extern void hist_browser__init_hpp(void); | 32 | extern void hist_browser__init_hpp(void); |
@@ -310,6 +310,8 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) | |||
310 | "Or reduce the sampling frequency."); | 310 | "Or reduce the sampling frequency."); |
311 | } | 311 | } |
312 | 312 | ||
313 | static void hist_browser__update_pcnt_entries(struct hist_browser *hb); | ||
314 | |||
313 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | 315 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, |
314 | struct hist_browser_timer *hbt) | 316 | struct hist_browser_timer *hbt) |
315 | { | 317 | { |
@@ -319,6 +321,8 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
319 | 321 | ||
320 | browser->b.entries = &browser->hists->entries; | 322 | browser->b.entries = &browser->hists->entries; |
321 | browser->b.nr_entries = browser->hists->nr_entries; | 323 | browser->b.nr_entries = browser->hists->nr_entries; |
324 | if (browser->min_pcnt) | ||
325 | browser->b.nr_entries = browser->nr_pcnt_entries; | ||
322 | 326 | ||
323 | hist_browser__refresh_dimensions(browser); | 327 | hist_browser__refresh_dimensions(browser); |
324 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); | 328 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); |
@@ -331,9 +335,18 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
331 | key = ui_browser__run(&browser->b, delay_secs); | 335 | key = ui_browser__run(&browser->b, delay_secs); |
332 | 336 | ||
333 | switch (key) { | 337 | switch (key) { |
334 | case K_TIMER: | 338 | case K_TIMER: { |
339 | u64 nr_entries; | ||
335 | hbt->timer(hbt->arg); | 340 | hbt->timer(hbt->arg); |
336 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | 341 | |
342 | if (browser->min_pcnt) { | ||
343 | hist_browser__update_pcnt_entries(browser); | ||
344 | nr_entries = browser->nr_pcnt_entries; | ||
345 | } else { | ||
346 | nr_entries = browser->hists->nr_entries; | ||
347 | } | ||
348 | |||
349 | ui_browser__update_nr_entries(&browser->b, nr_entries); | ||
337 | 350 | ||
338 | if (browser->hists->stats.nr_lost_warned != | 351 | if (browser->hists->stats.nr_lost_warned != |
339 | browser->hists->stats.nr_events[PERF_RECORD_LOST]) { | 352 | browser->hists->stats.nr_events[PERF_RECORD_LOST]) { |
@@ -345,6 +358,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
345 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); | 358 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); |
346 | ui_browser__show_title(&browser->b, title); | 359 | ui_browser__show_title(&browser->b, title); |
347 | continue; | 360 | continue; |
361 | } | ||
348 | case 'D': { /* Debug */ | 362 | case 'D': { /* Debug */ |
349 | static int seq; | 363 | static int seq; |
350 | struct hist_entry *h = rb_entry(browser->b.top, | 364 | struct hist_entry *h = rb_entry(browser->b.top, |
@@ -567,26 +581,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
567 | return row - first_row; | 581 | return row - first_row; |
568 | } | 582 | } |
569 | 583 | ||
570 | #define HPP__COLOR_FN(_name, _field) \ | 584 | struct hpp_arg { |
571 | static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ | 585 | struct ui_browser *b; |
572 | struct hist_entry *he) \ | 586 | char folded_sign; |
587 | bool current_entry; | ||
588 | }; | ||
589 | |||
590 | static int __hpp__color_callchain(struct hpp_arg *arg) | ||
591 | { | ||
592 | if (!symbol_conf.use_callchain) | ||
593 | return 0; | ||
594 | |||
595 | slsmg_printf("%c ", arg->folded_sign); | ||
596 | return 2; | ||
597 | } | ||
598 | |||
599 | static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
600 | u64 (*get_field)(struct hist_entry *), | ||
601 | int (*callchain_cb)(struct hpp_arg *)) | ||
602 | { | ||
603 | int ret = 0; | ||
604 | double percent = 0.0; | ||
605 | struct hists *hists = he->hists; | ||
606 | struct hpp_arg *arg = hpp->ptr; | ||
607 | |||
608 | if (hists->stats.total_period) | ||
609 | percent = 100.0 * get_field(he) / hists->stats.total_period; | ||
610 | |||
611 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | ||
612 | |||
613 | if (callchain_cb) | ||
614 | ret += callchain_cb(arg); | ||
615 | |||
616 | ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | ||
617 | slsmg_printf("%s", hpp->buf); | ||
618 | |||
619 | if (symbol_conf.event_group) { | ||
620 | int prev_idx, idx_delta; | ||
621 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
622 | struct hist_entry *pair; | ||
623 | int nr_members = evsel->nr_members; | ||
624 | |||
625 | if (nr_members <= 1) | ||
626 | goto out; | ||
627 | |||
628 | prev_idx = perf_evsel__group_idx(evsel); | ||
629 | |||
630 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | ||
631 | u64 period = get_field(pair); | ||
632 | u64 total = pair->hists->stats.total_period; | ||
633 | |||
634 | if (!total) | ||
635 | continue; | ||
636 | |||
637 | evsel = hists_to_evsel(pair->hists); | ||
638 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; | ||
639 | |||
640 | while (idx_delta--) { | ||
641 | /* | ||
642 | * zero-fill group members in the middle which | ||
643 | * have no sample | ||
644 | */ | ||
645 | ui_browser__set_percent_color(arg->b, 0.0, | ||
646 | arg->current_entry); | ||
647 | ret += scnprintf(hpp->buf, hpp->size, | ||
648 | " %6.2f%%", 0.0); | ||
649 | slsmg_printf("%s", hpp->buf); | ||
650 | } | ||
651 | |||
652 | percent = 100.0 * period / total; | ||
653 | ui_browser__set_percent_color(arg->b, percent, | ||
654 | arg->current_entry); | ||
655 | ret += scnprintf(hpp->buf, hpp->size, | ||
656 | " %6.2f%%", percent); | ||
657 | slsmg_printf("%s", hpp->buf); | ||
658 | |||
659 | prev_idx = perf_evsel__group_idx(evsel); | ||
660 | } | ||
661 | |||
662 | idx_delta = nr_members - prev_idx - 1; | ||
663 | |||
664 | while (idx_delta--) { | ||
665 | /* | ||
666 | * zero-fill group members at last which have no sample | ||
667 | */ | ||
668 | ui_browser__set_percent_color(arg->b, 0.0, | ||
669 | arg->current_entry); | ||
670 | ret += scnprintf(hpp->buf, hpp->size, | ||
671 | " %6.2f%%", 0.0); | ||
672 | slsmg_printf("%s", hpp->buf); | ||
673 | } | ||
674 | } | ||
675 | out: | ||
676 | if (!arg->current_entry || !arg->b->navkeypressed) | ||
677 | ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); | ||
678 | |||
679 | return ret; | ||
680 | } | ||
681 | |||
682 | #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ | ||
683 | static u64 __hpp_get_##_field(struct hist_entry *he) \ | ||
684 | { \ | ||
685 | return he->stat._field; \ | ||
686 | } \ | ||
687 | \ | ||
688 | static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \ | ||
689 | struct hist_entry *he) \ | ||
573 | { \ | 690 | { \ |
574 | struct hists *hists = he->hists; \ | 691 | return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \ |
575 | double percent = 100.0 * he->stat._field / hists->stats.total_period; \ | ||
576 | *(double *)hpp->ptr = percent; \ | ||
577 | return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ | ||
578 | } | 692 | } |
579 | 693 | ||
580 | HPP__COLOR_FN(overhead, period) | 694 | __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) |
581 | HPP__COLOR_FN(overhead_sys, period_sys) | 695 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) |
582 | HPP__COLOR_FN(overhead_us, period_us) | 696 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) |
583 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | 697 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) |
584 | HPP__COLOR_FN(overhead_guest_us, period_guest_us) | 698 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) |
585 | 699 | ||
586 | #undef HPP__COLOR_FN | 700 | #undef __HPP_COLOR_PERCENT_FN |
587 | 701 | ||
588 | void hist_browser__init_hpp(void) | 702 | void hist_browser__init_hpp(void) |
589 | { | 703 | { |
704 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
705 | |||
590 | perf_hpp__init(); | 706 | perf_hpp__init(); |
591 | 707 | ||
592 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 708 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
@@ -606,13 +722,13 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
606 | unsigned short row) | 722 | unsigned short row) |
607 | { | 723 | { |
608 | char s[256]; | 724 | char s[256]; |
609 | double percent; | 725 | int printed = 0; |
610 | int i, printed = 0; | ||
611 | int width = browser->b.width; | 726 | int width = browser->b.width; |
612 | char folded_sign = ' '; | 727 | char folded_sign = ' '; |
613 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); | 728 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); |
614 | off_t row_offset = entry->row_offset; | 729 | off_t row_offset = entry->row_offset; |
615 | bool first = true; | 730 | bool first = true; |
731 | struct perf_hpp_fmt *fmt; | ||
616 | 732 | ||
617 | if (current_entry) { | 733 | if (current_entry) { |
618 | browser->he_selection = entry; | 734 | browser->he_selection = entry; |
@@ -625,41 +741,30 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
625 | } | 741 | } |
626 | 742 | ||
627 | if (row_offset == 0) { | 743 | if (row_offset == 0) { |
744 | struct hpp_arg arg = { | ||
745 | .b = &browser->b, | ||
746 | .folded_sign = folded_sign, | ||
747 | .current_entry = current_entry, | ||
748 | }; | ||
628 | struct perf_hpp hpp = { | 749 | struct perf_hpp hpp = { |
629 | .buf = s, | 750 | .buf = s, |
630 | .size = sizeof(s), | 751 | .size = sizeof(s), |
752 | .ptr = &arg, | ||
631 | }; | 753 | }; |
632 | 754 | ||
633 | ui_browser__gotorc(&browser->b, row, 0); | 755 | ui_browser__gotorc(&browser->b, row, 0); |
634 | 756 | ||
635 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 757 | perf_hpp__for_each_format(fmt) { |
636 | if (!perf_hpp__format[i].cond) | ||
637 | continue; | ||
638 | |||
639 | if (!first) { | 758 | if (!first) { |
640 | slsmg_printf(" "); | 759 | slsmg_printf(" "); |
641 | width -= 2; | 760 | width -= 2; |
642 | } | 761 | } |
643 | first = false; | 762 | first = false; |
644 | 763 | ||
645 | if (perf_hpp__format[i].color) { | 764 | if (fmt->color) { |
646 | hpp.ptr = &percent; | 765 | width -= fmt->color(&hpp, entry); |
647 | /* It will set percent for us. See HPP__COLOR_FN above. */ | ||
648 | width -= perf_hpp__format[i].color(&hpp, entry); | ||
649 | |||
650 | ui_browser__set_percent_color(&browser->b, percent, current_entry); | ||
651 | |||
652 | if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) { | ||
653 | slsmg_printf("%c ", folded_sign); | ||
654 | width -= 2; | ||
655 | } | ||
656 | |||
657 | slsmg_printf("%s", s); | ||
658 | |||
659 | if (!current_entry || !browser->b.navkeypressed) | ||
660 | ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); | ||
661 | } else { | 766 | } else { |
662 | width -= perf_hpp__format[i].entry(&hpp, entry); | 767 | width -= fmt->entry(&hpp, entry); |
663 | slsmg_printf("%s", s); | 768 | slsmg_printf("%s", s); |
664 | } | 769 | } |
665 | } | 770 | } |
@@ -706,10 +811,15 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) | |||
706 | 811 | ||
707 | for (nd = browser->top; nd; nd = rb_next(nd)) { | 812 | for (nd = browser->top; nd; nd = rb_next(nd)) { |
708 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 813 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
814 | float percent = h->stat.period * 100.0 / | ||
815 | hb->hists->stats.total_period; | ||
709 | 816 | ||
710 | if (h->filtered) | 817 | if (h->filtered) |
711 | continue; | 818 | continue; |
712 | 819 | ||
820 | if (percent < hb->min_pcnt) | ||
821 | continue; | ||
822 | |||
713 | row += hist_browser__show_entry(hb, h, row); | 823 | row += hist_browser__show_entry(hb, h, row); |
714 | if (row == browser->height) | 824 | if (row == browser->height) |
715 | break; | 825 | break; |
@@ -718,10 +828,18 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) | |||
718 | return row; | 828 | return row; |
719 | } | 829 | } |
720 | 830 | ||
721 | static struct rb_node *hists__filter_entries(struct rb_node *nd) | 831 | static struct rb_node *hists__filter_entries(struct rb_node *nd, |
832 | struct hists *hists, | ||
833 | float min_pcnt) | ||
722 | { | 834 | { |
723 | while (nd != NULL) { | 835 | while (nd != NULL) { |
724 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 836 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
837 | float percent = h->stat.period * 100.0 / | ||
838 | hists->stats.total_period; | ||
839 | |||
840 | if (percent < min_pcnt) | ||
841 | return NULL; | ||
842 | |||
725 | if (!h->filtered) | 843 | if (!h->filtered) |
726 | return nd; | 844 | return nd; |
727 | 845 | ||
@@ -731,11 +849,16 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd) | |||
731 | return NULL; | 849 | return NULL; |
732 | } | 850 | } |
733 | 851 | ||
734 | static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) | 852 | static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, |
853 | struct hists *hists, | ||
854 | float min_pcnt) | ||
735 | { | 855 | { |
736 | while (nd != NULL) { | 856 | while (nd != NULL) { |
737 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 857 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
738 | if (!h->filtered) | 858 | float percent = h->stat.period * 100.0 / |
859 | hists->stats.total_period; | ||
860 | |||
861 | if (!h->filtered && percent >= min_pcnt) | ||
739 | return nd; | 862 | return nd; |
740 | 863 | ||
741 | nd = rb_prev(nd); | 864 | nd = rb_prev(nd); |
@@ -750,6 +873,9 @@ static void ui_browser__hists_seek(struct ui_browser *browser, | |||
750 | struct hist_entry *h; | 873 | struct hist_entry *h; |
751 | struct rb_node *nd; | 874 | struct rb_node *nd; |
752 | bool first = true; | 875 | bool first = true; |
876 | struct hist_browser *hb; | ||
877 | |||
878 | hb = container_of(browser, struct hist_browser, b); | ||
753 | 879 | ||
754 | if (browser->nr_entries == 0) | 880 | if (browser->nr_entries == 0) |
755 | return; | 881 | return; |
@@ -758,13 +884,15 @@ static void ui_browser__hists_seek(struct ui_browser *browser, | |||
758 | 884 | ||
759 | switch (whence) { | 885 | switch (whence) { |
760 | case SEEK_SET: | 886 | case SEEK_SET: |
761 | nd = hists__filter_entries(rb_first(browser->entries)); | 887 | nd = hists__filter_entries(rb_first(browser->entries), |
888 | hb->hists, hb->min_pcnt); | ||
762 | break; | 889 | break; |
763 | case SEEK_CUR: | 890 | case SEEK_CUR: |
764 | nd = browser->top; | 891 | nd = browser->top; |
765 | goto do_offset; | 892 | goto do_offset; |
766 | case SEEK_END: | 893 | case SEEK_END: |
767 | nd = hists__filter_prev_entries(rb_last(browser->entries)); | 894 | nd = hists__filter_prev_entries(rb_last(browser->entries), |
895 | hb->hists, hb->min_pcnt); | ||
768 | first = false; | 896 | first = false; |
769 | break; | 897 | break; |
770 | default: | 898 | default: |
@@ -807,7 +935,8 @@ do_offset: | |||
807 | break; | 935 | break; |
808 | } | 936 | } |
809 | } | 937 | } |
810 | nd = hists__filter_entries(rb_next(nd)); | 938 | nd = hists__filter_entries(rb_next(nd), hb->hists, |
939 | hb->min_pcnt); | ||
811 | if (nd == NULL) | 940 | if (nd == NULL) |
812 | break; | 941 | break; |
813 | --offset; | 942 | --offset; |
@@ -840,7 +969,8 @@ do_offset: | |||
840 | } | 969 | } |
841 | } | 970 | } |
842 | 971 | ||
843 | nd = hists__filter_prev_entries(rb_prev(nd)); | 972 | nd = hists__filter_prev_entries(rb_prev(nd), hb->hists, |
973 | hb->min_pcnt); | ||
844 | if (nd == NULL) | 974 | if (nd == NULL) |
845 | break; | 975 | break; |
846 | ++offset; | 976 | ++offset; |
@@ -1009,14 +1139,17 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
1009 | 1139 | ||
1010 | static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) | 1140 | static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) |
1011 | { | 1141 | { |
1012 | struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries)); | 1142 | struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), |
1143 | browser->hists, | ||
1144 | browser->min_pcnt); | ||
1013 | int printed = 0; | 1145 | int printed = 0; |
1014 | 1146 | ||
1015 | while (nd) { | 1147 | while (nd) { |
1016 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1148 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1017 | 1149 | ||
1018 | printed += hist_browser__fprintf_entry(browser, h, fp); | 1150 | printed += hist_browser__fprintf_entry(browser, h, fp); |
1019 | nd = hists__filter_entries(rb_next(nd)); | 1151 | nd = hists__filter_entries(rb_next(nd), browser->hists, |
1152 | browser->min_pcnt); | ||
1020 | } | 1153 | } |
1021 | 1154 | ||
1022 | return printed; | 1155 | return printed; |
@@ -1065,10 +1198,6 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
1065 | browser->b.refresh = hist_browser__refresh; | 1198 | browser->b.refresh = hist_browser__refresh; |
1066 | browser->b.seek = ui_browser__hists_seek; | 1199 | browser->b.seek = ui_browser__hists_seek; |
1067 | browser->b.use_navkeypressed = true; | 1200 | browser->b.use_navkeypressed = true; |
1068 | if (sort__branch_mode == 1) | ||
1069 | browser->has_symbols = sort_sym_from.list.next != NULL; | ||
1070 | else | ||
1071 | browser->has_symbols = sort_sym.list.next != NULL; | ||
1072 | } | 1201 | } |
1073 | 1202 | ||
1074 | return browser; | 1203 | return browser; |
@@ -1098,6 +1227,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1098 | const struct thread *thread = hists->thread_filter; | 1227 | const struct thread *thread = hists->thread_filter; |
1099 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 1228 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
1100 | u64 nr_events = hists->stats.total_period; | 1229 | u64 nr_events = hists->stats.total_period; |
1230 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
1231 | char buf[512]; | ||
1232 | size_t buflen = sizeof(buf); | ||
1233 | |||
1234 | if (perf_evsel__is_group_event(evsel)) { | ||
1235 | struct perf_evsel *pos; | ||
1236 | |||
1237 | perf_evsel__group_desc(evsel, buf, buflen); | ||
1238 | ev_name = buf; | ||
1239 | |||
1240 | for_each_group_member(pos, evsel) { | ||
1241 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1242 | nr_events += pos->hists.stats.total_period; | ||
1243 | } | ||
1244 | } | ||
1101 | 1245 | ||
1102 | nr_samples = convert_unit(nr_samples, &unit); | 1246 | nr_samples = convert_unit(nr_samples, &unit); |
1103 | printed = scnprintf(bf, size, | 1247 | printed = scnprintf(bf, size, |
@@ -1135,10 +1279,114 @@ static inline bool is_report_browser(void *timer) | |||
1135 | return timer == NULL; | 1279 | return timer == NULL; |
1136 | } | 1280 | } |
1137 | 1281 | ||
1282 | /* | ||
1283 | * Only runtime switching of perf data file will make "input_name" point | ||
1284 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide | ||
1285 | * whether we need to call free() for current "input_name" during the switch. | ||
1286 | */ | ||
1287 | static bool is_input_name_malloced = false; | ||
1288 | |||
1289 | static int switch_data_file(void) | ||
1290 | { | ||
1291 | char *pwd, *options[32], *abs_path[32], *tmp; | ||
1292 | DIR *pwd_dir; | ||
1293 | int nr_options = 0, choice = -1, ret = -1; | ||
1294 | struct dirent *dent; | ||
1295 | |||
1296 | pwd = getenv("PWD"); | ||
1297 | if (!pwd) | ||
1298 | return ret; | ||
1299 | |||
1300 | pwd_dir = opendir(pwd); | ||
1301 | if (!pwd_dir) | ||
1302 | return ret; | ||
1303 | |||
1304 | memset(options, 0, sizeof(options)); | ||
1305 | memset(options, 0, sizeof(abs_path)); | ||
1306 | |||
1307 | while ((dent = readdir(pwd_dir))) { | ||
1308 | char path[PATH_MAX]; | ||
1309 | u64 magic; | ||
1310 | char *name = dent->d_name; | ||
1311 | FILE *file; | ||
1312 | |||
1313 | if (!(dent->d_type == DT_REG)) | ||
1314 | continue; | ||
1315 | |||
1316 | snprintf(path, sizeof(path), "%s/%s", pwd, name); | ||
1317 | |||
1318 | file = fopen(path, "r"); | ||
1319 | if (!file) | ||
1320 | continue; | ||
1321 | |||
1322 | if (fread(&magic, 1, 8, file) < 8) | ||
1323 | goto close_file_and_continue; | ||
1324 | |||
1325 | if (is_perf_magic(magic)) { | ||
1326 | options[nr_options] = strdup(name); | ||
1327 | if (!options[nr_options]) | ||
1328 | goto close_file_and_continue; | ||
1329 | |||
1330 | abs_path[nr_options] = strdup(path); | ||
1331 | if (!abs_path[nr_options]) { | ||
1332 | free(options[nr_options]); | ||
1333 | ui__warning("Can't search all data files due to memory shortage.\n"); | ||
1334 | fclose(file); | ||
1335 | break; | ||
1336 | } | ||
1337 | |||
1338 | nr_options++; | ||
1339 | } | ||
1340 | |||
1341 | close_file_and_continue: | ||
1342 | fclose(file); | ||
1343 | if (nr_options >= 32) { | ||
1344 | ui__warning("Too many perf data files in PWD!\n" | ||
1345 | "Only the first 32 files will be listed.\n"); | ||
1346 | break; | ||
1347 | } | ||
1348 | } | ||
1349 | closedir(pwd_dir); | ||
1350 | |||
1351 | if (nr_options) { | ||
1352 | choice = ui__popup_menu(nr_options, options); | ||
1353 | if (choice < nr_options && choice >= 0) { | ||
1354 | tmp = strdup(abs_path[choice]); | ||
1355 | if (tmp) { | ||
1356 | if (is_input_name_malloced) | ||
1357 | free((void *)input_name); | ||
1358 | input_name = tmp; | ||
1359 | is_input_name_malloced = true; | ||
1360 | ret = 0; | ||
1361 | } else | ||
1362 | ui__warning("Data switch failed due to memory shortage!\n"); | ||
1363 | } | ||
1364 | } | ||
1365 | |||
1366 | free_popup_options(options, nr_options); | ||
1367 | free_popup_options(abs_path, nr_options); | ||
1368 | return ret; | ||
1369 | } | ||
1370 | |||
1371 | static void hist_browser__update_pcnt_entries(struct hist_browser *hb) | ||
1372 | { | ||
1373 | u64 nr_entries = 0; | ||
1374 | struct rb_node *nd = rb_first(&hb->hists->entries); | ||
1375 | |||
1376 | while (nd) { | ||
1377 | nr_entries++; | ||
1378 | nd = hists__filter_entries(rb_next(nd), hb->hists, | ||
1379 | hb->min_pcnt); | ||
1380 | } | ||
1381 | |||
1382 | hb->nr_pcnt_entries = nr_entries; | ||
1383 | } | ||
1384 | |||
1138 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 1385 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
1139 | const char *helpline, const char *ev_name, | 1386 | const char *helpline, const char *ev_name, |
1140 | bool left_exits, | 1387 | bool left_exits, |
1141 | struct hist_browser_timer *hbt, | 1388 | struct hist_browser_timer *hbt, |
1389 | float min_pcnt, | ||
1142 | struct perf_session_env *env) | 1390 | struct perf_session_env *env) |
1143 | { | 1391 | { |
1144 | struct hists *hists = &evsel->hists; | 1392 | struct hists *hists = &evsel->hists; |
@@ -1155,6 +1403,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1155 | if (browser == NULL) | 1403 | if (browser == NULL) |
1156 | return -1; | 1404 | return -1; |
1157 | 1405 | ||
1406 | if (min_pcnt) { | ||
1407 | browser->min_pcnt = min_pcnt; | ||
1408 | hist_browser__update_pcnt_entries(browser); | ||
1409 | } | ||
1410 | |||
1158 | fstack = pstack__new(2); | 1411 | fstack = pstack__new(2); |
1159 | if (fstack == NULL) | 1412 | if (fstack == NULL) |
1160 | goto out; | 1413 | goto out; |
@@ -1169,7 +1422,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1169 | int choice = 0, | 1422 | int choice = 0, |
1170 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 1423 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
1171 | annotate_f = -2, annotate_t = -2, browse_map = -2; | 1424 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
1172 | int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2; | 1425 | int scripts_comm = -2, scripts_symbol = -2, |
1426 | scripts_all = -2, switch_data = -2; | ||
1173 | 1427 | ||
1174 | nr_options = 0; | 1428 | nr_options = 0; |
1175 | 1429 | ||
@@ -1190,7 +1444,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1190 | */ | 1444 | */ |
1191 | goto out_free_stack; | 1445 | goto out_free_stack; |
1192 | case 'a': | 1446 | case 'a': |
1193 | if (!browser->has_symbols) { | 1447 | if (!sort__has_sym) { |
1194 | ui_browser__warning(&browser->b, delay_secs * 2, | 1448 | ui_browser__warning(&browser->b, delay_secs * 2, |
1195 | "Annotation is only available for symbolic views, " | 1449 | "Annotation is only available for symbolic views, " |
1196 | "include \"sym*\" in --sort to use it."); | 1450 | "include \"sym*\" in --sort to use it."); |
@@ -1226,6 +1480,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1226 | if (is_report_browser(hbt)) | 1480 | if (is_report_browser(hbt)) |
1227 | goto do_scripts; | 1481 | goto do_scripts; |
1228 | continue; | 1482 | continue; |
1483 | case 's': | ||
1484 | if (is_report_browser(hbt)) | ||
1485 | goto do_data_switch; | ||
1486 | continue; | ||
1229 | case K_F1: | 1487 | case K_F1: |
1230 | case 'h': | 1488 | case 'h': |
1231 | case '?': | 1489 | case '?': |
@@ -1245,6 +1503,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1245 | "d Zoom into current DSO\n" | 1503 | "d Zoom into current DSO\n" |
1246 | "t Zoom into current Thread\n" | 1504 | "t Zoom into current Thread\n" |
1247 | "r Run available scripts('perf report' only)\n" | 1505 | "r Run available scripts('perf report' only)\n" |
1506 | "s Switch to another data file in PWD ('perf report' only)\n" | ||
1248 | "P Print histograms to perf.hist.N\n" | 1507 | "P Print histograms to perf.hist.N\n" |
1249 | "V Verbose (DSO names in callchains, etc)\n" | 1508 | "V Verbose (DSO names in callchains, etc)\n" |
1250 | "/ Filter symbol by name"); | 1509 | "/ Filter symbol by name"); |
@@ -1284,10 +1543,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1284 | continue; | 1543 | continue; |
1285 | } | 1544 | } |
1286 | 1545 | ||
1287 | if (!browser->has_symbols) | 1546 | if (!sort__has_sym) |
1288 | goto add_exit_option; | 1547 | goto add_exit_option; |
1289 | 1548 | ||
1290 | if (sort__branch_mode == 1) { | 1549 | if (sort__mode == SORT_MODE__BRANCH) { |
1291 | bi = browser->he_selection->branch_info; | 1550 | bi = browser->he_selection->branch_info; |
1292 | if (browser->selection != NULL && | 1551 | if (browser->selection != NULL && |
1293 | bi && | 1552 | bi && |
@@ -1352,6 +1611,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1352 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) | 1611 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) |
1353 | scripts_all = nr_options++; | 1612 | scripts_all = nr_options++; |
1354 | 1613 | ||
1614 | if (is_report_browser(hbt) && asprintf(&options[nr_options], | ||
1615 | "Switch to another data file in PWD") > 0) | ||
1616 | switch_data = nr_options++; | ||
1355 | add_exit_option: | 1617 | add_exit_option: |
1356 | options[nr_options++] = (char *)"Exit"; | 1618 | options[nr_options++] = (char *)"Exit"; |
1357 | retry_popup_menu: | 1619 | retry_popup_menu: |
@@ -1394,7 +1656,7 @@ do_annotate: | |||
1394 | * Don't let this be freed, say, by hists__decay_entry. | 1656 | * Don't let this be freed, say, by hists__decay_entry. |
1395 | */ | 1657 | */ |
1396 | he->used = true; | 1658 | he->used = true; |
1397 | err = hist_entry__tui_annotate(he, evsel->idx, hbt); | 1659 | err = hist_entry__tui_annotate(he, evsel, hbt); |
1398 | he->used = false; | 1660 | he->used = false; |
1399 | /* | 1661 | /* |
1400 | * offer option to annotate the other branch source or target | 1662 | * offer option to annotate the other branch source or target |
@@ -1462,6 +1724,16 @@ do_scripts: | |||
1462 | 1724 | ||
1463 | script_browse(script_opt); | 1725 | script_browse(script_opt); |
1464 | } | 1726 | } |
1727 | /* Switch to another data file */ | ||
1728 | else if (choice == switch_data) { | ||
1729 | do_data_switch: | ||
1730 | if (!switch_data_file()) { | ||
1731 | key = K_SWITCH_INPUT_DATA; | ||
1732 | break; | ||
1733 | } else | ||
1734 | ui__warning("Won't switch the data files due to\n" | ||
1735 | "no valid data file get selected!\n"); | ||
1736 | } | ||
1465 | } | 1737 | } |
1466 | out_free_stack: | 1738 | out_free_stack: |
1467 | pstack__delete(fstack); | 1739 | pstack__delete(fstack); |
@@ -1475,6 +1747,7 @@ struct perf_evsel_menu { | |||
1475 | struct ui_browser b; | 1747 | struct ui_browser b; |
1476 | struct perf_evsel *selection; | 1748 | struct perf_evsel *selection; |
1477 | bool lost_events, lost_events_warned; | 1749 | bool lost_events, lost_events_warned; |
1750 | float min_pcnt; | ||
1478 | struct perf_session_env *env; | 1751 | struct perf_session_env *env; |
1479 | }; | 1752 | }; |
1480 | 1753 | ||
@@ -1494,6 +1767,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1494 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1767 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1495 | HE_COLORSET_NORMAL); | 1768 | HE_COLORSET_NORMAL); |
1496 | 1769 | ||
1770 | if (perf_evsel__is_group_event(evsel)) { | ||
1771 | struct perf_evsel *pos; | ||
1772 | |||
1773 | ev_name = perf_evsel__group_name(evsel); | ||
1774 | |||
1775 | for_each_group_member(pos, evsel) { | ||
1776 | nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1777 | } | ||
1778 | } | ||
1779 | |||
1497 | nr_events = convert_unit(nr_events, &unit); | 1780 | nr_events = convert_unit(nr_events, &unit); |
1498 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1781 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1499 | unit, unit == ' ' ? "" : " ", ev_name); | 1782 | unit, unit == ' ' ? "" : " ", ev_name); |
@@ -1558,6 +1841,7 @@ browse_hists: | |||
1558 | ev_name = perf_evsel__name(pos); | 1841 | ev_name = perf_evsel__name(pos); |
1559 | key = perf_evsel__hists_browse(pos, nr_events, help, | 1842 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1560 | ev_name, true, hbt, | 1843 | ev_name, true, hbt, |
1844 | menu->min_pcnt, | ||
1561 | menu->env); | 1845 | menu->env); |
1562 | ui_browser__show_title(&menu->b, title); | 1846 | ui_browser__show_title(&menu->b, title); |
1563 | switch (key) { | 1847 | switch (key) { |
@@ -1578,6 +1862,7 @@ browse_hists: | |||
1578 | "Do you really want to exit?")) | 1862 | "Do you really want to exit?")) |
1579 | continue; | 1863 | continue; |
1580 | /* Fall thru */ | 1864 | /* Fall thru */ |
1865 | case K_SWITCH_INPUT_DATA: | ||
1581 | case 'q': | 1866 | case 'q': |
1582 | case CTRL('c'): | 1867 | case CTRL('c'): |
1583 | goto out; | 1868 | goto out; |
@@ -1604,9 +1889,21 @@ out: | |||
1604 | return key; | 1889 | return key; |
1605 | } | 1890 | } |
1606 | 1891 | ||
1892 | static bool filter_group_entries(struct ui_browser *self __maybe_unused, | ||
1893 | void *entry) | ||
1894 | { | ||
1895 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1896 | |||
1897 | if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) | ||
1898 | return true; | ||
1899 | |||
1900 | return false; | ||
1901 | } | ||
1902 | |||
1607 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1903 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1608 | const char *help, | 1904 | int nr_entries, const char *help, |
1609 | struct hist_browser_timer *hbt, | 1905 | struct hist_browser_timer *hbt, |
1906 | float min_pcnt, | ||
1610 | struct perf_session_env *env) | 1907 | struct perf_session_env *env) |
1611 | { | 1908 | { |
1612 | struct perf_evsel *pos; | 1909 | struct perf_evsel *pos; |
@@ -1616,9 +1913,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1616 | .refresh = ui_browser__list_head_refresh, | 1913 | .refresh = ui_browser__list_head_refresh, |
1617 | .seek = ui_browser__list_head_seek, | 1914 | .seek = ui_browser__list_head_seek, |
1618 | .write = perf_evsel_menu__write, | 1915 | .write = perf_evsel_menu__write, |
1619 | .nr_entries = evlist->nr_entries, | 1916 | .filter = filter_group_entries, |
1917 | .nr_entries = nr_entries, | ||
1620 | .priv = evlist, | 1918 | .priv = evlist, |
1621 | }, | 1919 | }, |
1920 | .min_pcnt = min_pcnt, | ||
1622 | .env = env, | 1921 | .env = env, |
1623 | }; | 1922 | }; |
1624 | 1923 | ||
@@ -1632,20 +1931,39 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1632 | menu.b.width = line_len; | 1931 | menu.b.width = line_len; |
1633 | } | 1932 | } |
1634 | 1933 | ||
1635 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); | 1934 | return perf_evsel_menu__run(&menu, nr_entries, help, hbt); |
1636 | } | 1935 | } |
1637 | 1936 | ||
1638 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 1937 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1639 | struct hist_browser_timer *hbt, | 1938 | struct hist_browser_timer *hbt, |
1939 | float min_pcnt, | ||
1640 | struct perf_session_env *env) | 1940 | struct perf_session_env *env) |
1641 | { | 1941 | { |
1642 | if (evlist->nr_entries == 1) { | 1942 | int nr_entries = evlist->nr_entries; |
1943 | |||
1944 | single_entry: | ||
1945 | if (nr_entries == 1) { | ||
1643 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1946 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1644 | struct perf_evsel, node); | 1947 | struct perf_evsel, node); |
1645 | const char *ev_name = perf_evsel__name(first); | 1948 | const char *ev_name = perf_evsel__name(first); |
1646 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, | 1949 | |
1647 | ev_name, false, hbt, env); | 1950 | return perf_evsel__hists_browse(first, nr_entries, help, |
1951 | ev_name, false, hbt, min_pcnt, | ||
1952 | env); | ||
1953 | } | ||
1954 | |||
1955 | if (symbol_conf.event_group) { | ||
1956 | struct perf_evsel *pos; | ||
1957 | |||
1958 | nr_entries = 0; | ||
1959 | list_for_each_entry(pos, &evlist->entries, node) | ||
1960 | if (perf_evsel__is_group_leader(pos)) | ||
1961 | nr_entries++; | ||
1962 | |||
1963 | if (nr_entries == 1) | ||
1964 | goto single_entry; | ||
1648 | } | 1965 | } |
1649 | 1966 | ||
1650 | return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); | 1967 | return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, |
1968 | hbt, min_pcnt, env); | ||
1651 | } | 1969 | } |
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 98851d55a53e..95c7cfb8f2c6 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <newt.h> | ||
4 | #include <inttypes.h> | 3 | #include <inttypes.h> |
5 | #include <sys/ttydefaults.h> | 4 | #include <sys/ttydefaults.h> |
6 | #include <string.h> | 5 | #include <string.h> |
@@ -10,41 +9,9 @@ | |||
10 | #include "../../util/symbol.h" | 9 | #include "../../util/symbol.h" |
11 | #include "../browser.h" | 10 | #include "../browser.h" |
12 | #include "../helpline.h" | 11 | #include "../helpline.h" |
12 | #include "../keysyms.h" | ||
13 | #include "map.h" | 13 | #include "map.h" |
14 | 14 | ||
15 | static int ui_entry__read(const char *title, char *bf, size_t size, int width) | ||
16 | { | ||
17 | struct newtExitStruct es; | ||
18 | newtComponent form, entry; | ||
19 | const char *result; | ||
20 | int err = -1; | ||
21 | |||
22 | newtCenteredWindow(width, 1, title); | ||
23 | form = newtForm(NULL, NULL, 0); | ||
24 | if (form == NULL) | ||
25 | return -1; | ||
26 | |||
27 | entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); | ||
28 | if (entry == NULL) | ||
29 | goto out_free_form; | ||
30 | |||
31 | newtFormAddComponent(form, entry); | ||
32 | newtFormAddHotKey(form, NEWT_KEY_ENTER); | ||
33 | newtFormAddHotKey(form, NEWT_KEY_ESCAPE); | ||
34 | newtFormAddHotKey(form, NEWT_KEY_LEFT); | ||
35 | newtFormAddHotKey(form, CTRL('c')); | ||
36 | newtFormRun(form, &es); | ||
37 | |||
38 | if (result != NULL) { | ||
39 | strncpy(bf, result, size); | ||
40 | err = 0; | ||
41 | } | ||
42 | out_free_form: | ||
43 | newtPopWindow(); | ||
44 | newtFormDestroy(form); | ||
45 | return err; | ||
46 | } | ||
47 | |||
48 | struct map_browser { | 15 | struct map_browser { |
49 | struct ui_browser b; | 16 | struct ui_browser b; |
50 | struct map *map; | 17 | struct map *map; |
@@ -78,10 +45,11 @@ static int map_browser__search(struct map_browser *self) | |||
78 | { | 45 | { |
79 | char target[512]; | 46 | char target[512]; |
80 | struct symbol *sym; | 47 | struct symbol *sym; |
81 | int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); | 48 | int err = ui_browser__input_window("Search by name/addr", |
82 | 49 | "Prefix with 0x to search by address", | |
83 | if (err) | 50 | target, "ENTER: OK, ESC: Cancel", 0); |
84 | return err; | 51 | if (err != K_ENTER) |
52 | return -1; | ||
85 | 53 | ||
86 | if (target[0] == '0' && tolower(target[1]) == 'x') { | 54 | if (target[0] == '0' && tolower(target[1]) == 'x') { |
87 | u64 addr = strtoull(target, NULL, 16); | 55 | u64 addr = strtoull(target, NULL, 16); |
@@ -112,12 +80,20 @@ static int map_browser__run(struct map_browser *self) | |||
112 | while (1) { | 80 | while (1) { |
113 | key = ui_browser__run(&self->b, 0); | 81 | key = ui_browser__run(&self->b, 0); |
114 | 82 | ||
115 | if (verbose && key == '/') | 83 | switch (key) { |
116 | map_browser__search(self); | 84 | case '/': |
117 | else | 85 | if (verbose) |
86 | map_browser__search(self); | ||
87 | default: | ||
118 | break; | 88 | break; |
89 | case K_LEFT: | ||
90 | case K_ESC: | ||
91 | case 'q': | ||
92 | case CTRL('c'): | ||
93 | goto out; | ||
94 | } | ||
119 | } | 95 | } |
120 | 96 | out: | |
121 | ui_browser__hide(&self->b); | 97 | ui_browser__hide(&self->b); |
122 | return key; | 98 | return key; |
123 | } | 99 | } |
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index cbbd44b0d93e..12f009e61e94 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c | |||
@@ -1,5 +1,4 @@ | |||
1 | #include <elf.h> | 1 | #include <elf.h> |
2 | #include <newt.h> | ||
3 | #include <inttypes.h> | 2 | #include <inttypes.h> |
4 | #include <sys/ttydefaults.h> | 3 | #include <sys/ttydefaults.h> |
5 | #include <string.h> | 4 | #include <string.h> |
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c new file mode 100644 index 000000000000..f538794615db --- /dev/null +++ b/tools/perf/ui/gtk/annotate.c | |||
@@ -0,0 +1,245 @@ | |||
1 | #include "gtk.h" | ||
2 | #include "util/debug.h" | ||
3 | #include "util/annotate.h" | ||
4 | #include "util/evsel.h" | ||
5 | #include "ui/helpline.h" | ||
6 | |||
7 | |||
8 | enum { | ||
9 | ANN_COL__PERCENT, | ||
10 | ANN_COL__OFFSET, | ||
11 | ANN_COL__LINE, | ||
12 | |||
13 | MAX_ANN_COLS | ||
14 | }; | ||
15 | |||
16 | static const char *const col_names[] = { | ||
17 | "Overhead", | ||
18 | "Offset", | ||
19 | "Line" | ||
20 | }; | ||
21 | |||
22 | static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym, | ||
23 | struct disasm_line *dl, int evidx) | ||
24 | { | ||
25 | struct sym_hist *symhist; | ||
26 | double percent = 0.0; | ||
27 | const char *markup; | ||
28 | int ret = 0; | ||
29 | |||
30 | strcpy(buf, ""); | ||
31 | |||
32 | if (dl->offset == (s64) -1) | ||
33 | return 0; | ||
34 | |||
35 | symhist = annotation__histogram(symbol__annotation(sym), evidx); | ||
36 | if (!symbol_conf.event_group && !symhist->addr[dl->offset]) | ||
37 | return 0; | ||
38 | |||
39 | percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; | ||
40 | |||
41 | markup = perf_gtk__get_percent_color(percent); | ||
42 | if (markup) | ||
43 | ret += scnprintf(buf, size, "%s", markup); | ||
44 | ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent); | ||
45 | if (markup) | ||
46 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
47 | |||
48 | return ret; | ||
49 | } | ||
50 | |||
51 | static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym, | ||
52 | struct map *map, struct disasm_line *dl) | ||
53 | { | ||
54 | u64 start = map__rip_2objdump(map, sym->start); | ||
55 | |||
56 | strcpy(buf, ""); | ||
57 | |||
58 | if (dl->offset == (s64) -1) | ||
59 | return 0; | ||
60 | |||
61 | return scnprintf(buf, size, "%"PRIx64, start + dl->offset); | ||
62 | } | ||
63 | |||
64 | static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl) | ||
65 | { | ||
66 | int ret = 0; | ||
67 | char *line = g_markup_escape_text(dl->line, -1); | ||
68 | const char *markup = "<span fgcolor='gray'>"; | ||
69 | |||
70 | strcpy(buf, ""); | ||
71 | |||
72 | if (!line) | ||
73 | return 0; | ||
74 | |||
75 | if (dl->offset != (s64) -1) | ||
76 | markup = NULL; | ||
77 | |||
78 | if (markup) | ||
79 | ret += scnprintf(buf, size, "%s", markup); | ||
80 | ret += scnprintf(buf + ret, size - ret, "%s", line); | ||
81 | if (markup) | ||
82 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
83 | |||
84 | g_free(line); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, | ||
89 | struct map *map, struct perf_evsel *evsel, | ||
90 | struct hist_browser_timer *hbt __maybe_unused) | ||
91 | { | ||
92 | struct disasm_line *pos, *n; | ||
93 | struct annotation *notes; | ||
94 | GType col_types[MAX_ANN_COLS]; | ||
95 | GtkCellRenderer *renderer; | ||
96 | GtkListStore *store; | ||
97 | GtkWidget *view; | ||
98 | int i; | ||
99 | char s[512]; | ||
100 | |||
101 | notes = symbol__annotation(sym); | ||
102 | |||
103 | for (i = 0; i < MAX_ANN_COLS; i++) { | ||
104 | col_types[i] = G_TYPE_STRING; | ||
105 | } | ||
106 | store = gtk_list_store_newv(MAX_ANN_COLS, col_types); | ||
107 | |||
108 | view = gtk_tree_view_new(); | ||
109 | renderer = gtk_cell_renderer_text_new(); | ||
110 | |||
111 | for (i = 0; i < MAX_ANN_COLS; i++) { | ||
112 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
113 | -1, col_names[i], renderer, "markup", | ||
114 | i, NULL); | ||
115 | } | ||
116 | |||
117 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
118 | g_object_unref(GTK_TREE_MODEL(store)); | ||
119 | |||
120 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
121 | GtkTreeIter iter; | ||
122 | int ret = 0; | ||
123 | |||
124 | gtk_list_store_append(store, &iter); | ||
125 | |||
126 | if (perf_evsel__is_group_event(evsel)) { | ||
127 | for (i = 0; i < evsel->nr_members; i++) { | ||
128 | ret += perf_gtk__get_percent(s + ret, | ||
129 | sizeof(s) - ret, | ||
130 | sym, pos, | ||
131 | evsel->idx + i); | ||
132 | ret += scnprintf(s + ret, sizeof(s) - ret, " "); | ||
133 | } | ||
134 | } else { | ||
135 | ret = perf_gtk__get_percent(s, sizeof(s), sym, pos, | ||
136 | evsel->idx); | ||
137 | } | ||
138 | |||
139 | if (ret) | ||
140 | gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); | ||
141 | if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) | ||
142 | gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); | ||
143 | if (perf_gtk__get_line(s, sizeof(s), pos)) | ||
144 | gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1); | ||
145 | } | ||
146 | |||
147 | gtk_container_add(GTK_CONTAINER(window), view); | ||
148 | |||
149 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | ||
150 | list_del(&pos->node); | ||
151 | disasm_line__free(pos); | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, | ||
158 | struct perf_evsel *evsel, | ||
159 | struct hist_browser_timer *hbt) | ||
160 | { | ||
161 | GtkWidget *window; | ||
162 | GtkWidget *notebook; | ||
163 | GtkWidget *scrolled_window; | ||
164 | GtkWidget *tab_label; | ||
165 | |||
166 | if (map->dso->annotate_warned) | ||
167 | return -1; | ||
168 | |||
169 | if (symbol__annotate(sym, map, 0) < 0) { | ||
170 | ui__error("%s", ui_helpline__current); | ||
171 | return -1; | ||
172 | } | ||
173 | |||
174 | if (perf_gtk__is_active_context(pgctx)) { | ||
175 | window = pgctx->main_window; | ||
176 | notebook = pgctx->notebook; | ||
177 | } else { | ||
178 | GtkWidget *vbox; | ||
179 | GtkWidget *infobar; | ||
180 | GtkWidget *statbar; | ||
181 | |||
182 | signal(SIGSEGV, perf_gtk__signal); | ||
183 | signal(SIGFPE, perf_gtk__signal); | ||
184 | signal(SIGINT, perf_gtk__signal); | ||
185 | signal(SIGQUIT, perf_gtk__signal); | ||
186 | signal(SIGTERM, perf_gtk__signal); | ||
187 | |||
188 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
189 | gtk_window_set_title(GTK_WINDOW(window), "perf annotate"); | ||
190 | |||
191 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
192 | |||
193 | pgctx = perf_gtk__activate_context(window); | ||
194 | if (!pgctx) | ||
195 | return -1; | ||
196 | |||
197 | vbox = gtk_vbox_new(FALSE, 0); | ||
198 | notebook = gtk_notebook_new(); | ||
199 | pgctx->notebook = notebook; | ||
200 | |||
201 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
202 | |||
203 | infobar = perf_gtk__setup_info_bar(); | ||
204 | if (infobar) { | ||
205 | gtk_box_pack_start(GTK_BOX(vbox), infobar, | ||
206 | FALSE, FALSE, 0); | ||
207 | } | ||
208 | |||
209 | statbar = perf_gtk__setup_statusbar(); | ||
210 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
211 | |||
212 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
213 | } | ||
214 | |||
215 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
216 | tab_label = gtk_label_new(sym->name); | ||
217 | |||
218 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
219 | GTK_POLICY_AUTOMATIC, | ||
220 | GTK_POLICY_AUTOMATIC); | ||
221 | |||
222 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, | ||
223 | tab_label); | ||
224 | |||
225 | perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | void perf_gtk__show_annotations(void) | ||
230 | { | ||
231 | GtkWidget *window; | ||
232 | |||
233 | if (!perf_gtk__is_active_context(pgctx)) | ||
234 | return; | ||
235 | |||
236 | window = pgctx->main_window; | ||
237 | gtk_widget_show_all(window); | ||
238 | |||
239 | perf_gtk__resize_window(window); | ||
240 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
241 | |||
242 | gtk_main(); | ||
243 | |||
244 | perf_gtk__deactivate_context(&pgctx); | ||
245 | } | ||
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 253b6219a39e..c95012cdb438 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c | |||
@@ -8,15 +8,13 @@ | |||
8 | 8 | ||
9 | #include <signal.h> | 9 | #include <signal.h> |
10 | 10 | ||
11 | #define MAX_COLUMNS 32 | 11 | void perf_gtk__signal(int sig) |
12 | |||
13 | static void perf_gtk__signal(int sig) | ||
14 | { | 12 | { |
15 | perf_gtk__exit(false); | 13 | perf_gtk__exit(false); |
16 | psignal(sig, "perf"); | 14 | psignal(sig, "perf"); |
17 | } | 15 | } |
18 | 16 | ||
19 | static void perf_gtk__resize_window(GtkWidget *window) | 17 | void perf_gtk__resize_window(GtkWidget *window) |
20 | { | 18 | { |
21 | GdkRectangle rect; | 19 | GdkRectangle rect; |
22 | GdkScreen *screen; | 20 | GdkScreen *screen; |
@@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window) | |||
36 | gtk_window_resize(GTK_WINDOW(window), width, height); | 34 | gtk_window_resize(GTK_WINDOW(window), width, height); |
37 | } | 35 | } |
38 | 36 | ||
39 | static const char *perf_gtk__get_percent_color(double percent) | 37 | const char *perf_gtk__get_percent_color(double percent) |
40 | { | 38 | { |
41 | if (percent >= MIN_RED) | 39 | if (percent >= MIN_RED) |
42 | return "<span fgcolor='red'>"; | 40 | return "<span fgcolor='red'>"; |
@@ -45,155 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent) | |||
45 | return NULL; | 43 | return NULL; |
46 | } | 44 | } |
47 | 45 | ||
48 | #define HPP__COLOR_FN(_name, _field) \ | ||
49 | static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \ | ||
50 | struct hist_entry *he) \ | ||
51 | { \ | ||
52 | struct hists *hists = he->hists; \ | ||
53 | double percent = 100.0 * he->stat._field / hists->stats.total_period; \ | ||
54 | const char *markup; \ | ||
55 | int ret = 0; \ | ||
56 | \ | ||
57 | markup = perf_gtk__get_percent_color(percent); \ | ||
58 | if (markup) \ | ||
59 | ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \ | ||
60 | ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \ | ||
61 | if (markup) \ | ||
62 | ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \ | ||
63 | \ | ||
64 | return ret; \ | ||
65 | } | ||
66 | |||
67 | HPP__COLOR_FN(overhead, period) | ||
68 | HPP__COLOR_FN(overhead_sys, period_sys) | ||
69 | HPP__COLOR_FN(overhead_us, period_us) | ||
70 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | ||
71 | HPP__COLOR_FN(overhead_guest_us, period_guest_us) | ||
72 | |||
73 | #undef HPP__COLOR_FN | ||
74 | |||
75 | void perf_gtk__init_hpp(void) | ||
76 | { | ||
77 | perf_hpp__init(); | ||
78 | |||
79 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | ||
80 | perf_gtk__hpp_color_overhead; | ||
81 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | ||
82 | perf_gtk__hpp_color_overhead_sys; | ||
83 | perf_hpp__format[PERF_HPP__OVERHEAD_US].color = | ||
84 | perf_gtk__hpp_color_overhead_us; | ||
85 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = | ||
86 | perf_gtk__hpp_color_overhead_guest_sys; | ||
87 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = | ||
88 | perf_gtk__hpp_color_overhead_guest_us; | ||
89 | } | ||
90 | |||
91 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) | ||
92 | { | ||
93 | GType col_types[MAX_COLUMNS]; | ||
94 | GtkCellRenderer *renderer; | ||
95 | struct sort_entry *se; | ||
96 | GtkListStore *store; | ||
97 | struct rb_node *nd; | ||
98 | GtkWidget *view; | ||
99 | int i, col_idx; | ||
100 | int nr_cols; | ||
101 | char s[512]; | ||
102 | |||
103 | struct perf_hpp hpp = { | ||
104 | .buf = s, | ||
105 | .size = sizeof(s), | ||
106 | }; | ||
107 | |||
108 | nr_cols = 0; | ||
109 | |||
110 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
111 | if (!perf_hpp__format[i].cond) | ||
112 | continue; | ||
113 | |||
114 | col_types[nr_cols++] = G_TYPE_STRING; | ||
115 | } | ||
116 | |||
117 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
118 | if (se->elide) | ||
119 | continue; | ||
120 | |||
121 | col_types[nr_cols++] = G_TYPE_STRING; | ||
122 | } | ||
123 | |||
124 | store = gtk_list_store_newv(nr_cols, col_types); | ||
125 | |||
126 | view = gtk_tree_view_new(); | ||
127 | |||
128 | renderer = gtk_cell_renderer_text_new(); | ||
129 | |||
130 | col_idx = 0; | ||
131 | |||
132 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
133 | if (!perf_hpp__format[i].cond) | ||
134 | continue; | ||
135 | |||
136 | perf_hpp__format[i].header(&hpp); | ||
137 | |||
138 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
139 | -1, s, | ||
140 | renderer, "markup", | ||
141 | col_idx++, NULL); | ||
142 | } | ||
143 | |||
144 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
145 | if (se->elide) | ||
146 | continue; | ||
147 | |||
148 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
149 | -1, se->se_header, | ||
150 | renderer, "text", | ||
151 | col_idx++, NULL); | ||
152 | } | ||
153 | |||
154 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
155 | |||
156 | g_object_unref(GTK_TREE_MODEL(store)); | ||
157 | |||
158 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
159 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
160 | GtkTreeIter iter; | ||
161 | |||
162 | if (h->filtered) | ||
163 | continue; | ||
164 | |||
165 | gtk_list_store_append(store, &iter); | ||
166 | |||
167 | col_idx = 0; | ||
168 | |||
169 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
170 | if (!perf_hpp__format[i].cond) | ||
171 | continue; | ||
172 | |||
173 | if (perf_hpp__format[i].color) | ||
174 | perf_hpp__format[i].color(&hpp, h); | ||
175 | else | ||
176 | perf_hpp__format[i].entry(&hpp, h); | ||
177 | |||
178 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
179 | } | ||
180 | |||
181 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
182 | if (se->elide) | ||
183 | continue; | ||
184 | |||
185 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
186 | hists__col_len(hists, se->se_width_idx)); | ||
187 | |||
188 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | gtk_container_add(GTK_CONTAINER(window), view); | ||
193 | } | ||
194 | |||
195 | #ifdef HAVE_GTK_INFO_BAR | 46 | #ifdef HAVE_GTK_INFO_BAR |
196 | static GtkWidget *perf_gtk__setup_info_bar(void) | 47 | GtkWidget *perf_gtk__setup_info_bar(void) |
197 | { | 48 | { |
198 | GtkWidget *info_bar; | 49 | GtkWidget *info_bar; |
199 | GtkWidget *label; | 50 | GtkWidget *label; |
@@ -220,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void) | |||
220 | } | 71 | } |
221 | #endif | 72 | #endif |
222 | 73 | ||
223 | static GtkWidget *perf_gtk__setup_statusbar(void) | 74 | GtkWidget *perf_gtk__setup_statusbar(void) |
224 | { | 75 | { |
225 | GtkWidget *stbar; | 76 | GtkWidget *stbar; |
226 | unsigned ctxid; | 77 | unsigned ctxid; |
@@ -234,79 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void) | |||
234 | 85 | ||
235 | return stbar; | 86 | return stbar; |
236 | } | 87 | } |
237 | |||
238 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
239 | const char *help, | ||
240 | struct hist_browser_timer *hbt __maybe_unused) | ||
241 | { | ||
242 | struct perf_evsel *pos; | ||
243 | GtkWidget *vbox; | ||
244 | GtkWidget *notebook; | ||
245 | GtkWidget *info_bar; | ||
246 | GtkWidget *statbar; | ||
247 | GtkWidget *window; | ||
248 | |||
249 | signal(SIGSEGV, perf_gtk__signal); | ||
250 | signal(SIGFPE, perf_gtk__signal); | ||
251 | signal(SIGINT, perf_gtk__signal); | ||
252 | signal(SIGQUIT, perf_gtk__signal); | ||
253 | signal(SIGTERM, perf_gtk__signal); | ||
254 | |||
255 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
256 | |||
257 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
258 | |||
259 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
260 | |||
261 | pgctx = perf_gtk__activate_context(window); | ||
262 | if (!pgctx) | ||
263 | return -1; | ||
264 | |||
265 | vbox = gtk_vbox_new(FALSE, 0); | ||
266 | |||
267 | notebook = gtk_notebook_new(); | ||
268 | |||
269 | list_for_each_entry(pos, &evlist->entries, node) { | ||
270 | struct hists *hists = &pos->hists; | ||
271 | const char *evname = perf_evsel__name(pos); | ||
272 | GtkWidget *scrolled_window; | ||
273 | GtkWidget *tab_label; | ||
274 | |||
275 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
276 | |||
277 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
278 | GTK_POLICY_AUTOMATIC, | ||
279 | GTK_POLICY_AUTOMATIC); | ||
280 | |||
281 | perf_gtk__show_hists(scrolled_window, hists); | ||
282 | |||
283 | tab_label = gtk_label_new(evname); | ||
284 | |||
285 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
286 | } | ||
287 | |||
288 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
289 | |||
290 | info_bar = perf_gtk__setup_info_bar(); | ||
291 | if (info_bar) | ||
292 | gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); | ||
293 | |||
294 | statbar = perf_gtk__setup_statusbar(); | ||
295 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
296 | |||
297 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
298 | |||
299 | gtk_widget_show_all(window); | ||
300 | |||
301 | perf_gtk__resize_window(window); | ||
302 | |||
303 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
304 | |||
305 | ui_helpline__push(help); | ||
306 | |||
307 | gtk_main(); | ||
308 | |||
309 | perf_gtk__deactivate_context(&pgctx); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 856320e2cc05..3d96785ef155 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | struct perf_gtk_context { | 11 | struct perf_gtk_context { |
12 | GtkWidget *main_window; | 12 | GtkWidget *main_window; |
13 | GtkWidget *notebook; | ||
13 | 14 | ||
14 | #ifdef HAVE_GTK_INFO_BAR | 15 | #ifdef HAVE_GTK_INFO_BAR |
15 | GtkWidget *info_bar; | 16 | GtkWidget *info_bar; |
@@ -33,7 +34,14 @@ void perf_gtk__init_helpline(void); | |||
33 | void perf_gtk__init_progress(void); | 34 | void perf_gtk__init_progress(void); |
34 | void perf_gtk__init_hpp(void); | 35 | void perf_gtk__init_hpp(void); |
35 | 36 | ||
36 | #ifndef HAVE_GTK_INFO_BAR | 37 | void perf_gtk__signal(int sig); |
38 | void perf_gtk__resize_window(GtkWidget *window); | ||
39 | const char *perf_gtk__get_percent_color(double percent); | ||
40 | GtkWidget *perf_gtk__setup_statusbar(void); | ||
41 | |||
42 | #ifdef HAVE_GTK_INFO_BAR | ||
43 | GtkWidget *perf_gtk__setup_info_bar(void); | ||
44 | #else | ||
37 | static inline GtkWidget *perf_gtk__setup_info_bar(void) | 45 | static inline GtkWidget *perf_gtk__setup_info_bar(void) |
38 | { | 46 | { |
39 | return NULL; | 47 | return NULL; |
diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c index 5db4432ff12a..3388cbd12186 100644 --- a/tools/perf/ui/gtk/helpline.c +++ b/tools/perf/ui/gtk/helpline.c | |||
@@ -24,17 +24,7 @@ static void gtk_helpline_push(const char *msg) | |||
24 | pgctx->statbar_ctx_id, msg); | 24 | pgctx->statbar_ctx_id, msg); |
25 | } | 25 | } |
26 | 26 | ||
27 | static struct ui_helpline gtk_helpline_fns = { | 27 | static int gtk_helpline_show(const char *fmt, va_list ap) |
28 | .pop = gtk_helpline_pop, | ||
29 | .push = gtk_helpline_push, | ||
30 | }; | ||
31 | |||
32 | void perf_gtk__init_helpline(void) | ||
33 | { | ||
34 | helpline_fns = >k_helpline_fns; | ||
35 | } | ||
36 | |||
37 | int perf_gtk__show_helpline(const char *fmt, va_list ap) | ||
38 | { | 28 | { |
39 | int ret; | 29 | int ret; |
40 | char *ptr; | 30 | char *ptr; |
@@ -54,3 +44,14 @@ int perf_gtk__show_helpline(const char *fmt, va_list ap) | |||
54 | 44 | ||
55 | return ret; | 45 | return ret; |
56 | } | 46 | } |
47 | |||
48 | static struct ui_helpline gtk_helpline_fns = { | ||
49 | .pop = gtk_helpline_pop, | ||
50 | .push = gtk_helpline_push, | ||
51 | .show = gtk_helpline_show, | ||
52 | }; | ||
53 | |||
54 | void perf_gtk__init_helpline(void) | ||
55 | { | ||
56 | helpline_fns = >k_helpline_fns; | ||
57 | } | ||
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c new file mode 100644 index 000000000000..9708dd5fb8f3 --- /dev/null +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include "../evlist.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../evsel.h" | ||
4 | #include "../sort.h" | ||
5 | #include "../hist.h" | ||
6 | #include "../helpline.h" | ||
7 | #include "gtk.h" | ||
8 | |||
9 | #define MAX_COLUMNS 32 | ||
10 | |||
11 | static int __percent_color_snprintf(char *buf, size_t size, double percent) | ||
12 | { | ||
13 | int ret = 0; | ||
14 | const char *markup; | ||
15 | |||
16 | markup = perf_gtk__get_percent_color(percent); | ||
17 | if (markup) | ||
18 | ret += scnprintf(buf, size, markup); | ||
19 | |||
20 | ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent); | ||
21 | |||
22 | if (markup) | ||
23 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
24 | |||
25 | return ret; | ||
26 | } | ||
27 | |||
28 | |||
29 | static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
30 | u64 (*get_field)(struct hist_entry *)) | ||
31 | { | ||
32 | int ret; | ||
33 | double percent = 0.0; | ||
34 | struct hists *hists = he->hists; | ||
35 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
36 | |||
37 | if (hists->stats.total_period) | ||
38 | percent = 100.0 * get_field(he) / hists->stats.total_period; | ||
39 | |||
40 | ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); | ||
41 | |||
42 | if (perf_evsel__is_group_event(evsel)) { | ||
43 | int prev_idx, idx_delta; | ||
44 | struct hist_entry *pair; | ||
45 | int nr_members = evsel->nr_members; | ||
46 | |||
47 | prev_idx = perf_evsel__group_idx(evsel); | ||
48 | |||
49 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | ||
50 | u64 period = get_field(pair); | ||
51 | u64 total = pair->hists->stats.total_period; | ||
52 | |||
53 | evsel = hists_to_evsel(pair->hists); | ||
54 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; | ||
55 | |||
56 | while (idx_delta--) { | ||
57 | /* | ||
58 | * zero-fill group members in the middle which | ||
59 | * have no sample | ||
60 | */ | ||
61 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
62 | hpp->size - ret, | ||
63 | 0.0); | ||
64 | } | ||
65 | |||
66 | percent = 100.0 * period / total; | ||
67 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
68 | hpp->size - ret, | ||
69 | percent); | ||
70 | |||
71 | prev_idx = perf_evsel__group_idx(evsel); | ||
72 | } | ||
73 | |||
74 | idx_delta = nr_members - prev_idx - 1; | ||
75 | |||
76 | while (idx_delta--) { | ||
77 | /* | ||
78 | * zero-fill group members at last which have no sample | ||
79 | */ | ||
80 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
81 | hpp->size - ret, | ||
82 | 0.0); | ||
83 | } | ||
84 | } | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | #define __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
89 | static u64 he_get_##_field(struct hist_entry *he) \ | ||
90 | { \ | ||
91 | return he->stat._field; \ | ||
92 | } \ | ||
93 | \ | ||
94 | static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \ | ||
95 | struct hist_entry *he) \ | ||
96 | { \ | ||
97 | return __hpp__color_fmt(hpp, he, he_get_##_field); \ | ||
98 | } | ||
99 | |||
100 | __HPP_COLOR_PERCENT_FN(overhead, period) | ||
101 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) | ||
102 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us) | ||
103 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) | ||
104 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) | ||
105 | |||
106 | #undef __HPP_COLOR_PERCENT_FN | ||
107 | |||
108 | |||
109 | void perf_gtk__init_hpp(void) | ||
110 | { | ||
111 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
112 | |||
113 | perf_hpp__init(); | ||
114 | |||
115 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | ||
116 | perf_gtk__hpp_color_overhead; | ||
117 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | ||
118 | perf_gtk__hpp_color_overhead_sys; | ||
119 | perf_hpp__format[PERF_HPP__OVERHEAD_US].color = | ||
120 | perf_gtk__hpp_color_overhead_us; | ||
121 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = | ||
122 | perf_gtk__hpp_color_overhead_guest_sys; | ||
123 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = | ||
124 | perf_gtk__hpp_color_overhead_guest_us; | ||
125 | } | ||
126 | |||
127 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | ||
128 | float min_pcnt) | ||
129 | { | ||
130 | struct perf_hpp_fmt *fmt; | ||
131 | GType col_types[MAX_COLUMNS]; | ||
132 | GtkCellRenderer *renderer; | ||
133 | struct sort_entry *se; | ||
134 | GtkListStore *store; | ||
135 | struct rb_node *nd; | ||
136 | GtkWidget *view; | ||
137 | int col_idx; | ||
138 | int nr_cols; | ||
139 | char s[512]; | ||
140 | |||
141 | struct perf_hpp hpp = { | ||
142 | .buf = s, | ||
143 | .size = sizeof(s), | ||
144 | .ptr = hists_to_evsel(hists), | ||
145 | }; | ||
146 | |||
147 | nr_cols = 0; | ||
148 | |||
149 | perf_hpp__for_each_format(fmt) | ||
150 | col_types[nr_cols++] = G_TYPE_STRING; | ||
151 | |||
152 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
153 | if (se->elide) | ||
154 | continue; | ||
155 | |||
156 | col_types[nr_cols++] = G_TYPE_STRING; | ||
157 | } | ||
158 | |||
159 | store = gtk_list_store_newv(nr_cols, col_types); | ||
160 | |||
161 | view = gtk_tree_view_new(); | ||
162 | |||
163 | renderer = gtk_cell_renderer_text_new(); | ||
164 | |||
165 | col_idx = 0; | ||
166 | |||
167 | perf_hpp__for_each_format(fmt) { | ||
168 | fmt->header(&hpp); | ||
169 | |||
170 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
171 | -1, ltrim(s), | ||
172 | renderer, "markup", | ||
173 | col_idx++, NULL); | ||
174 | } | ||
175 | |||
176 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
177 | if (se->elide) | ||
178 | continue; | ||
179 | |||
180 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
181 | -1, se->se_header, | ||
182 | renderer, "text", | ||
183 | col_idx++, NULL); | ||
184 | } | ||
185 | |||
186 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
187 | |||
188 | g_object_unref(GTK_TREE_MODEL(store)); | ||
189 | |||
190 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
191 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
192 | GtkTreeIter iter; | ||
193 | float percent = h->stat.period * 100.0 / | ||
194 | hists->stats.total_period; | ||
195 | |||
196 | if (h->filtered) | ||
197 | continue; | ||
198 | |||
199 | if (percent < min_pcnt) | ||
200 | continue; | ||
201 | |||
202 | gtk_list_store_append(store, &iter); | ||
203 | |||
204 | col_idx = 0; | ||
205 | |||
206 | perf_hpp__for_each_format(fmt) { | ||
207 | if (fmt->color) | ||
208 | fmt->color(&hpp, h); | ||
209 | else | ||
210 | fmt->entry(&hpp, h); | ||
211 | |||
212 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
213 | } | ||
214 | |||
215 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
216 | if (se->elide) | ||
217 | continue; | ||
218 | |||
219 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
220 | hists__col_len(hists, se->se_width_idx)); | ||
221 | |||
222 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | gtk_container_add(GTK_CONTAINER(window), view); | ||
227 | } | ||
228 | |||
229 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
230 | const char *help, | ||
231 | struct hist_browser_timer *hbt __maybe_unused, | ||
232 | float min_pcnt) | ||
233 | { | ||
234 | struct perf_evsel *pos; | ||
235 | GtkWidget *vbox; | ||
236 | GtkWidget *notebook; | ||
237 | GtkWidget *info_bar; | ||
238 | GtkWidget *statbar; | ||
239 | GtkWidget *window; | ||
240 | |||
241 | signal(SIGSEGV, perf_gtk__signal); | ||
242 | signal(SIGFPE, perf_gtk__signal); | ||
243 | signal(SIGINT, perf_gtk__signal); | ||
244 | signal(SIGQUIT, perf_gtk__signal); | ||
245 | signal(SIGTERM, perf_gtk__signal); | ||
246 | |||
247 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
248 | |||
249 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
250 | |||
251 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
252 | |||
253 | pgctx = perf_gtk__activate_context(window); | ||
254 | if (!pgctx) | ||
255 | return -1; | ||
256 | |||
257 | vbox = gtk_vbox_new(FALSE, 0); | ||
258 | |||
259 | notebook = gtk_notebook_new(); | ||
260 | |||
261 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
262 | |||
263 | info_bar = perf_gtk__setup_info_bar(); | ||
264 | if (info_bar) | ||
265 | gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); | ||
266 | |||
267 | statbar = perf_gtk__setup_statusbar(); | ||
268 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
269 | |||
270 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
271 | |||
272 | list_for_each_entry(pos, &evlist->entries, node) { | ||
273 | struct hists *hists = &pos->hists; | ||
274 | const char *evname = perf_evsel__name(pos); | ||
275 | GtkWidget *scrolled_window; | ||
276 | GtkWidget *tab_label; | ||
277 | char buf[512]; | ||
278 | size_t size = sizeof(buf); | ||
279 | |||
280 | if (symbol_conf.event_group) { | ||
281 | if (!perf_evsel__is_group_leader(pos)) | ||
282 | continue; | ||
283 | |||
284 | if (pos->nr_members > 1) { | ||
285 | perf_evsel__group_desc(pos, buf, size); | ||
286 | evname = buf; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
291 | |||
292 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
293 | GTK_POLICY_AUTOMATIC, | ||
294 | GTK_POLICY_AUTOMATIC); | ||
295 | |||
296 | perf_gtk__show_hists(scrolled_window, hists, min_pcnt); | ||
297 | |||
298 | tab_label = gtk_label_new(evname); | ||
299 | |||
300 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
301 | } | ||
302 | |||
303 | gtk_widget_show_all(window); | ||
304 | |||
305 | perf_gtk__resize_window(window); | ||
306 | |||
307 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
308 | |||
309 | ui_helpline__push(help); | ||
310 | |||
311 | gtk_main(); | ||
312 | |||
313 | perf_gtk__deactivate_context(&pgctx); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c index a49bcf3c190b..700fb3cfa1c7 100644 --- a/tools/perf/ui/helpline.c +++ b/tools/perf/ui/helpline.c | |||
@@ -16,9 +16,16 @@ static void nop_helpline__push(const char *msg __maybe_unused) | |||
16 | { | 16 | { |
17 | } | 17 | } |
18 | 18 | ||
19 | static int nop_helpline__show(const char *fmt __maybe_unused, | ||
20 | va_list ap __maybe_unused) | ||
21 | { | ||
22 | return 0; | ||
23 | } | ||
24 | |||
19 | static struct ui_helpline default_helpline_fns = { | 25 | static struct ui_helpline default_helpline_fns = { |
20 | .pop = nop_helpline__pop, | 26 | .pop = nop_helpline__pop, |
21 | .push = nop_helpline__push, | 27 | .push = nop_helpline__push, |
28 | .show = nop_helpline__show, | ||
22 | }; | 29 | }; |
23 | 30 | ||
24 | struct ui_helpline *helpline_fns = &default_helpline_fns; | 31 | struct ui_helpline *helpline_fns = &default_helpline_fns; |
@@ -59,3 +66,8 @@ void ui_helpline__puts(const char *msg) | |||
59 | ui_helpline__pop(); | 66 | ui_helpline__pop(); |
60 | ui_helpline__push(msg); | 67 | ui_helpline__push(msg); |
61 | } | 68 | } |
69 | |||
70 | int ui_helpline__vshow(const char *fmt, va_list ap) | ||
71 | { | ||
72 | return helpline_fns->show(fmt, ap); | ||
73 | } | ||
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h index baa28a4d16b9..46181f4fc07e 100644 --- a/tools/perf/ui/helpline.h +++ b/tools/perf/ui/helpline.h | |||
@@ -9,6 +9,7 @@ | |||
9 | struct ui_helpline { | 9 | struct ui_helpline { |
10 | void (*pop)(void); | 10 | void (*pop)(void); |
11 | void (*push)(const char *msg); | 11 | void (*push)(const char *msg); |
12 | int (*show)(const char *fmt, va_list ap); | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | extern struct ui_helpline *helpline_fns; | 15 | extern struct ui_helpline *helpline_fns; |
@@ -20,28 +21,9 @@ void ui_helpline__push(const char *msg); | |||
20 | void ui_helpline__vpush(const char *fmt, va_list ap); | 21 | void ui_helpline__vpush(const char *fmt, va_list ap); |
21 | void ui_helpline__fpush(const char *fmt, ...); | 22 | void ui_helpline__fpush(const char *fmt, ...); |
22 | void ui_helpline__puts(const char *msg); | 23 | void ui_helpline__puts(const char *msg); |
24 | int ui_helpline__vshow(const char *fmt, va_list ap); | ||
23 | 25 | ||
24 | extern char ui_helpline__current[512]; | 26 | extern char ui_helpline__current[512]; |
25 | |||
26 | #ifdef NEWT_SUPPORT | ||
27 | extern char ui_helpline__last_msg[]; | 27 | extern char ui_helpline__last_msg[]; |
28 | int ui_helpline__show_help(const char *format, va_list ap); | ||
29 | #else | ||
30 | static inline int ui_helpline__show_help(const char *format __maybe_unused, | ||
31 | va_list ap __maybe_unused) | ||
32 | { | ||
33 | return 0; | ||
34 | } | ||
35 | #endif /* NEWT_SUPPORT */ | ||
36 | |||
37 | #ifdef GTK2_SUPPORT | ||
38 | int perf_gtk__show_helpline(const char *format, va_list ap); | ||
39 | #else | ||
40 | static inline int perf_gtk__show_helpline(const char *format __maybe_unused, | ||
41 | va_list ap __maybe_unused) | ||
42 | { | ||
43 | return 0; | ||
44 | } | ||
45 | #endif /* GTK2_SUPPORT */ | ||
46 | 28 | ||
47 | #endif /* _PERF_UI_HELPLINE_H_ */ | 29 | #endif /* _PERF_UI_HELPLINE_H_ */ |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index aa84130024d5..4bf91b09d62d 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -3,151 +3,160 @@ | |||
3 | #include "../util/hist.h" | 3 | #include "../util/hist.h" |
4 | #include "../util/util.h" | 4 | #include "../util/util.h" |
5 | #include "../util/sort.h" | 5 | #include "../util/sort.h" |
6 | 6 | #include "../util/evsel.h" | |
7 | 7 | ||
8 | /* hist period print (hpp) functions */ | 8 | /* hist period print (hpp) functions */ |
9 | static int hpp__header_overhead(struct perf_hpp *hpp) | ||
10 | { | ||
11 | return scnprintf(hpp->buf, hpp->size, "Overhead"); | ||
12 | } | ||
13 | |||
14 | static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) | ||
15 | { | ||
16 | return 8; | ||
17 | } | ||
18 | |||
19 | static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he) | ||
20 | { | ||
21 | struct hists *hists = he->hists; | ||
22 | double percent = 100.0 * he->stat.period / hists->stats.total_period; | ||
23 | |||
24 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | ||
25 | } | ||
26 | |||
27 | static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he) | ||
28 | { | ||
29 | struct hists *hists = he->hists; | ||
30 | double percent = 100.0 * he->stat.period / hists->stats.total_period; | ||
31 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; | ||
32 | |||
33 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
34 | } | ||
35 | |||
36 | static int hpp__header_overhead_sys(struct perf_hpp *hpp) | ||
37 | { | ||
38 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | ||
39 | |||
40 | return scnprintf(hpp->buf, hpp->size, fmt, "sys"); | ||
41 | } | ||
42 | |||
43 | static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused) | ||
44 | { | ||
45 | return 7; | ||
46 | } | ||
47 | |||
48 | static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) | ||
49 | { | ||
50 | struct hists *hists = he->hists; | ||
51 | double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; | ||
52 | |||
53 | return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | ||
54 | } | ||
55 | |||
56 | static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) | ||
57 | { | ||
58 | struct hists *hists = he->hists; | ||
59 | double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; | ||
60 | const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; | ||
61 | |||
62 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
63 | } | ||
64 | 9 | ||
65 | static int hpp__header_overhead_us(struct perf_hpp *hpp) | 10 | typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); |
66 | { | ||
67 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | ||
68 | |||
69 | return scnprintf(hpp->buf, hpp->size, fmt, "user"); | ||
70 | } | ||
71 | |||
72 | static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) | ||
73 | { | ||
74 | return 7; | ||
75 | } | ||
76 | 11 | ||
77 | static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) | 12 | static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
13 | u64 (*get_field)(struct hist_entry *), | ||
14 | const char *fmt, hpp_snprint_fn print_fn, | ||
15 | bool fmt_percent) | ||
78 | { | 16 | { |
17 | int ret; | ||
79 | struct hists *hists = he->hists; | 18 | struct hists *hists = he->hists; |
80 | double percent = 100.0 * he->stat.period_us / hists->stats.total_period; | 19 | struct perf_evsel *evsel = hists_to_evsel(hists); |
81 | 20 | ||
82 | return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | 21 | if (fmt_percent) { |
83 | } | 22 | double percent = 0.0; |
84 | 23 | ||
85 | static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) | 24 | if (hists->stats.total_period) |
86 | { | 25 | percent = 100.0 * get_field(he) / |
87 | struct hists *hists = he->hists; | 26 | hists->stats.total_period; |
88 | double percent = 100.0 * he->stat.period_us / hists->stats.total_period; | ||
89 | const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; | ||
90 | 27 | ||
91 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 28 | ret = print_fn(hpp->buf, hpp->size, fmt, percent); |
92 | } | 29 | } else |
30 | ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); | ||
93 | 31 | ||
94 | static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp) | 32 | if (perf_evsel__is_group_event(evsel)) { |
95 | { | 33 | int prev_idx, idx_delta; |
96 | return scnprintf(hpp->buf, hpp->size, "guest sys"); | 34 | struct hist_entry *pair; |
97 | } | 35 | int nr_members = evsel->nr_members; |
98 | 36 | ||
99 | static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) | 37 | prev_idx = perf_evsel__group_idx(evsel); |
100 | { | ||
101 | return 9; | ||
102 | } | ||
103 | 38 | ||
104 | static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, | 39 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { |
105 | struct hist_entry *he) | 40 | u64 period = get_field(pair); |
106 | { | 41 | u64 total = pair->hists->stats.total_period; |
107 | struct hists *hists = he->hists; | ||
108 | double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; | ||
109 | 42 | ||
110 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); | 43 | if (!total) |
111 | } | 44 | continue; |
112 | 45 | ||
113 | static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, | 46 | evsel = hists_to_evsel(pair->hists); |
114 | struct hist_entry *he) | 47 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; |
115 | { | ||
116 | struct hists *hists = he->hists; | ||
117 | double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; | ||
118 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; | ||
119 | 48 | ||
120 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 49 | while (idx_delta--) { |
121 | } | 50 | /* |
51 | * zero-fill group members in the middle which | ||
52 | * have no sample | ||
53 | */ | ||
54 | ret += print_fn(hpp->buf + ret, hpp->size - ret, | ||
55 | fmt, 0); | ||
56 | } | ||
122 | 57 | ||
123 | static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) | 58 | if (fmt_percent) |
124 | { | 59 | ret += print_fn(hpp->buf + ret, hpp->size - ret, |
125 | return scnprintf(hpp->buf, hpp->size, "guest usr"); | 60 | fmt, 100.0 * period / total); |
126 | } | 61 | else |
62 | ret += print_fn(hpp->buf + ret, hpp->size - ret, | ||
63 | fmt, period); | ||
127 | 64 | ||
128 | static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) | 65 | prev_idx = perf_evsel__group_idx(evsel); |
129 | { | 66 | } |
130 | return 9; | ||
131 | } | ||
132 | 67 | ||
133 | static int hpp__color_overhead_guest_us(struct perf_hpp *hpp, | 68 | idx_delta = nr_members - prev_idx - 1; |
134 | struct hist_entry *he) | ||
135 | { | ||
136 | struct hists *hists = he->hists; | ||
137 | double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; | ||
138 | 69 | ||
139 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); | 70 | while (idx_delta--) { |
71 | /* | ||
72 | * zero-fill group members at last which have no sample | ||
73 | */ | ||
74 | ret += print_fn(hpp->buf + ret, hpp->size - ret, | ||
75 | fmt, 0); | ||
76 | } | ||
77 | } | ||
78 | return ret; | ||
140 | } | 79 | } |
141 | 80 | ||
142 | static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp, | 81 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
143 | struct hist_entry *he) | 82 | static int hpp__header_##_type(struct perf_hpp *hpp) \ |
144 | { | 83 | { \ |
145 | struct hists *hists = he->hists; | 84 | int len = _min_width; \ |
146 | double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; | 85 | \ |
147 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; | 86 | if (symbol_conf.event_group) { \ |
87 | struct perf_evsel *evsel = hpp->ptr; \ | ||
88 | \ | ||
89 | len = max(len, evsel->nr_members * _unit_width); \ | ||
90 | } \ | ||
91 | return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ | ||
92 | } | ||
93 | |||
94 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
95 | static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \ | ||
96 | { \ | ||
97 | int len = _min_width; \ | ||
98 | \ | ||
99 | if (symbol_conf.event_group) { \ | ||
100 | struct perf_evsel *evsel = hpp->ptr; \ | ||
101 | \ | ||
102 | len = max(len, evsel->nr_members * _unit_width); \ | ||
103 | } \ | ||
104 | return len; \ | ||
105 | } | ||
106 | |||
107 | #define __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
108 | static u64 he_get_##_field(struct hist_entry *he) \ | ||
109 | { \ | ||
110 | return he->stat._field; \ | ||
111 | } \ | ||
112 | \ | ||
113 | static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
114 | { \ | ||
115 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | ||
116 | (hpp_snprint_fn)percent_color_snprintf, true); \ | ||
117 | } | ||
118 | |||
119 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ | ||
120 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
121 | { \ | ||
122 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | ||
123 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ | ||
124 | scnprintf, true); \ | ||
125 | } | ||
126 | |||
127 | #define __HPP_ENTRY_RAW_FN(_type, _field) \ | ||
128 | static u64 he_get_raw_##_field(struct hist_entry *he) \ | ||
129 | { \ | ||
130 | return he->stat._field; \ | ||
131 | } \ | ||
132 | \ | ||
133 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
134 | { \ | ||
135 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | ||
136 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ | ||
137 | } | ||
138 | |||
139 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ | ||
140 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
141 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
142 | __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
143 | __HPP_ENTRY_PERCENT_FN(_type, _field) | ||
144 | |||
145 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ | ||
146 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
147 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
148 | __HPP_ENTRY_RAW_FN(_type, _field) | ||
149 | |||
150 | |||
151 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | ||
152 | HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) | ||
153 | HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) | ||
154 | HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) | ||
155 | HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | ||
156 | |||
157 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | ||
158 | HPP_RAW_FNS(period, "Period", period, 12, 12) | ||
148 | 159 | ||
149 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
150 | } | ||
151 | 160 | ||
152 | static int hpp__header_baseline(struct perf_hpp *hpp) | 161 | static int hpp__header_baseline(struct perf_hpp *hpp) |
153 | { | 162 | { |
@@ -179,7 +188,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
179 | { | 188 | { |
180 | double percent = baseline_percent(he); | 189 | double percent = baseline_percent(he); |
181 | 190 | ||
182 | if (hist_entry__has_pairs(he)) | 191 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) |
183 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | 192 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); |
184 | else | 193 | else |
185 | return scnprintf(hpp->buf, hpp->size, " "); | 194 | return scnprintf(hpp->buf, hpp->size, " "); |
@@ -196,44 +205,6 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
196 | return scnprintf(hpp->buf, hpp->size, " "); | 205 | return scnprintf(hpp->buf, hpp->size, " "); |
197 | } | 206 | } |
198 | 207 | ||
199 | static int hpp__header_samples(struct perf_hpp *hpp) | ||
200 | { | ||
201 | const char *fmt = symbol_conf.field_sep ? "%s" : "%11s"; | ||
202 | |||
203 | return scnprintf(hpp->buf, hpp->size, fmt, "Samples"); | ||
204 | } | ||
205 | |||
206 | static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused) | ||
207 | { | ||
208 | return 11; | ||
209 | } | ||
210 | |||
211 | static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he) | ||
212 | { | ||
213 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64; | ||
214 | |||
215 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events); | ||
216 | } | ||
217 | |||
218 | static int hpp__header_period(struct perf_hpp *hpp) | ||
219 | { | ||
220 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; | ||
221 | |||
222 | return scnprintf(hpp->buf, hpp->size, fmt, "Period"); | ||
223 | } | ||
224 | |||
225 | static int hpp__width_period(struct perf_hpp *hpp __maybe_unused) | ||
226 | { | ||
227 | return 12; | ||
228 | } | ||
229 | |||
230 | static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he) | ||
231 | { | ||
232 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; | ||
233 | |||
234 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); | ||
235 | } | ||
236 | |||
237 | static int hpp__header_period_baseline(struct perf_hpp *hpp) | 208 | static int hpp__header_period_baseline(struct perf_hpp *hpp) |
238 | { | 209 | { |
239 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; | 210 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; |
@@ -254,6 +225,7 @@ static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *h | |||
254 | 225 | ||
255 | return scnprintf(hpp->buf, hpp->size, fmt, period); | 226 | return scnprintf(hpp->buf, hpp->size, fmt, period); |
256 | } | 227 | } |
228 | |||
257 | static int hpp__header_delta(struct perf_hpp *hpp) | 229 | static int hpp__header_delta(struct perf_hpp *hpp) |
258 | { | 230 | { |
259 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | 231 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; |
@@ -268,14 +240,18 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) | |||
268 | 240 | ||
269 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) | 241 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) |
270 | { | 242 | { |
243 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
271 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; | 244 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; |
272 | char buf[32] = " "; | 245 | char buf[32] = " "; |
273 | double diff; | 246 | double diff = 0.0; |
274 | 247 | ||
275 | if (he->diff.computed) | 248 | if (pair) { |
276 | diff = he->diff.period_ratio_delta; | 249 | if (he->diff.computed) |
277 | else | 250 | diff = he->diff.period_ratio_delta; |
278 | diff = perf_diff__compute_delta(he); | 251 | else |
252 | diff = perf_diff__compute_delta(he, pair); | ||
253 | } else | ||
254 | diff = perf_diff__period_percent(he, he->stat.period); | ||
279 | 255 | ||
280 | if (fabs(diff) >= 0.01) | 256 | if (fabs(diff) >= 0.01) |
281 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); | 257 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); |
@@ -297,14 +273,17 @@ static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) | |||
297 | 273 | ||
298 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) | 274 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) |
299 | { | 275 | { |
276 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
300 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | 277 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; |
301 | char buf[32] = " "; | 278 | char buf[32] = " "; |
302 | double ratio; | 279 | double ratio = 0.0; |
303 | 280 | ||
304 | if (he->diff.computed) | 281 | if (pair) { |
305 | ratio = he->diff.period_ratio; | 282 | if (he->diff.computed) |
306 | else | 283 | ratio = he->diff.period_ratio; |
307 | ratio = perf_diff__compute_ratio(he); | 284 | else |
285 | ratio = perf_diff__compute_ratio(he, pair); | ||
286 | } | ||
308 | 287 | ||
309 | if (ratio > 0.0) | 288 | if (ratio > 0.0) |
310 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); | 289 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); |
@@ -326,14 +305,17 @@ static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) | |||
326 | 305 | ||
327 | static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) | 306 | static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) |
328 | { | 307 | { |
308 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
329 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | 309 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; |
330 | char buf[32] = " "; | 310 | char buf[32] = " "; |
331 | s64 wdiff; | 311 | s64 wdiff = 0; |
332 | 312 | ||
333 | if (he->diff.computed) | 313 | if (pair) { |
334 | wdiff = he->diff.wdiff; | 314 | if (he->diff.computed) |
335 | else | 315 | wdiff = he->diff.wdiff; |
336 | wdiff = perf_diff__compute_wdiff(he); | 316 | else |
317 | wdiff = perf_diff__compute_wdiff(he, pair); | ||
318 | } | ||
337 | 319 | ||
338 | if (wdiff != 0) | 320 | if (wdiff != 0) |
339 | scnprintf(buf, sizeof(buf), "%14ld", wdiff); | 321 | scnprintf(buf, sizeof(buf), "%14ld", wdiff); |
@@ -341,30 +323,6 @@ static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) | |||
341 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 323 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
342 | } | 324 | } |
343 | 325 | ||
344 | static int hpp__header_displ(struct perf_hpp *hpp) | ||
345 | { | ||
346 | return scnprintf(hpp->buf, hpp->size, "Displ."); | ||
347 | } | ||
348 | |||
349 | static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused) | ||
350 | { | ||
351 | return 6; | ||
352 | } | ||
353 | |||
354 | static int hpp__entry_displ(struct perf_hpp *hpp, | ||
355 | struct hist_entry *he) | ||
356 | { | ||
357 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
358 | long displacement = pair ? pair->position - he->position : 0; | ||
359 | const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; | ||
360 | char buf[32] = " "; | ||
361 | |||
362 | if (displacement) | ||
363 | scnprintf(buf, sizeof(buf), "%+4ld", displacement); | ||
364 | |||
365 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
366 | } | ||
367 | |||
368 | static int hpp__header_formula(struct perf_hpp *hpp) | 326 | static int hpp__header_formula(struct perf_hpp *hpp) |
369 | { | 327 | { |
370 | const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; | 328 | const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; |
@@ -379,67 +337,91 @@ static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) | |||
379 | 337 | ||
380 | static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) | 338 | static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) |
381 | { | 339 | { |
340 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
382 | const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; | 341 | const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; |
383 | char buf[96] = " "; | 342 | char buf[96] = " "; |
384 | 343 | ||
385 | perf_diff__formula(buf, sizeof(buf), he); | 344 | if (pair) |
345 | perf_diff__formula(he, pair, buf, sizeof(buf)); | ||
346 | |||
386 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 347 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
387 | } | 348 | } |
388 | 349 | ||
389 | #define HPP__COLOR_PRINT_FNS(_name) \ | 350 | #define HPP__COLOR_PRINT_FNS(_name) \ |
390 | .header = hpp__header_ ## _name, \ | 351 | { \ |
391 | .width = hpp__width_ ## _name, \ | 352 | .header = hpp__header_ ## _name, \ |
392 | .color = hpp__color_ ## _name, \ | 353 | .width = hpp__width_ ## _name, \ |
393 | .entry = hpp__entry_ ## _name | 354 | .color = hpp__color_ ## _name, \ |
355 | .entry = hpp__entry_ ## _name \ | ||
356 | } | ||
394 | 357 | ||
395 | #define HPP__PRINT_FNS(_name) \ | 358 | #define HPP__PRINT_FNS(_name) \ |
396 | .header = hpp__header_ ## _name, \ | 359 | { \ |
397 | .width = hpp__width_ ## _name, \ | 360 | .header = hpp__header_ ## _name, \ |
398 | .entry = hpp__entry_ ## _name | 361 | .width = hpp__width_ ## _name, \ |
362 | .entry = hpp__entry_ ## _name \ | ||
363 | } | ||
399 | 364 | ||
400 | struct perf_hpp_fmt perf_hpp__format[] = { | 365 | struct perf_hpp_fmt perf_hpp__format[] = { |
401 | { .cond = false, HPP__COLOR_PRINT_FNS(baseline) }, | 366 | HPP__COLOR_PRINT_FNS(baseline), |
402 | { .cond = true, HPP__COLOR_PRINT_FNS(overhead) }, | 367 | HPP__COLOR_PRINT_FNS(overhead), |
403 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) }, | 368 | HPP__COLOR_PRINT_FNS(overhead_sys), |
404 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) }, | 369 | HPP__COLOR_PRINT_FNS(overhead_us), |
405 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_sys) }, | 370 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), |
406 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) }, | 371 | HPP__COLOR_PRINT_FNS(overhead_guest_us), |
407 | { .cond = false, HPP__PRINT_FNS(samples) }, | 372 | HPP__PRINT_FNS(samples), |
408 | { .cond = false, HPP__PRINT_FNS(period) }, | 373 | HPP__PRINT_FNS(period), |
409 | { .cond = false, HPP__PRINT_FNS(period_baseline) }, | 374 | HPP__PRINT_FNS(period_baseline), |
410 | { .cond = false, HPP__PRINT_FNS(delta) }, | 375 | HPP__PRINT_FNS(delta), |
411 | { .cond = false, HPP__PRINT_FNS(ratio) }, | 376 | HPP__PRINT_FNS(ratio), |
412 | { .cond = false, HPP__PRINT_FNS(wdiff) }, | 377 | HPP__PRINT_FNS(wdiff), |
413 | { .cond = false, HPP__PRINT_FNS(displ) }, | 378 | HPP__PRINT_FNS(formula) |
414 | { .cond = false, HPP__PRINT_FNS(formula) } | ||
415 | }; | 379 | }; |
416 | 380 | ||
381 | LIST_HEAD(perf_hpp__list); | ||
382 | |||
383 | |||
417 | #undef HPP__COLOR_PRINT_FNS | 384 | #undef HPP__COLOR_PRINT_FNS |
418 | #undef HPP__PRINT_FNS | 385 | #undef HPP__PRINT_FNS |
419 | 386 | ||
387 | #undef HPP_PERCENT_FNS | ||
388 | #undef HPP_RAW_FNS | ||
389 | |||
390 | #undef __HPP_HEADER_FN | ||
391 | #undef __HPP_WIDTH_FN | ||
392 | #undef __HPP_COLOR_PERCENT_FN | ||
393 | #undef __HPP_ENTRY_PERCENT_FN | ||
394 | #undef __HPP_ENTRY_RAW_FN | ||
395 | |||
396 | |||
420 | void perf_hpp__init(void) | 397 | void perf_hpp__init(void) |
421 | { | 398 | { |
422 | if (symbol_conf.show_cpu_utilization) { | 399 | if (symbol_conf.show_cpu_utilization) { |
423 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true; | 400 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); |
424 | perf_hpp__format[PERF_HPP__OVERHEAD_US].cond = true; | 401 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); |
425 | 402 | ||
426 | if (perf_guest) { | 403 | if (perf_guest) { |
427 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].cond = true; | 404 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS); |
428 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].cond = true; | 405 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US); |
429 | } | 406 | } |
430 | } | 407 | } |
431 | 408 | ||
432 | if (symbol_conf.show_nr_samples) | 409 | if (symbol_conf.show_nr_samples) |
433 | perf_hpp__format[PERF_HPP__SAMPLES].cond = true; | 410 | perf_hpp__column_enable(PERF_HPP__SAMPLES); |
434 | 411 | ||
435 | if (symbol_conf.show_total_period) | 412 | if (symbol_conf.show_total_period) |
436 | perf_hpp__format[PERF_HPP__PERIOD].cond = true; | 413 | perf_hpp__column_enable(PERF_HPP__PERIOD); |
414 | } | ||
415 | |||
416 | void perf_hpp__column_register(struct perf_hpp_fmt *format) | ||
417 | { | ||
418 | list_add_tail(&format->list, &perf_hpp__list); | ||
437 | } | 419 | } |
438 | 420 | ||
439 | void perf_hpp__column_enable(unsigned col, bool enable) | 421 | void perf_hpp__column_enable(unsigned col) |
440 | { | 422 | { |
441 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 423 | BUG_ON(col >= PERF_HPP__MAX_INDEX); |
442 | perf_hpp__format[col].cond = enable; | 424 | perf_hpp__column_register(&perf_hpp__format[col]); |
443 | } | 425 | } |
444 | 426 | ||
445 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 427 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
@@ -452,27 +434,29 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | |||
452 | bool color) | 434 | bool color) |
453 | { | 435 | { |
454 | const char *sep = symbol_conf.field_sep; | 436 | const char *sep = symbol_conf.field_sep; |
437 | struct perf_hpp_fmt *fmt; | ||
455 | char *start = hpp->buf; | 438 | char *start = hpp->buf; |
456 | int i, ret; | 439 | int ret; |
457 | bool first = true; | 440 | bool first = true; |
458 | 441 | ||
459 | if (symbol_conf.exclude_other && !he->parent) | 442 | if (symbol_conf.exclude_other && !he->parent) |
460 | return 0; | 443 | return 0; |
461 | 444 | ||
462 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 445 | perf_hpp__for_each_format(fmt) { |
463 | if (!perf_hpp__format[i].cond) | 446 | /* |
464 | continue; | 447 | * If there's no field_sep, we still need |
465 | 448 | * to display initial ' '. | |
449 | */ | ||
466 | if (!sep || !first) { | 450 | if (!sep || !first) { |
467 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); | 451 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); |
468 | advance_hpp(hpp, ret); | 452 | advance_hpp(hpp, ret); |
453 | } else | ||
469 | first = false; | 454 | first = false; |
470 | } | ||
471 | 455 | ||
472 | if (color && perf_hpp__format[i].color) | 456 | if (color && fmt->color) |
473 | ret = perf_hpp__format[i].color(hpp, he); | 457 | ret = fmt->color(hpp, he); |
474 | else | 458 | else |
475 | ret = perf_hpp__format[i].entry(hpp, he); | 459 | ret = fmt->entry(hpp, he); |
476 | 460 | ||
477 | advance_hpp(hpp, ret); | 461 | advance_hpp(hpp, ret); |
478 | } | 462 | } |
@@ -504,16 +488,18 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, | |||
504 | */ | 488 | */ |
505 | unsigned int hists__sort_list_width(struct hists *hists) | 489 | unsigned int hists__sort_list_width(struct hists *hists) |
506 | { | 490 | { |
491 | struct perf_hpp_fmt *fmt; | ||
507 | struct sort_entry *se; | 492 | struct sort_entry *se; |
508 | int i, ret = 0; | 493 | int i = 0, ret = 0; |
494 | struct perf_hpp dummy_hpp = { | ||
495 | .ptr = hists_to_evsel(hists), | ||
496 | }; | ||
509 | 497 | ||
510 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 498 | perf_hpp__for_each_format(fmt) { |
511 | if (!perf_hpp__format[i].cond) | ||
512 | continue; | ||
513 | if (i) | 499 | if (i) |
514 | ret += 2; | 500 | ret += 2; |
515 | 501 | ||
516 | ret += perf_hpp__format[i].width(NULL); | 502 | ret += fmt->width(&dummy_hpp); |
517 | } | 503 | } |
518 | 504 | ||
519 | list_for_each_entry(se, &hist_entry__sort_list, list) | 505 | list_for_each_entry(se, &hist_entry__sort_list, list) |
diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h index 809eca5707fa..65092d576b4e 100644 --- a/tools/perf/ui/keysyms.h +++ b/tools/perf/ui/keysyms.h | |||
@@ -23,5 +23,6 @@ | |||
23 | #define K_TIMER -1 | 23 | #define K_TIMER -1 |
24 | #define K_ERROR -2 | 24 | #define K_ERROR -2 |
25 | #define K_RESIZE -3 | 25 | #define K_RESIZE -3 |
26 | #define K_SWITCH_INPUT_DATA -4 | ||
26 | 27 | ||
27 | #endif /* _PERF_KEYSYMS_H_ */ | 28 | #endif /* _PERF_KEYSYMS_H_ */ |
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index ebb4cc107876..ae6a789cb0f6 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c | |||
@@ -8,7 +8,7 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | |||
8 | 8 | ||
9 | void setup_browser(bool fallback_to_pager) | 9 | void setup_browser(bool fallback_to_pager) |
10 | { | 10 | { |
11 | if (!isatty(1) || dump_trace) | 11 | if (use_browser < 2 && (!isatty(1) || dump_trace)) |
12 | use_browser = 0; | 12 | use_browser = 0; |
13 | 13 | ||
14 | /* default to TUI */ | 14 | /* default to TUI */ |
@@ -30,6 +30,7 @@ void setup_browser(bool fallback_to_pager) | |||
30 | if (fallback_to_pager) | 30 | if (fallback_to_pager) |
31 | setup_pager(); | 31 | setup_pager(); |
32 | 32 | ||
33 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
33 | perf_hpp__init(); | 34 | perf_hpp__init(); |
34 | break; | 35 | break; |
35 | } | 36 | } |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index f0ee204f99bb..ae7a75432249 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "../../util/util.h" | 3 | #include "../../util/util.h" |
4 | #include "../../util/hist.h" | 4 | #include "../../util/hist.h" |
5 | #include "../../util/sort.h" | 5 | #include "../../util/sort.h" |
6 | #include "../../util/evsel.h" | ||
6 | 7 | ||
7 | 8 | ||
8 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 9 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
@@ -333,19 +334,21 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, | |||
333 | } | 334 | } |
334 | 335 | ||
335 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | 336 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
336 | int max_cols, FILE *fp) | 337 | int max_cols, float min_pcnt, FILE *fp) |
337 | { | 338 | { |
339 | struct perf_hpp_fmt *fmt; | ||
338 | struct sort_entry *se; | 340 | struct sort_entry *se; |
339 | struct rb_node *nd; | 341 | struct rb_node *nd; |
340 | size_t ret = 0; | 342 | size_t ret = 0; |
341 | unsigned int width; | 343 | unsigned int width; |
342 | const char *sep = symbol_conf.field_sep; | 344 | const char *sep = symbol_conf.field_sep; |
343 | const char *col_width = symbol_conf.col_width_list_str; | 345 | const char *col_width = symbol_conf.col_width_list_str; |
344 | int idx, nr_rows = 0; | 346 | int nr_rows = 0; |
345 | char bf[96]; | 347 | char bf[96]; |
346 | struct perf_hpp dummy_hpp = { | 348 | struct perf_hpp dummy_hpp = { |
347 | .buf = bf, | 349 | .buf = bf, |
348 | .size = sizeof(bf), | 350 | .size = sizeof(bf), |
351 | .ptr = hists_to_evsel(hists), | ||
349 | }; | 352 | }; |
350 | bool first = true; | 353 | bool first = true; |
351 | 354 | ||
@@ -355,16 +358,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
355 | goto print_entries; | 358 | goto print_entries; |
356 | 359 | ||
357 | fprintf(fp, "# "); | 360 | fprintf(fp, "# "); |
358 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
359 | if (!perf_hpp__format[idx].cond) | ||
360 | continue; | ||
361 | 361 | ||
362 | perf_hpp__for_each_format(fmt) { | ||
362 | if (!first) | 363 | if (!first) |
363 | fprintf(fp, "%s", sep ?: " "); | 364 | fprintf(fp, "%s", sep ?: " "); |
364 | else | 365 | else |
365 | first = false; | 366 | first = false; |
366 | 367 | ||
367 | perf_hpp__format[idx].header(&dummy_hpp); | 368 | fmt->header(&dummy_hpp); |
368 | fprintf(fp, "%s", bf); | 369 | fprintf(fp, "%s", bf); |
369 | } | 370 | } |
370 | 371 | ||
@@ -400,18 +401,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
400 | first = true; | 401 | first = true; |
401 | 402 | ||
402 | fprintf(fp, "# "); | 403 | fprintf(fp, "# "); |
403 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
404 | unsigned int i; | ||
405 | 404 | ||
406 | if (!perf_hpp__format[idx].cond) | 405 | perf_hpp__for_each_format(fmt) { |
407 | continue; | 406 | unsigned int i; |
408 | 407 | ||
409 | if (!first) | 408 | if (!first) |
410 | fprintf(fp, "%s", sep ?: " "); | 409 | fprintf(fp, "%s", sep ?: " "); |
411 | else | 410 | else |
412 | first = false; | 411 | first = false; |
413 | 412 | ||
414 | width = perf_hpp__format[idx].width(&dummy_hpp); | 413 | width = fmt->width(&dummy_hpp); |
415 | for (i = 0; i < width; i++) | 414 | for (i = 0; i < width; i++) |
416 | fprintf(fp, "."); | 415 | fprintf(fp, "."); |
417 | } | 416 | } |
@@ -441,10 +440,15 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
441 | print_entries: | 440 | print_entries: |
442 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 441 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
443 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 442 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
443 | float percent = h->stat.period * 100.0 / | ||
444 | hists->stats.total_period; | ||
444 | 445 | ||
445 | if (h->filtered) | 446 | if (h->filtered) |
446 | continue; | 447 | continue; |
447 | 448 | ||
449 | if (percent < min_pcnt) | ||
450 | continue; | ||
451 | |||
448 | ret += hist_entry__fprintf(h, max_cols, hists, fp); | 452 | ret += hist_entry__fprintf(h, max_cols, hists, fp); |
449 | 453 | ||
450 | if (max_rows && ++nr_rows >= max_rows) | 454 | if (max_rows && ++nr_rows >= max_rows) |
@@ -462,7 +466,7 @@ out: | |||
462 | return ret; | 466 | return ret; |
463 | } | 467 | } |
464 | 468 | ||
465 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | 469 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp) |
466 | { | 470 | { |
467 | int i; | 471 | int i; |
468 | size_t ret = 0; | 472 | size_t ret = 0; |
@@ -470,7 +474,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | |||
470 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 474 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
471 | const char *name; | 475 | const char *name; |
472 | 476 | ||
473 | if (hists->stats.nr_events[i] == 0) | 477 | if (stats->nr_events[i] == 0) |
474 | continue; | 478 | continue; |
475 | 479 | ||
476 | name = perf_event__name(i); | 480 | name = perf_event__name(i); |
@@ -478,7 +482,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | |||
478 | continue; | 482 | continue; |
479 | 483 | ||
480 | ret += fprintf(fp, "%16s events: %10d\n", name, | 484 | ret += fprintf(fp, "%16s events: %10d\n", name, |
481 | hists->stats.nr_events[i]); | 485 | stats->nr_events[i]); |
482 | } | 486 | } |
483 | 487 | ||
484 | return ret; | 488 | return ret; |
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c index 2884d2f41e33..1c8b9afd5d6e 100644 --- a/tools/perf/ui/tui/helpline.c +++ b/tools/perf/ui/tui/helpline.c | |||
@@ -8,6 +8,8 @@ | |||
8 | #include "../ui.h" | 8 | #include "../ui.h" |
9 | #include "../libslang.h" | 9 | #include "../libslang.h" |
10 | 10 | ||
11 | char ui_helpline__last_msg[1024]; | ||
12 | |||
11 | static void tui_helpline__pop(void) | 13 | static void tui_helpline__pop(void) |
12 | { | 14 | { |
13 | } | 15 | } |
@@ -23,20 +25,7 @@ static void tui_helpline__push(const char *msg) | |||
23 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; | 25 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; |
24 | } | 26 | } |
25 | 27 | ||
26 | struct ui_helpline tui_helpline_fns = { | 28 | static int tui_helpline__show(const char *format, va_list ap) |
27 | .pop = tui_helpline__pop, | ||
28 | .push = tui_helpline__push, | ||
29 | }; | ||
30 | |||
31 | void ui_helpline__init(void) | ||
32 | { | ||
33 | helpline_fns = &tui_helpline_fns; | ||
34 | ui_helpline__puts(" "); | ||
35 | } | ||
36 | |||
37 | char ui_helpline__last_msg[1024]; | ||
38 | |||
39 | int ui_helpline__show_help(const char *format, va_list ap) | ||
40 | { | 29 | { |
41 | int ret; | 30 | int ret; |
42 | static int backlog; | 31 | static int backlog; |
@@ -55,3 +44,15 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
55 | 44 | ||
56 | return ret; | 45 | return ret; |
57 | } | 46 | } |
47 | |||
48 | struct ui_helpline tui_helpline_fns = { | ||
49 | .pop = tui_helpline__pop, | ||
50 | .push = tui_helpline__push, | ||
51 | .show = tui_helpline__show, | ||
52 | }; | ||
53 | |||
54 | void ui_helpline__init(void) | ||
55 | { | ||
56 | helpline_fns = &tui_helpline_fns; | ||
57 | ui_helpline__puts(" "); | ||
58 | } | ||
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 81efa192e86c..b9401482d110 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #include <newt.h> | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | 3 | ||
@@ -88,13 +87,6 @@ int ui__getch(int delay_secs) | |||
88 | return SLkp_getkey(); | 87 | return SLkp_getkey(); |
89 | } | 88 | } |
90 | 89 | ||
91 | static void newt_suspend(void *d __maybe_unused) | ||
92 | { | ||
93 | newtSuspend(); | ||
94 | raise(SIGTSTP); | ||
95 | newtResume(); | ||
96 | } | ||
97 | |||
98 | static void ui__signal(int sig) | 90 | static void ui__signal(int sig) |
99 | { | 91 | { |
100 | ui__exit(false); | 92 | ui__exit(false); |
@@ -106,7 +98,17 @@ int ui__init(void) | |||
106 | { | 98 | { |
107 | int err; | 99 | int err; |
108 | 100 | ||
109 | newtInit(); | 101 | SLutf8_enable(-1); |
102 | SLtt_get_terminfo(); | ||
103 | SLtt_get_screen_size(); | ||
104 | |||
105 | err = SLsmg_init_smg(); | ||
106 | if (err < 0) | ||
107 | goto out; | ||
108 | err = SLang_init_tty(0, 0, 0); | ||
109 | if (err < 0) | ||
110 | goto out; | ||
111 | |||
110 | err = SLkp_init(); | 112 | err = SLkp_init(); |
111 | if (err < 0) { | 113 | if (err < 0) { |
112 | pr_err("TUI initialization failed.\n"); | 114 | pr_err("TUI initialization failed.\n"); |
@@ -115,7 +117,6 @@ int ui__init(void) | |||
115 | 117 | ||
116 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); | 118 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); |
117 | 119 | ||
118 | newtSetSuspendCallback(newt_suspend, NULL); | ||
119 | ui_helpline__init(); | 120 | ui_helpline__init(); |
120 | ui_browser__init(); | 121 | ui_browser__init(); |
121 | ui_progress__init(); | 122 | ui_progress__init(); |
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index d86359c99907..70cb0d4eb8aa 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h | |||
@@ -12,7 +12,7 @@ extern int use_browser; | |||
12 | void setup_browser(bool fallback_to_pager); | 12 | void setup_browser(bool fallback_to_pager); |
13 | void exit_browser(bool wait_for_ok); | 13 | void exit_browser(bool wait_for_ok); |
14 | 14 | ||
15 | #ifdef NEWT_SUPPORT | 15 | #ifdef SLANG_SUPPORT |
16 | int ui__init(void); | 16 | int ui__init(void); |
17 | void ui__exit(bool wait_for_ok); | 17 | void ui__exit(bool wait_for_ok); |
18 | #else | 18 | #else |
diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c index 4f989774c8c6..e3e0a963d03a 100644 --- a/tools/perf/ui/util.c +++ b/tools/perf/ui/util.c | |||
@@ -52,7 +52,6 @@ int ui__warning(const char *format, ...) | |||
52 | return ret; | 52 | return ret; |
53 | } | 53 | } |
54 | 54 | ||
55 | |||
56 | /** | 55 | /** |
57 | * perf_error__register - Register error logging functions | 56 | * perf_error__register - Register error logging functions |
58 | * @eops: The pointer to error logging function struct | 57 | * @eops: The pointer to error logging function struct |
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 6aa34e5afdcf..055fef34b6f6 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -26,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)') | |||
26 | 26 | ||
27 | if test -r $GVF | 27 | if test -r $GVF |
28 | then | 28 | then |
29 | VC=$(sed -e 's/^PERF_VERSION = //' <$GVF) | 29 | VC=$(sed -e 's/^#define PERF_VERSION "\(.*\)"/\1/' <$GVF) |
30 | else | 30 | else |
31 | VC=unset | 31 | VC=unset |
32 | fi | 32 | fi |
33 | test "$VN" = "$VC" || { | 33 | test "$VN" = "$VC" || { |
34 | echo >&2 "PERF_VERSION = $VN" | 34 | echo >&2 "PERF_VERSION = $VN" |
35 | echo "PERF_VERSION = $VN" >$GVF | 35 | echo "#define PERF_VERSION \"$VN\"" >$GVF |
36 | } | 36 | } |
37 | 37 | ||
38 | 38 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 07aaeea60000..d102716c43a1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "symbol.h" | 14 | #include "symbol.h" |
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include "evsel.h" | ||
17 | #include <pthread.h> | 18 | #include <pthread.h> |
18 | #include <linux/bitops.h> | 19 | #include <linux/bitops.h> |
19 | 20 | ||
@@ -602,8 +603,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa | |||
602 | return NULL; | 603 | return NULL; |
603 | } | 604 | } |
604 | 605 | ||
606 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | ||
607 | s64 end, const char **path) | ||
608 | { | ||
609 | struct source_line *src_line = notes->src->lines; | ||
610 | double percent = 0.0; | ||
611 | |||
612 | if (src_line) { | ||
613 | size_t sizeof_src_line = sizeof(*src_line) + | ||
614 | sizeof(src_line->p) * (src_line->nr_pcnt - 1); | ||
615 | |||
616 | while (offset < end) { | ||
617 | src_line = (void *)notes->src->lines + | ||
618 | (sizeof_src_line * offset); | ||
619 | |||
620 | if (*path == NULL) | ||
621 | *path = src_line->path; | ||
622 | |||
623 | percent += src_line->p[evidx].percent; | ||
624 | offset++; | ||
625 | } | ||
626 | } else { | ||
627 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
628 | unsigned int hits = 0; | ||
629 | |||
630 | while (offset < end) | ||
631 | hits += h->addr[offset++]; | ||
632 | |||
633 | if (h->sum) | ||
634 | percent = 100.0 * hits / h->sum; | ||
635 | } | ||
636 | |||
637 | return percent; | ||
638 | } | ||
639 | |||
605 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, | 640 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, |
606 | int evidx, u64 len, int min_pcnt, int printed, | 641 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
607 | int max_lines, struct disasm_line *queue) | 642 | int max_lines, struct disasm_line *queue) |
608 | { | 643 | { |
609 | static const char *prev_line; | 644 | static const char *prev_line; |
@@ -611,34 +646,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
611 | 646 | ||
612 | if (dl->offset != -1) { | 647 | if (dl->offset != -1) { |
613 | const char *path = NULL; | 648 | const char *path = NULL; |
614 | unsigned int hits = 0; | 649 | double percent, max_percent = 0.0; |
615 | double percent = 0.0; | 650 | double *ppercents = &percent; |
651 | int i, nr_percent = 1; | ||
616 | const char *color; | 652 | const char *color; |
617 | struct annotation *notes = symbol__annotation(sym); | 653 | struct annotation *notes = symbol__annotation(sym); |
618 | struct source_line *src_line = notes->src->lines; | ||
619 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
620 | s64 offset = dl->offset; | 654 | s64 offset = dl->offset; |
621 | const u64 addr = start + offset; | 655 | const u64 addr = start + offset; |
622 | struct disasm_line *next; | 656 | struct disasm_line *next; |
623 | 657 | ||
624 | next = disasm__get_next_ip_line(¬es->src->source, dl); | 658 | next = disasm__get_next_ip_line(¬es->src->source, dl); |
625 | 659 | ||
626 | while (offset < (s64)len && | 660 | if (perf_evsel__is_group_event(evsel)) { |
627 | (next == NULL || offset < next->offset)) { | 661 | nr_percent = evsel->nr_members; |
628 | if (src_line) { | 662 | ppercents = calloc(nr_percent, sizeof(double)); |
629 | if (path == NULL) | 663 | if (ppercents == NULL) |
630 | path = src_line[offset].path; | 664 | return -1; |
631 | percent += src_line[offset].percent; | ||
632 | } else | ||
633 | hits += h->addr[offset]; | ||
634 | |||
635 | ++offset; | ||
636 | } | 665 | } |
637 | 666 | ||
638 | if (src_line == NULL && h->sum) | 667 | for (i = 0; i < nr_percent; i++) { |
639 | percent = 100.0 * hits / h->sum; | 668 | percent = disasm__calc_percent(notes, |
669 | notes->src->lines ? i : evsel->idx + i, | ||
670 | offset, | ||
671 | next ? next->offset : (s64) len, | ||
672 | &path); | ||
673 | |||
674 | ppercents[i] = percent; | ||
675 | if (percent > max_percent) | ||
676 | max_percent = percent; | ||
677 | } | ||
640 | 678 | ||
641 | if (percent < min_pcnt) | 679 | if (max_percent < min_pcnt) |
642 | return -1; | 680 | return -1; |
643 | 681 | ||
644 | if (max_lines && printed >= max_lines) | 682 | if (max_lines && printed >= max_lines) |
@@ -648,12 +686,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
648 | list_for_each_entry_from(queue, ¬es->src->source, node) { | 686 | list_for_each_entry_from(queue, ¬es->src->source, node) { |
649 | if (queue == dl) | 687 | if (queue == dl) |
650 | break; | 688 | break; |
651 | disasm_line__print(queue, sym, start, evidx, len, | 689 | disasm_line__print(queue, sym, start, evsel, len, |
652 | 0, 0, 1, NULL); | 690 | 0, 0, 1, NULL); |
653 | } | 691 | } |
654 | } | 692 | } |
655 | 693 | ||
656 | color = get_percent_color(percent); | 694 | color = get_percent_color(max_percent); |
657 | 695 | ||
658 | /* | 696 | /* |
659 | * Also color the filename and line if needed, with | 697 | * Also color the filename and line if needed, with |
@@ -669,25 +707,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
669 | } | 707 | } |
670 | } | 708 | } |
671 | 709 | ||
672 | color_fprintf(stdout, color, " %7.2f", percent); | 710 | for (i = 0; i < nr_percent; i++) { |
711 | percent = ppercents[i]; | ||
712 | color = get_percent_color(percent); | ||
713 | color_fprintf(stdout, color, " %7.2f", percent); | ||
714 | } | ||
715 | |||
673 | printf(" : "); | 716 | printf(" : "); |
674 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); | 717 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); |
675 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); | 718 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); |
719 | |||
720 | if (ppercents != &percent) | ||
721 | free(ppercents); | ||
722 | |||
676 | } else if (max_lines && printed >= max_lines) | 723 | } else if (max_lines && printed >= max_lines) |
677 | return 1; | 724 | return 1; |
678 | else { | 725 | else { |
726 | int width = 8; | ||
727 | |||
679 | if (queue) | 728 | if (queue) |
680 | return -1; | 729 | return -1; |
681 | 730 | ||
731 | if (perf_evsel__is_group_event(evsel)) | ||
732 | width *= evsel->nr_members; | ||
733 | |||
682 | if (!*dl->line) | 734 | if (!*dl->line) |
683 | printf(" :\n"); | 735 | printf(" %*s:\n", width, " "); |
684 | else | 736 | else |
685 | printf(" : %s\n", dl->line); | 737 | printf(" %*s: %s\n", width, " ", dl->line); |
686 | } | 738 | } |
687 | 739 | ||
688 | return 0; | 740 | return 0; |
689 | } | 741 | } |
690 | 742 | ||
743 | /* | ||
744 | * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) | ||
745 | * which looks like following | ||
746 | * | ||
747 | * 0000000000415500 <_init>: | ||
748 | * 415500: sub $0x8,%rsp | ||
749 | * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> | ||
750 | * 41550b: test %rax,%rax | ||
751 | * 41550e: je 415515 <_init+0x15> | ||
752 | * 415510: callq 416e70 <__gmon_start__@plt> | ||
753 | * 415515: add $0x8,%rsp | ||
754 | * 415519: retq | ||
755 | * | ||
756 | * it will be parsed and saved into struct disasm_line as | ||
757 | * <offset> <name> <ops.raw> | ||
758 | * | ||
759 | * The offset will be a relative offset from the start of the symbol and -1 | ||
760 | * means that it's not a disassembly line so should be treated differently. | ||
761 | * The ops.raw part will be parsed further according to type of the instruction. | ||
762 | */ | ||
691 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 763 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
692 | FILE *file, size_t privsize) | 764 | FILE *file, size_t privsize) |
693 | { | 765 | { |
@@ -809,7 +881,7 @@ fallback: | |||
809 | pr_err("Can't annotate %s:\n\n" | 881 | pr_err("Can't annotate %s:\n\n" |
810 | "No vmlinux file%s\nwas found in the path.\n\n" | 882 | "No vmlinux file%s\nwas found in the path.\n\n" |
811 | "Please use:\n\n" | 883 | "Please use:\n\n" |
812 | " perf buildid-cache -av vmlinux\n\n" | 884 | " perf buildid-cache -vu vmlinux\n\n" |
813 | "or:\n\n" | 885 | "or:\n\n" |
814 | " --vmlinux vmlinux\n", | 886 | " --vmlinux vmlinux\n", |
815 | sym->name, build_id_msg ?: ""); | 887 | sym->name, build_id_msg ?: ""); |
@@ -858,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
858 | struct source_line *iter; | 930 | struct source_line *iter; |
859 | struct rb_node **p = &root->rb_node; | 931 | struct rb_node **p = &root->rb_node; |
860 | struct rb_node *parent = NULL; | 932 | struct rb_node *parent = NULL; |
861 | int ret; | 933 | int i, ret; |
862 | 934 | ||
863 | while (*p != NULL) { | 935 | while (*p != NULL) { |
864 | parent = *p; | 936 | parent = *p; |
@@ -866,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
866 | 938 | ||
867 | ret = strcmp(iter->path, src_line->path); | 939 | ret = strcmp(iter->path, src_line->path); |
868 | if (ret == 0) { | 940 | if (ret == 0) { |
869 | iter->percent_sum += src_line->percent; | 941 | for (i = 0; i < src_line->nr_pcnt; i++) |
942 | iter->p[i].percent_sum += src_line->p[i].percent; | ||
870 | return; | 943 | return; |
871 | } | 944 | } |
872 | 945 | ||
@@ -876,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
876 | p = &(*p)->rb_right; | 949 | p = &(*p)->rb_right; |
877 | } | 950 | } |
878 | 951 | ||
879 | src_line->percent_sum = src_line->percent; | 952 | for (i = 0; i < src_line->nr_pcnt; i++) |
953 | src_line->p[i].percent_sum = src_line->p[i].percent; | ||
880 | 954 | ||
881 | rb_link_node(&src_line->node, parent, p); | 955 | rb_link_node(&src_line->node, parent, p); |
882 | rb_insert_color(&src_line->node, root); | 956 | rb_insert_color(&src_line->node, root); |
883 | } | 957 | } |
884 | 958 | ||
959 | static int cmp_source_line(struct source_line *a, struct source_line *b) | ||
960 | { | ||
961 | int i; | ||
962 | |||
963 | for (i = 0; i < a->nr_pcnt; i++) { | ||
964 | if (a->p[i].percent_sum == b->p[i].percent_sum) | ||
965 | continue; | ||
966 | return a->p[i].percent_sum > b->p[i].percent_sum; | ||
967 | } | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
885 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) | 972 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) |
886 | { | 973 | { |
887 | struct source_line *iter; | 974 | struct source_line *iter; |
@@ -892,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l | |||
892 | parent = *p; | 979 | parent = *p; |
893 | iter = rb_entry(parent, struct source_line, node); | 980 | iter = rb_entry(parent, struct source_line, node); |
894 | 981 | ||
895 | if (src_line->percent_sum > iter->percent_sum) | 982 | if (cmp_source_line(src_line, iter)) |
896 | p = &(*p)->rb_left; | 983 | p = &(*p)->rb_left; |
897 | else | 984 | else |
898 | p = &(*p)->rb_right; | 985 | p = &(*p)->rb_right; |
@@ -924,32 +1011,52 @@ static void symbol__free_source_line(struct symbol *sym, int len) | |||
924 | { | 1011 | { |
925 | struct annotation *notes = symbol__annotation(sym); | 1012 | struct annotation *notes = symbol__annotation(sym); |
926 | struct source_line *src_line = notes->src->lines; | 1013 | struct source_line *src_line = notes->src->lines; |
1014 | size_t sizeof_src_line; | ||
927 | int i; | 1015 | int i; |
928 | 1016 | ||
929 | for (i = 0; i < len; i++) | 1017 | sizeof_src_line = sizeof(*src_line) + |
930 | free(src_line[i].path); | 1018 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); |
1019 | |||
1020 | for (i = 0; i < len; i++) { | ||
1021 | free(src_line->path); | ||
1022 | src_line = (void *)src_line + sizeof_src_line; | ||
1023 | } | ||
931 | 1024 | ||
932 | free(src_line); | 1025 | free(notes->src->lines); |
933 | notes->src->lines = NULL; | 1026 | notes->src->lines = NULL; |
934 | } | 1027 | } |
935 | 1028 | ||
936 | /* Get the filename:line for the colored entries */ | 1029 | /* Get the filename:line for the colored entries */ |
937 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | 1030 | static int symbol__get_source_line(struct symbol *sym, struct map *map, |
938 | int evidx, struct rb_root *root, int len, | 1031 | struct perf_evsel *evsel, |
1032 | struct rb_root *root, int len, | ||
939 | const char *filename) | 1033 | const char *filename) |
940 | { | 1034 | { |
941 | u64 start; | 1035 | u64 start; |
942 | int i; | 1036 | int i, k; |
1037 | int evidx = evsel->idx; | ||
943 | char cmd[PATH_MAX * 2]; | 1038 | char cmd[PATH_MAX * 2]; |
944 | struct source_line *src_line; | 1039 | struct source_line *src_line; |
945 | struct annotation *notes = symbol__annotation(sym); | 1040 | struct annotation *notes = symbol__annotation(sym); |
946 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1041 | struct sym_hist *h = annotation__histogram(notes, evidx); |
947 | struct rb_root tmp_root = RB_ROOT; | 1042 | struct rb_root tmp_root = RB_ROOT; |
1043 | int nr_pcnt = 1; | ||
1044 | u64 h_sum = h->sum; | ||
1045 | size_t sizeof_src_line = sizeof(struct source_line); | ||
1046 | |||
1047 | if (perf_evsel__is_group_event(evsel)) { | ||
1048 | for (i = 1; i < evsel->nr_members; i++) { | ||
1049 | h = annotation__histogram(notes, evidx + i); | ||
1050 | h_sum += h->sum; | ||
1051 | } | ||
1052 | nr_pcnt = evsel->nr_members; | ||
1053 | sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); | ||
1054 | } | ||
948 | 1055 | ||
949 | if (!h->sum) | 1056 | if (!h_sum) |
950 | return 0; | 1057 | return 0; |
951 | 1058 | ||
952 | src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); | 1059 | src_line = notes->src->lines = calloc(len, sizeof_src_line); |
953 | if (!notes->src->lines) | 1060 | if (!notes->src->lines) |
954 | return -1; | 1061 | return -1; |
955 | 1062 | ||
@@ -960,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
960 | size_t line_len; | 1067 | size_t line_len; |
961 | u64 offset; | 1068 | u64 offset; |
962 | FILE *fp; | 1069 | FILE *fp; |
1070 | double percent_max = 0.0; | ||
963 | 1071 | ||
964 | src_line[i].percent = 100.0 * h->addr[i] / h->sum; | 1072 | src_line->nr_pcnt = nr_pcnt; |
965 | if (src_line[i].percent <= 0.5) | 1073 | |
966 | continue; | 1074 | for (k = 0; k < nr_pcnt; k++) { |
1075 | h = annotation__histogram(notes, evidx + k); | ||
1076 | src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; | ||
1077 | |||
1078 | if (src_line->p[k].percent > percent_max) | ||
1079 | percent_max = src_line->p[k].percent; | ||
1080 | } | ||
1081 | |||
1082 | if (percent_max <= 0.5) | ||
1083 | goto next; | ||
967 | 1084 | ||
968 | offset = start + i; | 1085 | offset = start + i; |
969 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | 1086 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); |
970 | fp = popen(cmd, "r"); | 1087 | fp = popen(cmd, "r"); |
971 | if (!fp) | 1088 | if (!fp) |
972 | continue; | 1089 | goto next; |
973 | 1090 | ||
974 | if (getline(&path, &line_len, fp) < 0 || !line_len) | 1091 | if (getline(&path, &line_len, fp) < 0 || !line_len) |
975 | goto next; | 1092 | goto next_close; |
976 | 1093 | ||
977 | src_line[i].path = malloc(sizeof(char) * line_len + 1); | 1094 | src_line->path = malloc(sizeof(char) * line_len + 1); |
978 | if (!src_line[i].path) | 1095 | if (!src_line->path) |
979 | goto next; | 1096 | goto next_close; |
980 | 1097 | ||
981 | strcpy(src_line[i].path, path); | 1098 | strcpy(src_line->path, path); |
982 | insert_source_line(&tmp_root, &src_line[i]); | 1099 | insert_source_line(&tmp_root, src_line); |
983 | 1100 | ||
984 | next: | 1101 | next_close: |
985 | pclose(fp); | 1102 | pclose(fp); |
1103 | next: | ||
1104 | src_line = (void *)src_line + sizeof_src_line; | ||
986 | } | 1105 | } |
987 | 1106 | ||
988 | resort_source_line(root, &tmp_root); | 1107 | resort_source_line(root, &tmp_root); |
@@ -1004,24 +1123,33 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
1004 | 1123 | ||
1005 | node = rb_first(root); | 1124 | node = rb_first(root); |
1006 | while (node) { | 1125 | while (node) { |
1007 | double percent; | 1126 | double percent, percent_max = 0.0; |
1008 | const char *color; | 1127 | const char *color; |
1009 | char *path; | 1128 | char *path; |
1129 | int i; | ||
1010 | 1130 | ||
1011 | src_line = rb_entry(node, struct source_line, node); | 1131 | src_line = rb_entry(node, struct source_line, node); |
1012 | percent = src_line->percent_sum; | 1132 | for (i = 0; i < src_line->nr_pcnt; i++) { |
1013 | color = get_percent_color(percent); | 1133 | percent = src_line->p[i].percent_sum; |
1134 | color = get_percent_color(percent); | ||
1135 | color_fprintf(stdout, color, " %7.2f", percent); | ||
1136 | |||
1137 | if (percent > percent_max) | ||
1138 | percent_max = percent; | ||
1139 | } | ||
1140 | |||
1014 | path = src_line->path; | 1141 | path = src_line->path; |
1142 | color = get_percent_color(percent_max); | ||
1143 | color_fprintf(stdout, color, " %s", path); | ||
1015 | 1144 | ||
1016 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
1017 | node = rb_next(node); | 1145 | node = rb_next(node); |
1018 | } | 1146 | } |
1019 | } | 1147 | } |
1020 | 1148 | ||
1021 | static void symbol__annotate_hits(struct symbol *sym, int evidx) | 1149 | static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) |
1022 | { | 1150 | { |
1023 | struct annotation *notes = symbol__annotation(sym); | 1151 | struct annotation *notes = symbol__annotation(sym); |
1024 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1152 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); |
1025 | u64 len = symbol__size(sym), offset; | 1153 | u64 len = symbol__size(sym), offset; |
1026 | 1154 | ||
1027 | for (offset = 0; offset < len; ++offset) | 1155 | for (offset = 0; offset < len; ++offset) |
@@ -1031,9 +1159,9 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) | |||
1031 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | 1159 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); |
1032 | } | 1160 | } |
1033 | 1161 | ||
1034 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | 1162 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
1035 | bool full_paths, int min_pcnt, int max_lines, | 1163 | struct perf_evsel *evsel, bool full_paths, |
1036 | int context) | 1164 | int min_pcnt, int max_lines, int context) |
1037 | { | 1165 | { |
1038 | struct dso *dso = map->dso; | 1166 | struct dso *dso = map->dso; |
1039 | char *filename; | 1167 | char *filename; |
@@ -1044,6 +1172,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1044 | int printed = 2, queue_len = 0; | 1172 | int printed = 2, queue_len = 0; |
1045 | int more = 0; | 1173 | int more = 0; |
1046 | u64 len; | 1174 | u64 len; |
1175 | int width = 8; | ||
1176 | int namelen; | ||
1047 | 1177 | ||
1048 | filename = strdup(dso->long_name); | 1178 | filename = strdup(dso->long_name); |
1049 | if (!filename) | 1179 | if (!filename) |
@@ -1055,12 +1185,18 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1055 | d_filename = basename(filename); | 1185 | d_filename = basename(filename); |
1056 | 1186 | ||
1057 | len = symbol__size(sym); | 1187 | len = symbol__size(sym); |
1188 | namelen = strlen(d_filename); | ||
1189 | |||
1190 | if (perf_evsel__is_group_event(evsel)) | ||
1191 | width *= evsel->nr_members; | ||
1058 | 1192 | ||
1059 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | 1193 | printf(" %-*.*s| Source code & Disassembly of %s\n", |
1060 | printf("------------------------------------------------\n"); | 1194 | width, width, "Percent", d_filename); |
1195 | printf("-%-*.*s-------------------------------------\n", | ||
1196 | width+namelen, width+namelen, graph_dotted_line); | ||
1061 | 1197 | ||
1062 | if (verbose) | 1198 | if (verbose) |
1063 | symbol__annotate_hits(sym, evidx); | 1199 | symbol__annotate_hits(sym, evsel); |
1064 | 1200 | ||
1065 | list_for_each_entry(pos, ¬es->src->source, node) { | 1201 | list_for_each_entry(pos, ¬es->src->source, node) { |
1066 | if (context && queue == NULL) { | 1202 | if (context && queue == NULL) { |
@@ -1068,7 +1204,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1068 | queue_len = 0; | 1204 | queue_len = 0; |
1069 | } | 1205 | } |
1070 | 1206 | ||
1071 | switch (disasm_line__print(pos, sym, start, evidx, len, | 1207 | switch (disasm_line__print(pos, sym, start, evsel, len, |
1072 | min_pcnt, printed, max_lines, | 1208 | min_pcnt, printed, max_lines, |
1073 | queue)) { | 1209 | queue)) { |
1074 | case 0: | 1210 | case 0: |
@@ -1163,9 +1299,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp) | |||
1163 | return printed; | 1299 | return printed; |
1164 | } | 1300 | } |
1165 | 1301 | ||
1166 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | 1302 | int symbol__tty_annotate(struct symbol *sym, struct map *map, |
1167 | bool print_lines, bool full_paths, int min_pcnt, | 1303 | struct perf_evsel *evsel, bool print_lines, |
1168 | int max_lines) | 1304 | bool full_paths, int min_pcnt, int max_lines) |
1169 | { | 1305 | { |
1170 | struct dso *dso = map->dso; | 1306 | struct dso *dso = map->dso; |
1171 | const char *filename = dso->long_name; | 1307 | const char *filename = dso->long_name; |
@@ -1178,12 +1314,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
1178 | len = symbol__size(sym); | 1314 | len = symbol__size(sym); |
1179 | 1315 | ||
1180 | if (print_lines) { | 1316 | if (print_lines) { |
1181 | symbol__get_source_line(sym, map, evidx, &source_line, | 1317 | symbol__get_source_line(sym, map, evsel, &source_line, |
1182 | len, filename); | 1318 | len, filename); |
1183 | print_summary(&source_line, filename); | 1319 | print_summary(&source_line, filename); |
1184 | } | 1320 | } |
1185 | 1321 | ||
1186 | symbol__annotate_printf(sym, map, evidx, full_paths, | 1322 | symbol__annotate_printf(sym, map, evsel, full_paths, |
1187 | min_pcnt, max_lines, 0); | 1323 | min_pcnt, max_lines, 0); |
1188 | if (print_lines) | 1324 | if (print_lines) |
1189 | symbol__free_source_line(sym, len); | 1325 | symbol__free_source_line(sym, len); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 8eec94358a4a..af755156d278 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "types.h" | 6 | #include "types.h" |
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include "hist.h" | 8 | #include "hist.h" |
9 | #include "sort.h" | ||
9 | #include <linux/list.h> | 10 | #include <linux/list.h> |
10 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
11 | #include <pthread.h> | 12 | #include <pthread.h> |
@@ -49,6 +50,8 @@ bool ins__is_jump(const struct ins *ins); | |||
49 | bool ins__is_call(const struct ins *ins); | 50 | bool ins__is_call(const struct ins *ins); |
50 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); | 51 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); |
51 | 52 | ||
53 | struct annotation; | ||
54 | |||
52 | struct disasm_line { | 55 | struct disasm_line { |
53 | struct list_head node; | 56 | struct list_head node; |
54 | s64 offset; | 57 | s64 offset; |
@@ -67,17 +70,24 @@ void disasm_line__free(struct disasm_line *dl); | |||
67 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); | 70 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); |
68 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); | 71 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); |
69 | size_t disasm__fprintf(struct list_head *head, FILE *fp); | 72 | size_t disasm__fprintf(struct list_head *head, FILE *fp); |
73 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | ||
74 | s64 end, const char **path); | ||
70 | 75 | ||
71 | struct sym_hist { | 76 | struct sym_hist { |
72 | u64 sum; | 77 | u64 sum; |
73 | u64 addr[0]; | 78 | u64 addr[0]; |
74 | }; | 79 | }; |
75 | 80 | ||
76 | struct source_line { | 81 | struct source_line_percent { |
77 | struct rb_node node; | ||
78 | double percent; | 82 | double percent; |
79 | double percent_sum; | 83 | double percent_sum; |
84 | }; | ||
85 | |||
86 | struct source_line { | ||
87 | struct rb_node node; | ||
80 | char *path; | 88 | char *path; |
89 | int nr_pcnt; | ||
90 | struct source_line_percent p[1]; | ||
81 | }; | 91 | }; |
82 | 92 | ||
83 | /** struct annotated_source - symbols with hits have this attached as in sannotation | 93 | /** struct annotated_source - symbols with hits have this attached as in sannotation |
@@ -129,31 +139,56 @@ void symbol__annotate_zero_histograms(struct symbol *sym); | |||
129 | 139 | ||
130 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | 140 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); |
131 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); | 141 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); |
132 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | 142 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
133 | bool full_paths, int min_pcnt, int max_lines, | 143 | struct perf_evsel *evsel, bool full_paths, |
134 | int context); | 144 | int min_pcnt, int max_lines, int context); |
135 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | 145 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); |
136 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); | 146 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); |
137 | void disasm__purge(struct list_head *head); | 147 | void disasm__purge(struct list_head *head); |
138 | 148 | ||
139 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | 149 | int symbol__tty_annotate(struct symbol *sym, struct map *map, |
140 | bool print_lines, bool full_paths, int min_pcnt, | 150 | struct perf_evsel *evsel, bool print_lines, |
141 | int max_lines); | 151 | bool full_paths, int min_pcnt, int max_lines); |
142 | 152 | ||
143 | #ifdef NEWT_SUPPORT | 153 | #ifdef SLANG_SUPPORT |
144 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 154 | int symbol__tui_annotate(struct symbol *sym, struct map *map, |
155 | struct perf_evsel *evsel, | ||
145 | struct hist_browser_timer *hbt); | 156 | struct hist_browser_timer *hbt); |
146 | #else | 157 | #else |
147 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, | 158 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
148 | struct map *map __maybe_unused, | 159 | struct map *map __maybe_unused, |
149 | int evidx __maybe_unused, | 160 | struct perf_evsel *evsel __maybe_unused, |
150 | struct hist_browser_timer *hbt | 161 | struct hist_browser_timer *hbt |
151 | __maybe_unused) | 162 | __maybe_unused) |
152 | { | 163 | { |
153 | return 0; | 164 | return 0; |
154 | } | 165 | } |
155 | #endif | 166 | #endif |
156 | 167 | ||
168 | #ifdef GTK2_SUPPORT | ||
169 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, | ||
170 | struct perf_evsel *evsel, | ||
171 | struct hist_browser_timer *hbt); | ||
172 | |||
173 | static inline int hist_entry__gtk_annotate(struct hist_entry *he, | ||
174 | struct perf_evsel *evsel, | ||
175 | struct hist_browser_timer *hbt) | ||
176 | { | ||
177 | return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); | ||
178 | } | ||
179 | |||
180 | void perf_gtk__show_annotations(void); | ||
181 | #else | ||
182 | static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, | ||
183 | struct perf_evsel *evsel __maybe_unused, | ||
184 | struct hist_browser_timer *hbt __maybe_unused) | ||
185 | { | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static inline void perf_gtk__show_annotations(void) {} | ||
190 | #endif | ||
191 | |||
157 | extern const char *disassembler_style; | 192 | extern const char *disassembler_style; |
158 | 193 | ||
159 | #endif /* __PERF_ANNOTATE_H */ | 194 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index d3b3f5d82137..42b6a632fe7b 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -444,7 +444,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor, | |||
444 | struct callchain_cursor_node *node = *cursor->last; | 444 | struct callchain_cursor_node *node = *cursor->last; |
445 | 445 | ||
446 | if (!node) { | 446 | if (!node) { |
447 | node = calloc(sizeof(*node), 1); | 447 | node = calloc(1, sizeof(*node)); |
448 | if (!node) | 448 | if (!node) |
449 | return -ENOMEM; | 449 | return -ENOMEM; |
450 | 450 | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index eb340571e7d6..3ee9f67d5af0 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -143,4 +143,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
143 | cursor->curr = cursor->curr->next; | 143 | cursor->curr = cursor->curr->next; |
144 | cursor->pos++; | 144 | cursor->pos++; |
145 | } | 145 | } |
146 | |||
147 | struct option; | ||
148 | |||
149 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | ||
150 | extern const char record_callchain_help[]; | ||
146 | #endif /* __PERF_CALLCHAIN_H */ | 151 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 2b32ffa9ebdb..beb8cf9f9976 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -1,8 +1,10 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "sysfs.h" | ||
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "cpumap.h" | 4 | #include "cpumap.h" |
4 | #include <assert.h> | 5 | #include <assert.h> |
5 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <stdlib.h> | ||
6 | 8 | ||
7 | static struct cpu_map *cpu_map__default_new(void) | 9 | static struct cpu_map *cpu_map__default_new(void) |
8 | { | 10 | { |
@@ -201,3 +203,117 @@ void cpu_map__delete(struct cpu_map *map) | |||
201 | { | 203 | { |
202 | free(map); | 204 | free(map); |
203 | } | 205 | } |
206 | |||
207 | int cpu_map__get_socket(struct cpu_map *map, int idx) | ||
208 | { | ||
209 | FILE *fp; | ||
210 | const char *mnt; | ||
211 | char path[PATH_MAX]; | ||
212 | int cpu, ret; | ||
213 | |||
214 | if (idx > map->nr) | ||
215 | return -1; | ||
216 | |||
217 | cpu = map->map[idx]; | ||
218 | |||
219 | mnt = sysfs_find_mountpoint(); | ||
220 | if (!mnt) | ||
221 | return -1; | ||
222 | |||
223 | snprintf(path, PATH_MAX, | ||
224 | "%s/devices/system/cpu/cpu%d/topology/physical_package_id", | ||
225 | mnt, cpu); | ||
226 | |||
227 | fp = fopen(path, "r"); | ||
228 | if (!fp) | ||
229 | return -1; | ||
230 | ret = fscanf(fp, "%d", &cpu); | ||
231 | fclose(fp); | ||
232 | return ret == 1 ? cpu : -1; | ||
233 | } | ||
234 | |||
235 | static int cmp_ids(const void *a, const void *b) | ||
236 | { | ||
237 | return *(int *)a - *(int *)b; | ||
238 | } | ||
239 | |||
240 | static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, | ||
241 | int (*f)(struct cpu_map *map, int cpu)) | ||
242 | { | ||
243 | struct cpu_map *c; | ||
244 | int nr = cpus->nr; | ||
245 | int cpu, s1, s2; | ||
246 | |||
247 | /* allocate as much as possible */ | ||
248 | c = calloc(1, sizeof(*c) + nr * sizeof(int)); | ||
249 | if (!c) | ||
250 | return -1; | ||
251 | |||
252 | for (cpu = 0; cpu < nr; cpu++) { | ||
253 | s1 = f(cpus, cpu); | ||
254 | for (s2 = 0; s2 < c->nr; s2++) { | ||
255 | if (s1 == c->map[s2]) | ||
256 | break; | ||
257 | } | ||
258 | if (s2 == c->nr) { | ||
259 | c->map[c->nr] = s1; | ||
260 | c->nr++; | ||
261 | } | ||
262 | } | ||
263 | /* ensure we process id in increasing order */ | ||
264 | qsort(c->map, c->nr, sizeof(int), cmp_ids); | ||
265 | |||
266 | *res = c; | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | int cpu_map__get_core(struct cpu_map *map, int idx) | ||
271 | { | ||
272 | FILE *fp; | ||
273 | const char *mnt; | ||
274 | char path[PATH_MAX]; | ||
275 | int cpu, ret, s; | ||
276 | |||
277 | if (idx > map->nr) | ||
278 | return -1; | ||
279 | |||
280 | cpu = map->map[idx]; | ||
281 | |||
282 | mnt = sysfs_find_mountpoint(); | ||
283 | if (!mnt) | ||
284 | return -1; | ||
285 | |||
286 | snprintf(path, PATH_MAX, | ||
287 | "%s/devices/system/cpu/cpu%d/topology/core_id", | ||
288 | mnt, cpu); | ||
289 | |||
290 | fp = fopen(path, "r"); | ||
291 | if (!fp) | ||
292 | return -1; | ||
293 | ret = fscanf(fp, "%d", &cpu); | ||
294 | fclose(fp); | ||
295 | if (ret != 1) | ||
296 | return -1; | ||
297 | |||
298 | s = cpu_map__get_socket(map, idx); | ||
299 | if (s == -1) | ||
300 | return -1; | ||
301 | |||
302 | /* | ||
303 | * encode socket in upper 16 bits | ||
304 | * core_id is relative to socket, and | ||
305 | * we need a global id. So we combine | ||
306 | * socket+ core id | ||
307 | */ | ||
308 | return (s << 16) | (cpu & 0xffff); | ||
309 | } | ||
310 | |||
311 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) | ||
312 | { | ||
313 | return cpu_map__build_map(cpus, sockp, cpu_map__get_socket); | ||
314 | } | ||
315 | |||
316 | int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) | ||
317 | { | ||
318 | return cpu_map__build_map(cpus, corep, cpu_map__get_core); | ||
319 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 2f68a3b8c285..9bed02e5fb3d 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -14,6 +14,27 @@ struct cpu_map *cpu_map__dummy_new(void); | |||
14 | void cpu_map__delete(struct cpu_map *map); | 14 | void cpu_map__delete(struct cpu_map *map); |
15 | struct cpu_map *cpu_map__read(FILE *file); | 15 | struct cpu_map *cpu_map__read(FILE *file); |
16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | 16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); |
17 | int cpu_map__get_socket(struct cpu_map *map, int idx); | ||
18 | int cpu_map__get_core(struct cpu_map *map, int idx); | ||
19 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp); | ||
20 | int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep); | ||
21 | |||
22 | static inline int cpu_map__socket(struct cpu_map *sock, int s) | ||
23 | { | ||
24 | if (!sock || s > sock->nr || s < 0) | ||
25 | return 0; | ||
26 | return sock->map[s]; | ||
27 | } | ||
28 | |||
29 | static inline int cpu_map__id_to_socket(int id) | ||
30 | { | ||
31 | return id >> 16; | ||
32 | } | ||
33 | |||
34 | static inline int cpu_map__id_to_cpu(int id) | ||
35 | { | ||
36 | return id & 0xffff; | ||
37 | } | ||
17 | 38 | ||
18 | static inline int cpu_map__nr(const struct cpu_map *map) | 39 | static inline int cpu_map__nr(const struct cpu_map *map) |
19 | { | 40 | { |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 03f830b48148..399e74c34c1a 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -23,10 +23,8 @@ int eprintf(int level, const char *fmt, ...) | |||
23 | 23 | ||
24 | if (verbose >= level) { | 24 | if (verbose >= level) { |
25 | va_start(args, fmt); | 25 | va_start(args, fmt); |
26 | if (use_browser == 1) | 26 | if (use_browser >= 1) |
27 | ret = ui_helpline__show_help(fmt, args); | 27 | ui_helpline__vshow(fmt, args); |
28 | else if (use_browser == 2) | ||
29 | ret = perf_gtk__show_helpline(fmt, args); | ||
30 | else | 28 | else |
31 | ret = vfprintf(stderr, fmt, args); | 29 | ret = vfprintf(stderr, fmt, args); |
32 | va_end(args); | 30 | va_end(args); |
@@ -49,28 +47,6 @@ int dump_printf(const char *fmt, ...) | |||
49 | return ret; | 47 | return ret; |
50 | } | 48 | } |
51 | 49 | ||
52 | #if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT) | ||
53 | int ui__warning(const char *format, ...) | ||
54 | { | ||
55 | va_list args; | ||
56 | |||
57 | va_start(args, format); | ||
58 | vfprintf(stderr, format, args); | ||
59 | va_end(args); | ||
60 | return 0; | ||
61 | } | ||
62 | #endif | ||
63 | |||
64 | int ui__error_paranoid(void) | ||
65 | { | ||
66 | return ui__error("Permission error - are you root?\n" | ||
67 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | ||
68 | " -1 - Not paranoid at all\n" | ||
69 | " 0 - Disallow raw tracepoint access for unpriv\n" | ||
70 | " 1 - Disallow cpu events for unpriv\n" | ||
71 | " 2 - Disallow kernel profiling for unpriv\n"); | ||
72 | } | ||
73 | |||
74 | void trace_event(union perf_event *event) | 50 | void trace_event(union perf_event *event) |
75 | { | 51 | { |
76 | unsigned char *raw_event = (void *)event; | 52 | unsigned char *raw_event = (void *)event; |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 83e8d234af6b..efbd98805ad0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "event.h" | 6 | #include "event.h" |
7 | #include "../ui/helpline.h" | 7 | #include "../ui/helpline.h" |
8 | #include "../ui/progress.h" | ||
9 | #include "../ui/util.h" | ||
8 | 10 | ||
9 | extern int verbose; | 11 | extern int verbose; |
10 | extern bool quiet, dump_trace; | 12 | extern bool quiet, dump_trace; |
@@ -12,39 +14,7 @@ extern bool quiet, dump_trace; | |||
12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 14 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
13 | void trace_event(union perf_event *event); | 15 | void trace_event(union perf_event *event); |
14 | 16 | ||
15 | struct ui_progress; | ||
16 | struct perf_error_ops; | ||
17 | |||
18 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) | ||
19 | |||
20 | #include "../ui/progress.h" | ||
21 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | 17 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); |
22 | #include "../ui/util.h" | ||
23 | |||
24 | #else | ||
25 | |||
26 | static inline void ui_progress__update(u64 curr __maybe_unused, | ||
27 | u64 total __maybe_unused, | ||
28 | const char *title __maybe_unused) {} | ||
29 | static inline void ui_progress__finish(void) {} | ||
30 | |||
31 | #define ui__error(format, arg...) ui__warning(format, ##arg) | ||
32 | |||
33 | static inline int | ||
34 | perf_error__register(struct perf_error_ops *eops __maybe_unused) | ||
35 | { | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static inline int | ||
40 | perf_error__unregister(struct perf_error_ops *eops __maybe_unused) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
46 | |||
47 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 18 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
48 | int ui__error_paranoid(void); | ||
49 | 19 | ||
50 | #endif /* __PERF_DEBUG_H */ | 20 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c deleted file mode 100644 index dd8b19319c03..000000000000 --- a/tools/perf/util/debugfs.c +++ /dev/null | |||
@@ -1,114 +0,0 @@ | |||
1 | #include "util.h" | ||
2 | #include "debugfs.h" | ||
3 | #include "cache.h" | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <sys/mount.h> | ||
7 | |||
8 | static int debugfs_premounted; | ||
9 | char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; | ||
10 | char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; | ||
11 | |||
12 | static const char *debugfs_known_mountpoints[] = { | ||
13 | "/sys/kernel/debug/", | ||
14 | "/debug/", | ||
15 | 0, | ||
16 | }; | ||
17 | |||
18 | static int debugfs_found; | ||
19 | |||
20 | /* find the path to the mounted debugfs */ | ||
21 | const char *debugfs_find_mountpoint(void) | ||
22 | { | ||
23 | const char **ptr; | ||
24 | char type[100]; | ||
25 | FILE *fp; | ||
26 | |||
27 | if (debugfs_found) | ||
28 | return (const char *) debugfs_mountpoint; | ||
29 | |||
30 | ptr = debugfs_known_mountpoints; | ||
31 | while (*ptr) { | ||
32 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
33 | debugfs_found = 1; | ||
34 | strcpy(debugfs_mountpoint, *ptr); | ||
35 | return debugfs_mountpoint; | ||
36 | } | ||
37 | ptr++; | ||
38 | } | ||
39 | |||
40 | /* give up and parse /proc/mounts */ | ||
41 | fp = fopen("/proc/mounts", "r"); | ||
42 | if (fp == NULL) | ||
43 | return NULL; | ||
44 | |||
45 | while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", | ||
46 | debugfs_mountpoint, type) == 2) { | ||
47 | if (strcmp(type, "debugfs") == 0) | ||
48 | break; | ||
49 | } | ||
50 | fclose(fp); | ||
51 | |||
52 | if (strcmp(type, "debugfs") != 0) | ||
53 | return NULL; | ||
54 | |||
55 | debugfs_found = 1; | ||
56 | |||
57 | return debugfs_mountpoint; | ||
58 | } | ||
59 | |||
60 | /* verify that a mountpoint is actually a debugfs instance */ | ||
61 | |||
62 | int debugfs_valid_mountpoint(const char *debugfs) | ||
63 | { | ||
64 | struct statfs st_fs; | ||
65 | |||
66 | if (statfs(debugfs, &st_fs) < 0) | ||
67 | return -ENOENT; | ||
68 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
69 | return -ENOENT; | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static void debugfs_set_tracing_events_path(const char *mountpoint) | ||
75 | { | ||
76 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | ||
77 | mountpoint, "tracing/events"); | ||
78 | } | ||
79 | |||
80 | /* mount the debugfs somewhere if it's not mounted */ | ||
81 | |||
82 | char *debugfs_mount(const char *mountpoint) | ||
83 | { | ||
84 | /* see if it's already mounted */ | ||
85 | if (debugfs_find_mountpoint()) { | ||
86 | debugfs_premounted = 1; | ||
87 | goto out; | ||
88 | } | ||
89 | |||
90 | /* if not mounted and no argument */ | ||
91 | if (mountpoint == NULL) { | ||
92 | /* see if environment variable set */ | ||
93 | mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
94 | /* if no environment variable, use default */ | ||
95 | if (mountpoint == NULL) | ||
96 | mountpoint = "/sys/kernel/debug"; | ||
97 | } | ||
98 | |||
99 | if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) | ||
100 | return NULL; | ||
101 | |||
102 | /* save the mountpoint */ | ||
103 | debugfs_found = 1; | ||
104 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | ||
105 | out: | ||
106 | debugfs_set_tracing_events_path(debugfs_mountpoint); | ||
107 | return debugfs_mountpoint; | ||
108 | } | ||
109 | |||
110 | void debugfs_set_path(const char *mountpoint) | ||
111 | { | ||
112 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); | ||
113 | debugfs_set_tracing_events_path(mountpoint); | ||
114 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h deleted file mode 100644 index 68f3e87ec57f..000000000000 --- a/tools/perf/util/debugfs.h +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | #ifndef __DEBUGFS_H__ | ||
2 | #define __DEBUGFS_H__ | ||
3 | |||
4 | const char *debugfs_find_mountpoint(void); | ||
5 | int debugfs_valid_mountpoint(const char *debugfs); | ||
6 | char *debugfs_mount(const char *mountpoint); | ||
7 | void debugfs_set_path(const char *mountpoint); | ||
8 | |||
9 | extern char debugfs_mountpoint[]; | ||
10 | extern char tracing_events_path[]; | ||
11 | |||
12 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index d6d9a465acdb..6f7d5a9d6b05 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -539,13 +539,13 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name) | |||
539 | } | 539 | } |
540 | 540 | ||
541 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 541 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
542 | bool with_hits) | 542 | bool (skip)(struct dso *dso, int parm), int parm) |
543 | { | 543 | { |
544 | struct dso *pos; | 544 | struct dso *pos; |
545 | size_t ret = 0; | 545 | size_t ret = 0; |
546 | 546 | ||
547 | list_for_each_entry(pos, head, node) { | 547 | list_for_each_entry(pos, head, node) { |
548 | if (with_hits && !pos->hit) | 548 | if (skip && skip(pos, parm)) |
549 | continue; | 549 | continue; |
550 | ret += dso__fprintf_buildid(pos, fp); | 550 | ret += dso__fprintf_buildid(pos, fp); |
551 | ret += fprintf(fp, " %s\n", pos->long_name); | 551 | ret += fprintf(fp, " %s\n", pos->long_name); |
@@ -583,7 +583,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | |||
583 | if (dso->short_name != dso->long_name) | 583 | if (dso->short_name != dso->long_name) |
584 | ret += fprintf(fp, "%s, ", dso->long_name); | 584 | ret += fprintf(fp, "%s, ", dso->long_name); |
585 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | 585 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], |
586 | dso->loaded ? "" : "NOT "); | 586 | dso__loaded(dso, type) ? "" : "NOT "); |
587 | ret += dso__fprintf_buildid(dso, fp); | 587 | ret += dso__fprintf_buildid(dso, fp); |
588 | ret += fprintf(fp, ")\n"); | 588 | ret += fprintf(fp, ")\n"); |
589 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | 589 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index e03276940b99..450199ab51b5 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -138,7 +138,7 @@ struct dso *__dsos__findnew(struct list_head *head, const char *name); | |||
138 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | 138 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); |
139 | 139 | ||
140 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 140 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
141 | bool with_hits); | 141 | bool (skip)(struct dso *dso, int parm), int parm); |
142 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); | 142 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); |
143 | 143 | ||
144 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | 144 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3cf2c3e0605f..5cd13d768cec 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -476,8 +476,10 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
476 | } | 476 | } |
477 | } | 477 | } |
478 | 478 | ||
479 | if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) | 479 | if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) { |
480 | free(event); | ||
480 | return -ENOENT; | 481 | return -ENOENT; |
482 | } | ||
481 | 483 | ||
482 | map = machine->vmlinux_maps[MAP__FUNCTION]; | 484 | map = machine->vmlinux_maps[MAP__FUNCTION]; |
483 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), | 485 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 0d573ff4771a..181389535c0c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -88,8 +88,10 @@ struct perf_sample { | |||
88 | u64 id; | 88 | u64 id; |
89 | u64 stream_id; | 89 | u64 stream_id; |
90 | u64 period; | 90 | u64 period; |
91 | u64 weight; | ||
91 | u32 cpu; | 92 | u32 cpu; |
92 | u32 raw_size; | 93 | u32 raw_size; |
94 | u64 data_src; | ||
93 | void *raw_data; | 95 | void *raw_data; |
94 | struct ip_callchain *callchain; | 96 | struct ip_callchain *callchain; |
95 | struct branch_stack *branch_stack; | 97 | struct branch_stack *branch_stack; |
@@ -97,6 +99,13 @@ struct perf_sample { | |||
97 | struct stack_dump user_stack; | 99 | struct stack_dump user_stack; |
98 | }; | 100 | }; |
99 | 101 | ||
102 | #define PERF_MEM_DATA_SRC_NONE \ | ||
103 | (PERF_MEM_S(OP, NA) |\ | ||
104 | PERF_MEM_S(LVL, NA) |\ | ||
105 | PERF_MEM_S(SNOOP, NA) |\ | ||
106 | PERF_MEM_S(LOCK, NA) |\ | ||
107 | PERF_MEM_S(TLB, NA)) | ||
108 | |||
100 | struct build_id_event { | 109 | struct build_id_event { |
101 | struct perf_event_header header; | 110 | struct perf_event_header header; |
102 | pid_t pid; | 111 | pid_t pid; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 705293489e3c..99b43dd18c57 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
8 | */ | 8 | */ |
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include "debugfs.h" | 10 | #include <lk/debugfs.h> |
11 | #include <poll.h> | 11 | #include <poll.h> |
12 | #include "cpumap.h" | 12 | #include "cpumap.h" |
13 | #include "thread_map.h" | 13 | #include "thread_map.h" |
@@ -38,21 +38,26 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
38 | evlist->workload.pid = -1; | 38 | evlist->workload.pid = -1; |
39 | } | 39 | } |
40 | 40 | ||
41 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 41 | struct perf_evlist *perf_evlist__new(void) |
42 | struct thread_map *threads) | ||
43 | { | 42 | { |
44 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | 43 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); |
45 | 44 | ||
46 | if (evlist != NULL) | 45 | if (evlist != NULL) |
47 | perf_evlist__init(evlist, cpus, threads); | 46 | perf_evlist__init(evlist, NULL, NULL); |
48 | 47 | ||
49 | return evlist; | 48 | return evlist; |
50 | } | 49 | } |
51 | 50 | ||
52 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 51 | void perf_evlist__config(struct perf_evlist *evlist, |
53 | struct perf_record_opts *opts) | 52 | struct perf_record_opts *opts) |
54 | { | 53 | { |
55 | struct perf_evsel *evsel; | 54 | struct perf_evsel *evsel; |
55 | /* | ||
56 | * Set the evsel leader links before we configure attributes, | ||
57 | * since some might depend on this info. | ||
58 | */ | ||
59 | if (opts->group) | ||
60 | perf_evlist__set_leader(evlist); | ||
56 | 61 | ||
57 | if (evlist->cpus->map[0] < 0) | 62 | if (evlist->cpus->map[0] < 0) |
58 | opts->no_inherit = true; | 63 | opts->no_inherit = true; |
@@ -61,7 +66,7 @@ void perf_evlist__config_attrs(struct perf_evlist *evlist, | |||
61 | perf_evsel__config(evsel, opts); | 66 | perf_evsel__config(evsel, opts); |
62 | 67 | ||
63 | if (evlist->nr_entries > 1) | 68 | if (evlist->nr_entries > 1) |
64 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 69 | perf_evsel__set_sample_id(evsel); |
65 | } | 70 | } |
66 | } | 71 | } |
67 | 72 | ||
@@ -111,18 +116,21 @@ void __perf_evlist__set_leader(struct list_head *list) | |||
111 | struct perf_evsel *evsel, *leader; | 116 | struct perf_evsel *evsel, *leader; |
112 | 117 | ||
113 | leader = list_entry(list->next, struct perf_evsel, node); | 118 | leader = list_entry(list->next, struct perf_evsel, node); |
114 | leader->leader = NULL; | 119 | evsel = list_entry(list->prev, struct perf_evsel, node); |
120 | |||
121 | leader->nr_members = evsel->idx - leader->idx + 1; | ||
115 | 122 | ||
116 | list_for_each_entry(evsel, list, node) { | 123 | list_for_each_entry(evsel, list, node) { |
117 | if (evsel != leader) | 124 | evsel->leader = leader; |
118 | evsel->leader = leader; | ||
119 | } | 125 | } |
120 | } | 126 | } |
121 | 127 | ||
122 | void perf_evlist__set_leader(struct perf_evlist *evlist) | 128 | void perf_evlist__set_leader(struct perf_evlist *evlist) |
123 | { | 129 | { |
124 | if (evlist->nr_entries) | 130 | if (evlist->nr_entries) { |
131 | evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0; | ||
125 | __perf_evlist__set_leader(&evlist->entries); | 132 | __perf_evlist__set_leader(&evlist->entries); |
133 | } | ||
126 | } | 134 | } |
127 | 135 | ||
128 | int perf_evlist__add_default(struct perf_evlist *evlist) | 136 | int perf_evlist__add_default(struct perf_evlist *evlist) |
@@ -219,12 +227,14 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
219 | { | 227 | { |
220 | int cpu, thread; | 228 | int cpu, thread; |
221 | struct perf_evsel *pos; | 229 | struct perf_evsel *pos; |
230 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
231 | int nr_threads = thread_map__nr(evlist->threads); | ||
222 | 232 | ||
223 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 233 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
224 | list_for_each_entry(pos, &evlist->entries, node) { | 234 | list_for_each_entry(pos, &evlist->entries, node) { |
225 | if (perf_evsel__is_group_member(pos)) | 235 | if (!perf_evsel__is_group_leader(pos)) |
226 | continue; | 236 | continue; |
227 | for (thread = 0; thread < evlist->threads->nr; thread++) | 237 | for (thread = 0; thread < nr_threads; thread++) |
228 | ioctl(FD(pos, cpu, thread), | 238 | ioctl(FD(pos, cpu, thread), |
229 | PERF_EVENT_IOC_DISABLE, 0); | 239 | PERF_EVENT_IOC_DISABLE, 0); |
230 | } | 240 | } |
@@ -235,12 +245,14 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
235 | { | 245 | { |
236 | int cpu, thread; | 246 | int cpu, thread; |
237 | struct perf_evsel *pos; | 247 | struct perf_evsel *pos; |
248 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
249 | int nr_threads = thread_map__nr(evlist->threads); | ||
238 | 250 | ||
239 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { | 251 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
240 | list_for_each_entry(pos, &evlist->entries, node) { | 252 | list_for_each_entry(pos, &evlist->entries, node) { |
241 | if (perf_evsel__is_group_member(pos)) | 253 | if (!perf_evsel__is_group_leader(pos)) |
242 | continue; | 254 | continue; |
243 | for (thread = 0; thread < evlist->threads->nr; thread++) | 255 | for (thread = 0; thread < nr_threads; thread++) |
244 | ioctl(FD(pos, cpu, thread), | 256 | ioctl(FD(pos, cpu, thread), |
245 | PERF_EVENT_IOC_ENABLE, 0); | 257 | PERF_EVENT_IOC_ENABLE, 0); |
246 | } | 258 | } |
@@ -249,7 +261,9 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
249 | 261 | ||
250 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 262 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
251 | { | 263 | { |
252 | int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries; | 264 | int nr_cpus = cpu_map__nr(evlist->cpus); |
265 | int nr_threads = thread_map__nr(evlist->threads); | ||
266 | int nfds = nr_cpus * nr_threads * evlist->nr_entries; | ||
253 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 267 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
254 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 268 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
255 | } | 269 | } |
@@ -305,7 +319,6 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
305 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | 319 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) |
306 | { | 320 | { |
307 | struct hlist_head *head; | 321 | struct hlist_head *head; |
308 | struct hlist_node *pos; | ||
309 | struct perf_sample_id *sid; | 322 | struct perf_sample_id *sid; |
310 | int hash; | 323 | int hash; |
311 | 324 | ||
@@ -315,7 +328,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
315 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 328 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
316 | head = &evlist->heads[hash]; | 329 | head = &evlist->heads[hash]; |
317 | 330 | ||
318 | hlist_for_each_entry(sid, pos, head, node) | 331 | hlist_for_each_entry(sid, head, node) |
319 | if (sid->id == id) | 332 | if (sid->id == id) |
320 | return sid->evsel; | 333 | return sid->evsel; |
321 | 334 | ||
@@ -366,7 +379,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
366 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | 379 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
367 | unsigned int offset = old; | 380 | unsigned int offset = old; |
368 | unsigned int len = min(sizeof(*event), size), cpy; | 381 | unsigned int len = min(sizeof(*event), size), cpy; |
369 | void *dst = &evlist->event_copy; | 382 | void *dst = &md->event_copy; |
370 | 383 | ||
371 | do { | 384 | do { |
372 | cpy = min(md->mask + 1 - (offset & md->mask), len); | 385 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
@@ -376,7 +389,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
376 | len -= cpy; | 389 | len -= cpy; |
377 | } while (len); | 390 | } while (len); |
378 | 391 | ||
379 | event = &evlist->event_copy; | 392 | event = &md->event_copy; |
380 | } | 393 | } |
381 | 394 | ||
382 | old += size; | 395 | old += size; |
@@ -409,7 +422,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | |||
409 | { | 422 | { |
410 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); | 423 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
411 | if (cpu_map__all(evlist->cpus)) | 424 | if (cpu_map__all(evlist->cpus)) |
412 | evlist->nr_mmaps = evlist->threads->nr; | 425 | evlist->nr_mmaps = thread_map__nr(evlist->threads); |
413 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 426 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
414 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 427 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
415 | } | 428 | } |
@@ -434,11 +447,13 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
434 | { | 447 | { |
435 | struct perf_evsel *evsel; | 448 | struct perf_evsel *evsel; |
436 | int cpu, thread; | 449 | int cpu, thread; |
450 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
451 | int nr_threads = thread_map__nr(evlist->threads); | ||
437 | 452 | ||
438 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 453 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
439 | int output = -1; | 454 | int output = -1; |
440 | 455 | ||
441 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 456 | for (thread = 0; thread < nr_threads; thread++) { |
442 | list_for_each_entry(evsel, &evlist->entries, node) { | 457 | list_for_each_entry(evsel, &evlist->entries, node) { |
443 | int fd = FD(evsel, cpu, thread); | 458 | int fd = FD(evsel, cpu, thread); |
444 | 459 | ||
@@ -462,7 +477,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
462 | return 0; | 477 | return 0; |
463 | 478 | ||
464 | out_unmap: | 479 | out_unmap: |
465 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 480 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
466 | if (evlist->mmap[cpu].base != NULL) { | 481 | if (evlist->mmap[cpu].base != NULL) { |
467 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | 482 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); |
468 | evlist->mmap[cpu].base = NULL; | 483 | evlist->mmap[cpu].base = NULL; |
@@ -475,8 +490,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
475 | { | 490 | { |
476 | struct perf_evsel *evsel; | 491 | struct perf_evsel *evsel; |
477 | int thread; | 492 | int thread; |
493 | int nr_threads = thread_map__nr(evlist->threads); | ||
478 | 494 | ||
479 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 495 | for (thread = 0; thread < nr_threads; thread++) { |
480 | int output = -1; | 496 | int output = -1; |
481 | 497 | ||
482 | list_for_each_entry(evsel, &evlist->entries, node) { | 498 | list_for_each_entry(evsel, &evlist->entries, node) { |
@@ -501,7 +517,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
501 | return 0; | 517 | return 0; |
502 | 518 | ||
503 | out_unmap: | 519 | out_unmap: |
504 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 520 | for (thread = 0; thread < nr_threads; thread++) { |
505 | if (evlist->mmap[thread].base != NULL) { | 521 | if (evlist->mmap[thread].base != NULL) { |
506 | munmap(evlist->mmap[thread].base, evlist->mmap_len); | 522 | munmap(evlist->mmap[thread].base, evlist->mmap_len); |
507 | evlist->mmap[thread].base = NULL; | 523 | evlist->mmap[thread].base = NULL; |
@@ -602,7 +618,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist) | |||
602 | struct perf_evsel *evsel; | 618 | struct perf_evsel *evsel; |
603 | int err = 0; | 619 | int err = 0; |
604 | const int ncpus = cpu_map__nr(evlist->cpus), | 620 | const int ncpus = cpu_map__nr(evlist->cpus), |
605 | nthreads = evlist->threads->nr; | 621 | nthreads = thread_map__nr(evlist->threads); |
606 | 622 | ||
607 | list_for_each_entry(evsel, &evlist->entries, node) { | 623 | list_for_each_entry(evsel, &evlist->entries, node) { |
608 | if (evsel->filter == NULL) | 624 | if (evsel->filter == NULL) |
@@ -621,7 +637,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | |||
621 | struct perf_evsel *evsel; | 637 | struct perf_evsel *evsel; |
622 | int err = 0; | 638 | int err = 0; |
623 | const int ncpus = cpu_map__nr(evlist->cpus), | 639 | const int ncpus = cpu_map__nr(evlist->cpus), |
624 | nthreads = evlist->threads->nr; | 640 | nthreads = thread_map__nr(evlist->threads); |
625 | 641 | ||
626 | list_for_each_entry(evsel, &evlist->entries, node) { | 642 | list_for_each_entry(evsel, &evlist->entries, node) { |
627 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); | 643 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); |
@@ -704,10 +720,20 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, | |||
704 | evlist->selected = evsel; | 720 | evlist->selected = evsel; |
705 | } | 721 | } |
706 | 722 | ||
723 | void perf_evlist__close(struct perf_evlist *evlist) | ||
724 | { | ||
725 | struct perf_evsel *evsel; | ||
726 | int ncpus = cpu_map__nr(evlist->cpus); | ||
727 | int nthreads = thread_map__nr(evlist->threads); | ||
728 | |||
729 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
730 | perf_evsel__close(evsel, ncpus, nthreads); | ||
731 | } | ||
732 | |||
707 | int perf_evlist__open(struct perf_evlist *evlist) | 733 | int perf_evlist__open(struct perf_evlist *evlist) |
708 | { | 734 | { |
709 | struct perf_evsel *evsel; | 735 | struct perf_evsel *evsel; |
710 | int err, ncpus, nthreads; | 736 | int err; |
711 | 737 | ||
712 | list_for_each_entry(evsel, &evlist->entries, node) { | 738 | list_for_each_entry(evsel, &evlist->entries, node) { |
713 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); | 739 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); |
@@ -717,19 +743,15 @@ int perf_evlist__open(struct perf_evlist *evlist) | |||
717 | 743 | ||
718 | return 0; | 744 | return 0; |
719 | out_err: | 745 | out_err: |
720 | ncpus = evlist->cpus ? evlist->cpus->nr : 1; | 746 | perf_evlist__close(evlist); |
721 | nthreads = evlist->threads ? evlist->threads->nr : 1; | ||
722 | |||
723 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
724 | perf_evsel__close(evsel, ncpus, nthreads); | ||
725 | |||
726 | errno = -err; | 747 | errno = -err; |
727 | return err; | 748 | return err; |
728 | } | 749 | } |
729 | 750 | ||
730 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 751 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
731 | struct perf_record_opts *opts, | 752 | struct perf_target *target, |
732 | const char *argv[]) | 753 | const char *argv[], bool pipe_output, |
754 | bool want_signal) | ||
733 | { | 755 | { |
734 | int child_ready_pipe[2], go_pipe[2]; | 756 | int child_ready_pipe[2], go_pipe[2]; |
735 | char bf; | 757 | char bf; |
@@ -751,9 +773,11 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
751 | } | 773 | } |
752 | 774 | ||
753 | if (!evlist->workload.pid) { | 775 | if (!evlist->workload.pid) { |
754 | if (opts->pipe_output) | 776 | if (pipe_output) |
755 | dup2(2, 1); | 777 | dup2(2, 1); |
756 | 778 | ||
779 | signal(SIGTERM, SIG_DFL); | ||
780 | |||
757 | close(child_ready_pipe[0]); | 781 | close(child_ready_pipe[0]); |
758 | close(go_pipe[1]); | 782 | close(go_pipe[1]); |
759 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | 783 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); |
@@ -779,11 +803,12 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
779 | execvp(argv[0], (char **)argv); | 803 | execvp(argv[0], (char **)argv); |
780 | 804 | ||
781 | perror(argv[0]); | 805 | perror(argv[0]); |
782 | kill(getppid(), SIGUSR1); | 806 | if (want_signal) |
807 | kill(getppid(), SIGUSR1); | ||
783 | exit(-1); | 808 | exit(-1); |
784 | } | 809 | } |
785 | 810 | ||
786 | if (perf_target__none(&opts->target)) | 811 | if (perf_target__none(target)) |
787 | evlist->threads->map[0] = evlist->workload.pid; | 812 | evlist->threads->map[0] = evlist->workload.pid; |
788 | 813 | ||
789 | close(child_ready_pipe[1]); | 814 | close(child_ready_pipe[1]); |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 56003f779e60..0583d36252be 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -17,10 +17,18 @@ struct perf_record_opts; | |||
17 | #define PERF_EVLIST__HLIST_BITS 8 | 17 | #define PERF_EVLIST__HLIST_BITS 8 |
18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
19 | 19 | ||
20 | struct perf_mmap { | ||
21 | void *base; | ||
22 | int mask; | ||
23 | unsigned int prev; | ||
24 | union perf_event event_copy; | ||
25 | }; | ||
26 | |||
20 | struct perf_evlist { | 27 | struct perf_evlist { |
21 | struct list_head entries; | 28 | struct list_head entries; |
22 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 29 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
23 | int nr_entries; | 30 | int nr_entries; |
31 | int nr_groups; | ||
24 | int nr_fds; | 32 | int nr_fds; |
25 | int nr_mmaps; | 33 | int nr_mmaps; |
26 | int mmap_len; | 34 | int mmap_len; |
@@ -29,7 +37,6 @@ struct perf_evlist { | |||
29 | pid_t pid; | 37 | pid_t pid; |
30 | } workload; | 38 | } workload; |
31 | bool overwrite; | 39 | bool overwrite; |
32 | union perf_event event_copy; | ||
33 | struct perf_mmap *mmap; | 40 | struct perf_mmap *mmap; |
34 | struct pollfd *pollfd; | 41 | struct pollfd *pollfd; |
35 | struct thread_map *threads; | 42 | struct thread_map *threads; |
@@ -42,8 +49,7 @@ struct perf_evsel_str_handler { | |||
42 | void *handler; | 49 | void *handler; |
43 | }; | 50 | }; |
44 | 51 | ||
45 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 52 | struct perf_evlist *perf_evlist__new(void); |
46 | struct thread_map *threads); | ||
47 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 53 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
48 | struct thread_map *threads); | 54 | struct thread_map *threads); |
49 | void perf_evlist__exit(struct perf_evlist *evlist); | 55 | void perf_evlist__exit(struct perf_evlist *evlist); |
@@ -75,13 +81,15 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
75 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 81 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
76 | 82 | ||
77 | int perf_evlist__open(struct perf_evlist *evlist); | 83 | int perf_evlist__open(struct perf_evlist *evlist); |
84 | void perf_evlist__close(struct perf_evlist *evlist); | ||
78 | 85 | ||
79 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 86 | void perf_evlist__config(struct perf_evlist *evlist, |
80 | struct perf_record_opts *opts); | 87 | struct perf_record_opts *opts); |
81 | 88 | ||
82 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 89 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
83 | struct perf_record_opts *opts, | 90 | struct perf_target *target, |
84 | const char *argv[]); | 91 | const char *argv[], bool pipe_output, |
92 | bool want_signal); | ||
85 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 93 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
86 | 94 | ||
87 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 95 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
@@ -135,4 +143,25 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | |||
135 | } | 143 | } |
136 | 144 | ||
137 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | 145 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); |
146 | |||
147 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
148 | { | ||
149 | struct perf_event_mmap_page *pc = mm->base; | ||
150 | int head = pc->data_head; | ||
151 | rmb(); | ||
152 | return head; | ||
153 | } | ||
154 | |||
155 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
156 | unsigned long tail) | ||
157 | { | ||
158 | struct perf_event_mmap_page *pc = md->base; | ||
159 | |||
160 | /* | ||
161 | * ensure all reads are done before we write the tail out. | ||
162 | */ | ||
163 | /* mb(); */ | ||
164 | pc->data_tail = tail; | ||
165 | } | ||
166 | |||
138 | #endif /* __PERF_EVLIST_H */ | 167 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1b16dd1edc8e..63b6f8c8edf2 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -10,7 +10,7 @@ | |||
10 | #include <byteswap.h> | 10 | #include <byteswap.h> |
11 | #include <linux/bitops.h> | 11 | #include <linux/bitops.h> |
12 | #include "asm/bug.h" | 12 | #include "asm/bug.h" |
13 | #include "debugfs.h" | 13 | #include <lk/debugfs.h> |
14 | #include "event-parse.h" | 14 | #include "event-parse.h" |
15 | #include "evsel.h" | 15 | #include "evsel.h" |
16 | #include "evlist.h" | 16 | #include "evlist.h" |
@@ -22,6 +22,11 @@ | |||
22 | #include <linux/perf_event.h> | 22 | #include <linux/perf_event.h> |
23 | #include "perf_regs.h" | 23 | #include "perf_regs.h" |
24 | 24 | ||
25 | static struct { | ||
26 | bool sample_id_all; | ||
27 | bool exclude_guest; | ||
28 | } perf_missing_features; | ||
29 | |||
25 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 30 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
26 | 31 | ||
27 | static int __perf_evsel__sample_size(u64 sample_type) | 32 | static int __perf_evsel__sample_size(u64 sample_type) |
@@ -50,11 +55,36 @@ void hists__init(struct hists *hists) | |||
50 | pthread_mutex_init(&hists->lock, NULL); | 55 | pthread_mutex_init(&hists->lock, NULL); |
51 | } | 56 | } |
52 | 57 | ||
58 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | ||
59 | enum perf_event_sample_format bit) | ||
60 | { | ||
61 | if (!(evsel->attr.sample_type & bit)) { | ||
62 | evsel->attr.sample_type |= bit; | ||
63 | evsel->sample_size += sizeof(u64); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | ||
68 | enum perf_event_sample_format bit) | ||
69 | { | ||
70 | if (evsel->attr.sample_type & bit) { | ||
71 | evsel->attr.sample_type &= ~bit; | ||
72 | evsel->sample_size -= sizeof(u64); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | void perf_evsel__set_sample_id(struct perf_evsel *evsel) | ||
77 | { | ||
78 | perf_evsel__set_sample_bit(evsel, ID); | ||
79 | evsel->attr.read_format |= PERF_FORMAT_ID; | ||
80 | } | ||
81 | |||
53 | void perf_evsel__init(struct perf_evsel *evsel, | 82 | void perf_evsel__init(struct perf_evsel *evsel, |
54 | struct perf_event_attr *attr, int idx) | 83 | struct perf_event_attr *attr, int idx) |
55 | { | 84 | { |
56 | evsel->idx = idx; | 85 | evsel->idx = idx; |
57 | evsel->attr = *attr; | 86 | evsel->attr = *attr; |
87 | evsel->leader = evsel; | ||
58 | INIT_LIST_HEAD(&evsel->node); | 88 | INIT_LIST_HEAD(&evsel->node); |
59 | hists__init(&evsel->hists); | 89 | hists__init(&evsel->hists); |
60 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 90 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
@@ -404,6 +434,31 @@ const char *perf_evsel__name(struct perf_evsel *evsel) | |||
404 | return evsel->name ?: "unknown"; | 434 | return evsel->name ?: "unknown"; |
405 | } | 435 | } |
406 | 436 | ||
437 | const char *perf_evsel__group_name(struct perf_evsel *evsel) | ||
438 | { | ||
439 | return evsel->group_name ?: "anon group"; | ||
440 | } | ||
441 | |||
442 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | ||
443 | { | ||
444 | int ret; | ||
445 | struct perf_evsel *pos; | ||
446 | const char *group_name = perf_evsel__group_name(evsel); | ||
447 | |||
448 | ret = scnprintf(buf, size, "%s", group_name); | ||
449 | |||
450 | ret += scnprintf(buf + ret, size - ret, " { %s", | ||
451 | perf_evsel__name(evsel)); | ||
452 | |||
453 | for_each_group_member(pos, evsel) | ||
454 | ret += scnprintf(buf + ret, size - ret, ", %s", | ||
455 | perf_evsel__name(pos)); | ||
456 | |||
457 | ret += scnprintf(buf + ret, size - ret, " }"); | ||
458 | |||
459 | return ret; | ||
460 | } | ||
461 | |||
407 | /* | 462 | /* |
408 | * The enable_on_exec/disabled value strategy: | 463 | * The enable_on_exec/disabled value strategy: |
409 | * | 464 | * |
@@ -438,13 +493,11 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
438 | struct perf_event_attr *attr = &evsel->attr; | 493 | struct perf_event_attr *attr = &evsel->attr; |
439 | int track = !evsel->idx; /* only the first counter needs these */ | 494 | int track = !evsel->idx; /* only the first counter needs these */ |
440 | 495 | ||
441 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | 496 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
442 | attr->inherit = !opts->no_inherit; | 497 | attr->inherit = !opts->no_inherit; |
443 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
444 | PERF_FORMAT_TOTAL_TIME_RUNNING | | ||
445 | PERF_FORMAT_ID; | ||
446 | 498 | ||
447 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 499 | perf_evsel__set_sample_bit(evsel, IP); |
500 | perf_evsel__set_sample_bit(evsel, TID); | ||
448 | 501 | ||
449 | /* | 502 | /* |
450 | * We default some events to a 1 default interval. But keep | 503 | * We default some events to a 1 default interval. But keep |
@@ -453,7 +506,7 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
453 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && | 506 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && |
454 | opts->user_interval != ULLONG_MAX)) { | 507 | opts->user_interval != ULLONG_MAX)) { |
455 | if (opts->freq) { | 508 | if (opts->freq) { |
456 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 509 | perf_evsel__set_sample_bit(evsel, PERIOD); |
457 | attr->freq = 1; | 510 | attr->freq = 1; |
458 | attr->sample_freq = opts->freq; | 511 | attr->sample_freq = opts->freq; |
459 | } else { | 512 | } else { |
@@ -468,16 +521,16 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
468 | attr->inherit_stat = 1; | 521 | attr->inherit_stat = 1; |
469 | 522 | ||
470 | if (opts->sample_address) { | 523 | if (opts->sample_address) { |
471 | attr->sample_type |= PERF_SAMPLE_ADDR; | 524 | perf_evsel__set_sample_bit(evsel, ADDR); |
472 | attr->mmap_data = track; | 525 | attr->mmap_data = track; |
473 | } | 526 | } |
474 | 527 | ||
475 | if (opts->call_graph) { | 528 | if (opts->call_graph) { |
476 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 529 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
477 | 530 | ||
478 | if (opts->call_graph == CALLCHAIN_DWARF) { | 531 | if (opts->call_graph == CALLCHAIN_DWARF) { |
479 | attr->sample_type |= PERF_SAMPLE_REGS_USER | | 532 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
480 | PERF_SAMPLE_STACK_USER; | 533 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
481 | attr->sample_regs_user = PERF_REGS_MASK; | 534 | attr->sample_regs_user = PERF_REGS_MASK; |
482 | attr->sample_stack_user = opts->stack_dump_size; | 535 | attr->sample_stack_user = opts->stack_dump_size; |
483 | attr->exclude_callchain_user = 1; | 536 | attr->exclude_callchain_user = 1; |
@@ -485,31 +538,37 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
485 | } | 538 | } |
486 | 539 | ||
487 | if (perf_target__has_cpu(&opts->target)) | 540 | if (perf_target__has_cpu(&opts->target)) |
488 | attr->sample_type |= PERF_SAMPLE_CPU; | 541 | perf_evsel__set_sample_bit(evsel, CPU); |
489 | 542 | ||
490 | if (opts->period) | 543 | if (opts->period) |
491 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 544 | perf_evsel__set_sample_bit(evsel, PERIOD); |
492 | 545 | ||
493 | if (!opts->sample_id_all_missing && | 546 | if (!perf_missing_features.sample_id_all && |
494 | (opts->sample_time || !opts->no_inherit || | 547 | (opts->sample_time || !opts->no_inherit || |
495 | perf_target__has_cpu(&opts->target))) | 548 | perf_target__has_cpu(&opts->target))) |
496 | attr->sample_type |= PERF_SAMPLE_TIME; | 549 | perf_evsel__set_sample_bit(evsel, TIME); |
497 | 550 | ||
498 | if (opts->raw_samples) { | 551 | if (opts->raw_samples) { |
499 | attr->sample_type |= PERF_SAMPLE_TIME; | 552 | perf_evsel__set_sample_bit(evsel, TIME); |
500 | attr->sample_type |= PERF_SAMPLE_RAW; | 553 | perf_evsel__set_sample_bit(evsel, RAW); |
501 | attr->sample_type |= PERF_SAMPLE_CPU; | 554 | perf_evsel__set_sample_bit(evsel, CPU); |
502 | } | 555 | } |
503 | 556 | ||
557 | if (opts->sample_address) | ||
558 | attr->sample_type |= PERF_SAMPLE_DATA_SRC; | ||
559 | |||
504 | if (opts->no_delay) { | 560 | if (opts->no_delay) { |
505 | attr->watermark = 0; | 561 | attr->watermark = 0; |
506 | attr->wakeup_events = 1; | 562 | attr->wakeup_events = 1; |
507 | } | 563 | } |
508 | if (opts->branch_stack) { | 564 | if (opts->branch_stack) { |
509 | attr->sample_type |= PERF_SAMPLE_BRANCH_STACK; | 565 | perf_evsel__set_sample_bit(evsel, BRANCH_STACK); |
510 | attr->branch_sample_type = opts->branch_stack; | 566 | attr->branch_sample_type = opts->branch_stack; |
511 | } | 567 | } |
512 | 568 | ||
569 | if (opts->sample_weight) | ||
570 | attr->sample_type |= PERF_SAMPLE_WEIGHT; | ||
571 | |||
513 | attr->mmap = track; | 572 | attr->mmap = track; |
514 | attr->comm = track; | 573 | attr->comm = track; |
515 | 574 | ||
@@ -519,14 +578,14 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
519 | * Disabling only independent events or group leaders, | 578 | * Disabling only independent events or group leaders, |
520 | * keeping group members enabled. | 579 | * keeping group members enabled. |
521 | */ | 580 | */ |
522 | if (!perf_evsel__is_group_member(evsel)) | 581 | if (perf_evsel__is_group_leader(evsel)) |
523 | attr->disabled = 1; | 582 | attr->disabled = 1; |
524 | 583 | ||
525 | /* | 584 | /* |
526 | * Setting enable_on_exec for independent events and | 585 | * Setting enable_on_exec for independent events and |
527 | * group leaders for traced executed by perf. | 586 | * group leaders for traced executed by perf. |
528 | */ | 587 | */ |
529 | if (perf_target__none(&opts->target) && !perf_evsel__is_group_member(evsel)) | 588 | if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) |
530 | attr->enable_on_exec = 1; | 589 | attr->enable_on_exec = 1; |
531 | } | 590 | } |
532 | 591 | ||
@@ -580,6 +639,12 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
580 | return 0; | 639 | return 0; |
581 | } | 640 | } |
582 | 641 | ||
642 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus) | ||
643 | { | ||
644 | memset(evsel->counts, 0, (sizeof(*evsel->counts) + | ||
645 | (ncpus * sizeof(struct perf_counts_values)))); | ||
646 | } | ||
647 | |||
583 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 648 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
584 | { | 649 | { |
585 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 650 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -612,12 +677,16 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
612 | } | 677 | } |
613 | } | 678 | } |
614 | 679 | ||
680 | void perf_evsel__free_counts(struct perf_evsel *evsel) | ||
681 | { | ||
682 | free(evsel->counts); | ||
683 | } | ||
684 | |||
615 | void perf_evsel__exit(struct perf_evsel *evsel) | 685 | void perf_evsel__exit(struct perf_evsel *evsel) |
616 | { | 686 | { |
617 | assert(list_empty(&evsel->node)); | 687 | assert(list_empty(&evsel->node)); |
618 | xyarray__delete(evsel->fd); | 688 | perf_evsel__free_fd(evsel); |
619 | xyarray__delete(evsel->sample_id); | 689 | perf_evsel__free_id(evsel); |
620 | free(evsel->id); | ||
621 | } | 690 | } |
622 | 691 | ||
623 | void perf_evsel__delete(struct perf_evsel *evsel) | 692 | void perf_evsel__delete(struct perf_evsel *evsel) |
@@ -631,6 +700,28 @@ void perf_evsel__delete(struct perf_evsel *evsel) | |||
631 | free(evsel); | 700 | free(evsel); |
632 | } | 701 | } |
633 | 702 | ||
703 | static inline void compute_deltas(struct perf_evsel *evsel, | ||
704 | int cpu, | ||
705 | struct perf_counts_values *count) | ||
706 | { | ||
707 | struct perf_counts_values tmp; | ||
708 | |||
709 | if (!evsel->prev_raw_counts) | ||
710 | return; | ||
711 | |||
712 | if (cpu == -1) { | ||
713 | tmp = evsel->prev_raw_counts->aggr; | ||
714 | evsel->prev_raw_counts->aggr = *count; | ||
715 | } else { | ||
716 | tmp = evsel->prev_raw_counts->cpu[cpu]; | ||
717 | evsel->prev_raw_counts->cpu[cpu] = *count; | ||
718 | } | ||
719 | |||
720 | count->val = count->val - tmp.val; | ||
721 | count->ena = count->ena - tmp.ena; | ||
722 | count->run = count->run - tmp.run; | ||
723 | } | ||
724 | |||
634 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 725 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
635 | int cpu, int thread, bool scale) | 726 | int cpu, int thread, bool scale) |
636 | { | 727 | { |
@@ -646,6 +737,8 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | |||
646 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | 737 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) |
647 | return -errno; | 738 | return -errno; |
648 | 739 | ||
740 | compute_deltas(evsel, cpu, &count); | ||
741 | |||
649 | if (scale) { | 742 | if (scale) { |
650 | if (count.run == 0) | 743 | if (count.run == 0) |
651 | count.val = 0; | 744 | count.val = 0; |
@@ -684,6 +777,8 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
684 | } | 777 | } |
685 | } | 778 | } |
686 | 779 | ||
780 | compute_deltas(evsel, -1, aggr); | ||
781 | |||
687 | evsel->counts->scaled = 0; | 782 | evsel->counts->scaled = 0; |
688 | if (scale) { | 783 | if (scale) { |
689 | if (aggr->run == 0) { | 784 | if (aggr->run == 0) { |
@@ -707,7 +802,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | |||
707 | struct perf_evsel *leader = evsel->leader; | 802 | struct perf_evsel *leader = evsel->leader; |
708 | int fd; | 803 | int fd; |
709 | 804 | ||
710 | if (!perf_evsel__is_group_member(evsel)) | 805 | if (perf_evsel__is_group_leader(evsel)) |
711 | return -1; | 806 | return -1; |
712 | 807 | ||
713 | /* | 808 | /* |
@@ -738,6 +833,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
738 | pid = evsel->cgrp->fd; | 833 | pid = evsel->cgrp->fd; |
739 | } | 834 | } |
740 | 835 | ||
836 | fallback_missing_features: | ||
837 | if (perf_missing_features.exclude_guest) | ||
838 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | ||
839 | retry_sample_id: | ||
840 | if (perf_missing_features.sample_id_all) | ||
841 | evsel->attr.sample_id_all = 0; | ||
842 | |||
741 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 843 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
742 | 844 | ||
743 | for (thread = 0; thread < threads->nr; thread++) { | 845 | for (thread = 0; thread < threads->nr; thread++) { |
@@ -754,13 +856,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
754 | group_fd, flags); | 856 | group_fd, flags); |
755 | if (FD(evsel, cpu, thread) < 0) { | 857 | if (FD(evsel, cpu, thread) < 0) { |
756 | err = -errno; | 858 | err = -errno; |
757 | goto out_close; | 859 | goto try_fallback; |
758 | } | 860 | } |
759 | } | 861 | } |
760 | } | 862 | } |
761 | 863 | ||
762 | return 0; | 864 | return 0; |
763 | 865 | ||
866 | try_fallback: | ||
867 | if (err != -EINVAL || cpu > 0 || thread > 0) | ||
868 | goto out_close; | ||
869 | |||
870 | if (!perf_missing_features.exclude_guest && | ||
871 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
872 | perf_missing_features.exclude_guest = true; | ||
873 | goto fallback_missing_features; | ||
874 | } else if (!perf_missing_features.sample_id_all) { | ||
875 | perf_missing_features.sample_id_all = true; | ||
876 | goto retry_sample_id; | ||
877 | } | ||
878 | |||
764 | out_close: | 879 | out_close: |
765 | do { | 880 | do { |
766 | while (--thread >= 0) { | 881 | while (--thread >= 0) { |
@@ -908,6 +1023,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
908 | data->cpu = data->pid = data->tid = -1; | 1023 | data->cpu = data->pid = data->tid = -1; |
909 | data->stream_id = data->id = data->time = -1ULL; | 1024 | data->stream_id = data->id = data->time = -1ULL; |
910 | data->period = 1; | 1025 | data->period = 1; |
1026 | data->weight = 0; | ||
911 | 1027 | ||
912 | if (event->header.type != PERF_RECORD_SAMPLE) { | 1028 | if (event->header.type != PERF_RECORD_SAMPLE) { |
913 | if (!evsel->attr.sample_id_all) | 1029 | if (!evsel->attr.sample_id_all) |
@@ -1058,6 +1174,18 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1058 | } | 1174 | } |
1059 | } | 1175 | } |
1060 | 1176 | ||
1177 | data->weight = 0; | ||
1178 | if (type & PERF_SAMPLE_WEIGHT) { | ||
1179 | data->weight = *array; | ||
1180 | array++; | ||
1181 | } | ||
1182 | |||
1183 | data->data_src = PERF_MEM_DATA_SRC_NONE; | ||
1184 | if (type & PERF_SAMPLE_DATA_SRC) { | ||
1185 | data->data_src = *array; | ||
1186 | array++; | ||
1187 | } | ||
1188 | |||
1061 | return 0; | 1189 | return 0; |
1062 | } | 1190 | } |
1063 | 1191 | ||
@@ -1205,3 +1333,225 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | |||
1205 | 1333 | ||
1206 | return 0; | 1334 | return 0; |
1207 | } | 1335 | } |
1336 | |||
1337 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) | ||
1338 | { | ||
1339 | va_list args; | ||
1340 | int ret = 0; | ||
1341 | |||
1342 | if (!*first) { | ||
1343 | ret += fprintf(fp, ","); | ||
1344 | } else { | ||
1345 | ret += fprintf(fp, ":"); | ||
1346 | *first = false; | ||
1347 | } | ||
1348 | |||
1349 | va_start(args, fmt); | ||
1350 | ret += vfprintf(fp, fmt, args); | ||
1351 | va_end(args); | ||
1352 | return ret; | ||
1353 | } | ||
1354 | |||
1355 | static int __if_fprintf(FILE *fp, bool *first, const char *field, u64 value) | ||
1356 | { | ||
1357 | if (value == 0) | ||
1358 | return 0; | ||
1359 | |||
1360 | return comma_fprintf(fp, first, " %s: %" PRIu64, field, value); | ||
1361 | } | ||
1362 | |||
1363 | #define if_print(field) printed += __if_fprintf(fp, &first, #field, evsel->attr.field) | ||
1364 | |||
1365 | struct bit_names { | ||
1366 | int bit; | ||
1367 | const char *name; | ||
1368 | }; | ||
1369 | |||
1370 | static int bits__fprintf(FILE *fp, const char *field, u64 value, | ||
1371 | struct bit_names *bits, bool *first) | ||
1372 | { | ||
1373 | int i = 0, printed = comma_fprintf(fp, first, " %s: ", field); | ||
1374 | bool first_bit = true; | ||
1375 | |||
1376 | do { | ||
1377 | if (value & bits[i].bit) { | ||
1378 | printed += fprintf(fp, "%s%s", first_bit ? "" : "|", bits[i].name); | ||
1379 | first_bit = false; | ||
1380 | } | ||
1381 | } while (bits[++i].name != NULL); | ||
1382 | |||
1383 | return printed; | ||
1384 | } | ||
1385 | |||
1386 | static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | ||
1387 | { | ||
1388 | #define bit_name(n) { PERF_SAMPLE_##n, #n } | ||
1389 | struct bit_names bits[] = { | ||
1390 | bit_name(IP), bit_name(TID), bit_name(TIME), bit_name(ADDR), | ||
1391 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | ||
1392 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | ||
1393 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | ||
1394 | { .name = NULL, } | ||
1395 | }; | ||
1396 | #undef bit_name | ||
1397 | return bits__fprintf(fp, "sample_type", value, bits, first); | ||
1398 | } | ||
1399 | |||
1400 | static int read_format__fprintf(FILE *fp, bool *first, u64 value) | ||
1401 | { | ||
1402 | #define bit_name(n) { PERF_FORMAT_##n, #n } | ||
1403 | struct bit_names bits[] = { | ||
1404 | bit_name(TOTAL_TIME_ENABLED), bit_name(TOTAL_TIME_RUNNING), | ||
1405 | bit_name(ID), bit_name(GROUP), | ||
1406 | { .name = NULL, } | ||
1407 | }; | ||
1408 | #undef bit_name | ||
1409 | return bits__fprintf(fp, "read_format", value, bits, first); | ||
1410 | } | ||
1411 | |||
1412 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
1413 | struct perf_attr_details *details, FILE *fp) | ||
1414 | { | ||
1415 | bool first = true; | ||
1416 | int printed = 0; | ||
1417 | |||
1418 | if (details->event_group) { | ||
1419 | struct perf_evsel *pos; | ||
1420 | |||
1421 | if (!perf_evsel__is_group_leader(evsel)) | ||
1422 | return 0; | ||
1423 | |||
1424 | if (evsel->nr_members > 1) | ||
1425 | printed += fprintf(fp, "%s{", evsel->group_name ?: ""); | ||
1426 | |||
1427 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
1428 | for_each_group_member(pos, evsel) | ||
1429 | printed += fprintf(fp, ",%s", perf_evsel__name(pos)); | ||
1430 | |||
1431 | if (evsel->nr_members > 1) | ||
1432 | printed += fprintf(fp, "}"); | ||
1433 | goto out; | ||
1434 | } | ||
1435 | |||
1436 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
1437 | |||
1438 | if (details->verbose || details->freq) { | ||
1439 | printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64, | ||
1440 | (u64)evsel->attr.sample_freq); | ||
1441 | } | ||
1442 | |||
1443 | if (details->verbose) { | ||
1444 | if_print(type); | ||
1445 | if_print(config); | ||
1446 | if_print(config1); | ||
1447 | if_print(config2); | ||
1448 | if_print(size); | ||
1449 | printed += sample_type__fprintf(fp, &first, evsel->attr.sample_type); | ||
1450 | if (evsel->attr.read_format) | ||
1451 | printed += read_format__fprintf(fp, &first, evsel->attr.read_format); | ||
1452 | if_print(disabled); | ||
1453 | if_print(inherit); | ||
1454 | if_print(pinned); | ||
1455 | if_print(exclusive); | ||
1456 | if_print(exclude_user); | ||
1457 | if_print(exclude_kernel); | ||
1458 | if_print(exclude_hv); | ||
1459 | if_print(exclude_idle); | ||
1460 | if_print(mmap); | ||
1461 | if_print(comm); | ||
1462 | if_print(freq); | ||
1463 | if_print(inherit_stat); | ||
1464 | if_print(enable_on_exec); | ||
1465 | if_print(task); | ||
1466 | if_print(watermark); | ||
1467 | if_print(precise_ip); | ||
1468 | if_print(mmap_data); | ||
1469 | if_print(sample_id_all); | ||
1470 | if_print(exclude_host); | ||
1471 | if_print(exclude_guest); | ||
1472 | if_print(__reserved_1); | ||
1473 | if_print(wakeup_events); | ||
1474 | if_print(bp_type); | ||
1475 | if_print(branch_sample_type); | ||
1476 | } | ||
1477 | out: | ||
1478 | fputc('\n', fp); | ||
1479 | return ++printed; | ||
1480 | } | ||
1481 | |||
1482 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | ||
1483 | char *msg, size_t msgsize) | ||
1484 | { | ||
1485 | if ((err == ENOENT || err == ENXIO) && | ||
1486 | evsel->attr.type == PERF_TYPE_HARDWARE && | ||
1487 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { | ||
1488 | /* | ||
1489 | * If it's cycles then fall back to hrtimer based | ||
1490 | * cpu-clock-tick sw counter, which is always available even if | ||
1491 | * no PMU support. | ||
1492 | * | ||
1493 | * PPC returns ENXIO until 2.6.37 (behavior changed with commit | ||
1494 | * b0a873e). | ||
1495 | */ | ||
1496 | scnprintf(msg, msgsize, "%s", | ||
1497 | "The cycles event is not supported, trying to fall back to cpu-clock-ticks"); | ||
1498 | |||
1499 | evsel->attr.type = PERF_TYPE_SOFTWARE; | ||
1500 | evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; | ||
1501 | |||
1502 | free(evsel->name); | ||
1503 | evsel->name = NULL; | ||
1504 | return true; | ||
1505 | } | ||
1506 | |||
1507 | return false; | ||
1508 | } | ||
1509 | |||
1510 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | ||
1511 | struct perf_target *target, | ||
1512 | int err, char *msg, size_t size) | ||
1513 | { | ||
1514 | switch (err) { | ||
1515 | case EPERM: | ||
1516 | case EACCES: | ||
1517 | return scnprintf(msg, size, | ||
1518 | "You may not have permission to collect %sstats.\n" | ||
1519 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | ||
1520 | " -1 - Not paranoid at all\n" | ||
1521 | " 0 - Disallow raw tracepoint access for unpriv\n" | ||
1522 | " 1 - Disallow cpu events for unpriv\n" | ||
1523 | " 2 - Disallow kernel profiling for unpriv", | ||
1524 | target->system_wide ? "system-wide " : ""); | ||
1525 | case ENOENT: | ||
1526 | return scnprintf(msg, size, "The %s event is not supported.", | ||
1527 | perf_evsel__name(evsel)); | ||
1528 | case EMFILE: | ||
1529 | return scnprintf(msg, size, "%s", | ||
1530 | "Too many events are opened.\n" | ||
1531 | "Try again after reducing the number of events."); | ||
1532 | case ENODEV: | ||
1533 | if (target->cpu_list) | ||
1534 | return scnprintf(msg, size, "%s", | ||
1535 | "No such device - did you specify an out-of-range profile CPU?\n"); | ||
1536 | break; | ||
1537 | case EOPNOTSUPP: | ||
1538 | if (evsel->attr.precise_ip) | ||
1539 | return scnprintf(msg, size, "%s", | ||
1540 | "\'precise\' request may not be supported. Try removing 'p' modifier."); | ||
1541 | #if defined(__i386__) || defined(__x86_64__) | ||
1542 | if (evsel->attr.type == PERF_TYPE_HARDWARE) | ||
1543 | return scnprintf(msg, size, "%s", | ||
1544 | "No hardware sampling interrupt available.\n" | ||
1545 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); | ||
1546 | #endif | ||
1547 | break; | ||
1548 | default: | ||
1549 | break; | ||
1550 | } | ||
1551 | |||
1552 | return scnprintf(msg, size, | ||
1553 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | ||
1554 | "/bin/dmesg may provide additional information.\n" | ||
1555 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | ||
1556 | err, strerror(err), perf_evsel__name(evsel)); | ||
1557 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 3d2b8017438c..3f156ccc1acb 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -9,6 +9,7 @@ | |||
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 | 13 | ||
13 | struct perf_counts_values { | 14 | struct perf_counts_values { |
14 | union { | 15 | union { |
@@ -53,6 +54,7 @@ struct perf_evsel { | |||
53 | struct xyarray *sample_id; | 54 | struct xyarray *sample_id; |
54 | u64 *id; | 55 | u64 *id; |
55 | struct perf_counts *counts; | 56 | struct perf_counts *counts; |
57 | struct perf_counts *prev_raw_counts; | ||
56 | int idx; | 58 | int idx; |
57 | u32 ids; | 59 | u32 ids; |
58 | struct hists hists; | 60 | struct hists hists; |
@@ -73,10 +75,13 @@ struct perf_evsel { | |||
73 | bool needs_swap; | 75 | bool needs_swap; |
74 | /* parse modifier helper */ | 76 | /* parse modifier helper */ |
75 | int exclude_GH; | 77 | int exclude_GH; |
78 | int nr_members; | ||
76 | struct perf_evsel *leader; | 79 | struct perf_evsel *leader; |
77 | char *group_name; | 80 | char *group_name; |
78 | }; | 81 | }; |
79 | 82 | ||
83 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) | ||
84 | |||
80 | struct cpu_map; | 85 | struct cpu_map; |
81 | struct thread_map; | 86 | struct thread_map; |
82 | struct perf_evlist; | 87 | struct perf_evlist; |
@@ -110,14 +115,31 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; | |||
110 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | 115 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, |
111 | char *bf, size_t size); | 116 | char *bf, size_t size); |
112 | const char *perf_evsel__name(struct perf_evsel *evsel); | 117 | const char *perf_evsel__name(struct perf_evsel *evsel); |
118 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | ||
119 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | ||
113 | 120 | ||
114 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 121 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
115 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 122 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
116 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 123 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
124 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); | ||
117 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 125 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
118 | void perf_evsel__free_id(struct perf_evsel *evsel); | 126 | void perf_evsel__free_id(struct perf_evsel *evsel); |
127 | void perf_evsel__free_counts(struct perf_evsel *evsel); | ||
119 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 128 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
120 | 129 | ||
130 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | ||
131 | enum perf_event_sample_format bit); | ||
132 | void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | ||
133 | enum perf_event_sample_format bit); | ||
134 | |||
135 | #define perf_evsel__set_sample_bit(evsel, bit) \ | ||
136 | __perf_evsel__set_sample_bit(evsel, PERF_SAMPLE_##bit) | ||
137 | |||
138 | #define perf_evsel__reset_sample_bit(evsel, bit) \ | ||
139 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) | ||
140 | |||
141 | void perf_evsel__set_sample_id(struct perf_evsel *evsel); | ||
142 | |||
121 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 143 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, |
122 | const char *filter); | 144 | const char *filter); |
123 | 145 | ||
@@ -226,8 +248,57 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
226 | return list_entry(evsel->node.next, struct perf_evsel, node); | 248 | return list_entry(evsel->node.next, struct perf_evsel, node); |
227 | } | 249 | } |
228 | 250 | ||
229 | static inline bool perf_evsel__is_group_member(const struct perf_evsel *evsel) | 251 | /** |
252 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event | ||
253 | * | ||
254 | * @evsel - evsel selector to be tested | ||
255 | * | ||
256 | * Return %true if @evsel is a group leader or a stand-alone event | ||
257 | */ | ||
258 | static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel) | ||
259 | { | ||
260 | return evsel->leader == evsel; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * perf_evsel__is_group_event - Return whether given evsel is a group event | ||
265 | * | ||
266 | * @evsel - evsel selector to be tested | ||
267 | * | ||
268 | * Return %true iff event group view is enabled and @evsel is a actual group | ||
269 | * leader which has other members in the group | ||
270 | */ | ||
271 | static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel) | ||
272 | { | ||
273 | if (!symbol_conf.event_group) | ||
274 | return false; | ||
275 | |||
276 | return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1; | ||
277 | } | ||
278 | |||
279 | struct perf_attr_details { | ||
280 | bool freq; | ||
281 | bool verbose; | ||
282 | bool event_group; | ||
283 | }; | ||
284 | |||
285 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
286 | struct perf_attr_details *details, FILE *fp); | ||
287 | |||
288 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | ||
289 | char *msg, size_t msgsize); | ||
290 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | ||
291 | struct perf_target *target, | ||
292 | int err, char *msg, size_t size); | ||
293 | |||
294 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) | ||
230 | { | 295 | { |
231 | return evsel->leader != NULL; | 296 | return evsel->idx - evsel->leader->idx; |
232 | } | 297 | } |
298 | |||
299 | #define for_each_group_member(_evsel, _leader) \ | ||
300 | for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \ | ||
301 | (_evsel) && (_evsel)->leader == (_leader); \ | ||
302 | (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node)) | ||
303 | |||
233 | #endif /* __PERF_EVSEL_H */ | 304 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b7da4634a047..738d3b8d9745 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,5 +1,3 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include "util.h" | 1 | #include "util.h" |
4 | #include <sys/types.h> | 2 | #include <sys/types.h> |
5 | #include <byteswap.h> | 3 | #include <byteswap.h> |
@@ -148,7 +146,7 @@ static char *do_read_string(int fd, struct perf_header *ph) | |||
148 | u32 len; | 146 | u32 len; |
149 | char *buf; | 147 | char *buf; |
150 | 148 | ||
151 | sz = read(fd, &len, sizeof(len)); | 149 | sz = readn(fd, &len, sizeof(len)); |
152 | if (sz < (ssize_t)sizeof(len)) | 150 | if (sz < (ssize_t)sizeof(len)) |
153 | return NULL; | 151 | return NULL; |
154 | 152 | ||
@@ -159,7 +157,7 @@ static char *do_read_string(int fd, struct perf_header *ph) | |||
159 | if (!buf) | 157 | if (!buf) |
160 | return NULL; | 158 | return NULL; |
161 | 159 | ||
162 | ret = read(fd, buf, len); | 160 | ret = readn(fd, buf, len); |
163 | if (ret == (ssize_t)len) { | 161 | if (ret == (ssize_t)len) { |
164 | /* | 162 | /* |
165 | * strings are padded by zeroes | 163 | * strings are padded by zeroes |
@@ -287,12 +285,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) | |||
287 | struct perf_session *session = container_of(header, | 285 | struct perf_session *session = container_of(header, |
288 | struct perf_session, header); | 286 | struct perf_session, header); |
289 | struct rb_node *nd; | 287 | struct rb_node *nd; |
290 | int err = machine__write_buildid_table(&session->host_machine, fd); | 288 | int err = machine__write_buildid_table(&session->machines.host, fd); |
291 | 289 | ||
292 | if (err) | 290 | if (err) |
293 | return err; | 291 | return err; |
294 | 292 | ||
295 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 293 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
296 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 294 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
297 | err = machine__write_buildid_table(pos, fd); | 295 | err = machine__write_buildid_table(pos, fd); |
298 | if (err) | 296 | if (err) |
@@ -313,7 +311,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
313 | if (is_kallsyms) { | 311 | if (is_kallsyms) { |
314 | if (symbol_conf.kptr_restrict) { | 312 | if (symbol_conf.kptr_restrict) { |
315 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | 313 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); |
316 | return 0; | 314 | err = 0; |
315 | goto out_free; | ||
317 | } | 316 | } |
318 | realname = (char *) name; | 317 | realname = (char *) name; |
319 | } else | 318 | } else |
@@ -448,9 +447,9 @@ static int perf_session__cache_build_ids(struct perf_session *session) | |||
448 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 447 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
449 | return -1; | 448 | return -1; |
450 | 449 | ||
451 | ret = machine__cache_build_ids(&session->host_machine, debugdir); | 450 | ret = machine__cache_build_ids(&session->machines.host, debugdir); |
452 | 451 | ||
453 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 452 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
454 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 453 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
455 | ret |= machine__cache_build_ids(pos, debugdir); | 454 | ret |= machine__cache_build_ids(pos, debugdir); |
456 | } | 455 | } |
@@ -467,9 +466,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits) | |||
467 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | 466 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) |
468 | { | 467 | { |
469 | struct rb_node *nd; | 468 | struct rb_node *nd; |
470 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); | 469 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); |
471 | 470 | ||
472 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 471 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
473 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 472 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
474 | ret |= machine__read_build_ids(pos, with_hits); | 473 | ret |= machine__read_build_ids(pos, with_hits); |
475 | } | 474 | } |
@@ -954,6 +953,7 @@ static int write_topo_node(int fd, int node) | |||
954 | } | 953 | } |
955 | 954 | ||
956 | fclose(fp); | 955 | fclose(fp); |
956 | fp = NULL; | ||
957 | 957 | ||
958 | ret = do_write(fd, &mem_total, sizeof(u64)); | 958 | ret = do_write(fd, &mem_total, sizeof(u64)); |
959 | if (ret) | 959 | if (ret) |
@@ -980,7 +980,8 @@ static int write_topo_node(int fd, int node) | |||
980 | ret = do_write_string(fd, buf); | 980 | ret = do_write_string(fd, buf); |
981 | done: | 981 | done: |
982 | free(buf); | 982 | free(buf); |
983 | fclose(fp); | 983 | if (fp) |
984 | fclose(fp); | ||
984 | return ret; | 985 | return ret; |
985 | } | 986 | } |
986 | 987 | ||
@@ -1051,16 +1052,25 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
1051 | struct perf_pmu *pmu = NULL; | 1052 | struct perf_pmu *pmu = NULL; |
1052 | off_t offset = lseek(fd, 0, SEEK_CUR); | 1053 | off_t offset = lseek(fd, 0, SEEK_CUR); |
1053 | __u32 pmu_num = 0; | 1054 | __u32 pmu_num = 0; |
1055 | int ret; | ||
1054 | 1056 | ||
1055 | /* write real pmu_num later */ | 1057 | /* write real pmu_num later */ |
1056 | do_write(fd, &pmu_num, sizeof(pmu_num)); | 1058 | ret = do_write(fd, &pmu_num, sizeof(pmu_num)); |
1059 | if (ret < 0) | ||
1060 | return ret; | ||
1057 | 1061 | ||
1058 | while ((pmu = perf_pmu__scan(pmu))) { | 1062 | while ((pmu = perf_pmu__scan(pmu))) { |
1059 | if (!pmu->name) | 1063 | if (!pmu->name) |
1060 | continue; | 1064 | continue; |
1061 | pmu_num++; | 1065 | pmu_num++; |
1062 | do_write(fd, &pmu->type, sizeof(pmu->type)); | 1066 | |
1063 | do_write_string(fd, pmu->name); | 1067 | ret = do_write(fd, &pmu->type, sizeof(pmu->type)); |
1068 | if (ret < 0) | ||
1069 | return ret; | ||
1070 | |||
1071 | ret = do_write_string(fd, pmu->name); | ||
1072 | if (ret < 0) | ||
1073 | return ret; | ||
1064 | } | 1074 | } |
1065 | 1075 | ||
1066 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { | 1076 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { |
@@ -1073,6 +1083,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
1073 | } | 1083 | } |
1074 | 1084 | ||
1075 | /* | 1085 | /* |
1086 | * File format: | ||
1087 | * | ||
1088 | * struct group_descs { | ||
1089 | * u32 nr_groups; | ||
1090 | * struct group_desc { | ||
1091 | * char name[]; | ||
1092 | * u32 leader_idx; | ||
1093 | * u32 nr_members; | ||
1094 | * }[nr_groups]; | ||
1095 | * }; | ||
1096 | */ | ||
1097 | static int write_group_desc(int fd, struct perf_header *h __maybe_unused, | ||
1098 | struct perf_evlist *evlist) | ||
1099 | { | ||
1100 | u32 nr_groups = evlist->nr_groups; | ||
1101 | struct perf_evsel *evsel; | ||
1102 | int ret; | ||
1103 | |||
1104 | ret = do_write(fd, &nr_groups, sizeof(nr_groups)); | ||
1105 | if (ret < 0) | ||
1106 | return ret; | ||
1107 | |||
1108 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1109 | if (perf_evsel__is_group_leader(evsel) && | ||
1110 | evsel->nr_members > 1) { | ||
1111 | const char *name = evsel->group_name ?: "{anon_group}"; | ||
1112 | u32 leader_idx = evsel->idx; | ||
1113 | u32 nr_members = evsel->nr_members; | ||
1114 | |||
1115 | ret = do_write_string(fd, name); | ||
1116 | if (ret < 0) | ||
1117 | return ret; | ||
1118 | |||
1119 | ret = do_write(fd, &leader_idx, sizeof(leader_idx)); | ||
1120 | if (ret < 0) | ||
1121 | return ret; | ||
1122 | |||
1123 | ret = do_write(fd, &nr_members, sizeof(nr_members)); | ||
1124 | if (ret < 0) | ||
1125 | return ret; | ||
1126 | } | ||
1127 | } | ||
1128 | return 0; | ||
1129 | } | ||
1130 | |||
1131 | /* | ||
1076 | * default get_cpuid(): nothing gets recorded | 1132 | * default get_cpuid(): nothing gets recorded |
1077 | * actual implementation must be in arch/$(ARCH)/util/header.c | 1133 | * actual implementation must be in arch/$(ARCH)/util/header.c |
1078 | */ | 1134 | */ |
@@ -1209,14 +1265,14 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1209 | size_t msz; | 1265 | size_t msz; |
1210 | 1266 | ||
1211 | /* number of events */ | 1267 | /* number of events */ |
1212 | ret = read(fd, &nre, sizeof(nre)); | 1268 | ret = readn(fd, &nre, sizeof(nre)); |
1213 | if (ret != (ssize_t)sizeof(nre)) | 1269 | if (ret != (ssize_t)sizeof(nre)) |
1214 | goto error; | 1270 | goto error; |
1215 | 1271 | ||
1216 | if (ph->needs_swap) | 1272 | if (ph->needs_swap) |
1217 | nre = bswap_32(nre); | 1273 | nre = bswap_32(nre); |
1218 | 1274 | ||
1219 | ret = read(fd, &sz, sizeof(sz)); | 1275 | ret = readn(fd, &sz, sizeof(sz)); |
1220 | if (ret != (ssize_t)sizeof(sz)) | 1276 | if (ret != (ssize_t)sizeof(sz)) |
1221 | goto error; | 1277 | goto error; |
1222 | 1278 | ||
@@ -1244,7 +1300,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1244 | * must read entire on-file attr struct to | 1300 | * must read entire on-file attr struct to |
1245 | * sync up with layout. | 1301 | * sync up with layout. |
1246 | */ | 1302 | */ |
1247 | ret = read(fd, buf, sz); | 1303 | ret = readn(fd, buf, sz); |
1248 | if (ret != (ssize_t)sz) | 1304 | if (ret != (ssize_t)sz) |
1249 | goto error; | 1305 | goto error; |
1250 | 1306 | ||
@@ -1253,7 +1309,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1253 | 1309 | ||
1254 | memcpy(&evsel->attr, buf, msz); | 1310 | memcpy(&evsel->attr, buf, msz); |
1255 | 1311 | ||
1256 | ret = read(fd, &nr, sizeof(nr)); | 1312 | ret = readn(fd, &nr, sizeof(nr)); |
1257 | if (ret != (ssize_t)sizeof(nr)) | 1313 | if (ret != (ssize_t)sizeof(nr)) |
1258 | goto error; | 1314 | goto error; |
1259 | 1315 | ||
@@ -1274,7 +1330,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1274 | evsel->id = id; | 1330 | evsel->id = id; |
1275 | 1331 | ||
1276 | for (j = 0 ; j < nr; j++) { | 1332 | for (j = 0 ; j < nr; j++) { |
1277 | ret = read(fd, id, sizeof(*id)); | 1333 | ret = readn(fd, id, sizeof(*id)); |
1278 | if (ret != (ssize_t)sizeof(*id)) | 1334 | if (ret != (ssize_t)sizeof(*id)) |
1279 | goto error; | 1335 | goto error; |
1280 | if (ph->needs_swap) | 1336 | if (ph->needs_swap) |
@@ -1435,6 +1491,31 @@ error: | |||
1435 | fprintf(fp, "# pmu mappings: unable to read\n"); | 1491 | fprintf(fp, "# pmu mappings: unable to read\n"); |
1436 | } | 1492 | } |
1437 | 1493 | ||
1494 | static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, | ||
1495 | FILE *fp) | ||
1496 | { | ||
1497 | struct perf_session *session; | ||
1498 | struct perf_evsel *evsel; | ||
1499 | u32 nr = 0; | ||
1500 | |||
1501 | session = container_of(ph, struct perf_session, header); | ||
1502 | |||
1503 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
1504 | if (perf_evsel__is_group_leader(evsel) && | ||
1505 | evsel->nr_members > 1) { | ||
1506 | fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", | ||
1507 | perf_evsel__name(evsel)); | ||
1508 | |||
1509 | nr = evsel->nr_members - 1; | ||
1510 | } else if (nr) { | ||
1511 | fprintf(fp, ",%s", perf_evsel__name(evsel)); | ||
1512 | |||
1513 | if (--nr == 0) | ||
1514 | fprintf(fp, "}\n"); | ||
1515 | } | ||
1516 | } | ||
1517 | } | ||
1518 | |||
1438 | static int __event_process_build_id(struct build_id_event *bev, | 1519 | static int __event_process_build_id(struct build_id_event *bev, |
1439 | char *filename, | 1520 | char *filename, |
1440 | struct perf_session *session) | 1521 | struct perf_session *session) |
@@ -1506,14 +1587,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | |||
1506 | while (offset < limit) { | 1587 | while (offset < limit) { |
1507 | ssize_t len; | 1588 | ssize_t len; |
1508 | 1589 | ||
1509 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | 1590 | if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) |
1510 | return -1; | 1591 | return -1; |
1511 | 1592 | ||
1512 | if (header->needs_swap) | 1593 | if (header->needs_swap) |
1513 | perf_event_header__bswap(&old_bev.header); | 1594 | perf_event_header__bswap(&old_bev.header); |
1514 | 1595 | ||
1515 | len = old_bev.header.size - sizeof(old_bev); | 1596 | len = old_bev.header.size - sizeof(old_bev); |
1516 | if (read(input, filename, len) != len) | 1597 | if (readn(input, filename, len) != len) |
1517 | return -1; | 1598 | return -1; |
1518 | 1599 | ||
1519 | bev.header = old_bev.header; | 1600 | bev.header = old_bev.header; |
@@ -1548,14 +1629,14 @@ static int perf_header__read_build_ids(struct perf_header *header, | |||
1548 | while (offset < limit) { | 1629 | while (offset < limit) { |
1549 | ssize_t len; | 1630 | ssize_t len; |
1550 | 1631 | ||
1551 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 1632 | if (readn(input, &bev, sizeof(bev)) != sizeof(bev)) |
1552 | goto out; | 1633 | goto out; |
1553 | 1634 | ||
1554 | if (header->needs_swap) | 1635 | if (header->needs_swap) |
1555 | perf_event_header__bswap(&bev.header); | 1636 | perf_event_header__bswap(&bev.header); |
1556 | 1637 | ||
1557 | len = bev.header.size - sizeof(bev); | 1638 | len = bev.header.size - sizeof(bev); |
1558 | if (read(input, filename, len) != len) | 1639 | if (readn(input, filename, len) != len) |
1559 | goto out; | 1640 | goto out; |
1560 | /* | 1641 | /* |
1561 | * The a1645ce1 changeset: | 1642 | * The a1645ce1 changeset: |
@@ -1589,8 +1670,8 @@ static int process_tracing_data(struct perf_file_section *section __maybe_unused | |||
1589 | struct perf_header *ph __maybe_unused, | 1670 | struct perf_header *ph __maybe_unused, |
1590 | int fd, void *data) | 1671 | int fd, void *data) |
1591 | { | 1672 | { |
1592 | trace_report(fd, data, false); | 1673 | ssize_t ret = trace_report(fd, data, false); |
1593 | return 0; | 1674 | return ret < 0 ? -1 : 0; |
1594 | } | 1675 | } |
1595 | 1676 | ||
1596 | static int process_build_id(struct perf_file_section *section, | 1677 | static int process_build_id(struct perf_file_section *section, |
@@ -1641,7 +1722,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, | |||
1641 | size_t ret; | 1722 | size_t ret; |
1642 | u32 nr; | 1723 | u32 nr; |
1643 | 1724 | ||
1644 | ret = read(fd, &nr, sizeof(nr)); | 1725 | ret = readn(fd, &nr, sizeof(nr)); |
1645 | if (ret != sizeof(nr)) | 1726 | if (ret != sizeof(nr)) |
1646 | return -1; | 1727 | return -1; |
1647 | 1728 | ||
@@ -1650,7 +1731,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, | |||
1650 | 1731 | ||
1651 | ph->env.nr_cpus_online = nr; | 1732 | ph->env.nr_cpus_online = nr; |
1652 | 1733 | ||
1653 | ret = read(fd, &nr, sizeof(nr)); | 1734 | ret = readn(fd, &nr, sizeof(nr)); |
1654 | if (ret != sizeof(nr)) | 1735 | if (ret != sizeof(nr)) |
1655 | return -1; | 1736 | return -1; |
1656 | 1737 | ||
@@ -1684,7 +1765,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused, | |||
1684 | uint64_t mem; | 1765 | uint64_t mem; |
1685 | size_t ret; | 1766 | size_t ret; |
1686 | 1767 | ||
1687 | ret = read(fd, &mem, sizeof(mem)); | 1768 | ret = readn(fd, &mem, sizeof(mem)); |
1688 | if (ret != sizeof(mem)) | 1769 | if (ret != sizeof(mem)) |
1689 | return -1; | 1770 | return -1; |
1690 | 1771 | ||
@@ -1756,7 +1837,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused, | |||
1756 | u32 nr, i; | 1837 | u32 nr, i; |
1757 | struct strbuf sb; | 1838 | struct strbuf sb; |
1758 | 1839 | ||
1759 | ret = read(fd, &nr, sizeof(nr)); | 1840 | ret = readn(fd, &nr, sizeof(nr)); |
1760 | if (ret != sizeof(nr)) | 1841 | if (ret != sizeof(nr)) |
1761 | return -1; | 1842 | return -1; |
1762 | 1843 | ||
@@ -1792,7 +1873,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused | |||
1792 | char *str; | 1873 | char *str; |
1793 | struct strbuf sb; | 1874 | struct strbuf sb; |
1794 | 1875 | ||
1795 | ret = read(fd, &nr, sizeof(nr)); | 1876 | ret = readn(fd, &nr, sizeof(nr)); |
1796 | if (ret != sizeof(nr)) | 1877 | if (ret != sizeof(nr)) |
1797 | return -1; | 1878 | return -1; |
1798 | 1879 | ||
@@ -1813,7 +1894,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused | |||
1813 | } | 1894 | } |
1814 | ph->env.sibling_cores = strbuf_detach(&sb, NULL); | 1895 | ph->env.sibling_cores = strbuf_detach(&sb, NULL); |
1815 | 1896 | ||
1816 | ret = read(fd, &nr, sizeof(nr)); | 1897 | ret = readn(fd, &nr, sizeof(nr)); |
1817 | if (ret != sizeof(nr)) | 1898 | if (ret != sizeof(nr)) |
1818 | return -1; | 1899 | return -1; |
1819 | 1900 | ||
@@ -1850,7 +1931,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse | |||
1850 | struct strbuf sb; | 1931 | struct strbuf sb; |
1851 | 1932 | ||
1852 | /* nr nodes */ | 1933 | /* nr nodes */ |
1853 | ret = read(fd, &nr, sizeof(nr)); | 1934 | ret = readn(fd, &nr, sizeof(nr)); |
1854 | if (ret != sizeof(nr)) | 1935 | if (ret != sizeof(nr)) |
1855 | goto error; | 1936 | goto error; |
1856 | 1937 | ||
@@ -1862,15 +1943,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse | |||
1862 | 1943 | ||
1863 | for (i = 0; i < nr; i++) { | 1944 | for (i = 0; i < nr; i++) { |
1864 | /* node number */ | 1945 | /* node number */ |
1865 | ret = read(fd, &node, sizeof(node)); | 1946 | ret = readn(fd, &node, sizeof(node)); |
1866 | if (ret != sizeof(node)) | 1947 | if (ret != sizeof(node)) |
1867 | goto error; | 1948 | goto error; |
1868 | 1949 | ||
1869 | ret = read(fd, &mem_total, sizeof(u64)); | 1950 | ret = readn(fd, &mem_total, sizeof(u64)); |
1870 | if (ret != sizeof(u64)) | 1951 | if (ret != sizeof(u64)) |
1871 | goto error; | 1952 | goto error; |
1872 | 1953 | ||
1873 | ret = read(fd, &mem_free, sizeof(u64)); | 1954 | ret = readn(fd, &mem_free, sizeof(u64)); |
1874 | if (ret != sizeof(u64)) | 1955 | if (ret != sizeof(u64)) |
1875 | goto error; | 1956 | goto error; |
1876 | 1957 | ||
@@ -1909,7 +1990,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused | |||
1909 | u32 type; | 1990 | u32 type; |
1910 | struct strbuf sb; | 1991 | struct strbuf sb; |
1911 | 1992 | ||
1912 | ret = read(fd, &pmu_num, sizeof(pmu_num)); | 1993 | ret = readn(fd, &pmu_num, sizeof(pmu_num)); |
1913 | if (ret != sizeof(pmu_num)) | 1994 | if (ret != sizeof(pmu_num)) |
1914 | return -1; | 1995 | return -1; |
1915 | 1996 | ||
@@ -1925,7 +2006,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused | |||
1925 | strbuf_init(&sb, 128); | 2006 | strbuf_init(&sb, 128); |
1926 | 2007 | ||
1927 | while (pmu_num) { | 2008 | while (pmu_num) { |
1928 | if (read(fd, &type, sizeof(type)) != sizeof(type)) | 2009 | if (readn(fd, &type, sizeof(type)) != sizeof(type)) |
1929 | goto error; | 2010 | goto error; |
1930 | if (ph->needs_swap) | 2011 | if (ph->needs_swap) |
1931 | type = bswap_32(type); | 2012 | type = bswap_32(type); |
@@ -1949,6 +2030,98 @@ error: | |||
1949 | return -1; | 2030 | return -1; |
1950 | } | 2031 | } |
1951 | 2032 | ||
2033 | static int process_group_desc(struct perf_file_section *section __maybe_unused, | ||
2034 | struct perf_header *ph, int fd, | ||
2035 | void *data __maybe_unused) | ||
2036 | { | ||
2037 | size_t ret = -1; | ||
2038 | u32 i, nr, nr_groups; | ||
2039 | struct perf_session *session; | ||
2040 | struct perf_evsel *evsel, *leader = NULL; | ||
2041 | struct group_desc { | ||
2042 | char *name; | ||
2043 | u32 leader_idx; | ||
2044 | u32 nr_members; | ||
2045 | } *desc; | ||
2046 | |||
2047 | if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups)) | ||
2048 | return -1; | ||
2049 | |||
2050 | if (ph->needs_swap) | ||
2051 | nr_groups = bswap_32(nr_groups); | ||
2052 | |||
2053 | ph->env.nr_groups = nr_groups; | ||
2054 | if (!nr_groups) { | ||
2055 | pr_debug("group desc not available\n"); | ||
2056 | return 0; | ||
2057 | } | ||
2058 | |||
2059 | desc = calloc(nr_groups, sizeof(*desc)); | ||
2060 | if (!desc) | ||
2061 | return -1; | ||
2062 | |||
2063 | for (i = 0; i < nr_groups; i++) { | ||
2064 | desc[i].name = do_read_string(fd, ph); | ||
2065 | if (!desc[i].name) | ||
2066 | goto out_free; | ||
2067 | |||
2068 | if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32)) | ||
2069 | goto out_free; | ||
2070 | |||
2071 | if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32)) | ||
2072 | goto out_free; | ||
2073 | |||
2074 | if (ph->needs_swap) { | ||
2075 | desc[i].leader_idx = bswap_32(desc[i].leader_idx); | ||
2076 | desc[i].nr_members = bswap_32(desc[i].nr_members); | ||
2077 | } | ||
2078 | } | ||
2079 | |||
2080 | /* | ||
2081 | * Rebuild group relationship based on the group_desc | ||
2082 | */ | ||
2083 | session = container_of(ph, struct perf_session, header); | ||
2084 | session->evlist->nr_groups = nr_groups; | ||
2085 | |||
2086 | i = nr = 0; | ||
2087 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
2088 | if (evsel->idx == (int) desc[i].leader_idx) { | ||
2089 | evsel->leader = evsel; | ||
2090 | /* {anon_group} is a dummy name */ | ||
2091 | if (strcmp(desc[i].name, "{anon_group}")) | ||
2092 | evsel->group_name = desc[i].name; | ||
2093 | evsel->nr_members = desc[i].nr_members; | ||
2094 | |||
2095 | if (i >= nr_groups || nr > 0) { | ||
2096 | pr_debug("invalid group desc\n"); | ||
2097 | goto out_free; | ||
2098 | } | ||
2099 | |||
2100 | leader = evsel; | ||
2101 | nr = evsel->nr_members - 1; | ||
2102 | i++; | ||
2103 | } else if (nr) { | ||
2104 | /* This is a group member */ | ||
2105 | evsel->leader = leader; | ||
2106 | |||
2107 | nr--; | ||
2108 | } | ||
2109 | } | ||
2110 | |||
2111 | if (i != nr_groups || nr != 0) { | ||
2112 | pr_debug("invalid group desc\n"); | ||
2113 | goto out_free; | ||
2114 | } | ||
2115 | |||
2116 | ret = 0; | ||
2117 | out_free: | ||
2118 | while ((int) --i >= 0) | ||
2119 | free(desc[i].name); | ||
2120 | free(desc); | ||
2121 | |||
2122 | return ret; | ||
2123 | } | ||
2124 | |||
1952 | struct feature_ops { | 2125 | struct feature_ops { |
1953 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 2126 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1954 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 2127 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1988,6 +2161,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1988 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 2161 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1989 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 2162 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
1990 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), | 2163 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), |
2164 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | ||
1991 | }; | 2165 | }; |
1992 | 2166 | ||
1993 | struct header_print_data { | 2167 | struct header_print_data { |
@@ -2077,7 +2251,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
2077 | if (!nr_sections) | 2251 | if (!nr_sections) |
2078 | return 0; | 2252 | return 0; |
2079 | 2253 | ||
2080 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); | 2254 | feat_sec = p = calloc(nr_sections, sizeof(*feat_sec)); |
2081 | if (feat_sec == NULL) | 2255 | if (feat_sec == NULL) |
2082 | return -ENOMEM; | 2256 | return -ENOMEM; |
2083 | 2257 | ||
@@ -2217,7 +2391,6 @@ out_err_write: | |||
2217 | } | 2391 | } |
2218 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); | 2392 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
2219 | 2393 | ||
2220 | header->frozen = 1; | ||
2221 | return 0; | 2394 | return 0; |
2222 | } | 2395 | } |
2223 | 2396 | ||
@@ -2249,7 +2422,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
2249 | if (!nr_sections) | 2422 | if (!nr_sections) |
2250 | return 0; | 2423 | return 0; |
2251 | 2424 | ||
2252 | feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections); | 2425 | feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec)); |
2253 | if (!feat_sec) | 2426 | if (!feat_sec) |
2254 | return -1; | 2427 | return -1; |
2255 | 2428 | ||
@@ -2576,6 +2749,11 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, | |||
2576 | if (evsel->tp_format) | 2749 | if (evsel->tp_format) |
2577 | return 0; | 2750 | return 0; |
2578 | 2751 | ||
2752 | if (pevent == NULL) { | ||
2753 | pr_debug("broken or missing trace data\n"); | ||
2754 | return -1; | ||
2755 | } | ||
2756 | |||
2579 | event = pevent_find_event(pevent, evsel->attr.config); | 2757 | event = pevent_find_event(pevent, evsel->attr.config); |
2580 | if (event == NULL) | 2758 | if (event == NULL) |
2581 | return -1; | 2759 | return -1; |
@@ -2613,7 +2791,7 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2613 | u64 f_id; | 2791 | u64 f_id; |
2614 | int nr_attrs, nr_ids, i, j; | 2792 | int nr_attrs, nr_ids, i, j; |
2615 | 2793 | ||
2616 | session->evlist = perf_evlist__new(NULL, NULL); | 2794 | session->evlist = perf_evlist__new(); |
2617 | if (session->evlist == NULL) | 2795 | if (session->evlist == NULL) |
2618 | return -ENOMEM; | 2796 | return -ENOMEM; |
2619 | 2797 | ||
@@ -2692,7 +2870,6 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2692 | session->pevent)) | 2870 | session->pevent)) |
2693 | goto out_delete_evlist; | 2871 | goto out_delete_evlist; |
2694 | 2872 | ||
2695 | header->frozen = 1; | ||
2696 | return 0; | 2873 | return 0; |
2697 | out_errno: | 2874 | out_errno: |
2698 | return -errno; | 2875 | return -errno; |
@@ -2764,7 +2941,7 @@ int perf_event__process_attr(union perf_event *event, | |||
2764 | struct perf_evlist *evlist = *pevlist; | 2941 | struct perf_evlist *evlist = *pevlist; |
2765 | 2942 | ||
2766 | if (evlist == NULL) { | 2943 | if (evlist == NULL) { |
2767 | *pevlist = evlist = perf_evlist__new(NULL, NULL); | 2944 | *pevlist = evlist = perf_evlist__new(); |
2768 | if (evlist == NULL) | 2945 | if (evlist == NULL) |
2769 | return -ENOMEM; | 2946 | return -ENOMEM; |
2770 | } | 2947 | } |
@@ -2912,16 +3089,22 @@ int perf_event__process_tracing_data(union perf_event *event, | |||
2912 | session->repipe); | 3089 | session->repipe); |
2913 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; | 3090 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; |
2914 | 3091 | ||
2915 | if (read(session->fd, buf, padding) < 0) | 3092 | if (readn(session->fd, buf, padding) < 0) { |
2916 | die("reading input file"); | 3093 | pr_err("%s: reading input file", __func__); |
3094 | return -1; | ||
3095 | } | ||
2917 | if (session->repipe) { | 3096 | if (session->repipe) { |
2918 | int retw = write(STDOUT_FILENO, buf, padding); | 3097 | int retw = write(STDOUT_FILENO, buf, padding); |
2919 | if (retw <= 0 || retw != padding) | 3098 | if (retw <= 0 || retw != padding) { |
2920 | die("repiping tracing data padding"); | 3099 | pr_err("%s: repiping tracing data padding", __func__); |
3100 | return -1; | ||
3101 | } | ||
2921 | } | 3102 | } |
2922 | 3103 | ||
2923 | if (size_read + padding != size) | 3104 | if (size_read + padding != size) { |
2924 | die("tracing data size mismatch"); | 3105 | pr_err("%s: tracing data size mismatch", __func__); |
3106 | return -1; | ||
3107 | } | ||
2925 | 3108 | ||
2926 | perf_evlist__prepare_tracepoint_events(session->evlist, | 3109 | perf_evlist__prepare_tracepoint_events(session->evlist, |
2927 | session->pevent); | 3110 | session->pevent); |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 20f0344accb1..16a3e83c584e 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -29,6 +29,7 @@ enum { | |||
29 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | 31 | HEADER_PMU_MAPPINGS, |
32 | HEADER_GROUP_DESC, | ||
32 | HEADER_LAST_FEATURE, | 33 | HEADER_LAST_FEATURE, |
33 | HEADER_FEAT_BITS = 256, | 34 | HEADER_FEAT_BITS = 256, |
34 | }; | 35 | }; |
@@ -79,10 +80,10 @@ struct perf_session_env { | |||
79 | char *numa_nodes; | 80 | char *numa_nodes; |
80 | int nr_pmu_mappings; | 81 | int nr_pmu_mappings; |
81 | char *pmu_mappings; | 82 | char *pmu_mappings; |
83 | int nr_groups; | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | struct perf_header { | 86 | struct perf_header { |
85 | int frozen; | ||
86 | bool needs_swap; | 87 | bool needs_swap; |
87 | s64 attr_offset; | 88 | s64 attr_offset; |
88 | u64 data_offset; | 89 | u64 data_offset; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index cb17e2a8c6ed..b11a6cfdb414 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "hist.h" | 4 | #include "hist.h" |
5 | #include "session.h" | 5 | #include "session.h" |
6 | #include "sort.h" | 6 | #include "sort.h" |
7 | #include "evsel.h" | ||
7 | #include <math.h> | 8 | #include <math.h> |
8 | 9 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | 10 | static bool hists__filter_entry_by_dso(struct hists *hists, |
@@ -66,12 +67,24 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso) | |||
66 | void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | 67 | void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
67 | { | 68 | { |
68 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 69 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
70 | int symlen; | ||
69 | u16 len; | 71 | u16 len; |
70 | 72 | ||
71 | if (h->ms.sym) | 73 | /* |
72 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); | 74 | * +4 accounts for '[x] ' priv level info |
73 | else | 75 | * +2 accounts for 0x prefix on raw addresses |
76 | * +3 accounts for ' y ' symtab origin info | ||
77 | */ | ||
78 | if (h->ms.sym) { | ||
79 | symlen = h->ms.sym->namelen + 4; | ||
80 | if (verbose) | ||
81 | symlen += BITS_PER_LONG / 4 + 2 + 3; | ||
82 | hists__new_col_len(hists, HISTC_SYMBOL, symlen); | ||
83 | } else { | ||
84 | symlen = unresolved_col_width + 4 + 2; | ||
85 | hists__new_col_len(hists, HISTC_SYMBOL, symlen); | ||
74 | hists__set_unres_dso_col_len(hists, HISTC_DSO); | 86 | hists__set_unres_dso_col_len(hists, HISTC_DSO); |
87 | } | ||
75 | 88 | ||
76 | len = thread__comm_len(h->thread); | 89 | len = thread__comm_len(h->thread); |
77 | if (hists__new_col_len(hists, HISTC_COMM, len)) | 90 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
@@ -82,14 +95,14 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
82 | hists__new_col_len(hists, HISTC_DSO, len); | 95 | hists__new_col_len(hists, HISTC_DSO, len); |
83 | } | 96 | } |
84 | 97 | ||
98 | if (h->parent) | ||
99 | hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); | ||
100 | |||
85 | if (h->branch_info) { | 101 | if (h->branch_info) { |
86 | int symlen; | ||
87 | /* | ||
88 | * +4 accounts for '[x] ' priv level info | ||
89 | * +2 account of 0x prefix on raw addresses | ||
90 | */ | ||
91 | if (h->branch_info->from.sym) { | 102 | if (h->branch_info->from.sym) { |
92 | symlen = (int)h->branch_info->from.sym->namelen + 4; | 103 | symlen = (int)h->branch_info->from.sym->namelen + 4; |
104 | if (verbose) | ||
105 | symlen += BITS_PER_LONG / 4 + 2 + 3; | ||
93 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); | 106 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); |
94 | 107 | ||
95 | symlen = dso__name_len(h->branch_info->from.map->dso); | 108 | symlen = dso__name_len(h->branch_info->from.map->dso); |
@@ -102,6 +115,8 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
102 | 115 | ||
103 | if (h->branch_info->to.sym) { | 116 | if (h->branch_info->to.sym) { |
104 | symlen = (int)h->branch_info->to.sym->namelen + 4; | 117 | symlen = (int)h->branch_info->to.sym->namelen + 4; |
118 | if (verbose) | ||
119 | symlen += BITS_PER_LONG / 4 + 2 + 3; | ||
105 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); | 120 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); |
106 | 121 | ||
107 | symlen = dso__name_len(h->branch_info->to.map->dso); | 122 | symlen = dso__name_len(h->branch_info->to.map->dso); |
@@ -112,6 +127,38 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
112 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); | 127 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); |
113 | } | 128 | } |
114 | } | 129 | } |
130 | |||
131 | if (h->mem_info) { | ||
132 | if (h->mem_info->daddr.sym) { | ||
133 | symlen = (int)h->mem_info->daddr.sym->namelen + 4 | ||
134 | + unresolved_col_width + 2; | ||
135 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | ||
136 | symlen); | ||
137 | } else { | ||
138 | symlen = unresolved_col_width + 4 + 2; | ||
139 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | ||
140 | symlen); | ||
141 | } | ||
142 | if (h->mem_info->daddr.map) { | ||
143 | symlen = dso__name_len(h->mem_info->daddr.map->dso); | ||
144 | hists__new_col_len(hists, HISTC_MEM_DADDR_DSO, | ||
145 | symlen); | ||
146 | } else { | ||
147 | symlen = unresolved_col_width + 4 + 2; | ||
148 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); | ||
149 | } | ||
150 | } else { | ||
151 | symlen = unresolved_col_width + 4 + 2; | ||
152 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen); | ||
153 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); | ||
154 | } | ||
155 | |||
156 | hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); | ||
157 | hists__new_col_len(hists, HISTC_MEM_TLB, 22); | ||
158 | hists__new_col_len(hists, HISTC_MEM_SNOOP, 12); | ||
159 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); | ||
160 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); | ||
161 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); | ||
115 | } | 162 | } |
116 | 163 | ||
117 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 164 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
@@ -151,9 +198,12 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he, | |||
151 | } | 198 | } |
152 | } | 199 | } |
153 | 200 | ||
154 | static void he_stat__add_period(struct he_stat *he_stat, u64 period) | 201 | static void he_stat__add_period(struct he_stat *he_stat, u64 period, |
202 | u64 weight) | ||
155 | { | 203 | { |
204 | |||
156 | he_stat->period += period; | 205 | he_stat->period += period; |
206 | he_stat->weight += weight; | ||
157 | he_stat->nr_events += 1; | 207 | he_stat->nr_events += 1; |
158 | } | 208 | } |
159 | 209 | ||
@@ -165,12 +215,14 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) | |||
165 | dest->period_guest_sys += src->period_guest_sys; | 215 | dest->period_guest_sys += src->period_guest_sys; |
166 | dest->period_guest_us += src->period_guest_us; | 216 | dest->period_guest_us += src->period_guest_us; |
167 | dest->nr_events += src->nr_events; | 217 | dest->nr_events += src->nr_events; |
218 | dest->weight += src->weight; | ||
168 | } | 219 | } |
169 | 220 | ||
170 | static void hist_entry__decay(struct hist_entry *he) | 221 | static void hist_entry__decay(struct hist_entry *he) |
171 | { | 222 | { |
172 | he->stat.period = (he->stat.period * 7) / 8; | 223 | he->stat.period = (he->stat.period * 7) / 8; |
173 | he->stat.nr_events = (he->stat.nr_events * 7) / 8; | 224 | he->stat.nr_events = (he->stat.nr_events * 7) / 8; |
225 | /* XXX need decay for weight too? */ | ||
174 | } | 226 | } |
175 | 227 | ||
176 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 228 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
@@ -188,8 +240,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | |||
188 | return he->stat.period == 0; | 240 | return he->stat.period == 0; |
189 | } | 241 | } |
190 | 242 | ||
191 | static void __hists__decay_entries(struct hists *hists, bool zap_user, | 243 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) |
192 | bool zap_kernel, bool threaded) | ||
193 | { | 244 | { |
194 | struct rb_node *next = rb_first(&hists->entries); | 245 | struct rb_node *next = rb_first(&hists->entries); |
195 | struct hist_entry *n; | 246 | struct hist_entry *n; |
@@ -208,7 +259,7 @@ static void __hists__decay_entries(struct hists *hists, bool zap_user, | |||
208 | !n->used) { | 259 | !n->used) { |
209 | rb_erase(&n->rb_node, &hists->entries); | 260 | rb_erase(&n->rb_node, &hists->entries); |
210 | 261 | ||
211 | if (sort__need_collapse || threaded) | 262 | if (sort__need_collapse) |
212 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | 263 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); |
213 | 264 | ||
214 | hist_entry__free(n); | 265 | hist_entry__free(n); |
@@ -217,17 +268,6 @@ static void __hists__decay_entries(struct hists *hists, bool zap_user, | |||
217 | } | 268 | } |
218 | } | 269 | } |
219 | 270 | ||
220 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | ||
221 | { | ||
222 | return __hists__decay_entries(hists, zap_user, zap_kernel, false); | ||
223 | } | ||
224 | |||
225 | void hists__decay_entries_threaded(struct hists *hists, | ||
226 | bool zap_user, bool zap_kernel) | ||
227 | { | ||
228 | return __hists__decay_entries(hists, zap_user, zap_kernel, true); | ||
229 | } | ||
230 | |||
231 | /* | 271 | /* |
232 | * histogram, sorted on item, collects periods | 272 | * histogram, sorted on item, collects periods |
233 | */ | 273 | */ |
@@ -235,13 +275,42 @@ void hists__decay_entries_threaded(struct hists *hists, | |||
235 | static struct hist_entry *hist_entry__new(struct hist_entry *template) | 275 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
236 | { | 276 | { |
237 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; | 277 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; |
238 | struct hist_entry *he = malloc(sizeof(*he) + callchain_size); | 278 | struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); |
239 | 279 | ||
240 | if (he != NULL) { | 280 | if (he != NULL) { |
241 | *he = *template; | 281 | *he = *template; |
242 | 282 | ||
243 | if (he->ms.map) | 283 | if (he->ms.map) |
244 | he->ms.map->referenced = true; | 284 | he->ms.map->referenced = true; |
285 | |||
286 | if (he->branch_info) { | ||
287 | /* | ||
288 | * This branch info is (a part of) allocated from | ||
289 | * machine__resolve_bstack() and will be freed after | ||
290 | * adding new entries. So we need to save a copy. | ||
291 | */ | ||
292 | he->branch_info = malloc(sizeof(*he->branch_info)); | ||
293 | if (he->branch_info == NULL) { | ||
294 | free(he); | ||
295 | return NULL; | ||
296 | } | ||
297 | |||
298 | memcpy(he->branch_info, template->branch_info, | ||
299 | sizeof(*he->branch_info)); | ||
300 | |||
301 | if (he->branch_info->from.map) | ||
302 | he->branch_info->from.map->referenced = true; | ||
303 | if (he->branch_info->to.map) | ||
304 | he->branch_info->to.map->referenced = true; | ||
305 | } | ||
306 | |||
307 | if (he->mem_info) { | ||
308 | if (he->mem_info->iaddr.map) | ||
309 | he->mem_info->iaddr.map->referenced = true; | ||
310 | if (he->mem_info->daddr.map) | ||
311 | he->mem_info->daddr.map->referenced = true; | ||
312 | } | ||
313 | |||
245 | if (symbol_conf.use_callchain) | 314 | if (symbol_conf.use_callchain) |
246 | callchain_init(he->callchain); | 315 | callchain_init(he->callchain); |
247 | 316 | ||
@@ -251,7 +320,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
251 | return he; | 320 | return he; |
252 | } | 321 | } |
253 | 322 | ||
254 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | 323 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
255 | { | 324 | { |
256 | if (!h->filtered) { | 325 | if (!h->filtered) { |
257 | hists__calc_col_len(hists, h); | 326 | hists__calc_col_len(hists, h); |
@@ -270,25 +339,36 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
270 | static struct hist_entry *add_hist_entry(struct hists *hists, | 339 | static struct hist_entry *add_hist_entry(struct hists *hists, |
271 | struct hist_entry *entry, | 340 | struct hist_entry *entry, |
272 | struct addr_location *al, | 341 | struct addr_location *al, |
273 | u64 period) | 342 | u64 period, |
343 | u64 weight) | ||
274 | { | 344 | { |
275 | struct rb_node **p; | 345 | struct rb_node **p; |
276 | struct rb_node *parent = NULL; | 346 | struct rb_node *parent = NULL; |
277 | struct hist_entry *he; | 347 | struct hist_entry *he; |
278 | int cmp; | 348 | int cmp; |
279 | 349 | ||
280 | pthread_mutex_lock(&hists->lock); | ||
281 | |||
282 | p = &hists->entries_in->rb_node; | 350 | p = &hists->entries_in->rb_node; |
283 | 351 | ||
284 | while (*p != NULL) { | 352 | while (*p != NULL) { |
285 | parent = *p; | 353 | parent = *p; |
286 | he = rb_entry(parent, struct hist_entry, rb_node_in); | 354 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
287 | 355 | ||
288 | cmp = hist_entry__cmp(entry, he); | 356 | /* |
357 | * Make sure that it receives arguments in a same order as | ||
358 | * hist_entry__collapse() so that we can use an appropriate | ||
359 | * function when searching an entry regardless which sort | ||
360 | * keys were used. | ||
361 | */ | ||
362 | cmp = hist_entry__cmp(he, entry); | ||
289 | 363 | ||
290 | if (!cmp) { | 364 | if (!cmp) { |
291 | he_stat__add_period(&he->stat, period); | 365 | he_stat__add_period(&he->stat, period, weight); |
366 | |||
367 | /* | ||
368 | * This mem info was allocated from machine__resolve_mem | ||
369 | * and will not be used anymore. | ||
370 | */ | ||
371 | free(entry->mem_info); | ||
292 | 372 | ||
293 | /* If the map of an existing hist_entry has | 373 | /* If the map of an existing hist_entry has |
294 | * become out-of-date due to an exec() or | 374 | * become out-of-date due to an exec() or |
@@ -312,22 +392,51 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
312 | 392 | ||
313 | he = hist_entry__new(entry); | 393 | he = hist_entry__new(entry); |
314 | if (!he) | 394 | if (!he) |
315 | goto out_unlock; | 395 | return NULL; |
316 | 396 | ||
317 | rb_link_node(&he->rb_node_in, parent, p); | 397 | rb_link_node(&he->rb_node_in, parent, p); |
318 | rb_insert_color(&he->rb_node_in, hists->entries_in); | 398 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
319 | out: | 399 | out: |
320 | hist_entry__add_cpumode_period(he, al->cpumode, period); | 400 | hist_entry__add_cpumode_period(he, al->cpumode, period); |
321 | out_unlock: | ||
322 | pthread_mutex_unlock(&hists->lock); | ||
323 | return he; | 401 | return he; |
324 | } | 402 | } |
325 | 403 | ||
404 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | ||
405 | struct addr_location *al, | ||
406 | struct symbol *sym_parent, | ||
407 | struct mem_info *mi, | ||
408 | u64 period, | ||
409 | u64 weight) | ||
410 | { | ||
411 | struct hist_entry entry = { | ||
412 | .thread = al->thread, | ||
413 | .ms = { | ||
414 | .map = al->map, | ||
415 | .sym = al->sym, | ||
416 | }, | ||
417 | .stat = { | ||
418 | .period = period, | ||
419 | .weight = weight, | ||
420 | .nr_events = 1, | ||
421 | }, | ||
422 | .cpu = al->cpu, | ||
423 | .ip = al->addr, | ||
424 | .level = al->level, | ||
425 | .parent = sym_parent, | ||
426 | .filtered = symbol__parent_filter(sym_parent), | ||
427 | .hists = self, | ||
428 | .mem_info = mi, | ||
429 | .branch_info = NULL, | ||
430 | }; | ||
431 | return add_hist_entry(self, &entry, al, period, weight); | ||
432 | } | ||
433 | |||
326 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | 434 | struct hist_entry *__hists__add_branch_entry(struct hists *self, |
327 | struct addr_location *al, | 435 | struct addr_location *al, |
328 | struct symbol *sym_parent, | 436 | struct symbol *sym_parent, |
329 | struct branch_info *bi, | 437 | struct branch_info *bi, |
330 | u64 period) | 438 | u64 period, |
439 | u64 weight) | ||
331 | { | 440 | { |
332 | struct hist_entry entry = { | 441 | struct hist_entry entry = { |
333 | .thread = al->thread, | 442 | .thread = al->thread, |
@@ -341,19 +450,22 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, | |||
341 | .stat = { | 450 | .stat = { |
342 | .period = period, | 451 | .period = period, |
343 | .nr_events = 1, | 452 | .nr_events = 1, |
453 | .weight = weight, | ||
344 | }, | 454 | }, |
345 | .parent = sym_parent, | 455 | .parent = sym_parent, |
346 | .filtered = symbol__parent_filter(sym_parent), | 456 | .filtered = symbol__parent_filter(sym_parent), |
347 | .branch_info = bi, | 457 | .branch_info = bi, |
348 | .hists = self, | 458 | .hists = self, |
459 | .mem_info = NULL, | ||
349 | }; | 460 | }; |
350 | 461 | ||
351 | return add_hist_entry(self, &entry, al, period); | 462 | return add_hist_entry(self, &entry, al, period, weight); |
352 | } | 463 | } |
353 | 464 | ||
354 | struct hist_entry *__hists__add_entry(struct hists *self, | 465 | struct hist_entry *__hists__add_entry(struct hists *self, |
355 | struct addr_location *al, | 466 | struct addr_location *al, |
356 | struct symbol *sym_parent, u64 period) | 467 | struct symbol *sym_parent, u64 period, |
468 | u64 weight) | ||
357 | { | 469 | { |
358 | struct hist_entry entry = { | 470 | struct hist_entry entry = { |
359 | .thread = al->thread, | 471 | .thread = al->thread, |
@@ -367,13 +479,16 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
367 | .stat = { | 479 | .stat = { |
368 | .period = period, | 480 | .period = period, |
369 | .nr_events = 1, | 481 | .nr_events = 1, |
482 | .weight = weight, | ||
370 | }, | 483 | }, |
371 | .parent = sym_parent, | 484 | .parent = sym_parent, |
372 | .filtered = symbol__parent_filter(sym_parent), | 485 | .filtered = symbol__parent_filter(sym_parent), |
373 | .hists = self, | 486 | .hists = self, |
487 | .branch_info = NULL, | ||
488 | .mem_info = NULL, | ||
374 | }; | 489 | }; |
375 | 490 | ||
376 | return add_hist_entry(self, &entry, al, period); | 491 | return add_hist_entry(self, &entry, al, period, weight); |
377 | } | 492 | } |
378 | 493 | ||
379 | int64_t | 494 | int64_t |
@@ -413,6 +528,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
413 | void hist_entry__free(struct hist_entry *he) | 528 | void hist_entry__free(struct hist_entry *he) |
414 | { | 529 | { |
415 | free(he->branch_info); | 530 | free(he->branch_info); |
531 | free(he->mem_info); | ||
416 | free(he); | 532 | free(he); |
417 | } | 533 | } |
418 | 534 | ||
@@ -481,13 +597,13 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
481 | hists__filter_entry_by_symbol(hists, he); | 597 | hists__filter_entry_by_symbol(hists, he); |
482 | } | 598 | } |
483 | 599 | ||
484 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | 600 | void hists__collapse_resort(struct hists *hists) |
485 | { | 601 | { |
486 | struct rb_root *root; | 602 | struct rb_root *root; |
487 | struct rb_node *next; | 603 | struct rb_node *next; |
488 | struct hist_entry *n; | 604 | struct hist_entry *n; |
489 | 605 | ||
490 | if (!sort__need_collapse && !threaded) | 606 | if (!sort__need_collapse) |
491 | return; | 607 | return; |
492 | 608 | ||
493 | root = hists__get_rotate_entries_in(hists); | 609 | root = hists__get_rotate_entries_in(hists); |
@@ -509,19 +625,65 @@ static void __hists__collapse_resort(struct hists *hists, bool threaded) | |||
509 | } | 625 | } |
510 | } | 626 | } |
511 | 627 | ||
512 | void hists__collapse_resort(struct hists *hists) | 628 | /* |
629 | * reverse the map, sort on period. | ||
630 | */ | ||
631 | |||
632 | static int period_cmp(u64 period_a, u64 period_b) | ||
513 | { | 633 | { |
514 | return __hists__collapse_resort(hists, false); | 634 | if (period_a > period_b) |
635 | return 1; | ||
636 | if (period_a < period_b) | ||
637 | return -1; | ||
638 | return 0; | ||
515 | } | 639 | } |
516 | 640 | ||
517 | void hists__collapse_resort_threaded(struct hists *hists) | 641 | static int hist_entry__sort_on_period(struct hist_entry *a, |
642 | struct hist_entry *b) | ||
518 | { | 643 | { |
519 | return __hists__collapse_resort(hists, true); | 644 | int ret; |
520 | } | 645 | int i, nr_members; |
646 | struct perf_evsel *evsel; | ||
647 | struct hist_entry *pair; | ||
648 | u64 *periods_a, *periods_b; | ||
521 | 649 | ||
522 | /* | 650 | ret = period_cmp(a->stat.period, b->stat.period); |
523 | * reverse the map, sort on period. | 651 | if (ret || !symbol_conf.event_group) |
524 | */ | 652 | return ret; |
653 | |||
654 | evsel = hists_to_evsel(a->hists); | ||
655 | nr_members = evsel->nr_members; | ||
656 | if (nr_members <= 1) | ||
657 | return ret; | ||
658 | |||
659 | periods_a = zalloc(sizeof(periods_a) * nr_members); | ||
660 | periods_b = zalloc(sizeof(periods_b) * nr_members); | ||
661 | |||
662 | if (!periods_a || !periods_b) | ||
663 | goto out; | ||
664 | |||
665 | list_for_each_entry(pair, &a->pairs.head, pairs.node) { | ||
666 | evsel = hists_to_evsel(pair->hists); | ||
667 | periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
668 | } | ||
669 | |||
670 | list_for_each_entry(pair, &b->pairs.head, pairs.node) { | ||
671 | evsel = hists_to_evsel(pair->hists); | ||
672 | periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
673 | } | ||
674 | |||
675 | for (i = 1; i < nr_members; i++) { | ||
676 | ret = period_cmp(periods_a[i], periods_b[i]); | ||
677 | if (ret) | ||
678 | break; | ||
679 | } | ||
680 | |||
681 | out: | ||
682 | free(periods_a); | ||
683 | free(periods_b); | ||
684 | |||
685 | return ret; | ||
686 | } | ||
525 | 687 | ||
526 | static void __hists__insert_output_entry(struct rb_root *entries, | 688 | static void __hists__insert_output_entry(struct rb_root *entries, |
527 | struct hist_entry *he, | 689 | struct hist_entry *he, |
@@ -539,7 +701,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
539 | parent = *p; | 701 | parent = *p; |
540 | iter = rb_entry(parent, struct hist_entry, rb_node); | 702 | iter = rb_entry(parent, struct hist_entry, rb_node); |
541 | 703 | ||
542 | if (he->stat.period > iter->stat.period) | 704 | if (hist_entry__sort_on_period(he, iter) > 0) |
543 | p = &(*p)->rb_left; | 705 | p = &(*p)->rb_left; |
544 | else | 706 | else |
545 | p = &(*p)->rb_right; | 707 | p = &(*p)->rb_right; |
@@ -549,7 +711,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
549 | rb_insert_color(&he->rb_node, entries); | 711 | rb_insert_color(&he->rb_node, entries); |
550 | } | 712 | } |
551 | 713 | ||
552 | static void __hists__output_resort(struct hists *hists, bool threaded) | 714 | void hists__output_resort(struct hists *hists) |
553 | { | 715 | { |
554 | struct rb_root *root; | 716 | struct rb_root *root; |
555 | struct rb_node *next; | 717 | struct rb_node *next; |
@@ -558,7 +720,7 @@ static void __hists__output_resort(struct hists *hists, bool threaded) | |||
558 | 720 | ||
559 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); | 721 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); |
560 | 722 | ||
561 | if (sort__need_collapse || threaded) | 723 | if (sort__need_collapse) |
562 | root = &hists->entries_collapsed; | 724 | root = &hists->entries_collapsed; |
563 | else | 725 | else |
564 | root = hists->entries_in; | 726 | root = hists->entries_in; |
@@ -579,16 +741,6 @@ static void __hists__output_resort(struct hists *hists, bool threaded) | |||
579 | } | 741 | } |
580 | } | 742 | } |
581 | 743 | ||
582 | void hists__output_resort(struct hists *hists) | ||
583 | { | ||
584 | return __hists__output_resort(hists, false); | ||
585 | } | ||
586 | |||
587 | void hists__output_resort_threaded(struct hists *hists) | ||
588 | { | ||
589 | return __hists__output_resort(hists, true); | ||
590 | } | ||
591 | |||
592 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, | 744 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
593 | enum hist_filter filter) | 745 | enum hist_filter filter) |
594 | { | 746 | { |
@@ -711,25 +863,38 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
711 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 863 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
712 | } | 864 | } |
713 | 865 | ||
866 | void events_stats__inc(struct events_stats *stats, u32 type) | ||
867 | { | ||
868 | ++stats->nr_events[0]; | ||
869 | ++stats->nr_events[type]; | ||
870 | } | ||
871 | |||
714 | void hists__inc_nr_events(struct hists *hists, u32 type) | 872 | void hists__inc_nr_events(struct hists *hists, u32 type) |
715 | { | 873 | { |
716 | ++hists->stats.nr_events[0]; | 874 | events_stats__inc(&hists->stats, type); |
717 | ++hists->stats.nr_events[type]; | ||
718 | } | 875 | } |
719 | 876 | ||
720 | static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | 877 | static struct hist_entry *hists__add_dummy_entry(struct hists *hists, |
721 | struct hist_entry *pair) | 878 | struct hist_entry *pair) |
722 | { | 879 | { |
723 | struct rb_node **p = &hists->entries.rb_node; | 880 | struct rb_root *root; |
881 | struct rb_node **p; | ||
724 | struct rb_node *parent = NULL; | 882 | struct rb_node *parent = NULL; |
725 | struct hist_entry *he; | 883 | struct hist_entry *he; |
726 | int cmp; | 884 | int cmp; |
727 | 885 | ||
886 | if (sort__need_collapse) | ||
887 | root = &hists->entries_collapsed; | ||
888 | else | ||
889 | root = hists->entries_in; | ||
890 | |||
891 | p = &root->rb_node; | ||
892 | |||
728 | while (*p != NULL) { | 893 | while (*p != NULL) { |
729 | parent = *p; | 894 | parent = *p; |
730 | he = rb_entry(parent, struct hist_entry, rb_node); | 895 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
731 | 896 | ||
732 | cmp = hist_entry__cmp(pair, he); | 897 | cmp = hist_entry__collapse(he, pair); |
733 | 898 | ||
734 | if (!cmp) | 899 | if (!cmp) |
735 | goto out; | 900 | goto out; |
@@ -744,8 +909,8 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | |||
744 | if (he) { | 909 | if (he) { |
745 | memset(&he->stat, 0, sizeof(he->stat)); | 910 | memset(&he->stat, 0, sizeof(he->stat)); |
746 | he->hists = hists; | 911 | he->hists = hists; |
747 | rb_link_node(&he->rb_node, parent, p); | 912 | rb_link_node(&he->rb_node_in, parent, p); |
748 | rb_insert_color(&he->rb_node, &hists->entries); | 913 | rb_insert_color(&he->rb_node_in, root); |
749 | hists__inc_nr_entries(hists, he); | 914 | hists__inc_nr_entries(hists, he); |
750 | } | 915 | } |
751 | out: | 916 | out: |
@@ -755,11 +920,16 @@ out: | |||
755 | static struct hist_entry *hists__find_entry(struct hists *hists, | 920 | static struct hist_entry *hists__find_entry(struct hists *hists, |
756 | struct hist_entry *he) | 921 | struct hist_entry *he) |
757 | { | 922 | { |
758 | struct rb_node *n = hists->entries.rb_node; | 923 | struct rb_node *n; |
924 | |||
925 | if (sort__need_collapse) | ||
926 | n = hists->entries_collapsed.rb_node; | ||
927 | else | ||
928 | n = hists->entries_in->rb_node; | ||
759 | 929 | ||
760 | while (n) { | 930 | while (n) { |
761 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | 931 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in); |
762 | int64_t cmp = hist_entry__cmp(he, iter); | 932 | int64_t cmp = hist_entry__collapse(iter, he); |
763 | 933 | ||
764 | if (cmp < 0) | 934 | if (cmp < 0) |
765 | n = n->rb_left; | 935 | n = n->rb_left; |
@@ -777,15 +947,21 @@ static struct hist_entry *hists__find_entry(struct hists *hists, | |||
777 | */ | 947 | */ |
778 | void hists__match(struct hists *leader, struct hists *other) | 948 | void hists__match(struct hists *leader, struct hists *other) |
779 | { | 949 | { |
950 | struct rb_root *root; | ||
780 | struct rb_node *nd; | 951 | struct rb_node *nd; |
781 | struct hist_entry *pos, *pair; | 952 | struct hist_entry *pos, *pair; |
782 | 953 | ||
783 | for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) { | 954 | if (sort__need_collapse) |
784 | pos = rb_entry(nd, struct hist_entry, rb_node); | 955 | root = &leader->entries_collapsed; |
956 | else | ||
957 | root = leader->entries_in; | ||
958 | |||
959 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
960 | pos = rb_entry(nd, struct hist_entry, rb_node_in); | ||
785 | pair = hists__find_entry(other, pos); | 961 | pair = hists__find_entry(other, pos); |
786 | 962 | ||
787 | if (pair) | 963 | if (pair) |
788 | hist__entry_add_pair(pos, pair); | 964 | hist_entry__add_pair(pair, pos); |
789 | } | 965 | } |
790 | } | 966 | } |
791 | 967 | ||
@@ -796,17 +972,23 @@ void hists__match(struct hists *leader, struct hists *other) | |||
796 | */ | 972 | */ |
797 | int hists__link(struct hists *leader, struct hists *other) | 973 | int hists__link(struct hists *leader, struct hists *other) |
798 | { | 974 | { |
975 | struct rb_root *root; | ||
799 | struct rb_node *nd; | 976 | struct rb_node *nd; |
800 | struct hist_entry *pos, *pair; | 977 | struct hist_entry *pos, *pair; |
801 | 978 | ||
802 | for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) { | 979 | if (sort__need_collapse) |
803 | pos = rb_entry(nd, struct hist_entry, rb_node); | 980 | root = &other->entries_collapsed; |
981 | else | ||
982 | root = other->entries_in; | ||
983 | |||
984 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
985 | pos = rb_entry(nd, struct hist_entry, rb_node_in); | ||
804 | 986 | ||
805 | if (!hist_entry__has_pairs(pos)) { | 987 | if (!hist_entry__has_pairs(pos)) { |
806 | pair = hists__add_dummy_entry(leader, pos); | 988 | pair = hists__add_dummy_entry(leader, pos); |
807 | if (pair == NULL) | 989 | if (pair == NULL) |
808 | return -1; | 990 | return -1; |
809 | hist__entry_add_pair(pair, pos); | 991 | hist_entry__add_pair(pos, pair); |
810 | } | 992 | } |
811 | } | 993 | } |
812 | 994 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 8b091a51e4a2..2d3790fd99bb 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -43,12 +43,20 @@ enum hist_column { | |||
43 | HISTC_COMM, | 43 | HISTC_COMM, |
44 | HISTC_PARENT, | 44 | HISTC_PARENT, |
45 | HISTC_CPU, | 45 | HISTC_CPU, |
46 | HISTC_SRCLINE, | ||
46 | HISTC_MISPREDICT, | 47 | HISTC_MISPREDICT, |
47 | HISTC_SYMBOL_FROM, | 48 | HISTC_SYMBOL_FROM, |
48 | HISTC_SYMBOL_TO, | 49 | HISTC_SYMBOL_TO, |
49 | HISTC_DSO_FROM, | 50 | HISTC_DSO_FROM, |
50 | HISTC_DSO_TO, | 51 | HISTC_DSO_TO, |
51 | HISTC_SRCLINE, | 52 | HISTC_LOCAL_WEIGHT, |
53 | HISTC_GLOBAL_WEIGHT, | ||
54 | HISTC_MEM_DADDR_SYMBOL, | ||
55 | HISTC_MEM_DADDR_DSO, | ||
56 | HISTC_MEM_LOCKED, | ||
57 | HISTC_MEM_TLB, | ||
58 | HISTC_MEM_LVL, | ||
59 | HISTC_MEM_SNOOP, | ||
52 | HISTC_NR_COLS, /* Last entry */ | 60 | HISTC_NR_COLS, /* Last entry */ |
53 | }; | 61 | }; |
54 | 62 | ||
@@ -73,7 +81,8 @@ struct hists { | |||
73 | 81 | ||
74 | struct hist_entry *__hists__add_entry(struct hists *self, | 82 | struct hist_entry *__hists__add_entry(struct hists *self, |
75 | struct addr_location *al, | 83 | struct addr_location *al, |
76 | struct symbol *parent, u64 period); | 84 | struct symbol *parent, u64 period, |
85 | u64 weight); | ||
77 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 86 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
78 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 87 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
79 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, | 88 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, |
@@ -84,23 +93,29 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, | |||
84 | struct addr_location *al, | 93 | struct addr_location *al, |
85 | struct symbol *sym_parent, | 94 | struct symbol *sym_parent, |
86 | struct branch_info *bi, | 95 | struct branch_info *bi, |
87 | u64 period); | 96 | u64 period, |
97 | u64 weight); | ||
98 | |||
99 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | ||
100 | struct addr_location *al, | ||
101 | struct symbol *sym_parent, | ||
102 | struct mem_info *mi, | ||
103 | u64 period, | ||
104 | u64 weight); | ||
88 | 105 | ||
89 | void hists__output_resort(struct hists *self); | 106 | void hists__output_resort(struct hists *self); |
90 | void hists__output_resort_threaded(struct hists *hists); | ||
91 | void hists__collapse_resort(struct hists *self); | 107 | void hists__collapse_resort(struct hists *self); |
92 | void hists__collapse_resort_threaded(struct hists *hists); | ||
93 | 108 | ||
94 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 109 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
95 | void hists__decay_entries_threaded(struct hists *hists, bool zap_user, | ||
96 | bool zap_kernel); | ||
97 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 110 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
98 | 111 | ||
112 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); | ||
99 | void hists__inc_nr_events(struct hists *self, u32 type); | 113 | void hists__inc_nr_events(struct hists *self, u32 type); |
100 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 114 | void events_stats__inc(struct events_stats *stats, u32 type); |
115 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | ||
101 | 116 | ||
102 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, | 117 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, |
103 | int max_cols, FILE *fp); | 118 | int max_cols, float min_pcnt, FILE *fp); |
104 | 119 | ||
105 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 120 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
106 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 121 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
@@ -126,13 +141,19 @@ struct perf_hpp { | |||
126 | }; | 141 | }; |
127 | 142 | ||
128 | struct perf_hpp_fmt { | 143 | struct perf_hpp_fmt { |
129 | bool cond; | ||
130 | int (*header)(struct perf_hpp *hpp); | 144 | int (*header)(struct perf_hpp *hpp); |
131 | int (*width)(struct perf_hpp *hpp); | 145 | int (*width)(struct perf_hpp *hpp); |
132 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); | 146 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); |
133 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); | 147 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); |
148 | |||
149 | struct list_head list; | ||
134 | }; | 150 | }; |
135 | 151 | ||
152 | extern struct list_head perf_hpp__list; | ||
153 | |||
154 | #define perf_hpp__for_each_format(format) \ | ||
155 | list_for_each_entry(format, &perf_hpp__list, list) | ||
156 | |||
136 | extern struct perf_hpp_fmt perf_hpp__format[]; | 157 | extern struct perf_hpp_fmt perf_hpp__format[]; |
137 | 158 | ||
138 | enum { | 159 | enum { |
@@ -148,14 +169,14 @@ enum { | |||
148 | PERF_HPP__DELTA, | 169 | PERF_HPP__DELTA, |
149 | PERF_HPP__RATIO, | 170 | PERF_HPP__RATIO, |
150 | PERF_HPP__WEIGHTED_DIFF, | 171 | PERF_HPP__WEIGHTED_DIFF, |
151 | PERF_HPP__DISPL, | ||
152 | PERF_HPP__FORMULA, | 172 | PERF_HPP__FORMULA, |
153 | 173 | ||
154 | PERF_HPP__MAX_INDEX | 174 | PERF_HPP__MAX_INDEX |
155 | }; | 175 | }; |
156 | 176 | ||
157 | void perf_hpp__init(void); | 177 | void perf_hpp__init(void); |
158 | void perf_hpp__column_enable(unsigned col, bool enable); | 178 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
179 | void perf_hpp__column_enable(unsigned col); | ||
159 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | 180 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, |
160 | bool color); | 181 | bool color); |
161 | 182 | ||
@@ -167,13 +188,14 @@ struct hist_browser_timer { | |||
167 | int refresh; | 188 | int refresh; |
168 | }; | 189 | }; |
169 | 190 | ||
170 | #ifdef NEWT_SUPPORT | 191 | #ifdef SLANG_SUPPORT |
171 | #include "../ui/keysyms.h" | 192 | #include "../ui/keysyms.h" |
172 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 193 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, |
173 | struct hist_browser_timer *hbt); | 194 | struct hist_browser_timer *hbt); |
174 | 195 | ||
175 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 196 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
176 | struct hist_browser_timer *hbt, | 197 | struct hist_browser_timer *hbt, |
198 | float min_pcnt, | ||
177 | struct perf_session_env *env); | 199 | struct perf_session_env *env); |
178 | int script_browse(const char *script_opt); | 200 | int script_browse(const char *script_opt); |
179 | #else | 201 | #else |
@@ -181,6 +203,7 @@ static inline | |||
181 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | 203 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
182 | const char *help __maybe_unused, | 204 | const char *help __maybe_unused, |
183 | struct hist_browser_timer *hbt __maybe_unused, | 205 | struct hist_browser_timer *hbt __maybe_unused, |
206 | float min_pcnt __maybe_unused, | ||
184 | struct perf_session_env *env __maybe_unused) | 207 | struct perf_session_env *env __maybe_unused) |
185 | { | 208 | { |
186 | return 0; | 209 | return 0; |
@@ -188,7 +211,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
188 | 211 | ||
189 | static inline int hist_entry__tui_annotate(struct hist_entry *self | 212 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
190 | __maybe_unused, | 213 | __maybe_unused, |
191 | int evidx __maybe_unused, | 214 | struct perf_evsel *evsel |
215 | __maybe_unused, | ||
192 | struct hist_browser_timer *hbt | 216 | struct hist_browser_timer *hbt |
193 | __maybe_unused) | 217 | __maybe_unused) |
194 | { | 218 | { |
@@ -200,18 +224,21 @@ static inline int script_browse(const char *script_opt __maybe_unused) | |||
200 | return 0; | 224 | return 0; |
201 | } | 225 | } |
202 | 226 | ||
203 | #define K_LEFT -1 | 227 | #define K_LEFT -1000 |
204 | #define K_RIGHT -2 | 228 | #define K_RIGHT -2000 |
229 | #define K_SWITCH_INPUT_DATA -3000 | ||
205 | #endif | 230 | #endif |
206 | 231 | ||
207 | #ifdef GTK2_SUPPORT | 232 | #ifdef GTK2_SUPPORT |
208 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | 233 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, |
209 | struct hist_browser_timer *hbt __maybe_unused); | 234 | struct hist_browser_timer *hbt __maybe_unused, |
235 | float min_pcnt); | ||
210 | #else | 236 | #else |
211 | static inline | 237 | static inline |
212 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | 238 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, |
213 | const char *help __maybe_unused, | 239 | const char *help __maybe_unused, |
214 | struct hist_browser_timer *hbt __maybe_unused) | 240 | struct hist_browser_timer *hbt __maybe_unused, |
241 | float min_pcnt __maybe_unused) | ||
215 | { | 242 | { |
216 | return 0; | 243 | return 0; |
217 | } | 244 | } |
@@ -219,8 +246,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
219 | 246 | ||
220 | unsigned int hists__sort_list_width(struct hists *self); | 247 | unsigned int hists__sort_list_width(struct hists *self); |
221 | 248 | ||
222 | double perf_diff__compute_delta(struct hist_entry *he); | 249 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair); |
223 | double perf_diff__compute_ratio(struct hist_entry *he); | 250 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair); |
224 | s64 perf_diff__compute_wdiff(struct hist_entry *he); | 251 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair); |
225 | int perf_diff__formula(char *buf, size_t size, struct hist_entry *he); | 252 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, |
253 | char *buf, size_t size); | ||
254 | double perf_diff__period_percent(struct hist_entry *he, u64 period); | ||
226 | #endif /* __PERF_HIST_H */ | 255 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index a55d8cf083c9..45cf10a562bd 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) | 14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) |
15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) | 15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) |
16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) | 16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) |
17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) | ||
17 | 18 | ||
18 | #define for_each_set_bit(bit, addr, size) \ | 19 | #define for_each_set_bit(bit, addr, size) \ |
19 | for ((bit) = find_first_bit((addr), (size)); \ | 20 | for ((bit) = find_first_bit((addr), (size)); \ |
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 9d0740024ba8..11a8d86f7fea 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c | |||
@@ -59,16 +59,40 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) | |||
59 | 59 | ||
60 | struct int_node *intlist__find(struct intlist *ilist, int i) | 60 | struct int_node *intlist__find(struct intlist *ilist, int i) |
61 | { | 61 | { |
62 | struct int_node *node = NULL; | 62 | struct int_node *node; |
63 | struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | 63 | struct rb_node *rb_node; |
64 | 64 | ||
65 | if (ilist == NULL) | ||
66 | return NULL; | ||
67 | |||
68 | node = NULL; | ||
69 | rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | ||
65 | if (rb_node) | 70 | if (rb_node) |
66 | node = container_of(rb_node, struct int_node, rb_node); | 71 | node = container_of(rb_node, struct int_node, rb_node); |
67 | 72 | ||
68 | return node; | 73 | return node; |
69 | } | 74 | } |
70 | 75 | ||
71 | struct intlist *intlist__new(void) | 76 | static int intlist__parse_list(struct intlist *ilist, const char *s) |
77 | { | ||
78 | char *sep; | ||
79 | int err; | ||
80 | |||
81 | do { | ||
82 | long value = strtol(s, &sep, 10); | ||
83 | err = -EINVAL; | ||
84 | if (*sep != ',' && *sep != '\0') | ||
85 | break; | ||
86 | err = intlist__add(ilist, value); | ||
87 | if (err) | ||
88 | break; | ||
89 | s = sep + 1; | ||
90 | } while (*sep != '\0'); | ||
91 | |||
92 | return err; | ||
93 | } | ||
94 | |||
95 | struct intlist *intlist__new(const char *slist) | ||
72 | { | 96 | { |
73 | struct intlist *ilist = malloc(sizeof(*ilist)); | 97 | struct intlist *ilist = malloc(sizeof(*ilist)); |
74 | 98 | ||
@@ -77,9 +101,15 @@ struct intlist *intlist__new(void) | |||
77 | ilist->rblist.node_cmp = intlist__node_cmp; | 101 | ilist->rblist.node_cmp = intlist__node_cmp; |
78 | ilist->rblist.node_new = intlist__node_new; | 102 | ilist->rblist.node_new = intlist__node_new; |
79 | ilist->rblist.node_delete = intlist__node_delete; | 103 | ilist->rblist.node_delete = intlist__node_delete; |
104 | |||
105 | if (slist && intlist__parse_list(ilist, slist)) | ||
106 | goto out_delete; | ||
80 | } | 107 | } |
81 | 108 | ||
82 | return ilist; | 109 | return ilist; |
110 | out_delete: | ||
111 | intlist__delete(ilist); | ||
112 | return NULL; | ||
83 | } | 113 | } |
84 | 114 | ||
85 | void intlist__delete(struct intlist *ilist) | 115 | void intlist__delete(struct intlist *ilist) |
diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 6d63ab90db50..62351dad848f 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h | |||
@@ -15,7 +15,7 @@ struct intlist { | |||
15 | struct rblist rblist; | 15 | struct rblist rblist; |
16 | }; | 16 | }; |
17 | 17 | ||
18 | struct intlist *intlist__new(void); | 18 | struct intlist *intlist__new(const char *slist); |
19 | void intlist__delete(struct intlist *ilist); | 19 | void intlist__delete(struct intlist *ilist); |
20 | 20 | ||
21 | void intlist__remove(struct intlist *ilist, struct int_node *in); | 21 | void intlist__remove(struct intlist *ilist, struct int_node *in); |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 1f09d0581e6b..b2ecad6ec46b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1,10 +1,15 @@ | |||
1 | #include "callchain.h" | ||
1 | #include "debug.h" | 2 | #include "debug.h" |
2 | #include "event.h" | 3 | #include "event.h" |
4 | #include "evsel.h" | ||
5 | #include "hist.h" | ||
3 | #include "machine.h" | 6 | #include "machine.h" |
4 | #include "map.h" | 7 | #include "map.h" |
8 | #include "sort.h" | ||
5 | #include "strlist.h" | 9 | #include "strlist.h" |
6 | #include "thread.h" | 10 | #include "thread.h" |
7 | #include <stdbool.h> | 11 | #include <stdbool.h> |
12 | #include "unwind.h" | ||
8 | 13 | ||
9 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 14 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
10 | { | 15 | { |
@@ -48,6 +53,29 @@ static void dsos__delete(struct list_head *dsos) | |||
48 | } | 53 | } |
49 | } | 54 | } |
50 | 55 | ||
56 | void machine__delete_dead_threads(struct machine *machine) | ||
57 | { | ||
58 | struct thread *n, *t; | ||
59 | |||
60 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
61 | list_del(&t->node); | ||
62 | thread__delete(t); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | void machine__delete_threads(struct machine *machine) | ||
67 | { | ||
68 | struct rb_node *nd = rb_first(&machine->threads); | ||
69 | |||
70 | while (nd) { | ||
71 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
72 | |||
73 | rb_erase(&t->rb_node, &machine->threads); | ||
74 | nd = rb_next(nd); | ||
75 | thread__delete(t); | ||
76 | } | ||
77 | } | ||
78 | |||
51 | void machine__exit(struct machine *machine) | 79 | void machine__exit(struct machine *machine) |
52 | { | 80 | { |
53 | map_groups__exit(&machine->kmaps); | 81 | map_groups__exit(&machine->kmaps); |
@@ -63,10 +91,22 @@ void machine__delete(struct machine *machine) | |||
63 | free(machine); | 91 | free(machine); |
64 | } | 92 | } |
65 | 93 | ||
66 | struct machine *machines__add(struct rb_root *machines, pid_t pid, | 94 | void machines__init(struct machines *machines) |
95 | { | ||
96 | machine__init(&machines->host, "", HOST_KERNEL_ID); | ||
97 | machines->guests = RB_ROOT; | ||
98 | } | ||
99 | |||
100 | void machines__exit(struct machines *machines) | ||
101 | { | ||
102 | machine__exit(&machines->host); | ||
103 | /* XXX exit guest */ | ||
104 | } | ||
105 | |||
106 | struct machine *machines__add(struct machines *machines, pid_t pid, | ||
67 | const char *root_dir) | 107 | const char *root_dir) |
68 | { | 108 | { |
69 | struct rb_node **p = &machines->rb_node; | 109 | struct rb_node **p = &machines->guests.rb_node; |
70 | struct rb_node *parent = NULL; | 110 | struct rb_node *parent = NULL; |
71 | struct machine *pos, *machine = malloc(sizeof(*machine)); | 111 | struct machine *pos, *machine = malloc(sizeof(*machine)); |
72 | 112 | ||
@@ -88,18 +128,21 @@ struct machine *machines__add(struct rb_root *machines, pid_t pid, | |||
88 | } | 128 | } |
89 | 129 | ||
90 | rb_link_node(&machine->rb_node, parent, p); | 130 | rb_link_node(&machine->rb_node, parent, p); |
91 | rb_insert_color(&machine->rb_node, machines); | 131 | rb_insert_color(&machine->rb_node, &machines->guests); |
92 | 132 | ||
93 | return machine; | 133 | return machine; |
94 | } | 134 | } |
95 | 135 | ||
96 | struct machine *machines__find(struct rb_root *machines, pid_t pid) | 136 | struct machine *machines__find(struct machines *machines, pid_t pid) |
97 | { | 137 | { |
98 | struct rb_node **p = &machines->rb_node; | 138 | struct rb_node **p = &machines->guests.rb_node; |
99 | struct rb_node *parent = NULL; | 139 | struct rb_node *parent = NULL; |
100 | struct machine *machine; | 140 | struct machine *machine; |
101 | struct machine *default_machine = NULL; | 141 | struct machine *default_machine = NULL; |
102 | 142 | ||
143 | if (pid == HOST_KERNEL_ID) | ||
144 | return &machines->host; | ||
145 | |||
103 | while (*p != NULL) { | 146 | while (*p != NULL) { |
104 | parent = *p; | 147 | parent = *p; |
105 | machine = rb_entry(parent, struct machine, rb_node); | 148 | machine = rb_entry(parent, struct machine, rb_node); |
@@ -116,7 +159,7 @@ struct machine *machines__find(struct rb_root *machines, pid_t pid) | |||
116 | return default_machine; | 159 | return default_machine; |
117 | } | 160 | } |
118 | 161 | ||
119 | struct machine *machines__findnew(struct rb_root *machines, pid_t pid) | 162 | struct machine *machines__findnew(struct machines *machines, pid_t pid) |
120 | { | 163 | { |
121 | char path[PATH_MAX]; | 164 | char path[PATH_MAX]; |
122 | const char *root_dir = ""; | 165 | const char *root_dir = ""; |
@@ -150,12 +193,12 @@ out: | |||
150 | return machine; | 193 | return machine; |
151 | } | 194 | } |
152 | 195 | ||
153 | void machines__process(struct rb_root *machines, | 196 | void machines__process_guests(struct machines *machines, |
154 | machine__process_t process, void *data) | 197 | machine__process_t process, void *data) |
155 | { | 198 | { |
156 | struct rb_node *nd; | 199 | struct rb_node *nd; |
157 | 200 | ||
158 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | 201 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { |
159 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 202 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
160 | process(pos, data); | 203 | process(pos, data); |
161 | } | 204 | } |
@@ -175,12 +218,14 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size) | |||
175 | return bf; | 218 | return bf; |
176 | } | 219 | } |
177 | 220 | ||
178 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) | 221 | void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) |
179 | { | 222 | { |
180 | struct rb_node *node; | 223 | struct rb_node *node; |
181 | struct machine *machine; | 224 | struct machine *machine; |
182 | 225 | ||
183 | for (node = rb_first(machines); node; node = rb_next(node)) { | 226 | machines->host.id_hdr_size = id_hdr_size; |
227 | |||
228 | for (node = rb_first(&machines->guests); node; node = rb_next(node)) { | ||
184 | machine = rb_entry(node, struct machine, rb_node); | 229 | machine = rb_entry(node, struct machine, rb_node); |
185 | machine->id_hdr_size = id_hdr_size; | 230 | machine->id_hdr_size = id_hdr_size; |
186 | } | 231 | } |
@@ -264,6 +309,537 @@ int machine__process_lost_event(struct machine *machine __maybe_unused, | |||
264 | return 0; | 309 | return 0; |
265 | } | 310 | } |
266 | 311 | ||
312 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
313 | const char *filename) | ||
314 | { | ||
315 | struct map *map; | ||
316 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
317 | |||
318 | if (dso == NULL) | ||
319 | return NULL; | ||
320 | |||
321 | map = map__new2(start, dso, MAP__FUNCTION); | ||
322 | if (map == NULL) | ||
323 | return NULL; | ||
324 | |||
325 | if (machine__is_host(machine)) | ||
326 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
327 | else | ||
328 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
329 | map_groups__insert(&machine->kmaps, map); | ||
330 | return map; | ||
331 | } | ||
332 | |||
333 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | ||
334 | { | ||
335 | struct rb_node *nd; | ||
336 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | ||
337 | __dsos__fprintf(&machines->host.user_dsos, fp); | ||
338 | |||
339 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
340 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
341 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
342 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
343 | } | ||
344 | |||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
349 | bool (skip)(struct dso *dso, int parm), int parm) | ||
350 | { | ||
351 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | ||
352 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | ||
353 | } | ||
354 | |||
355 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | ||
356 | bool (skip)(struct dso *dso, int parm), int parm) | ||
357 | { | ||
358 | struct rb_node *nd; | ||
359 | size_t ret = machine__fprintf_dsos_buildid(&machines->host, fp, skip, parm); | ||
360 | |||
361 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
362 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
363 | ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm); | ||
364 | } | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
369 | { | ||
370 | int i; | ||
371 | size_t printed = 0; | ||
372 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
373 | |||
374 | if (kdso->has_build_id) { | ||
375 | char filename[PATH_MAX]; | ||
376 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
377 | printed += fprintf(fp, "[0] %s\n", filename); | ||
378 | } | ||
379 | |||
380 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
381 | printed += fprintf(fp, "[%d] %s\n", | ||
382 | i + kdso->has_build_id, vmlinux_path[i]); | ||
383 | |||
384 | return printed; | ||
385 | } | ||
386 | |||
387 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
388 | { | ||
389 | size_t ret = 0; | ||
390 | struct rb_node *nd; | ||
391 | |||
392 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
393 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
394 | |||
395 | ret += thread__fprintf(pos, fp); | ||
396 | } | ||
397 | |||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | static struct dso *machine__get_kernel(struct machine *machine) | ||
402 | { | ||
403 | const char *vmlinux_name = NULL; | ||
404 | struct dso *kernel; | ||
405 | |||
406 | if (machine__is_host(machine)) { | ||
407 | vmlinux_name = symbol_conf.vmlinux_name; | ||
408 | if (!vmlinux_name) | ||
409 | vmlinux_name = "[kernel.kallsyms]"; | ||
410 | |||
411 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
412 | "[kernel]", | ||
413 | DSO_TYPE_KERNEL); | ||
414 | } else { | ||
415 | char bf[PATH_MAX]; | ||
416 | |||
417 | if (machine__is_default_guest(machine)) | ||
418 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
419 | if (!vmlinux_name) | ||
420 | vmlinux_name = machine__mmap_name(machine, bf, | ||
421 | sizeof(bf)); | ||
422 | |||
423 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
424 | "[guest.kernel]", | ||
425 | DSO_TYPE_GUEST_KERNEL); | ||
426 | } | ||
427 | |||
428 | if (kernel != NULL && (!kernel->has_build_id)) | ||
429 | dso__read_running_kernel_build_id(kernel, machine); | ||
430 | |||
431 | return kernel; | ||
432 | } | ||
433 | |||
434 | struct process_args { | ||
435 | u64 start; | ||
436 | }; | ||
437 | |||
438 | static int symbol__in_kernel(void *arg, const char *name, | ||
439 | char type __maybe_unused, u64 start) | ||
440 | { | ||
441 | struct process_args *args = arg; | ||
442 | |||
443 | if (strchr(name, '[')) | ||
444 | return 0; | ||
445 | |||
446 | args->start = start; | ||
447 | return 1; | ||
448 | } | ||
449 | |||
450 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
451 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
452 | { | ||
453 | const char *filename; | ||
454 | char path[PATH_MAX]; | ||
455 | struct process_args args; | ||
456 | |||
457 | if (machine__is_host(machine)) { | ||
458 | filename = "/proc/kallsyms"; | ||
459 | } else { | ||
460 | if (machine__is_default_guest(machine)) | ||
461 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
462 | else { | ||
463 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
464 | filename = path; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
469 | return 0; | ||
470 | |||
471 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
472 | return 0; | ||
473 | |||
474 | return args.start; | ||
475 | } | ||
476 | |||
477 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
478 | { | ||
479 | enum map_type type; | ||
480 | u64 start = machine__get_kernel_start_addr(machine); | ||
481 | |||
482 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
483 | struct kmap *kmap; | ||
484 | |||
485 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
486 | if (machine->vmlinux_maps[type] == NULL) | ||
487 | return -1; | ||
488 | |||
489 | machine->vmlinux_maps[type]->map_ip = | ||
490 | machine->vmlinux_maps[type]->unmap_ip = | ||
491 | identity__map_ip; | ||
492 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
493 | kmap->kmaps = &machine->kmaps; | ||
494 | map_groups__insert(&machine->kmaps, | ||
495 | machine->vmlinux_maps[type]); | ||
496 | } | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | void machine__destroy_kernel_maps(struct machine *machine) | ||
502 | { | ||
503 | enum map_type type; | ||
504 | |||
505 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
506 | struct kmap *kmap; | ||
507 | |||
508 | if (machine->vmlinux_maps[type] == NULL) | ||
509 | continue; | ||
510 | |||
511 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
512 | map_groups__remove(&machine->kmaps, | ||
513 | machine->vmlinux_maps[type]); | ||
514 | if (kmap->ref_reloc_sym) { | ||
515 | /* | ||
516 | * ref_reloc_sym is shared among all maps, so free just | ||
517 | * on one of them. | ||
518 | */ | ||
519 | if (type == MAP__FUNCTION) { | ||
520 | free((char *)kmap->ref_reloc_sym->name); | ||
521 | kmap->ref_reloc_sym->name = NULL; | ||
522 | free(kmap->ref_reloc_sym); | ||
523 | } | ||
524 | kmap->ref_reloc_sym = NULL; | ||
525 | } | ||
526 | |||
527 | map__delete(machine->vmlinux_maps[type]); | ||
528 | machine->vmlinux_maps[type] = NULL; | ||
529 | } | ||
530 | } | ||
531 | |||
532 | int machines__create_guest_kernel_maps(struct machines *machines) | ||
533 | { | ||
534 | int ret = 0; | ||
535 | struct dirent **namelist = NULL; | ||
536 | int i, items = 0; | ||
537 | char path[PATH_MAX]; | ||
538 | pid_t pid; | ||
539 | char *endp; | ||
540 | |||
541 | if (symbol_conf.default_guest_vmlinux_name || | ||
542 | symbol_conf.default_guest_modules || | ||
543 | symbol_conf.default_guest_kallsyms) { | ||
544 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
545 | } | ||
546 | |||
547 | if (symbol_conf.guestmount) { | ||
548 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
549 | if (items <= 0) | ||
550 | return -ENOENT; | ||
551 | for (i = 0; i < items; i++) { | ||
552 | if (!isdigit(namelist[i]->d_name[0])) { | ||
553 | /* Filter out . and .. */ | ||
554 | continue; | ||
555 | } | ||
556 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
557 | if ((*endp != '\0') || | ||
558 | (endp == namelist[i]->d_name) || | ||
559 | (errno == ERANGE)) { | ||
560 | pr_debug("invalid directory (%s). Skipping.\n", | ||
561 | namelist[i]->d_name); | ||
562 | continue; | ||
563 | } | ||
564 | sprintf(path, "%s/%s/proc/kallsyms", | ||
565 | symbol_conf.guestmount, | ||
566 | namelist[i]->d_name); | ||
567 | ret = access(path, R_OK); | ||
568 | if (ret) { | ||
569 | pr_debug("Can't access file %s\n", path); | ||
570 | goto failure; | ||
571 | } | ||
572 | machines__create_kernel_maps(machines, pid); | ||
573 | } | ||
574 | failure: | ||
575 | free(namelist); | ||
576 | } | ||
577 | |||
578 | return ret; | ||
579 | } | ||
580 | |||
581 | void machines__destroy_kernel_maps(struct machines *machines) | ||
582 | { | ||
583 | struct rb_node *next = rb_first(&machines->guests); | ||
584 | |||
585 | machine__destroy_kernel_maps(&machines->host); | ||
586 | |||
587 | while (next) { | ||
588 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
589 | |||
590 | next = rb_next(&pos->rb_node); | ||
591 | rb_erase(&pos->rb_node, &machines->guests); | ||
592 | machine__delete(pos); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | int machines__create_kernel_maps(struct machines *machines, pid_t pid) | ||
597 | { | ||
598 | struct machine *machine = machines__findnew(machines, pid); | ||
599 | |||
600 | if (machine == NULL) | ||
601 | return -1; | ||
602 | |||
603 | return machine__create_kernel_maps(machine); | ||
604 | } | ||
605 | |||
606 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
607 | enum map_type type, symbol_filter_t filter) | ||
608 | { | ||
609 | struct map *map = machine->vmlinux_maps[type]; | ||
610 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
611 | |||
612 | if (ret > 0) { | ||
613 | dso__set_loaded(map->dso, type); | ||
614 | /* | ||
615 | * Since /proc/kallsyms will have multiple sessions for the | ||
616 | * kernel, with modules between them, fixup the end of all | ||
617 | * sections. | ||
618 | */ | ||
619 | __map_groups__fixup_end(&machine->kmaps, type); | ||
620 | } | ||
621 | |||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
626 | symbol_filter_t filter) | ||
627 | { | ||
628 | struct map *map = machine->vmlinux_maps[type]; | ||
629 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
630 | |||
631 | if (ret > 0) { | ||
632 | dso__set_loaded(map->dso, type); | ||
633 | map__reloc_vmlinux(map); | ||
634 | } | ||
635 | |||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | static void map_groups__fixup_end(struct map_groups *mg) | ||
640 | { | ||
641 | int i; | ||
642 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
643 | __map_groups__fixup_end(mg, i); | ||
644 | } | ||
645 | |||
646 | static char *get_kernel_version(const char *root_dir) | ||
647 | { | ||
648 | char version[PATH_MAX]; | ||
649 | FILE *file; | ||
650 | char *name, *tmp; | ||
651 | const char *prefix = "Linux version "; | ||
652 | |||
653 | sprintf(version, "%s/proc/version", root_dir); | ||
654 | file = fopen(version, "r"); | ||
655 | if (!file) | ||
656 | return NULL; | ||
657 | |||
658 | version[0] = '\0'; | ||
659 | tmp = fgets(version, sizeof(version), file); | ||
660 | fclose(file); | ||
661 | |||
662 | name = strstr(version, prefix); | ||
663 | if (!name) | ||
664 | return NULL; | ||
665 | name += strlen(prefix); | ||
666 | tmp = strchr(name, ' '); | ||
667 | if (tmp) | ||
668 | *tmp = '\0'; | ||
669 | |||
670 | return strdup(name); | ||
671 | } | ||
672 | |||
673 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
674 | const char *dir_name) | ||
675 | { | ||
676 | struct dirent *dent; | ||
677 | DIR *dir = opendir(dir_name); | ||
678 | int ret = 0; | ||
679 | |||
680 | if (!dir) { | ||
681 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
682 | return -1; | ||
683 | } | ||
684 | |||
685 | while ((dent = readdir(dir)) != NULL) { | ||
686 | char path[PATH_MAX]; | ||
687 | struct stat st; | ||
688 | |||
689 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
690 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
691 | if (stat(path, &st)) | ||
692 | continue; | ||
693 | |||
694 | if (S_ISDIR(st.st_mode)) { | ||
695 | if (!strcmp(dent->d_name, ".") || | ||
696 | !strcmp(dent->d_name, "..")) | ||
697 | continue; | ||
698 | |||
699 | ret = map_groups__set_modules_path_dir(mg, path); | ||
700 | if (ret < 0) | ||
701 | goto out; | ||
702 | } else { | ||
703 | char *dot = strrchr(dent->d_name, '.'), | ||
704 | dso_name[PATH_MAX]; | ||
705 | struct map *map; | ||
706 | char *long_name; | ||
707 | |||
708 | if (dot == NULL || strcmp(dot, ".ko")) | ||
709 | continue; | ||
710 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
711 | (int)(dot - dent->d_name), dent->d_name); | ||
712 | |||
713 | strxfrchar(dso_name, '-', '_'); | ||
714 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
715 | dso_name); | ||
716 | if (map == NULL) | ||
717 | continue; | ||
718 | |||
719 | long_name = strdup(path); | ||
720 | if (long_name == NULL) { | ||
721 | ret = -1; | ||
722 | goto out; | ||
723 | } | ||
724 | dso__set_long_name(map->dso, long_name); | ||
725 | map->dso->lname_alloc = 1; | ||
726 | dso__kernel_module_get_build_id(map->dso, ""); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | out: | ||
731 | closedir(dir); | ||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | static int machine__set_modules_path(struct machine *machine) | ||
736 | { | ||
737 | char *version; | ||
738 | char modules_path[PATH_MAX]; | ||
739 | |||
740 | version = get_kernel_version(machine->root_dir); | ||
741 | if (!version) | ||
742 | return -1; | ||
743 | |||
744 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
745 | machine->root_dir, version); | ||
746 | free(version); | ||
747 | |||
748 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
749 | } | ||
750 | |||
751 | static int machine__create_modules(struct machine *machine) | ||
752 | { | ||
753 | char *line = NULL; | ||
754 | size_t n; | ||
755 | FILE *file; | ||
756 | struct map *map; | ||
757 | const char *modules; | ||
758 | char path[PATH_MAX]; | ||
759 | |||
760 | if (machine__is_default_guest(machine)) | ||
761 | modules = symbol_conf.default_guest_modules; | ||
762 | else { | ||
763 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
764 | modules = path; | ||
765 | } | ||
766 | |||
767 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
768 | return -1; | ||
769 | |||
770 | file = fopen(modules, "r"); | ||
771 | if (file == NULL) | ||
772 | return -1; | ||
773 | |||
774 | while (!feof(file)) { | ||
775 | char name[PATH_MAX]; | ||
776 | u64 start; | ||
777 | char *sep; | ||
778 | int line_len; | ||
779 | |||
780 | line_len = getline(&line, &n, file); | ||
781 | if (line_len < 0) | ||
782 | break; | ||
783 | |||
784 | if (!line) | ||
785 | goto out_failure; | ||
786 | |||
787 | line[--line_len] = '\0'; /* \n */ | ||
788 | |||
789 | sep = strrchr(line, 'x'); | ||
790 | if (sep == NULL) | ||
791 | continue; | ||
792 | |||
793 | hex2u64(sep + 1, &start); | ||
794 | |||
795 | sep = strchr(line, ' '); | ||
796 | if (sep == NULL) | ||
797 | continue; | ||
798 | |||
799 | *sep = '\0'; | ||
800 | |||
801 | snprintf(name, sizeof(name), "[%s]", line); | ||
802 | map = machine__new_module(machine, start, name); | ||
803 | if (map == NULL) | ||
804 | goto out_delete_line; | ||
805 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
806 | } | ||
807 | |||
808 | free(line); | ||
809 | fclose(file); | ||
810 | |||
811 | return machine__set_modules_path(machine); | ||
812 | |||
813 | out_delete_line: | ||
814 | free(line); | ||
815 | out_failure: | ||
816 | return -1; | ||
817 | } | ||
818 | |||
819 | int machine__create_kernel_maps(struct machine *machine) | ||
820 | { | ||
821 | struct dso *kernel = machine__get_kernel(machine); | ||
822 | |||
823 | if (kernel == NULL || | ||
824 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
825 | return -1; | ||
826 | |||
827 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
828 | if (machine__is_host(machine)) | ||
829 | pr_debug("Problems creating module maps, " | ||
830 | "continuing anyway...\n"); | ||
831 | else | ||
832 | pr_debug("Problems creating module maps for guest %d, " | ||
833 | "continuing anyway...\n", machine->pid); | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * Now that we have all the maps created, just set the ->end of them: | ||
838 | */ | ||
839 | map_groups__fixup_end(&machine->kmaps); | ||
840 | return 0; | ||
841 | } | ||
842 | |||
267 | static void machine__set_kernel_mmap_len(struct machine *machine, | 843 | static void machine__set_kernel_mmap_len(struct machine *machine, |
268 | union perf_event *event) | 844 | union perf_event *event) |
269 | { | 845 | { |
@@ -379,6 +955,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
379 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 955 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
380 | struct thread *thread; | 956 | struct thread *thread; |
381 | struct map *map; | 957 | struct map *map; |
958 | enum map_type type; | ||
382 | int ret = 0; | 959 | int ret = 0; |
383 | 960 | ||
384 | if (dump_trace) | 961 | if (dump_trace) |
@@ -395,10 +972,17 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
395 | thread = machine__findnew_thread(machine, event->mmap.pid); | 972 | thread = machine__findnew_thread(machine, event->mmap.pid); |
396 | if (thread == NULL) | 973 | if (thread == NULL) |
397 | goto out_problem; | 974 | goto out_problem; |
975 | |||
976 | if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) | ||
977 | type = MAP__VARIABLE; | ||
978 | else | ||
979 | type = MAP__FUNCTION; | ||
980 | |||
398 | map = map__new(&machine->user_dsos, event->mmap.start, | 981 | map = map__new(&machine->user_dsos, event->mmap.start, |
399 | event->mmap.len, event->mmap.pgoff, | 982 | event->mmap.len, event->mmap.pgoff, |
400 | event->mmap.pid, event->mmap.filename, | 983 | event->mmap.pid, event->mmap.filename, |
401 | MAP__FUNCTION); | 984 | type); |
985 | |||
402 | if (map == NULL) | 986 | if (map == NULL) |
403 | goto out_problem; | 987 | goto out_problem; |
404 | 988 | ||
@@ -427,6 +1011,17 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
427 | return 0; | 1011 | return 0; |
428 | } | 1012 | } |
429 | 1013 | ||
1014 | static void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1015 | { | ||
1016 | machine->last_match = NULL; | ||
1017 | rb_erase(&th->rb_node, &machine->threads); | ||
1018 | /* | ||
1019 | * We may have references to this thread, for instance in some hist_entry | ||
1020 | * instances, so just move them to a separate list. | ||
1021 | */ | ||
1022 | list_add_tail(&th->node, &machine->dead_threads); | ||
1023 | } | ||
1024 | |||
430 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | 1025 | int machine__process_exit_event(struct machine *machine, union perf_event *event) |
431 | { | 1026 | { |
432 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1027 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
@@ -462,3 +1057,210 @@ int machine__process_event(struct machine *machine, union perf_event *event) | |||
462 | 1057 | ||
463 | return ret; | 1058 | return ret; |
464 | } | 1059 | } |
1060 | |||
1061 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
1062 | { | ||
1063 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
1064 | return 1; | ||
1065 | |||
1066 | return 0; | ||
1067 | } | ||
1068 | |||
1069 | static const u8 cpumodes[] = { | ||
1070 | PERF_RECORD_MISC_USER, | ||
1071 | PERF_RECORD_MISC_KERNEL, | ||
1072 | PERF_RECORD_MISC_GUEST_USER, | ||
1073 | PERF_RECORD_MISC_GUEST_KERNEL | ||
1074 | }; | ||
1075 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
1076 | |||
1077 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | ||
1078 | struct addr_map_symbol *ams, | ||
1079 | u64 ip) | ||
1080 | { | ||
1081 | struct addr_location al; | ||
1082 | size_t i; | ||
1083 | u8 m; | ||
1084 | |||
1085 | memset(&al, 0, sizeof(al)); | ||
1086 | |||
1087 | for (i = 0; i < NCPUMODES; i++) { | ||
1088 | m = cpumodes[i]; | ||
1089 | /* | ||
1090 | * We cannot use the header.misc hint to determine whether a | ||
1091 | * branch stack address is user, kernel, guest, hypervisor. | ||
1092 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
1093 | * Thus, we have to try consecutively until we find a match | ||
1094 | * or else, the symbol is unknown | ||
1095 | */ | ||
1096 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, | ||
1097 | ip, &al, NULL); | ||
1098 | if (al.sym) | ||
1099 | goto found; | ||
1100 | } | ||
1101 | found: | ||
1102 | ams->addr = ip; | ||
1103 | ams->al_addr = al.addr; | ||
1104 | ams->sym = al.sym; | ||
1105 | ams->map = al.map; | ||
1106 | } | ||
1107 | |||
1108 | static void ip__resolve_data(struct machine *machine, struct thread *thread, | ||
1109 | u8 m, struct addr_map_symbol *ams, u64 addr) | ||
1110 | { | ||
1111 | struct addr_location al; | ||
1112 | |||
1113 | memset(&al, 0, sizeof(al)); | ||
1114 | |||
1115 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al, | ||
1116 | NULL); | ||
1117 | ams->addr = addr; | ||
1118 | ams->al_addr = al.addr; | ||
1119 | ams->sym = al.sym; | ||
1120 | ams->map = al.map; | ||
1121 | } | ||
1122 | |||
1123 | struct mem_info *machine__resolve_mem(struct machine *machine, | ||
1124 | struct thread *thr, | ||
1125 | struct perf_sample *sample, | ||
1126 | u8 cpumode) | ||
1127 | { | ||
1128 | struct mem_info *mi = zalloc(sizeof(*mi)); | ||
1129 | |||
1130 | if (!mi) | ||
1131 | return NULL; | ||
1132 | |||
1133 | ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip); | ||
1134 | ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr); | ||
1135 | mi->data_src.val = sample->data_src; | ||
1136 | |||
1137 | return mi; | ||
1138 | } | ||
1139 | |||
1140 | struct branch_info *machine__resolve_bstack(struct machine *machine, | ||
1141 | struct thread *thr, | ||
1142 | struct branch_stack *bs) | ||
1143 | { | ||
1144 | struct branch_info *bi; | ||
1145 | unsigned int i; | ||
1146 | |||
1147 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
1148 | if (!bi) | ||
1149 | return NULL; | ||
1150 | |||
1151 | for (i = 0; i < bs->nr; i++) { | ||
1152 | ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to); | ||
1153 | ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from); | ||
1154 | bi[i].flags = bs->entries[i].flags; | ||
1155 | } | ||
1156 | return bi; | ||
1157 | } | ||
1158 | |||
1159 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
1160 | struct thread *thread, | ||
1161 | struct ip_callchain *chain, | ||
1162 | struct symbol **parent) | ||
1163 | |||
1164 | { | ||
1165 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
1166 | unsigned int i; | ||
1167 | int err; | ||
1168 | |||
1169 | callchain_cursor_reset(&callchain_cursor); | ||
1170 | |||
1171 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
1172 | pr_warning("corrupted callchain. skipping...\n"); | ||
1173 | return 0; | ||
1174 | } | ||
1175 | |||
1176 | for (i = 0; i < chain->nr; i++) { | ||
1177 | u64 ip; | ||
1178 | struct addr_location al; | ||
1179 | |||
1180 | if (callchain_param.order == ORDER_CALLEE) | ||
1181 | ip = chain->ips[i]; | ||
1182 | else | ||
1183 | ip = chain->ips[chain->nr - i - 1]; | ||
1184 | |||
1185 | if (ip >= PERF_CONTEXT_MAX) { | ||
1186 | switch (ip) { | ||
1187 | case PERF_CONTEXT_HV: | ||
1188 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
1189 | break; | ||
1190 | case PERF_CONTEXT_KERNEL: | ||
1191 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
1192 | break; | ||
1193 | case PERF_CONTEXT_USER: | ||
1194 | cpumode = PERF_RECORD_MISC_USER; | ||
1195 | break; | ||
1196 | default: | ||
1197 | pr_debug("invalid callchain context: " | ||
1198 | "%"PRId64"\n", (s64) ip); | ||
1199 | /* | ||
1200 | * It seems the callchain is corrupted. | ||
1201 | * Discard all. | ||
1202 | */ | ||
1203 | callchain_cursor_reset(&callchain_cursor); | ||
1204 | return 0; | ||
1205 | } | ||
1206 | continue; | ||
1207 | } | ||
1208 | |||
1209 | al.filtered = false; | ||
1210 | thread__find_addr_location(thread, machine, cpumode, | ||
1211 | MAP__FUNCTION, ip, &al, NULL); | ||
1212 | if (al.sym != NULL) { | ||
1213 | if (sort__has_parent && !*parent && | ||
1214 | symbol__match_parent_regex(al.sym)) | ||
1215 | *parent = al.sym; | ||
1216 | if (!symbol_conf.use_callchain) | ||
1217 | break; | ||
1218 | } | ||
1219 | |||
1220 | err = callchain_cursor_append(&callchain_cursor, | ||
1221 | ip, al.map, al.sym); | ||
1222 | if (err) | ||
1223 | return err; | ||
1224 | } | ||
1225 | |||
1226 | return 0; | ||
1227 | } | ||
1228 | |||
1229 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
1230 | { | ||
1231 | struct callchain_cursor *cursor = arg; | ||
1232 | return callchain_cursor_append(cursor, entry->ip, | ||
1233 | entry->map, entry->sym); | ||
1234 | } | ||
1235 | |||
1236 | int machine__resolve_callchain(struct machine *machine, | ||
1237 | struct perf_evsel *evsel, | ||
1238 | struct thread *thread, | ||
1239 | struct perf_sample *sample, | ||
1240 | struct symbol **parent) | ||
1241 | |||
1242 | { | ||
1243 | int ret; | ||
1244 | |||
1245 | callchain_cursor_reset(&callchain_cursor); | ||
1246 | |||
1247 | ret = machine__resolve_callchain_sample(machine, thread, | ||
1248 | sample->callchain, parent); | ||
1249 | if (ret) | ||
1250 | return ret; | ||
1251 | |||
1252 | /* Can we do dwarf post unwind? */ | ||
1253 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
1254 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
1255 | return 0; | ||
1256 | |||
1257 | /* Bail out if nothing was captured. */ | ||
1258 | if ((!sample->user_regs.regs) || | ||
1259 | (!sample->user_stack.size)) | ||
1260 | return 0; | ||
1261 | |||
1262 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
1263 | thread, evsel->attr.sample_regs_user, | ||
1264 | sample); | ||
1265 | |||
1266 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index b7cde7467d55..77940680f1fc 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -47,26 +47,38 @@ int machine__process_event(struct machine *machine, union perf_event *event); | |||
47 | 47 | ||
48 | typedef void (*machine__process_t)(struct machine *machine, void *data); | 48 | typedef void (*machine__process_t)(struct machine *machine, void *data); |
49 | 49 | ||
50 | void machines__process(struct rb_root *machines, | 50 | struct machines { |
51 | machine__process_t process, void *data); | 51 | struct machine host; |
52 | struct rb_root guests; | ||
53 | }; | ||
54 | |||
55 | void machines__init(struct machines *machines); | ||
56 | void machines__exit(struct machines *machines); | ||
52 | 57 | ||
53 | struct machine *machines__add(struct rb_root *machines, pid_t pid, | 58 | void machines__process_guests(struct machines *machines, |
59 | machine__process_t process, void *data); | ||
60 | |||
61 | struct machine *machines__add(struct machines *machines, pid_t pid, | ||
54 | const char *root_dir); | 62 | const char *root_dir); |
55 | struct machine *machines__find_host(struct rb_root *machines); | 63 | struct machine *machines__find_host(struct machines *machines); |
56 | struct machine *machines__find(struct rb_root *machines, pid_t pid); | 64 | struct machine *machines__find(struct machines *machines, pid_t pid); |
57 | struct machine *machines__findnew(struct rb_root *machines, pid_t pid); | 65 | struct machine *machines__findnew(struct machines *machines, pid_t pid); |
58 | 66 | ||
59 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size); | 67 | void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size); |
60 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | 68 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size); |
61 | 69 | ||
62 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 70 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
63 | void machine__exit(struct machine *machine); | 71 | void machine__exit(struct machine *machine); |
72 | void machine__delete_dead_threads(struct machine *machine); | ||
73 | void machine__delete_threads(struct machine *machine); | ||
64 | void machine__delete(struct machine *machine); | 74 | void machine__delete(struct machine *machine); |
65 | 75 | ||
66 | |||
67 | struct branch_info *machine__resolve_bstack(struct machine *machine, | 76 | struct branch_info *machine__resolve_bstack(struct machine *machine, |
68 | struct thread *thread, | 77 | struct thread *thread, |
69 | struct branch_stack *bs); | 78 | struct branch_stack *bs); |
79 | struct mem_info *machine__resolve_mem(struct machine *machine, | ||
80 | struct thread *thread, | ||
81 | struct perf_sample *sample, u8 cpumode); | ||
70 | int machine__resolve_callchain(struct machine *machine, | 82 | int machine__resolve_callchain(struct machine *machine, |
71 | struct perf_evsel *evsel, | 83 | struct perf_evsel *evsel, |
72 | struct thread *thread, | 84 | struct thread *thread, |
@@ -88,7 +100,6 @@ static inline bool machine__is_host(struct machine *machine) | |||
88 | } | 100 | } |
89 | 101 | ||
90 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | 102 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); |
91 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
92 | 103 | ||
93 | size_t machine__fprintf(struct machine *machine, FILE *fp); | 104 | size_t machine__fprintf(struct machine *machine, FILE *fp); |
94 | 105 | ||
@@ -129,19 +140,19 @@ int machine__load_kallsyms(struct machine *machine, const char *filename, | |||
129 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | 140 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, |
130 | symbol_filter_t filter); | 141 | symbol_filter_t filter); |
131 | 142 | ||
132 | size_t machine__fprintf_dsos_buildid(struct machine *machine, | 143 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, |
133 | FILE *fp, bool with_hits); | 144 | bool (skip)(struct dso *dso, int parm), int parm); |
134 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp); | 145 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp); |
135 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | 146 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, |
136 | FILE *fp, bool with_hits); | 147 | bool (skip)(struct dso *dso, int parm), int parm); |
137 | 148 | ||
138 | void machine__destroy_kernel_maps(struct machine *machine); | 149 | void machine__destroy_kernel_maps(struct machine *machine); |
139 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | 150 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); |
140 | int machine__create_kernel_maps(struct machine *machine); | 151 | int machine__create_kernel_maps(struct machine *machine); |
141 | 152 | ||
142 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid); | 153 | int machines__create_kernel_maps(struct machines *machines, pid_t pid); |
143 | int machines__create_guest_kernel_maps(struct rb_root *machines); | 154 | int machines__create_guest_kernel_maps(struct machines *machines); |
144 | void machines__destroy_guest_kernel_maps(struct rb_root *machines); | 155 | void machines__destroy_kernel_maps(struct machines *machines); |
145 | 156 | ||
146 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | 157 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); |
147 | 158 | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 0328d45c4f2a..8bcdf9e54089 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include "strlist.h" | 11 | #include "strlist.h" |
12 | #include "vdso.h" | 12 | #include "vdso.h" |
13 | #include "build-id.h" | 13 | #include "build-id.h" |
14 | #include <linux/string.h> | ||
14 | 15 | ||
15 | const char *map_type__name[MAP__NR_TYPES] = { | 16 | const char *map_type__name[MAP__NR_TYPES] = { |
16 | [MAP__FUNCTION] = "Functions", | 17 | [MAP__FUNCTION] = "Functions", |
@@ -19,7 +20,9 @@ const char *map_type__name[MAP__NR_TYPES] = { | |||
19 | 20 | ||
20 | static inline int is_anon_memory(const char *filename) | 21 | static inline int is_anon_memory(const char *filename) |
21 | { | 22 | { |
22 | return strcmp(filename, "//anon") == 0; | 23 | return !strcmp(filename, "//anon") || |
24 | !strcmp(filename, "/dev/zero (deleted)") || | ||
25 | !strcmp(filename, "/anon_hugepage (deleted)"); | ||
23 | } | 26 | } |
24 | 27 | ||
25 | static inline int is_no_dso_memory(const char *filename) | 28 | static inline int is_no_dso_memory(const char *filename) |
@@ -28,29 +31,29 @@ static inline int is_no_dso_memory(const char *filename) | |||
28 | !strcmp(filename, "[heap]"); | 31 | !strcmp(filename, "[heap]"); |
29 | } | 32 | } |
30 | 33 | ||
31 | void map__init(struct map *self, enum map_type type, | 34 | void map__init(struct map *map, enum map_type type, |
32 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 35 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
33 | { | 36 | { |
34 | self->type = type; | 37 | map->type = type; |
35 | self->start = start; | 38 | map->start = start; |
36 | self->end = end; | 39 | map->end = end; |
37 | self->pgoff = pgoff; | 40 | map->pgoff = pgoff; |
38 | self->dso = dso; | 41 | map->dso = dso; |
39 | self->map_ip = map__map_ip; | 42 | map->map_ip = map__map_ip; |
40 | self->unmap_ip = map__unmap_ip; | 43 | map->unmap_ip = map__unmap_ip; |
41 | RB_CLEAR_NODE(&self->rb_node); | 44 | RB_CLEAR_NODE(&map->rb_node); |
42 | self->groups = NULL; | 45 | map->groups = NULL; |
43 | self->referenced = false; | 46 | map->referenced = false; |
44 | self->erange_warned = false; | 47 | map->erange_warned = false; |
45 | } | 48 | } |
46 | 49 | ||
47 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 50 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
48 | u64 pgoff, u32 pid, char *filename, | 51 | u64 pgoff, u32 pid, char *filename, |
49 | enum map_type type) | 52 | enum map_type type) |
50 | { | 53 | { |
51 | struct map *self = malloc(sizeof(*self)); | 54 | struct map *map = malloc(sizeof(*map)); |
52 | 55 | ||
53 | if (self != NULL) { | 56 | if (map != NULL) { |
54 | char newfilename[PATH_MAX]; | 57 | char newfilename[PATH_MAX]; |
55 | struct dso *dso; | 58 | struct dso *dso; |
56 | int anon, no_dso, vdso; | 59 | int anon, no_dso, vdso; |
@@ -73,10 +76,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
73 | if (dso == NULL) | 76 | if (dso == NULL) |
74 | goto out_delete; | 77 | goto out_delete; |
75 | 78 | ||
76 | map__init(self, type, start, start + len, pgoff, dso); | 79 | map__init(map, type, start, start + len, pgoff, dso); |
77 | 80 | ||
78 | if (anon || no_dso) { | 81 | if (anon || no_dso) { |
79 | self->map_ip = self->unmap_ip = identity__map_ip; | 82 | map->map_ip = map->unmap_ip = identity__map_ip; |
80 | 83 | ||
81 | /* | 84 | /* |
82 | * Set memory without DSO as loaded. All map__find_* | 85 | * Set memory without DSO as loaded. All map__find_* |
@@ -84,12 +87,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
84 | * unnecessary map__load warning. | 87 | * unnecessary map__load warning. |
85 | */ | 88 | */ |
86 | if (no_dso) | 89 | if (no_dso) |
87 | dso__set_loaded(dso, self->type); | 90 | dso__set_loaded(dso, map->type); |
88 | } | 91 | } |
89 | } | 92 | } |
90 | return self; | 93 | return map; |
91 | out_delete: | 94 | out_delete: |
92 | free(self); | 95 | free(map); |
93 | return NULL; | 96 | return NULL; |
94 | } | 97 | } |
95 | 98 | ||
@@ -112,48 +115,48 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
112 | return map; | 115 | return map; |
113 | } | 116 | } |
114 | 117 | ||
115 | void map__delete(struct map *self) | 118 | void map__delete(struct map *map) |
116 | { | 119 | { |
117 | free(self); | 120 | free(map); |
118 | } | 121 | } |
119 | 122 | ||
120 | void map__fixup_start(struct map *self) | 123 | void map__fixup_start(struct map *map) |
121 | { | 124 | { |
122 | struct rb_root *symbols = &self->dso->symbols[self->type]; | 125 | struct rb_root *symbols = &map->dso->symbols[map->type]; |
123 | struct rb_node *nd = rb_first(symbols); | 126 | struct rb_node *nd = rb_first(symbols); |
124 | if (nd != NULL) { | 127 | if (nd != NULL) { |
125 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | 128 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
126 | self->start = sym->start; | 129 | map->start = sym->start; |
127 | } | 130 | } |
128 | } | 131 | } |
129 | 132 | ||
130 | void map__fixup_end(struct map *self) | 133 | void map__fixup_end(struct map *map) |
131 | { | 134 | { |
132 | struct rb_root *symbols = &self->dso->symbols[self->type]; | 135 | struct rb_root *symbols = &map->dso->symbols[map->type]; |
133 | struct rb_node *nd = rb_last(symbols); | 136 | struct rb_node *nd = rb_last(symbols); |
134 | if (nd != NULL) { | 137 | if (nd != NULL) { |
135 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | 138 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
136 | self->end = sym->end; | 139 | map->end = sym->end; |
137 | } | 140 | } |
138 | } | 141 | } |
139 | 142 | ||
140 | #define DSO__DELETED "(deleted)" | 143 | #define DSO__DELETED "(deleted)" |
141 | 144 | ||
142 | int map__load(struct map *self, symbol_filter_t filter) | 145 | int map__load(struct map *map, symbol_filter_t filter) |
143 | { | 146 | { |
144 | const char *name = self->dso->long_name; | 147 | const char *name = map->dso->long_name; |
145 | int nr; | 148 | int nr; |
146 | 149 | ||
147 | if (dso__loaded(self->dso, self->type)) | 150 | if (dso__loaded(map->dso, map->type)) |
148 | return 0; | 151 | return 0; |
149 | 152 | ||
150 | nr = dso__load(self->dso, self, filter); | 153 | nr = dso__load(map->dso, map, filter); |
151 | if (nr < 0) { | 154 | if (nr < 0) { |
152 | if (self->dso->has_build_id) { | 155 | if (map->dso->has_build_id) { |
153 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 156 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
154 | 157 | ||
155 | build_id__sprintf(self->dso->build_id, | 158 | build_id__sprintf(map->dso->build_id, |
156 | sizeof(self->dso->build_id), | 159 | sizeof(map->dso->build_id), |
157 | sbuild_id); | 160 | sbuild_id); |
158 | pr_warning("%s with build id %s not found", | 161 | pr_warning("%s with build id %s not found", |
159 | name, sbuild_id); | 162 | name, sbuild_id); |
@@ -183,43 +186,36 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
183 | * Only applies to the kernel, as its symtabs aren't relative like the | 186 | * Only applies to the kernel, as its symtabs aren't relative like the |
184 | * module ones. | 187 | * module ones. |
185 | */ | 188 | */ |
186 | if (self->dso->kernel) | 189 | if (map->dso->kernel) |
187 | map__reloc_vmlinux(self); | 190 | map__reloc_vmlinux(map); |
188 | 191 | ||
189 | return 0; | 192 | return 0; |
190 | } | 193 | } |
191 | 194 | ||
192 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 195 | struct symbol *map__find_symbol(struct map *map, u64 addr, |
193 | symbol_filter_t filter) | 196 | symbol_filter_t filter) |
194 | { | 197 | { |
195 | if (map__load(self, filter) < 0) | 198 | if (map__load(map, filter) < 0) |
196 | return NULL; | 199 | return NULL; |
197 | 200 | ||
198 | return dso__find_symbol(self->dso, self->type, addr); | 201 | return dso__find_symbol(map->dso, map->type, addr); |
199 | } | 202 | } |
200 | 203 | ||
201 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 204 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, |
202 | symbol_filter_t filter) | 205 | symbol_filter_t filter) |
203 | { | 206 | { |
204 | if (map__load(self, filter) < 0) | 207 | if (map__load(map, filter) < 0) |
205 | return NULL; | 208 | return NULL; |
206 | 209 | ||
207 | if (!dso__sorted_by_name(self->dso, self->type)) | 210 | if (!dso__sorted_by_name(map->dso, map->type)) |
208 | dso__sort_by_name(self->dso, self->type); | 211 | dso__sort_by_name(map->dso, map->type); |
209 | 212 | ||
210 | return dso__find_symbol_by_name(self->dso, self->type, name); | 213 | return dso__find_symbol_by_name(map->dso, map->type, name); |
211 | } | 214 | } |
212 | 215 | ||
213 | struct map *map__clone(struct map *self) | 216 | struct map *map__clone(struct map *map) |
214 | { | 217 | { |
215 | struct map *map = malloc(sizeof(*self)); | 218 | return memdup(map, sizeof(*map)); |
216 | |||
217 | if (!map) | ||
218 | return NULL; | ||
219 | |||
220 | memcpy(map, self, sizeof(*self)); | ||
221 | |||
222 | return map; | ||
223 | } | 219 | } |
224 | 220 | ||
225 | int map__overlap(struct map *l, struct map *r) | 221 | int map__overlap(struct map *l, struct map *r) |
@@ -236,10 +232,10 @@ int map__overlap(struct map *l, struct map *r) | |||
236 | return 0; | 232 | return 0; |
237 | } | 233 | } |
238 | 234 | ||
239 | size_t map__fprintf(struct map *self, FILE *fp) | 235 | size_t map__fprintf(struct map *map, FILE *fp) |
240 | { | 236 | { |
241 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", | 237 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", |
242 | self->start, self->end, self->pgoff, self->dso->name); | 238 | map->start, map->end, map->pgoff, map->dso->name); |
243 | } | 239 | } |
244 | 240 | ||
245 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) | 241 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) |
@@ -527,9 +523,9 @@ static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | |||
527 | return ip - (s64)map->pgoff; | 523 | return ip - (s64)map->pgoff; |
528 | } | 524 | } |
529 | 525 | ||
530 | void map__reloc_vmlinux(struct map *self) | 526 | void map__reloc_vmlinux(struct map *map) |
531 | { | 527 | { |
532 | struct kmap *kmap = map__kmap(self); | 528 | struct kmap *kmap = map__kmap(map); |
533 | s64 reloc; | 529 | s64 reloc; |
534 | 530 | ||
535 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | 531 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) |
@@ -541,9 +537,9 @@ void map__reloc_vmlinux(struct map *self) | |||
541 | if (!reloc) | 537 | if (!reloc) |
542 | return; | 538 | return; |
543 | 539 | ||
544 | self->map_ip = map__reloc_map_ip; | 540 | map->map_ip = map__reloc_map_ip; |
545 | self->unmap_ip = map__reloc_unmap_ip; | 541 | map->unmap_ip = map__reloc_unmap_ip; |
546 | self->pgoff = reloc; | 542 | map->pgoff = reloc; |
547 | } | 543 | } |
548 | 544 | ||
549 | void maps__insert(struct rb_root *maps, struct map *map) | 545 | void maps__insert(struct rb_root *maps, struct map *map) |
@@ -566,9 +562,9 @@ void maps__insert(struct rb_root *maps, struct map *map) | |||
566 | rb_insert_color(&map->rb_node, maps); | 562 | rb_insert_color(&map->rb_node, maps); |
567 | } | 563 | } |
568 | 564 | ||
569 | void maps__remove(struct rb_root *self, struct map *map) | 565 | void maps__remove(struct rb_root *maps, struct map *map) |
570 | { | 566 | { |
571 | rb_erase(&map->rb_node, self); | 567 | rb_erase(&map->rb_node, maps); |
572 | } | 568 | } |
573 | 569 | ||
574 | struct map *maps__find(struct rb_root *maps, u64 ip) | 570 | struct map *maps__find(struct rb_root *maps, u64 ip) |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index bcb39e2a6965..a887f2c9dfbb 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -57,9 +57,9 @@ struct map_groups { | |||
57 | struct machine *machine; | 57 | struct machine *machine; |
58 | }; | 58 | }; |
59 | 59 | ||
60 | static inline struct kmap *map__kmap(struct map *self) | 60 | static inline struct kmap *map__kmap(struct map *map) |
61 | { | 61 | { |
62 | return (struct kmap *)(self + 1); | 62 | return (struct kmap *)(map + 1); |
63 | } | 63 | } |
64 | 64 | ||
65 | static inline u64 map__map_ip(struct map *map, u64 ip) | 65 | static inline u64 map__map_ip(struct map *map, u64 ip) |
@@ -85,27 +85,27 @@ struct symbol; | |||
85 | 85 | ||
86 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 86 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
87 | 87 | ||
88 | void map__init(struct map *self, enum map_type type, | 88 | void map__init(struct map *map, enum map_type type, |
89 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 89 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
90 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 90 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
91 | u64 pgoff, u32 pid, char *filename, | 91 | u64 pgoff, u32 pid, char *filename, |
92 | enum map_type type); | 92 | enum map_type type); |
93 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 93 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
94 | void map__delete(struct map *self); | 94 | void map__delete(struct map *map); |
95 | struct map *map__clone(struct map *self); | 95 | struct map *map__clone(struct map *map); |
96 | int map__overlap(struct map *l, struct map *r); | 96 | int map__overlap(struct map *l, struct map *r); |
97 | size_t map__fprintf(struct map *self, FILE *fp); | 97 | size_t map__fprintf(struct map *map, FILE *fp); |
98 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); | 98 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); |
99 | 99 | ||
100 | int map__load(struct map *self, symbol_filter_t filter); | 100 | int map__load(struct map *map, symbol_filter_t filter); |
101 | struct symbol *map__find_symbol(struct map *self, | 101 | struct symbol *map__find_symbol(struct map *map, |
102 | u64 addr, symbol_filter_t filter); | 102 | u64 addr, symbol_filter_t filter); |
103 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 103 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, |
104 | symbol_filter_t filter); | 104 | symbol_filter_t filter); |
105 | void map__fixup_start(struct map *self); | 105 | void map__fixup_start(struct map *map); |
106 | void map__fixup_end(struct map *self); | 106 | void map__fixup_end(struct map *map); |
107 | 107 | ||
108 | void map__reloc_vmlinux(struct map *self); | 108 | void map__reloc_vmlinux(struct map *map); |
109 | 109 | ||
110 | size_t __map_groups__fprintf_maps(struct map_groups *mg, | 110 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
111 | enum map_type type, int verbose, FILE *fp); | 111 | enum map_type type, int verbose, FILE *fp); |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 2d8d53bec17e..6c8bb0fb189b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -10,7 +10,7 @@ | |||
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include <lk/debugfs.h> |
14 | #include "parse-events-bison.h" | 14 | #include "parse-events-bison.h" |
15 | #define YY_EXTRA_TYPE int | 15 | #define YY_EXTRA_TYPE int |
16 | #include "parse-events-flex.h" | 16 | #include "parse-events-flex.h" |
@@ -380,8 +380,8 @@ static int add_tracepoint(struct list_head **listp, int *idx, | |||
380 | return 0; | 380 | return 0; |
381 | } | 381 | } |
382 | 382 | ||
383 | static int add_tracepoint_multi(struct list_head **list, int *idx, | 383 | static int add_tracepoint_multi_event(struct list_head **list, int *idx, |
384 | char *sys_name, char *evt_name) | 384 | char *sys_name, char *evt_name) |
385 | { | 385 | { |
386 | char evt_path[MAXPATHLEN]; | 386 | char evt_path[MAXPATHLEN]; |
387 | struct dirent *evt_ent; | 387 | struct dirent *evt_ent; |
@@ -408,6 +408,47 @@ static int add_tracepoint_multi(struct list_head **list, int *idx, | |||
408 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); | 408 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
409 | } | 409 | } |
410 | 410 | ||
411 | closedir(evt_dir); | ||
412 | return ret; | ||
413 | } | ||
414 | |||
415 | static int add_tracepoint_event(struct list_head **list, int *idx, | ||
416 | char *sys_name, char *evt_name) | ||
417 | { | ||
418 | return strpbrk(evt_name, "*?") ? | ||
419 | add_tracepoint_multi_event(list, idx, sys_name, evt_name) : | ||
420 | add_tracepoint(list, idx, sys_name, evt_name); | ||
421 | } | ||
422 | |||
423 | static int add_tracepoint_multi_sys(struct list_head **list, int *idx, | ||
424 | char *sys_name, char *evt_name) | ||
425 | { | ||
426 | struct dirent *events_ent; | ||
427 | DIR *events_dir; | ||
428 | int ret = 0; | ||
429 | |||
430 | events_dir = opendir(tracing_events_path); | ||
431 | if (!events_dir) { | ||
432 | perror("Can't open event dir"); | ||
433 | return -1; | ||
434 | } | ||
435 | |||
436 | while (!ret && (events_ent = readdir(events_dir))) { | ||
437 | if (!strcmp(events_ent->d_name, ".") | ||
438 | || !strcmp(events_ent->d_name, "..") | ||
439 | || !strcmp(events_ent->d_name, "enable") | ||
440 | || !strcmp(events_ent->d_name, "header_event") | ||
441 | || !strcmp(events_ent->d_name, "header_page")) | ||
442 | continue; | ||
443 | |||
444 | if (!strglobmatch(events_ent->d_name, sys_name)) | ||
445 | continue; | ||
446 | |||
447 | ret = add_tracepoint_event(list, idx, events_ent->d_name, | ||
448 | evt_name); | ||
449 | } | ||
450 | |||
451 | closedir(events_dir); | ||
411 | return ret; | 452 | return ret; |
412 | } | 453 | } |
413 | 454 | ||
@@ -420,9 +461,10 @@ int parse_events_add_tracepoint(struct list_head **list, int *idx, | |||
420 | if (ret) | 461 | if (ret) |
421 | return ret; | 462 | return ret; |
422 | 463 | ||
423 | return strpbrk(event, "*?") ? | 464 | if (strpbrk(sys, "*?")) |
424 | add_tracepoint_multi(list, idx, sys, event) : | 465 | return add_tracepoint_multi_sys(list, idx, sys, event); |
425 | add_tracepoint(list, idx, sys, event); | 466 | else |
467 | return add_tracepoint_event(list, idx, sys, event); | ||
426 | } | 468 | } |
427 | 469 | ||
428 | static int | 470 | static int |
@@ -492,7 +534,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx, | |||
492 | } | 534 | } |
493 | 535 | ||
494 | static int config_term(struct perf_event_attr *attr, | 536 | static int config_term(struct perf_event_attr *attr, |
495 | struct parse_events__term *term) | 537 | struct parse_events_term *term) |
496 | { | 538 | { |
497 | #define CHECK_TYPE_VAL(type) \ | 539 | #define CHECK_TYPE_VAL(type) \ |
498 | do { \ | 540 | do { \ |
@@ -537,7 +579,7 @@ do { \ | |||
537 | static int config_attr(struct perf_event_attr *attr, | 579 | static int config_attr(struct perf_event_attr *attr, |
538 | struct list_head *head, int fail) | 580 | struct list_head *head, int fail) |
539 | { | 581 | { |
540 | struct parse_events__term *term; | 582 | struct parse_events_term *term; |
541 | 583 | ||
542 | list_for_each_entry(term, head, list) | 584 | list_for_each_entry(term, head, list) |
543 | if (config_term(attr, term) && fail) | 585 | if (config_term(attr, term) && fail) |
@@ -563,14 +605,14 @@ int parse_events_add_numeric(struct list_head **list, int *idx, | |||
563 | return add_event(list, idx, &attr, NULL); | 605 | return add_event(list, idx, &attr, NULL); |
564 | } | 606 | } |
565 | 607 | ||
566 | static int parse_events__is_name_term(struct parse_events__term *term) | 608 | static int parse_events__is_name_term(struct parse_events_term *term) |
567 | { | 609 | { |
568 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | 610 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; |
569 | } | 611 | } |
570 | 612 | ||
571 | static char *pmu_event_name(struct list_head *head_terms) | 613 | static char *pmu_event_name(struct list_head *head_terms) |
572 | { | 614 | { |
573 | struct parse_events__term *term; | 615 | struct parse_events_term *term; |
574 | 616 | ||
575 | list_for_each_entry(term, head_terms, list) | 617 | list_for_each_entry(term, head_terms, list) |
576 | if (parse_events__is_name_term(term)) | 618 | if (parse_events__is_name_term(term)) |
@@ -657,14 +699,6 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
657 | int exclude = eu | ek | eh; | 699 | int exclude = eu | ek | eh; |
658 | int exclude_GH = evsel ? evsel->exclude_GH : 0; | 700 | int exclude_GH = evsel ? evsel->exclude_GH : 0; |
659 | 701 | ||
660 | /* | ||
661 | * We are here for group and 'GH' was not set as event | ||
662 | * modifier and whatever event/group modifier override | ||
663 | * default 'GH' setup. | ||
664 | */ | ||
665 | if (evsel && !exclude_GH) | ||
666 | eH = eG = 0; | ||
667 | |||
668 | memset(mod, 0, sizeof(*mod)); | 702 | memset(mod, 0, sizeof(*mod)); |
669 | 703 | ||
670 | while (*str) { | 704 | while (*str) { |
@@ -814,7 +848,7 @@ static int parse_events__scanner(const char *str, void *data, int start_token) | |||
814 | */ | 848 | */ |
815 | int parse_events_terms(struct list_head *terms, const char *str) | 849 | int parse_events_terms(struct list_head *terms, const char *str) |
816 | { | 850 | { |
817 | struct parse_events_data__terms data = { | 851 | struct parse_events_terms data = { |
818 | .terms = NULL, | 852 | .terms = NULL, |
819 | }; | 853 | }; |
820 | int ret; | 854 | int ret; |
@@ -830,10 +864,9 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
830 | return ret; | 864 | return ret; |
831 | } | 865 | } |
832 | 866 | ||
833 | int parse_events(struct perf_evlist *evlist, const char *str, | 867 | int parse_events(struct perf_evlist *evlist, const char *str) |
834 | int unset __maybe_unused) | ||
835 | { | 868 | { |
836 | struct parse_events_data__events data = { | 869 | struct parse_events_evlist data = { |
837 | .list = LIST_HEAD_INIT(data.list), | 870 | .list = LIST_HEAD_INIT(data.list), |
838 | .idx = evlist->nr_entries, | 871 | .idx = evlist->nr_entries, |
839 | }; | 872 | }; |
@@ -843,6 +876,7 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
843 | if (!ret) { | 876 | if (!ret) { |
844 | int entries = data.idx - evlist->nr_entries; | 877 | int entries = data.idx - evlist->nr_entries; |
845 | perf_evlist__splice_list_tail(evlist, &data.list, entries); | 878 | perf_evlist__splice_list_tail(evlist, &data.list, entries); |
879 | evlist->nr_groups += data.nr_groups; | ||
846 | return 0; | 880 | return 0; |
847 | } | 881 | } |
848 | 882 | ||
@@ -858,7 +892,7 @@ int parse_events_option(const struct option *opt, const char *str, | |||
858 | int unset __maybe_unused) | 892 | int unset __maybe_unused) |
859 | { | 893 | { |
860 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 894 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
861 | int ret = parse_events(evlist, str, unset); | 895 | int ret = parse_events(evlist, str); |
862 | 896 | ||
863 | if (ret) { | 897 | if (ret) { |
864 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | 898 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); |
@@ -1121,16 +1155,16 @@ void print_events(const char *event_glob, bool name_only) | |||
1121 | print_tracepoint_events(NULL, NULL, name_only); | 1155 | print_tracepoint_events(NULL, NULL, name_only); |
1122 | } | 1156 | } |
1123 | 1157 | ||
1124 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | 1158 | int parse_events__is_hardcoded_term(struct parse_events_term *term) |
1125 | { | 1159 | { |
1126 | return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; | 1160 | return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; |
1127 | } | 1161 | } |
1128 | 1162 | ||
1129 | static int new_term(struct parse_events__term **_term, int type_val, | 1163 | static int new_term(struct parse_events_term **_term, int type_val, |
1130 | int type_term, char *config, | 1164 | int type_term, char *config, |
1131 | char *str, u64 num) | 1165 | char *str, u64 num) |
1132 | { | 1166 | { |
1133 | struct parse_events__term *term; | 1167 | struct parse_events_term *term; |
1134 | 1168 | ||
1135 | term = zalloc(sizeof(*term)); | 1169 | term = zalloc(sizeof(*term)); |
1136 | if (!term) | 1170 | if (!term) |
@@ -1156,21 +1190,21 @@ static int new_term(struct parse_events__term **_term, int type_val, | |||
1156 | return 0; | 1190 | return 0; |
1157 | } | 1191 | } |
1158 | 1192 | ||
1159 | int parse_events__term_num(struct parse_events__term **term, | 1193 | int parse_events_term__num(struct parse_events_term **term, |
1160 | int type_term, char *config, u64 num) | 1194 | int type_term, char *config, u64 num) |
1161 | { | 1195 | { |
1162 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, | 1196 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, |
1163 | config, NULL, num); | 1197 | config, NULL, num); |
1164 | } | 1198 | } |
1165 | 1199 | ||
1166 | int parse_events__term_str(struct parse_events__term **term, | 1200 | int parse_events_term__str(struct parse_events_term **term, |
1167 | int type_term, char *config, char *str) | 1201 | int type_term, char *config, char *str) |
1168 | { | 1202 | { |
1169 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, | 1203 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, |
1170 | config, str, 0); | 1204 | config, str, 0); |
1171 | } | 1205 | } |
1172 | 1206 | ||
1173 | int parse_events__term_sym_hw(struct parse_events__term **term, | 1207 | int parse_events_term__sym_hw(struct parse_events_term **term, |
1174 | char *config, unsigned idx) | 1208 | char *config, unsigned idx) |
1175 | { | 1209 | { |
1176 | struct event_symbol *sym; | 1210 | struct event_symbol *sym; |
@@ -1188,8 +1222,8 @@ int parse_events__term_sym_hw(struct parse_events__term **term, | |||
1188 | (char *) "event", (char *) sym->symbol, 0); | 1222 | (char *) "event", (char *) sym->symbol, 0); |
1189 | } | 1223 | } |
1190 | 1224 | ||
1191 | int parse_events__term_clone(struct parse_events__term **new, | 1225 | int parse_events_term__clone(struct parse_events_term **new, |
1192 | struct parse_events__term *term) | 1226 | struct parse_events_term *term) |
1193 | { | 1227 | { |
1194 | return new_term(new, term->type_val, term->type_term, term->config, | 1228 | return new_term(new, term->type_val, term->type_term, term->config, |
1195 | term->val.str, term->val.num); | 1229 | term->val.str, term->val.num); |
@@ -1197,7 +1231,7 @@ int parse_events__term_clone(struct parse_events__term **new, | |||
1197 | 1231 | ||
1198 | void parse_events__free_terms(struct list_head *terms) | 1232 | void parse_events__free_terms(struct list_head *terms) |
1199 | { | 1233 | { |
1200 | struct parse_events__term *term, *h; | 1234 | struct parse_events_term *term, *h; |
1201 | 1235 | ||
1202 | list_for_each_entry_safe(term, h, terms, list) | 1236 | list_for_each_entry_safe(term, h, terms, list) |
1203 | free(term); | 1237 | free(term); |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b7af80b8bdda..8a4859315fd9 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -29,8 +29,7 @@ const char *event_type(int type); | |||
29 | 29 | ||
30 | extern int parse_events_option(const struct option *opt, const char *str, | 30 | extern int parse_events_option(const struct option *opt, const char *str, |
31 | int unset); | 31 | int unset); |
32 | extern int parse_events(struct perf_evlist *evlist, const char *str, | 32 | extern int parse_events(struct perf_evlist *evlist, const char *str); |
33 | int unset); | ||
34 | extern int parse_events_terms(struct list_head *terms, const char *str); | 33 | extern int parse_events_terms(struct list_head *terms, const char *str); |
35 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 34 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
36 | 35 | ||
@@ -51,7 +50,7 @@ enum { | |||
51 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | 50 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, |
52 | }; | 51 | }; |
53 | 52 | ||
54 | struct parse_events__term { | 53 | struct parse_events_term { |
55 | char *config; | 54 | char *config; |
56 | union { | 55 | union { |
57 | char *str; | 56 | char *str; |
@@ -62,24 +61,25 @@ struct parse_events__term { | |||
62 | struct list_head list; | 61 | struct list_head list; |
63 | }; | 62 | }; |
64 | 63 | ||
65 | struct parse_events_data__events { | 64 | struct parse_events_evlist { |
66 | struct list_head list; | 65 | struct list_head list; |
67 | int idx; | 66 | int idx; |
67 | int nr_groups; | ||
68 | }; | 68 | }; |
69 | 69 | ||
70 | struct parse_events_data__terms { | 70 | struct parse_events_terms { |
71 | struct list_head *terms; | 71 | struct list_head *terms; |
72 | }; | 72 | }; |
73 | 73 | ||
74 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | 74 | int parse_events__is_hardcoded_term(struct parse_events_term *term); |
75 | int parse_events__term_num(struct parse_events__term **_term, | 75 | int parse_events_term__num(struct parse_events_term **_term, |
76 | int type_term, char *config, u64 num); | 76 | int type_term, char *config, u64 num); |
77 | int parse_events__term_str(struct parse_events__term **_term, | 77 | int parse_events_term__str(struct parse_events_term **_term, |
78 | int type_term, char *config, char *str); | 78 | int type_term, char *config, char *str); |
79 | int parse_events__term_sym_hw(struct parse_events__term **term, | 79 | int parse_events_term__sym_hw(struct parse_events_term **term, |
80 | char *config, unsigned idx); | 80 | char *config, unsigned idx); |
81 | int parse_events__term_clone(struct parse_events__term **new, | 81 | int parse_events_term__clone(struct parse_events_term **new, |
82 | struct parse_events__term *term); | 82 | struct parse_events_term *term); |
83 | void parse_events__free_terms(struct list_head *terms); | 83 | void parse_events__free_terms(struct list_head *terms); |
84 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); | 84 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
85 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | 85 | int parse_events__modifier_group(struct list_head *list, char *event_mod); |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0f9914ae6bac..afc44c18dfe1 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -1,5 +1,4 @@ | |||
1 | %pure-parser | 1 | %pure-parser |
2 | %name-prefix "parse_events_" | ||
3 | %parse-param {void *_data} | 2 | %parse-param {void *_data} |
4 | %parse-param {void *scanner} | 3 | %parse-param {void *scanner} |
5 | %lex-param {void* scanner} | 4 | %lex-param {void* scanner} |
@@ -23,6 +22,14 @@ do { \ | |||
23 | YYABORT; \ | 22 | YYABORT; \ |
24 | } while (0) | 23 | } while (0) |
25 | 24 | ||
25 | static inc_group_count(struct list_head *list, | ||
26 | struct parse_events_evlist *data) | ||
27 | { | ||
28 | /* Count groups only have more than 1 members */ | ||
29 | if (!list_is_last(list->next, list)) | ||
30 | data->nr_groups++; | ||
31 | } | ||
32 | |||
26 | %} | 33 | %} |
27 | 34 | ||
28 | %token PE_START_EVENTS PE_START_TERMS | 35 | %token PE_START_EVENTS PE_START_TERMS |
@@ -68,7 +75,7 @@ do { \ | |||
68 | char *str; | 75 | char *str; |
69 | u64 num; | 76 | u64 num; |
70 | struct list_head *head; | 77 | struct list_head *head; |
71 | struct parse_events__term *term; | 78 | struct parse_events_term *term; |
72 | } | 79 | } |
73 | %% | 80 | %% |
74 | 81 | ||
@@ -79,7 +86,7 @@ PE_START_TERMS start_terms | |||
79 | 86 | ||
80 | start_events: groups | 87 | start_events: groups |
81 | { | 88 | { |
82 | struct parse_events_data__events *data = _data; | 89 | struct parse_events_evlist *data = _data; |
83 | 90 | ||
84 | parse_events_update_lists($1, &data->list); | 91 | parse_events_update_lists($1, &data->list); |
85 | } | 92 | } |
@@ -123,6 +130,7 @@ PE_NAME '{' events '}' | |||
123 | { | 130 | { |
124 | struct list_head *list = $3; | 131 | struct list_head *list = $3; |
125 | 132 | ||
133 | inc_group_count(list, _data); | ||
126 | parse_events__set_leader($1, list); | 134 | parse_events__set_leader($1, list); |
127 | $$ = list; | 135 | $$ = list; |
128 | } | 136 | } |
@@ -131,6 +139,7 @@ PE_NAME '{' events '}' | |||
131 | { | 139 | { |
132 | struct list_head *list = $2; | 140 | struct list_head *list = $2; |
133 | 141 | ||
142 | inc_group_count(list, _data); | ||
134 | parse_events__set_leader(NULL, list); | 143 | parse_events__set_leader(NULL, list); |
135 | $$ = list; | 144 | $$ = list; |
136 | } | 145 | } |
@@ -186,7 +195,7 @@ event_def: event_pmu | | |||
186 | event_pmu: | 195 | event_pmu: |
187 | PE_NAME '/' event_config '/' | 196 | PE_NAME '/' event_config '/' |
188 | { | 197 | { |
189 | struct parse_events_data__events *data = _data; | 198 | struct parse_events_evlist *data = _data; |
190 | struct list_head *list = NULL; | 199 | struct list_head *list = NULL; |
191 | 200 | ||
192 | ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); | 201 | ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); |
@@ -202,7 +211,7 @@ PE_VALUE_SYM_SW | |||
202 | event_legacy_symbol: | 211 | event_legacy_symbol: |
203 | value_sym '/' event_config '/' | 212 | value_sym '/' event_config '/' |
204 | { | 213 | { |
205 | struct parse_events_data__events *data = _data; | 214 | struct parse_events_evlist *data = _data; |
206 | struct list_head *list = NULL; | 215 | struct list_head *list = NULL; |
207 | int type = $1 >> 16; | 216 | int type = $1 >> 16; |
208 | int config = $1 & 255; | 217 | int config = $1 & 255; |
@@ -215,7 +224,7 @@ value_sym '/' event_config '/' | |||
215 | | | 224 | | |
216 | value_sym sep_slash_dc | 225 | value_sym sep_slash_dc |
217 | { | 226 | { |
218 | struct parse_events_data__events *data = _data; | 227 | struct parse_events_evlist *data = _data; |
219 | struct list_head *list = NULL; | 228 | struct list_head *list = NULL; |
220 | int type = $1 >> 16; | 229 | int type = $1 >> 16; |
221 | int config = $1 & 255; | 230 | int config = $1 & 255; |
@@ -228,7 +237,7 @@ value_sym sep_slash_dc | |||
228 | event_legacy_cache: | 237 | event_legacy_cache: |
229 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | 238 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT |
230 | { | 239 | { |
231 | struct parse_events_data__events *data = _data; | 240 | struct parse_events_evlist *data = _data; |
232 | struct list_head *list = NULL; | 241 | struct list_head *list = NULL; |
233 | 242 | ||
234 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); | 243 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); |
@@ -237,7 +246,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | |||
237 | | | 246 | | |
238 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | 247 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT |
239 | { | 248 | { |
240 | struct parse_events_data__events *data = _data; | 249 | struct parse_events_evlist *data = _data; |
241 | struct list_head *list = NULL; | 250 | struct list_head *list = NULL; |
242 | 251 | ||
243 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); | 252 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); |
@@ -246,7 +255,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | |||
246 | | | 255 | | |
247 | PE_NAME_CACHE_TYPE | 256 | PE_NAME_CACHE_TYPE |
248 | { | 257 | { |
249 | struct parse_events_data__events *data = _data; | 258 | struct parse_events_evlist *data = _data; |
250 | struct list_head *list = NULL; | 259 | struct list_head *list = NULL; |
251 | 260 | ||
252 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); | 261 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); |
@@ -256,7 +265,7 @@ PE_NAME_CACHE_TYPE | |||
256 | event_legacy_mem: | 265 | event_legacy_mem: |
257 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | 266 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc |
258 | { | 267 | { |
259 | struct parse_events_data__events *data = _data; | 268 | struct parse_events_evlist *data = _data; |
260 | struct list_head *list = NULL; | 269 | struct list_head *list = NULL; |
261 | 270 | ||
262 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 271 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, |
@@ -266,7 +275,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | |||
266 | | | 275 | | |
267 | PE_PREFIX_MEM PE_VALUE sep_dc | 276 | PE_PREFIX_MEM PE_VALUE sep_dc |
268 | { | 277 | { |
269 | struct parse_events_data__events *data = _data; | 278 | struct parse_events_evlist *data = _data; |
270 | struct list_head *list = NULL; | 279 | struct list_head *list = NULL; |
271 | 280 | ||
272 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 281 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, |
@@ -277,7 +286,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
277 | event_legacy_tracepoint: | 286 | event_legacy_tracepoint: |
278 | PE_NAME ':' PE_NAME | 287 | PE_NAME ':' PE_NAME |
279 | { | 288 | { |
280 | struct parse_events_data__events *data = _data; | 289 | struct parse_events_evlist *data = _data; |
281 | struct list_head *list = NULL; | 290 | struct list_head *list = NULL; |
282 | 291 | ||
283 | ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); | 292 | ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); |
@@ -287,7 +296,7 @@ PE_NAME ':' PE_NAME | |||
287 | event_legacy_numeric: | 296 | event_legacy_numeric: |
288 | PE_VALUE ':' PE_VALUE | 297 | PE_VALUE ':' PE_VALUE |
289 | { | 298 | { |
290 | struct parse_events_data__events *data = _data; | 299 | struct parse_events_evlist *data = _data; |
291 | struct list_head *list = NULL; | 300 | struct list_head *list = NULL; |
292 | 301 | ||
293 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); | 302 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); |
@@ -297,7 +306,7 @@ PE_VALUE ':' PE_VALUE | |||
297 | event_legacy_raw: | 306 | event_legacy_raw: |
298 | PE_RAW | 307 | PE_RAW |
299 | { | 308 | { |
300 | struct parse_events_data__events *data = _data; | 309 | struct parse_events_evlist *data = _data; |
301 | struct list_head *list = NULL; | 310 | struct list_head *list = NULL; |
302 | 311 | ||
303 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, | 312 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, |
@@ -307,7 +316,7 @@ PE_RAW | |||
307 | 316 | ||
308 | start_terms: event_config | 317 | start_terms: event_config |
309 | { | 318 | { |
310 | struct parse_events_data__terms *data = _data; | 319 | struct parse_events_terms *data = _data; |
311 | data->terms = $1; | 320 | data->terms = $1; |
312 | } | 321 | } |
313 | 322 | ||
@@ -315,7 +324,7 @@ event_config: | |||
315 | event_config ',' event_term | 324 | event_config ',' event_term |
316 | { | 325 | { |
317 | struct list_head *head = $1; | 326 | struct list_head *head = $1; |
318 | struct parse_events__term *term = $3; | 327 | struct parse_events_term *term = $3; |
319 | 328 | ||
320 | ABORT_ON(!head); | 329 | ABORT_ON(!head); |
321 | list_add_tail(&term->list, head); | 330 | list_add_tail(&term->list, head); |
@@ -325,7 +334,7 @@ event_config ',' event_term | |||
325 | event_term | 334 | event_term |
326 | { | 335 | { |
327 | struct list_head *head = malloc(sizeof(*head)); | 336 | struct list_head *head = malloc(sizeof(*head)); |
328 | struct parse_events__term *term = $1; | 337 | struct parse_events_term *term = $1; |
329 | 338 | ||
330 | ABORT_ON(!head); | 339 | ABORT_ON(!head); |
331 | INIT_LIST_HEAD(head); | 340 | INIT_LIST_HEAD(head); |
@@ -336,70 +345,70 @@ event_term | |||
336 | event_term: | 345 | event_term: |
337 | PE_NAME '=' PE_NAME | 346 | PE_NAME '=' PE_NAME |
338 | { | 347 | { |
339 | struct parse_events__term *term; | 348 | struct parse_events_term *term; |
340 | 349 | ||
341 | ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER, | 350 | ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, |
342 | $1, $3)); | 351 | $1, $3)); |
343 | $$ = term; | 352 | $$ = term; |
344 | } | 353 | } |
345 | | | 354 | | |
346 | PE_NAME '=' PE_VALUE | 355 | PE_NAME '=' PE_VALUE |
347 | { | 356 | { |
348 | struct parse_events__term *term; | 357 | struct parse_events_term *term; |
349 | 358 | ||
350 | ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 359 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
351 | $1, $3)); | 360 | $1, $3)); |
352 | $$ = term; | 361 | $$ = term; |
353 | } | 362 | } |
354 | | | 363 | | |
355 | PE_NAME '=' PE_VALUE_SYM_HW | 364 | PE_NAME '=' PE_VALUE_SYM_HW |
356 | { | 365 | { |
357 | struct parse_events__term *term; | 366 | struct parse_events_term *term; |
358 | int config = $3 & 255; | 367 | int config = $3 & 255; |
359 | 368 | ||
360 | ABORT_ON(parse_events__term_sym_hw(&term, $1, config)); | 369 | ABORT_ON(parse_events_term__sym_hw(&term, $1, config)); |
361 | $$ = term; | 370 | $$ = term; |
362 | } | 371 | } |
363 | | | 372 | | |
364 | PE_NAME | 373 | PE_NAME |
365 | { | 374 | { |
366 | struct parse_events__term *term; | 375 | struct parse_events_term *term; |
367 | 376 | ||
368 | ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 377 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
369 | $1, 1)); | 378 | $1, 1)); |
370 | $$ = term; | 379 | $$ = term; |
371 | } | 380 | } |
372 | | | 381 | | |
373 | PE_VALUE_SYM_HW | 382 | PE_VALUE_SYM_HW |
374 | { | 383 | { |
375 | struct parse_events__term *term; | 384 | struct parse_events_term *term; |
376 | int config = $1 & 255; | 385 | int config = $1 & 255; |
377 | 386 | ||
378 | ABORT_ON(parse_events__term_sym_hw(&term, NULL, config)); | 387 | ABORT_ON(parse_events_term__sym_hw(&term, NULL, config)); |
379 | $$ = term; | 388 | $$ = term; |
380 | } | 389 | } |
381 | | | 390 | | |
382 | PE_TERM '=' PE_NAME | 391 | PE_TERM '=' PE_NAME |
383 | { | 392 | { |
384 | struct parse_events__term *term; | 393 | struct parse_events_term *term; |
385 | 394 | ||
386 | ABORT_ON(parse_events__term_str(&term, (int)$1, NULL, $3)); | 395 | ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3)); |
387 | $$ = term; | 396 | $$ = term; |
388 | } | 397 | } |
389 | | | 398 | | |
390 | PE_TERM '=' PE_VALUE | 399 | PE_TERM '=' PE_VALUE |
391 | { | 400 | { |
392 | struct parse_events__term *term; | 401 | struct parse_events_term *term; |
393 | 402 | ||
394 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, $3)); | 403 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3)); |
395 | $$ = term; | 404 | $$ = term; |
396 | } | 405 | } |
397 | | | 406 | | |
398 | PE_TERM | 407 | PE_TERM |
399 | { | 408 | { |
400 | struct parse_events__term *term; | 409 | struct parse_events_term *term; |
401 | 410 | ||
402 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, 1)); | 411 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1)); |
403 | $$ = term; | 412 | $$ = term; |
404 | } | 413 | } |
405 | 414 | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 9bdc60c6f138..4c6f9c490a8d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | #include <linux/list.h> | 1 | #include <linux/list.h> |
3 | #include <sys/types.h> | 2 | #include <sys/types.h> |
4 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
@@ -11,6 +10,19 @@ | |||
11 | #include "parse-events.h" | 10 | #include "parse-events.h" |
12 | #include "cpumap.h" | 11 | #include "cpumap.h" |
13 | 12 | ||
13 | struct perf_pmu_alias { | ||
14 | char *name; | ||
15 | struct list_head terms; | ||
16 | struct list_head list; | ||
17 | }; | ||
18 | |||
19 | struct perf_pmu_format { | ||
20 | char *name; | ||
21 | int value; | ||
22 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
23 | struct list_head list; | ||
24 | }; | ||
25 | |||
14 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" | 26 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" |
15 | 27 | ||
16 | int perf_pmu_parse(struct list_head *list, char *name); | 28 | int perf_pmu_parse(struct list_head *list, char *name); |
@@ -85,7 +97,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
85 | 97 | ||
86 | static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) | 98 | static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) |
87 | { | 99 | { |
88 | struct perf_pmu__alias *alias; | 100 | struct perf_pmu_alias *alias; |
89 | char buf[256]; | 101 | char buf[256]; |
90 | int ret; | 102 | int ret; |
91 | 103 | ||
@@ -172,15 +184,15 @@ static int pmu_aliases(char *name, struct list_head *head) | |||
172 | return 0; | 184 | return 0; |
173 | } | 185 | } |
174 | 186 | ||
175 | static int pmu_alias_terms(struct perf_pmu__alias *alias, | 187 | static int pmu_alias_terms(struct perf_pmu_alias *alias, |
176 | struct list_head *terms) | 188 | struct list_head *terms) |
177 | { | 189 | { |
178 | struct parse_events__term *term, *clone; | 190 | struct parse_events_term *term, *clone; |
179 | LIST_HEAD(list); | 191 | LIST_HEAD(list); |
180 | int ret; | 192 | int ret; |
181 | 193 | ||
182 | list_for_each_entry(term, &alias->terms, list) { | 194 | list_for_each_entry(term, &alias->terms, list) { |
183 | ret = parse_events__term_clone(&clone, term); | 195 | ret = parse_events_term__clone(&clone, term); |
184 | if (ret) { | 196 | if (ret) { |
185 | parse_events__free_terms(&list); | 197 | parse_events__free_terms(&list); |
186 | return ret; | 198 | return ret; |
@@ -360,10 +372,10 @@ struct perf_pmu *perf_pmu__find(char *name) | |||
360 | return pmu_lookup(name); | 372 | return pmu_lookup(name); |
361 | } | 373 | } |
362 | 374 | ||
363 | static struct perf_pmu__format* | 375 | static struct perf_pmu_format * |
364 | pmu_find_format(struct list_head *formats, char *name) | 376 | pmu_find_format(struct list_head *formats, char *name) |
365 | { | 377 | { |
366 | struct perf_pmu__format *format; | 378 | struct perf_pmu_format *format; |
367 | 379 | ||
368 | list_for_each_entry(format, formats, list) | 380 | list_for_each_entry(format, formats, list) |
369 | if (!strcmp(format->name, name)) | 381 | if (!strcmp(format->name, name)) |
@@ -403,9 +415,9 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
403 | */ | 415 | */ |
404 | static int pmu_config_term(struct list_head *formats, | 416 | static int pmu_config_term(struct list_head *formats, |
405 | struct perf_event_attr *attr, | 417 | struct perf_event_attr *attr, |
406 | struct parse_events__term *term) | 418 | struct parse_events_term *term) |
407 | { | 419 | { |
408 | struct perf_pmu__format *format; | 420 | struct perf_pmu_format *format; |
409 | __u64 *vp; | 421 | __u64 *vp; |
410 | 422 | ||
411 | /* | 423 | /* |
@@ -450,7 +462,7 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
450 | struct perf_event_attr *attr, | 462 | struct perf_event_attr *attr, |
451 | struct list_head *head_terms) | 463 | struct list_head *head_terms) |
452 | { | 464 | { |
453 | struct parse_events__term *term; | 465 | struct parse_events_term *term; |
454 | 466 | ||
455 | list_for_each_entry(term, head_terms, list) | 467 | list_for_each_entry(term, head_terms, list) |
456 | if (pmu_config_term(formats, attr, term)) | 468 | if (pmu_config_term(formats, attr, term)) |
@@ -471,10 +483,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
471 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); | 483 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); |
472 | } | 484 | } |
473 | 485 | ||
474 | static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | 486 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
475 | struct parse_events__term *term) | 487 | struct parse_events_term *term) |
476 | { | 488 | { |
477 | struct perf_pmu__alias *alias; | 489 | struct perf_pmu_alias *alias; |
478 | char *name; | 490 | char *name; |
479 | 491 | ||
480 | if (parse_events__is_hardcoded_term(term)) | 492 | if (parse_events__is_hardcoded_term(term)) |
@@ -507,8 +519,8 @@ static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | |||
507 | */ | 519 | */ |
508 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | 520 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) |
509 | { | 521 | { |
510 | struct parse_events__term *term, *h; | 522 | struct parse_events_term *term, *h; |
511 | struct perf_pmu__alias *alias; | 523 | struct perf_pmu_alias *alias; |
512 | int ret; | 524 | int ret; |
513 | 525 | ||
514 | list_for_each_entry_safe(term, h, head_terms, list) { | 526 | list_for_each_entry_safe(term, h, head_terms, list) { |
@@ -527,7 +539,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | |||
527 | int perf_pmu__new_format(struct list_head *list, char *name, | 539 | int perf_pmu__new_format(struct list_head *list, char *name, |
528 | int config, unsigned long *bits) | 540 | int config, unsigned long *bits) |
529 | { | 541 | { |
530 | struct perf_pmu__format *format; | 542 | struct perf_pmu_format *format; |
531 | 543 | ||
532 | format = zalloc(sizeof(*format)); | 544 | format = zalloc(sizeof(*format)); |
533 | if (!format) | 545 | if (!format) |
@@ -548,7 +560,7 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) | |||
548 | if (!to) | 560 | if (!to) |
549 | to = from; | 561 | to = from; |
550 | 562 | ||
551 | memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); | 563 | memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); |
552 | for (b = from; b <= to; b++) | 564 | for (b = from; b <= to; b++) |
553 | set_bit(b, bits); | 565 | set_bit(b, bits); |
554 | } | 566 | } |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index a313ed76a49a..32fe55b659fa 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -12,19 +12,6 @@ enum { | |||
12 | 12 | ||
13 | #define PERF_PMU_FORMAT_BITS 64 | 13 | #define PERF_PMU_FORMAT_BITS 64 |
14 | 14 | ||
15 | struct perf_pmu__format { | ||
16 | char *name; | ||
17 | int value; | ||
18 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
19 | struct list_head list; | ||
20 | }; | ||
21 | |||
22 | struct perf_pmu__alias { | ||
23 | char *name; | ||
24 | struct list_head terms; | ||
25 | struct list_head list; | ||
26 | }; | ||
27 | |||
28 | struct perf_pmu { | 15 | struct perf_pmu { |
29 | char *name; | 16 | char *name; |
30 | __u32 type; | 17 | __u32 type; |
@@ -42,7 +29,7 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
42 | struct list_head *head_terms); | 29 | struct list_head *head_terms); |
43 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); | 30 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); |
44 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 31 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
45 | struct list_head *head_terms); | 32 | struct list_head *head_terms); |
46 | int perf_pmu_wrap(void); | 33 | int perf_pmu_wrap(void); |
47 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | 34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); |
48 | 35 | ||
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y index ec898047ebb9..bfd7e8509869 100644 --- a/tools/perf/util/pmu.y +++ b/tools/perf/util/pmu.y | |||
@@ -1,5 +1,4 @@ | |||
1 | 1 | ||
2 | %name-prefix "perf_pmu_" | ||
3 | %parse-param {struct list_head *format} | 2 | %parse-param {struct list_head *format} |
4 | %parse-param {char *name} | 3 | %parse-param {char *name} |
5 | 4 | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 49a256e6e0a2..aa04bf9c9ad7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -40,7 +40,7 @@ | |||
40 | #include "color.h" | 40 | #include "color.h" |
41 | #include "symbol.h" | 41 | #include "symbol.h" |
42 | #include "thread.h" | 42 | #include "thread.h" |
43 | #include "debugfs.h" | 43 | #include <lk/debugfs.h> |
44 | #include "trace-event.h" /* For __maybe_unused */ | 44 | #include "trace-event.h" /* For __maybe_unused */ |
45 | #include "probe-event.h" | 45 | #include "probe-event.h" |
46 | #include "probe-finder.h" | 46 | #include "probe-finder.h" |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1daf5c14e751..be0329394d56 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -413,12 +413,12 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
413 | dwarf_diename(vr_die), dwarf_diename(&type)); | 413 | dwarf_diename(vr_die), dwarf_diename(&type)); |
414 | return -EINVAL; | 414 | return -EINVAL; |
415 | } | 415 | } |
416 | if (die_get_real_type(&type, &type) == NULL) { | ||
417 | pr_warning("Failed to get a type" | ||
418 | " information.\n"); | ||
419 | return -ENOENT; | ||
420 | } | ||
416 | if (ret == DW_TAG_pointer_type) { | 421 | if (ret == DW_TAG_pointer_type) { |
417 | if (die_get_real_type(&type, &type) == NULL) { | ||
418 | pr_warning("Failed to get a type" | ||
419 | " information.\n"); | ||
420 | return -ENOENT; | ||
421 | } | ||
422 | while (*ref_ptr) | 422 | while (*ref_ptr) |
423 | ref_ptr = &(*ref_ptr)->next; | 423 | ref_ptr = &(*ref_ptr)->next; |
424 | /* Add new reference with offset +0 */ | 424 | /* Add new reference with offset +0 */ |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index c40c2d33199e..f75ae1b9900c 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -15,7 +15,7 @@ util/thread_map.c | |||
15 | util/util.c | 15 | util/util.c |
16 | util/xyarray.c | 16 | util/xyarray.c |
17 | util/cgroup.c | 17 | util/cgroup.c |
18 | util/debugfs.c | ||
19 | util/rblist.c | 18 | util/rblist.c |
20 | util/strlist.c | 19 | util/strlist.c |
20 | util/sysfs.c | ||
21 | ../../lib/rbtree.c | 21 | ../../lib/rbtree.c |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a2657fd96837..925e0c3e6d91 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -1045,3 +1045,12 @@ error: | |||
1045 | if (PyErr_Occurred()) | 1045 | if (PyErr_Occurred()) |
1046 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); | 1046 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); |
1047 | } | 1047 | } |
1048 | |||
1049 | /* | ||
1050 | * Dummy, to avoid dragging all the test_attr infrastructure in the python | ||
1051 | * binding. | ||
1052 | */ | ||
1053 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
1054 | int fd, int group_fd, unsigned long flags) | ||
1055 | { | ||
1056 | } | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index f80605eb1855..eacec859f299 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -292,6 +292,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
292 | ns = nsecs - s * NSECS_PER_SEC; | 292 | ns = nsecs - s * NSECS_PER_SEC; |
293 | 293 | ||
294 | scripting_context->event_data = data; | 294 | scripting_context->event_data = data; |
295 | scripting_context->pevent = evsel->tp_format->pevent; | ||
295 | 296 | ||
296 | ENTER; | 297 | ENTER; |
297 | SAVETMPS; | 298 | SAVETMPS; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 14683dfca2ee..e87aa5d9696b 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -265,6 +265,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
265 | ns = nsecs - s * NSECS_PER_SEC; | 265 | ns = nsecs - s * NSECS_PER_SEC; |
266 | 266 | ||
267 | scripting_context->event_data = data; | 267 | scripting_context->event_data = data; |
268 | scripting_context->pevent = evsel->tp_format->pevent; | ||
268 | 269 | ||
269 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | 270 | context = PyCObject_FromVoidPtr(scripting_context, NULL); |
270 | 271 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ce6f51162386..cf1fe01b7e89 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,5 +1,3 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
4 | 2 | ||
5 | #include <byteswap.h> | 3 | #include <byteswap.h> |
@@ -16,7 +14,6 @@ | |||
16 | #include "cpumap.h" | 14 | #include "cpumap.h" |
17 | #include "event-parse.h" | 15 | #include "event-parse.h" |
18 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
19 | #include "unwind.h" | ||
20 | #include "vdso.h" | 17 | #include "vdso.h" |
21 | 18 | ||
22 | static int perf_session__open(struct perf_session *self, bool force) | 19 | static int perf_session__open(struct perf_session *self, bool force) |
@@ -87,13 +84,12 @@ void perf_session__set_id_hdr_size(struct perf_session *session) | |||
87 | { | 84 | { |
88 | u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist); | 85 | u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist); |
89 | 86 | ||
90 | session->host_machine.id_hdr_size = id_hdr_size; | ||
91 | machines__set_id_hdr_size(&session->machines, id_hdr_size); | 87 | machines__set_id_hdr_size(&session->machines, id_hdr_size); |
92 | } | 88 | } |
93 | 89 | ||
94 | int perf_session__create_kernel_maps(struct perf_session *self) | 90 | int perf_session__create_kernel_maps(struct perf_session *self) |
95 | { | 91 | { |
96 | int ret = machine__create_kernel_maps(&self->host_machine); | 92 | int ret = machine__create_kernel_maps(&self->machines.host); |
97 | 93 | ||
98 | if (ret >= 0) | 94 | if (ret >= 0) |
99 | ret = machines__create_guest_kernel_maps(&self->machines); | 95 | ret = machines__create_guest_kernel_maps(&self->machines); |
@@ -102,8 +98,7 @@ int perf_session__create_kernel_maps(struct perf_session *self) | |||
102 | 98 | ||
103 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | 99 | static void perf_session__destroy_kernel_maps(struct perf_session *self) |
104 | { | 100 | { |
105 | machine__destroy_kernel_maps(&self->host_machine); | 101 | machines__destroy_kernel_maps(&self->machines); |
106 | machines__destroy_guest_kernel_maps(&self->machines); | ||
107 | } | 102 | } |
108 | 103 | ||
109 | struct perf_session *perf_session__new(const char *filename, int mode, | 104 | struct perf_session *perf_session__new(const char *filename, int mode, |
@@ -128,22 +123,11 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
128 | goto out; | 123 | goto out; |
129 | 124 | ||
130 | memcpy(self->filename, filename, len); | 125 | memcpy(self->filename, filename, len); |
131 | /* | ||
132 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | ||
133 | * slices. On 32bit we use 32MB. | ||
134 | */ | ||
135 | #if BITS_PER_LONG == 64 | ||
136 | self->mmap_window = ULLONG_MAX; | ||
137 | #else | ||
138 | self->mmap_window = 32 * 1024 * 1024ULL; | ||
139 | #endif | ||
140 | self->machines = RB_ROOT; | ||
141 | self->repipe = repipe; | 126 | self->repipe = repipe; |
142 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 127 | INIT_LIST_HEAD(&self->ordered_samples.samples); |
143 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 128 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
144 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 129 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
145 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 130 | machines__init(&self->machines); |
146 | hists__init(&self->hists); | ||
147 | 131 | ||
148 | if (mode == O_RDONLY) { | 132 | if (mode == O_RDONLY) { |
149 | if (perf_session__open(self, force) < 0) | 133 | if (perf_session__open(self, force) < 0) |
@@ -171,37 +155,30 @@ out_delete: | |||
171 | return NULL; | 155 | return NULL; |
172 | } | 156 | } |
173 | 157 | ||
174 | static void machine__delete_dead_threads(struct machine *machine) | ||
175 | { | ||
176 | struct thread *n, *t; | ||
177 | |||
178 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
179 | list_del(&t->node); | ||
180 | thread__delete(t); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static void perf_session__delete_dead_threads(struct perf_session *session) | 158 | static void perf_session__delete_dead_threads(struct perf_session *session) |
185 | { | 159 | { |
186 | machine__delete_dead_threads(&session->host_machine); | 160 | machine__delete_dead_threads(&session->machines.host); |
187 | } | 161 | } |
188 | 162 | ||
189 | static void machine__delete_threads(struct machine *self) | 163 | static void perf_session__delete_threads(struct perf_session *session) |
190 | { | 164 | { |
191 | struct rb_node *nd = rb_first(&self->threads); | 165 | machine__delete_threads(&session->machines.host); |
192 | |||
193 | while (nd) { | ||
194 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
195 | |||
196 | rb_erase(&t->rb_node, &self->threads); | ||
197 | nd = rb_next(nd); | ||
198 | thread__delete(t); | ||
199 | } | ||
200 | } | 166 | } |
201 | 167 | ||
202 | static void perf_session__delete_threads(struct perf_session *session) | 168 | static void perf_session_env__delete(struct perf_session_env *env) |
203 | { | 169 | { |
204 | machine__delete_threads(&session->host_machine); | 170 | free(env->hostname); |
171 | free(env->os_release); | ||
172 | free(env->version); | ||
173 | free(env->arch); | ||
174 | free(env->cpu_desc); | ||
175 | free(env->cpuid); | ||
176 | |||
177 | free(env->cmdline); | ||
178 | free(env->sibling_cores); | ||
179 | free(env->sibling_threads); | ||
180 | free(env->numa_nodes); | ||
181 | free(env->pmu_mappings); | ||
205 | } | 182 | } |
206 | 183 | ||
207 | void perf_session__delete(struct perf_session *self) | 184 | void perf_session__delete(struct perf_session *self) |
@@ -209,198 +186,13 @@ void perf_session__delete(struct perf_session *self) | |||
209 | perf_session__destroy_kernel_maps(self); | 186 | perf_session__destroy_kernel_maps(self); |
210 | perf_session__delete_dead_threads(self); | 187 | perf_session__delete_dead_threads(self); |
211 | perf_session__delete_threads(self); | 188 | perf_session__delete_threads(self); |
212 | machine__exit(&self->host_machine); | 189 | perf_session_env__delete(&self->header.env); |
190 | machines__exit(&self->machines); | ||
213 | close(self->fd); | 191 | close(self->fd); |
214 | free(self); | 192 | free(self); |
215 | vdso__exit(); | 193 | vdso__exit(); |
216 | } | 194 | } |
217 | 195 | ||
218 | void machine__remove_thread(struct machine *self, struct thread *th) | ||
219 | { | ||
220 | self->last_match = NULL; | ||
221 | rb_erase(&th->rb_node, &self->threads); | ||
222 | /* | ||
223 | * We may have references to this thread, for instance in some hist_entry | ||
224 | * instances, so just move them to a separate list. | ||
225 | */ | ||
226 | list_add_tail(&th->node, &self->dead_threads); | ||
227 | } | ||
228 | |||
229 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
230 | { | ||
231 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
232 | return 1; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static const u8 cpumodes[] = { | ||
238 | PERF_RECORD_MISC_USER, | ||
239 | PERF_RECORD_MISC_KERNEL, | ||
240 | PERF_RECORD_MISC_GUEST_USER, | ||
241 | PERF_RECORD_MISC_GUEST_KERNEL | ||
242 | }; | ||
243 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
244 | |||
245 | static void ip__resolve_ams(struct machine *self, struct thread *thread, | ||
246 | struct addr_map_symbol *ams, | ||
247 | u64 ip) | ||
248 | { | ||
249 | struct addr_location al; | ||
250 | size_t i; | ||
251 | u8 m; | ||
252 | |||
253 | memset(&al, 0, sizeof(al)); | ||
254 | |||
255 | for (i = 0; i < NCPUMODES; i++) { | ||
256 | m = cpumodes[i]; | ||
257 | /* | ||
258 | * We cannot use the header.misc hint to determine whether a | ||
259 | * branch stack address is user, kernel, guest, hypervisor. | ||
260 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
261 | * Thus, we have to try consecutively until we find a match | ||
262 | * or else, the symbol is unknown | ||
263 | */ | ||
264 | thread__find_addr_location(thread, self, m, MAP__FUNCTION, | ||
265 | ip, &al, NULL); | ||
266 | if (al.sym) | ||
267 | goto found; | ||
268 | } | ||
269 | found: | ||
270 | ams->addr = ip; | ||
271 | ams->al_addr = al.addr; | ||
272 | ams->sym = al.sym; | ||
273 | ams->map = al.map; | ||
274 | } | ||
275 | |||
276 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
277 | struct thread *thr, | ||
278 | struct branch_stack *bs) | ||
279 | { | ||
280 | struct branch_info *bi; | ||
281 | unsigned int i; | ||
282 | |||
283 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
284 | if (!bi) | ||
285 | return NULL; | ||
286 | |||
287 | for (i = 0; i < bs->nr; i++) { | ||
288 | ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to); | ||
289 | ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from); | ||
290 | bi[i].flags = bs->entries[i].flags; | ||
291 | } | ||
292 | return bi; | ||
293 | } | ||
294 | |||
295 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
296 | struct thread *thread, | ||
297 | struct ip_callchain *chain, | ||
298 | struct symbol **parent) | ||
299 | |||
300 | { | ||
301 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
302 | unsigned int i; | ||
303 | int err; | ||
304 | |||
305 | callchain_cursor_reset(&callchain_cursor); | ||
306 | |||
307 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
308 | pr_warning("corrupted callchain. skipping...\n"); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | for (i = 0; i < chain->nr; i++) { | ||
313 | u64 ip; | ||
314 | struct addr_location al; | ||
315 | |||
316 | if (callchain_param.order == ORDER_CALLEE) | ||
317 | ip = chain->ips[i]; | ||
318 | else | ||
319 | ip = chain->ips[chain->nr - i - 1]; | ||
320 | |||
321 | if (ip >= PERF_CONTEXT_MAX) { | ||
322 | switch (ip) { | ||
323 | case PERF_CONTEXT_HV: | ||
324 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
325 | break; | ||
326 | case PERF_CONTEXT_KERNEL: | ||
327 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
328 | break; | ||
329 | case PERF_CONTEXT_USER: | ||
330 | cpumode = PERF_RECORD_MISC_USER; | ||
331 | break; | ||
332 | default: | ||
333 | pr_debug("invalid callchain context: " | ||
334 | "%"PRId64"\n", (s64) ip); | ||
335 | /* | ||
336 | * It seems the callchain is corrupted. | ||
337 | * Discard all. | ||
338 | */ | ||
339 | callchain_cursor_reset(&callchain_cursor); | ||
340 | return 0; | ||
341 | } | ||
342 | continue; | ||
343 | } | ||
344 | |||
345 | al.filtered = false; | ||
346 | thread__find_addr_location(thread, machine, cpumode, | ||
347 | MAP__FUNCTION, ip, &al, NULL); | ||
348 | if (al.sym != NULL) { | ||
349 | if (sort__has_parent && !*parent && | ||
350 | symbol__match_parent_regex(al.sym)) | ||
351 | *parent = al.sym; | ||
352 | if (!symbol_conf.use_callchain) | ||
353 | break; | ||
354 | } | ||
355 | |||
356 | err = callchain_cursor_append(&callchain_cursor, | ||
357 | ip, al.map, al.sym); | ||
358 | if (err) | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
366 | { | ||
367 | struct callchain_cursor *cursor = arg; | ||
368 | return callchain_cursor_append(cursor, entry->ip, | ||
369 | entry->map, entry->sym); | ||
370 | } | ||
371 | |||
372 | int machine__resolve_callchain(struct machine *machine, | ||
373 | struct perf_evsel *evsel, | ||
374 | struct thread *thread, | ||
375 | struct perf_sample *sample, | ||
376 | struct symbol **parent) | ||
377 | |||
378 | { | ||
379 | int ret; | ||
380 | |||
381 | callchain_cursor_reset(&callchain_cursor); | ||
382 | |||
383 | ret = machine__resolve_callchain_sample(machine, thread, | ||
384 | sample->callchain, parent); | ||
385 | if (ret) | ||
386 | return ret; | ||
387 | |||
388 | /* Can we do dwarf post unwind? */ | ||
389 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
390 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
391 | return 0; | ||
392 | |||
393 | /* Bail out if nothing was captured. */ | ||
394 | if ((!sample->user_regs.regs) || | ||
395 | (!sample->user_stack.size)) | ||
396 | return 0; | ||
397 | |||
398 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
399 | thread, evsel->attr.sample_regs_user, | ||
400 | sample); | ||
401 | |||
402 | } | ||
403 | |||
404 | static int process_event_synth_tracing_data_stub(union perf_event *event | 196 | static int process_event_synth_tracing_data_stub(union perf_event *event |
405 | __maybe_unused, | 197 | __maybe_unused, |
406 | struct perf_session *session | 198 | struct perf_session *session |
@@ -1006,6 +798,12 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1006 | 798 | ||
1007 | if (sample_type & PERF_SAMPLE_STACK_USER) | 799 | if (sample_type & PERF_SAMPLE_STACK_USER) |
1008 | stack_user__printf(&sample->user_stack); | 800 | stack_user__printf(&sample->user_stack); |
801 | |||
802 | if (sample_type & PERF_SAMPLE_WEIGHT) | ||
803 | printf("... weight: %" PRIu64 "\n", sample->weight); | ||
804 | |||
805 | if (sample_type & PERF_SAMPLE_DATA_SRC) | ||
806 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); | ||
1009 | } | 807 | } |
1010 | 808 | ||
1011 | static struct machine * | 809 | static struct machine * |
@@ -1027,7 +825,7 @@ static struct machine * | |||
1027 | return perf_session__findnew_machine(session, pid); | 825 | return perf_session__findnew_machine(session, pid); |
1028 | } | 826 | } |
1029 | 827 | ||
1030 | return perf_session__find_host_machine(session); | 828 | return &session->machines.host; |
1031 | } | 829 | } |
1032 | 830 | ||
1033 | static int perf_session_deliver_event(struct perf_session *session, | 831 | static int perf_session_deliver_event(struct perf_session *session, |
@@ -1065,11 +863,11 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1065 | case PERF_RECORD_SAMPLE: | 863 | case PERF_RECORD_SAMPLE: |
1066 | dump_sample(evsel, event, sample); | 864 | dump_sample(evsel, event, sample); |
1067 | if (evsel == NULL) { | 865 | if (evsel == NULL) { |
1068 | ++session->hists.stats.nr_unknown_id; | 866 | ++session->stats.nr_unknown_id; |
1069 | return 0; | 867 | return 0; |
1070 | } | 868 | } |
1071 | if (machine == NULL) { | 869 | if (machine == NULL) { |
1072 | ++session->hists.stats.nr_unprocessable_samples; | 870 | ++session->stats.nr_unprocessable_samples; |
1073 | return 0; | 871 | return 0; |
1074 | } | 872 | } |
1075 | return tool->sample(tool, event, sample, evsel, machine); | 873 | return tool->sample(tool, event, sample, evsel, machine); |
@@ -1083,7 +881,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1083 | return tool->exit(tool, event, sample, machine); | 881 | return tool->exit(tool, event, sample, machine); |
1084 | case PERF_RECORD_LOST: | 882 | case PERF_RECORD_LOST: |
1085 | if (tool->lost == perf_event__process_lost) | 883 | if (tool->lost == perf_event__process_lost) |
1086 | session->hists.stats.total_lost += event->lost.lost; | 884 | session->stats.total_lost += event->lost.lost; |
1087 | return tool->lost(tool, event, sample, machine); | 885 | return tool->lost(tool, event, sample, machine); |
1088 | case PERF_RECORD_READ: | 886 | case PERF_RECORD_READ: |
1089 | return tool->read(tool, event, sample, evsel, machine); | 887 | return tool->read(tool, event, sample, evsel, machine); |
@@ -1092,7 +890,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1092 | case PERF_RECORD_UNTHROTTLE: | 890 | case PERF_RECORD_UNTHROTTLE: |
1093 | return tool->unthrottle(tool, event, sample, machine); | 891 | return tool->unthrottle(tool, event, sample, machine); |
1094 | default: | 892 | default: |
1095 | ++session->hists.stats.nr_unknown_events; | 893 | ++session->stats.nr_unknown_events; |
1096 | return -1; | 894 | return -1; |
1097 | } | 895 | } |
1098 | } | 896 | } |
@@ -1106,8 +904,8 @@ static int perf_session__preprocess_sample(struct perf_session *session, | |||
1106 | 904 | ||
1107 | if (!ip_callchain__valid(sample->callchain, event)) { | 905 | if (!ip_callchain__valid(sample->callchain, event)) { |
1108 | pr_debug("call-chain problem with event, skipping it.\n"); | 906 | pr_debug("call-chain problem with event, skipping it.\n"); |
1109 | ++session->hists.stats.nr_invalid_chains; | 907 | ++session->stats.nr_invalid_chains; |
1110 | session->hists.stats.total_invalid_chains += sample->period; | 908 | session->stats.total_invalid_chains += sample->period; |
1111 | return -EINVAL; | 909 | return -EINVAL; |
1112 | } | 910 | } |
1113 | return 0; | 911 | return 0; |
@@ -1165,7 +963,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
1165 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | 963 | if (event->header.type >= PERF_RECORD_HEADER_MAX) |
1166 | return -EINVAL; | 964 | return -EINVAL; |
1167 | 965 | ||
1168 | hists__inc_nr_events(&session->hists, event->header.type); | 966 | events_stats__inc(&session->stats, event->header.type); |
1169 | 967 | ||
1170 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | 968 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) |
1171 | return perf_session__process_user_event(session, event, tool, file_offset); | 969 | return perf_session__process_user_event(session, event, tool, file_offset); |
@@ -1201,7 +999,7 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
1201 | 999 | ||
1202 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | 1000 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) |
1203 | { | 1001 | { |
1204 | return machine__findnew_thread(&session->host_machine, pid); | 1002 | return machine__findnew_thread(&session->machines.host, pid); |
1205 | } | 1003 | } |
1206 | 1004 | ||
1207 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 1005 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
@@ -1220,39 +1018,39 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
1220 | const struct perf_tool *tool) | 1018 | const struct perf_tool *tool) |
1221 | { | 1019 | { |
1222 | if (tool->lost == perf_event__process_lost && | 1020 | if (tool->lost == perf_event__process_lost && |
1223 | session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { | 1021 | session->stats.nr_events[PERF_RECORD_LOST] != 0) { |
1224 | ui__warning("Processed %d events and lost %d chunks!\n\n" | 1022 | ui__warning("Processed %d events and lost %d chunks!\n\n" |
1225 | "Check IO/CPU overload!\n\n", | 1023 | "Check IO/CPU overload!\n\n", |
1226 | session->hists.stats.nr_events[0], | 1024 | session->stats.nr_events[0], |
1227 | session->hists.stats.nr_events[PERF_RECORD_LOST]); | 1025 | session->stats.nr_events[PERF_RECORD_LOST]); |
1228 | } | 1026 | } |
1229 | 1027 | ||
1230 | if (session->hists.stats.nr_unknown_events != 0) { | 1028 | if (session->stats.nr_unknown_events != 0) { |
1231 | ui__warning("Found %u unknown events!\n\n" | 1029 | ui__warning("Found %u unknown events!\n\n" |
1232 | "Is this an older tool processing a perf.data " | 1030 | "Is this an older tool processing a perf.data " |
1233 | "file generated by a more recent tool?\n\n" | 1031 | "file generated by a more recent tool?\n\n" |
1234 | "If that is not the case, consider " | 1032 | "If that is not the case, consider " |
1235 | "reporting to linux-kernel@vger.kernel.org.\n\n", | 1033 | "reporting to linux-kernel@vger.kernel.org.\n\n", |
1236 | session->hists.stats.nr_unknown_events); | 1034 | session->stats.nr_unknown_events); |
1237 | } | 1035 | } |
1238 | 1036 | ||
1239 | if (session->hists.stats.nr_unknown_id != 0) { | 1037 | if (session->stats.nr_unknown_id != 0) { |
1240 | ui__warning("%u samples with id not present in the header\n", | 1038 | ui__warning("%u samples with id not present in the header\n", |
1241 | session->hists.stats.nr_unknown_id); | 1039 | session->stats.nr_unknown_id); |
1242 | } | 1040 | } |
1243 | 1041 | ||
1244 | if (session->hists.stats.nr_invalid_chains != 0) { | 1042 | if (session->stats.nr_invalid_chains != 0) { |
1245 | ui__warning("Found invalid callchains!\n\n" | 1043 | ui__warning("Found invalid callchains!\n\n" |
1246 | "%u out of %u events were discarded for this reason.\n\n" | 1044 | "%u out of %u events were discarded for this reason.\n\n" |
1247 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", | 1045 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", |
1248 | session->hists.stats.nr_invalid_chains, | 1046 | session->stats.nr_invalid_chains, |
1249 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | 1047 | session->stats.nr_events[PERF_RECORD_SAMPLE]); |
1250 | } | 1048 | } |
1251 | 1049 | ||
1252 | if (session->hists.stats.nr_unprocessable_samples != 0) { | 1050 | if (session->stats.nr_unprocessable_samples != 0) { |
1253 | ui__warning("%u unprocessable samples recorded.\n" | 1051 | ui__warning("%u unprocessable samples recorded.\n" |
1254 | "Do you have a KVM guest running and not using 'perf kvm'?\n", | 1052 | "Do you have a KVM guest running and not using 'perf kvm'?\n", |
1255 | session->hists.stats.nr_unprocessable_samples); | 1053 | session->stats.nr_unprocessable_samples); |
1256 | } | 1054 | } |
1257 | } | 1055 | } |
1258 | 1056 | ||
@@ -1369,6 +1167,18 @@ fetch_mmaped_event(struct perf_session *session, | |||
1369 | return event; | 1167 | return event; |
1370 | } | 1168 | } |
1371 | 1169 | ||
1170 | /* | ||
1171 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | ||
1172 | * slices. On 32bit we use 32MB. | ||
1173 | */ | ||
1174 | #if BITS_PER_LONG == 64 | ||
1175 | #define MMAP_SIZE ULLONG_MAX | ||
1176 | #define NUM_MMAPS 1 | ||
1177 | #else | ||
1178 | #define MMAP_SIZE (32 * 1024 * 1024ULL) | ||
1179 | #define NUM_MMAPS 128 | ||
1180 | #endif | ||
1181 | |||
1372 | int __perf_session__process_events(struct perf_session *session, | 1182 | int __perf_session__process_events(struct perf_session *session, |
1373 | u64 data_offset, u64 data_size, | 1183 | u64 data_offset, u64 data_size, |
1374 | u64 file_size, struct perf_tool *tool) | 1184 | u64 file_size, struct perf_tool *tool) |
@@ -1376,7 +1186,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1376 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1186 | u64 head, page_offset, file_offset, file_pos, progress_next; |
1377 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1187 | int err, mmap_prot, mmap_flags, map_idx = 0; |
1378 | size_t mmap_size; | 1188 | size_t mmap_size; |
1379 | char *buf, *mmaps[8]; | 1189 | char *buf, *mmaps[NUM_MMAPS]; |
1380 | union perf_event *event; | 1190 | union perf_event *event; |
1381 | uint32_t size; | 1191 | uint32_t size; |
1382 | 1192 | ||
@@ -1391,7 +1201,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1391 | 1201 | ||
1392 | progress_next = file_size / 16; | 1202 | progress_next = file_size / 16; |
1393 | 1203 | ||
1394 | mmap_size = session->mmap_window; | 1204 | mmap_size = MMAP_SIZE; |
1395 | if (mmap_size > file_size) | 1205 | if (mmap_size > file_size) |
1396 | mmap_size = file_size; | 1206 | mmap_size = file_size; |
1397 | 1207 | ||
@@ -1526,16 +1336,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
1526 | 1336 | ||
1527 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) | 1337 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) |
1528 | { | 1338 | { |
1529 | return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + | 1339 | return machines__fprintf_dsos(&self->machines, fp); |
1530 | __dsos__fprintf(&self->host_machine.user_dsos, fp) + | ||
1531 | machines__fprintf_dsos(&self->machines, fp); | ||
1532 | } | 1340 | } |
1533 | 1341 | ||
1534 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | 1342 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, |
1535 | bool with_hits) | 1343 | bool (skip)(struct dso *dso, int parm), int parm) |
1536 | { | 1344 | { |
1537 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 1345 | return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); |
1538 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | ||
1539 | } | 1346 | } |
1540 | 1347 | ||
1541 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1348 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
@@ -1543,11 +1350,11 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | |||
1543 | struct perf_evsel *pos; | 1350 | struct perf_evsel *pos; |
1544 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | 1351 | size_t ret = fprintf(fp, "Aggregated stats:\n"); |
1545 | 1352 | ||
1546 | ret += hists__fprintf_nr_events(&session->hists, fp); | 1353 | ret += events_stats__fprintf(&session->stats, fp); |
1547 | 1354 | ||
1548 | list_for_each_entry(pos, &session->evlist->entries, node) { | 1355 | list_for_each_entry(pos, &session->evlist->entries, node) { |
1549 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | 1356 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); |
1550 | ret += hists__fprintf_nr_events(&pos->hists, fp); | 1357 | ret += events_stats__fprintf(&pos->hists.stats, fp); |
1551 | } | 1358 | } |
1552 | 1359 | ||
1553 | return ret; | 1360 | return ret; |
@@ -1559,19 +1366,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp) | |||
1559 | * FIXME: Here we have to actually print all the machines in this | 1366 | * FIXME: Here we have to actually print all the machines in this |
1560 | * session, not just the host... | 1367 | * session, not just the host... |
1561 | */ | 1368 | */ |
1562 | return machine__fprintf(&session->host_machine, fp); | 1369 | return machine__fprintf(&session->machines.host, fp); |
1563 | } | ||
1564 | |||
1565 | void perf_session__remove_thread(struct perf_session *session, | ||
1566 | struct thread *th) | ||
1567 | { | ||
1568 | /* | ||
1569 | * FIXME: This one makes no sense, we need to remove the thread from | ||
1570 | * the machine it belongs to, perf_session can have many machines, so | ||
1571 | * doing it always on ->host_machine is wrong. Fix when auditing all | ||
1572 | * the 'perf kvm' code. | ||
1573 | */ | ||
1574 | machine__remove_thread(&session->host_machine, th); | ||
1575 | } | 1370 | } |
1576 | 1371 | ||
1577 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 1372 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index cea133a6bdf1..f3b235ec7bf4 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -30,20 +30,13 @@ struct ordered_samples { | |||
30 | struct perf_session { | 30 | struct perf_session { |
31 | struct perf_header header; | 31 | struct perf_header header; |
32 | unsigned long size; | 32 | unsigned long size; |
33 | unsigned long mmap_window; | 33 | struct machines machines; |
34 | struct machine host_machine; | ||
35 | struct rb_root machines; | ||
36 | struct perf_evlist *evlist; | 34 | struct perf_evlist *evlist; |
37 | struct pevent *pevent; | 35 | struct pevent *pevent; |
38 | /* | 36 | struct events_stats stats; |
39 | * FIXME: Need to split this up further, we need global | ||
40 | * stats + per event stats. | ||
41 | */ | ||
42 | struct hists hists; | ||
43 | int fd; | 37 | int fd; |
44 | bool fd_pipe; | 38 | bool fd_pipe; |
45 | bool repipe; | 39 | bool repipe; |
46 | int cwdlen; | ||
47 | char *cwd; | 40 | char *cwd; |
48 | struct ordered_samples ordered_samples; | 41 | struct ordered_samples ordered_samples; |
49 | char filename[1]; | 42 | char filename[1]; |
@@ -54,7 +47,7 @@ struct perf_tool; | |||
54 | struct perf_session *perf_session__new(const char *filename, int mode, | 47 | struct perf_session *perf_session__new(const char *filename, int mode, |
55 | bool force, bool repipe, | 48 | bool force, bool repipe, |
56 | struct perf_tool *tool); | 49 | struct perf_tool *tool); |
57 | void perf_session__delete(struct perf_session *self); | 50 | void perf_session__delete(struct perf_session *session); |
58 | 51 | ||
59 | void perf_event_header__bswap(struct perf_event_header *self); | 52 | void perf_event_header__bswap(struct perf_event_header *self); |
60 | 53 | ||
@@ -78,46 +71,26 @@ void perf_event__attr_swap(struct perf_event_attr *attr); | |||
78 | int perf_session__create_kernel_maps(struct perf_session *self); | 71 | int perf_session__create_kernel_maps(struct perf_session *self); |
79 | 72 | ||
80 | void perf_session__set_id_hdr_size(struct perf_session *session); | 73 | void perf_session__set_id_hdr_size(struct perf_session *session); |
81 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | ||
82 | |||
83 | static inline | ||
84 | struct machine *perf_session__find_host_machine(struct perf_session *self) | ||
85 | { | ||
86 | return &self->host_machine; | ||
87 | } | ||
88 | 74 | ||
89 | static inline | 75 | static inline |
90 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) | 76 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) |
91 | { | 77 | { |
92 | if (pid == HOST_KERNEL_ID) | ||
93 | return &self->host_machine; | ||
94 | return machines__find(&self->machines, pid); | 78 | return machines__find(&self->machines, pid); |
95 | } | 79 | } |
96 | 80 | ||
97 | static inline | 81 | static inline |
98 | struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) | 82 | struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) |
99 | { | 83 | { |
100 | if (pid == HOST_KERNEL_ID) | ||
101 | return &self->host_machine; | ||
102 | return machines__findnew(&self->machines, pid); | 84 | return machines__findnew(&self->machines, pid); |
103 | } | 85 | } |
104 | 86 | ||
105 | static inline | ||
106 | void perf_session__process_machines(struct perf_session *self, | ||
107 | struct perf_tool *tool, | ||
108 | machine__process_t process) | ||
109 | { | ||
110 | process(&self->host_machine, tool); | ||
111 | return machines__process(&self->machines, process, tool); | ||
112 | } | ||
113 | |||
114 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 87 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
115 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); | 88 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
116 | 89 | ||
117 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | 90 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); |
118 | 91 | ||
119 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 92 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, |
120 | FILE *fp, bool with_hits); | 93 | bool (fn)(struct dso *dso, int parm), int parm); |
121 | 94 | ||
122 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); | 95 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
123 | 96 | ||
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 73d510269784..58ea5ca6c255 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -18,12 +18,14 @@ class install_lib(_install_lib): | |||
18 | self.build_dir = build_lib | 18 | self.build_dir = build_lib |
19 | 19 | ||
20 | 20 | ||
21 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] | 21 | cflags = getenv('CFLAGS', '').split() |
22 | cflags += getenv('CFLAGS', '').split() | 22 | # switch off several checks (need to be at the end of cflags list) |
23 | cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' ] | ||
23 | 24 | ||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 25 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 26 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
26 | libtraceevent = getenv('LIBTRACEEVENT') | 27 | libtraceevent = getenv('LIBTRACEEVENT') |
28 | liblk = getenv('LIBLK') | ||
27 | 29 | ||
28 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | 30 | ext_sources = [f.strip() for f in file('util/python-ext-sources') |
29 | if len(f.strip()) > 0 and f[0] != '#'] | 31 | if len(f.strip()) > 0 and f[0] != '#'] |
@@ -32,7 +34,7 @@ perf = Extension('perf', | |||
32 | sources = ext_sources, | 34 | sources = ext_sources, |
33 | include_dirs = ['util/include'], | 35 | include_dirs = ['util/include'], |
34 | extra_compile_args = cflags, | 36 | extra_compile_args = cflags, |
35 | extra_objects = [libtraceevent], | 37 | extra_objects = [libtraceevent, liblk], |
36 | ) | 38 | ) |
37 | 39 | ||
38 | setup(name='perf', | 40 | setup(name='perf', |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cfd1c0feb32d..313a5a730112 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "sort.h" | 1 | #include "sort.h" |
2 | #include "hist.h" | 2 | #include "hist.h" |
3 | #include "symbol.h" | ||
3 | 4 | ||
4 | regex_t parent_regex; | 5 | regex_t parent_regex; |
5 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 6 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
@@ -9,7 +10,7 @@ const char *sort_order = default_sort_order; | |||
9 | int sort__need_collapse = 0; | 10 | int sort__need_collapse = 0; |
10 | int sort__has_parent = 0; | 11 | int sort__has_parent = 0; |
11 | int sort__has_sym = 0; | 12 | int sort__has_sym = 0; |
12 | int sort__branch_mode = -1; /* -1 = means not set */ | 13 | enum sort_mode sort__mode = SORT_MODE__NORMAL; |
13 | 14 | ||
14 | enum sort_type sort__first_dimension; | 15 | enum sort_type sort__first_dimension; |
15 | 16 | ||
@@ -60,7 +61,7 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 61 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
61 | size_t size, unsigned int width) | 62 | size_t size, unsigned int width) |
62 | { | 63 | { |
63 | return repsep_snprintf(bf, size, "%*s:%5d", width, | 64 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
64 | self->thread->comm ?: "", self->thread->pid); | 65 | self->thread->comm ?: "", self->thread->pid); |
65 | } | 66 | } |
66 | 67 | ||
@@ -97,6 +98,16 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |||
97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 98 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
98 | } | 99 | } |
99 | 100 | ||
101 | struct sort_entry sort_comm = { | ||
102 | .se_header = "Command", | ||
103 | .se_cmp = sort__comm_cmp, | ||
104 | .se_collapse = sort__comm_collapse, | ||
105 | .se_snprintf = hist_entry__comm_snprintf, | ||
106 | .se_width_idx = HISTC_COMM, | ||
107 | }; | ||
108 | |||
109 | /* --sort dso */ | ||
110 | |||
100 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | 111 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) |
101 | { | 112 | { |
102 | struct dso *dso_l = map_l ? map_l->dso : NULL; | 113 | struct dso *dso_l = map_l ? map_l->dso : NULL; |
@@ -117,40 +128,12 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | |||
117 | return strcmp(dso_name_l, dso_name_r); | 128 | return strcmp(dso_name_l, dso_name_r); |
118 | } | 129 | } |
119 | 130 | ||
120 | struct sort_entry sort_comm = { | ||
121 | .se_header = "Command", | ||
122 | .se_cmp = sort__comm_cmp, | ||
123 | .se_collapse = sort__comm_collapse, | ||
124 | .se_snprintf = hist_entry__comm_snprintf, | ||
125 | .se_width_idx = HISTC_COMM, | ||
126 | }; | ||
127 | |||
128 | /* --sort dso */ | ||
129 | |||
130 | static int64_t | 131 | static int64_t |
131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
132 | { | 133 | { |
133 | return _sort__dso_cmp(left->ms.map, right->ms.map); | 134 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
134 | } | 135 | } |
135 | 136 | ||
136 | |||
137 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, | ||
138 | u64 ip_l, u64 ip_r) | ||
139 | { | ||
140 | if (!sym_l || !sym_r) | ||
141 | return cmp_null(sym_l, sym_r); | ||
142 | |||
143 | if (sym_l == sym_r) | ||
144 | return 0; | ||
145 | |||
146 | if (sym_l) | ||
147 | ip_l = sym_l->start; | ||
148 | if (sym_r) | ||
149 | ip_r = sym_r->start; | ||
150 | |||
151 | return (int64_t)(ip_r - ip_l); | ||
152 | } | ||
153 | |||
154 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | 137 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, |
155 | size_t size, unsigned int width) | 138 | size_t size, unsigned int width) |
156 | { | 139 | { |
@@ -169,24 +152,66 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
169 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); | 152 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); |
170 | } | 153 | } |
171 | 154 | ||
155 | struct sort_entry sort_dso = { | ||
156 | .se_header = "Shared Object", | ||
157 | .se_cmp = sort__dso_cmp, | ||
158 | .se_snprintf = hist_entry__dso_snprintf, | ||
159 | .se_width_idx = HISTC_DSO, | ||
160 | }; | ||
161 | |||
162 | /* --sort symbol */ | ||
163 | |||
164 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) | ||
165 | { | ||
166 | u64 ip_l, ip_r; | ||
167 | |||
168 | if (!sym_l || !sym_r) | ||
169 | return cmp_null(sym_l, sym_r); | ||
170 | |||
171 | if (sym_l == sym_r) | ||
172 | return 0; | ||
173 | |||
174 | ip_l = sym_l->start; | ||
175 | ip_r = sym_r->start; | ||
176 | |||
177 | return (int64_t)(ip_r - ip_l); | ||
178 | } | ||
179 | |||
180 | static int64_t | ||
181 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
182 | { | ||
183 | if (!left->ms.sym && !right->ms.sym) | ||
184 | return right->level - left->level; | ||
185 | |||
186 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | ||
187 | } | ||
188 | |||
172 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | 189 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
173 | u64 ip, char level, char *bf, size_t size, | 190 | u64 ip, char level, char *bf, size_t size, |
174 | unsigned int width __maybe_unused) | 191 | unsigned int width) |
175 | { | 192 | { |
176 | size_t ret = 0; | 193 | size_t ret = 0; |
177 | 194 | ||
178 | if (verbose) { | 195 | if (verbose) { |
179 | char o = map ? dso__symtab_origin(map->dso) : '!'; | 196 | char o = map ? dso__symtab_origin(map->dso) : '!'; |
180 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", | 197 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", |
181 | BITS_PER_LONG / 4, ip, o); | 198 | BITS_PER_LONG / 4 + 2, ip, o); |
182 | } | 199 | } |
183 | 200 | ||
184 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); | 201 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); |
185 | if (sym) | 202 | if (sym && map) { |
186 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | 203 | if (map->type == MAP__VARIABLE) { |
187 | width - ret, | 204 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); |
188 | sym->name); | 205 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", |
189 | else { | 206 | ip - map->unmap_ip(map, sym->start)); |
207 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
208 | width - ret, ""); | ||
209 | } else { | ||
210 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
211 | width - ret, | ||
212 | sym->name); | ||
213 | } | ||
214 | } else { | ||
190 | size_t len = BITS_PER_LONG / 4; | 215 | size_t len = BITS_PER_LONG / 4; |
191 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", | 216 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", |
192 | len, ip); | 217 | len, ip); |
@@ -197,43 +222,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
197 | return ret; | 222 | return ret; |
198 | } | 223 | } |
199 | 224 | ||
200 | |||
201 | struct sort_entry sort_dso = { | ||
202 | .se_header = "Shared Object", | ||
203 | .se_cmp = sort__dso_cmp, | ||
204 | .se_snprintf = hist_entry__dso_snprintf, | ||
205 | .se_width_idx = HISTC_DSO, | ||
206 | }; | ||
207 | |||
208 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | 225 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
209 | size_t size, | 226 | size_t size, unsigned int width) |
210 | unsigned int width __maybe_unused) | ||
211 | { | 227 | { |
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | 228 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, |
213 | self->level, bf, size, width); | 229 | self->level, bf, size, width); |
214 | } | 230 | } |
215 | 231 | ||
216 | /* --sort symbol */ | ||
217 | static int64_t | ||
218 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
219 | { | ||
220 | u64 ip_l, ip_r; | ||
221 | |||
222 | if (!left->ms.sym && !right->ms.sym) | ||
223 | return right->level - left->level; | ||
224 | |||
225 | if (!left->ms.sym || !right->ms.sym) | ||
226 | return cmp_null(left->ms.sym, right->ms.sym); | ||
227 | |||
228 | if (left->ms.sym == right->ms.sym) | ||
229 | return 0; | ||
230 | |||
231 | ip_l = left->ms.sym->start; | ||
232 | ip_r = right->ms.sym->start; | ||
233 | |||
234 | return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r); | ||
235 | } | ||
236 | |||
237 | struct sort_entry sort_sym = { | 232 | struct sort_entry sort_sym = { |
238 | .se_header = "Symbol", | 233 | .se_header = "Symbol", |
239 | .se_cmp = sort__sym_cmp, | 234 | .se_cmp = sort__sym_cmp, |
@@ -253,7 +248,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
253 | size_t size, | 248 | size_t size, |
254 | unsigned int width __maybe_unused) | 249 | unsigned int width __maybe_unused) |
255 | { | 250 | { |
256 | FILE *fp; | 251 | FILE *fp = NULL; |
257 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; | 252 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; |
258 | size_t line_len; | 253 | size_t line_len; |
259 | 254 | ||
@@ -274,7 +269,6 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
274 | 269 | ||
275 | if (getline(&path, &line_len, fp) < 0 || !line_len) | 270 | if (getline(&path, &line_len, fp) < 0 || !line_len) |
276 | goto out_ip; | 271 | goto out_ip; |
277 | fclose(fp); | ||
278 | self->srcline = strdup(path); | 272 | self->srcline = strdup(path); |
279 | if (self->srcline == NULL) | 273 | if (self->srcline == NULL) |
280 | goto out_ip; | 274 | goto out_ip; |
@@ -284,8 +278,12 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
284 | *nl = '\0'; | 278 | *nl = '\0'; |
285 | path = self->srcline; | 279 | path = self->srcline; |
286 | out_path: | 280 | out_path: |
281 | if (fp) | ||
282 | pclose(fp); | ||
287 | return repsep_snprintf(bf, size, "%s", path); | 283 | return repsep_snprintf(bf, size, "%s", path); |
288 | out_ip: | 284 | out_ip: |
285 | if (fp) | ||
286 | pclose(fp); | ||
289 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); | 287 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); |
290 | } | 288 | } |
291 | 289 | ||
@@ -335,7 +333,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
335 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | 333 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, |
336 | size_t size, unsigned int width) | 334 | size_t size, unsigned int width) |
337 | { | 335 | { |
338 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 336 | return repsep_snprintf(bf, size, "%*d", width, self->cpu); |
339 | } | 337 | } |
340 | 338 | ||
341 | struct sort_entry sort_cpu = { | 339 | struct sort_entry sort_cpu = { |
@@ -345,6 +343,8 @@ struct sort_entry sort_cpu = { | |||
345 | .se_width_idx = HISTC_CPU, | 343 | .se_width_idx = HISTC_CPU, |
346 | }; | 344 | }; |
347 | 345 | ||
346 | /* sort keys for branch stacks */ | ||
347 | |||
348 | static int64_t | 348 | static int64_t |
349 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | 349 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) |
350 | { | 350 | { |
@@ -359,13 +359,6 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | |||
359 | bf, size, width); | 359 | bf, size, width); |
360 | } | 360 | } |
361 | 361 | ||
362 | struct sort_entry sort_dso_from = { | ||
363 | .se_header = "Source Shared Object", | ||
364 | .se_cmp = sort__dso_from_cmp, | ||
365 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
366 | .se_width_idx = HISTC_DSO_FROM, | ||
367 | }; | ||
368 | |||
369 | static int64_t | 362 | static int64_t |
370 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 363 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
371 | { | 364 | { |
@@ -389,8 +382,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
389 | if (!from_l->sym && !from_r->sym) | 382 | if (!from_l->sym && !from_r->sym) |
390 | return right->level - left->level; | 383 | return right->level - left->level; |
391 | 384 | ||
392 | return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, | 385 | return _sort__sym_cmp(from_l->sym, from_r->sym); |
393 | from_r->addr); | ||
394 | } | 386 | } |
395 | 387 | ||
396 | static int64_t | 388 | static int64_t |
@@ -402,12 +394,11 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
402 | if (!to_l->sym && !to_r->sym) | 394 | if (!to_l->sym && !to_r->sym) |
403 | return right->level - left->level; | 395 | return right->level - left->level; |
404 | 396 | ||
405 | return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); | 397 | return _sort__sym_cmp(to_l->sym, to_r->sym); |
406 | } | 398 | } |
407 | 399 | ||
408 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 400 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, |
409 | size_t size, | 401 | size_t size, unsigned int width) |
410 | unsigned int width __maybe_unused) | ||
411 | { | 402 | { |
412 | struct addr_map_symbol *from = &self->branch_info->from; | 403 | struct addr_map_symbol *from = &self->branch_info->from; |
413 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 404 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
@@ -416,8 +407,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |||
416 | } | 407 | } |
417 | 408 | ||
418 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 409 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, |
419 | size_t size, | 410 | size_t size, unsigned int width) |
420 | unsigned int width __maybe_unused) | ||
421 | { | 411 | { |
422 | struct addr_map_symbol *to = &self->branch_info->to; | 412 | struct addr_map_symbol *to = &self->branch_info->to; |
423 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 413 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
@@ -425,6 +415,13 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | |||
425 | 415 | ||
426 | } | 416 | } |
427 | 417 | ||
418 | struct sort_entry sort_dso_from = { | ||
419 | .se_header = "Source Shared Object", | ||
420 | .se_cmp = sort__dso_from_cmp, | ||
421 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
422 | .se_width_idx = HISTC_DSO_FROM, | ||
423 | }; | ||
424 | |||
428 | struct sort_entry sort_dso_to = { | 425 | struct sort_entry sort_dso_to = { |
429 | .se_header = "Target Shared Object", | 426 | .se_header = "Target Shared Object", |
430 | .se_cmp = sort__dso_to_cmp, | 427 | .se_cmp = sort__dso_to_cmp, |
@@ -469,6 +466,304 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, | |||
469 | return repsep_snprintf(bf, size, "%-*s", width, out); | 466 | return repsep_snprintf(bf, size, "%-*s", width, out); |
470 | } | 467 | } |
471 | 468 | ||
469 | /* --sort daddr_sym */ | ||
470 | static int64_t | ||
471 | sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) | ||
472 | { | ||
473 | uint64_t l = 0, r = 0; | ||
474 | |||
475 | if (left->mem_info) | ||
476 | l = left->mem_info->daddr.addr; | ||
477 | if (right->mem_info) | ||
478 | r = right->mem_info->daddr.addr; | ||
479 | |||
480 | return (int64_t)(r - l); | ||
481 | } | ||
482 | |||
483 | static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, | ||
484 | size_t size, unsigned int width) | ||
485 | { | ||
486 | uint64_t addr = 0; | ||
487 | struct map *map = NULL; | ||
488 | struct symbol *sym = NULL; | ||
489 | |||
490 | if (self->mem_info) { | ||
491 | addr = self->mem_info->daddr.addr; | ||
492 | map = self->mem_info->daddr.map; | ||
493 | sym = self->mem_info->daddr.sym; | ||
494 | } | ||
495 | return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, | ||
496 | width); | ||
497 | } | ||
498 | |||
499 | static int64_t | ||
500 | sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) | ||
501 | { | ||
502 | struct map *map_l = NULL; | ||
503 | struct map *map_r = NULL; | ||
504 | |||
505 | if (left->mem_info) | ||
506 | map_l = left->mem_info->daddr.map; | ||
507 | if (right->mem_info) | ||
508 | map_r = right->mem_info->daddr.map; | ||
509 | |||
510 | return _sort__dso_cmp(map_l, map_r); | ||
511 | } | ||
512 | |||
513 | static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, | ||
514 | size_t size, unsigned int width) | ||
515 | { | ||
516 | struct map *map = NULL; | ||
517 | |||
518 | if (self->mem_info) | ||
519 | map = self->mem_info->daddr.map; | ||
520 | |||
521 | return _hist_entry__dso_snprintf(map, bf, size, width); | ||
522 | } | ||
523 | |||
524 | static int64_t | ||
525 | sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) | ||
526 | { | ||
527 | union perf_mem_data_src data_src_l; | ||
528 | union perf_mem_data_src data_src_r; | ||
529 | |||
530 | if (left->mem_info) | ||
531 | data_src_l = left->mem_info->data_src; | ||
532 | else | ||
533 | data_src_l.mem_lock = PERF_MEM_LOCK_NA; | ||
534 | |||
535 | if (right->mem_info) | ||
536 | data_src_r = right->mem_info->data_src; | ||
537 | else | ||
538 | data_src_r.mem_lock = PERF_MEM_LOCK_NA; | ||
539 | |||
540 | return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); | ||
541 | } | ||
542 | |||
543 | static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, | ||
544 | size_t size, unsigned int width) | ||
545 | { | ||
546 | const char *out; | ||
547 | u64 mask = PERF_MEM_LOCK_NA; | ||
548 | |||
549 | if (self->mem_info) | ||
550 | mask = self->mem_info->data_src.mem_lock; | ||
551 | |||
552 | if (mask & PERF_MEM_LOCK_NA) | ||
553 | out = "N/A"; | ||
554 | else if (mask & PERF_MEM_LOCK_LOCKED) | ||
555 | out = "Yes"; | ||
556 | else | ||
557 | out = "No"; | ||
558 | |||
559 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
560 | } | ||
561 | |||
562 | static int64_t | ||
563 | sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) | ||
564 | { | ||
565 | union perf_mem_data_src data_src_l; | ||
566 | union perf_mem_data_src data_src_r; | ||
567 | |||
568 | if (left->mem_info) | ||
569 | data_src_l = left->mem_info->data_src; | ||
570 | else | ||
571 | data_src_l.mem_dtlb = PERF_MEM_TLB_NA; | ||
572 | |||
573 | if (right->mem_info) | ||
574 | data_src_r = right->mem_info->data_src; | ||
575 | else | ||
576 | data_src_r.mem_dtlb = PERF_MEM_TLB_NA; | ||
577 | |||
578 | return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); | ||
579 | } | ||
580 | |||
581 | static const char * const tlb_access[] = { | ||
582 | "N/A", | ||
583 | "HIT", | ||
584 | "MISS", | ||
585 | "L1", | ||
586 | "L2", | ||
587 | "Walker", | ||
588 | "Fault", | ||
589 | }; | ||
590 | #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) | ||
591 | |||
592 | static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, | ||
593 | size_t size, unsigned int width) | ||
594 | { | ||
595 | char out[64]; | ||
596 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
597 | size_t l = 0, i; | ||
598 | u64 m = PERF_MEM_TLB_NA; | ||
599 | u64 hit, miss; | ||
600 | |||
601 | out[0] = '\0'; | ||
602 | |||
603 | if (self->mem_info) | ||
604 | m = self->mem_info->data_src.mem_dtlb; | ||
605 | |||
606 | hit = m & PERF_MEM_TLB_HIT; | ||
607 | miss = m & PERF_MEM_TLB_MISS; | ||
608 | |||
609 | /* already taken care of */ | ||
610 | m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); | ||
611 | |||
612 | for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { | ||
613 | if (!(m & 0x1)) | ||
614 | continue; | ||
615 | if (l) { | ||
616 | strcat(out, " or "); | ||
617 | l += 4; | ||
618 | } | ||
619 | strncat(out, tlb_access[i], sz - l); | ||
620 | l += strlen(tlb_access[i]); | ||
621 | } | ||
622 | if (*out == '\0') | ||
623 | strcpy(out, "N/A"); | ||
624 | if (hit) | ||
625 | strncat(out, " hit", sz - l); | ||
626 | if (miss) | ||
627 | strncat(out, " miss", sz - l); | ||
628 | |||
629 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
630 | } | ||
631 | |||
632 | static int64_t | ||
633 | sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) | ||
634 | { | ||
635 | union perf_mem_data_src data_src_l; | ||
636 | union perf_mem_data_src data_src_r; | ||
637 | |||
638 | if (left->mem_info) | ||
639 | data_src_l = left->mem_info->data_src; | ||
640 | else | ||
641 | data_src_l.mem_lvl = PERF_MEM_LVL_NA; | ||
642 | |||
643 | if (right->mem_info) | ||
644 | data_src_r = right->mem_info->data_src; | ||
645 | else | ||
646 | data_src_r.mem_lvl = PERF_MEM_LVL_NA; | ||
647 | |||
648 | return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); | ||
649 | } | ||
650 | |||
651 | static const char * const mem_lvl[] = { | ||
652 | "N/A", | ||
653 | "HIT", | ||
654 | "MISS", | ||
655 | "L1", | ||
656 | "LFB", | ||
657 | "L2", | ||
658 | "L3", | ||
659 | "Local RAM", | ||
660 | "Remote RAM (1 hop)", | ||
661 | "Remote RAM (2 hops)", | ||
662 | "Remote Cache (1 hop)", | ||
663 | "Remote Cache (2 hops)", | ||
664 | "I/O", | ||
665 | "Uncached", | ||
666 | }; | ||
667 | #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) | ||
668 | |||
669 | static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, | ||
670 | size_t size, unsigned int width) | ||
671 | { | ||
672 | char out[64]; | ||
673 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
674 | size_t i, l = 0; | ||
675 | u64 m = PERF_MEM_LVL_NA; | ||
676 | u64 hit, miss; | ||
677 | |||
678 | if (self->mem_info) | ||
679 | m = self->mem_info->data_src.mem_lvl; | ||
680 | |||
681 | out[0] = '\0'; | ||
682 | |||
683 | hit = m & PERF_MEM_LVL_HIT; | ||
684 | miss = m & PERF_MEM_LVL_MISS; | ||
685 | |||
686 | /* already taken care of */ | ||
687 | m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); | ||
688 | |||
689 | for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { | ||
690 | if (!(m & 0x1)) | ||
691 | continue; | ||
692 | if (l) { | ||
693 | strcat(out, " or "); | ||
694 | l += 4; | ||
695 | } | ||
696 | strncat(out, mem_lvl[i], sz - l); | ||
697 | l += strlen(mem_lvl[i]); | ||
698 | } | ||
699 | if (*out == '\0') | ||
700 | strcpy(out, "N/A"); | ||
701 | if (hit) | ||
702 | strncat(out, " hit", sz - l); | ||
703 | if (miss) | ||
704 | strncat(out, " miss", sz - l); | ||
705 | |||
706 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
707 | } | ||
708 | |||
709 | static int64_t | ||
710 | sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) | ||
711 | { | ||
712 | union perf_mem_data_src data_src_l; | ||
713 | union perf_mem_data_src data_src_r; | ||
714 | |||
715 | if (left->mem_info) | ||
716 | data_src_l = left->mem_info->data_src; | ||
717 | else | ||
718 | data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; | ||
719 | |||
720 | if (right->mem_info) | ||
721 | data_src_r = right->mem_info->data_src; | ||
722 | else | ||
723 | data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; | ||
724 | |||
725 | return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); | ||
726 | } | ||
727 | |||
728 | static const char * const snoop_access[] = { | ||
729 | "N/A", | ||
730 | "None", | ||
731 | "Miss", | ||
732 | "Hit", | ||
733 | "HitM", | ||
734 | }; | ||
735 | #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) | ||
736 | |||
737 | static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, | ||
738 | size_t size, unsigned int width) | ||
739 | { | ||
740 | char out[64]; | ||
741 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
742 | size_t i, l = 0; | ||
743 | u64 m = PERF_MEM_SNOOP_NA; | ||
744 | |||
745 | out[0] = '\0'; | ||
746 | |||
747 | if (self->mem_info) | ||
748 | m = self->mem_info->data_src.mem_snoop; | ||
749 | |||
750 | for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { | ||
751 | if (!(m & 0x1)) | ||
752 | continue; | ||
753 | if (l) { | ||
754 | strcat(out, " or "); | ||
755 | l += 4; | ||
756 | } | ||
757 | strncat(out, snoop_access[i], sz - l); | ||
758 | l += strlen(snoop_access[i]); | ||
759 | } | ||
760 | |||
761 | if (*out == '\0') | ||
762 | strcpy(out, "N/A"); | ||
763 | |||
764 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
765 | } | ||
766 | |||
472 | struct sort_entry sort_mispredict = { | 767 | struct sort_entry sort_mispredict = { |
473 | .se_header = "Branch Mispredicted", | 768 | .se_header = "Branch Mispredicted", |
474 | .se_cmp = sort__mispredict_cmp, | 769 | .se_cmp = sort__mispredict_cmp, |
@@ -476,6 +771,91 @@ struct sort_entry sort_mispredict = { | |||
476 | .se_width_idx = HISTC_MISPREDICT, | 771 | .se_width_idx = HISTC_MISPREDICT, |
477 | }; | 772 | }; |
478 | 773 | ||
774 | static u64 he_weight(struct hist_entry *he) | ||
775 | { | ||
776 | return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0; | ||
777 | } | ||
778 | |||
779 | static int64_t | ||
780 | sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) | ||
781 | { | ||
782 | return he_weight(left) - he_weight(right); | ||
783 | } | ||
784 | |||
785 | static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, | ||
786 | size_t size, unsigned int width) | ||
787 | { | ||
788 | return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); | ||
789 | } | ||
790 | |||
791 | struct sort_entry sort_local_weight = { | ||
792 | .se_header = "Local Weight", | ||
793 | .se_cmp = sort__local_weight_cmp, | ||
794 | .se_snprintf = hist_entry__local_weight_snprintf, | ||
795 | .se_width_idx = HISTC_LOCAL_WEIGHT, | ||
796 | }; | ||
797 | |||
798 | static int64_t | ||
799 | sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) | ||
800 | { | ||
801 | return left->stat.weight - right->stat.weight; | ||
802 | } | ||
803 | |||
804 | static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, | ||
805 | size_t size, unsigned int width) | ||
806 | { | ||
807 | return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); | ||
808 | } | ||
809 | |||
810 | struct sort_entry sort_global_weight = { | ||
811 | .se_header = "Weight", | ||
812 | .se_cmp = sort__global_weight_cmp, | ||
813 | .se_snprintf = hist_entry__global_weight_snprintf, | ||
814 | .se_width_idx = HISTC_GLOBAL_WEIGHT, | ||
815 | }; | ||
816 | |||
817 | struct sort_entry sort_mem_daddr_sym = { | ||
818 | .se_header = "Data Symbol", | ||
819 | .se_cmp = sort__daddr_cmp, | ||
820 | .se_snprintf = hist_entry__daddr_snprintf, | ||
821 | .se_width_idx = HISTC_MEM_DADDR_SYMBOL, | ||
822 | }; | ||
823 | |||
824 | struct sort_entry sort_mem_daddr_dso = { | ||
825 | .se_header = "Data Object", | ||
826 | .se_cmp = sort__dso_daddr_cmp, | ||
827 | .se_snprintf = hist_entry__dso_daddr_snprintf, | ||
828 | .se_width_idx = HISTC_MEM_DADDR_SYMBOL, | ||
829 | }; | ||
830 | |||
831 | struct sort_entry sort_mem_locked = { | ||
832 | .se_header = "Locked", | ||
833 | .se_cmp = sort__locked_cmp, | ||
834 | .se_snprintf = hist_entry__locked_snprintf, | ||
835 | .se_width_idx = HISTC_MEM_LOCKED, | ||
836 | }; | ||
837 | |||
838 | struct sort_entry sort_mem_tlb = { | ||
839 | .se_header = "TLB access", | ||
840 | .se_cmp = sort__tlb_cmp, | ||
841 | .se_snprintf = hist_entry__tlb_snprintf, | ||
842 | .se_width_idx = HISTC_MEM_TLB, | ||
843 | }; | ||
844 | |||
845 | struct sort_entry sort_mem_lvl = { | ||
846 | .se_header = "Memory access", | ||
847 | .se_cmp = sort__lvl_cmp, | ||
848 | .se_snprintf = hist_entry__lvl_snprintf, | ||
849 | .se_width_idx = HISTC_MEM_LVL, | ||
850 | }; | ||
851 | |||
852 | struct sort_entry sort_mem_snoop = { | ||
853 | .se_header = "Snoop", | ||
854 | .se_cmp = sort__snoop_cmp, | ||
855 | .se_snprintf = hist_entry__snoop_snprintf, | ||
856 | .se_width_idx = HISTC_MEM_SNOOP, | ||
857 | }; | ||
858 | |||
479 | struct sort_dimension { | 859 | struct sort_dimension { |
480 | const char *name; | 860 | const char *name; |
481 | struct sort_entry *entry; | 861 | struct sort_entry *entry; |
@@ -484,30 +864,70 @@ struct sort_dimension { | |||
484 | 864 | ||
485 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } | 865 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } |
486 | 866 | ||
487 | static struct sort_dimension sort_dimensions[] = { | 867 | static struct sort_dimension common_sort_dimensions[] = { |
488 | DIM(SORT_PID, "pid", sort_thread), | 868 | DIM(SORT_PID, "pid", sort_thread), |
489 | DIM(SORT_COMM, "comm", sort_comm), | 869 | DIM(SORT_COMM, "comm", sort_comm), |
490 | DIM(SORT_DSO, "dso", sort_dso), | 870 | DIM(SORT_DSO, "dso", sort_dso), |
491 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
492 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
493 | DIM(SORT_SYM, "symbol", sort_sym), | 871 | DIM(SORT_SYM, "symbol", sort_sym), |
494 | DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), | ||
495 | DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), | ||
496 | DIM(SORT_PARENT, "parent", sort_parent), | 872 | DIM(SORT_PARENT, "parent", sort_parent), |
497 | DIM(SORT_CPU, "cpu", sort_cpu), | 873 | DIM(SORT_CPU, "cpu", sort_cpu), |
498 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
499 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 874 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
500 | }; | 875 | }; |
501 | 876 | ||
877 | #undef DIM | ||
878 | |||
879 | #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } | ||
880 | |||
881 | static struct sort_dimension bstack_sort_dimensions[] = { | ||
882 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
883 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
884 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), | ||
885 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), | ||
886 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
887 | }; | ||
888 | |||
889 | #undef DIM | ||
890 | |||
891 | #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } | ||
892 | |||
893 | static struct sort_dimension memory_sort_dimensions[] = { | ||
894 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | ||
895 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | ||
896 | DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), | ||
897 | DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), | ||
898 | DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), | ||
899 | DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), | ||
900 | DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), | ||
901 | DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), | ||
902 | }; | ||
903 | |||
904 | #undef DIM | ||
905 | |||
906 | static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) | ||
907 | { | ||
908 | if (sd->taken) | ||
909 | return; | ||
910 | |||
911 | if (sd->entry->se_collapse) | ||
912 | sort__need_collapse = 1; | ||
913 | |||
914 | if (list_empty(&hist_entry__sort_list)) | ||
915 | sort__first_dimension = idx; | ||
916 | |||
917 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
918 | sd->taken = 1; | ||
919 | } | ||
920 | |||
502 | int sort_dimension__add(const char *tok) | 921 | int sort_dimension__add(const char *tok) |
503 | { | 922 | { |
504 | unsigned int i; | 923 | unsigned int i; |
505 | 924 | ||
506 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 925 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { |
507 | struct sort_dimension *sd = &sort_dimensions[i]; | 926 | struct sort_dimension *sd = &common_sort_dimensions[i]; |
508 | 927 | ||
509 | if (strncasecmp(tok, sd->name, strlen(tok))) | 928 | if (strncasecmp(tok, sd->name, strlen(tok))) |
510 | continue; | 929 | continue; |
930 | |||
511 | if (sd->entry == &sort_parent) { | 931 | if (sd->entry == &sort_parent) { |
512 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 932 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
513 | if (ret) { | 933 | if (ret) { |
@@ -518,68 +938,78 @@ int sort_dimension__add(const char *tok) | |||
518 | return -EINVAL; | 938 | return -EINVAL; |
519 | } | 939 | } |
520 | sort__has_parent = 1; | 940 | sort__has_parent = 1; |
521 | } else if (sd->entry == &sort_sym || | 941 | } else if (sd->entry == &sort_sym) { |
522 | sd->entry == &sort_sym_from || | ||
523 | sd->entry == &sort_sym_to) { | ||
524 | sort__has_sym = 1; | 942 | sort__has_sym = 1; |
525 | } | 943 | } |
526 | 944 | ||
527 | if (sd->taken) | 945 | __sort_dimension__add(sd, i); |
528 | return 0; | 946 | return 0; |
529 | 947 | } | |
530 | if (sd->entry->se_collapse) | 948 | |
531 | sort__need_collapse = 1; | 949 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { |
532 | 950 | struct sort_dimension *sd = &bstack_sort_dimensions[i]; | |
533 | if (list_empty(&hist_entry__sort_list)) { | 951 | |
534 | if (!strcmp(sd->name, "pid")) | 952 | if (strncasecmp(tok, sd->name, strlen(tok))) |
535 | sort__first_dimension = SORT_PID; | 953 | continue; |
536 | else if (!strcmp(sd->name, "comm")) | 954 | |
537 | sort__first_dimension = SORT_COMM; | 955 | if (sort__mode != SORT_MODE__BRANCH) |
538 | else if (!strcmp(sd->name, "dso")) | 956 | return -EINVAL; |
539 | sort__first_dimension = SORT_DSO; | 957 | |
540 | else if (!strcmp(sd->name, "symbol")) | 958 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) |
541 | sort__first_dimension = SORT_SYM; | 959 | sort__has_sym = 1; |
542 | else if (!strcmp(sd->name, "parent")) | 960 | |
543 | sort__first_dimension = SORT_PARENT; | 961 | __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); |
544 | else if (!strcmp(sd->name, "cpu")) | 962 | return 0; |
545 | sort__first_dimension = SORT_CPU; | 963 | } |
546 | else if (!strcmp(sd->name, "symbol_from")) | 964 | |
547 | sort__first_dimension = SORT_SYM_FROM; | 965 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { |
548 | else if (!strcmp(sd->name, "symbol_to")) | 966 | struct sort_dimension *sd = &memory_sort_dimensions[i]; |
549 | sort__first_dimension = SORT_SYM_TO; | ||
550 | else if (!strcmp(sd->name, "dso_from")) | ||
551 | sort__first_dimension = SORT_DSO_FROM; | ||
552 | else if (!strcmp(sd->name, "dso_to")) | ||
553 | sort__first_dimension = SORT_DSO_TO; | ||
554 | else if (!strcmp(sd->name, "mispredict")) | ||
555 | sort__first_dimension = SORT_MISPREDICT; | ||
556 | } | ||
557 | 967 | ||
558 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 968 | if (strncasecmp(tok, sd->name, strlen(tok))) |
559 | sd->taken = 1; | 969 | continue; |
970 | |||
971 | if (sort__mode != SORT_MODE__MEMORY) | ||
972 | return -EINVAL; | ||
973 | |||
974 | if (sd->entry == &sort_mem_daddr_sym) | ||
975 | sort__has_sym = 1; | ||
560 | 976 | ||
977 | __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); | ||
561 | return 0; | 978 | return 0; |
562 | } | 979 | } |
980 | |||
563 | return -ESRCH; | 981 | return -ESRCH; |
564 | } | 982 | } |
565 | 983 | ||
566 | void setup_sorting(const char * const usagestr[], const struct option *opts) | 984 | int setup_sorting(void) |
567 | { | 985 | { |
568 | char *tmp, *tok, *str = strdup(sort_order); | 986 | char *tmp, *tok, *str = strdup(sort_order); |
987 | int ret = 0; | ||
988 | |||
989 | if (str == NULL) { | ||
990 | error("Not enough memory to setup sort keys"); | ||
991 | return -ENOMEM; | ||
992 | } | ||
569 | 993 | ||
570 | for (tok = strtok_r(str, ", ", &tmp); | 994 | for (tok = strtok_r(str, ", ", &tmp); |
571 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 995 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
572 | if (sort_dimension__add(tok) < 0) { | 996 | ret = sort_dimension__add(tok); |
997 | if (ret == -EINVAL) { | ||
998 | error("Invalid --sort key: `%s'", tok); | ||
999 | break; | ||
1000 | } else if (ret == -ESRCH) { | ||
573 | error("Unknown --sort key: `%s'", tok); | 1001 | error("Unknown --sort key: `%s'", tok); |
574 | usage_with_options(usagestr, opts); | 1002 | break; |
575 | } | 1003 | } |
576 | } | 1004 | } |
577 | 1005 | ||
578 | free(str); | 1006 | free(str); |
1007 | return ret; | ||
579 | } | 1008 | } |
580 | 1009 | ||
581 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 1010 | static void sort_entry__setup_elide(struct sort_entry *self, |
582 | const char *list_name, FILE *fp) | 1011 | struct strlist *list, |
1012 | const char *list_name, FILE *fp) | ||
583 | { | 1013 | { |
584 | if (list && strlist__nr_entries(list) == 1) { | 1014 | if (list && strlist__nr_entries(list) == 1) { |
585 | if (fp != NULL) | 1015 | if (fp != NULL) |
@@ -588,3 +1018,42 @@ void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | |||
588 | self->elide = true; | 1018 | self->elide = true; |
589 | } | 1019 | } |
590 | } | 1020 | } |
1021 | |||
1022 | void sort__setup_elide(FILE *output) | ||
1023 | { | ||
1024 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1025 | "dso", output); | ||
1026 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, | ||
1027 | "comm", output); | ||
1028 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, | ||
1029 | "symbol", output); | ||
1030 | |||
1031 | if (sort__mode == SORT_MODE__BRANCH) { | ||
1032 | sort_entry__setup_elide(&sort_dso_from, | ||
1033 | symbol_conf.dso_from_list, | ||
1034 | "dso_from", output); | ||
1035 | sort_entry__setup_elide(&sort_dso_to, | ||
1036 | symbol_conf.dso_to_list, | ||
1037 | "dso_to", output); | ||
1038 | sort_entry__setup_elide(&sort_sym_from, | ||
1039 | symbol_conf.sym_from_list, | ||
1040 | "sym_from", output); | ||
1041 | sort_entry__setup_elide(&sort_sym_to, | ||
1042 | symbol_conf.sym_to_list, | ||
1043 | "sym_to", output); | ||
1044 | } else if (sort__mode == SORT_MODE__MEMORY) { | ||
1045 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1046 | "symbol_daddr", output); | ||
1047 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1048 | "dso_daddr", output); | ||
1049 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1050 | "mem", output); | ||
1051 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1052 | "local_weight", output); | ||
1053 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1054 | "tlb", output); | ||
1055 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | ||
1056 | "snoop", output); | ||
1057 | } | ||
1058 | |||
1059 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b4e8c3ba559d..45ac84c1e037 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -32,7 +32,7 @@ extern const char default_sort_order[]; | |||
32 | extern int sort__need_collapse; | 32 | extern int sort__need_collapse; |
33 | extern int sort__has_parent; | 33 | extern int sort__has_parent; |
34 | extern int sort__has_sym; | 34 | extern int sort__has_sym; |
35 | extern int sort__branch_mode; | 35 | extern enum sort_mode sort__mode; |
36 | extern struct sort_entry sort_comm; | 36 | extern struct sort_entry sort_comm; |
37 | extern struct sort_entry sort_dso; | 37 | extern struct sort_entry sort_dso; |
38 | extern struct sort_entry sort_sym; | 38 | extern struct sort_entry sort_sym; |
@@ -49,15 +49,13 @@ struct he_stat { | |||
49 | u64 period_us; | 49 | u64 period_us; |
50 | u64 period_guest_sys; | 50 | u64 period_guest_sys; |
51 | u64 period_guest_us; | 51 | u64 period_guest_us; |
52 | u64 weight; | ||
52 | u32 nr_events; | 53 | u32 nr_events; |
53 | }; | 54 | }; |
54 | 55 | ||
55 | struct hist_entry_diff { | 56 | struct hist_entry_diff { |
56 | bool computed; | 57 | bool computed; |
57 | 58 | ||
58 | /* PERF_HPP__DISPL */ | ||
59 | int displacement; | ||
60 | |||
61 | /* PERF_HPP__DELTA */ | 59 | /* PERF_HPP__DELTA */ |
62 | double period_ratio_delta; | 60 | double period_ratio_delta; |
63 | 61 | ||
@@ -103,7 +101,8 @@ struct hist_entry { | |||
103 | struct rb_root sorted_chain; | 101 | struct rb_root sorted_chain; |
104 | struct branch_info *branch_info; | 102 | struct branch_info *branch_info; |
105 | struct hists *hists; | 103 | struct hists *hists; |
106 | struct callchain_root callchain[0]; | 104 | struct mem_info *mem_info; |
105 | struct callchain_root callchain[0]; /* must be last member */ | ||
107 | }; | 106 | }; |
108 | 107 | ||
109 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | 108 | static inline bool hist_entry__has_pairs(struct hist_entry *he) |
@@ -118,25 +117,46 @@ static inline struct hist_entry *hist_entry__next_pair(struct hist_entry *he) | |||
118 | return NULL; | 117 | return NULL; |
119 | } | 118 | } |
120 | 119 | ||
121 | static inline void hist__entry_add_pair(struct hist_entry *he, | 120 | static inline void hist_entry__add_pair(struct hist_entry *pair, |
122 | struct hist_entry *pair) | 121 | struct hist_entry *he) |
123 | { | 122 | { |
124 | list_add_tail(&he->pairs.head, &pair->pairs.node); | 123 | list_add_tail(&pair->pairs.node, &he->pairs.head); |
125 | } | 124 | } |
126 | 125 | ||
126 | enum sort_mode { | ||
127 | SORT_MODE__NORMAL, | ||
128 | SORT_MODE__BRANCH, | ||
129 | SORT_MODE__MEMORY, | ||
130 | }; | ||
131 | |||
127 | enum sort_type { | 132 | enum sort_type { |
133 | /* common sort keys */ | ||
128 | SORT_PID, | 134 | SORT_PID, |
129 | SORT_COMM, | 135 | SORT_COMM, |
130 | SORT_DSO, | 136 | SORT_DSO, |
131 | SORT_SYM, | 137 | SORT_SYM, |
132 | SORT_PARENT, | 138 | SORT_PARENT, |
133 | SORT_CPU, | 139 | SORT_CPU, |
134 | SORT_DSO_FROM, | 140 | SORT_SRCLINE, |
141 | |||
142 | /* branch stack specific sort keys */ | ||
143 | __SORT_BRANCH_STACK, | ||
144 | SORT_DSO_FROM = __SORT_BRANCH_STACK, | ||
135 | SORT_DSO_TO, | 145 | SORT_DSO_TO, |
136 | SORT_SYM_FROM, | 146 | SORT_SYM_FROM, |
137 | SORT_SYM_TO, | 147 | SORT_SYM_TO, |
138 | SORT_MISPREDICT, | 148 | SORT_MISPREDICT, |
139 | SORT_SRCLINE, | 149 | |
150 | /* memory mode specific sort keys */ | ||
151 | __SORT_MEMORY_MODE, | ||
152 | SORT_LOCAL_WEIGHT = __SORT_MEMORY_MODE, | ||
153 | SORT_GLOBAL_WEIGHT, | ||
154 | SORT_MEM_DADDR_SYMBOL, | ||
155 | SORT_MEM_DADDR_DSO, | ||
156 | SORT_MEM_LOCKED, | ||
157 | SORT_MEM_TLB, | ||
158 | SORT_MEM_LVL, | ||
159 | SORT_MEM_SNOOP, | ||
140 | }; | 160 | }; |
141 | 161 | ||
142 | /* | 162 | /* |
@@ -159,9 +179,8 @@ struct sort_entry { | |||
159 | extern struct sort_entry sort_thread; | 179 | extern struct sort_entry sort_thread; |
160 | extern struct list_head hist_entry__sort_list; | 180 | extern struct list_head hist_entry__sort_list; |
161 | 181 | ||
162 | void setup_sorting(const char * const usagestr[], const struct option *opts); | 182 | int setup_sorting(void); |
163 | extern int sort_dimension__add(const char *); | 183 | extern int sort_dimension__add(const char *); |
164 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 184 | void sort__setup_elide(FILE *fp); |
165 | const char *list_name, FILE *fp); | ||
166 | 185 | ||
167 | #endif /* __PERF_SORT_H */ | 186 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 23742126f47c..7c59c28afcc5 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
@@ -37,7 +37,7 @@ double stddev_stats(struct stats *stats) | |||
37 | { | 37 | { |
38 | double variance, variance_mean; | 38 | double variance, variance_mean; |
39 | 39 | ||
40 | if (!stats->n) | 40 | if (stats->n < 2) |
41 | return 0.0; | 41 | return 0.0; |
42 | 42 | ||
43 | variance = stats->M2 / (stats->n - 1); | 43 | variance = stats->M2 / (stats->n - 1); |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 346707df04b9..29c7b2cb2521 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -332,6 +332,24 @@ char *strxfrchar(char *s, char from, char to) | |||
332 | } | 332 | } |
333 | 333 | ||
334 | /** | 334 | /** |
335 | * ltrim - Removes leading whitespace from @s. | ||
336 | * @s: The string to be stripped. | ||
337 | * | ||
338 | * Return pointer to the first non-whitespace character in @s. | ||
339 | */ | ||
340 | char *ltrim(char *s) | ||
341 | { | ||
342 | int len = strlen(s); | ||
343 | |||
344 | while (len && isspace(*s)) { | ||
345 | len--; | ||
346 | s++; | ||
347 | } | ||
348 | |||
349 | return s; | ||
350 | } | ||
351 | |||
352 | /** | ||
335 | * rtrim - Removes trailing whitespace from @s. | 353 | * rtrim - Removes trailing whitespace from @s. |
336 | * @s: The string to be stripped. | 354 | * @s: The string to be stripped. |
337 | * | 355 | * |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 155d8b7078a7..eabdce0a2daa 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
@@ -35,11 +35,11 @@ out_delete: | |||
35 | return NULL; | 35 | return NULL; |
36 | } | 36 | } |
37 | 37 | ||
38 | static void str_node__delete(struct str_node *self, bool dupstr) | 38 | static void str_node__delete(struct str_node *snode, bool dupstr) |
39 | { | 39 | { |
40 | if (dupstr) | 40 | if (dupstr) |
41 | free((void *)self->s); | 41 | free((void *)snode->s); |
42 | free(self); | 42 | free(snode); |
43 | } | 43 | } |
44 | 44 | ||
45 | static | 45 | static |
@@ -59,12 +59,12 @@ static int strlist__node_cmp(struct rb_node *rb_node, const void *entry) | |||
59 | return strcmp(snode->s, str); | 59 | return strcmp(snode->s, str); |
60 | } | 60 | } |
61 | 61 | ||
62 | int strlist__add(struct strlist *self, const char *new_entry) | 62 | int strlist__add(struct strlist *slist, const char *new_entry) |
63 | { | 63 | { |
64 | return rblist__add_node(&self->rblist, new_entry); | 64 | return rblist__add_node(&slist->rblist, new_entry); |
65 | } | 65 | } |
66 | 66 | ||
67 | int strlist__load(struct strlist *self, const char *filename) | 67 | int strlist__load(struct strlist *slist, const char *filename) |
68 | { | 68 | { |
69 | char entry[1024]; | 69 | char entry[1024]; |
70 | int err; | 70 | int err; |
@@ -80,7 +80,7 @@ int strlist__load(struct strlist *self, const char *filename) | |||
80 | continue; | 80 | continue; |
81 | entry[len - 1] = '\0'; | 81 | entry[len - 1] = '\0'; |
82 | 82 | ||
83 | err = strlist__add(self, entry); | 83 | err = strlist__add(slist, entry); |
84 | if (err != 0) | 84 | if (err != 0) |
85 | goto out; | 85 | goto out; |
86 | } | 86 | } |
@@ -107,56 +107,56 @@ struct str_node *strlist__find(struct strlist *slist, const char *entry) | |||
107 | return snode; | 107 | return snode; |
108 | } | 108 | } |
109 | 109 | ||
110 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 110 | static int strlist__parse_list_entry(struct strlist *slist, const char *s) |
111 | { | 111 | { |
112 | if (strncmp(s, "file://", 7) == 0) | 112 | if (strncmp(s, "file://", 7) == 0) |
113 | return strlist__load(self, s + 7); | 113 | return strlist__load(slist, s + 7); |
114 | 114 | ||
115 | return strlist__add(self, s); | 115 | return strlist__add(slist, s); |
116 | } | 116 | } |
117 | 117 | ||
118 | int strlist__parse_list(struct strlist *self, const char *s) | 118 | int strlist__parse_list(struct strlist *slist, const char *s) |
119 | { | 119 | { |
120 | char *sep; | 120 | char *sep; |
121 | int err; | 121 | int err; |
122 | 122 | ||
123 | while ((sep = strchr(s, ',')) != NULL) { | 123 | while ((sep = strchr(s, ',')) != NULL) { |
124 | *sep = '\0'; | 124 | *sep = '\0'; |
125 | err = strlist__parse_list_entry(self, s); | 125 | err = strlist__parse_list_entry(slist, s); |
126 | *sep = ','; | 126 | *sep = ','; |
127 | if (err != 0) | 127 | if (err != 0) |
128 | return err; | 128 | return err; |
129 | s = sep + 1; | 129 | s = sep + 1; |
130 | } | 130 | } |
131 | 131 | ||
132 | return *s ? strlist__parse_list_entry(self, s) : 0; | 132 | return *s ? strlist__parse_list_entry(slist, s) : 0; |
133 | } | 133 | } |
134 | 134 | ||
135 | struct strlist *strlist__new(bool dupstr, const char *slist) | 135 | struct strlist *strlist__new(bool dupstr, const char *list) |
136 | { | 136 | { |
137 | struct strlist *self = malloc(sizeof(*self)); | 137 | struct strlist *slist = malloc(sizeof(*slist)); |
138 | 138 | ||
139 | if (self != NULL) { | 139 | if (slist != NULL) { |
140 | rblist__init(&self->rblist); | 140 | rblist__init(&slist->rblist); |
141 | self->rblist.node_cmp = strlist__node_cmp; | 141 | slist->rblist.node_cmp = strlist__node_cmp; |
142 | self->rblist.node_new = strlist__node_new; | 142 | slist->rblist.node_new = strlist__node_new; |
143 | self->rblist.node_delete = strlist__node_delete; | 143 | slist->rblist.node_delete = strlist__node_delete; |
144 | 144 | ||
145 | self->dupstr = dupstr; | 145 | slist->dupstr = dupstr; |
146 | if (slist && strlist__parse_list(self, slist) != 0) | 146 | if (list && strlist__parse_list(slist, list) != 0) |
147 | goto out_error; | 147 | goto out_error; |
148 | } | 148 | } |
149 | 149 | ||
150 | return self; | 150 | return slist; |
151 | out_error: | 151 | out_error: |
152 | free(self); | 152 | free(slist); |
153 | return NULL; | 153 | return NULL; |
154 | } | 154 | } |
155 | 155 | ||
156 | void strlist__delete(struct strlist *self) | 156 | void strlist__delete(struct strlist *slist) |
157 | { | 157 | { |
158 | if (self != NULL) | 158 | if (slist != NULL) |
159 | rblist__delete(&self->rblist); | 159 | rblist__delete(&slist->rblist); |
160 | } | 160 | } |
161 | 161 | ||
162 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx) | 162 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index dd9f922ec67c..5c7f87069d9c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
@@ -17,34 +17,34 @@ struct strlist { | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | struct strlist *strlist__new(bool dupstr, const char *slist); | 19 | struct strlist *strlist__new(bool dupstr, const char *slist); |
20 | void strlist__delete(struct strlist *self); | 20 | void strlist__delete(struct strlist *slist); |
21 | 21 | ||
22 | void strlist__remove(struct strlist *self, struct str_node *sn); | 22 | void strlist__remove(struct strlist *slist, struct str_node *sn); |
23 | int strlist__load(struct strlist *self, const char *filename); | 23 | int strlist__load(struct strlist *slist, const char *filename); |
24 | int strlist__add(struct strlist *self, const char *str); | 24 | int strlist__add(struct strlist *slist, const char *str); |
25 | 25 | ||
26 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 26 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx); |
27 | struct str_node *strlist__find(struct strlist *self, const char *entry); | 27 | struct str_node *strlist__find(struct strlist *slist, const char *entry); |
28 | 28 | ||
29 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | 29 | static inline bool strlist__has_entry(struct strlist *slist, const char *entry) |
30 | { | 30 | { |
31 | return strlist__find(self, entry) != NULL; | 31 | return strlist__find(slist, entry) != NULL; |
32 | } | 32 | } |
33 | 33 | ||
34 | static inline bool strlist__empty(const struct strlist *self) | 34 | static inline bool strlist__empty(const struct strlist *slist) |
35 | { | 35 | { |
36 | return rblist__empty(&self->rblist); | 36 | return rblist__empty(&slist->rblist); |
37 | } | 37 | } |
38 | 38 | ||
39 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | 39 | static inline unsigned int strlist__nr_entries(const struct strlist *slist) |
40 | { | 40 | { |
41 | return rblist__nr_entries(&self->rblist); | 41 | return rblist__nr_entries(&slist->rblist); |
42 | } | 42 | } |
43 | 43 | ||
44 | /* For strlist iteration */ | 44 | /* For strlist iteration */ |
45 | static inline struct str_node *strlist__first(struct strlist *self) | 45 | static inline struct str_node *strlist__first(struct strlist *slist) |
46 | { | 46 | { |
47 | struct rb_node *rn = rb_first(&self->rblist.entries); | 47 | struct rb_node *rn = rb_first(&slist->rblist.entries); |
48 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | 48 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; |
49 | } | 49 | } |
50 | static inline struct str_node *strlist__next(struct str_node *sn) | 50 | static inline struct str_node *strlist__next(struct str_node *sn) |
@@ -59,21 +59,21 @@ static inline struct str_node *strlist__next(struct str_node *sn) | |||
59 | /** | 59 | /** |
60 | * strlist_for_each - iterate over a strlist | 60 | * strlist_for_each - iterate over a strlist |
61 | * @pos: the &struct str_node to use as a loop cursor. | 61 | * @pos: the &struct str_node to use as a loop cursor. |
62 | * @self: the &struct strlist for loop. | 62 | * @slist: the &struct strlist for loop. |
63 | */ | 63 | */ |
64 | #define strlist__for_each(pos, self) \ | 64 | #define strlist__for_each(pos, slist) \ |
65 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | 65 | for (pos = strlist__first(slist); pos; pos = strlist__next(pos)) |
66 | 66 | ||
67 | /** | 67 | /** |
68 | * strlist_for_each_safe - iterate over a strlist safe against removal of | 68 | * strlist_for_each_safe - iterate over a strlist safe against removal of |
69 | * str_node | 69 | * str_node |
70 | * @pos: the &struct str_node to use as a loop cursor. | 70 | * @pos: the &struct str_node to use as a loop cursor. |
71 | * @n: another &struct str_node to use as temporary storage. | 71 | * @n: another &struct str_node to use as temporary storage. |
72 | * @self: the &struct strlist for loop. | 72 | * @slist: the &struct strlist for loop. |
73 | */ | 73 | */ |
74 | #define strlist__for_each_safe(pos, n, self) \ | 74 | #define strlist__for_each_safe(pos, n, slist) \ |
75 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | 75 | for (pos = strlist__first(slist), n = strlist__next(pos); pos;\ |
76 | pos = n, n = strlist__next(n)) | 76 | pos = n, n = strlist__next(n)) |
77 | 77 | ||
78 | int strlist__parse_list(struct strlist *self, const char *s); | 78 | int strlist__parse_list(struct strlist *slist, const char *s); |
79 | #endif /* __PERF_STRLIST_H */ | 79 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index db0cc92cf2ea..4b12bf850325 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -1,6 +1,3 @@ | |||
1 | #include <libelf.h> | ||
2 | #include <gelf.h> | ||
3 | #include <elf.h> | ||
4 | #include <fcntl.h> | 1 | #include <fcntl.h> |
5 | #include <stdio.h> | 2 | #include <stdio.h> |
6 | #include <errno.h> | 3 | #include <errno.h> |
@@ -718,6 +715,17 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
718 | sym.st_value); | 715 | sym.st_value); |
719 | used_opd = true; | 716 | used_opd = true; |
720 | } | 717 | } |
718 | /* | ||
719 | * When loading symbols in a data mapping, ABS symbols (which | ||
720 | * has a value of SHN_ABS in its st_shndx) failed at | ||
721 | * elf_getscn(). And it marks the loading as a failure so | ||
722 | * already loaded symbols cannot be fixed up. | ||
723 | * | ||
724 | * I'm not sure what should be done. Just ignore them for now. | ||
725 | * - Namhyung Kim | ||
726 | */ | ||
727 | if (sym.st_shndx == SHN_ABS) | ||
728 | continue; | ||
721 | 729 | ||
722 | sec = elf_getscn(runtime_ss->elf, sym.st_shndx); | 730 | sec = elf_getscn(runtime_ss->elf, sym.st_shndx); |
723 | if (!sec) | 731 | if (!sec) |
@@ -798,9 +806,12 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
798 | * DWARF DW_compile_unit has this, but we don't always have access | 806 | * DWARF DW_compile_unit has this, but we don't always have access |
799 | * to it... | 807 | * to it... |
800 | */ | 808 | */ |
801 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 809 | if (symbol_conf.demangle) { |
802 | if (demangled != NULL) | 810 | demangled = bfd_demangle(NULL, elf_name, |
803 | elf_name = demangled; | 811 | DMGL_PARAMS | DMGL_ANSI); |
812 | if (demangled != NULL) | ||
813 | elf_name = demangled; | ||
814 | } | ||
804 | new_symbol: | 815 | new_symbol: |
805 | f = symbol__new(sym.st_value, sym.st_size, | 816 | f = symbol__new(sym.st_value, sym.st_size, |
806 | GELF_ST_BIND(sym.st_info), elf_name); | 817 | GELF_ST_BIND(sym.st_info), elf_name); |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 259f8f2ea9c9..a7390cde63bc 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "symbol.h" | 1 | #include "symbol.h" |
2 | 2 | ||
3 | #include <elf.h> | ||
4 | #include <stdio.h> | 3 | #include <stdio.h> |
5 | #include <fcntl.h> | 4 | #include <fcntl.h> |
6 | #include <string.h> | 5 | #include <string.h> |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 295f8d4feedf..8cf3b5426a9a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -28,14 +28,15 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
28 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
30 | symbol_filter_t filter); | 30 | symbol_filter_t filter); |
31 | static int vmlinux_path__nr_entries; | 31 | int vmlinux_path__nr_entries; |
32 | static char **vmlinux_path; | 32 | char **vmlinux_path; |
33 | 33 | ||
34 | struct symbol_conf symbol_conf = { | 34 | struct symbol_conf symbol_conf = { |
35 | .exclude_other = true, | 35 | .exclude_other = true, |
36 | .use_modules = true, | 36 | .use_modules = true, |
37 | .try_vmlinux_path = true, | 37 | .try_vmlinux_path = true, |
38 | .annotate_src = true, | 38 | .annotate_src = true, |
39 | .demangle = true, | ||
39 | .symfs = "", | 40 | .symfs = "", |
40 | }; | 41 | }; |
41 | 42 | ||
@@ -202,13 +203,6 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | |||
202 | curr->end = ~0ULL; | 203 | curr->end = ~0ULL; |
203 | } | 204 | } |
204 | 205 | ||
205 | static void map_groups__fixup_end(struct map_groups *mg) | ||
206 | { | ||
207 | int i; | ||
208 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
209 | __map_groups__fixup_end(mg, i); | ||
210 | } | ||
211 | |||
212 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | 206 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
213 | { | 207 | { |
214 | size_t namelen = strlen(name) + 1; | 208 | size_t namelen = strlen(name) + 1; |
@@ -652,8 +646,8 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
652 | return count + moved; | 646 | return count + moved; |
653 | } | 647 | } |
654 | 648 | ||
655 | static bool symbol__restricted_filename(const char *filename, | 649 | bool symbol__restricted_filename(const char *filename, |
656 | const char *restricted_filename) | 650 | const char *restricted_filename) |
657 | { | 651 | { |
658 | bool restricted = false; | 652 | bool restricted = false; |
659 | 653 | ||
@@ -775,10 +769,6 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
775 | else | 769 | else |
776 | machine = NULL; | 770 | machine = NULL; |
777 | 771 | ||
778 | name = malloc(PATH_MAX); | ||
779 | if (!name) | ||
780 | return -1; | ||
781 | |||
782 | dso->adjust_symbols = 0; | 772 | dso->adjust_symbols = 0; |
783 | 773 | ||
784 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 774 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
@@ -802,6 +792,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
802 | if (machine) | 792 | if (machine) |
803 | root_dir = machine->root_dir; | 793 | root_dir = machine->root_dir; |
804 | 794 | ||
795 | name = malloc(PATH_MAX); | ||
796 | if (!name) | ||
797 | return -1; | ||
798 | |||
805 | /* Iterate over candidate debug images. | 799 | /* Iterate over candidate debug images. |
806 | * Keep track of "interesting" ones (those which have a symtab, dynsym, | 800 | * Keep track of "interesting" ones (those which have a symtab, dynsym, |
807 | * and/or opd section) for processing. | 801 | * and/or opd section) for processing. |
@@ -887,200 +881,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, | |||
887 | return NULL; | 881 | return NULL; |
888 | } | 882 | } |
889 | 883 | ||
890 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
891 | const char *dir_name) | ||
892 | { | ||
893 | struct dirent *dent; | ||
894 | DIR *dir = opendir(dir_name); | ||
895 | int ret = 0; | ||
896 | |||
897 | if (!dir) { | ||
898 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
899 | return -1; | ||
900 | } | ||
901 | |||
902 | while ((dent = readdir(dir)) != NULL) { | ||
903 | char path[PATH_MAX]; | ||
904 | struct stat st; | ||
905 | |||
906 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
907 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
908 | if (stat(path, &st)) | ||
909 | continue; | ||
910 | |||
911 | if (S_ISDIR(st.st_mode)) { | ||
912 | if (!strcmp(dent->d_name, ".") || | ||
913 | !strcmp(dent->d_name, "..")) | ||
914 | continue; | ||
915 | |||
916 | ret = map_groups__set_modules_path_dir(mg, path); | ||
917 | if (ret < 0) | ||
918 | goto out; | ||
919 | } else { | ||
920 | char *dot = strrchr(dent->d_name, '.'), | ||
921 | dso_name[PATH_MAX]; | ||
922 | struct map *map; | ||
923 | char *long_name; | ||
924 | |||
925 | if (dot == NULL || strcmp(dot, ".ko")) | ||
926 | continue; | ||
927 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
928 | (int)(dot - dent->d_name), dent->d_name); | ||
929 | |||
930 | strxfrchar(dso_name, '-', '_'); | ||
931 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
932 | dso_name); | ||
933 | if (map == NULL) | ||
934 | continue; | ||
935 | |||
936 | long_name = strdup(path); | ||
937 | if (long_name == NULL) { | ||
938 | ret = -1; | ||
939 | goto out; | ||
940 | } | ||
941 | dso__set_long_name(map->dso, long_name); | ||
942 | map->dso->lname_alloc = 1; | ||
943 | dso__kernel_module_get_build_id(map->dso, ""); | ||
944 | } | ||
945 | } | ||
946 | |||
947 | out: | ||
948 | closedir(dir); | ||
949 | return ret; | ||
950 | } | ||
951 | |||
952 | static char *get_kernel_version(const char *root_dir) | ||
953 | { | ||
954 | char version[PATH_MAX]; | ||
955 | FILE *file; | ||
956 | char *name, *tmp; | ||
957 | const char *prefix = "Linux version "; | ||
958 | |||
959 | sprintf(version, "%s/proc/version", root_dir); | ||
960 | file = fopen(version, "r"); | ||
961 | if (!file) | ||
962 | return NULL; | ||
963 | |||
964 | version[0] = '\0'; | ||
965 | tmp = fgets(version, sizeof(version), file); | ||
966 | fclose(file); | ||
967 | |||
968 | name = strstr(version, prefix); | ||
969 | if (!name) | ||
970 | return NULL; | ||
971 | name += strlen(prefix); | ||
972 | tmp = strchr(name, ' '); | ||
973 | if (tmp) | ||
974 | *tmp = '\0'; | ||
975 | |||
976 | return strdup(name); | ||
977 | } | ||
978 | |||
979 | static int machine__set_modules_path(struct machine *machine) | ||
980 | { | ||
981 | char *version; | ||
982 | char modules_path[PATH_MAX]; | ||
983 | |||
984 | version = get_kernel_version(machine->root_dir); | ||
985 | if (!version) | ||
986 | return -1; | ||
987 | |||
988 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
989 | machine->root_dir, version); | ||
990 | free(version); | ||
991 | |||
992 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
993 | } | ||
994 | |||
995 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
996 | const char *filename) | ||
997 | { | ||
998 | struct map *map; | ||
999 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
1000 | |||
1001 | if (dso == NULL) | ||
1002 | return NULL; | ||
1003 | |||
1004 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1005 | if (map == NULL) | ||
1006 | return NULL; | ||
1007 | |||
1008 | if (machine__is_host(machine)) | ||
1009 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
1010 | else | ||
1011 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
1012 | map_groups__insert(&machine->kmaps, map); | ||
1013 | return map; | ||
1014 | } | ||
1015 | |||
1016 | static int machine__create_modules(struct machine *machine) | ||
1017 | { | ||
1018 | char *line = NULL; | ||
1019 | size_t n; | ||
1020 | FILE *file; | ||
1021 | struct map *map; | ||
1022 | const char *modules; | ||
1023 | char path[PATH_MAX]; | ||
1024 | |||
1025 | if (machine__is_default_guest(machine)) | ||
1026 | modules = symbol_conf.default_guest_modules; | ||
1027 | else { | ||
1028 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
1029 | modules = path; | ||
1030 | } | ||
1031 | |||
1032 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
1033 | return -1; | ||
1034 | |||
1035 | file = fopen(modules, "r"); | ||
1036 | if (file == NULL) | ||
1037 | return -1; | ||
1038 | |||
1039 | while (!feof(file)) { | ||
1040 | char name[PATH_MAX]; | ||
1041 | u64 start; | ||
1042 | char *sep; | ||
1043 | int line_len; | ||
1044 | |||
1045 | line_len = getline(&line, &n, file); | ||
1046 | if (line_len < 0) | ||
1047 | break; | ||
1048 | |||
1049 | if (!line) | ||
1050 | goto out_failure; | ||
1051 | |||
1052 | line[--line_len] = '\0'; /* \n */ | ||
1053 | |||
1054 | sep = strrchr(line, 'x'); | ||
1055 | if (sep == NULL) | ||
1056 | continue; | ||
1057 | |||
1058 | hex2u64(sep + 1, &start); | ||
1059 | |||
1060 | sep = strchr(line, ' '); | ||
1061 | if (sep == NULL) | ||
1062 | continue; | ||
1063 | |||
1064 | *sep = '\0'; | ||
1065 | |||
1066 | snprintf(name, sizeof(name), "[%s]", line); | ||
1067 | map = machine__new_module(machine, start, name); | ||
1068 | if (map == NULL) | ||
1069 | goto out_delete_line; | ||
1070 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
1071 | } | ||
1072 | |||
1073 | free(line); | ||
1074 | fclose(file); | ||
1075 | |||
1076 | return machine__set_modules_path(machine); | ||
1077 | |||
1078 | out_delete_line: | ||
1079 | free(line); | ||
1080 | out_failure: | ||
1081 | return -1; | ||
1082 | } | ||
1083 | |||
1084 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 884 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
1085 | const char *vmlinux, symbol_filter_t filter) | 885 | const char *vmlinux, symbol_filter_t filter) |
1086 | { | 886 | { |
@@ -1124,8 +924,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1124 | filename = dso__build_id_filename(dso, NULL, 0); | 924 | filename = dso__build_id_filename(dso, NULL, 0); |
1125 | if (filename != NULL) { | 925 | if (filename != NULL) { |
1126 | err = dso__load_vmlinux(dso, map, filename, filter); | 926 | err = dso__load_vmlinux(dso, map, filename, filter); |
1127 | if (err > 0) | 927 | if (err > 0) { |
928 | dso->lname_alloc = 1; | ||
1128 | goto out; | 929 | goto out; |
930 | } | ||
1129 | free(filename); | 931 | free(filename); |
1130 | } | 932 | } |
1131 | 933 | ||
@@ -1133,6 +935,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1133 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); | 935 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); |
1134 | if (err > 0) { | 936 | if (err > 0) { |
1135 | dso__set_long_name(dso, strdup(vmlinux_path[i])); | 937 | dso__set_long_name(dso, strdup(vmlinux_path[i])); |
938 | dso->lname_alloc = 1; | ||
1136 | break; | 939 | break; |
1137 | } | 940 | } |
1138 | } | 941 | } |
@@ -1172,6 +975,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1172 | if (err > 0) { | 975 | if (err > 0) { |
1173 | dso__set_long_name(dso, | 976 | dso__set_long_name(dso, |
1174 | strdup(symbol_conf.vmlinux_name)); | 977 | strdup(symbol_conf.vmlinux_name)); |
978 | dso->lname_alloc = 1; | ||
1175 | goto out_fixup; | 979 | goto out_fixup; |
1176 | } | 980 | } |
1177 | return err; | 981 | return err; |
@@ -1300,195 +1104,6 @@ out_try_fixup: | |||
1300 | return err; | 1104 | return err; |
1301 | } | 1105 | } |
1302 | 1106 | ||
1303 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | ||
1304 | { | ||
1305 | struct rb_node *nd; | ||
1306 | size_t ret = 0; | ||
1307 | |||
1308 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1309 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1310 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
1311 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
1312 | } | ||
1313 | |||
1314 | return ret; | ||
1315 | } | ||
1316 | |||
1317 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
1318 | bool with_hits) | ||
1319 | { | ||
1320 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, with_hits) + | ||
1321 | __dsos__fprintf_buildid(&machine->user_dsos, fp, with_hits); | ||
1322 | } | ||
1323 | |||
1324 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | ||
1325 | FILE *fp, bool with_hits) | ||
1326 | { | ||
1327 | struct rb_node *nd; | ||
1328 | size_t ret = 0; | ||
1329 | |||
1330 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1331 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1332 | ret += machine__fprintf_dsos_buildid(pos, fp, with_hits); | ||
1333 | } | ||
1334 | return ret; | ||
1335 | } | ||
1336 | |||
1337 | static struct dso *machine__get_kernel(struct machine *machine) | ||
1338 | { | ||
1339 | const char *vmlinux_name = NULL; | ||
1340 | struct dso *kernel; | ||
1341 | |||
1342 | if (machine__is_host(machine)) { | ||
1343 | vmlinux_name = symbol_conf.vmlinux_name; | ||
1344 | if (!vmlinux_name) | ||
1345 | vmlinux_name = "[kernel.kallsyms]"; | ||
1346 | |||
1347 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1348 | "[kernel]", | ||
1349 | DSO_TYPE_KERNEL); | ||
1350 | } else { | ||
1351 | char bf[PATH_MAX]; | ||
1352 | |||
1353 | if (machine__is_default_guest(machine)) | ||
1354 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
1355 | if (!vmlinux_name) | ||
1356 | vmlinux_name = machine__mmap_name(machine, bf, | ||
1357 | sizeof(bf)); | ||
1358 | |||
1359 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1360 | "[guest.kernel]", | ||
1361 | DSO_TYPE_GUEST_KERNEL); | ||
1362 | } | ||
1363 | |||
1364 | if (kernel != NULL && (!kernel->has_build_id)) | ||
1365 | dso__read_running_kernel_build_id(kernel, machine); | ||
1366 | |||
1367 | return kernel; | ||
1368 | } | ||
1369 | |||
1370 | struct process_args { | ||
1371 | u64 start; | ||
1372 | }; | ||
1373 | |||
1374 | static int symbol__in_kernel(void *arg, const char *name, | ||
1375 | char type __maybe_unused, u64 start) | ||
1376 | { | ||
1377 | struct process_args *args = arg; | ||
1378 | |||
1379 | if (strchr(name, '[')) | ||
1380 | return 0; | ||
1381 | |||
1382 | args->start = start; | ||
1383 | return 1; | ||
1384 | } | ||
1385 | |||
1386 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
1387 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
1388 | { | ||
1389 | const char *filename; | ||
1390 | char path[PATH_MAX]; | ||
1391 | struct process_args args; | ||
1392 | |||
1393 | if (machine__is_host(machine)) { | ||
1394 | filename = "/proc/kallsyms"; | ||
1395 | } else { | ||
1396 | if (machine__is_default_guest(machine)) | ||
1397 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
1398 | else { | ||
1399 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
1400 | filename = path; | ||
1401 | } | ||
1402 | } | ||
1403 | |||
1404 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
1405 | return 0; | ||
1406 | |||
1407 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
1408 | return 0; | ||
1409 | |||
1410 | return args.start; | ||
1411 | } | ||
1412 | |||
1413 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
1414 | { | ||
1415 | enum map_type type; | ||
1416 | u64 start = machine__get_kernel_start_addr(machine); | ||
1417 | |||
1418 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1419 | struct kmap *kmap; | ||
1420 | |||
1421 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
1422 | if (machine->vmlinux_maps[type] == NULL) | ||
1423 | return -1; | ||
1424 | |||
1425 | machine->vmlinux_maps[type]->map_ip = | ||
1426 | machine->vmlinux_maps[type]->unmap_ip = | ||
1427 | identity__map_ip; | ||
1428 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1429 | kmap->kmaps = &machine->kmaps; | ||
1430 | map_groups__insert(&machine->kmaps, | ||
1431 | machine->vmlinux_maps[type]); | ||
1432 | } | ||
1433 | |||
1434 | return 0; | ||
1435 | } | ||
1436 | |||
1437 | void machine__destroy_kernel_maps(struct machine *machine) | ||
1438 | { | ||
1439 | enum map_type type; | ||
1440 | |||
1441 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1442 | struct kmap *kmap; | ||
1443 | |||
1444 | if (machine->vmlinux_maps[type] == NULL) | ||
1445 | continue; | ||
1446 | |||
1447 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1448 | map_groups__remove(&machine->kmaps, | ||
1449 | machine->vmlinux_maps[type]); | ||
1450 | if (kmap->ref_reloc_sym) { | ||
1451 | /* | ||
1452 | * ref_reloc_sym is shared among all maps, so free just | ||
1453 | * on one of them. | ||
1454 | */ | ||
1455 | if (type == MAP__FUNCTION) { | ||
1456 | free((char *)kmap->ref_reloc_sym->name); | ||
1457 | kmap->ref_reloc_sym->name = NULL; | ||
1458 | free(kmap->ref_reloc_sym); | ||
1459 | } | ||
1460 | kmap->ref_reloc_sym = NULL; | ||
1461 | } | ||
1462 | |||
1463 | map__delete(machine->vmlinux_maps[type]); | ||
1464 | machine->vmlinux_maps[type] = NULL; | ||
1465 | } | ||
1466 | } | ||
1467 | |||
1468 | int machine__create_kernel_maps(struct machine *machine) | ||
1469 | { | ||
1470 | struct dso *kernel = machine__get_kernel(machine); | ||
1471 | |||
1472 | if (kernel == NULL || | ||
1473 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
1474 | return -1; | ||
1475 | |||
1476 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
1477 | if (machine__is_host(machine)) | ||
1478 | pr_debug("Problems creating module maps, " | ||
1479 | "continuing anyway...\n"); | ||
1480 | else | ||
1481 | pr_debug("Problems creating module maps for guest %d, " | ||
1482 | "continuing anyway...\n", machine->pid); | ||
1483 | } | ||
1484 | |||
1485 | /* | ||
1486 | * Now that we have all the maps created, just set the ->end of them: | ||
1487 | */ | ||
1488 | map_groups__fixup_end(&machine->kmaps); | ||
1489 | return 0; | ||
1490 | } | ||
1491 | |||
1492 | static void vmlinux_path__exit(void) | 1107 | static void vmlinux_path__exit(void) |
1493 | { | 1108 | { |
1494 | while (--vmlinux_path__nr_entries >= 0) { | 1109 | while (--vmlinux_path__nr_entries >= 0) { |
@@ -1549,25 +1164,6 @@ out_fail: | |||
1549 | return -1; | 1164 | return -1; |
1550 | } | 1165 | } |
1551 | 1166 | ||
1552 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
1553 | { | ||
1554 | int i; | ||
1555 | size_t printed = 0; | ||
1556 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
1557 | |||
1558 | if (kdso->has_build_id) { | ||
1559 | char filename[PATH_MAX]; | ||
1560 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
1561 | printed += fprintf(fp, "[0] %s\n", filename); | ||
1562 | } | ||
1563 | |||
1564 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
1565 | printed += fprintf(fp, "[%d] %s\n", | ||
1566 | i + kdso->has_build_id, vmlinux_path[i]); | ||
1567 | |||
1568 | return printed; | ||
1569 | } | ||
1570 | |||
1571 | static int setup_list(struct strlist **list, const char *list_str, | 1167 | static int setup_list(struct strlist **list, const char *list_str, |
1572 | const char *list_name) | 1168 | const char *list_name) |
1573 | { | 1169 | { |
@@ -1671,108 +1267,3 @@ void symbol__exit(void) | |||
1671 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 1267 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
1672 | symbol_conf.initialized = false; | 1268 | symbol_conf.initialized = false; |
1673 | } | 1269 | } |
1674 | |||
1675 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | ||
1676 | { | ||
1677 | struct machine *machine = machines__findnew(machines, pid); | ||
1678 | |||
1679 | if (machine == NULL) | ||
1680 | return -1; | ||
1681 | |||
1682 | return machine__create_kernel_maps(machine); | ||
1683 | } | ||
1684 | |||
1685 | int machines__create_guest_kernel_maps(struct rb_root *machines) | ||
1686 | { | ||
1687 | int ret = 0; | ||
1688 | struct dirent **namelist = NULL; | ||
1689 | int i, items = 0; | ||
1690 | char path[PATH_MAX]; | ||
1691 | pid_t pid; | ||
1692 | char *endp; | ||
1693 | |||
1694 | if (symbol_conf.default_guest_vmlinux_name || | ||
1695 | symbol_conf.default_guest_modules || | ||
1696 | symbol_conf.default_guest_kallsyms) { | ||
1697 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
1698 | } | ||
1699 | |||
1700 | if (symbol_conf.guestmount) { | ||
1701 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
1702 | if (items <= 0) | ||
1703 | return -ENOENT; | ||
1704 | for (i = 0; i < items; i++) { | ||
1705 | if (!isdigit(namelist[i]->d_name[0])) { | ||
1706 | /* Filter out . and .. */ | ||
1707 | continue; | ||
1708 | } | ||
1709 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
1710 | if ((*endp != '\0') || | ||
1711 | (endp == namelist[i]->d_name) || | ||
1712 | (errno == ERANGE)) { | ||
1713 | pr_debug("invalid directory (%s). Skipping.\n", | ||
1714 | namelist[i]->d_name); | ||
1715 | continue; | ||
1716 | } | ||
1717 | sprintf(path, "%s/%s/proc/kallsyms", | ||
1718 | symbol_conf.guestmount, | ||
1719 | namelist[i]->d_name); | ||
1720 | ret = access(path, R_OK); | ||
1721 | if (ret) { | ||
1722 | pr_debug("Can't access file %s\n", path); | ||
1723 | goto failure; | ||
1724 | } | ||
1725 | machines__create_kernel_maps(machines, pid); | ||
1726 | } | ||
1727 | failure: | ||
1728 | free(namelist); | ||
1729 | } | ||
1730 | |||
1731 | return ret; | ||
1732 | } | ||
1733 | |||
1734 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) | ||
1735 | { | ||
1736 | struct rb_node *next = rb_first(machines); | ||
1737 | |||
1738 | while (next) { | ||
1739 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
1740 | |||
1741 | next = rb_next(&pos->rb_node); | ||
1742 | rb_erase(&pos->rb_node, machines); | ||
1743 | machine__delete(pos); | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1747 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
1748 | enum map_type type, symbol_filter_t filter) | ||
1749 | { | ||
1750 | struct map *map = machine->vmlinux_maps[type]; | ||
1751 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
1752 | |||
1753 | if (ret > 0) { | ||
1754 | dso__set_loaded(map->dso, type); | ||
1755 | /* | ||
1756 | * Since /proc/kallsyms will have multiple sessions for the | ||
1757 | * kernel, with modules between them, fixup the end of all | ||
1758 | * sections. | ||
1759 | */ | ||
1760 | __map_groups__fixup_end(&machine->kmaps, type); | ||
1761 | } | ||
1762 | |||
1763 | return ret; | ||
1764 | } | ||
1765 | |||
1766 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
1767 | symbol_filter_t filter) | ||
1768 | { | ||
1769 | struct map *map = machine->vmlinux_maps[type]; | ||
1770 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
1771 | |||
1772 | if (ret > 0) { | ||
1773 | dso__set_loaded(map->dso, type); | ||
1774 | map__reloc_vmlinux(map); | ||
1775 | } | ||
1776 | |||
1777 | return ret; | ||
1778 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index de68f98b236d..5f720dc076da 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -16,8 +16,8 @@ | |||
16 | #ifdef LIBELF_SUPPORT | 16 | #ifdef LIBELF_SUPPORT |
17 | #include <libelf.h> | 17 | #include <libelf.h> |
18 | #include <gelf.h> | 18 | #include <gelf.h> |
19 | #include <elf.h> | ||
20 | #endif | 19 | #endif |
20 | #include <elf.h> | ||
21 | 21 | ||
22 | #include "dso.h" | 22 | #include "dso.h" |
23 | 23 | ||
@@ -96,7 +96,9 @@ struct symbol_conf { | |||
96 | initialized, | 96 | initialized, |
97 | kptr_restrict, | 97 | kptr_restrict, |
98 | annotate_asm_raw, | 98 | annotate_asm_raw, |
99 | annotate_src; | 99 | annotate_src, |
100 | event_group, | ||
101 | demangle; | ||
100 | const char *vmlinux_name, | 102 | const char *vmlinux_name, |
101 | *kallsyms_name, | 103 | *kallsyms_name, |
102 | *source_prefix, | 104 | *source_prefix, |
@@ -120,6 +122,8 @@ struct symbol_conf { | |||
120 | }; | 122 | }; |
121 | 123 | ||
122 | extern struct symbol_conf symbol_conf; | 124 | extern struct symbol_conf symbol_conf; |
125 | extern int vmlinux_path__nr_entries; | ||
126 | extern char **vmlinux_path; | ||
123 | 127 | ||
124 | static inline void *symbol__priv(struct symbol *sym) | 128 | static inline void *symbol__priv(struct symbol *sym) |
125 | { | 129 | { |
@@ -152,6 +156,12 @@ struct branch_info { | |||
152 | struct branch_flags flags; | 156 | struct branch_flags flags; |
153 | }; | 157 | }; |
154 | 158 | ||
159 | struct mem_info { | ||
160 | struct addr_map_symbol iaddr; | ||
161 | struct addr_map_symbol daddr; | ||
162 | union perf_mem_data_src data_src; | ||
163 | }; | ||
164 | |||
155 | struct addr_location { | 165 | struct addr_location { |
156 | struct thread *thread; | 166 | struct thread *thread; |
157 | struct map *map; | 167 | struct map *map; |
@@ -223,6 +233,8 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym, | |||
223 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 233 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
224 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); | 234 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); |
225 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 235 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
236 | bool symbol__restricted_filename(const char *filename, | ||
237 | const char *restricted_filename); | ||
226 | 238 | ||
227 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | 239 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, |
228 | struct symsrc *runtime_ss, symbol_filter_t filter, | 240 | struct symsrc *runtime_ss, symbol_filter_t filter, |
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c index 48c6902e749f..f71e9eafe15a 100644 --- a/tools/perf/util/sysfs.c +++ b/tools/perf/util/sysfs.c | |||
@@ -8,7 +8,7 @@ static const char * const sysfs_known_mountpoints[] = { | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | static int sysfs_found; | 10 | static int sysfs_found; |
11 | char sysfs_mountpoint[PATH_MAX]; | 11 | char sysfs_mountpoint[PATH_MAX + 1]; |
12 | 12 | ||
13 | static int sysfs_valid_mountpoint(const char *sysfs) | 13 | static int sysfs_valid_mountpoint(const char *sysfs) |
14 | { | 14 | { |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index df59623ac763..40399cbcca77 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -14,6 +14,7 @@ struct thread *thread__new(pid_t pid) | |||
14 | if (self != NULL) { | 14 | if (self != NULL) { |
15 | map_groups__init(&self->mg); | 15 | map_groups__init(&self->mg); |
16 | self->pid = pid; | 16 | self->pid = pid; |
17 | self->ppid = -1; | ||
17 | self->comm = malloc(32); | 18 | self->comm = malloc(32); |
18 | if (self->comm) | 19 | if (self->comm) |
19 | snprintf(self->comm, 32, ":%d", self->pid); | 20 | snprintf(self->comm, 32, ":%d", self->pid); |
@@ -54,10 +55,10 @@ int thread__comm_len(struct thread *self) | |||
54 | return self->comm_len; | 55 | return self->comm_len; |
55 | } | 56 | } |
56 | 57 | ||
57 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 58 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
58 | { | 59 | { |
59 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | 60 | return fprintf(fp, "Thread %d %s\n", thread->pid, thread->comm) + |
60 | map_groups__fprintf(&self->mg, verbose, fp); | 61 | map_groups__fprintf(&thread->mg, verbose, fp); |
61 | } | 62 | } |
62 | 63 | ||
63 | void thread__insert_map(struct thread *self, struct map *map) | 64 | void thread__insert_map(struct thread *self, struct map *map) |
@@ -82,19 +83,8 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
82 | for (i = 0; i < MAP__NR_TYPES; ++i) | 83 | for (i = 0; i < MAP__NR_TYPES; ++i) |
83 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | 84 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) |
84 | return -ENOMEM; | 85 | return -ENOMEM; |
85 | return 0; | ||
86 | } | ||
87 | |||
88 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
89 | { | ||
90 | size_t ret = 0; | ||
91 | struct rb_node *nd; | ||
92 | 86 | ||
93 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | 87 | self->ppid = parent->pid; |
94 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
95 | 88 | ||
96 | ret += thread__fprintf(pos, fp); | 89 | return 0; |
97 | } | ||
98 | |||
99 | return ret; | ||
100 | } | 90 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index f2fa17caa7d5..eeb7ac62b9e3 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -13,6 +13,7 @@ struct thread { | |||
13 | }; | 13 | }; |
14 | struct map_groups mg; | 14 | struct map_groups mg; |
15 | pid_t pid; | 15 | pid_t pid; |
16 | pid_t ppid; | ||
16 | char shortname[3]; | 17 | char shortname[3]; |
17 | bool comm_set; | 18 | bool comm_set; |
18 | char *comm; | 19 | char *comm; |
@@ -30,6 +31,7 @@ int thread__set_comm(struct thread *self, const char *comm); | |||
30 | int thread__comm_len(struct thread *self); | 31 | int thread__comm_len(struct thread *self); |
31 | void thread__insert_map(struct thread *self, struct map *map); | 32 | void thread__insert_map(struct thread *self, struct map *map); |
32 | int thread__fork(struct thread *self, struct thread *parent); | 33 | int thread__fork(struct thread *self, struct thread *parent); |
34 | size_t thread__fprintf(struct thread *thread, FILE *fp); | ||
33 | 35 | ||
34 | static inline struct map *thread__find_map(struct thread *self, | 36 | static inline struct map *thread__find_map(struct thread *self, |
35 | enum map_type type, u64 addr) | 37 | enum map_type type, u64 addr) |
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index f718df8a3c59..0cd8b3108084 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -21,4 +21,9 @@ void thread_map__delete(struct thread_map *threads); | |||
21 | 21 | ||
22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); | 22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); |
23 | 23 | ||
24 | static inline int thread_map__nr(struct thread_map *threads) | ||
25 | { | ||
26 | return threads ? threads->nr : 1; | ||
27 | } | ||
28 | |||
24 | #endif /* __PERF_THREAD_MAP_H */ | 29 | #endif /* __PERF_THREAD_MAP_H */ |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 884dde9b9bc1..f857b51b6bde 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -23,18 +23,31 @@ | |||
23 | 23 | ||
24 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | 24 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) |
25 | { | 25 | { |
26 | float samples_per_sec = top->samples / top->delay_secs; | 26 | float samples_per_sec; |
27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | 27 | float ksamples_per_sec; |
28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | 28 | float esamples_percent; |
29 | struct perf_record_opts *opts = &top->record_opts; | ||
30 | struct perf_target *target = &opts->target; | ||
29 | size_t ret = 0; | 31 | size_t ret = 0; |
30 | 32 | ||
33 | if (top->samples) { | ||
34 | samples_per_sec = top->samples / top->delay_secs; | ||
35 | ksamples_per_sec = top->kernel_samples / top->delay_secs; | ||
36 | esamples_percent = (100.0 * top->exact_samples) / top->samples; | ||
37 | } else { | ||
38 | samples_per_sec = ksamples_per_sec = esamples_percent = 0.0; | ||
39 | } | ||
40 | |||
31 | if (!perf_guest) { | 41 | if (!perf_guest) { |
42 | float ksamples_percent = 0.0; | ||
43 | |||
44 | if (samples_per_sec) | ||
45 | ksamples_percent = (100.0 * ksamples_per_sec) / | ||
46 | samples_per_sec; | ||
32 | ret = SNPRINTF(bf, size, | 47 | ret = SNPRINTF(bf, size, |
33 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | 48 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%%" |
34 | " exact: %4.1f%% [", samples_per_sec, | 49 | " exact: %4.1f%% [", samples_per_sec, |
35 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | 50 | ksamples_percent, esamples_percent); |
36 | samples_per_sec)), | ||
37 | esamples_percent); | ||
38 | } else { | 51 | } else { |
39 | float us_samples_per_sec = top->us_samples / top->delay_secs; | 52 | float us_samples_per_sec = top->us_samples / top->delay_secs; |
40 | float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; | 53 | float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; |
@@ -61,31 +74,31 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
61 | struct perf_evsel *first = perf_evlist__first(top->evlist); | 74 | struct perf_evsel *first = perf_evlist__first(top->evlist); |
62 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 75 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
63 | (uint64_t)first->attr.sample_period, | 76 | (uint64_t)first->attr.sample_period, |
64 | top->freq ? "Hz" : ""); | 77 | opts->freq ? "Hz" : ""); |
65 | } | 78 | } |
66 | 79 | ||
67 | ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel)); | 80 | ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel)); |
68 | 81 | ||
69 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 82 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
70 | 83 | ||
71 | if (top->target.pid) | 84 | if (target->pid) |
72 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", | 85 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", |
73 | top->target.pid); | 86 | target->pid); |
74 | else if (top->target.tid) | 87 | else if (target->tid) |
75 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", | 88 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", |
76 | top->target.tid); | 89 | target->tid); |
77 | else if (top->target.uid_str != NULL) | 90 | else if (target->uid_str != NULL) |
78 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", | 91 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", |
79 | top->target.uid_str); | 92 | target->uid_str); |
80 | else | 93 | else |
81 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | 94 | ret += SNPRINTF(bf + ret, size - ret, " (all"); |
82 | 95 | ||
83 | if (top->target.cpu_list) | 96 | if (target->cpu_list) |
84 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | 97 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", |
85 | top->evlist->cpus->nr > 1 ? "s" : "", | 98 | top->evlist->cpus->nr > 1 ? "s" : "", |
86 | top->target.cpu_list); | 99 | target->cpu_list); |
87 | else { | 100 | else { |
88 | if (top->target.tid) | 101 | if (target->tid) |
89 | ret += SNPRINTF(bf + ret, size - ret, ")"); | 102 | ret += SNPRINTF(bf + ret, size - ret, ")"); |
90 | else | 103 | else |
91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | 104 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 86ff1b15059b..df46be93d902 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -14,7 +14,7 @@ struct perf_session; | |||
14 | struct perf_top { | 14 | struct perf_top { |
15 | struct perf_tool tool; | 15 | struct perf_tool tool; |
16 | struct perf_evlist *evlist; | 16 | struct perf_evlist *evlist; |
17 | struct perf_target target; | 17 | struct perf_record_opts record_opts; |
18 | /* | 18 | /* |
19 | * Symbols will be added here in perf_event__process_sample and will | 19 | * Symbols will be added here in perf_event__process_sample and will |
20 | * get out after decayed. | 20 | * get out after decayed. |
@@ -24,27 +24,19 @@ struct perf_top { | |||
24 | u64 exact_samples; | 24 | u64 exact_samples; |
25 | u64 guest_us_samples, guest_kernel_samples; | 25 | u64 guest_us_samples, guest_kernel_samples; |
26 | int print_entries, count_filter, delay_secs; | 26 | int print_entries, count_filter, delay_secs; |
27 | int freq; | ||
28 | bool hide_kernel_symbols, hide_user_symbols, zero; | 27 | bool hide_kernel_symbols, hide_user_symbols, zero; |
29 | bool use_tui, use_stdio; | 28 | bool use_tui, use_stdio; |
30 | bool sort_has_symbols; | ||
31 | bool dont_use_callchains; | ||
32 | bool kptr_restrict_warned; | 29 | bool kptr_restrict_warned; |
33 | bool vmlinux_warned; | 30 | bool vmlinux_warned; |
34 | bool inherit; | ||
35 | bool group; | ||
36 | bool sample_id_all_missing; | ||
37 | bool exclude_guest_missing; | ||
38 | bool dump_symtab; | 31 | bool dump_symtab; |
39 | struct hist_entry *sym_filter_entry; | 32 | struct hist_entry *sym_filter_entry; |
40 | struct perf_evsel *sym_evsel; | 33 | struct perf_evsel *sym_evsel; |
41 | struct perf_session *session; | 34 | struct perf_session *session; |
42 | struct winsize winsize; | 35 | struct winsize winsize; |
43 | unsigned int mmap_pages; | ||
44 | int default_interval; | ||
45 | int realtime_prio; | 36 | int realtime_prio; |
46 | int sym_pcnt_filter; | 37 | int sym_pcnt_filter; |
47 | const char *sym_filter; | 38 | const char *sym_filter; |
39 | float min_percent; | ||
48 | }; | 40 | }; |
49 | 41 | ||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 42 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index a8d81c35ef66..3917eb9a8479 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -38,52 +38,20 @@ | |||
38 | 38 | ||
39 | #include "../perf.h" | 39 | #include "../perf.h" |
40 | #include "trace-event.h" | 40 | #include "trace-event.h" |
41 | #include "debugfs.h" | 41 | #include <lk/debugfs.h> |
42 | #include "evsel.h" | 42 | #include "evsel.h" |
43 | 43 | ||
44 | #define VERSION "0.5" | 44 | #define VERSION "0.5" |
45 | 45 | ||
46 | #define TRACE_CTRL "tracing_on" | ||
47 | #define TRACE "trace" | ||
48 | #define AVAILABLE "available_tracers" | ||
49 | #define CURRENT "current_tracer" | ||
50 | #define ITER_CTRL "trace_options" | ||
51 | #define MAX_LATENCY "tracing_max_latency" | ||
52 | |||
53 | unsigned int page_size; | ||
54 | |||
55 | static const char *output_file = "trace.info"; | ||
56 | static int output_fd; | 46 | static int output_fd; |
57 | 47 | ||
58 | struct event_list { | ||
59 | struct event_list *next; | ||
60 | const char *event; | ||
61 | }; | ||
62 | |||
63 | struct events { | ||
64 | struct events *sibling; | ||
65 | struct events *children; | ||
66 | struct events *next; | ||
67 | char *name; | ||
68 | }; | ||
69 | |||
70 | |||
71 | static void *malloc_or_die(unsigned int size) | ||
72 | { | ||
73 | void *data; | ||
74 | |||
75 | data = malloc(size); | ||
76 | if (!data) | ||
77 | die("malloc"); | ||
78 | return data; | ||
79 | } | ||
80 | 48 | ||
81 | static const char *find_debugfs(void) | 49 | static const char *find_debugfs(void) |
82 | { | 50 | { |
83 | const char *path = debugfs_mount(NULL); | 51 | const char *path = perf_debugfs_mount(NULL); |
84 | 52 | ||
85 | if (!path) | 53 | if (!path) |
86 | die("Your kernel not support debugfs filesystem"); | 54 | pr_debug("Your kernel does not support the debugfs filesystem"); |
87 | 55 | ||
88 | return path; | 56 | return path; |
89 | } | 57 | } |
@@ -102,8 +70,12 @@ static const char *find_tracing_dir(void) | |||
102 | return tracing; | 70 | return tracing; |
103 | 71 | ||
104 | debugfs = find_debugfs(); | 72 | debugfs = find_debugfs(); |
73 | if (!debugfs) | ||
74 | return NULL; | ||
105 | 75 | ||
106 | tracing = malloc_or_die(strlen(debugfs) + 9); | 76 | tracing = malloc(strlen(debugfs) + 9); |
77 | if (!tracing) | ||
78 | return NULL; | ||
107 | 79 | ||
108 | sprintf(tracing, "%s/tracing", debugfs); | 80 | sprintf(tracing, "%s/tracing", debugfs); |
109 | 81 | ||
@@ -120,7 +92,9 @@ static char *get_tracing_file(const char *name) | |||
120 | if (!tracing) | 92 | if (!tracing) |
121 | return NULL; | 93 | return NULL; |
122 | 94 | ||
123 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); | 95 | file = malloc(strlen(tracing) + strlen(name) + 2); |
96 | if (!file) | ||
97 | return NULL; | ||
124 | 98 | ||
125 | sprintf(file, "%s/%s", tracing, name); | 99 | sprintf(file, "%s/%s", tracing, name); |
126 | return file; | 100 | return file; |
@@ -131,24 +105,6 @@ static void put_tracing_file(char *file) | |||
131 | free(file); | 105 | free(file); |
132 | } | 106 | } |
133 | 107 | ||
134 | static ssize_t calc_data_size; | ||
135 | |||
136 | static ssize_t write_or_die(const void *buf, size_t len) | ||
137 | { | ||
138 | int ret; | ||
139 | |||
140 | if (calc_data_size) { | ||
141 | calc_data_size += len; | ||
142 | return len; | ||
143 | } | ||
144 | |||
145 | ret = write(output_fd, buf, len); | ||
146 | if (ret < 0) | ||
147 | die("writing to '%s'", output_file); | ||
148 | |||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | int bigendian(void) | 108 | int bigendian(void) |
153 | { | 109 | { |
154 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; | 110 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; |
@@ -159,59 +115,106 @@ int bigendian(void) | |||
159 | } | 115 | } |
160 | 116 | ||
161 | /* unfortunately, you can not stat debugfs or proc files for size */ | 117 | /* unfortunately, you can not stat debugfs or proc files for size */ |
162 | static void record_file(const char *file, size_t hdr_sz) | 118 | static int record_file(const char *file, ssize_t hdr_sz) |
163 | { | 119 | { |
164 | unsigned long long size = 0; | 120 | unsigned long long size = 0; |
165 | char buf[BUFSIZ], *sizep; | 121 | char buf[BUFSIZ], *sizep; |
166 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); | 122 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
167 | int r, fd; | 123 | int r, fd; |
124 | int err = -EIO; | ||
168 | 125 | ||
169 | fd = open(file, O_RDONLY); | 126 | fd = open(file, O_RDONLY); |
170 | if (fd < 0) | 127 | if (fd < 0) { |
171 | die("Can't read '%s'", file); | 128 | pr_debug("Can't read '%s'", file); |
129 | return -errno; | ||
130 | } | ||
172 | 131 | ||
173 | /* put in zeros for file size, then fill true size later */ | 132 | /* put in zeros for file size, then fill true size later */ |
174 | if (hdr_sz) | 133 | if (hdr_sz) { |
175 | write_or_die(&size, hdr_sz); | 134 | if (write(output_fd, &size, hdr_sz) != hdr_sz) |
135 | goto out; | ||
136 | } | ||
176 | 137 | ||
177 | do { | 138 | do { |
178 | r = read(fd, buf, BUFSIZ); | 139 | r = read(fd, buf, BUFSIZ); |
179 | if (r > 0) { | 140 | if (r > 0) { |
180 | size += r; | 141 | size += r; |
181 | write_or_die(buf, r); | 142 | if (write(output_fd, buf, r) != r) |
143 | goto out; | ||
182 | } | 144 | } |
183 | } while (r > 0); | 145 | } while (r > 0); |
184 | close(fd); | ||
185 | 146 | ||
186 | /* ugh, handle big-endian hdr_size == 4 */ | 147 | /* ugh, handle big-endian hdr_size == 4 */ |
187 | sizep = (char*)&size; | 148 | sizep = (char*)&size; |
188 | if (bigendian()) | 149 | if (bigendian()) |
189 | sizep += sizeof(u64) - hdr_sz; | 150 | sizep += sizeof(u64) - hdr_sz; |
190 | 151 | ||
191 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | 152 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) { |
192 | die("writing to %s", output_file); | 153 | pr_debug("writing file size failed\n"); |
154 | goto out; | ||
155 | } | ||
156 | |||
157 | err = 0; | ||
158 | out: | ||
159 | close(fd); | ||
160 | return err; | ||
193 | } | 161 | } |
194 | 162 | ||
195 | static void read_header_files(void) | 163 | static int read_header_files(void) |
196 | { | 164 | { |
197 | char *path; | 165 | char *path; |
198 | struct stat st; | 166 | struct stat st; |
167 | int err = -EIO; | ||
199 | 168 | ||
200 | path = get_tracing_file("events/header_page"); | 169 | path = get_tracing_file("events/header_page"); |
201 | if (stat(path, &st) < 0) | 170 | if (!path) { |
202 | die("can't read '%s'", path); | 171 | pr_debug("can't get tracing/events/header_page"); |
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | if (stat(path, &st) < 0) { | ||
176 | pr_debug("can't read '%s'", path); | ||
177 | goto out; | ||
178 | } | ||
179 | |||
180 | if (write(output_fd, "header_page", 12) != 12) { | ||
181 | pr_debug("can't write header_page\n"); | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | if (record_file(path, 8) < 0) { | ||
186 | pr_debug("can't record header_page file\n"); | ||
187 | goto out; | ||
188 | } | ||
203 | 189 | ||
204 | write_or_die("header_page", 12); | ||
205 | record_file(path, 8); | ||
206 | put_tracing_file(path); | 190 | put_tracing_file(path); |
207 | 191 | ||
208 | path = get_tracing_file("events/header_event"); | 192 | path = get_tracing_file("events/header_event"); |
209 | if (stat(path, &st) < 0) | 193 | if (!path) { |
210 | die("can't read '%s'", path); | 194 | pr_debug("can't get tracing/events/header_event"); |
195 | err = -ENOMEM; | ||
196 | goto out; | ||
197 | } | ||
198 | |||
199 | if (stat(path, &st) < 0) { | ||
200 | pr_debug("can't read '%s'", path); | ||
201 | goto out; | ||
202 | } | ||
211 | 203 | ||
212 | write_or_die("header_event", 13); | 204 | if (write(output_fd, "header_event", 13) != 13) { |
213 | record_file(path, 8); | 205 | pr_debug("can't write header_event\n"); |
206 | goto out; | ||
207 | } | ||
208 | |||
209 | if (record_file(path, 8) < 0) { | ||
210 | pr_debug("can't record header_event file\n"); | ||
211 | goto out; | ||
212 | } | ||
213 | |||
214 | err = 0; | ||
215 | out: | ||
214 | put_tracing_file(path); | 216 | put_tracing_file(path); |
217 | return err; | ||
215 | } | 218 | } |
216 | 219 | ||
217 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 220 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -225,7 +228,7 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
225 | return false; | 228 | return false; |
226 | } | 229 | } |
227 | 230 | ||
228 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) | 231 | static int copy_event_system(const char *sys, struct tracepoint_path *tps) |
229 | { | 232 | { |
230 | struct dirent *dent; | 233 | struct dirent *dent; |
231 | struct stat st; | 234 | struct stat st; |
@@ -233,10 +236,13 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
233 | DIR *dir; | 236 | DIR *dir; |
234 | int count = 0; | 237 | int count = 0; |
235 | int ret; | 238 | int ret; |
239 | int err; | ||
236 | 240 | ||
237 | dir = opendir(sys); | 241 | dir = opendir(sys); |
238 | if (!dir) | 242 | if (!dir) { |
239 | die("can't read directory '%s'", sys); | 243 | pr_debug("can't read directory '%s'", sys); |
244 | return -errno; | ||
245 | } | ||
240 | 246 | ||
241 | while ((dent = readdir(dir))) { | 247 | while ((dent = readdir(dir))) { |
242 | if (dent->d_type != DT_DIR || | 248 | if (dent->d_type != DT_DIR || |
@@ -244,7 +250,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
244 | strcmp(dent->d_name, "..") == 0 || | 250 | strcmp(dent->d_name, "..") == 0 || |
245 | !name_in_tp_list(dent->d_name, tps)) | 251 | !name_in_tp_list(dent->d_name, tps)) |
246 | continue; | 252 | continue; |
247 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | 253 | format = malloc(strlen(sys) + strlen(dent->d_name) + 10); |
254 | if (!format) { | ||
255 | err = -ENOMEM; | ||
256 | goto out; | ||
257 | } | ||
248 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 258 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
249 | ret = stat(format, &st); | 259 | ret = stat(format, &st); |
250 | free(format); | 260 | free(format); |
@@ -253,7 +263,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
253 | count++; | 263 | count++; |
254 | } | 264 | } |
255 | 265 | ||
256 | write_or_die(&count, 4); | 266 | if (write(output_fd, &count, 4) != 4) { |
267 | err = -EIO; | ||
268 | pr_debug("can't write count\n"); | ||
269 | goto out; | ||
270 | } | ||
257 | 271 | ||
258 | rewinddir(dir); | 272 | rewinddir(dir); |
259 | while ((dent = readdir(dir))) { | 273 | while ((dent = readdir(dir))) { |
@@ -262,27 +276,45 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
262 | strcmp(dent->d_name, "..") == 0 || | 276 | strcmp(dent->d_name, "..") == 0 || |
263 | !name_in_tp_list(dent->d_name, tps)) | 277 | !name_in_tp_list(dent->d_name, tps)) |
264 | continue; | 278 | continue; |
265 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | 279 | format = malloc(strlen(sys) + strlen(dent->d_name) + 10); |
280 | if (!format) { | ||
281 | err = -ENOMEM; | ||
282 | goto out; | ||
283 | } | ||
266 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 284 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
267 | ret = stat(format, &st); | 285 | ret = stat(format, &st); |
268 | 286 | ||
269 | if (ret >= 0) | 287 | if (ret >= 0) { |
270 | record_file(format, 8); | 288 | err = record_file(format, 8); |
271 | 289 | if (err) { | |
290 | free(format); | ||
291 | goto out; | ||
292 | } | ||
293 | } | ||
272 | free(format); | 294 | free(format); |
273 | } | 295 | } |
296 | err = 0; | ||
297 | out: | ||
274 | closedir(dir); | 298 | closedir(dir); |
299 | return err; | ||
275 | } | 300 | } |
276 | 301 | ||
277 | static void read_ftrace_files(struct tracepoint_path *tps) | 302 | static int read_ftrace_files(struct tracepoint_path *tps) |
278 | { | 303 | { |
279 | char *path; | 304 | char *path; |
305 | int ret; | ||
280 | 306 | ||
281 | path = get_tracing_file("events/ftrace"); | 307 | path = get_tracing_file("events/ftrace"); |
308 | if (!path) { | ||
309 | pr_debug("can't get tracing/events/ftrace"); | ||
310 | return -ENOMEM; | ||
311 | } | ||
282 | 312 | ||
283 | copy_event_system(path, tps); | 313 | ret = copy_event_system(path, tps); |
284 | 314 | ||
285 | put_tracing_file(path); | 315 | put_tracing_file(path); |
316 | |||
317 | return ret; | ||
286 | } | 318 | } |
287 | 319 | ||
288 | static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) | 320 | static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -296,7 +328,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
296 | return false; | 328 | return false; |
297 | } | 329 | } |
298 | 330 | ||
299 | static void read_event_files(struct tracepoint_path *tps) | 331 | static int read_event_files(struct tracepoint_path *tps) |
300 | { | 332 | { |
301 | struct dirent *dent; | 333 | struct dirent *dent; |
302 | struct stat st; | 334 | struct stat st; |
@@ -305,12 +337,20 @@ static void read_event_files(struct tracepoint_path *tps) | |||
305 | DIR *dir; | 337 | DIR *dir; |
306 | int count = 0; | 338 | int count = 0; |
307 | int ret; | 339 | int ret; |
340 | int err; | ||
308 | 341 | ||
309 | path = get_tracing_file("events"); | 342 | path = get_tracing_file("events"); |
343 | if (!path) { | ||
344 | pr_debug("can't get tracing/events"); | ||
345 | return -ENOMEM; | ||
346 | } | ||
310 | 347 | ||
311 | dir = opendir(path); | 348 | dir = opendir(path); |
312 | if (!dir) | 349 | if (!dir) { |
313 | die("can't read directory '%s'", path); | 350 | err = -errno; |
351 | pr_debug("can't read directory '%s'", path); | ||
352 | goto out; | ||
353 | } | ||
314 | 354 | ||
315 | while ((dent = readdir(dir))) { | 355 | while ((dent = readdir(dir))) { |
316 | if (dent->d_type != DT_DIR || | 356 | if (dent->d_type != DT_DIR || |
@@ -322,7 +362,11 @@ static void read_event_files(struct tracepoint_path *tps) | |||
322 | count++; | 362 | count++; |
323 | } | 363 | } |
324 | 364 | ||
325 | write_or_die(&count, 4); | 365 | if (write(output_fd, &count, 4) != 4) { |
366 | err = -EIO; | ||
367 | pr_debug("can't write count\n"); | ||
368 | goto out; | ||
369 | } | ||
326 | 370 | ||
327 | rewinddir(dir); | 371 | rewinddir(dir); |
328 | while ((dent = readdir(dir))) { | 372 | while ((dent = readdir(dir))) { |
@@ -332,56 +376,90 @@ static void read_event_files(struct tracepoint_path *tps) | |||
332 | strcmp(dent->d_name, "ftrace") == 0 || | 376 | strcmp(dent->d_name, "ftrace") == 0 || |
333 | !system_in_tp_list(dent->d_name, tps)) | 377 | !system_in_tp_list(dent->d_name, tps)) |
334 | continue; | 378 | continue; |
335 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 379 | sys = malloc(strlen(path) + strlen(dent->d_name) + 2); |
380 | if (!sys) { | ||
381 | err = -ENOMEM; | ||
382 | goto out; | ||
383 | } | ||
336 | sprintf(sys, "%s/%s", path, dent->d_name); | 384 | sprintf(sys, "%s/%s", path, dent->d_name); |
337 | ret = stat(sys, &st); | 385 | ret = stat(sys, &st); |
338 | if (ret >= 0) { | 386 | if (ret >= 0) { |
339 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 387 | ssize_t size = strlen(dent->d_name) + 1; |
340 | copy_event_system(sys, tps); | 388 | |
389 | if (write(output_fd, dent->d_name, size) != size || | ||
390 | copy_event_system(sys, tps) < 0) { | ||
391 | err = -EIO; | ||
392 | free(sys); | ||
393 | goto out; | ||
394 | } | ||
341 | } | 395 | } |
342 | free(sys); | 396 | free(sys); |
343 | } | 397 | } |
344 | 398 | err = 0; | |
399 | out: | ||
345 | closedir(dir); | 400 | closedir(dir); |
346 | put_tracing_file(path); | 401 | put_tracing_file(path); |
402 | |||
403 | return err; | ||
347 | } | 404 | } |
348 | 405 | ||
349 | static void read_proc_kallsyms(void) | 406 | static int read_proc_kallsyms(void) |
350 | { | 407 | { |
351 | unsigned int size; | 408 | unsigned int size; |
352 | const char *path = "/proc/kallsyms"; | 409 | const char *path = "/proc/kallsyms"; |
353 | struct stat st; | 410 | struct stat st; |
354 | int ret; | 411 | int ret, err = 0; |
355 | 412 | ||
356 | ret = stat(path, &st); | 413 | ret = stat(path, &st); |
357 | if (ret < 0) { | 414 | if (ret < 0) { |
358 | /* not found */ | 415 | /* not found */ |
359 | size = 0; | 416 | size = 0; |
360 | write_or_die(&size, 4); | 417 | if (write(output_fd, &size, 4) != 4) |
361 | return; | 418 | err = -EIO; |
419 | return err; | ||
362 | } | 420 | } |
363 | record_file(path, 4); | 421 | return record_file(path, 4); |
364 | } | 422 | } |
365 | 423 | ||
366 | static void read_ftrace_printk(void) | 424 | static int read_ftrace_printk(void) |
367 | { | 425 | { |
368 | unsigned int size; | 426 | unsigned int size; |
369 | char *path; | 427 | char *path; |
370 | struct stat st; | 428 | struct stat st; |
371 | int ret; | 429 | int ret, err = 0; |
372 | 430 | ||
373 | path = get_tracing_file("printk_formats"); | 431 | path = get_tracing_file("printk_formats"); |
432 | if (!path) { | ||
433 | pr_debug("can't get tracing/printk_formats"); | ||
434 | return -ENOMEM; | ||
435 | } | ||
436 | |||
374 | ret = stat(path, &st); | 437 | ret = stat(path, &st); |
375 | if (ret < 0) { | 438 | if (ret < 0) { |
376 | /* not found */ | 439 | /* not found */ |
377 | size = 0; | 440 | size = 0; |
378 | write_or_die(&size, 4); | 441 | if (write(output_fd, &size, 4) != 4) |
442 | err = -EIO; | ||
379 | goto out; | 443 | goto out; |
380 | } | 444 | } |
381 | record_file(path, 4); | 445 | err = record_file(path, 4); |
382 | 446 | ||
383 | out: | 447 | out: |
384 | put_tracing_file(path); | 448 | put_tracing_file(path); |
449 | return err; | ||
450 | } | ||
451 | |||
452 | static void | ||
453 | put_tracepoints_path(struct tracepoint_path *tps) | ||
454 | { | ||
455 | while (tps) { | ||
456 | struct tracepoint_path *t = tps; | ||
457 | |||
458 | tps = tps->next; | ||
459 | free(t->name); | ||
460 | free(t->system); | ||
461 | free(t); | ||
462 | } | ||
385 | } | 463 | } |
386 | 464 | ||
387 | static struct tracepoint_path * | 465 | static struct tracepoint_path * |
@@ -396,27 +474,17 @@ get_tracepoints_path(struct list_head *pattrs) | |||
396 | continue; | 474 | continue; |
397 | ++nr_tracepoints; | 475 | ++nr_tracepoints; |
398 | ppath->next = tracepoint_id_to_path(pos->attr.config); | 476 | ppath->next = tracepoint_id_to_path(pos->attr.config); |
399 | if (!ppath->next) | 477 | if (!ppath->next) { |
400 | die("%s\n", "No memory to alloc tracepoints list"); | 478 | pr_debug("No memory to alloc tracepoints list\n"); |
479 | put_tracepoints_path(&path); | ||
480 | return NULL; | ||
481 | } | ||
401 | ppath = ppath->next; | 482 | ppath = ppath->next; |
402 | } | 483 | } |
403 | 484 | ||
404 | return nr_tracepoints > 0 ? path.next : NULL; | 485 | return nr_tracepoints > 0 ? path.next : NULL; |
405 | } | 486 | } |
406 | 487 | ||
407 | static void | ||
408 | put_tracepoints_path(struct tracepoint_path *tps) | ||
409 | { | ||
410 | while (tps) { | ||
411 | struct tracepoint_path *t = tps; | ||
412 | |||
413 | tps = tps->next; | ||
414 | free(t->name); | ||
415 | free(t->system); | ||
416 | free(t); | ||
417 | } | ||
418 | } | ||
419 | |||
420 | bool have_tracepoints(struct list_head *pattrs) | 488 | bool have_tracepoints(struct list_head *pattrs) |
421 | { | 489 | { |
422 | struct perf_evsel *pos; | 490 | struct perf_evsel *pos; |
@@ -428,9 +496,10 @@ bool have_tracepoints(struct list_head *pattrs) | |||
428 | return false; | 496 | return false; |
429 | } | 497 | } |
430 | 498 | ||
431 | static void tracing_data_header(void) | 499 | static int tracing_data_header(void) |
432 | { | 500 | { |
433 | char buf[20]; | 501 | char buf[20]; |
502 | ssize_t size; | ||
434 | 503 | ||
435 | /* just guessing this is someone's birthday.. ;) */ | 504 | /* just guessing this is someone's birthday.. ;) */ |
436 | buf[0] = 23; | 505 | buf[0] = 23; |
@@ -438,9 +507,12 @@ static void tracing_data_header(void) | |||
438 | buf[2] = 68; | 507 | buf[2] = 68; |
439 | memcpy(buf + 3, "tracing", 7); | 508 | memcpy(buf + 3, "tracing", 7); |
440 | 509 | ||
441 | write_or_die(buf, 10); | 510 | if (write(output_fd, buf, 10) != 10) |
511 | return -1; | ||
442 | 512 | ||
443 | write_or_die(VERSION, strlen(VERSION) + 1); | 513 | size = strlen(VERSION) + 1; |
514 | if (write(output_fd, VERSION, size) != size) | ||
515 | return -1; | ||
444 | 516 | ||
445 | /* save endian */ | 517 | /* save endian */ |
446 | if (bigendian()) | 518 | if (bigendian()) |
@@ -450,15 +522,19 @@ static void tracing_data_header(void) | |||
450 | 522 | ||
451 | read_trace_init(buf[0], buf[0]); | 523 | read_trace_init(buf[0], buf[0]); |
452 | 524 | ||
453 | write_or_die(buf, 1); | 525 | if (write(output_fd, buf, 1) != 1) |
526 | return -1; | ||
454 | 527 | ||
455 | /* save size of long */ | 528 | /* save size of long */ |
456 | buf[0] = sizeof(long); | 529 | buf[0] = sizeof(long); |
457 | write_or_die(buf, 1); | 530 | if (write(output_fd, buf, 1) != 1) |
531 | return -1; | ||
458 | 532 | ||
459 | /* save page_size */ | 533 | /* save page_size */ |
460 | page_size = sysconf(_SC_PAGESIZE); | 534 | if (write(output_fd, &page_size, 4) != 4) |
461 | write_or_die(&page_size, 4); | 535 | return -1; |
536 | |||
537 | return 0; | ||
462 | } | 538 | } |
463 | 539 | ||
464 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | 540 | struct tracing_data *tracing_data_get(struct list_head *pattrs, |
@@ -466,6 +542,7 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
466 | { | 542 | { |
467 | struct tracepoint_path *tps; | 543 | struct tracepoint_path *tps; |
468 | struct tracing_data *tdata; | 544 | struct tracing_data *tdata; |
545 | int err; | ||
469 | 546 | ||
470 | output_fd = fd; | 547 | output_fd = fd; |
471 | 548 | ||
@@ -473,7 +550,10 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
473 | if (!tps) | 550 | if (!tps) |
474 | return NULL; | 551 | return NULL; |
475 | 552 | ||
476 | tdata = malloc_or_die(sizeof(*tdata)); | 553 | tdata = malloc(sizeof(*tdata)); |
554 | if (!tdata) | ||
555 | return NULL; | ||
556 | |||
477 | tdata->temp = temp; | 557 | tdata->temp = temp; |
478 | tdata->size = 0; | 558 | tdata->size = 0; |
479 | 559 | ||
@@ -482,12 +562,16 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
482 | 562 | ||
483 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), | 563 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), |
484 | "/tmp/perf-XXXXXX"); | 564 | "/tmp/perf-XXXXXX"); |
485 | if (!mkstemp(tdata->temp_file)) | 565 | if (!mkstemp(tdata->temp_file)) { |
486 | die("Can't make temp file"); | 566 | pr_debug("Can't make temp file"); |
567 | return NULL; | ||
568 | } | ||
487 | 569 | ||
488 | temp_fd = open(tdata->temp_file, O_RDWR); | 570 | temp_fd = open(tdata->temp_file, O_RDWR); |
489 | if (temp_fd < 0) | 571 | if (temp_fd < 0) { |
490 | die("Can't read '%s'", tdata->temp_file); | 572 | pr_debug("Can't read '%s'", tdata->temp_file); |
573 | return NULL; | ||
574 | } | ||
491 | 575 | ||
492 | /* | 576 | /* |
493 | * Set the temp file the default output, so all the | 577 | * Set the temp file the default output, so all the |
@@ -496,13 +580,24 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
496 | output_fd = temp_fd; | 580 | output_fd = temp_fd; |
497 | } | 581 | } |
498 | 582 | ||
499 | tracing_data_header(); | 583 | err = tracing_data_header(); |
500 | read_header_files(); | 584 | if (err) |
501 | read_ftrace_files(tps); | 585 | goto out; |
502 | read_event_files(tps); | 586 | err = read_header_files(); |
503 | read_proc_kallsyms(); | 587 | if (err) |
504 | read_ftrace_printk(); | 588 | goto out; |
589 | err = read_ftrace_files(tps); | ||
590 | if (err) | ||
591 | goto out; | ||
592 | err = read_event_files(tps); | ||
593 | if (err) | ||
594 | goto out; | ||
595 | err = read_proc_kallsyms(); | ||
596 | if (err) | ||
597 | goto out; | ||
598 | err = read_ftrace_printk(); | ||
505 | 599 | ||
600 | out: | ||
506 | /* | 601 | /* |
507 | * All tracing data are stored by now, we can restore | 602 | * All tracing data are stored by now, we can restore |
508 | * the default output file in case we used temp file. | 603 | * the default output file in case we used temp file. |
@@ -513,22 +608,31 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
513 | output_fd = fd; | 608 | output_fd = fd; |
514 | } | 609 | } |
515 | 610 | ||
611 | if (err) { | ||
612 | free(tdata); | ||
613 | tdata = NULL; | ||
614 | } | ||
615 | |||
516 | put_tracepoints_path(tps); | 616 | put_tracepoints_path(tps); |
517 | return tdata; | 617 | return tdata; |
518 | } | 618 | } |
519 | 619 | ||
520 | void tracing_data_put(struct tracing_data *tdata) | 620 | int tracing_data_put(struct tracing_data *tdata) |
521 | { | 621 | { |
622 | int err = 0; | ||
623 | |||
522 | if (tdata->temp) { | 624 | if (tdata->temp) { |
523 | record_file(tdata->temp_file, 0); | 625 | err = record_file(tdata->temp_file, 0); |
524 | unlink(tdata->temp_file); | 626 | unlink(tdata->temp_file); |
525 | } | 627 | } |
526 | 628 | ||
527 | free(tdata); | 629 | free(tdata); |
630 | return err; | ||
528 | } | 631 | } |
529 | 632 | ||
530 | int read_tracing_data(int fd, struct list_head *pattrs) | 633 | int read_tracing_data(int fd, struct list_head *pattrs) |
531 | { | 634 | { |
635 | int err; | ||
532 | struct tracing_data *tdata; | 636 | struct tracing_data *tdata; |
533 | 637 | ||
534 | /* | 638 | /* |
@@ -539,6 +643,6 @@ int read_tracing_data(int fd, struct list_head *pattrs) | |||
539 | if (!tdata) | 643 | if (!tdata) |
540 | return -ENOMEM; | 644 | return -ENOMEM; |
541 | 645 | ||
542 | tracing_data_put(tdata); | 646 | err = tracing_data_put(tdata); |
543 | return 0; | 647 | return err; |
544 | } | 648 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 3aabcd687cd5..4454835a9ebc 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -183,43 +183,6 @@ void event_format__print(struct event_format *event, | |||
183 | trace_seq_do_printf(&s); | 183 | trace_seq_do_printf(&s); |
184 | } | 184 | } |
185 | 185 | ||
186 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) | ||
187 | { | ||
188 | int type = trace_parse_common_type(pevent, data); | ||
189 | struct event_format *event = pevent_find_event(pevent, type); | ||
190 | |||
191 | if (!event) { | ||
192 | warning("ug! no event found for type %d", type); | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | event_format__print(event, cpu, data, size); | ||
197 | } | ||
198 | |||
199 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | ||
200 | unsigned long long nsecs, char *comm) | ||
201 | { | ||
202 | struct pevent_record record; | ||
203 | struct trace_seq s; | ||
204 | int pid; | ||
205 | |||
206 | pevent->latency_format = latency_format; | ||
207 | |||
208 | record.ts = nsecs; | ||
209 | record.cpu = cpu; | ||
210 | record.size = size; | ||
211 | record.data = data; | ||
212 | pid = pevent_data_pid(pevent, &record); | ||
213 | |||
214 | if (!pevent_pid_is_registered(pevent, pid)) | ||
215 | pevent_register_comm(pevent, comm, pid); | ||
216 | |||
217 | trace_seq_init(&s); | ||
218 | pevent_print_event(pevent, &s, &record); | ||
219 | trace_seq_do_printf(&s); | ||
220 | printf("\n"); | ||
221 | } | ||
222 | |||
223 | void parse_proc_kallsyms(struct pevent *pevent, | 186 | void parse_proc_kallsyms(struct pevent *pevent, |
224 | char *file, unsigned int size __maybe_unused) | 187 | char *file, unsigned int size __maybe_unused) |
225 | { | 188 | { |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 3741572696af..af215c0d2379 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -18,8 +18,6 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #define _FILE_OFFSET_BITS 64 | ||
22 | |||
23 | #include <dirent.h> | 21 | #include <dirent.h> |
24 | #include <stdio.h> | 22 | #include <stdio.h> |
25 | #include <stdlib.h> | 23 | #include <stdlib.h> |
@@ -41,26 +39,14 @@ | |||
41 | 39 | ||
42 | static int input_fd; | 40 | static int input_fd; |
43 | 41 | ||
44 | static int read_page; | ||
45 | |||
46 | int file_bigendian; | 42 | int file_bigendian; |
47 | int host_bigendian; | 43 | int host_bigendian; |
48 | static int long_size; | 44 | static int long_size; |
49 | 45 | ||
50 | static ssize_t calc_data_size; | 46 | static ssize_t trace_data_size; |
51 | static bool repipe; | 47 | static bool repipe; |
52 | 48 | ||
53 | static void *malloc_or_die(int size) | 49 | static int __do_read(int fd, void *buf, int size) |
54 | { | ||
55 | void *ret; | ||
56 | |||
57 | ret = malloc(size); | ||
58 | if (!ret) | ||
59 | die("malloc"); | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static int do_read(int fd, void *buf, int size) | ||
64 | { | 50 | { |
65 | int rsize = size; | 51 | int rsize = size; |
66 | 52 | ||
@@ -73,8 +59,10 @@ static int do_read(int fd, void *buf, int size) | |||
73 | if (repipe) { | 59 | if (repipe) { |
74 | int retw = write(STDOUT_FILENO, buf, ret); | 60 | int retw = write(STDOUT_FILENO, buf, ret); |
75 | 61 | ||
76 | if (retw <= 0 || retw != ret) | 62 | if (retw <= 0 || retw != ret) { |
77 | die("repiping input file"); | 63 | pr_debug("repiping input file"); |
64 | return -1; | ||
65 | } | ||
78 | } | 66 | } |
79 | 67 | ||
80 | size -= ret; | 68 | size -= ret; |
@@ -84,17 +72,18 @@ static int do_read(int fd, void *buf, int size) | |||
84 | return rsize; | 72 | return rsize; |
85 | } | 73 | } |
86 | 74 | ||
87 | static int read_or_die(void *data, int size) | 75 | static int do_read(void *data, int size) |
88 | { | 76 | { |
89 | int r; | 77 | int r; |
90 | 78 | ||
91 | r = do_read(input_fd, data, size); | 79 | r = __do_read(input_fd, data, size); |
92 | if (r <= 0) | 80 | if (r <= 0) { |
93 | die("reading input file (size expected=%d received=%d)", | 81 | pr_debug("reading input file (size expected=%d received=%d)", |
94 | size, r); | 82 | size, r); |
83 | return -1; | ||
84 | } | ||
95 | 85 | ||
96 | if (calc_data_size) | 86 | trace_data_size += r; |
97 | calc_data_size += r; | ||
98 | 87 | ||
99 | return r; | 88 | return r; |
100 | } | 89 | } |
@@ -107,7 +96,7 @@ static void skip(int size) | |||
107 | 96 | ||
108 | while (size) { | 97 | while (size) { |
109 | r = size > BUFSIZ ? BUFSIZ : size; | 98 | r = size > BUFSIZ ? BUFSIZ : size; |
110 | read_or_die(buf, r); | 99 | do_read(buf, r); |
111 | size -= r; | 100 | size -= r; |
112 | }; | 101 | }; |
113 | } | 102 | } |
@@ -116,7 +105,8 @@ static unsigned int read4(struct pevent *pevent) | |||
116 | { | 105 | { |
117 | unsigned int data; | 106 | unsigned int data; |
118 | 107 | ||
119 | read_or_die(&data, 4); | 108 | if (do_read(&data, 4) < 0) |
109 | return 0; | ||
120 | return __data2host4(pevent, data); | 110 | return __data2host4(pevent, data); |
121 | } | 111 | } |
122 | 112 | ||
@@ -124,7 +114,8 @@ static unsigned long long read8(struct pevent *pevent) | |||
124 | { | 114 | { |
125 | unsigned long long data; | 115 | unsigned long long data; |
126 | 116 | ||
127 | read_or_die(&data, 8); | 117 | if (do_read(&data, 8) < 0) |
118 | return 0; | ||
128 | return __data2host8(pevent, data); | 119 | return __data2host8(pevent, data); |
129 | } | 120 | } |
130 | 121 | ||
@@ -138,17 +129,23 @@ static char *read_string(void) | |||
138 | 129 | ||
139 | for (;;) { | 130 | for (;;) { |
140 | r = read(input_fd, &c, 1); | 131 | r = read(input_fd, &c, 1); |
141 | if (r < 0) | 132 | if (r < 0) { |
142 | die("reading input file"); | 133 | pr_debug("reading input file"); |
134 | goto out; | ||
135 | } | ||
143 | 136 | ||
144 | if (!r) | 137 | if (!r) { |
145 | die("no data"); | 138 | pr_debug("no data"); |
139 | goto out; | ||
140 | } | ||
146 | 141 | ||
147 | if (repipe) { | 142 | if (repipe) { |
148 | int retw = write(STDOUT_FILENO, &c, 1); | 143 | int retw = write(STDOUT_FILENO, &c, 1); |
149 | 144 | ||
150 | if (retw <= 0 || retw != r) | 145 | if (retw <= 0 || retw != r) { |
151 | die("repiping input file string"); | 146 | pr_debug("repiping input file string"); |
147 | goto out; | ||
148 | } | ||
152 | } | 149 | } |
153 | 150 | ||
154 | buf[size++] = c; | 151 | buf[size++] = c; |
@@ -157,60 +154,79 @@ static char *read_string(void) | |||
157 | break; | 154 | break; |
158 | } | 155 | } |
159 | 156 | ||
160 | if (calc_data_size) | 157 | trace_data_size += size; |
161 | calc_data_size += size; | ||
162 | |||
163 | str = malloc_or_die(size); | ||
164 | memcpy(str, buf, size); | ||
165 | 158 | ||
159 | str = malloc(size); | ||
160 | if (str) | ||
161 | memcpy(str, buf, size); | ||
162 | out: | ||
166 | return str; | 163 | return str; |
167 | } | 164 | } |
168 | 165 | ||
169 | static void read_proc_kallsyms(struct pevent *pevent) | 166 | static int read_proc_kallsyms(struct pevent *pevent) |
170 | { | 167 | { |
171 | unsigned int size; | 168 | unsigned int size; |
172 | char *buf; | 169 | char *buf; |
173 | 170 | ||
174 | size = read4(pevent); | 171 | size = read4(pevent); |
175 | if (!size) | 172 | if (!size) |
176 | return; | 173 | return 0; |
174 | |||
175 | buf = malloc(size + 1); | ||
176 | if (buf == NULL) | ||
177 | return -1; | ||
177 | 178 | ||
178 | buf = malloc_or_die(size + 1); | 179 | if (do_read(buf, size) < 0) { |
179 | read_or_die(buf, size); | 180 | free(buf); |
181 | return -1; | ||
182 | } | ||
180 | buf[size] = '\0'; | 183 | buf[size] = '\0'; |
181 | 184 | ||
182 | parse_proc_kallsyms(pevent, buf, size); | 185 | parse_proc_kallsyms(pevent, buf, size); |
183 | 186 | ||
184 | free(buf); | 187 | free(buf); |
188 | return 0; | ||
185 | } | 189 | } |
186 | 190 | ||
187 | static void read_ftrace_printk(struct pevent *pevent) | 191 | static int read_ftrace_printk(struct pevent *pevent) |
188 | { | 192 | { |
189 | unsigned int size; | 193 | unsigned int size; |
190 | char *buf; | 194 | char *buf; |
191 | 195 | ||
196 | /* it can have 0 size */ | ||
192 | size = read4(pevent); | 197 | size = read4(pevent); |
193 | if (!size) | 198 | if (!size) |
194 | return; | 199 | return 0; |
200 | |||
201 | buf = malloc(size); | ||
202 | if (buf == NULL) | ||
203 | return -1; | ||
195 | 204 | ||
196 | buf = malloc_or_die(size); | 205 | if (do_read(buf, size) < 0) { |
197 | read_or_die(buf, size); | 206 | free(buf); |
207 | return -1; | ||
208 | } | ||
198 | 209 | ||
199 | parse_ftrace_printk(pevent, buf, size); | 210 | parse_ftrace_printk(pevent, buf, size); |
200 | 211 | ||
201 | free(buf); | 212 | free(buf); |
213 | return 0; | ||
202 | } | 214 | } |
203 | 215 | ||
204 | static void read_header_files(struct pevent *pevent) | 216 | static int read_header_files(struct pevent *pevent) |
205 | { | 217 | { |
206 | unsigned long long size; | 218 | unsigned long long size; |
207 | char *header_event; | 219 | char *header_event; |
208 | char buf[BUFSIZ]; | 220 | char buf[BUFSIZ]; |
221 | int ret = 0; | ||
209 | 222 | ||
210 | read_or_die(buf, 12); | 223 | if (do_read(buf, 12) < 0) |
224 | return -1; | ||
211 | 225 | ||
212 | if (memcmp(buf, "header_page", 12) != 0) | 226 | if (memcmp(buf, "header_page", 12) != 0) { |
213 | die("did not read header page"); | 227 | pr_debug("did not read header page"); |
228 | return -1; | ||
229 | } | ||
214 | 230 | ||
215 | size = read8(pevent); | 231 | size = read8(pevent); |
216 | skip(size); | 232 | skip(size); |
@@ -221,269 +237,107 @@ static void read_header_files(struct pevent *pevent) | |||
221 | */ | 237 | */ |
222 | long_size = header_page_size_size; | 238 | long_size = header_page_size_size; |
223 | 239 | ||
224 | read_or_die(buf, 13); | 240 | if (do_read(buf, 13) < 0) |
225 | if (memcmp(buf, "header_event", 13) != 0) | 241 | return -1; |
226 | die("did not read header event"); | 242 | |
243 | if (memcmp(buf, "header_event", 13) != 0) { | ||
244 | pr_debug("did not read header event"); | ||
245 | return -1; | ||
246 | } | ||
227 | 247 | ||
228 | size = read8(pevent); | 248 | size = read8(pevent); |
229 | header_event = malloc_or_die(size); | 249 | header_event = malloc(size); |
230 | read_or_die(header_event, size); | 250 | if (header_event == NULL) |
251 | return -1; | ||
252 | |||
253 | if (do_read(header_event, size) < 0) | ||
254 | ret = -1; | ||
255 | |||
231 | free(header_event); | 256 | free(header_event); |
257 | return ret; | ||
232 | } | 258 | } |
233 | 259 | ||
234 | static void read_ftrace_file(struct pevent *pevent, unsigned long long size) | 260 | static int read_ftrace_file(struct pevent *pevent, unsigned long long size) |
235 | { | 261 | { |
236 | char *buf; | 262 | char *buf; |
237 | 263 | ||
238 | buf = malloc_or_die(size); | 264 | buf = malloc(size); |
239 | read_or_die(buf, size); | 265 | if (buf == NULL) |
266 | return -1; | ||
267 | |||
268 | if (do_read(buf, size) < 0) { | ||
269 | free(buf); | ||
270 | return -1; | ||
271 | } | ||
272 | |||
240 | parse_ftrace_file(pevent, buf, size); | 273 | parse_ftrace_file(pevent, buf, size); |
241 | free(buf); | 274 | free(buf); |
275 | return 0; | ||
242 | } | 276 | } |
243 | 277 | ||
244 | static void read_event_file(struct pevent *pevent, char *sys, | 278 | static int read_event_file(struct pevent *pevent, char *sys, |
245 | unsigned long long size) | 279 | unsigned long long size) |
246 | { | 280 | { |
247 | char *buf; | 281 | char *buf; |
248 | 282 | ||
249 | buf = malloc_or_die(size); | 283 | buf = malloc(size); |
250 | read_or_die(buf, size); | 284 | if (buf == NULL) |
285 | return -1; | ||
286 | |||
287 | if (do_read(buf, size) < 0) { | ||
288 | free(buf); | ||
289 | return -1; | ||
290 | } | ||
291 | |||
251 | parse_event_file(pevent, buf, size, sys); | 292 | parse_event_file(pevent, buf, size, sys); |
252 | free(buf); | 293 | free(buf); |
294 | return 0; | ||
253 | } | 295 | } |
254 | 296 | ||
255 | static void read_ftrace_files(struct pevent *pevent) | 297 | static int read_ftrace_files(struct pevent *pevent) |
256 | { | 298 | { |
257 | unsigned long long size; | 299 | unsigned long long size; |
258 | int count; | 300 | int count; |
259 | int i; | 301 | int i; |
302 | int ret; | ||
260 | 303 | ||
261 | count = read4(pevent); | 304 | count = read4(pevent); |
262 | 305 | ||
263 | for (i = 0; i < count; i++) { | 306 | for (i = 0; i < count; i++) { |
264 | size = read8(pevent); | 307 | size = read8(pevent); |
265 | read_ftrace_file(pevent, size); | 308 | ret = read_ftrace_file(pevent, size); |
309 | if (ret) | ||
310 | return ret; | ||
266 | } | 311 | } |
312 | return 0; | ||
267 | } | 313 | } |
268 | 314 | ||
269 | static void read_event_files(struct pevent *pevent) | 315 | static int read_event_files(struct pevent *pevent) |
270 | { | 316 | { |
271 | unsigned long long size; | 317 | unsigned long long size; |
272 | char *sys; | 318 | char *sys; |
273 | int systems; | 319 | int systems; |
274 | int count; | 320 | int count; |
275 | int i,x; | 321 | int i,x; |
322 | int ret; | ||
276 | 323 | ||
277 | systems = read4(pevent); | 324 | systems = read4(pevent); |
278 | 325 | ||
279 | for (i = 0; i < systems; i++) { | 326 | for (i = 0; i < systems; i++) { |
280 | sys = read_string(); | 327 | sys = read_string(); |
328 | if (sys == NULL) | ||
329 | return -1; | ||
281 | 330 | ||
282 | count = read4(pevent); | 331 | count = read4(pevent); |
332 | |||
283 | for (x=0; x < count; x++) { | 333 | for (x=0; x < count; x++) { |
284 | size = read8(pevent); | 334 | size = read8(pevent); |
285 | read_event_file(pevent, sys, size); | 335 | ret = read_event_file(pevent, sys, size); |
336 | if (ret) | ||
337 | return ret; | ||
286 | } | 338 | } |
287 | } | 339 | } |
288 | } | 340 | return 0; |
289 | |||
290 | struct cpu_data { | ||
291 | unsigned long long offset; | ||
292 | unsigned long long size; | ||
293 | unsigned long long timestamp; | ||
294 | struct pevent_record *next; | ||
295 | char *page; | ||
296 | int cpu; | ||
297 | int index; | ||
298 | int page_size; | ||
299 | }; | ||
300 | |||
301 | static struct cpu_data *cpu_data; | ||
302 | |||
303 | static void update_cpu_data_index(int cpu) | ||
304 | { | ||
305 | cpu_data[cpu].offset += page_size; | ||
306 | cpu_data[cpu].size -= page_size; | ||
307 | cpu_data[cpu].index = 0; | ||
308 | } | ||
309 | |||
310 | static void get_next_page(int cpu) | ||
311 | { | ||
312 | off_t save_seek; | ||
313 | off_t ret; | ||
314 | |||
315 | if (!cpu_data[cpu].page) | ||
316 | return; | ||
317 | |||
318 | if (read_page) { | ||
319 | if (cpu_data[cpu].size <= page_size) { | ||
320 | free(cpu_data[cpu].page); | ||
321 | cpu_data[cpu].page = NULL; | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | update_cpu_data_index(cpu); | ||
326 | |||
327 | /* other parts of the code may expect the pointer to not move */ | ||
328 | save_seek = lseek(input_fd, 0, SEEK_CUR); | ||
329 | |||
330 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); | ||
331 | if (ret == (off_t)-1) | ||
332 | die("failed to lseek"); | ||
333 | ret = read(input_fd, cpu_data[cpu].page, page_size); | ||
334 | if (ret < 0) | ||
335 | die("failed to read page"); | ||
336 | |||
337 | /* reset the file pointer back */ | ||
338 | lseek(input_fd, save_seek, SEEK_SET); | ||
339 | |||
340 | return; | ||
341 | } | ||
342 | |||
343 | munmap(cpu_data[cpu].page, page_size); | ||
344 | cpu_data[cpu].page = NULL; | ||
345 | |||
346 | if (cpu_data[cpu].size <= page_size) | ||
347 | return; | ||
348 | |||
349 | update_cpu_data_index(cpu); | ||
350 | |||
351 | cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, | ||
352 | input_fd, cpu_data[cpu].offset); | ||
353 | if (cpu_data[cpu].page == MAP_FAILED) | ||
354 | die("failed to mmap cpu %d at offset 0x%llx", | ||
355 | cpu, cpu_data[cpu].offset); | ||
356 | } | ||
357 | |||
358 | static unsigned int type_len4host(unsigned int type_len_ts) | ||
359 | { | ||
360 | if (file_bigendian) | ||
361 | return (type_len_ts >> 27) & ((1 << 5) - 1); | ||
362 | else | ||
363 | return type_len_ts & ((1 << 5) - 1); | ||
364 | } | ||
365 | |||
366 | static unsigned int ts4host(unsigned int type_len_ts) | ||
367 | { | ||
368 | if (file_bigendian) | ||
369 | return type_len_ts & ((1 << 27) - 1); | ||
370 | else | ||
371 | return type_len_ts >> 5; | ||
372 | } | ||
373 | |||
374 | static int calc_index(void *ptr, int cpu) | ||
375 | { | ||
376 | return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page; | ||
377 | } | ||
378 | |||
379 | struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu) | ||
380 | { | ||
381 | struct pevent_record *data; | ||
382 | void *page = cpu_data[cpu].page; | ||
383 | int idx = cpu_data[cpu].index; | ||
384 | void *ptr = page + idx; | ||
385 | unsigned long long extend; | ||
386 | unsigned int type_len_ts; | ||
387 | unsigned int type_len; | ||
388 | unsigned int delta; | ||
389 | unsigned int length = 0; | ||
390 | |||
391 | if (cpu_data[cpu].next) | ||
392 | return cpu_data[cpu].next; | ||
393 | |||
394 | if (!page) | ||
395 | return NULL; | ||
396 | |||
397 | if (!idx) { | ||
398 | /* FIXME: handle header page */ | ||
399 | if (header_page_ts_size != 8) | ||
400 | die("expected a long long type for timestamp"); | ||
401 | cpu_data[cpu].timestamp = data2host8(pevent, ptr); | ||
402 | ptr += 8; | ||
403 | switch (header_page_size_size) { | ||
404 | case 4: | ||
405 | cpu_data[cpu].page_size = data2host4(pevent, ptr); | ||
406 | ptr += 4; | ||
407 | break; | ||
408 | case 8: | ||
409 | cpu_data[cpu].page_size = data2host8(pevent, ptr); | ||
410 | ptr += 8; | ||
411 | break; | ||
412 | default: | ||
413 | die("bad long size"); | ||
414 | } | ||
415 | ptr = cpu_data[cpu].page + header_page_data_offset; | ||
416 | } | ||
417 | |||
418 | read_again: | ||
419 | idx = calc_index(ptr, cpu); | ||
420 | |||
421 | if (idx >= cpu_data[cpu].page_size) { | ||
422 | get_next_page(cpu); | ||
423 | return trace_peek_data(pevent, cpu); | ||
424 | } | ||
425 | |||
426 | type_len_ts = data2host4(pevent, ptr); | ||
427 | ptr += 4; | ||
428 | |||
429 | type_len = type_len4host(type_len_ts); | ||
430 | delta = ts4host(type_len_ts); | ||
431 | |||
432 | switch (type_len) { | ||
433 | case RINGBUF_TYPE_PADDING: | ||
434 | if (!delta) | ||
435 | die("error, hit unexpected end of page"); | ||
436 | length = data2host4(pevent, ptr); | ||
437 | ptr += 4; | ||
438 | length *= 4; | ||
439 | ptr += length; | ||
440 | goto read_again; | ||
441 | |||
442 | case RINGBUF_TYPE_TIME_EXTEND: | ||
443 | extend = data2host4(pevent, ptr); | ||
444 | ptr += 4; | ||
445 | extend <<= TS_SHIFT; | ||
446 | extend += delta; | ||
447 | cpu_data[cpu].timestamp += extend; | ||
448 | goto read_again; | ||
449 | |||
450 | case RINGBUF_TYPE_TIME_STAMP: | ||
451 | ptr += 12; | ||
452 | break; | ||
453 | case 0: | ||
454 | length = data2host4(pevent, ptr); | ||
455 | ptr += 4; | ||
456 | die("here! length=%d", length); | ||
457 | break; | ||
458 | default: | ||
459 | length = type_len * 4; | ||
460 | break; | ||
461 | } | ||
462 | |||
463 | cpu_data[cpu].timestamp += delta; | ||
464 | |||
465 | data = malloc_or_die(sizeof(*data)); | ||
466 | memset(data, 0, sizeof(*data)); | ||
467 | |||
468 | data->ts = cpu_data[cpu].timestamp; | ||
469 | data->size = length; | ||
470 | data->data = ptr; | ||
471 | ptr += length; | ||
472 | |||
473 | cpu_data[cpu].index = calc_index(ptr, cpu); | ||
474 | cpu_data[cpu].next = data; | ||
475 | |||
476 | return data; | ||
477 | } | ||
478 | |||
479 | struct pevent_record *trace_read_data(struct pevent *pevent, int cpu) | ||
480 | { | ||
481 | struct pevent_record *data; | ||
482 | |||
483 | data = trace_peek_data(pevent, cpu); | ||
484 | cpu_data[cpu].next = NULL; | ||
485 | |||
486 | return data; | ||
487 | } | 341 | } |
488 | 342 | ||
489 | ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | 343 | ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) |
@@ -494,58 +348,85 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | |||
494 | int show_version = 0; | 348 | int show_version = 0; |
495 | int show_funcs = 0; | 349 | int show_funcs = 0; |
496 | int show_printk = 0; | 350 | int show_printk = 0; |
497 | ssize_t size; | 351 | ssize_t size = -1; |
352 | struct pevent *pevent; | ||
353 | int err; | ||
498 | 354 | ||
499 | calc_data_size = 1; | 355 | *ppevent = NULL; |
500 | repipe = __repipe; | ||
501 | 356 | ||
357 | repipe = __repipe; | ||
502 | input_fd = fd; | 358 | input_fd = fd; |
503 | 359 | ||
504 | read_or_die(buf, 3); | 360 | if (do_read(buf, 3) < 0) |
505 | if (memcmp(buf, test, 3) != 0) | 361 | return -1; |
506 | die("no trace data in the file"); | 362 | if (memcmp(buf, test, 3) != 0) { |
363 | pr_debug("no trace data in the file"); | ||
364 | return -1; | ||
365 | } | ||
507 | 366 | ||
508 | read_or_die(buf, 7); | 367 | if (do_read(buf, 7) < 0) |
509 | if (memcmp(buf, "tracing", 7) != 0) | 368 | return -1; |
510 | die("not a trace file (missing 'tracing' tag)"); | 369 | if (memcmp(buf, "tracing", 7) != 0) { |
370 | pr_debug("not a trace file (missing 'tracing' tag)"); | ||
371 | return -1; | ||
372 | } | ||
511 | 373 | ||
512 | version = read_string(); | 374 | version = read_string(); |
375 | if (version == NULL) | ||
376 | return -1; | ||
513 | if (show_version) | 377 | if (show_version) |
514 | printf("version = %s\n", version); | 378 | printf("version = %s\n", version); |
515 | free(version); | 379 | free(version); |
516 | 380 | ||
517 | read_or_die(buf, 1); | 381 | if (do_read(buf, 1) < 0) |
382 | return -1; | ||
518 | file_bigendian = buf[0]; | 383 | file_bigendian = buf[0]; |
519 | host_bigendian = bigendian(); | 384 | host_bigendian = bigendian(); |
520 | 385 | ||
521 | *ppevent = read_trace_init(file_bigendian, host_bigendian); | 386 | pevent = read_trace_init(file_bigendian, host_bigendian); |
522 | if (*ppevent == NULL) | 387 | if (pevent == NULL) { |
523 | die("read_trace_init failed"); | 388 | pr_debug("read_trace_init failed"); |
389 | goto out; | ||
390 | } | ||
524 | 391 | ||
525 | read_or_die(buf, 1); | 392 | if (do_read(buf, 1) < 0) |
393 | goto out; | ||
526 | long_size = buf[0]; | 394 | long_size = buf[0]; |
527 | 395 | ||
528 | page_size = read4(*ppevent); | 396 | page_size = read4(pevent); |
529 | 397 | if (!page_size) | |
530 | read_header_files(*ppevent); | 398 | goto out; |
531 | 399 | ||
532 | read_ftrace_files(*ppevent); | 400 | err = read_header_files(pevent); |
533 | read_event_files(*ppevent); | 401 | if (err) |
534 | read_proc_kallsyms(*ppevent); | 402 | goto out; |
535 | read_ftrace_printk(*ppevent); | 403 | err = read_ftrace_files(pevent); |
536 | 404 | if (err) | |
537 | size = calc_data_size - 1; | 405 | goto out; |
538 | calc_data_size = 0; | 406 | err = read_event_files(pevent); |
407 | if (err) | ||
408 | goto out; | ||
409 | err = read_proc_kallsyms(pevent); | ||
410 | if (err) | ||
411 | goto out; | ||
412 | err = read_ftrace_printk(pevent); | ||
413 | if (err) | ||
414 | goto out; | ||
415 | |||
416 | size = trace_data_size; | ||
539 | repipe = false; | 417 | repipe = false; |
540 | 418 | ||
541 | if (show_funcs) { | 419 | if (show_funcs) { |
542 | pevent_print_funcs(*ppevent); | 420 | pevent_print_funcs(pevent); |
543 | return size; | 421 | } else if (show_printk) { |
544 | } | 422 | pevent_print_printk(pevent); |
545 | if (show_printk) { | ||
546 | pevent_print_printk(*ppevent); | ||
547 | return size; | ||
548 | } | 423 | } |
549 | 424 | ||
425 | *ppevent = pevent; | ||
426 | pevent = NULL; | ||
427 | |||
428 | out: | ||
429 | if (pevent) | ||
430 | pevent_free(pevent); | ||
550 | return size; | 431 | return size; |
551 | } | 432 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index a55fd37ffea1..1978c398ad87 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -30,13 +30,9 @@ enum { | |||
30 | int bigendian(void); | 30 | int bigendian(void); |
31 | 31 | ||
32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); | 32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); |
33 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); | ||
34 | void event_format__print(struct event_format *event, | 33 | void event_format__print(struct event_format *event, |
35 | int cpu, void *data, int size); | 34 | int cpu, void *data, int size); |
36 | 35 | ||
37 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | ||
38 | unsigned long long nsecs, char *comm); | ||
39 | |||
40 | int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); | 36 | int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); |
41 | int parse_event_file(struct pevent *pevent, | 37 | int parse_event_file(struct pevent *pevent, |
42 | char *buf, unsigned long size, char *sys); | 38 | char *buf, unsigned long size, char *sys); |
@@ -72,7 +68,7 @@ struct tracing_data { | |||
72 | 68 | ||
73 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | 69 | struct tracing_data *tracing_data_get(struct list_head *pattrs, |
74 | int fd, bool temp); | 70 | int fd, bool temp); |
75 | void tracing_data_put(struct tracing_data *tdata); | 71 | int tracing_data_put(struct tracing_data *tdata); |
76 | 72 | ||
77 | 73 | ||
78 | struct addr_location; | 74 | struct addr_location; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 5906e8426cc7..59d868add275 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -12,9 +12,13 @@ | |||
12 | */ | 12 | */ |
13 | unsigned int page_size; | 13 | unsigned int page_size; |
14 | 14 | ||
15 | bool test_attr__enabled; | ||
16 | |||
15 | bool perf_host = true; | 17 | bool perf_host = true; |
16 | bool perf_guest = false; | 18 | bool perf_guest = false; |
17 | 19 | ||
20 | char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; | ||
21 | |||
18 | void event_attr_init(struct perf_event_attr *attr) | 22 | void event_attr_init(struct perf_event_attr *attr) |
19 | { | 23 | { |
20 | if (!perf_host) | 24 | if (!perf_host) |
@@ -218,3 +222,50 @@ void dump_stack(void) | |||
218 | #else | 222 | #else |
219 | void dump_stack(void) {} | 223 | void dump_stack(void) {} |
220 | #endif | 224 | #endif |
225 | |||
226 | void get_term_dimensions(struct winsize *ws) | ||
227 | { | ||
228 | char *s = getenv("LINES"); | ||
229 | |||
230 | if (s != NULL) { | ||
231 | ws->ws_row = atoi(s); | ||
232 | s = getenv("COLUMNS"); | ||
233 | if (s != NULL) { | ||
234 | ws->ws_col = atoi(s); | ||
235 | if (ws->ws_row && ws->ws_col) | ||
236 | return; | ||
237 | } | ||
238 | } | ||
239 | #ifdef TIOCGWINSZ | ||
240 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
241 | ws->ws_row && ws->ws_col) | ||
242 | return; | ||
243 | #endif | ||
244 | ws->ws_row = 25; | ||
245 | ws->ws_col = 80; | ||
246 | } | ||
247 | |||
248 | static void set_tracing_events_path(const char *mountpoint) | ||
249 | { | ||
250 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | ||
251 | mountpoint, "tracing/events"); | ||
252 | } | ||
253 | |||
254 | const char *perf_debugfs_mount(const char *mountpoint) | ||
255 | { | ||
256 | const char *mnt; | ||
257 | |||
258 | mnt = debugfs_mount(mountpoint); | ||
259 | if (!mnt) | ||
260 | return NULL; | ||
261 | |||
262 | set_tracing_events_path(mnt); | ||
263 | |||
264 | return mnt; | ||
265 | } | ||
266 | |||
267 | void perf_debugfs_set_path(const char *mntpt) | ||
268 | { | ||
269 | snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); | ||
270 | set_tracing_events_path(mntpt); | ||
271 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c2330918110c..7a484c97e500 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -1,8 +1,6 @@ | |||
1 | #ifndef GIT_COMPAT_UTIL_H | 1 | #ifndef GIT_COMPAT_UTIL_H |
2 | #define GIT_COMPAT_UTIL_H | 2 | #define GIT_COMPAT_UTIL_H |
3 | 3 | ||
4 | #define _FILE_OFFSET_BITS 64 | ||
5 | |||
6 | #ifndef FLEX_ARRAY | 4 | #ifndef FLEX_ARRAY |
7 | /* | 5 | /* |
8 | * See if our compiler is known to support flexible array members. | 6 | * See if our compiler is known to support flexible array members. |
@@ -73,10 +71,14 @@ | |||
73 | #include <linux/magic.h> | 71 | #include <linux/magic.h> |
74 | #include "types.h" | 72 | #include "types.h" |
75 | #include <sys/ttydefaults.h> | 73 | #include <sys/ttydefaults.h> |
74 | #include <lk/debugfs.h> | ||
76 | 75 | ||
77 | extern const char *graph_line; | 76 | extern const char *graph_line; |
78 | extern const char *graph_dotted_line; | 77 | extern const char *graph_dotted_line; |
79 | extern char buildid_dir[]; | 78 | extern char buildid_dir[]; |
79 | extern char tracing_events_path[]; | ||
80 | extern void perf_debugfs_set_path(const char *mountpoint); | ||
81 | const char *perf_debugfs_mount(const char *mountpoint); | ||
80 | 82 | ||
81 | /* On most systems <limits.h> would have given us this, but | 83 | /* On most systems <limits.h> would have given us this, but |
82 | * not on some systems (e.g. GNU/Hurd). | 84 | * not on some systems (e.g. GNU/Hurd). |
@@ -219,8 +221,8 @@ extern unsigned char sane_ctype[256]; | |||
219 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 221 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
220 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 222 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
221 | #define isprint(x) sane_istest(x,GIT_PRINT) | 223 | #define isprint(x) sane_istest(x,GIT_PRINT) |
222 | #define islower(x) (sane_istest(x,GIT_ALPHA) && sane_istest(x,0x20)) | 224 | #define islower(x) (sane_istest(x,GIT_ALPHA) && (x & 0x20)) |
223 | #define isupper(x) (sane_istest(x,GIT_ALPHA) && !sane_istest(x,0x20)) | 225 | #define isupper(x) (sane_istest(x,GIT_ALPHA) && !(x & 0x20)) |
224 | #define tolower(x) sane_case((unsigned char)(x), 0x20) | 226 | #define tolower(x) sane_case((unsigned char)(x), 0x20) |
225 | #define toupper(x) sane_case((unsigned char)(x), 0) | 227 | #define toupper(x) sane_case((unsigned char)(x), 0) |
226 | 228 | ||
@@ -265,10 +267,13 @@ bool is_power_of_2(unsigned long n) | |||
265 | size_t hex_width(u64 v); | 267 | size_t hex_width(u64 v); |
266 | int hex2u64(const char *ptr, u64 *val); | 268 | int hex2u64(const char *ptr, u64 *val); |
267 | 269 | ||
270 | char *ltrim(char *s); | ||
268 | char *rtrim(char *s); | 271 | char *rtrim(char *s); |
269 | 272 | ||
270 | void dump_stack(void); | 273 | void dump_stack(void); |
271 | 274 | ||
272 | extern unsigned int page_size; | 275 | extern unsigned int page_size; |
273 | 276 | ||
274 | #endif | 277 | struct winsize; |
278 | void get_term_dimensions(struct winsize *ws); | ||
279 | #endif /* GIT_COMPAT_UTIL_H */ | ||