diff options
Diffstat (limited to 'tools')
97 files changed, 7300 insertions, 4135 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index cb43289e447f..416684be0ad3 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
@@ -1,4 +1,3 @@ | |||
1 | PERF-BUILD-OPTIONS | ||
2 | PERF-CFLAGS | 1 | PERF-CFLAGS |
3 | PERF-GUI-VARS | 2 | PERF-GUI-VARS |
4 | PERF-VERSION-FILE | 3 | PERF-VERSION-FILE |
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index bd498d496952..4626a398836a 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -178,8 +178,8 @@ install-pdf: pdf | |||
178 | $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) | 178 | $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) |
179 | $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir) | 179 | $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir) |
180 | 180 | ||
181 | install-html: html | 181 | #install-html: html |
182 | '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) | 182 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) |
183 | 183 | ||
184 | ../PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 184 | ../PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
185 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) PERF-VERSION-FILE | 185 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) PERF-VERSION-FILE |
@@ -288,15 +288,16 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt | |||
288 | sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \ | 288 | sed -e '1,/^$$/d' $< | $(ASCIIDOC) -b xhtml11 - >$@+ && \ |
289 | mv $@+ $@ | 289 | mv $@+ $@ |
290 | 290 | ||
291 | install-webdoc : html | 291 | # UNIMPLEMENTED |
292 | '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST) | 292 | #install-webdoc : html |
293 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(WEBDOC_DEST) | ||
293 | 294 | ||
294 | quick-install: quick-install-man | 295 | # quick-install: quick-install-man |
295 | 296 | ||
296 | quick-install-man: | 297 | # quick-install-man: |
297 | '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir) | 298 | # '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir) |
298 | 299 | ||
299 | quick-install-html: | 300 | #quick-install-html: |
300 | '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir) | 301 | # '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir) |
301 | 302 | ||
302 | .PHONY: .FORCE-PERF-VERSION-FILE | 303 | .PHONY: .FORCE-PERF-VERSION-FILE |
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt new file mode 100644 index 000000000000..0cada9e053dc --- /dev/null +++ b/tools/perf/Documentation/perf-evlist.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | perf-evlist(1) | ||
2 | ============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-evlist - List the event names in a perf.data file | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf evlist <options>' | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command displays the names of events sampled in a perf.data file. | ||
16 | |||
17 | OPTIONS | ||
18 | ------- | ||
19 | -i:: | ||
20 | --input=:: | ||
21 | Input file name. (default: perf.data) | ||
22 | |||
23 | SEE ALSO | ||
24 | -------- | ||
25 | linkperf:perf-record[1], linkperf:perf-list[1], | ||
26 | linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 399751befeed..7a527f7e9da9 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -8,7 +8,7 @@ perf-list - List all symbolic event types | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf list' | 11 | 'perf list' [hw|sw|cache|tracepoint|event_glob] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
@@ -63,7 +63,26 @@ details. Some of them are referenced in the SEE ALSO section below. | |||
63 | 63 | ||
64 | OPTIONS | 64 | OPTIONS |
65 | ------- | 65 | ------- |
66 | None | 66 | |
67 | Without options all known events will be listed. | ||
68 | |||
69 | To limit the list use: | ||
70 | |||
71 | . 'hw' or 'hardware' to list hardware events such as cache-misses, etc. | ||
72 | |||
73 | . 'sw' or 'software' to list software events such as context switches, etc. | ||
74 | |||
75 | . 'cache' or 'hwcache' to list hardware cache events such as L1-dcache-loads, etc. | ||
76 | |||
77 | . 'tracepoint' to list all tracepoint events, alternatively use | ||
78 | 'subsys_glob:event_glob' to filter by tracepoint subsystems such as sched, | ||
79 | block, etc. | ||
80 | |||
81 | . If none of the above is matched, it will apply the supplied glob to all | ||
82 | events, printing the ones that match. | ||
83 | |||
84 | One or more types can be used at the same time, listing the events for the | ||
85 | types specified. | ||
67 | 86 | ||
68 | SEE ALSO | 87 | SEE ALSO |
69 | -------- | 88 | -------- |
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index 921de259ea10..4a26a2f3a6a3 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt | |||
@@ -24,8 +24,8 @@ and statistics with this 'perf lock' command. | |||
24 | 24 | ||
25 | 'perf lock report' reports statistical data. | 25 | 'perf lock report' reports statistical data. |
26 | 26 | ||
27 | OPTIONS | 27 | COMMON OPTIONS |
28 | ------- | 28 | -------------- |
29 | 29 | ||
30 | -i:: | 30 | -i:: |
31 | --input=<file>:: | 31 | --input=<file>:: |
@@ -39,6 +39,14 @@ OPTIONS | |||
39 | --dump-raw-trace:: | 39 | --dump-raw-trace:: |
40 | Dump raw trace in ASCII. | 40 | Dump raw trace in ASCII. |
41 | 41 | ||
42 | REPORT OPTIONS | ||
43 | -------------- | ||
44 | |||
45 | -k:: | ||
46 | --key=<value>:: | ||
47 | Sorting key. Possible values: acquired (default), contended, | ||
48 | wait_total, wait_max, wait_min. | ||
49 | |||
42 | SEE ALSO | 50 | SEE ALSO |
43 | -------- | 51 | -------- |
44 | linkperf:perf[1] | 52 | linkperf:perf[1] |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 86b797a35aa6..02bafce4b341 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -16,7 +16,7 @@ or | |||
16 | or | 16 | or |
17 | 'perf probe' --list | 17 | 'perf probe' --list |
18 | or | 18 | or |
19 | 'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' | 19 | 'perf probe' [options] --line='LINE' |
20 | or | 20 | or |
21 | 'perf probe' [options] --vars='PROBEPOINT' | 21 | 'perf probe' [options] --vars='PROBEPOINT' |
22 | 22 | ||
@@ -73,6 +73,17 @@ OPTIONS | |||
73 | (Only for --vars) Show external defined variables in addition to local | 73 | (Only for --vars) Show external defined variables in addition to local |
74 | variables. | 74 | variables. |
75 | 75 | ||
76 | -F:: | ||
77 | --funcs:: | ||
78 | Show available functions in given module or kernel. | ||
79 | |||
80 | --filter=FILTER:: | ||
81 | (Only for --vars and --funcs) Set filter. FILTER is a combination of glob | ||
82 | pattern, see FILTER PATTERN for detail. | ||
83 | Default FILTER is "!__k???tab_* & !__crc_*" for --vars, and "!_*" | ||
84 | for --funcs. | ||
85 | If several filters are specified, only the last filter is used. | ||
86 | |||
76 | -f:: | 87 | -f:: |
77 | --force:: | 88 | --force:: |
78 | Forcibly add events with existing name. | 89 | Forcibly add events with existing name. |
@@ -117,13 +128,14 @@ LINE SYNTAX | |||
117 | ----------- | 128 | ----------- |
118 | Line range is described by following syntax. | 129 | Line range is described by following syntax. |
119 | 130 | ||
120 | "FUNC[:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]" | 131 | "FUNC[@SRC][:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]" |
121 | 132 | ||
122 | FUNC specifies the function name of showing lines. 'RLN' is the start line | 133 | FUNC specifies the function name of showing lines. 'RLN' is the start line |
123 | number from function entry line, and 'RLN2' is the end line number. As same as | 134 | number from function entry line, and 'RLN2' is the end line number. As same as |
124 | probe syntax, 'SRC' means the source file path, 'ALN' is start line number, | 135 | probe syntax, 'SRC' means the source file path, 'ALN' is start line number, |
125 | and 'ALN2' is end line number in the file. It is also possible to specify how | 136 | and 'ALN2' is end line number in the file. It is also possible to specify how |
126 | many lines to show by using 'NUM'. | 137 | many lines to show by using 'NUM'. Moreover, 'FUNC@SRC' combination is good |
138 | for searching a specific function when several functions share same name. | ||
127 | So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. | 139 | So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. |
128 | 140 | ||
129 | LAZY MATCHING | 141 | LAZY MATCHING |
@@ -135,6 +147,14 @@ e.g. | |||
135 | 147 | ||
136 | This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.) | 148 | This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.) |
137 | 149 | ||
150 | FILTER PATTERN | ||
151 | -------------- | ||
152 | The filter pattern is a glob matching pattern(s) to filter variables. | ||
153 | In addition, you can use "!" for specifying filter-out rule. You also can give several rules combined with "&" or "|", and fold those rules as one rule by using "(" ")". | ||
154 | |||
155 | e.g. | ||
156 | With --filter "foo* | bar*", perf probe -V shows variables which start with "foo" or "bar". | ||
157 | With --filter "!foo* & *bar", perf probe -V shows variables which don't start with "foo" and end with "bar", like "fizzbar". But "foobar" is filtered out. | ||
138 | 158 | ||
139 | EXAMPLES | 159 | EXAMPLES |
140 | -------- | 160 | -------- |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index e032716c839b..5a520f825295 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -137,6 +137,17 @@ Do not update the builid cache. This saves some overhead in situations | |||
137 | where the information in the perf.data file (which includes buildids) | 137 | where the information in the perf.data file (which includes buildids) |
138 | is sufficient. | 138 | is sufficient. |
139 | 139 | ||
140 | -G name,...:: | ||
141 | --cgroup name,...:: | ||
142 | monitor only in the container (cgroup) called "name". This option is available only | ||
143 | in per-cpu mode. The cgroup filesystem must be mounted. All threads belonging to | ||
144 | container "name" are monitored when they run on the monitored CPUs. Multiple cgroups | ||
145 | can be provided. Each cgroup is applied to the corresponding event, i.e., first cgroup | ||
146 | to first event, second cgroup to second event and so on. It is possible to provide | ||
147 | an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have | ||
148 | corresponding events, i.e., they always refer to events defined earlier on the command | ||
149 | line. | ||
150 | |||
140 | SEE ALSO | 151 | SEE ALSO |
141 | -------- | 152 | -------- |
142 | linkperf:perf-stat[1], linkperf:perf-list[1] | 153 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 29ad94293cd2..66f040b30729 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -112,6 +112,28 @@ OPTIONS | |||
112 | --debug-mode:: | 112 | --debug-mode:: |
113 | Do various checks like samples ordering and lost events. | 113 | Do various checks like samples ordering and lost events. |
114 | 114 | ||
115 | -f:: | ||
116 | --fields | ||
117 | Comma separated list of fields to print. Options are: | ||
118 | comm, tid, pid, time, cpu, event, trace, sym. Field | ||
119 | list must be prepended with the type, trace, sw or hw, | ||
120 | to indicate to which event type the field list applies. | ||
121 | e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace | ||
122 | |||
123 | -k:: | ||
124 | --vmlinux=<file>:: | ||
125 | vmlinux pathname | ||
126 | |||
127 | --kallsyms=<file>:: | ||
128 | kallsyms pathname | ||
129 | |||
130 | --symfs=<directory>:: | ||
131 | Look for files with symbols relative to this directory. | ||
132 | |||
133 | -G:: | ||
134 | --hide-call-graph:: | ||
135 | When printing symbols do not display call chain. | ||
136 | |||
115 | SEE ALSO | 137 | SEE ALSO |
116 | -------- | 138 | -------- |
117 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 139 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index b6da7affbbee..918cc38ee6d1 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -83,6 +83,17 @@ This option is only valid in system-wide mode. | |||
83 | print counts using a CSV-style output to make it easy to import directly into | 83 | print counts using a CSV-style output to make it easy to import directly into |
84 | spreadsheets. Columns are separated by the string specified in SEP. | 84 | spreadsheets. Columns are separated by the string specified in SEP. |
85 | 85 | ||
86 | -G name:: | ||
87 | --cgroup name:: | ||
88 | monitor only in the container (cgroup) called "name". This option is available only | ||
89 | in per-cpu mode. The cgroup filesystem must be mounted. All threads belonging to | ||
90 | container "name" are monitored when they run on the monitored CPUs. Multiple cgroups | ||
91 | can be provided. Each cgroup is applied to the corresponding event, i.e., first cgroup | ||
92 | to first event, second cgroup to second event and so on. It is possible to provide | ||
93 | an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must have | ||
94 | corresponding events, i.e., they always refer to events defined earlier on the command | ||
95 | line. | ||
96 | |||
86 | EXAMPLES | 97 | EXAMPLES |
87 | -------- | 98 | -------- |
88 | 99 | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7141c42e1469..158c30e8210c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -3,7 +3,7 @@ ifeq ("$(origin O)", "command line") | |||
3 | endif | 3 | endif |
4 | 4 | ||
5 | # The default target of this Makefile is... | 5 | # The default target of this Makefile is... |
6 | all:: | 6 | all: |
7 | 7 | ||
8 | ifneq ($(OUTPUT),) | 8 | ifneq ($(OUTPUT),) |
9 | # check that the output directory actually exists | 9 | # check that the output directory actually exists |
@@ -11,152 +11,12 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) | |||
11 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) | 11 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) |
12 | endif | 12 | endif |
13 | 13 | ||
14 | # Define V=1 to have a more verbose compile. | 14 | # Define V to have a more verbose compile. |
15 | # Define V=2 to have an even more verbose compile. | ||
16 | # | ||
17 | # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() | ||
18 | # or vsnprintf() return -1 instead of number of characters which would | ||
19 | # have been written to the final string if enough space had been available. | ||
20 | # | ||
21 | # Define FREAD_READS_DIRECTORIES if your are on a system which succeeds | ||
22 | # when attempting to read from an fopen'ed directory. | ||
23 | # | ||
24 | # Define NO_OPENSSL environment variable if you do not have OpenSSL. | ||
25 | # This also implies MOZILLA_SHA1. | ||
26 | # | ||
27 | # Define CURLDIR=/foo/bar if your curl header and library files are in | ||
28 | # /foo/bar/include and /foo/bar/lib directories. | ||
29 | # | ||
30 | # Define EXPATDIR=/foo/bar if your expat header and library files are in | ||
31 | # /foo/bar/include and /foo/bar/lib directories. | ||
32 | # | ||
33 | # Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent. | ||
34 | # | ||
35 | # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks | ||
36 | # d_type in struct dirent (latest Cygwin -- will be fixed soonish). | ||
37 | # | ||
38 | # Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.) | ||
39 | # do not support the 'size specifiers' introduced by C99, namely ll, hh, | ||
40 | # j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t). | ||
41 | # some C compilers supported these specifiers prior to C99 as an extension. | ||
42 | # | ||
43 | # Define NO_STRCASESTR if you don't have strcasestr. | ||
44 | # | ||
45 | # Define NO_MEMMEM if you don't have memmem. | ||
46 | # | ||
47 | # Define NO_STRTOUMAX if you don't have strtoumax in the C library. | ||
48 | # If your compiler also does not support long long or does not have | ||
49 | # strtoull, define NO_STRTOULL. | ||
50 | # | ||
51 | # Define NO_SETENV if you don't have setenv in the C library. | ||
52 | # | ||
53 | # Define NO_UNSETENV if you don't have unsetenv in the C library. | ||
54 | # | ||
55 | # Define NO_MKDTEMP if you don't have mkdtemp in the C library. | ||
56 | # | ||
57 | # Define NO_SYS_SELECT_H if you don't have sys/select.h. | ||
58 | # | ||
59 | # Define NO_SYMLINK_HEAD if you never want .perf/HEAD to be a symbolic link. | ||
60 | # Enable it on Windows. By default, symrefs are still used. | ||
61 | # | ||
62 | # Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability | ||
63 | # tests. These tests take up a significant amount of the total test time | ||
64 | # but are not needed unless you plan to talk to SVN repos. | ||
65 | # | ||
66 | # Define NO_FINK if you are building on Darwin/Mac OS X, have Fink | ||
67 | # installed in /sw, but don't want PERF to link against any libraries | ||
68 | # installed there. If defined you may specify your own (or Fink's) | ||
69 | # include directories and library directories by defining CFLAGS | ||
70 | # and LDFLAGS appropriately. | ||
71 | # | ||
72 | # Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X, | ||
73 | # have DarwinPorts installed in /opt/local, but don't want PERF to | ||
74 | # link against any libraries installed there. If defined you may | ||
75 | # specify your own (or DarwinPort's) include directories and | ||
76 | # library directories by defining CFLAGS and LDFLAGS appropriately. | ||
77 | # | ||
78 | # Define PPC_SHA1 environment variable when running make to make use of | ||
79 | # a bundled SHA1 routine optimized for PowerPC. | ||
80 | # | ||
81 | # Define ARM_SHA1 environment variable when running make to make use of | ||
82 | # a bundled SHA1 routine optimized for ARM. | ||
83 | # | ||
84 | # Define MOZILLA_SHA1 environment variable when running make to make use of | ||
85 | # a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast | ||
86 | # on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default | ||
87 | # choice) has very fast version optimized for i586. | ||
88 | # | ||
89 | # Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin). | ||
90 | # | ||
91 | # Define NEEDS_LIBICONV if linking with libc is not enough (Darwin). | ||
92 | # | ||
93 | # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, | ||
94 | # Patrick Mauritz). | ||
95 | # | ||
96 | # Define NO_MMAP if you want to avoid mmap. | ||
97 | # | ||
98 | # Define NO_PTHREADS if you do not have or do not want to use Pthreads. | ||
99 | # | ||
100 | # Define NO_PREAD if you have a problem with pread() system call (e.g. | ||
101 | # cygwin.dll before v1.5.22). | ||
102 | # | ||
103 | # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is | ||
104 | # generally faster on your platform than accessing the working directory. | ||
105 | # | ||
106 | # Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support | ||
107 | # the executable mode bit, but doesn't really do so. | ||
108 | # | ||
109 | # Define NO_IPV6 if you lack IPv6 support and getaddrinfo(). | ||
110 | # | ||
111 | # Define NO_SOCKADDR_STORAGE if your platform does not have struct | ||
112 | # sockaddr_storage. | ||
113 | # | ||
114 | # Define NO_ICONV if your libc does not properly support iconv. | ||
115 | # | ||
116 | # Define OLD_ICONV if your library has an old iconv(), where the second | ||
117 | # (input buffer pointer) parameter is declared with type (const char **). | ||
118 | # | ||
119 | # Define NO_DEFLATE_BOUND if your zlib does not have deflateBound. | ||
120 | # | ||
121 | # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib" | ||
122 | # that tells runtime paths to dynamic libraries; | ||
123 | # "-Wl,-rpath=/path/lib" is used instead. | ||
124 | # | ||
125 | # Define USE_NSEC below if you want perf to care about sub-second file mtimes | ||
126 | # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and | ||
127 | # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely | ||
128 | # randomly break unless your underlying filesystem supports those sub-second | ||
129 | # times (my ext3 doesn't). | ||
130 | # | ||
131 | # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of | ||
132 | # "st_ctim" | ||
133 | # | ||
134 | # Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" | ||
135 | # available. This automatically turns USE_NSEC off. | ||
136 | # | ||
137 | # Define USE_STDEV below if you want perf to care about the underlying device | ||
138 | # change being considered an inode change from the update-index perspective. | ||
139 | # | ||
140 | # Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks | ||
141 | # field that counts the on-disk footprint in 512-byte blocks. | ||
142 | # | 15 | # |
143 | # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 | 16 | # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 |
144 | # | 17 | # |
145 | # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. | 18 | # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. |
146 | # | 19 | # |
147 | # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's | ||
148 | # MakeMaker (e.g. using ActiveState under Cygwin). | ||
149 | # | ||
150 | # Define NO_PERL if you do not want Perl scripts or libraries at all. | ||
151 | # | ||
152 | # Define INTERNAL_QSORT to use Git's implementation of qsort(), which | ||
153 | # is a simplified version of the merge sort used in glibc. This is | ||
154 | # recommended if Git triggers O(n^2) behavior in your platform's qsort(). | ||
155 | # | ||
156 | # Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call | ||
157 | # your external grep (e.g., if your system lacks grep, if its grep is | ||
158 | # broken, or spawning external process is slower than built-in grep perf has). | ||
159 | # | ||
160 | # Define LDFLAGS=-static to build a static binary. | 20 | # Define LDFLAGS=-static to build a static binary. |
161 | # | 21 | # |
162 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. | 22 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. |
@@ -167,12 +27,7 @@ $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | |||
167 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 27 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
168 | -include $(OUTPUT)PERF-VERSION-FILE | 28 | -include $(OUTPUT)PERF-VERSION-FILE |
169 | 29 | ||
170 | uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') | 30 | uname_M := $(shell uname -m 2>/dev/null || echo not) |
171 | uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') | ||
172 | uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') | ||
173 | uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') | ||
174 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') | ||
175 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') | ||
176 | 31 | ||
177 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ | 32 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ |
178 | -e s/arm.*/arm/ -e s/sa110/arm/ \ | 33 | -e s/arm.*/arm/ -e s/sa110/arm/ \ |
@@ -191,8 +46,6 @@ ifeq ($(ARCH),x86_64) | |||
191 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S | 46 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S |
192 | endif | 47 | endif |
193 | 48 | ||
194 | # CFLAGS and LDFLAGS are for the users to override from the command line. | ||
195 | |||
196 | # | 49 | # |
197 | # Include saner warnings here, which can catch bugs: | 50 | # Include saner warnings here, which can catch bugs: |
198 | # | 51 | # |
@@ -270,22 +123,13 @@ CC = $(CROSS_COMPILE)gcc | |||
270 | AR = $(CROSS_COMPILE)ar | 123 | AR = $(CROSS_COMPILE)ar |
271 | RM = rm -f | 124 | RM = rm -f |
272 | MKDIR = mkdir | 125 | MKDIR = mkdir |
273 | TAR = tar | ||
274 | FIND = find | 126 | FIND = find |
275 | INSTALL = install | 127 | INSTALL = install |
276 | RPMBUILD = rpmbuild | ||
277 | PTHREAD_LIBS = -lpthread | ||
278 | 128 | ||
279 | # sparse is architecture-neutral, which means that we need to tell it | 129 | # sparse is architecture-neutral, which means that we need to tell it |
280 | # explicitly what architecture to check for. Fix this up for yours.. | 130 | # explicitly what architecture to check for. Fix this up for yours.. |
281 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | 131 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
282 | 132 | ||
283 | ifeq ($(V), 2) | ||
284 | QUIET_STDERR = ">/dev/null" | ||
285 | else | ||
286 | QUIET_STDERR = ">/dev/null 2>&1" | ||
287 | endif | ||
288 | |||
289 | -include feature-tests.mak | 133 | -include feature-tests.mak |
290 | 134 | ||
291 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) | 135 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) |
@@ -310,49 +154,37 @@ BASIC_LDFLAGS = | |||
310 | 154 | ||
311 | # Guard against environment variables | 155 | # Guard against environment variables |
312 | BUILTIN_OBJS = | 156 | BUILTIN_OBJS = |
313 | BUILT_INS = | ||
314 | COMPAT_CFLAGS = | ||
315 | COMPAT_OBJS = | ||
316 | LIB_H = | 157 | LIB_H = |
317 | LIB_OBJS = | 158 | LIB_OBJS = |
318 | SCRIPT_PERL = | 159 | PYRF_OBJS = |
319 | SCRIPT_SH = | 160 | SCRIPT_SH = |
320 | TEST_PROGRAMS = | ||
321 | 161 | ||
322 | SCRIPT_SH += perf-archive.sh | 162 | SCRIPT_SH += perf-archive.sh |
323 | 163 | ||
324 | grep-libs = $(filter -l%,$(1)) | 164 | grep-libs = $(filter -l%,$(1)) |
325 | strip-libs = $(filter-out -l%,$(1)) | 165 | strip-libs = $(filter-out -l%,$(1)) |
326 | 166 | ||
167 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) | ||
168 | $(QUIET_GEN)python util/setup.py --quiet build_ext --build-lib='$(OUTPUT)python' \ | ||
169 | --build-temp='$(OUTPUT)python/temp' | ||
327 | # | 170 | # |
328 | # No Perl scripts right now: | 171 | # No Perl scripts right now: |
329 | # | 172 | # |
330 | 173 | ||
331 | # SCRIPT_PERL += perf-add--interactive.perl | 174 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) |
332 | |||
333 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ | ||
334 | $(patsubst %.perl,%,$(SCRIPT_PERL)) | ||
335 | |||
336 | # Empty... | ||
337 | EXTRA_PROGRAMS = | ||
338 | |||
339 | # ... and all the rest that could be moved out of bindir to perfexecdir | ||
340 | PROGRAMS += $(EXTRA_PROGRAMS) | ||
341 | 175 | ||
342 | # | 176 | # |
343 | # Single 'perf' binary right now: | 177 | # Single 'perf' binary right now: |
344 | # | 178 | # |
345 | PROGRAMS += $(OUTPUT)perf | 179 | PROGRAMS += $(OUTPUT)perf |
346 | 180 | ||
347 | # List built-in command $C whose implementation cmd_$C() is not in | 181 | LANG_BINDINGS = |
348 | # builtin-$C.o but is linked in as part of some other command. | ||
349 | # | ||
350 | 182 | ||
351 | # what 'all' will build and 'install' will install, in perfexecdir | 183 | # what 'all' will build and 'install' will install, in perfexecdir |
352 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 184 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
353 | 185 | ||
354 | # what 'all' will build but not install in perfexecdir | 186 | # what 'all' will build but not install in perfexecdir |
355 | OTHER_PROGRAMS = $(OUTPUT)perf$X | 187 | OTHER_PROGRAMS = $(OUTPUT)perf |
356 | 188 | ||
357 | # Set paths to tools early so that they can be used for version tests. | 189 | # Set paths to tools early so that they can be used for version tests. |
358 | ifndef SHELL_PATH | 190 | ifndef SHELL_PATH |
@@ -395,6 +227,7 @@ LIB_H += util/include/dwarf-regs.h | |||
395 | LIB_H += util/include/asm/dwarf2.h | 227 | LIB_H += util/include/asm/dwarf2.h |
396 | LIB_H += util/include/asm/cpufeature.h | 228 | LIB_H += util/include/asm/cpufeature.h |
397 | LIB_H += perf.h | 229 | LIB_H += perf.h |
230 | LIB_H += util/annotate.h | ||
398 | LIB_H += util/cache.h | 231 | LIB_H += util/cache.h |
399 | LIB_H += util/callchain.h | 232 | LIB_H += util/callchain.h |
400 | LIB_H += util/build-id.h | 233 | LIB_H += util/build-id.h |
@@ -402,6 +235,7 @@ LIB_H += util/debug.h | |||
402 | LIB_H += util/debugfs.h | 235 | LIB_H += util/debugfs.h |
403 | LIB_H += util/event.h | 236 | LIB_H += util/event.h |
404 | LIB_H += util/evsel.h | 237 | LIB_H += util/evsel.h |
238 | LIB_H += util/evlist.h | ||
405 | LIB_H += util/exec_cmd.h | 239 | LIB_H += util/exec_cmd.h |
406 | LIB_H += util/types.h | 240 | LIB_H += util/types.h |
407 | LIB_H += util/levenshtein.h | 241 | LIB_H += util/levenshtein.h |
@@ -416,6 +250,7 @@ LIB_H += util/help.h | |||
416 | LIB_H += util/session.h | 250 | LIB_H += util/session.h |
417 | LIB_H += util/strbuf.h | 251 | LIB_H += util/strbuf.h |
418 | LIB_H += util/strlist.h | 252 | LIB_H += util/strlist.h |
253 | LIB_H += util/strfilter.h | ||
419 | LIB_H += util/svghelper.h | 254 | LIB_H += util/svghelper.h |
420 | LIB_H += util/run-command.h | 255 | LIB_H += util/run-command.h |
421 | LIB_H += util/sigchain.h | 256 | LIB_H += util/sigchain.h |
@@ -425,21 +260,26 @@ LIB_H += util/values.h | |||
425 | LIB_H += util/sort.h | 260 | LIB_H += util/sort.h |
426 | LIB_H += util/hist.h | 261 | LIB_H += util/hist.h |
427 | LIB_H += util/thread.h | 262 | LIB_H += util/thread.h |
263 | LIB_H += util/thread_map.h | ||
428 | LIB_H += util/trace-event.h | 264 | LIB_H += util/trace-event.h |
429 | LIB_H += util/probe-finder.h | 265 | LIB_H += util/probe-finder.h |
430 | LIB_H += util/probe-event.h | 266 | LIB_H += util/probe-event.h |
431 | LIB_H += util/pstack.h | 267 | LIB_H += util/pstack.h |
432 | LIB_H += util/cpumap.h | 268 | LIB_H += util/cpumap.h |
269 | LIB_H += util/top.h | ||
433 | LIB_H += $(ARCH_INCLUDE) | 270 | LIB_H += $(ARCH_INCLUDE) |
271 | LIB_H += util/cgroup.h | ||
434 | 272 | ||
435 | LIB_OBJS += $(OUTPUT)util/abspath.o | 273 | LIB_OBJS += $(OUTPUT)util/abspath.o |
436 | LIB_OBJS += $(OUTPUT)util/alias.o | 274 | LIB_OBJS += $(OUTPUT)util/alias.o |
275 | LIB_OBJS += $(OUTPUT)util/annotate.o | ||
437 | LIB_OBJS += $(OUTPUT)util/build-id.o | 276 | LIB_OBJS += $(OUTPUT)util/build-id.o |
438 | LIB_OBJS += $(OUTPUT)util/config.o | 277 | LIB_OBJS += $(OUTPUT)util/config.o |
439 | LIB_OBJS += $(OUTPUT)util/ctype.o | 278 | LIB_OBJS += $(OUTPUT)util/ctype.o |
440 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 279 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
441 | LIB_OBJS += $(OUTPUT)util/environment.o | 280 | LIB_OBJS += $(OUTPUT)util/environment.o |
442 | LIB_OBJS += $(OUTPUT)util/event.o | 281 | LIB_OBJS += $(OUTPUT)util/event.o |
282 | LIB_OBJS += $(OUTPUT)util/evlist.o | ||
443 | LIB_OBJS += $(OUTPUT)util/evsel.o | 283 | LIB_OBJS += $(OUTPUT)util/evsel.o |
444 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 284 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
445 | LIB_OBJS += $(OUTPUT)util/help.o | 285 | LIB_OBJS += $(OUTPUT)util/help.o |
@@ -455,6 +295,8 @@ LIB_OBJS += $(OUTPUT)util/quote.o | |||
455 | LIB_OBJS += $(OUTPUT)util/strbuf.o | 295 | LIB_OBJS += $(OUTPUT)util/strbuf.o |
456 | LIB_OBJS += $(OUTPUT)util/string.o | 296 | LIB_OBJS += $(OUTPUT)util/string.o |
457 | LIB_OBJS += $(OUTPUT)util/strlist.o | 297 | LIB_OBJS += $(OUTPUT)util/strlist.o |
298 | LIB_OBJS += $(OUTPUT)util/strfilter.o | ||
299 | LIB_OBJS += $(OUTPUT)util/top.o | ||
458 | LIB_OBJS += $(OUTPUT)util/usage.o | 300 | LIB_OBJS += $(OUTPUT)util/usage.o |
459 | LIB_OBJS += $(OUTPUT)util/wrapper.o | 301 | LIB_OBJS += $(OUTPUT)util/wrapper.o |
460 | LIB_OBJS += $(OUTPUT)util/sigchain.o | 302 | LIB_OBJS += $(OUTPUT)util/sigchain.o |
@@ -469,6 +311,7 @@ LIB_OBJS += $(OUTPUT)util/map.o | |||
469 | LIB_OBJS += $(OUTPUT)util/pstack.o | 311 | LIB_OBJS += $(OUTPUT)util/pstack.o |
470 | LIB_OBJS += $(OUTPUT)util/session.o | 312 | LIB_OBJS += $(OUTPUT)util/session.o |
471 | LIB_OBJS += $(OUTPUT)util/thread.o | 313 | LIB_OBJS += $(OUTPUT)util/thread.o |
314 | LIB_OBJS += $(OUTPUT)util/thread_map.o | ||
472 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 315 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
473 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 316 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
474 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 317 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
@@ -480,6 +323,7 @@ LIB_OBJS += $(OUTPUT)util/probe-event.o | |||
480 | LIB_OBJS += $(OUTPUT)util/util.o | 323 | LIB_OBJS += $(OUTPUT)util/util.o |
481 | LIB_OBJS += $(OUTPUT)util/xyarray.o | 324 | LIB_OBJS += $(OUTPUT)util/xyarray.o |
482 | LIB_OBJS += $(OUTPUT)util/cpumap.o | 325 | LIB_OBJS += $(OUTPUT)util/cpumap.o |
326 | LIB_OBJS += $(OUTPUT)util/cgroup.o | ||
483 | 327 | ||
484 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 328 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
485 | 329 | ||
@@ -494,6 +338,7 @@ endif | |||
494 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o | 338 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o |
495 | 339 | ||
496 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o | 340 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o |
341 | BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o | ||
497 | BUILTIN_OBJS += $(OUTPUT)builtin-help.o | 342 | BUILTIN_OBJS += $(OUTPUT)builtin-help.o |
498 | BUILTIN_OBJS += $(OUTPUT)builtin-sched.o | 343 | BUILTIN_OBJS += $(OUTPUT)builtin-sched.o |
499 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o | 344 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o |
@@ -514,6 +359,20 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | |||
514 | 359 | ||
515 | PERFLIBS = $(LIB_FILE) | 360 | PERFLIBS = $(LIB_FILE) |
516 | 361 | ||
362 | # Files needed for the python binding, perf.so | ||
363 | # pyrf is just an internal name needed for all those wrappers. | ||
364 | # This has to be in sync with what is in the 'sources' variable in | ||
365 | # tools/perf/util/setup.py | ||
366 | |||
367 | PYRF_OBJS += $(OUTPUT)util/cpumap.o | ||
368 | PYRF_OBJS += $(OUTPUT)util/ctype.o | ||
369 | PYRF_OBJS += $(OUTPUT)util/evlist.o | ||
370 | PYRF_OBJS += $(OUTPUT)util/evsel.o | ||
371 | PYRF_OBJS += $(OUTPUT)util/python.o | ||
372 | PYRF_OBJS += $(OUTPUT)util/thread_map.o | ||
373 | PYRF_OBJS += $(OUTPUT)util/util.o | ||
374 | PYRF_OBJS += $(OUTPUT)util/xyarray.o | ||
375 | |||
517 | # | 376 | # |
518 | # Platform specific tweaks | 377 | # Platform specific tweaks |
519 | # | 378 | # |
@@ -535,22 +394,6 @@ endif # NO_DWARF | |||
535 | 394 | ||
536 | -include arch/$(ARCH)/Makefile | 395 | -include arch/$(ARCH)/Makefile |
537 | 396 | ||
538 | ifeq ($(uname_S),Darwin) | ||
539 | ifndef NO_FINK | ||
540 | ifeq ($(shell test -d /sw/lib && echo y),y) | ||
541 | BASIC_CFLAGS += -I/sw/include | ||
542 | BASIC_LDFLAGS += -L/sw/lib | ||
543 | endif | ||
544 | endif | ||
545 | ifndef NO_DARWIN_PORTS | ||
546 | ifeq ($(shell test -d /opt/local/lib && echo y),y) | ||
547 | BASIC_CFLAGS += -I/opt/local/include | ||
548 | BASIC_LDFLAGS += -L/opt/local/lib | ||
549 | endif | ||
550 | endif | ||
551 | PTHREAD_LIBS = | ||
552 | endif | ||
553 | |||
554 | ifneq ($(OUTPUT),) | 397 | ifneq ($(OUTPUT),) |
555 | BASIC_CFLAGS += -I$(OUTPUT) | 398 | BASIC_CFLAGS += -I$(OUTPUT) |
556 | endif | 399 | endif |
@@ -595,6 +438,7 @@ else | |||
595 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o | 438 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o |
596 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o | 439 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o |
597 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o | 440 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o |
441 | LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o | ||
598 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o | 442 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o |
599 | LIB_OBJS += $(OUTPUT)util/ui/progress.o | 443 | LIB_OBJS += $(OUTPUT)util/ui/progress.o |
600 | LIB_OBJS += $(OUTPUT)util/ui/util.o | 444 | LIB_OBJS += $(OUTPUT)util/ui/util.o |
@@ -604,6 +448,7 @@ else | |||
604 | LIB_H += util/ui/libslang.h | 448 | LIB_H += util/ui/libslang.h |
605 | LIB_H += util/ui/progress.h | 449 | LIB_H += util/ui/progress.h |
606 | LIB_H += util/ui/util.h | 450 | LIB_H += util/ui/util.h |
451 | LIB_H += util/ui/ui.h | ||
607 | endif | 452 | endif |
608 | endif | 453 | endif |
609 | 454 | ||
@@ -635,12 +480,14 @@ else | |||
635 | PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` | 480 | PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` |
636 | FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | 481 | FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) |
637 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) | 482 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) |
483 | msg := $(warning No Python.h found, install python-dev[el] to have python support in 'perf script' and to build the python bindings) | ||
638 | BASIC_CFLAGS += -DNO_LIBPYTHON | 484 | BASIC_CFLAGS += -DNO_LIBPYTHON |
639 | else | 485 | else |
640 | ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) | 486 | ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) |
641 | EXTLIBS += $(PYTHON_EMBED_LIBADD) | 487 | EXTLIBS += $(PYTHON_EMBED_LIBADD) |
642 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o | 488 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o |
643 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o | 489 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o |
490 | LANG_BINDINGS += $(OUTPUT)python/perf.so | ||
644 | endif | 491 | endif |
645 | endif | 492 | endif |
646 | 493 | ||
@@ -690,201 +537,13 @@ else | |||
690 | endif | 537 | endif |
691 | endif | 538 | endif |
692 | 539 | ||
693 | ifndef CC_LD_DYNPATH | ||
694 | ifdef NO_R_TO_GCC_LINKER | ||
695 | # Some gcc does not accept and pass -R to the linker to specify | ||
696 | # the runtime dynamic library path. | ||
697 | CC_LD_DYNPATH = -Wl,-rpath, | ||
698 | else | ||
699 | CC_LD_DYNPATH = -R | ||
700 | endif | ||
701 | endif | ||
702 | |||
703 | ifdef NEEDS_SOCKET | ||
704 | EXTLIBS += -lsocket | ||
705 | endif | ||
706 | ifdef NEEDS_NSL | ||
707 | EXTLIBS += -lnsl | ||
708 | endif | ||
709 | ifdef NO_D_TYPE_IN_DIRENT | ||
710 | BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT | ||
711 | endif | ||
712 | ifdef NO_D_INO_IN_DIRENT | ||
713 | BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT | ||
714 | endif | ||
715 | ifdef NO_ST_BLOCKS_IN_STRUCT_STAT | ||
716 | BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT | ||
717 | endif | ||
718 | ifdef USE_NSEC | ||
719 | BASIC_CFLAGS += -DUSE_NSEC | ||
720 | endif | ||
721 | ifdef USE_ST_TIMESPEC | ||
722 | BASIC_CFLAGS += -DUSE_ST_TIMESPEC | ||
723 | endif | ||
724 | ifdef NO_NSEC | ||
725 | BASIC_CFLAGS += -DNO_NSEC | ||
726 | endif | ||
727 | ifdef NO_C99_FORMAT | ||
728 | BASIC_CFLAGS += -DNO_C99_FORMAT | ||
729 | endif | ||
730 | ifdef SNPRINTF_RETURNS_BOGUS | ||
731 | COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS | ||
732 | COMPAT_OBJS += $(OUTPUT)compat/snprintf.o | ||
733 | endif | ||
734 | ifdef FREAD_READS_DIRECTORIES | ||
735 | COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES | ||
736 | COMPAT_OBJS += $(OUTPUT)compat/fopen.o | ||
737 | endif | ||
738 | ifdef NO_SYMLINK_HEAD | ||
739 | BASIC_CFLAGS += -DNO_SYMLINK_HEAD | ||
740 | endif | ||
741 | ifdef NO_STRCASESTR | ||
742 | COMPAT_CFLAGS += -DNO_STRCASESTR | ||
743 | COMPAT_OBJS += $(OUTPUT)compat/strcasestr.o | ||
744 | endif | ||
745 | ifdef NO_STRTOUMAX | ||
746 | COMPAT_CFLAGS += -DNO_STRTOUMAX | ||
747 | COMPAT_OBJS += $(OUTPUT)compat/strtoumax.o | ||
748 | endif | ||
749 | ifdef NO_STRTOULL | ||
750 | COMPAT_CFLAGS += -DNO_STRTOULL | ||
751 | endif | ||
752 | ifdef NO_SETENV | ||
753 | COMPAT_CFLAGS += -DNO_SETENV | ||
754 | COMPAT_OBJS += $(OUTPUT)compat/setenv.o | ||
755 | endif | ||
756 | ifdef NO_MKDTEMP | ||
757 | COMPAT_CFLAGS += -DNO_MKDTEMP | ||
758 | COMPAT_OBJS += $(OUTPUT)compat/mkdtemp.o | ||
759 | endif | ||
760 | ifdef NO_UNSETENV | ||
761 | COMPAT_CFLAGS += -DNO_UNSETENV | ||
762 | COMPAT_OBJS += $(OUTPUT)compat/unsetenv.o | ||
763 | endif | ||
764 | ifdef NO_SYS_SELECT_H | ||
765 | BASIC_CFLAGS += -DNO_SYS_SELECT_H | ||
766 | endif | ||
767 | ifdef NO_MMAP | ||
768 | COMPAT_CFLAGS += -DNO_MMAP | ||
769 | COMPAT_OBJS += $(OUTPUT)compat/mmap.o | ||
770 | else | ||
771 | ifdef USE_WIN32_MMAP | ||
772 | COMPAT_CFLAGS += -DUSE_WIN32_MMAP | ||
773 | COMPAT_OBJS += $(OUTPUT)compat/win32mmap.o | ||
774 | endif | ||
775 | endif | ||
776 | ifdef NO_PREAD | ||
777 | COMPAT_CFLAGS += -DNO_PREAD | ||
778 | COMPAT_OBJS += $(OUTPUT)compat/pread.o | ||
779 | endif | ||
780 | ifdef NO_FAST_WORKING_DIRECTORY | ||
781 | BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY | ||
782 | endif | ||
783 | ifdef NO_TRUSTABLE_FILEMODE | ||
784 | BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE | ||
785 | endif | ||
786 | ifdef NO_IPV6 | ||
787 | BASIC_CFLAGS += -DNO_IPV6 | ||
788 | endif | ||
789 | ifdef NO_UINTMAX_T | ||
790 | BASIC_CFLAGS += -Duintmax_t=uint32_t | ||
791 | endif | ||
792 | ifdef NO_SOCKADDR_STORAGE | ||
793 | ifdef NO_IPV6 | ||
794 | BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in | ||
795 | else | ||
796 | BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6 | ||
797 | endif | ||
798 | endif | ||
799 | ifdef NO_INET_NTOP | ||
800 | LIB_OBJS += $(OUTPUT)compat/inet_ntop.o | ||
801 | endif | ||
802 | ifdef NO_INET_PTON | ||
803 | LIB_OBJS += $(OUTPUT)compat/inet_pton.o | ||
804 | endif | ||
805 | |||
806 | ifdef NO_ICONV | ||
807 | BASIC_CFLAGS += -DNO_ICONV | ||
808 | endif | ||
809 | |||
810 | ifdef OLD_ICONV | ||
811 | BASIC_CFLAGS += -DOLD_ICONV | ||
812 | endif | ||
813 | |||
814 | ifdef NO_DEFLATE_BOUND | ||
815 | BASIC_CFLAGS += -DNO_DEFLATE_BOUND | ||
816 | endif | ||
817 | |||
818 | ifdef PPC_SHA1 | ||
819 | SHA1_HEADER = "ppc/sha1.h" | ||
820 | LIB_OBJS += $(OUTPUT)ppc/sha1.o ppc/sha1ppc.o | ||
821 | else | ||
822 | ifdef ARM_SHA1 | ||
823 | SHA1_HEADER = "arm/sha1.h" | ||
824 | LIB_OBJS += $(OUTPUT)arm/sha1.o $(OUTPUT)arm/sha1_arm.o | ||
825 | else | ||
826 | ifdef MOZILLA_SHA1 | ||
827 | SHA1_HEADER = "mozilla-sha1/sha1.h" | ||
828 | LIB_OBJS += $(OUTPUT)mozilla-sha1/sha1.o | ||
829 | else | ||
830 | SHA1_HEADER = <openssl/sha.h> | ||
831 | EXTLIBS += $(LIB_4_CRYPTO) | ||
832 | endif | ||
833 | endif | ||
834 | endif | ||
835 | ifdef NO_PERL_MAKEMAKER | ||
836 | export NO_PERL_MAKEMAKER | ||
837 | endif | ||
838 | ifdef NO_HSTRERROR | ||
839 | COMPAT_CFLAGS += -DNO_HSTRERROR | ||
840 | COMPAT_OBJS += $(OUTPUT)compat/hstrerror.o | ||
841 | endif | ||
842 | ifdef NO_MEMMEM | ||
843 | COMPAT_CFLAGS += -DNO_MEMMEM | ||
844 | COMPAT_OBJS += $(OUTPUT)compat/memmem.o | ||
845 | endif | ||
846 | ifdef INTERNAL_QSORT | ||
847 | COMPAT_CFLAGS += -DINTERNAL_QSORT | ||
848 | COMPAT_OBJS += $(OUTPUT)compat/qsort.o | ||
849 | endif | ||
850 | ifdef RUNTIME_PREFIX | ||
851 | COMPAT_CFLAGS += -DRUNTIME_PREFIX | ||
852 | endif | ||
853 | |||
854 | ifdef DIR_HAS_BSD_GROUP_SEMANTICS | ||
855 | COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS | ||
856 | endif | ||
857 | ifdef NO_EXTERNAL_GREP | ||
858 | BASIC_CFLAGS += -DNO_EXTERNAL_GREP | ||
859 | endif | ||
860 | |||
861 | ifeq ($(PERL_PATH),) | ||
862 | NO_PERL=NoThanks | ||
863 | endif | ||
864 | |||
865 | QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir | ||
866 | QUIET_SUBDIR1 = | ||
867 | |||
868 | ifneq ($(findstring $(MAKEFLAGS),w),w) | ||
869 | PRINT_DIR = --no-print-directory | ||
870 | else # "make -w" | ||
871 | NO_SUBDIR = : | ||
872 | endif | ||
873 | |||
874 | ifneq ($(findstring $(MAKEFLAGS),s),s) | 540 | ifneq ($(findstring $(MAKEFLAGS),s),s) |
875 | ifndef V | 541 | ifndef V |
876 | QUIET_CC = @echo ' ' CC $@; | 542 | QUIET_CC = @echo ' ' CC $@; |
877 | QUIET_AR = @echo ' ' AR $@; | 543 | QUIET_AR = @echo ' ' AR $@; |
878 | QUIET_LINK = @echo ' ' LINK $@; | 544 | QUIET_LINK = @echo ' ' LINK $@; |
879 | QUIET_MKDIR = @echo ' ' MKDIR $@; | 545 | QUIET_MKDIR = @echo ' ' MKDIR $@; |
880 | QUIET_BUILT_IN = @echo ' ' BUILTIN $@; | ||
881 | QUIET_GEN = @echo ' ' GEN $@; | 546 | QUIET_GEN = @echo ' ' GEN $@; |
882 | QUIET_SUBDIR0 = +@subdir= | ||
883 | QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ | ||
884 | $(MAKE) $(PRINT_DIR) -C $$subdir | ||
885 | export V | ||
886 | export QUIET_GEN | ||
887 | export QUIET_BUILT_IN | ||
888 | endif | 547 | endif |
889 | endif | 548 | endif |
890 | 549 | ||
@@ -894,7 +553,6 @@ endif | |||
894 | 553 | ||
895 | # Shell quote (do not use $(call) to accommodate ancient setups); | 554 | # Shell quote (do not use $(call) to accommodate ancient setups); |
896 | 555 | ||
897 | SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) | ||
898 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) | 556 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) |
899 | 557 | ||
900 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) | 558 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) |
@@ -908,46 +566,36 @@ htmldir_SQ = $(subst ','\'',$(htmldir)) | |||
908 | prefix_SQ = $(subst ','\'',$(prefix)) | 566 | prefix_SQ = $(subst ','\'',$(prefix)) |
909 | 567 | ||
910 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) | 568 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) |
911 | PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) | ||
912 | 569 | ||
913 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive $(EXTLIBS) | 570 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive $(EXTLIBS) |
914 | 571 | ||
915 | BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ | ||
916 | $(COMPAT_CFLAGS) | ||
917 | LIB_OBJS += $(COMPAT_OBJS) | ||
918 | |||
919 | ALL_CFLAGS += $(BASIC_CFLAGS) | 572 | ALL_CFLAGS += $(BASIC_CFLAGS) |
920 | ALL_CFLAGS += $(ARCH_CFLAGS) | 573 | ALL_CFLAGS += $(ARCH_CFLAGS) |
921 | ALL_LDFLAGS += $(BASIC_LDFLAGS) | 574 | ALL_LDFLAGS += $(BASIC_LDFLAGS) |
922 | 575 | ||
923 | export TAR INSTALL DESTDIR SHELL_PATH | 576 | export INSTALL SHELL_PATH |
924 | 577 | ||
925 | 578 | ||
926 | ### Build rules | 579 | ### Build rules |
927 | 580 | ||
928 | SHELL = $(SHELL_PATH) | 581 | SHELL = $(SHELL_PATH) |
929 | 582 | ||
930 | all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS | 583 | all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) |
931 | ifneq (,$X) | ||
932 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) | ||
933 | endif | ||
934 | |||
935 | all:: | ||
936 | 584 | ||
937 | please_set_SHELL_PATH_to_a_more_modern_shell: | 585 | please_set_SHELL_PATH_to_a_more_modern_shell: |
938 | @$$(:) | 586 | @$$(:) |
939 | 587 | ||
940 | shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell | 588 | shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell |
941 | 589 | ||
942 | strip: $(PROGRAMS) $(OUTPUT)perf$X | 590 | strip: $(PROGRAMS) $(OUTPUT)perf |
943 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf$X | 591 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf |
944 | 592 | ||
945 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 593 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
946 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ | 594 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ |
947 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 595 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
948 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ | 596 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ |
949 | 597 | ||
950 | $(OUTPUT)perf$X: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) | 598 | $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) |
951 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \ | 599 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \ |
952 | $(BUILTIN_OBJS) $(LIBS) -o $@ | 600 | $(BUILTIN_OBJS) $(LIBS) -o $@ |
953 | 601 | ||
@@ -963,39 +611,17 @@ $(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPU | |||
963 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ | 611 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ |
964 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< | 612 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< |
965 | 613 | ||
966 | $(BUILT_INS): $(OUTPUT)perf$X | ||
967 | $(QUIET_BUILT_IN)$(RM) $@ && \ | ||
968 | ln perf$X $@ 2>/dev/null || \ | ||
969 | ln -s perf$X $@ 2>/dev/null || \ | ||
970 | cp perf$X $@ | ||
971 | |||
972 | $(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt | 614 | $(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt |
973 | 615 | ||
974 | $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) | 616 | $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) |
975 | $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ | 617 | $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ |
976 | 618 | ||
977 | $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh | 619 | $(SCRIPTS) : % : %.sh |
978 | $(QUIET_GEN)$(RM) $(OUTPUT)$@ $(OUTPUT)$@+ && \ | 620 | $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' |
979 | sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ | ||
980 | -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ | ||
981 | -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ | ||
982 | -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \ | ||
983 | -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ | ||
984 | $@.sh > $(OUTPUT)$@+ && \ | ||
985 | chmod +x $(OUTPUT)$@+ && \ | ||
986 | mv $(OUTPUT)$@+ $(OUTPUT)$@ | ||
987 | |||
988 | configure: configure.ac | ||
989 | $(QUIET_GEN)$(RM) $@ $<+ && \ | ||
990 | sed -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \ | ||
991 | $< > $<+ && \ | ||
992 | autoconf -o $@ $<+ && \ | ||
993 | $(RM) $<+ | ||
994 | 621 | ||
995 | # These can record PERF_VERSION | 622 | # These can record PERF_VERSION |
996 | $(OUTPUT)perf.o perf.spec \ | 623 | $(OUTPUT)perf.o perf.spec \ |
997 | $(patsubst %.sh,%,$(SCRIPT_SH)) \ | 624 | $(SCRIPTS) \ |
998 | $(patsubst %.perl,%,$(SCRIPT_PERL)) \ | ||
999 | : $(OUTPUT)PERF-VERSION-FILE | 625 | : $(OUTPUT)PERF-VERSION-FILE |
1000 | 626 | ||
1001 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS | 627 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS |
@@ -1012,9 +638,6 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | |||
1012 | '-DPREFIX="$(prefix_SQ)"' \ | 638 | '-DPREFIX="$(prefix_SQ)"' \ |
1013 | $< | 639 | $< |
1014 | 640 | ||
1015 | $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS | ||
1016 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $< | ||
1017 | |||
1018 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 641 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
1019 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 642 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
1020 | 643 | ||
@@ -1024,6 +647,9 @@ $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS | |||
1024 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS | 647 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS |
1025 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 648 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
1026 | 649 | ||
650 | $(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS | ||
651 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
652 | |||
1027 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | 653 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS |
1028 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 654 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
1029 | 655 | ||
@@ -1045,12 +671,11 @@ $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/tra | |||
1045 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 671 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
1046 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 672 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< |
1047 | 673 | ||
1048 | $(OUTPUT)perf-%$X: %.o $(PERFLIBS) | 674 | $(OUTPUT)perf-%: %.o $(PERFLIBS) |
1049 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | 675 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) |
1050 | 676 | ||
1051 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) | 677 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) |
1052 | $(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) | 678 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) |
1053 | builtin-revert.o wt-status.o: wt-status.h | ||
1054 | 679 | ||
1055 | # we compile into subdirectories. if the target directory is not the source directory, they might not exists. So | 680 | # we compile into subdirectories. if the target directory is not the source directory, they might not exists. So |
1056 | # we depend the various files onto their directories. | 681 | # we depend the various files onto their directories. |
@@ -1063,6 +688,36 @@ $(sort $(dir $(DIRECTORY_DEPS))): | |||
1063 | $(LIB_FILE): $(LIB_OBJS) | 688 | $(LIB_FILE): $(LIB_OBJS) |
1064 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) | 689 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) |
1065 | 690 | ||
691 | help: | ||
692 | @echo 'Perf make targets:' | ||
693 | @echo ' doc - make *all* documentation (see below)' | ||
694 | @echo ' man - make manpage documentation (access with man <foo>)' | ||
695 | @echo ' html - make html documentation' | ||
696 | @echo ' info - make GNU info documentation (access with info <foo>)' | ||
697 | @echo ' pdf - make pdf documentation' | ||
698 | @echo ' TAGS - use etags to make tag information for source browsing' | ||
699 | @echo ' tags - use ctags to make tag information for source browsing' | ||
700 | @echo ' cscope - use cscope to make interactive browsing database' | ||
701 | @echo '' | ||
702 | @echo 'Perf install targets:' | ||
703 | @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' | ||
704 | @echo ' HINT: use "make prefix=<path> <install target>" to install to a particular' | ||
705 | @echo ' path like make prefix=/usr/local install install-doc' | ||
706 | @echo ' install - install compiled binaries' | ||
707 | @echo ' install-doc - install *all* documentation' | ||
708 | @echo ' install-man - install manpage documentation' | ||
709 | @echo ' install-html - install html documentation' | ||
710 | @echo ' install-info - install GNU info documentation' | ||
711 | @echo ' install-pdf - install pdf documentation' | ||
712 | @echo '' | ||
713 | @echo ' quick-install-doc - alias for quick-install-man' | ||
714 | @echo ' quick-install-man - install the documentation quickly' | ||
715 | @echo ' quick-install-html - install the html documentation quickly' | ||
716 | @echo '' | ||
717 | @echo 'Perf maintainer targets:' | ||
718 | @echo ' distclean - alias to clean' | ||
719 | @echo ' clean - clean all binary objects and build output' | ||
720 | |||
1066 | doc: | 721 | doc: |
1067 | $(MAKE) -C Documentation all | 722 | $(MAKE) -C Documentation all |
1068 | 723 | ||
@@ -1101,30 +756,12 @@ $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS | |||
1101 | echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ | 756 | echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ |
1102 | fi | 757 | fi |
1103 | 758 | ||
1104 | # We need to apply sq twice, once to protect from the shell | ||
1105 | # that runs $(OUTPUT)PERF-BUILD-OPTIONS, and then again to protect it | ||
1106 | # and the first level quoting from the shell that runs "echo". | ||
1107 | $(OUTPUT)PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS | ||
1108 | @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ | ||
1109 | @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ | ||
1110 | @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ | ||
1111 | @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@ | ||
1112 | |||
1113 | ### Testing rules | 759 | ### Testing rules |
1114 | 760 | ||
1115 | # | ||
1116 | # None right now: | ||
1117 | # | ||
1118 | # TEST_PROGRAMS += test-something$X | ||
1119 | |||
1120 | all:: $(TEST_PROGRAMS) | ||
1121 | |||
1122 | # GNU make supports exporting all variables by "export" without parameters. | 761 | # GNU make supports exporting all variables by "export" without parameters. |
1123 | # However, the environment gets quite big, and some programs have problems | 762 | # However, the environment gets quite big, and some programs have problems |
1124 | # with that. | 763 | # with that. |
1125 | 764 | ||
1126 | export NO_SVN_TESTS | ||
1127 | |||
1128 | check: $(OUTPUT)common-cmds.h | 765 | check: $(OUTPUT)common-cmds.h |
1129 | if sparse; \ | 766 | if sparse; \ |
1130 | then \ | 767 | then \ |
@@ -1133,33 +770,21 @@ check: $(OUTPUT)common-cmds.h | |||
1133 | sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ | 770 | sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ |
1134 | done; \ | 771 | done; \ |
1135 | else \ | 772 | else \ |
1136 | echo 2>&1 "Did you mean 'make test'?"; \ | ||
1137 | exit 1; \ | 773 | exit 1; \ |
1138 | fi | 774 | fi |
1139 | 775 | ||
1140 | remove-dashes: | ||
1141 | ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS) | ||
1142 | |||
1143 | ### Installation rules | 776 | ### Installation rules |
1144 | 777 | ||
1145 | ifneq ($(filter /%,$(firstword $(template_dir))),) | ||
1146 | template_instdir = $(template_dir) | ||
1147 | else | ||
1148 | template_instdir = $(prefix)/$(template_dir) | ||
1149 | endif | ||
1150 | export template_instdir | ||
1151 | |||
1152 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) | 778 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) |
1153 | perfexec_instdir = $(perfexecdir) | 779 | perfexec_instdir = $(perfexecdir) |
1154 | else | 780 | else |
1155 | perfexec_instdir = $(prefix)/$(perfexecdir) | 781 | perfexec_instdir = $(prefix)/$(perfexecdir) |
1156 | endif | 782 | endif |
1157 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | 783 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) |
1158 | export perfexec_instdir | ||
1159 | 784 | ||
1160 | install: all | 785 | install: all |
1161 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 786 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
1162 | $(INSTALL) $(OUTPUT)perf$X '$(DESTDIR_SQ)$(bindir_SQ)' | 787 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
1163 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 788 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
1164 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 789 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
1165 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 790 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
@@ -1172,14 +797,6 @@ install: all | |||
1172 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | 797 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' |
1173 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 798 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
1174 | 799 | ||
1175 | ifdef BUILT_INS | ||
1176 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
1177 | $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
1178 | ifneq (,$X) | ||
1179 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) $(OUTPUT)perf$X)), $(RM) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p';) | ||
1180 | endif | ||
1181 | endif | ||
1182 | |||
1183 | install-doc: | 800 | install-doc: |
1184 | $(MAKE) -C Documentation install | 801 | $(MAKE) -C Documentation install |
1185 | 802 | ||
@@ -1204,104 +821,17 @@ quick-install-man: | |||
1204 | quick-install-html: | 821 | quick-install-html: |
1205 | $(MAKE) -C Documentation quick-install-html | 822 | $(MAKE) -C Documentation quick-install-html |
1206 | 823 | ||
1207 | |||
1208 | ### Maintainer's dist rules | ||
1209 | # | ||
1210 | # None right now | ||
1211 | # | ||
1212 | # | ||
1213 | # perf.spec: perf.spec.in | ||
1214 | # sed -e 's/@@VERSION@@/$(PERF_VERSION)/g' < $< > $@+ | ||
1215 | # mv $@+ $@ | ||
1216 | # | ||
1217 | # PERF_TARNAME=perf-$(PERF_VERSION) | ||
1218 | # dist: perf.spec perf-archive$(X) configure | ||
1219 | # ./perf-archive --format=tar \ | ||
1220 | # --prefix=$(PERF_TARNAME)/ HEAD^{tree} > $(PERF_TARNAME).tar | ||
1221 | # @mkdir -p $(PERF_TARNAME) | ||
1222 | # @cp perf.spec configure $(PERF_TARNAME) | ||
1223 | # @echo $(PERF_VERSION) > $(PERF_TARNAME)/version | ||
1224 | # $(TAR) rf $(PERF_TARNAME).tar \ | ||
1225 | # $(PERF_TARNAME)/perf.spec \ | ||
1226 | # $(PERF_TARNAME)/configure \ | ||
1227 | # $(PERF_TARNAME)/version | ||
1228 | # @$(RM) -r $(PERF_TARNAME) | ||
1229 | # gzip -f -9 $(PERF_TARNAME).tar | ||
1230 | # | ||
1231 | # htmldocs = perf-htmldocs-$(PERF_VERSION) | ||
1232 | # manpages = perf-manpages-$(PERF_VERSION) | ||
1233 | # dist-doc: | ||
1234 | # $(RM) -r .doc-tmp-dir | ||
1235 | # mkdir .doc-tmp-dir | ||
1236 | # $(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc | ||
1237 | # cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar . | ||
1238 | # gzip -n -9 -f $(htmldocs).tar | ||
1239 | # : | ||
1240 | # $(RM) -r .doc-tmp-dir | ||
1241 | # mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7 | ||
1242 | # $(MAKE) -C Documentation DESTDIR=./ \ | ||
1243 | # man1dir=../.doc-tmp-dir/man1 \ | ||
1244 | # man5dir=../.doc-tmp-dir/man5 \ | ||
1245 | # man7dir=../.doc-tmp-dir/man7 \ | ||
1246 | # install | ||
1247 | # cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar . | ||
1248 | # gzip -n -9 -f $(manpages).tar | ||
1249 | # $(RM) -r .doc-tmp-dir | ||
1250 | # | ||
1251 | # rpm: dist | ||
1252 | # $(RPMBUILD) -ta $(PERF_TARNAME).tar.gz | ||
1253 | |||
1254 | ### Cleaning rules | 824 | ### Cleaning rules |
1255 | 825 | ||
1256 | distclean: clean | ||
1257 | # $(RM) configure | ||
1258 | |||
1259 | clean: | 826 | clean: |
1260 | $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE) | 827 | $(RM) $(OUTPUT){*.o,*/*.o,*/*/*.o,*/*/*/*.o,$(LIB_FILE),perf-archive} |
1261 | $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X | 828 | $(RM) $(ALL_PROGRAMS) perf |
1262 | $(RM) $(TEST_PROGRAMS) | ||
1263 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 829 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
1264 | $(RM) -r autom4te.cache | ||
1265 | $(RM) config.log config.mak.autogen config.mak.append config.status config.cache | ||
1266 | $(RM) -r $(PERF_TARNAME) .doc-tmp-dir | ||
1267 | $(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz | ||
1268 | $(RM) $(htmldocs).tar.gz $(manpages).tar.gz | ||
1269 | $(MAKE) -C Documentation/ clean | 830 | $(MAKE) -C Documentation/ clean |
1270 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-BUILD-OPTIONS | 831 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
832 | @python util/setup.py clean --build-lib='$(OUTPUT)python' \ | ||
833 | --build-temp='$(OUTPUT)python/temp' | ||
1271 | 834 | ||
1272 | .PHONY: all install clean strip | 835 | .PHONY: all install clean strip |
1273 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell | 836 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell |
1274 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 837 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
1275 | .PHONY: .FORCE-PERF-BUILD-OPTIONS | ||
1276 | |||
1277 | ### Make sure built-ins do not have dups and listed in perf.c | ||
1278 | # | ||
1279 | check-builtins:: | ||
1280 | ./check-builtins.sh | ||
1281 | |||
1282 | ### Test suite coverage testing | ||
1283 | # | ||
1284 | # None right now | ||
1285 | # | ||
1286 | # .PHONY: coverage coverage-clean coverage-build coverage-report | ||
1287 | # | ||
1288 | # coverage: | ||
1289 | # $(MAKE) coverage-build | ||
1290 | # $(MAKE) coverage-report | ||
1291 | # | ||
1292 | # coverage-clean: | ||
1293 | # rm -f *.gcda *.gcno | ||
1294 | # | ||
1295 | # COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs | ||
1296 | # COVERAGE_LDFLAGS = $(CFLAGS) -O0 -lgcov | ||
1297 | # | ||
1298 | # coverage-build: coverage-clean | ||
1299 | # $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all | ||
1300 | # $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \ | ||
1301 | # -j1 test | ||
1302 | # | ||
1303 | # coverage-report: | ||
1304 | # gcov -b *.c */*.c | ||
1305 | # grep '^function.*called 0 ' *.c.gcov */*.c.gcov \ | ||
1306 | # | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \ | ||
1307 | # | tee coverage-untested-functions | ||
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index d9ab3ce446ac..0c7454f8b8a9 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c | |||
@@ -55,7 +55,7 @@ int bench_sched_pipe(int argc, const char **argv, | |||
55 | * discarding returned value of read(), write() | 55 | * discarding returned value of read(), write() |
56 | * causes error in building environment for perf | 56 | * causes error in building environment for perf |
57 | */ | 57 | */ |
58 | int ret, wait_stat; | 58 | int __used ret, wait_stat; |
59 | pid_t pid, retpid; | 59 | pid_t pid, retpid; |
60 | 60 | ||
61 | argc = parse_options(argc, argv, options, | 61 | argc = parse_options(argc, argv, options, |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 8879463807e4..695de4b5ae63 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | 11 | ||
12 | #include "util/util.h" | ||
12 | #include "util/color.h" | 13 | #include "util/color.h" |
13 | #include <linux/list.h> | 14 | #include <linux/list.h> |
14 | #include "util/cache.h" | 15 | #include "util/cache.h" |
@@ -18,6 +19,9 @@ | |||
18 | #include "perf.h" | 19 | #include "perf.h" |
19 | #include "util/debug.h" | 20 | #include "util/debug.h" |
20 | 21 | ||
22 | #include "util/evlist.h" | ||
23 | #include "util/evsel.h" | ||
24 | #include "util/annotate.h" | ||
21 | #include "util/event.h" | 25 | #include "util/event.h" |
22 | #include "util/parse-options.h" | 26 | #include "util/parse-options.h" |
23 | #include "util/parse-events.h" | 27 | #include "util/parse-events.h" |
@@ -36,9 +40,13 @@ static bool print_line; | |||
36 | 40 | ||
37 | static const char *sym_hist_filter; | 41 | static const char *sym_hist_filter; |
38 | 42 | ||
39 | static int hists__add_entry(struct hists *self, struct addr_location *al) | 43 | static int perf_evlist__add_sample(struct perf_evlist *evlist, |
44 | struct perf_sample *sample, | ||
45 | struct addr_location *al) | ||
40 | { | 46 | { |
47 | struct perf_evsel *evsel; | ||
41 | struct hist_entry *he; | 48 | struct hist_entry *he; |
49 | int ret; | ||
42 | 50 | ||
43 | if (sym_hist_filter != NULL && | 51 | if (sym_hist_filter != NULL && |
44 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { | 52 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { |
@@ -51,25 +59,51 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
51 | return 0; | 59 | return 0; |
52 | } | 60 | } |
53 | 61 | ||
54 | he = __hists__add_entry(self, al, NULL, 1); | 62 | evsel = perf_evlist__id2evsel(evlist, sample->id); |
63 | if (evsel == NULL) { | ||
64 | /* | ||
65 | * FIXME: Propagate this back, but at least we're in a builtin, | ||
66 | * where exit() is allowed. ;-) | ||
67 | */ | ||
68 | ui__warning("Invalid %s file, contains samples with id not in " | ||
69 | "its header!\n", input_name); | ||
70 | exit_browser(0); | ||
71 | exit(1); | ||
72 | } | ||
73 | |||
74 | he = __hists__add_entry(&evsel->hists, al, NULL, 1); | ||
55 | if (he == NULL) | 75 | if (he == NULL) |
56 | return -ENOMEM; | 76 | return -ENOMEM; |
57 | 77 | ||
58 | return hist_entry__inc_addr_samples(he, al->addr); | 78 | ret = 0; |
79 | if (he->ms.sym != NULL) { | ||
80 | struct annotation *notes = symbol__annotation(he->ms.sym); | ||
81 | if (notes->src == NULL && | ||
82 | symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0) | ||
83 | return -ENOMEM; | ||
84 | |||
85 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
86 | } | ||
87 | |||
88 | evsel->hists.stats.total_period += sample->period; | ||
89 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
90 | return ret; | ||
59 | } | 91 | } |
60 | 92 | ||
61 | static int process_sample_event(event_t *event, struct sample_data *sample, | 93 | static int process_sample_event(union perf_event *event, |
94 | struct perf_sample *sample, | ||
62 | struct perf_session *session) | 95 | struct perf_session *session) |
63 | { | 96 | { |
64 | struct addr_location al; | 97 | struct addr_location al; |
65 | 98 | ||
66 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { | 99 | if (perf_event__preprocess_sample(event, session, &al, sample, |
100 | symbol__annotate_init) < 0) { | ||
67 | pr_warning("problem processing %d event, skipping it.\n", | 101 | pr_warning("problem processing %d event, skipping it.\n", |
68 | event->header.type); | 102 | event->header.type); |
69 | return -1; | 103 | return -1; |
70 | } | 104 | } |
71 | 105 | ||
72 | if (!al.filtered && hists__add_entry(&session->hists, &al)) { | 106 | if (!al.filtered && perf_evlist__add_sample(session->evlist, sample, &al)) { |
73 | pr_warning("problem incrementing symbol count, " | 107 | pr_warning("problem incrementing symbol count, " |
74 | "skipping event\n"); | 108 | "skipping event\n"); |
75 | return -1; | 109 | return -1; |
@@ -78,261 +112,26 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
78 | return 0; | 112 | return 0; |
79 | } | 113 | } |
80 | 114 | ||
81 | static int objdump_line__print(struct objdump_line *self, | 115 | static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) |
82 | struct list_head *head, | ||
83 | struct hist_entry *he, u64 len) | ||
84 | { | ||
85 | struct symbol *sym = he->ms.sym; | ||
86 | static const char *prev_line; | ||
87 | static const char *prev_color; | ||
88 | |||
89 | if (self->offset != -1) { | ||
90 | const char *path = NULL; | ||
91 | unsigned int hits = 0; | ||
92 | double percent = 0.0; | ||
93 | const char *color; | ||
94 | struct sym_priv *priv = symbol__priv(sym); | ||
95 | struct sym_ext *sym_ext = priv->ext; | ||
96 | struct sym_hist *h = priv->hist; | ||
97 | s64 offset = self->offset; | ||
98 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | ||
99 | |||
100 | while (offset < (s64)len && | ||
101 | (next == NULL || offset < next->offset)) { | ||
102 | if (sym_ext) { | ||
103 | if (path == NULL) | ||
104 | path = sym_ext[offset].path; | ||
105 | percent += sym_ext[offset].percent; | ||
106 | } else | ||
107 | hits += h->ip[offset]; | ||
108 | |||
109 | ++offset; | ||
110 | } | ||
111 | |||
112 | if (sym_ext == NULL && h->sum) | ||
113 | percent = 100.0 * hits / h->sum; | ||
114 | |||
115 | color = get_percent_color(percent); | ||
116 | |||
117 | /* | ||
118 | * Also color the filename and line if needed, with | ||
119 | * the same color than the percentage. Don't print it | ||
120 | * twice for close colored ip with the same filename:line | ||
121 | */ | ||
122 | if (path) { | ||
123 | if (!prev_line || strcmp(prev_line, path) | ||
124 | || color != prev_color) { | ||
125 | color_fprintf(stdout, color, " %s", path); | ||
126 | prev_line = path; | ||
127 | prev_color = color; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | color_fprintf(stdout, color, " %7.2f", percent); | ||
132 | printf(" : "); | ||
133 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); | ||
134 | } else { | ||
135 | if (!*self->line) | ||
136 | printf(" :\n"); | ||
137 | else | ||
138 | printf(" : %s\n", self->line); | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static struct rb_root root_sym_ext; | ||
145 | |||
146 | static void insert_source_line(struct sym_ext *sym_ext) | ||
147 | { | ||
148 | struct sym_ext *iter; | ||
149 | struct rb_node **p = &root_sym_ext.rb_node; | ||
150 | struct rb_node *parent = NULL; | ||
151 | |||
152 | while (*p != NULL) { | ||
153 | parent = *p; | ||
154 | iter = rb_entry(parent, struct sym_ext, node); | ||
155 | |||
156 | if (sym_ext->percent > iter->percent) | ||
157 | p = &(*p)->rb_left; | ||
158 | else | ||
159 | p = &(*p)->rb_right; | ||
160 | } | ||
161 | |||
162 | rb_link_node(&sym_ext->node, parent, p); | ||
163 | rb_insert_color(&sym_ext->node, &root_sym_ext); | ||
164 | } | ||
165 | |||
166 | static void free_source_line(struct hist_entry *he, int len) | ||
167 | { | ||
168 | struct sym_priv *priv = symbol__priv(he->ms.sym); | ||
169 | struct sym_ext *sym_ext = priv->ext; | ||
170 | int i; | ||
171 | |||
172 | if (!sym_ext) | ||
173 | return; | ||
174 | |||
175 | for (i = 0; i < len; i++) | ||
176 | free(sym_ext[i].path); | ||
177 | free(sym_ext); | ||
178 | |||
179 | priv->ext = NULL; | ||
180 | root_sym_ext = RB_ROOT; | ||
181 | } | ||
182 | |||
183 | /* Get the filename:line for the colored entries */ | ||
184 | static void | ||
185 | get_source_line(struct hist_entry *he, int len, const char *filename) | ||
186 | { | ||
187 | struct symbol *sym = he->ms.sym; | ||
188 | u64 start; | ||
189 | int i; | ||
190 | char cmd[PATH_MAX * 2]; | ||
191 | struct sym_ext *sym_ext; | ||
192 | struct sym_priv *priv = symbol__priv(sym); | ||
193 | struct sym_hist *h = priv->hist; | ||
194 | |||
195 | if (!h->sum) | ||
196 | return; | ||
197 | |||
198 | sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); | ||
199 | if (!priv->ext) | ||
200 | return; | ||
201 | |||
202 | start = he->ms.map->unmap_ip(he->ms.map, sym->start); | ||
203 | |||
204 | for (i = 0; i < len; i++) { | ||
205 | char *path = NULL; | ||
206 | size_t line_len; | ||
207 | u64 offset; | ||
208 | FILE *fp; | ||
209 | |||
210 | sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; | ||
211 | if (sym_ext[i].percent <= 0.5) | ||
212 | continue; | ||
213 | |||
214 | offset = start + i; | ||
215 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | ||
216 | fp = popen(cmd, "r"); | ||
217 | if (!fp) | ||
218 | continue; | ||
219 | |||
220 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
221 | goto next; | ||
222 | |||
223 | sym_ext[i].path = malloc(sizeof(char) * line_len + 1); | ||
224 | if (!sym_ext[i].path) | ||
225 | goto next; | ||
226 | |||
227 | strcpy(sym_ext[i].path, path); | ||
228 | insert_source_line(&sym_ext[i]); | ||
229 | |||
230 | next: | ||
231 | pclose(fp); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | static void print_summary(const char *filename) | ||
236 | { | ||
237 | struct sym_ext *sym_ext; | ||
238 | struct rb_node *node; | ||
239 | |||
240 | printf("\nSorted summary for file %s\n", filename); | ||
241 | printf("----------------------------------------------\n\n"); | ||
242 | |||
243 | if (RB_EMPTY_ROOT(&root_sym_ext)) { | ||
244 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | node = rb_first(&root_sym_ext); | ||
249 | while (node) { | ||
250 | double percent; | ||
251 | const char *color; | ||
252 | char *path; | ||
253 | |||
254 | sym_ext = rb_entry(node, struct sym_ext, node); | ||
255 | percent = sym_ext->percent; | ||
256 | color = get_percent_color(percent); | ||
257 | path = sym_ext->path; | ||
258 | |||
259 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
260 | node = rb_next(node); | ||
261 | } | ||
262 | } | ||
263 | |||
264 | static void hist_entry__print_hits(struct hist_entry *self) | ||
265 | { | ||
266 | struct symbol *sym = self->ms.sym; | ||
267 | struct sym_priv *priv = symbol__priv(sym); | ||
268 | struct sym_hist *h = priv->hist; | ||
269 | u64 len = sym->end - sym->start, offset; | ||
270 | |||
271 | for (offset = 0; offset < len; ++offset) | ||
272 | if (h->ip[offset] != 0) | ||
273 | printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | ||
274 | sym->start + offset, h->ip[offset]); | ||
275 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||
276 | } | ||
277 | |||
278 | static int hist_entry__tty_annotate(struct hist_entry *he) | ||
279 | { | 116 | { |
280 | struct map *map = he->ms.map; | 117 | return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, |
281 | struct dso *dso = map->dso; | 118 | print_line, full_paths, 0, 0); |
282 | struct symbol *sym = he->ms.sym; | ||
283 | const char *filename = dso->long_name, *d_filename; | ||
284 | u64 len; | ||
285 | LIST_HEAD(head); | ||
286 | struct objdump_line *pos, *n; | ||
287 | |||
288 | if (hist_entry__annotate(he, &head, 0) < 0) | ||
289 | return -1; | ||
290 | |||
291 | if (full_paths) | ||
292 | d_filename = filename; | ||
293 | else | ||
294 | d_filename = basename(filename); | ||
295 | |||
296 | len = sym->end - sym->start; | ||
297 | |||
298 | if (print_line) { | ||
299 | get_source_line(he, len, filename); | ||
300 | print_summary(filename); | ||
301 | } | ||
302 | |||
303 | printf("\n\n------------------------------------------------\n"); | ||
304 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | ||
305 | printf("------------------------------------------------\n"); | ||
306 | |||
307 | if (verbose) | ||
308 | hist_entry__print_hits(he); | ||
309 | |||
310 | list_for_each_entry_safe(pos, n, &head, node) { | ||
311 | objdump_line__print(pos, &head, he, len); | ||
312 | list_del(&pos->node); | ||
313 | objdump_line__free(pos); | ||
314 | } | ||
315 | |||
316 | if (print_line) | ||
317 | free_source_line(he, len); | ||
318 | |||
319 | return 0; | ||
320 | } | 119 | } |
321 | 120 | ||
322 | static void hists__find_annotations(struct hists *self) | 121 | static void hists__find_annotations(struct hists *self, int evidx) |
323 | { | 122 | { |
324 | struct rb_node *nd = rb_first(&self->entries), *next; | 123 | struct rb_node *nd = rb_first(&self->entries), *next; |
325 | int key = KEY_RIGHT; | 124 | int key = KEY_RIGHT; |
326 | 125 | ||
327 | while (nd) { | 126 | while (nd) { |
328 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 127 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
329 | struct sym_priv *priv; | 128 | struct annotation *notes; |
330 | 129 | ||
331 | if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned) | 130 | if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned) |
332 | goto find_next; | 131 | goto find_next; |
333 | 132 | ||
334 | priv = symbol__priv(he->ms.sym); | 133 | notes = symbol__annotation(he->ms.sym); |
335 | if (priv->hist == NULL) { | 134 | if (notes->src == NULL) { |
336 | find_next: | 135 | find_next: |
337 | if (key == KEY_LEFT) | 136 | if (key == KEY_LEFT) |
338 | nd = rb_prev(nd); | 137 | nd = rb_prev(nd); |
@@ -342,7 +141,7 @@ find_next: | |||
342 | } | 141 | } |
343 | 142 | ||
344 | if (use_browser > 0) { | 143 | if (use_browser > 0) { |
345 | key = hist_entry__tui_annotate(he); | 144 | key = hist_entry__tui_annotate(he, evidx); |
346 | switch (key) { | 145 | switch (key) { |
347 | case KEY_RIGHT: | 146 | case KEY_RIGHT: |
348 | next = rb_next(nd); | 147 | next = rb_next(nd); |
@@ -357,24 +156,24 @@ find_next: | |||
357 | if (next != NULL) | 156 | if (next != NULL) |
358 | nd = next; | 157 | nd = next; |
359 | } else { | 158 | } else { |
360 | hist_entry__tty_annotate(he); | 159 | hist_entry__tty_annotate(he, evidx); |
361 | nd = rb_next(nd); | 160 | nd = rb_next(nd); |
362 | /* | 161 | /* |
363 | * Since we have a hist_entry per IP for the same | 162 | * Since we have a hist_entry per IP for the same |
364 | * symbol, free he->ms.sym->hist to signal we already | 163 | * symbol, free he->ms.sym->src to signal we already |
365 | * processed this symbol. | 164 | * processed this symbol. |
366 | */ | 165 | */ |
367 | free(priv->hist); | 166 | free(notes->src); |
368 | priv->hist = NULL; | 167 | notes->src = NULL; |
369 | } | 168 | } |
370 | } | 169 | } |
371 | } | 170 | } |
372 | 171 | ||
373 | static struct perf_event_ops event_ops = { | 172 | static struct perf_event_ops event_ops = { |
374 | .sample = process_sample_event, | 173 | .sample = process_sample_event, |
375 | .mmap = event__process_mmap, | 174 | .mmap = perf_event__process_mmap, |
376 | .comm = event__process_comm, | 175 | .comm = perf_event__process_comm, |
377 | .fork = event__process_task, | 176 | .fork = perf_event__process_task, |
378 | .ordered_samples = true, | 177 | .ordered_samples = true, |
379 | .ordering_requires_timestamps = true, | 178 | .ordering_requires_timestamps = true, |
380 | }; | 179 | }; |
@@ -383,6 +182,8 @@ static int __cmd_annotate(void) | |||
383 | { | 182 | { |
384 | int ret; | 183 | int ret; |
385 | struct perf_session *session; | 184 | struct perf_session *session; |
185 | struct perf_evsel *pos; | ||
186 | u64 total_nr_samples; | ||
386 | 187 | ||
387 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); | 188 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); |
388 | if (session == NULL) | 189 | if (session == NULL) |
@@ -403,12 +204,36 @@ static int __cmd_annotate(void) | |||
403 | if (verbose > 2) | 204 | if (verbose > 2) |
404 | perf_session__fprintf_dsos(session, stdout); | 205 | perf_session__fprintf_dsos(session, stdout); |
405 | 206 | ||
406 | hists__collapse_resort(&session->hists); | 207 | total_nr_samples = 0; |
407 | hists__output_resort(&session->hists); | 208 | list_for_each_entry(pos, &session->evlist->entries, node) { |
408 | hists__find_annotations(&session->hists); | 209 | struct hists *hists = &pos->hists; |
409 | out_delete: | 210 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
410 | perf_session__delete(session); | 211 | |
212 | if (nr_samples > 0) { | ||
213 | total_nr_samples += nr_samples; | ||
214 | hists__collapse_resort(hists); | ||
215 | hists__output_resort(hists); | ||
216 | hists__find_annotations(hists, pos->idx); | ||
217 | } | ||
218 | } | ||
411 | 219 | ||
220 | if (total_nr_samples == 0) { | ||
221 | ui__warning("The %s file has no samples!\n", input_name); | ||
222 | goto out_delete; | ||
223 | } | ||
224 | out_delete: | ||
225 | /* | ||
226 | * Speed up the exit process, for large files this can | ||
227 | * take quite a while. | ||
228 | * | ||
229 | * XXX Enable this when using valgrind or if we ever | ||
230 | * librarize this command. | ||
231 | * | ||
232 | * Also experiment with obstacks to see how much speed | ||
233 | * up we'll get here. | ||
234 | * | ||
235 | * perf_session__delete(session); | ||
236 | */ | ||
412 | return ret; | 237 | return ret; |
413 | } | 238 | } |
414 | 239 | ||
@@ -451,9 +276,9 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) | |||
451 | else if (use_tui) | 276 | else if (use_tui) |
452 | use_browser = 1; | 277 | use_browser = 1; |
453 | 278 | ||
454 | setup_browser(); | 279 | setup_browser(true); |
455 | 280 | ||
456 | symbol_conf.priv_size = sizeof(struct sym_priv); | 281 | symbol_conf.priv_size = sizeof(struct annotation); |
457 | symbol_conf.try_vmlinux_path = true; | 282 | symbol_conf.try_vmlinux_path = true; |
458 | 283 | ||
459 | if (symbol__init() < 0) | 284 | if (symbol__init() < 0) |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 3153e492dbcc..6b7d91160ecb 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -30,13 +30,13 @@ static int hists__add_entry(struct hists *self, | |||
30 | return -ENOMEM; | 30 | return -ENOMEM; |
31 | } | 31 | } |
32 | 32 | ||
33 | static int diff__process_sample_event(event_t *event, | 33 | static int diff__process_sample_event(union perf_event *event, |
34 | struct sample_data *sample, | 34 | struct perf_sample *sample, |
35 | struct perf_session *session) | 35 | struct perf_session *session) |
36 | { | 36 | { |
37 | struct addr_location al; | 37 | struct addr_location al; |
38 | 38 | ||
39 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { | 39 | if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) { |
40 | pr_warning("problem processing %d event, skipping it.\n", | 40 | pr_warning("problem processing %d event, skipping it.\n", |
41 | event->header.type); | 41 | event->header.type); |
42 | return -1; | 42 | return -1; |
@@ -56,11 +56,11 @@ static int diff__process_sample_event(event_t *event, | |||
56 | 56 | ||
57 | static struct perf_event_ops event_ops = { | 57 | static struct perf_event_ops event_ops = { |
58 | .sample = diff__process_sample_event, | 58 | .sample = diff__process_sample_event, |
59 | .mmap = event__process_mmap, | 59 | .mmap = perf_event__process_mmap, |
60 | .comm = event__process_comm, | 60 | .comm = perf_event__process_comm, |
61 | .exit = event__process_task, | 61 | .exit = perf_event__process_task, |
62 | .fork = event__process_task, | 62 | .fork = perf_event__process_task, |
63 | .lost = event__process_lost, | 63 | .lost = perf_event__process_lost, |
64 | .ordered_samples = true, | 64 | .ordered_samples = true, |
65 | .ordering_requires_timestamps = true, | 65 | .ordering_requires_timestamps = true, |
66 | }; | 66 | }; |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c new file mode 100644 index 000000000000..4c5e9e04a41f --- /dev/null +++ b/tools/perf/builtin-evlist.c | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * Builtin evlist command: Show the list of event selectors present | ||
3 | * in a perf.data file. | ||
4 | */ | ||
5 | #include "builtin.h" | ||
6 | |||
7 | #include "util/util.h" | ||
8 | |||
9 | #include <linux/list.h> | ||
10 | |||
11 | #include "perf.h" | ||
12 | #include "util/evlist.h" | ||
13 | #include "util/evsel.h" | ||
14 | #include "util/parse-events.h" | ||
15 | #include "util/parse-options.h" | ||
16 | #include "util/session.h" | ||
17 | |||
18 | static char const *input_name = "perf.data"; | ||
19 | |||
20 | static int __cmd_evlist(void) | ||
21 | { | ||
22 | struct perf_session *session; | ||
23 | struct perf_evsel *pos; | ||
24 | |||
25 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | ||
26 | if (session == NULL) | ||
27 | return -ENOMEM; | ||
28 | |||
29 | list_for_each_entry(pos, &session->evlist->entries, node) | ||
30 | printf("%s\n", event_name(pos)); | ||
31 | |||
32 | perf_session__delete(session); | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static const char * const evlist_usage[] = { | ||
37 | "perf evlist [<options>]", | ||
38 | NULL | ||
39 | }; | ||
40 | |||
41 | static const struct option options[] = { | ||
42 | OPT_STRING('i', "input", &input_name, "file", | ||
43 | "input file name"), | ||
44 | OPT_END() | ||
45 | }; | ||
46 | |||
47 | int cmd_evlist(int argc, const char **argv, const char *prefix __used) | ||
48 | { | ||
49 | argc = parse_options(argc, argv, options, evlist_usage, 0); | ||
50 | if (argc) | ||
51 | usage_with_options(evlist_usage, options); | ||
52 | |||
53 | return __cmd_evlist(); | ||
54 | } | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 0c78ffa7bf67..e29f04ed3396 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -16,8 +16,8 @@ | |||
16 | static char const *input_name = "-"; | 16 | static char const *input_name = "-"; |
17 | static bool inject_build_ids; | 17 | static bool inject_build_ids; |
18 | 18 | ||
19 | static int event__repipe_synth(event_t *event, | 19 | static int perf_event__repipe_synth(union perf_event *event, |
20 | struct perf_session *session __used) | 20 | struct perf_session *session __used) |
21 | { | 21 | { |
22 | uint32_t size; | 22 | uint32_t size; |
23 | void *buf = event; | 23 | void *buf = event; |
@@ -36,41 +36,44 @@ static int event__repipe_synth(event_t *event, | |||
36 | return 0; | 36 | return 0; |
37 | } | 37 | } |
38 | 38 | ||
39 | static int event__repipe(event_t *event, struct sample_data *sample __used, | 39 | static int perf_event__repipe(union perf_event *event, |
40 | struct perf_session *session) | 40 | struct perf_sample *sample __used, |
41 | struct perf_session *session) | ||
41 | { | 42 | { |
42 | return event__repipe_synth(event, session); | 43 | return perf_event__repipe_synth(event, session); |
43 | } | 44 | } |
44 | 45 | ||
45 | static int event__repipe_mmap(event_t *self, struct sample_data *sample, | 46 | static int perf_event__repipe_mmap(union perf_event *event, |
46 | struct perf_session *session) | 47 | struct perf_sample *sample, |
48 | struct perf_session *session) | ||
47 | { | 49 | { |
48 | int err; | 50 | int err; |
49 | 51 | ||
50 | err = event__process_mmap(self, sample, session); | 52 | err = perf_event__process_mmap(event, sample, session); |
51 | event__repipe(self, sample, session); | 53 | perf_event__repipe(event, sample, session); |
52 | 54 | ||
53 | return err; | 55 | return err; |
54 | } | 56 | } |
55 | 57 | ||
56 | static int event__repipe_task(event_t *self, struct sample_data *sample, | 58 | static int perf_event__repipe_task(union perf_event *event, |
57 | struct perf_session *session) | 59 | struct perf_sample *sample, |
60 | struct perf_session *session) | ||
58 | { | 61 | { |
59 | int err; | 62 | int err; |
60 | 63 | ||
61 | err = event__process_task(self, sample, session); | 64 | err = perf_event__process_task(event, sample, session); |
62 | event__repipe(self, sample, session); | 65 | perf_event__repipe(event, sample, session); |
63 | 66 | ||
64 | return err; | 67 | return err; |
65 | } | 68 | } |
66 | 69 | ||
67 | static int event__repipe_tracing_data(event_t *self, | 70 | static int perf_event__repipe_tracing_data(union perf_event *event, |
68 | struct perf_session *session) | 71 | struct perf_session *session) |
69 | { | 72 | { |
70 | int err; | 73 | int err; |
71 | 74 | ||
72 | event__repipe_synth(self, session); | 75 | perf_event__repipe_synth(event, session); |
73 | err = event__process_tracing_data(self, session); | 76 | err = perf_event__process_tracing_data(event, session); |
74 | 77 | ||
75 | return err; | 78 | return err; |
76 | } | 79 | } |
@@ -109,8 +112,8 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) | |||
109 | if (self->kernel) | 112 | if (self->kernel) |
110 | misc = PERF_RECORD_MISC_KERNEL; | 113 | misc = PERF_RECORD_MISC_KERNEL; |
111 | 114 | ||
112 | err = event__synthesize_build_id(self, misc, event__repipe, | 115 | err = perf_event__synthesize_build_id(self, misc, perf_event__repipe, |
113 | machine, session); | 116 | machine, session); |
114 | if (err) { | 117 | if (err) { |
115 | pr_err("Can't synthesize build_id event for %s\n", self->long_name); | 118 | pr_err("Can't synthesize build_id event for %s\n", self->long_name); |
116 | return -1; | 119 | return -1; |
@@ -119,8 +122,9 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) | |||
119 | return 0; | 122 | return 0; |
120 | } | 123 | } |
121 | 124 | ||
122 | static int event__inject_buildid(event_t *event, struct sample_data *sample, | 125 | static int perf_event__inject_buildid(union perf_event *event, |
123 | struct perf_session *session) | 126 | struct perf_sample *sample, |
127 | struct perf_session *session) | ||
124 | { | 128 | { |
125 | struct addr_location al; | 129 | struct addr_location al; |
126 | struct thread *thread; | 130 | struct thread *thread; |
@@ -155,24 +159,24 @@ static int event__inject_buildid(event_t *event, struct sample_data *sample, | |||
155 | } | 159 | } |
156 | 160 | ||
157 | repipe: | 161 | repipe: |
158 | event__repipe(event, sample, session); | 162 | perf_event__repipe(event, sample, session); |
159 | return 0; | 163 | return 0; |
160 | } | 164 | } |
161 | 165 | ||
162 | struct perf_event_ops inject_ops = { | 166 | struct perf_event_ops inject_ops = { |
163 | .sample = event__repipe, | 167 | .sample = perf_event__repipe, |
164 | .mmap = event__repipe, | 168 | .mmap = perf_event__repipe, |
165 | .comm = event__repipe, | 169 | .comm = perf_event__repipe, |
166 | .fork = event__repipe, | 170 | .fork = perf_event__repipe, |
167 | .exit = event__repipe, | 171 | .exit = perf_event__repipe, |
168 | .lost = event__repipe, | 172 | .lost = perf_event__repipe, |
169 | .read = event__repipe, | 173 | .read = perf_event__repipe, |
170 | .throttle = event__repipe, | 174 | .throttle = perf_event__repipe, |
171 | .unthrottle = event__repipe, | 175 | .unthrottle = perf_event__repipe, |
172 | .attr = event__repipe_synth, | 176 | .attr = perf_event__repipe_synth, |
173 | .event_type = event__repipe_synth, | 177 | .event_type = perf_event__repipe_synth, |
174 | .tracing_data = event__repipe_synth, | 178 | .tracing_data = perf_event__repipe_synth, |
175 | .build_id = event__repipe_synth, | 179 | .build_id = perf_event__repipe_synth, |
176 | }; | 180 | }; |
177 | 181 | ||
178 | extern volatile int session_done; | 182 | extern volatile int session_done; |
@@ -190,10 +194,10 @@ static int __cmd_inject(void) | |||
190 | signal(SIGINT, sig_handler); | 194 | signal(SIGINT, sig_handler); |
191 | 195 | ||
192 | if (inject_build_ids) { | 196 | if (inject_build_ids) { |
193 | inject_ops.sample = event__inject_buildid; | 197 | inject_ops.sample = perf_event__inject_buildid; |
194 | inject_ops.mmap = event__repipe_mmap; | 198 | inject_ops.mmap = perf_event__repipe_mmap; |
195 | inject_ops.fork = event__repipe_task; | 199 | inject_ops.fork = perf_event__repipe_task; |
196 | inject_ops.tracing_data = event__repipe_tracing_data; | 200 | inject_ops.tracing_data = perf_event__repipe_tracing_data; |
197 | } | 201 | } |
198 | 202 | ||
199 | session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); | 203 | session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index d97256d65980..7f618f4e7b79 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -275,9 +275,8 @@ static void process_free_event(void *data, | |||
275 | s_alloc->alloc_cpu = -1; | 275 | s_alloc->alloc_cpu = -1; |
276 | } | 276 | } |
277 | 277 | ||
278 | static void | 278 | static void process_raw_event(union perf_event *raw_event __used, void *data, |
279 | process_raw_event(event_t *raw_event __used, void *data, | 279 | int cpu, u64 timestamp, struct thread *thread) |
280 | int cpu, u64 timestamp, struct thread *thread) | ||
281 | { | 280 | { |
282 | struct event *event; | 281 | struct event *event; |
283 | int type; | 282 | int type; |
@@ -304,7 +303,8 @@ process_raw_event(event_t *raw_event __used, void *data, | |||
304 | } | 303 | } |
305 | } | 304 | } |
306 | 305 | ||
307 | static int process_sample_event(event_t *event, struct sample_data *sample, | 306 | static int process_sample_event(union perf_event *event, |
307 | struct perf_sample *sample, | ||
308 | struct perf_session *session) | 308 | struct perf_session *session) |
309 | { | 309 | { |
310 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | 310 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
@@ -325,7 +325,7 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
325 | 325 | ||
326 | static struct perf_event_ops event_ops = { | 326 | static struct perf_event_ops event_ops = { |
327 | .sample = process_sample_event, | 327 | .sample = process_sample_event, |
328 | .comm = event__process_comm, | 328 | .comm = perf_event__process_comm, |
329 | .ordered_samples = true, | 329 | .ordered_samples = true, |
330 | }; | 330 | }; |
331 | 331 | ||
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index d88c6961274c..6313b6eb3ebb 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * | 5 | * |
6 | * Copyright (C) 2009, Thomas Gleixner <tglx@linutronix.de> | 6 | * Copyright (C) 2009, Thomas Gleixner <tglx@linutronix.de> |
7 | * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar <mingo@redhat.com> | 7 | * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar <mingo@redhat.com> |
8 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | 9 | */ |
9 | #include "builtin.h" | 10 | #include "builtin.h" |
10 | 11 | ||
@@ -13,9 +14,47 @@ | |||
13 | #include "util/parse-events.h" | 14 | #include "util/parse-events.h" |
14 | #include "util/cache.h" | 15 | #include "util/cache.h" |
15 | 16 | ||
16 | int cmd_list(int argc __used, const char **argv __used, const char *prefix __used) | 17 | int cmd_list(int argc, const char **argv, const char *prefix __used) |
17 | { | 18 | { |
18 | setup_pager(); | 19 | setup_pager(); |
19 | print_events(); | 20 | |
21 | if (argc == 1) | ||
22 | print_events(NULL); | ||
23 | else { | ||
24 | int i; | ||
25 | |||
26 | for (i = 1; i < argc; ++i) { | ||
27 | if (i > 1) | ||
28 | putchar('\n'); | ||
29 | if (strncmp(argv[i], "tracepoint", 10) == 0) | ||
30 | print_tracepoint_events(NULL, NULL); | ||
31 | else if (strcmp(argv[i], "hw") == 0 || | ||
32 | strcmp(argv[i], "hardware") == 0) | ||
33 | print_events_type(PERF_TYPE_HARDWARE); | ||
34 | else if (strcmp(argv[i], "sw") == 0 || | ||
35 | strcmp(argv[i], "software") == 0) | ||
36 | print_events_type(PERF_TYPE_SOFTWARE); | ||
37 | else if (strcmp(argv[i], "cache") == 0 || | ||
38 | strcmp(argv[i], "hwcache") == 0) | ||
39 | print_hwcache_events(NULL); | ||
40 | else { | ||
41 | char *sep = strchr(argv[i], ':'), *s; | ||
42 | int sep_idx; | ||
43 | |||
44 | if (sep == NULL) { | ||
45 | print_events(argv[i]); | ||
46 | continue; | ||
47 | } | ||
48 | sep_idx = sep - argv[i]; | ||
49 | s = strdup(argv[i]); | ||
50 | if (s == NULL) | ||
51 | return -1; | ||
52 | |||
53 | s[sep_idx] = '\0'; | ||
54 | print_tracepoint_events(s, s + sep_idx + 1); | ||
55 | free(s); | ||
56 | } | ||
57 | } | ||
58 | } | ||
20 | return 0; | 59 | return 0; |
21 | } | 60 | } |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2b36defc5d73..7a2a79d2cf2c 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -202,9 +202,20 @@ static struct thread_stat *thread_stat_findnew_first(u32 tid) | |||
202 | SINGLE_KEY(nr_acquired) | 202 | SINGLE_KEY(nr_acquired) |
203 | SINGLE_KEY(nr_contended) | 203 | SINGLE_KEY(nr_contended) |
204 | SINGLE_KEY(wait_time_total) | 204 | SINGLE_KEY(wait_time_total) |
205 | SINGLE_KEY(wait_time_min) | ||
206 | SINGLE_KEY(wait_time_max) | 205 | SINGLE_KEY(wait_time_max) |
207 | 206 | ||
207 | static int lock_stat_key_wait_time_min(struct lock_stat *one, | ||
208 | struct lock_stat *two) | ||
209 | { | ||
210 | u64 s1 = one->wait_time_min; | ||
211 | u64 s2 = two->wait_time_min; | ||
212 | if (s1 == ULLONG_MAX) | ||
213 | s1 = 0; | ||
214 | if (s2 == ULLONG_MAX) | ||
215 | s2 = 0; | ||
216 | return s1 > s2; | ||
217 | } | ||
218 | |||
208 | struct lock_key { | 219 | struct lock_key { |
209 | /* | 220 | /* |
210 | * name: the value for specify by user | 221 | * name: the value for specify by user |
@@ -834,14 +845,14 @@ static void dump_info(void) | |||
834 | die("Unknown type of information\n"); | 845 | die("Unknown type of information\n"); |
835 | } | 846 | } |
836 | 847 | ||
837 | static int process_sample_event(event_t *self, struct sample_data *sample, | 848 | static int process_sample_event(union perf_event *event, struct perf_sample *sample, |
838 | struct perf_session *s) | 849 | struct perf_session *s) |
839 | { | 850 | { |
840 | struct thread *thread = perf_session__findnew(s, sample->tid); | 851 | struct thread *thread = perf_session__findnew(s, sample->tid); |
841 | 852 | ||
842 | if (thread == NULL) { | 853 | if (thread == NULL) { |
843 | pr_debug("problem processing %d event, skipping it.\n", | 854 | pr_debug("problem processing %d event, skipping it.\n", |
844 | self->header.type); | 855 | event->header.type); |
845 | return -1; | 856 | return -1; |
846 | } | 857 | } |
847 | 858 | ||
@@ -852,7 +863,7 @@ static int process_sample_event(event_t *self, struct sample_data *sample, | |||
852 | 863 | ||
853 | static struct perf_event_ops eops = { | 864 | static struct perf_event_ops eops = { |
854 | .sample = process_sample_event, | 865 | .sample = process_sample_event, |
855 | .comm = event__process_comm, | 866 | .comm = perf_event__process_comm, |
856 | .ordered_samples = true, | 867 | .ordered_samples = true, |
857 | }; | 868 | }; |
858 | 869 | ||
@@ -893,7 +904,7 @@ static const char * const report_usage[] = { | |||
893 | 904 | ||
894 | static const struct option report_options[] = { | 905 | static const struct option report_options[] = { |
895 | OPT_STRING('k', "key", &sort_key, "acquired", | 906 | OPT_STRING('k', "key", &sort_key, "acquired", |
896 | "key for sorting"), | 907 | "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), |
897 | /* TODO: type */ | 908 | /* TODO: type */ |
898 | OPT_END() | 909 | OPT_END() |
899 | }; | 910 | }; |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index add163c9f0e7..2c0e64d0b4aa 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include "builtin.h" | 36 | #include "builtin.h" |
37 | #include "util/util.h" | 37 | #include "util/util.h" |
38 | #include "util/strlist.h" | 38 | #include "util/strlist.h" |
39 | #include "util/strfilter.h" | ||
39 | #include "util/symbol.h" | 40 | #include "util/symbol.h" |
40 | #include "util/debug.h" | 41 | #include "util/debug.h" |
41 | #include "util/debugfs.h" | 42 | #include "util/debugfs.h" |
@@ -43,6 +44,8 @@ | |||
43 | #include "util/probe-finder.h" | 44 | #include "util/probe-finder.h" |
44 | #include "util/probe-event.h" | 45 | #include "util/probe-event.h" |
45 | 46 | ||
47 | #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" | ||
48 | #define DEFAULT_FUNC_FILTER "!_*" | ||
46 | #define MAX_PATH_LEN 256 | 49 | #define MAX_PATH_LEN 256 |
47 | 50 | ||
48 | /* Session management structure */ | 51 | /* Session management structure */ |
@@ -52,6 +55,7 @@ static struct { | |||
52 | bool show_lines; | 55 | bool show_lines; |
53 | bool show_vars; | 56 | bool show_vars; |
54 | bool show_ext_vars; | 57 | bool show_ext_vars; |
58 | bool show_funcs; | ||
55 | bool mod_events; | 59 | bool mod_events; |
56 | int nevents; | 60 | int nevents; |
57 | struct perf_probe_event events[MAX_PROBES]; | 61 | struct perf_probe_event events[MAX_PROBES]; |
@@ -59,6 +63,7 @@ static struct { | |||
59 | struct line_range line_range; | 63 | struct line_range line_range; |
60 | const char *target_module; | 64 | const char *target_module; |
61 | int max_probe_points; | 65 | int max_probe_points; |
66 | struct strfilter *filter; | ||
62 | } params; | 67 | } params; |
63 | 68 | ||
64 | /* Parse an event definition. Note that any error must die. */ | 69 | /* Parse an event definition. Note that any error must die. */ |
@@ -157,6 +162,27 @@ static int opt_show_vars(const struct option *opt __used, | |||
157 | } | 162 | } |
158 | #endif | 163 | #endif |
159 | 164 | ||
165 | static int opt_set_filter(const struct option *opt __used, | ||
166 | const char *str, int unset __used) | ||
167 | { | ||
168 | const char *err; | ||
169 | |||
170 | if (str) { | ||
171 | pr_debug2("Set filter: %s\n", str); | ||
172 | if (params.filter) | ||
173 | strfilter__delete(params.filter); | ||
174 | params.filter = strfilter__new(str, &err); | ||
175 | if (!params.filter) { | ||
176 | pr_err("Filter parse error at %td.\n", err - str + 1); | ||
177 | pr_err("Source: \"%s\"\n", str); | ||
178 | pr_err(" %*c\n", (int)(err - str + 1), '^'); | ||
179 | return -EINVAL; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
160 | static const char * const probe_usage[] = { | 186 | static const char * const probe_usage[] = { |
161 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", | 187 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", |
162 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | 188 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", |
@@ -221,6 +247,13 @@ static const struct option options[] = { | |||
221 | OPT__DRY_RUN(&probe_event_dry_run), | 247 | OPT__DRY_RUN(&probe_event_dry_run), |
222 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 248 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
223 | "Set how many probe points can be found for a probe."), | 249 | "Set how many probe points can be found for a probe."), |
250 | OPT_BOOLEAN('F', "funcs", ¶ms.show_funcs, | ||
251 | "Show potential probe-able functions."), | ||
252 | OPT_CALLBACK('\0', "filter", NULL, | ||
253 | "[!]FILTER", "Set a filter (with --vars/funcs only)\n" | ||
254 | "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" | ||
255 | "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)", | ||
256 | opt_set_filter), | ||
224 | OPT_END() | 257 | OPT_END() |
225 | }; | 258 | }; |
226 | 259 | ||
@@ -246,7 +279,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
246 | params.max_probe_points = MAX_PROBES; | 279 | params.max_probe_points = MAX_PROBES; |
247 | 280 | ||
248 | if ((!params.nevents && !params.dellist && !params.list_events && | 281 | if ((!params.nevents && !params.dellist && !params.list_events && |
249 | !params.show_lines)) | 282 | !params.show_lines && !params.show_funcs)) |
250 | usage_with_options(probe_usage, options); | 283 | usage_with_options(probe_usage, options); |
251 | 284 | ||
252 | /* | 285 | /* |
@@ -267,12 +300,41 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
267 | pr_err(" Error: Don't use --list with --vars.\n"); | 300 | pr_err(" Error: Don't use --list with --vars.\n"); |
268 | usage_with_options(probe_usage, options); | 301 | usage_with_options(probe_usage, options); |
269 | } | 302 | } |
303 | if (params.show_funcs) { | ||
304 | pr_err(" Error: Don't use --list with --funcs.\n"); | ||
305 | usage_with_options(probe_usage, options); | ||
306 | } | ||
270 | ret = show_perf_probe_events(); | 307 | ret = show_perf_probe_events(); |
271 | if (ret < 0) | 308 | if (ret < 0) |
272 | pr_err(" Error: Failed to show event list. (%d)\n", | 309 | pr_err(" Error: Failed to show event list. (%d)\n", |
273 | ret); | 310 | ret); |
274 | return ret; | 311 | return ret; |
275 | } | 312 | } |
313 | if (params.show_funcs) { | ||
314 | if (params.nevents != 0 || params.dellist) { | ||
315 | pr_err(" Error: Don't use --funcs with" | ||
316 | " --add/--del.\n"); | ||
317 | usage_with_options(probe_usage, options); | ||
318 | } | ||
319 | if (params.show_lines) { | ||
320 | pr_err(" Error: Don't use --funcs with --line.\n"); | ||
321 | usage_with_options(probe_usage, options); | ||
322 | } | ||
323 | if (params.show_vars) { | ||
324 | pr_err(" Error: Don't use --funcs with --vars.\n"); | ||
325 | usage_with_options(probe_usage, options); | ||
326 | } | ||
327 | if (!params.filter) | ||
328 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, | ||
329 | NULL); | ||
330 | ret = show_available_funcs(params.target_module, | ||
331 | params.filter); | ||
332 | strfilter__delete(params.filter); | ||
333 | if (ret < 0) | ||
334 | pr_err(" Error: Failed to show functions." | ||
335 | " (%d)\n", ret); | ||
336 | return ret; | ||
337 | } | ||
276 | 338 | ||
277 | #ifdef DWARF_SUPPORT | 339 | #ifdef DWARF_SUPPORT |
278 | if (params.show_lines) { | 340 | if (params.show_lines) { |
@@ -297,10 +359,16 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
297 | " --add/--del.\n"); | 359 | " --add/--del.\n"); |
298 | usage_with_options(probe_usage, options); | 360 | usage_with_options(probe_usage, options); |
299 | } | 361 | } |
362 | if (!params.filter) | ||
363 | params.filter = strfilter__new(DEFAULT_VAR_FILTER, | ||
364 | NULL); | ||
365 | |||
300 | ret = show_available_vars(params.events, params.nevents, | 366 | ret = show_available_vars(params.events, params.nevents, |
301 | params.max_probe_points, | 367 | params.max_probe_points, |
302 | params.target_module, | 368 | params.target_module, |
369 | params.filter, | ||
303 | params.show_ext_vars); | 370 | params.show_ext_vars); |
371 | strfilter__delete(params.filter); | ||
304 | if (ret < 0) | 372 | if (ret < 0) |
305 | pr_err(" Error: Failed to show vars. (%d)\n", ret); | 373 | pr_err(" Error: Failed to show vars. (%d)\n", ret); |
306 | return ret; | 374 | return ret; |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 60cac6f92e8b..6febcc168a8c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -18,11 +18,13 @@ | |||
18 | 18 | ||
19 | #include "util/header.h" | 19 | #include "util/header.h" |
20 | #include "util/event.h" | 20 | #include "util/event.h" |
21 | #include "util/evlist.h" | ||
21 | #include "util/evsel.h" | 22 | #include "util/evsel.h" |
22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
23 | #include "util/session.h" | 24 | #include "util/session.h" |
24 | #include "util/symbol.h" | 25 | #include "util/symbol.h" |
25 | #include "util/cpumap.h" | 26 | #include "util/cpumap.h" |
27 | #include "util/thread_map.h" | ||
26 | 28 | ||
27 | #include <unistd.h> | 29 | #include <unistd.h> |
28 | #include <sched.h> | 30 | #include <sched.h> |
@@ -37,16 +39,14 @@ enum write_mode_t { | |||
37 | 39 | ||
38 | static u64 user_interval = ULLONG_MAX; | 40 | static u64 user_interval = ULLONG_MAX; |
39 | static u64 default_interval = 0; | 41 | static u64 default_interval = 0; |
40 | static u64 sample_type; | ||
41 | 42 | ||
42 | static struct cpu_map *cpus; | ||
43 | static unsigned int page_size; | 43 | static unsigned int page_size; |
44 | static unsigned int mmap_pages = 128; | 44 | static unsigned int mmap_pages = 128; |
45 | static unsigned int user_freq = UINT_MAX; | 45 | static unsigned int user_freq = UINT_MAX; |
46 | static int freq = 1000; | 46 | static int freq = 1000; |
47 | static int output; | 47 | static int output; |
48 | static int pipe_output = 0; | 48 | static int pipe_output = 0; |
49 | static const char *output_name = "perf.data"; | 49 | static const char *output_name = NULL; |
50 | static int group = 0; | 50 | static int group = 0; |
51 | static int realtime_prio = 0; | 51 | static int realtime_prio = 0; |
52 | static bool nodelay = false; | 52 | static bool nodelay = false; |
@@ -55,7 +55,6 @@ static bool sample_id_all_avail = true; | |||
55 | static bool system_wide = false; | 55 | static bool system_wide = false; |
56 | static pid_t target_pid = -1; | 56 | static pid_t target_pid = -1; |
57 | static pid_t target_tid = -1; | 57 | static pid_t target_tid = -1; |
58 | static struct thread_map *threads; | ||
59 | static pid_t child_pid = -1; | 58 | static pid_t child_pid = -1; |
60 | static bool no_inherit = false; | 59 | static bool no_inherit = false; |
61 | static enum write_mode_t write_mode = WRITE_FORCE; | 60 | static enum write_mode_t write_mode = WRITE_FORCE; |
@@ -66,51 +65,17 @@ static bool sample_address = false; | |||
66 | static bool sample_time = false; | 65 | static bool sample_time = false; |
67 | static bool no_buildid = false; | 66 | static bool no_buildid = false; |
68 | static bool no_buildid_cache = false; | 67 | static bool no_buildid_cache = false; |
68 | static struct perf_evlist *evsel_list; | ||
69 | 69 | ||
70 | static long samples = 0; | 70 | static long samples = 0; |
71 | static u64 bytes_written = 0; | 71 | static u64 bytes_written = 0; |
72 | 72 | ||
73 | static struct pollfd *event_array; | ||
74 | |||
75 | static int nr_poll = 0; | ||
76 | static int nr_cpu = 0; | ||
77 | |||
78 | static int file_new = 1; | 73 | static int file_new = 1; |
79 | static off_t post_processing_offset; | 74 | static off_t post_processing_offset; |
80 | 75 | ||
81 | static struct perf_session *session; | 76 | static struct perf_session *session; |
82 | static const char *cpu_list; | 77 | static const char *cpu_list; |
83 | 78 | ||
84 | struct mmap_data { | ||
85 | void *base; | ||
86 | unsigned int mask; | ||
87 | unsigned int prev; | ||
88 | }; | ||
89 | |||
90 | static struct mmap_data mmap_array[MAX_NR_CPUS]; | ||
91 | |||
92 | static unsigned long mmap_read_head(struct mmap_data *md) | ||
93 | { | ||
94 | struct perf_event_mmap_page *pc = md->base; | ||
95 | long head; | ||
96 | |||
97 | head = pc->data_head; | ||
98 | rmb(); | ||
99 | |||
100 | return head; | ||
101 | } | ||
102 | |||
103 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) | ||
104 | { | ||
105 | struct perf_event_mmap_page *pc = md->base; | ||
106 | |||
107 | /* | ||
108 | * ensure all reads are done before we write the tail out. | ||
109 | */ | ||
110 | /* mb(); */ | ||
111 | pc->data_tail = tail; | ||
112 | } | ||
113 | |||
114 | static void advance_output(size_t size) | 79 | static void advance_output(size_t size) |
115 | { | 80 | { |
116 | bytes_written += size; | 81 | bytes_written += size; |
@@ -131,42 +96,26 @@ static void write_output(void *buf, size_t size) | |||
131 | } | 96 | } |
132 | } | 97 | } |
133 | 98 | ||
134 | static int process_synthesized_event(event_t *event, | 99 | static int process_synthesized_event(union perf_event *event, |
135 | struct sample_data *sample __used, | 100 | struct perf_sample *sample __used, |
136 | struct perf_session *self __used) | 101 | struct perf_session *self __used) |
137 | { | 102 | { |
138 | write_output(event, event->header.size); | 103 | write_output(event, event->header.size); |
139 | return 0; | 104 | return 0; |
140 | } | 105 | } |
141 | 106 | ||
142 | static void mmap_read(struct mmap_data *md) | 107 | static void mmap_read(struct perf_mmap *md) |
143 | { | 108 | { |
144 | unsigned int head = mmap_read_head(md); | 109 | unsigned int head = perf_mmap__read_head(md); |
145 | unsigned int old = md->prev; | 110 | unsigned int old = md->prev; |
146 | unsigned char *data = md->base + page_size; | 111 | unsigned char *data = md->base + page_size; |
147 | unsigned long size; | 112 | unsigned long size; |
148 | void *buf; | 113 | void *buf; |
149 | int diff; | ||
150 | 114 | ||
151 | /* | 115 | if (old == head) |
152 | * If we're further behind than half the buffer, there's a chance | 116 | return; |
153 | * the writer will bite our tail and mess up the samples under us. | ||
154 | * | ||
155 | * If we somehow ended up ahead of the head, we got messed up. | ||
156 | * | ||
157 | * In either case, truncate and restart at head. | ||
158 | */ | ||
159 | diff = head - old; | ||
160 | if (diff < 0) { | ||
161 | fprintf(stderr, "WARNING: failed to keep up with mmap data\n"); | ||
162 | /* | ||
163 | * head points to a known good entry, start there. | ||
164 | */ | ||
165 | old = head; | ||
166 | } | ||
167 | 117 | ||
168 | if (old != head) | 118 | samples++; |
169 | samples++; | ||
170 | 119 | ||
171 | size = head - old; | 120 | size = head - old; |
172 | 121 | ||
@@ -185,7 +134,7 @@ static void mmap_read(struct mmap_data *md) | |||
185 | write_output(buf, size); | 134 | write_output(buf, size); |
186 | 135 | ||
187 | md->prev = old; | 136 | md->prev = old; |
188 | mmap_write_tail(md, old); | 137 | perf_mmap__write_tail(md, old); |
189 | } | 138 | } |
190 | 139 | ||
191 | static volatile int done = 0; | 140 | static volatile int done = 0; |
@@ -209,53 +158,10 @@ static void sig_atexit(void) | |||
209 | kill(getpid(), signr); | 158 | kill(getpid(), signr); |
210 | } | 159 | } |
211 | 160 | ||
212 | static int group_fd; | 161 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) |
213 | |||
214 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | ||
215 | { | ||
216 | struct perf_header_attr *h_attr; | ||
217 | |||
218 | if (nr < session->header.attrs) { | ||
219 | h_attr = session->header.attr[nr]; | ||
220 | } else { | ||
221 | h_attr = perf_header_attr__new(a); | ||
222 | if (h_attr != NULL) | ||
223 | if (perf_header__add_attr(&session->header, h_attr) < 0) { | ||
224 | perf_header_attr__delete(h_attr); | ||
225 | h_attr = NULL; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | return h_attr; | ||
230 | } | ||
231 | |||
232 | static void create_counter(struct perf_evsel *evsel, int cpu) | ||
233 | { | 162 | { |
234 | char *filter = evsel->filter; | ||
235 | struct perf_event_attr *attr = &evsel->attr; | 163 | struct perf_event_attr *attr = &evsel->attr; |
236 | struct perf_header_attr *h_attr; | ||
237 | int track = !evsel->idx; /* only the first counter needs these */ | 164 | int track = !evsel->idx; /* only the first counter needs these */ |
238 | int thread_index; | ||
239 | int ret; | ||
240 | struct { | ||
241 | u64 count; | ||
242 | u64 time_enabled; | ||
243 | u64 time_running; | ||
244 | u64 id; | ||
245 | } read_data; | ||
246 | /* | ||
247 | * Check if parse_single_tracepoint_event has already asked for | ||
248 | * PERF_SAMPLE_TIME. | ||
249 | * | ||
250 | * XXX this is kludgy but short term fix for problems introduced by | ||
251 | * eac23d1c that broke 'perf script' by having different sample_types | ||
252 | * when using multiple tracepoint events when we use a perf binary | ||
253 | * that tries to use sample_id_all on an older kernel. | ||
254 | * | ||
255 | * We need to move counter creation to perf_session, support | ||
256 | * different sample_types, etc. | ||
257 | */ | ||
258 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
259 | 165 | ||
260 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 166 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
261 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 167 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -263,7 +169,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
263 | 169 | ||
264 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 170 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
265 | 171 | ||
266 | if (nr_counters > 1) | 172 | if (evlist->nr_entries > 1) |
267 | attr->sample_type |= PERF_SAMPLE_ID; | 173 | attr->sample_type |= PERF_SAMPLE_ID; |
268 | 174 | ||
269 | /* | 175 | /* |
@@ -315,19 +221,58 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
315 | 221 | ||
316 | attr->mmap = track; | 222 | attr->mmap = track; |
317 | attr->comm = track; | 223 | attr->comm = track; |
318 | attr->inherit = !no_inherit; | 224 | |
319 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 225 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
320 | attr->disabled = 1; | 226 | attr->disabled = 1; |
321 | attr->enable_on_exec = 1; | 227 | attr->enable_on_exec = 1; |
322 | } | 228 | } |
323 | retry_sample_id: | 229 | } |
324 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
325 | 230 | ||
326 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 231 | static bool perf_evlist__equal(struct perf_evlist *evlist, |
327 | try_again: | 232 | struct perf_evlist *other) |
328 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); | 233 | { |
234 | struct perf_evsel *pos, *pair; | ||
235 | |||
236 | if (evlist->nr_entries != other->nr_entries) | ||
237 | return false; | ||
238 | |||
239 | pair = list_entry(other->entries.next, struct perf_evsel, node); | ||
329 | 240 | ||
330 | if (FD(evsel, nr_cpu, thread_index) < 0) { | 241 | list_for_each_entry(pos, &evlist->entries, node) { |
242 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) | ||
243 | return false; | ||
244 | pair = list_entry(pair->node.next, struct perf_evsel, node); | ||
245 | } | ||
246 | |||
247 | return true; | ||
248 | } | ||
249 | |||
250 | static void open_counters(struct perf_evlist *evlist) | ||
251 | { | ||
252 | struct perf_evsel *pos; | ||
253 | |||
254 | list_for_each_entry(pos, &evlist->entries, node) { | ||
255 | struct perf_event_attr *attr = &pos->attr; | ||
256 | /* | ||
257 | * Check if parse_single_tracepoint_event has already asked for | ||
258 | * PERF_SAMPLE_TIME. | ||
259 | * | ||
260 | * XXX this is kludgy but short term fix for problems introduced by | ||
261 | * eac23d1c that broke 'perf script' by having different sample_types | ||
262 | * when using multiple tracepoint events when we use a perf binary | ||
263 | * that tries to use sample_id_all on an older kernel. | ||
264 | * | ||
265 | * We need to move counter creation to perf_session, support | ||
266 | * different sample_types, etc. | ||
267 | */ | ||
268 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
269 | |||
270 | config_attr(pos, evlist); | ||
271 | retry_sample_id: | ||
272 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
273 | try_again: | ||
274 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group, | ||
275 | !no_inherit) < 0) { | ||
331 | int err = errno; | 276 | int err = errno; |
332 | 277 | ||
333 | if (err == EPERM || err == EACCES) | 278 | if (err == EPERM || err == EACCES) |
@@ -364,7 +309,7 @@ try_again: | |||
364 | } | 309 | } |
365 | printf("\n"); | 310 | printf("\n"); |
366 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 311 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
367 | FD(evsel, nr_cpu, thread_index), strerror(err)); | 312 | err, strerror(err)); |
368 | 313 | ||
369 | #if defined(__i386__) || defined(__x86_64__) | 314 | #if defined(__i386__) || defined(__x86_64__) |
370 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 315 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -375,90 +320,28 @@ try_again: | |||
375 | #endif | 320 | #endif |
376 | 321 | ||
377 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 322 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
378 | exit(-1); | ||
379 | } | 323 | } |
324 | } | ||
380 | 325 | ||
381 | h_attr = get_header_attr(attr, evsel->idx); | 326 | if (perf_evlist__set_filters(evlist)) { |
382 | if (h_attr == NULL) | 327 | error("failed to set filter with %d (%s)\n", errno, |
383 | die("nomem\n"); | 328 | strerror(errno)); |
329 | exit(-1); | ||
330 | } | ||
384 | 331 | ||
385 | if (!file_new) { | 332 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) |
386 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | 333 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
387 | fprintf(stderr, "incompatible append\n"); | ||
388 | exit(-1); | ||
389 | } | ||
390 | } | ||
391 | 334 | ||
392 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | 335 | if (file_new) |
393 | perror("Unable to read perf file descriptor"); | 336 | session->evlist = evlist; |
337 | else { | ||
338 | if (!perf_evlist__equal(session->evlist, evlist)) { | ||
339 | fprintf(stderr, "incompatible append\n"); | ||
394 | exit(-1); | 340 | exit(-1); |
395 | } | 341 | } |
342 | } | ||
396 | 343 | ||
397 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | 344 | perf_session__update_sample_type(session); |
398 | pr_warning("Not enough memory to add id\n"); | ||
399 | exit(-1); | ||
400 | } | ||
401 | |||
402 | assert(FD(evsel, nr_cpu, thread_index) >= 0); | ||
403 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); | ||
404 | |||
405 | /* | ||
406 | * First counter acts as the group leader: | ||
407 | */ | ||
408 | if (group && group_fd == -1) | ||
409 | group_fd = FD(evsel, nr_cpu, thread_index); | ||
410 | |||
411 | if (evsel->idx || thread_index) { | ||
412 | struct perf_evsel *first; | ||
413 | first = list_entry(evsel_list.next, struct perf_evsel, node); | ||
414 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
415 | PERF_EVENT_IOC_SET_OUTPUT, | ||
416 | FD(first, nr_cpu, 0)); | ||
417 | if (ret) { | ||
418 | error("failed to set output: %d (%s)\n", errno, | ||
419 | strerror(errno)); | ||
420 | exit(-1); | ||
421 | } | ||
422 | } else { | ||
423 | mmap_array[nr_cpu].prev = 0; | ||
424 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | ||
425 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
426 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); | ||
427 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | ||
428 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
429 | exit(-1); | ||
430 | } | ||
431 | |||
432 | event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); | ||
433 | event_array[nr_poll].events = POLLIN; | ||
434 | nr_poll++; | ||
435 | } | ||
436 | |||
437 | if (filter != NULL) { | ||
438 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
439 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
440 | if (ret) { | ||
441 | error("failed to set filter with %d (%s)\n", errno, | ||
442 | strerror(errno)); | ||
443 | exit(-1); | ||
444 | } | ||
445 | } | ||
446 | } | ||
447 | |||
448 | if (!sample_type) | ||
449 | sample_type = attr->sample_type; | ||
450 | } | ||
451 | |||
452 | static void open_counters(int cpu) | ||
453 | { | ||
454 | struct perf_evsel *pos; | ||
455 | |||
456 | group_fd = -1; | ||
457 | |||
458 | list_for_each_entry(pos, &evsel_list, node) | ||
459 | create_counter(pos, cpu); | ||
460 | |||
461 | nr_cpu++; | ||
462 | } | 345 | } |
463 | 346 | ||
464 | static int process_buildids(void) | 347 | static int process_buildids(void) |
@@ -481,14 +364,14 @@ static void atexit_header(void) | |||
481 | 364 | ||
482 | if (!no_buildid) | 365 | if (!no_buildid) |
483 | process_buildids(); | 366 | process_buildids(); |
484 | perf_header__write(&session->header, output, true); | 367 | perf_session__write_header(session, evsel_list, output, true); |
485 | perf_session__delete(session); | 368 | perf_session__delete(session); |
486 | perf_evsel_list__delete(); | 369 | perf_evlist__delete(evsel_list); |
487 | symbol__exit(); | 370 | symbol__exit(); |
488 | } | 371 | } |
489 | } | 372 | } |
490 | 373 | ||
491 | static void event__synthesize_guest_os(struct machine *machine, void *data) | 374 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) |
492 | { | 375 | { |
493 | int err; | 376 | int err; |
494 | struct perf_session *psession = data; | 377 | struct perf_session *psession = data; |
@@ -504,8 +387,8 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) | |||
504 | *method is used to avoid symbol missing when the first addr is | 387 | *method is used to avoid symbol missing when the first addr is |
505 | *in module instead of in guest kernel. | 388 | *in module instead of in guest kernel. |
506 | */ | 389 | */ |
507 | err = event__synthesize_modules(process_synthesized_event, | 390 | err = perf_event__synthesize_modules(process_synthesized_event, |
508 | psession, machine); | 391 | psession, machine); |
509 | if (err < 0) | 392 | if (err < 0) |
510 | pr_err("Couldn't record guest kernel [%d]'s reference" | 393 | pr_err("Couldn't record guest kernel [%d]'s reference" |
511 | " relocation symbol.\n", machine->pid); | 394 | " relocation symbol.\n", machine->pid); |
@@ -514,11 +397,12 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) | |||
514 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms | 397 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms |
515 | * have no _text sometimes. | 398 | * have no _text sometimes. |
516 | */ | 399 | */ |
517 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 400 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
518 | psession, machine, "_text"); | 401 | psession, machine, "_text"); |
519 | if (err < 0) | 402 | if (err < 0) |
520 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 403 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
521 | psession, machine, "_stext"); | 404 | psession, machine, |
405 | "_stext"); | ||
522 | if (err < 0) | 406 | if (err < 0) |
523 | pr_err("Couldn't record guest kernel [%d]'s reference" | 407 | pr_err("Couldn't record guest kernel [%d]'s reference" |
524 | " relocation symbol.\n", machine->pid); | 408 | " relocation symbol.\n", machine->pid); |
@@ -533,9 +417,9 @@ static void mmap_read_all(void) | |||
533 | { | 417 | { |
534 | int i; | 418 | int i; |
535 | 419 | ||
536 | for (i = 0; i < nr_cpu; i++) { | 420 | for (i = 0; i < evsel_list->cpus->nr; i++) { |
537 | if (mmap_array[i].base) | 421 | if (evsel_list->mmap[i].base) |
538 | mmap_read(&mmap_array[i]); | 422 | mmap_read(&evsel_list->mmap[i]); |
539 | } | 423 | } |
540 | 424 | ||
541 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) | 425 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) |
@@ -566,18 +450,26 @@ static int __cmd_record(int argc, const char **argv) | |||
566 | exit(-1); | 450 | exit(-1); |
567 | } | 451 | } |
568 | 452 | ||
569 | if (!strcmp(output_name, "-")) | 453 | if (!output_name) { |
570 | pipe_output = 1; | 454 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
571 | else if (!stat(output_name, &st) && st.st_size) { | 455 | pipe_output = 1; |
572 | if (write_mode == WRITE_FORCE) { | 456 | else |
573 | char oldname[PATH_MAX]; | 457 | output_name = "perf.data"; |
574 | snprintf(oldname, sizeof(oldname), "%s.old", | 458 | } |
575 | output_name); | 459 | if (output_name) { |
576 | unlink(oldname); | 460 | if (!strcmp(output_name, "-")) |
577 | rename(output_name, oldname); | 461 | pipe_output = 1; |
462 | else if (!stat(output_name, &st) && st.st_size) { | ||
463 | if (write_mode == WRITE_FORCE) { | ||
464 | char oldname[PATH_MAX]; | ||
465 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
466 | output_name); | ||
467 | unlink(oldname); | ||
468 | rename(output_name, oldname); | ||
469 | } | ||
470 | } else if (write_mode == WRITE_APPEND) { | ||
471 | write_mode = WRITE_FORCE; | ||
578 | } | 472 | } |
579 | } else if (write_mode == WRITE_APPEND) { | ||
580 | write_mode = WRITE_FORCE; | ||
581 | } | 473 | } |
582 | 474 | ||
583 | flags = O_CREAT|O_RDWR; | 475 | flags = O_CREAT|O_RDWR; |
@@ -606,19 +498,14 @@ static int __cmd_record(int argc, const char **argv) | |||
606 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | 498 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); |
607 | 499 | ||
608 | if (!file_new) { | 500 | if (!file_new) { |
609 | err = perf_header__read(session, output); | 501 | err = perf_session__read_header(session, output); |
610 | if (err < 0) | 502 | if (err < 0) |
611 | goto out_delete_session; | 503 | goto out_delete_session; |
612 | } | 504 | } |
613 | 505 | ||
614 | if (have_tracepoints(&evsel_list)) | 506 | if (have_tracepoints(&evsel_list->entries)) |
615 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 507 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
616 | 508 | ||
617 | /* | ||
618 | * perf_session__delete(session) will be called at atexit_header() | ||
619 | */ | ||
620 | atexit(atexit_header); | ||
621 | |||
622 | if (forks) { | 509 | if (forks) { |
623 | child_pid = fork(); | 510 | child_pid = fork(); |
624 | if (child_pid < 0) { | 511 | if (child_pid < 0) { |
@@ -659,7 +546,7 @@ static int __cmd_record(int argc, const char **argv) | |||
659 | } | 546 | } |
660 | 547 | ||
661 | if (!system_wide && target_tid == -1 && target_pid == -1) | 548 | if (!system_wide && target_tid == -1 && target_pid == -1) |
662 | threads->map[0] = child_pid; | 549 | evsel_list->threads->map[0] = child_pid; |
663 | 550 | ||
664 | close(child_ready_pipe[1]); | 551 | close(child_ready_pipe[1]); |
665 | close(go_pipe[0]); | 552 | close(go_pipe[0]); |
@@ -673,46 +560,42 @@ static int __cmd_record(int argc, const char **argv) | |||
673 | close(child_ready_pipe[0]); | 560 | close(child_ready_pipe[0]); |
674 | } | 561 | } |
675 | 562 | ||
676 | if (!system_wide && no_inherit && !cpu_list) { | 563 | open_counters(evsel_list); |
677 | open_counters(-1); | ||
678 | } else { | ||
679 | for (i = 0; i < cpus->nr; i++) | ||
680 | open_counters(cpus->map[i]); | ||
681 | } | ||
682 | 564 | ||
683 | perf_session__set_sample_type(session, sample_type); | 565 | /* |
566 | * perf_session__delete(session) will be called at atexit_header() | ||
567 | */ | ||
568 | atexit(atexit_header); | ||
684 | 569 | ||
685 | if (pipe_output) { | 570 | if (pipe_output) { |
686 | err = perf_header__write_pipe(output); | 571 | err = perf_header__write_pipe(output); |
687 | if (err < 0) | 572 | if (err < 0) |
688 | return err; | 573 | return err; |
689 | } else if (file_new) { | 574 | } else if (file_new) { |
690 | err = perf_header__write(&session->header, output, false); | 575 | err = perf_session__write_header(session, evsel_list, |
576 | output, false); | ||
691 | if (err < 0) | 577 | if (err < 0) |
692 | return err; | 578 | return err; |
693 | } | 579 | } |
694 | 580 | ||
695 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 581 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
696 | 582 | ||
697 | perf_session__set_sample_id_all(session, sample_id_all_avail); | ||
698 | |||
699 | if (pipe_output) { | 583 | if (pipe_output) { |
700 | err = event__synthesize_attrs(&session->header, | 584 | err = perf_session__synthesize_attrs(session, |
701 | process_synthesized_event, | 585 | process_synthesized_event); |
702 | session); | ||
703 | if (err < 0) { | 586 | if (err < 0) { |
704 | pr_err("Couldn't synthesize attrs.\n"); | 587 | pr_err("Couldn't synthesize attrs.\n"); |
705 | return err; | 588 | return err; |
706 | } | 589 | } |
707 | 590 | ||
708 | err = event__synthesize_event_types(process_synthesized_event, | 591 | err = perf_event__synthesize_event_types(process_synthesized_event, |
709 | session); | 592 | session); |
710 | if (err < 0) { | 593 | if (err < 0) { |
711 | pr_err("Couldn't synthesize event_types.\n"); | 594 | pr_err("Couldn't synthesize event_types.\n"); |
712 | return err; | 595 | return err; |
713 | } | 596 | } |
714 | 597 | ||
715 | if (have_tracepoints(&evsel_list)) { | 598 | if (have_tracepoints(&evsel_list->entries)) { |
716 | /* | 599 | /* |
717 | * FIXME err <= 0 here actually means that | 600 | * FIXME err <= 0 here actually means that |
718 | * there were no tracepoints so its not really | 601 | * there were no tracepoints so its not really |
@@ -721,9 +604,9 @@ static int __cmd_record(int argc, const char **argv) | |||
721 | * return this more properly and also | 604 | * return this more properly and also |
722 | * propagate errors that now are calling die() | 605 | * propagate errors that now are calling die() |
723 | */ | 606 | */ |
724 | err = event__synthesize_tracing_data(output, &evsel_list, | 607 | err = perf_event__synthesize_tracing_data(output, evsel_list, |
725 | process_synthesized_event, | 608 | process_synthesized_event, |
726 | session); | 609 | session); |
727 | if (err <= 0) { | 610 | if (err <= 0) { |
728 | pr_err("Couldn't record tracing data.\n"); | 611 | pr_err("Couldn't record tracing data.\n"); |
729 | return err; | 612 | return err; |
@@ -738,31 +621,34 @@ static int __cmd_record(int argc, const char **argv) | |||
738 | return -1; | 621 | return -1; |
739 | } | 622 | } |
740 | 623 | ||
741 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 624 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
742 | session, machine, "_text"); | 625 | session, machine, "_text"); |
743 | if (err < 0) | 626 | if (err < 0) |
744 | err = event__synthesize_kernel_mmap(process_synthesized_event, | 627 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
745 | session, machine, "_stext"); | 628 | session, machine, "_stext"); |
746 | if (err < 0) | 629 | if (err < 0) |
747 | pr_err("Couldn't record kernel reference relocation symbol\n" | 630 | pr_err("Couldn't record kernel reference relocation symbol\n" |
748 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 631 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
749 | "Check /proc/kallsyms permission or run as root.\n"); | 632 | "Check /proc/kallsyms permission or run as root.\n"); |
750 | 633 | ||
751 | err = event__synthesize_modules(process_synthesized_event, | 634 | err = perf_event__synthesize_modules(process_synthesized_event, |
752 | session, machine); | 635 | session, machine); |
753 | if (err < 0) | 636 | if (err < 0) |
754 | pr_err("Couldn't record kernel module information.\n" | 637 | pr_err("Couldn't record kernel module information.\n" |
755 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 638 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
756 | "Check /proc/modules permission or run as root.\n"); | 639 | "Check /proc/modules permission or run as root.\n"); |
757 | 640 | ||
758 | if (perf_guest) | 641 | if (perf_guest) |
759 | perf_session__process_machines(session, event__synthesize_guest_os); | 642 | perf_session__process_machines(session, |
643 | perf_event__synthesize_guest_os); | ||
760 | 644 | ||
761 | if (!system_wide) | 645 | if (!system_wide) |
762 | event__synthesize_thread_map(threads, process_synthesized_event, | 646 | perf_event__synthesize_thread_map(evsel_list->threads, |
763 | session); | 647 | process_synthesized_event, |
648 | session); | ||
764 | else | 649 | else |
765 | event__synthesize_threads(process_synthesized_event, session); | 650 | perf_event__synthesize_threads(process_synthesized_event, |
651 | session); | ||
766 | 652 | ||
767 | if (realtime_prio) { | 653 | if (realtime_prio) { |
768 | struct sched_param param; | 654 | struct sched_param param; |
@@ -789,17 +675,17 @@ static int __cmd_record(int argc, const char **argv) | |||
789 | if (hits == samples) { | 675 | if (hits == samples) { |
790 | if (done) | 676 | if (done) |
791 | break; | 677 | break; |
792 | err = poll(event_array, nr_poll, -1); | 678 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); |
793 | waking++; | 679 | waking++; |
794 | } | 680 | } |
795 | 681 | ||
796 | if (done) { | 682 | if (done) { |
797 | for (i = 0; i < nr_cpu; i++) { | 683 | for (i = 0; i < evsel_list->cpus->nr; i++) { |
798 | struct perf_evsel *pos; | 684 | struct perf_evsel *pos; |
799 | 685 | ||
800 | list_for_each_entry(pos, &evsel_list, node) { | 686 | list_for_each_entry(pos, &evsel_list->entries, node) { |
801 | for (thread = 0; | 687 | for (thread = 0; |
802 | thread < threads->nr; | 688 | thread < evsel_list->threads->nr; |
803 | thread++) | 689 | thread++) |
804 | ioctl(FD(pos, i, thread), | 690 | ioctl(FD(pos, i, thread), |
805 | PERF_EVENT_IOC_DISABLE); | 691 | PERF_EVENT_IOC_DISABLE); |
@@ -838,10 +724,10 @@ static const char * const record_usage[] = { | |||
838 | static bool force, append_file; | 724 | static bool force, append_file; |
839 | 725 | ||
840 | const struct option record_options[] = { | 726 | const struct option record_options[] = { |
841 | OPT_CALLBACK('e', "event", NULL, "event", | 727 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
842 | "event selector. use 'perf list' to list available events", | 728 | "event selector. use 'perf list' to list available events", |
843 | parse_events), | 729 | parse_events), |
844 | OPT_CALLBACK(0, "filter", NULL, "filter", | 730 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
845 | "event filter", parse_filter), | 731 | "event filter", parse_filter), |
846 | OPT_INTEGER('p', "pid", &target_pid, | 732 | OPT_INTEGER('p', "pid", &target_pid, |
847 | "record events on existing process id"), | 733 | "record events on existing process id"), |
@@ -884,6 +770,9 @@ const struct option record_options[] = { | |||
884 | "do not update the buildid cache"), | 770 | "do not update the buildid cache"), |
885 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, | 771 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, |
886 | "do not collect buildids in perf.data"), | 772 | "do not collect buildids in perf.data"), |
773 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | ||
774 | "monitor event in cgroup name only", | ||
775 | parse_cgroups), | ||
887 | OPT_END() | 776 | OPT_END() |
888 | }; | 777 | }; |
889 | 778 | ||
@@ -892,6 +781,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
892 | int err = -ENOMEM; | 781 | int err = -ENOMEM; |
893 | struct perf_evsel *pos; | 782 | struct perf_evsel *pos; |
894 | 783 | ||
784 | evsel_list = perf_evlist__new(NULL, NULL); | ||
785 | if (evsel_list == NULL) | ||
786 | return -ENOMEM; | ||
787 | |||
895 | argc = parse_options(argc, argv, record_options, record_usage, | 788 | argc = parse_options(argc, argv, record_options, record_usage, |
896 | PARSE_OPT_STOP_AT_NON_OPTION); | 789 | PARSE_OPT_STOP_AT_NON_OPTION); |
897 | if (!argc && target_pid == -1 && target_tid == -1 && | 790 | if (!argc && target_pid == -1 && target_tid == -1 && |
@@ -908,12 +801,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
908 | write_mode = WRITE_FORCE; | 801 | write_mode = WRITE_FORCE; |
909 | } | 802 | } |
910 | 803 | ||
804 | if (nr_cgroups && !system_wide) { | ||
805 | fprintf(stderr, "cgroup monitoring only available in" | ||
806 | " system-wide mode\n"); | ||
807 | usage_with_options(record_usage, record_options); | ||
808 | } | ||
809 | |||
911 | symbol__init(); | 810 | symbol__init(); |
912 | 811 | ||
913 | if (no_buildid_cache || no_buildid) | 812 | if (no_buildid_cache || no_buildid) |
914 | disable_buildid_cache(); | 813 | disable_buildid_cache(); |
915 | 814 | ||
916 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { | 815 | if (evsel_list->nr_entries == 0 && |
816 | perf_evlist__add_default(evsel_list) < 0) { | ||
917 | pr_err("Not enough memory for event selector list\n"); | 817 | pr_err("Not enough memory for event selector list\n"); |
918 | goto out_symbol_exit; | 818 | goto out_symbol_exit; |
919 | } | 819 | } |
@@ -921,27 +821,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
921 | if (target_pid != -1) | 821 | if (target_pid != -1) |
922 | target_tid = target_pid; | 822 | target_tid = target_pid; |
923 | 823 | ||
924 | threads = thread_map__new(target_pid, target_tid); | 824 | if (perf_evlist__create_maps(evsel_list, target_pid, |
925 | if (threads == NULL) { | 825 | target_tid, cpu_list) < 0) |
926 | pr_err("Problems finding threads of monitor\n"); | ||
927 | usage_with_options(record_usage, record_options); | 826 | usage_with_options(record_usage, record_options); |
928 | } | ||
929 | 827 | ||
930 | cpus = cpu_map__new(cpu_list); | 828 | list_for_each_entry(pos, &evsel_list->entries, node) { |
931 | if (cpus == NULL) { | 829 | if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, |
932 | perror("failed to parse CPUs map"); | 830 | evsel_list->threads->nr) < 0) |
933 | return -1; | ||
934 | } | ||
935 | |||
936 | list_for_each_entry(pos, &evsel_list, node) { | ||
937 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
938 | goto out_free_fd; | 831 | goto out_free_fd; |
939 | if (perf_header__push_event(pos->attr.config, event_name(pos))) | 832 | if (perf_header__push_event(pos->attr.config, event_name(pos))) |
940 | goto out_free_fd; | 833 | goto out_free_fd; |
941 | } | 834 | } |
942 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * | 835 | |
943 | MAX_COUNTERS * threads->nr)); | 836 | if (perf_evlist__alloc_pollfd(evsel_list) < 0) |
944 | if (!event_array) | ||
945 | goto out_free_fd; | 837 | goto out_free_fd; |
946 | 838 | ||
947 | if (user_interval != ULLONG_MAX) | 839 | if (user_interval != ULLONG_MAX) |
@@ -959,16 +851,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
959 | } else { | 851 | } else { |
960 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 852 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
961 | err = -EINVAL; | 853 | err = -EINVAL; |
962 | goto out_free_event_array; | 854 | goto out_free_fd; |
963 | } | 855 | } |
964 | 856 | ||
965 | err = __cmd_record(argc, argv); | 857 | err = __cmd_record(argc, argv); |
966 | |||
967 | out_free_event_array: | ||
968 | free(event_array); | ||
969 | out_free_fd: | 858 | out_free_fd: |
970 | thread_map__delete(threads); | 859 | perf_evlist__delete_maps(evsel_list); |
971 | threads = NULL; | ||
972 | out_symbol_exit: | 860 | out_symbol_exit: |
973 | symbol__exit(); | 861 | symbol__exit(); |
974 | return err; | 862 | return err; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e6..b1b82009ab9b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | 11 | ||
12 | #include "util/annotate.h" | ||
12 | #include "util/color.h" | 13 | #include "util/color.h" |
13 | #include <linux/list.h> | 14 | #include <linux/list.h> |
14 | #include "util/cache.h" | 15 | #include "util/cache.h" |
@@ -20,6 +21,8 @@ | |||
20 | 21 | ||
21 | #include "perf.h" | 22 | #include "perf.h" |
22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/evlist.h" | ||
25 | #include "util/evsel.h" | ||
23 | #include "util/header.h" | 26 | #include "util/header.h" |
24 | #include "util/session.h" | 27 | #include "util/session.h" |
25 | 28 | ||
@@ -43,120 +46,79 @@ static const char default_pretty_printing_style[] = "normal"; | |||
43 | static const char *pretty_printing_style = default_pretty_printing_style; | 46 | static const char *pretty_printing_style = default_pretty_printing_style; |
44 | 47 | ||
45 | static char callchain_default_opt[] = "fractal,0.5"; | 48 | static char callchain_default_opt[] = "fractal,0.5"; |
49 | static symbol_filter_t annotate_init; | ||
46 | 50 | ||
47 | static struct hists *perf_session__hists_findnew(struct perf_session *self, | 51 | static int perf_session__add_hist_entry(struct perf_session *session, |
48 | u64 event_stream, u32 type, | ||
49 | u64 config) | ||
50 | { | ||
51 | struct rb_node **p = &self->hists_tree.rb_node; | ||
52 | struct rb_node *parent = NULL; | ||
53 | struct hists *iter, *new; | ||
54 | |||
55 | while (*p != NULL) { | ||
56 | parent = *p; | ||
57 | iter = rb_entry(parent, struct hists, rb_node); | ||
58 | if (iter->config == config) | ||
59 | return iter; | ||
60 | |||
61 | |||
62 | if (config > iter->config) | ||
63 | p = &(*p)->rb_right; | ||
64 | else | ||
65 | p = &(*p)->rb_left; | ||
66 | } | ||
67 | |||
68 | new = malloc(sizeof(struct hists)); | ||
69 | if (new == NULL) | ||
70 | return NULL; | ||
71 | memset(new, 0, sizeof(struct hists)); | ||
72 | new->event_stream = event_stream; | ||
73 | new->config = config; | ||
74 | new->type = type; | ||
75 | rb_link_node(&new->rb_node, parent, p); | ||
76 | rb_insert_color(&new->rb_node, &self->hists_tree); | ||
77 | return new; | ||
78 | } | ||
79 | |||
80 | static int perf_session__add_hist_entry(struct perf_session *self, | ||
81 | struct addr_location *al, | 52 | struct addr_location *al, |
82 | struct sample_data *data) | 53 | struct perf_sample *sample) |
83 | { | 54 | { |
84 | struct map_symbol *syms = NULL; | ||
85 | struct symbol *parent = NULL; | 55 | struct symbol *parent = NULL; |
86 | int err = -ENOMEM; | 56 | int err = 0; |
87 | struct hist_entry *he; | 57 | struct hist_entry *he; |
88 | struct hists *hists; | 58 | struct perf_evsel *evsel; |
89 | struct perf_event_attr *attr; | 59 | |
90 | 60 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | |
91 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { | 61 | err = perf_session__resolve_callchain(session, al->thread, |
92 | syms = perf_session__resolve_callchain(self, al->thread, | 62 | sample->callchain, &parent); |
93 | data->callchain, &parent); | 63 | if (err) |
94 | if (syms == NULL) | 64 | return err; |
95 | return -ENOMEM; | ||
96 | } | 65 | } |
97 | 66 | ||
98 | attr = perf_header__find_attr(data->id, &self->header); | 67 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
99 | if (attr) | 68 | if (evsel == NULL) { |
100 | hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); | 69 | /* |
101 | else | 70 | * FIXME: Propagate this back, but at least we're in a builtin, |
102 | hists = perf_session__hists_findnew(self, data->id, 0, 0); | 71 | * where exit() is allowed. ;-) |
103 | if (hists == NULL) | 72 | */ |
104 | goto out_free_syms; | 73 | ui__warning("Invalid %s file, contains samples with id %" PRIu64 " not in " |
105 | he = __hists__add_entry(hists, al, parent, data->period); | 74 | "its header!\n", input_name, sample->id); |
75 | exit_browser(0); | ||
76 | exit(1); | ||
77 | } | ||
78 | |||
79 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | ||
106 | if (he == NULL) | 80 | if (he == NULL) |
107 | goto out_free_syms; | 81 | return -ENOMEM; |
108 | err = 0; | 82 | |
109 | if (symbol_conf.use_callchain) { | 83 | if (symbol_conf.use_callchain) { |
110 | err = callchain_append(he->callchain, data->callchain, syms, | 84 | err = callchain_append(he->callchain, &session->callchain_cursor, |
111 | data->period); | 85 | sample->period); |
112 | if (err) | 86 | if (err) |
113 | goto out_free_syms; | 87 | return err; |
114 | } | 88 | } |
115 | /* | 89 | /* |
116 | * Only in the newt browser we are doing integrated annotation, | 90 | * Only in the newt browser we are doing integrated annotation, |
117 | * so we don't allocated the extra space needed because the stdio | 91 | * so we don't allocated the extra space needed because the stdio |
118 | * code will not use it. | 92 | * code will not use it. |
119 | */ | 93 | */ |
120 | if (use_browser > 0) | 94 | if (al->sym != NULL && use_browser > 0) { |
121 | err = hist_entry__inc_addr_samples(he, al->addr); | 95 | struct annotation *notes = symbol__annotation(he->ms.sym); |
122 | out_free_syms: | ||
123 | free(syms); | ||
124 | return err; | ||
125 | } | ||
126 | 96 | ||
127 | static int add_event_total(struct perf_session *session, | 97 | assert(evsel != NULL); |
128 | struct sample_data *data, | ||
129 | struct perf_event_attr *attr) | ||
130 | { | ||
131 | struct hists *hists; | ||
132 | 98 | ||
133 | if (attr) | 99 | err = -ENOMEM; |
134 | hists = perf_session__hists_findnew(session, data->id, | 100 | if (notes->src == NULL && |
135 | attr->type, attr->config); | 101 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) |
136 | else | 102 | goto out; |
137 | hists = perf_session__hists_findnew(session, data->id, 0, 0); | ||
138 | 103 | ||
139 | if (!hists) | 104 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
140 | return -ENOMEM; | 105 | } |
141 | 106 | ||
142 | hists->stats.total_period += data->period; | 107 | evsel->hists.stats.total_period += sample->period; |
143 | /* | 108 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
144 | * FIXME: add_event_total should be moved from here to | 109 | out: |
145 | * perf_session__process_event so that the proper hist is passed to | 110 | return err; |
146 | * the event_op methods. | ||
147 | */ | ||
148 | hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); | ||
149 | session->hists.stats.total_period += data->period; | ||
150 | return 0; | ||
151 | } | 111 | } |
152 | 112 | ||
153 | static int process_sample_event(event_t *event, struct sample_data *sample, | 113 | |
114 | static int process_sample_event(union perf_event *event, | ||
115 | struct perf_sample *sample, | ||
154 | struct perf_session *session) | 116 | struct perf_session *session) |
155 | { | 117 | { |
156 | struct addr_location al; | 118 | struct addr_location al; |
157 | struct perf_event_attr *attr; | ||
158 | 119 | ||
159 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { | 120 | if (perf_event__preprocess_sample(event, session, &al, sample, |
121 | annotate_init) < 0) { | ||
160 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 122 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
161 | event->header.type); | 123 | event->header.type); |
162 | return -1; | 124 | return -1; |
@@ -170,26 +132,17 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
170 | return -1; | 132 | return -1; |
171 | } | 133 | } |
172 | 134 | ||
173 | attr = perf_header__find_attr(sample->id, &session->header); | ||
174 | |||
175 | if (add_event_total(session, sample, attr)) { | ||
176 | pr_debug("problem adding event period\n"); | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | return 0; | 135 | return 0; |
181 | } | 136 | } |
182 | 137 | ||
183 | static int process_read_event(event_t *event, struct sample_data *sample __used, | 138 | static int process_read_event(union perf_event *event, |
184 | struct perf_session *session __used) | 139 | struct perf_sample *sample __used, |
140 | struct perf_session *session) | ||
185 | { | 141 | { |
186 | struct perf_event_attr *attr; | 142 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, |
187 | 143 | event->read.id); | |
188 | attr = perf_header__find_attr(event->read.id, &session->header); | ||
189 | |||
190 | if (show_threads) { | 144 | if (show_threads) { |
191 | const char *name = attr ? __event_name(attr->type, attr->config) | 145 | const char *name = evsel ? event_name(evsel) : "unknown"; |
192 | : "unknown"; | ||
193 | perf_read_values_add_value(&show_threads_values, | 146 | perf_read_values_add_value(&show_threads_values, |
194 | event->read.pid, event->read.tid, | 147 | event->read.pid, event->read.tid, |
195 | event->read.id, | 148 | event->read.id, |
@@ -198,7 +151,7 @@ static int process_read_event(event_t *event, struct sample_data *sample __used, | |||
198 | } | 151 | } |
199 | 152 | ||
200 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, | 153 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, |
201 | attr ? __event_name(attr->type, attr->config) : "FAIL", | 154 | evsel ? event_name(evsel) : "FAIL", |
202 | event->read.value); | 155 | event->read.value); |
203 | 156 | ||
204 | return 0; | 157 | return 0; |
@@ -222,7 +175,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
222 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 175 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
223 | !symbol_conf.use_callchain) { | 176 | !symbol_conf.use_callchain) { |
224 | symbol_conf.use_callchain = true; | 177 | symbol_conf.use_callchain = true; |
225 | if (register_callchain_param(&callchain_param) < 0) { | 178 | if (callchain_register_param(&callchain_param) < 0) { |
226 | fprintf(stderr, "Can't register callchain" | 179 | fprintf(stderr, "Can't register callchain" |
227 | " params\n"); | 180 | " params\n"); |
228 | return -EINVAL; | 181 | return -EINVAL; |
@@ -233,17 +186,17 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
233 | } | 186 | } |
234 | 187 | ||
235 | static struct perf_event_ops event_ops = { | 188 | static struct perf_event_ops event_ops = { |
236 | .sample = process_sample_event, | 189 | .sample = process_sample_event, |
237 | .mmap = event__process_mmap, | 190 | .mmap = perf_event__process_mmap, |
238 | .comm = event__process_comm, | 191 | .comm = perf_event__process_comm, |
239 | .exit = event__process_task, | 192 | .exit = perf_event__process_task, |
240 | .fork = event__process_task, | 193 | .fork = perf_event__process_task, |
241 | .lost = event__process_lost, | 194 | .lost = perf_event__process_lost, |
242 | .read = process_read_event, | 195 | .read = process_read_event, |
243 | .attr = event__process_attr, | 196 | .attr = perf_event__process_attr, |
244 | .event_type = event__process_event_type, | 197 | .event_type = perf_event__process_event_type, |
245 | .tracing_data = event__process_tracing_data, | 198 | .tracing_data = perf_event__process_tracing_data, |
246 | .build_id = event__process_build_id, | 199 | .build_id = perf_event__process_build_id, |
247 | .ordered_samples = true, | 200 | .ordered_samples = true, |
248 | .ordering_requires_timestamps = true, | 201 | .ordering_requires_timestamps = true, |
249 | }; | 202 | }; |
@@ -269,21 +222,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
269 | return ret + fprintf(fp, "\n#\n"); | 222 | return ret + fprintf(fp, "\n#\n"); |
270 | } | 223 | } |
271 | 224 | ||
272 | static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | 225 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
226 | const char *help) | ||
273 | { | 227 | { |
274 | struct rb_node *next = rb_first(tree); | 228 | struct perf_evsel *pos; |
275 | 229 | ||
276 | while (next) { | 230 | list_for_each_entry(pos, &evlist->entries, node) { |
277 | struct hists *hists = rb_entry(next, struct hists, rb_node); | 231 | struct hists *hists = &pos->hists; |
278 | const char *evname = NULL; | 232 | const char *evname = NULL; |
279 | 233 | ||
280 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | 234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) |
281 | evname = __event_name(hists->type, hists->config); | 235 | evname = event_name(pos); |
282 | 236 | ||
283 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 237 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
284 | hists__fprintf(hists, NULL, false, stdout); | 238 | hists__fprintf(hists, NULL, false, stdout); |
285 | fprintf(stdout, "\n\n"); | 239 | fprintf(stdout, "\n\n"); |
286 | next = rb_next(&hists->rb_node); | ||
287 | } | 240 | } |
288 | 241 | ||
289 | if (sort_order == default_sort_order && | 242 | if (sort_order == default_sort_order && |
@@ -304,8 +257,9 @@ static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | |||
304 | static int __cmd_report(void) | 257 | static int __cmd_report(void) |
305 | { | 258 | { |
306 | int ret = -EINVAL; | 259 | int ret = -EINVAL; |
260 | u64 nr_samples; | ||
307 | struct perf_session *session; | 261 | struct perf_session *session; |
308 | struct rb_node *next; | 262 | struct perf_evsel *pos; |
309 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 263 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
310 | 264 | ||
311 | signal(SIGINT, sig_handler); | 265 | signal(SIGINT, sig_handler); |
@@ -336,20 +290,24 @@ static int __cmd_report(void) | |||
336 | if (verbose > 2) | 290 | if (verbose > 2) |
337 | perf_session__fprintf_dsos(session, stdout); | 291 | perf_session__fprintf_dsos(session, stdout); |
338 | 292 | ||
339 | next = rb_first(&session->hists_tree); | 293 | nr_samples = 0; |
340 | while (next) { | 294 | list_for_each_entry(pos, &session->evlist->entries, node) { |
341 | struct hists *hists; | 295 | struct hists *hists = &pos->hists; |
342 | 296 | ||
343 | hists = rb_entry(next, struct hists, rb_node); | ||
344 | hists__collapse_resort(hists); | 297 | hists__collapse_resort(hists); |
345 | hists__output_resort(hists); | 298 | hists__output_resort(hists); |
346 | next = rb_next(&hists->rb_node); | 299 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
300 | } | ||
301 | |||
302 | if (nr_samples == 0) { | ||
303 | ui__warning("The %s file has no samples!\n", input_name); | ||
304 | goto out_delete; | ||
347 | } | 305 | } |
348 | 306 | ||
349 | if (use_browser > 0) | 307 | if (use_browser > 0) |
350 | hists__tui_browse_tree(&session->hists_tree, help); | 308 | perf_evlist__tui_browse_hists(session->evlist, help); |
351 | else | 309 | else |
352 | hists__tty_browse_tree(&session->hists_tree, help); | 310 | perf_evlist__tty_browse_hists(session->evlist, help); |
353 | 311 | ||
354 | out_delete: | 312 | out_delete: |
355 | /* | 313 | /* |
@@ -424,7 +382,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
424 | if (tok2) | 382 | if (tok2) |
425 | callchain_param.print_limit = strtod(tok2, &endptr); | 383 | callchain_param.print_limit = strtod(tok2, &endptr); |
426 | setup: | 384 | setup: |
427 | if (register_callchain_param(&callchain_param) < 0) { | 385 | if (callchain_register_param(&callchain_param) < 0) { |
428 | fprintf(stderr, "Can't register callchain params\n"); | 386 | fprintf(stderr, "Can't register callchain params\n"); |
429 | return -1; | 387 | return -1; |
430 | } | 388 | } |
@@ -498,7 +456,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
498 | use_browser = 1; | 456 | use_browser = 1; |
499 | 457 | ||
500 | if (strcmp(input_name, "-") != 0) | 458 | if (strcmp(input_name, "-") != 0) |
501 | setup_browser(); | 459 | setup_browser(true); |
502 | else | 460 | else |
503 | use_browser = 0; | 461 | use_browser = 0; |
504 | /* | 462 | /* |
@@ -507,7 +465,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
507 | * implementation. | 465 | * implementation. |
508 | */ | 466 | */ |
509 | if (use_browser > 0) { | 467 | if (use_browser > 0) { |
510 | symbol_conf.priv_size = sizeof(struct sym_priv); | 468 | symbol_conf.priv_size = sizeof(struct annotation); |
469 | annotate_init = symbol__annotate_init; | ||
511 | /* | 470 | /* |
512 | * For searching by name on the "Browse map details". | 471 | * For searching by name on the "Browse map details". |
513 | * providing it only in verbose mode not to bloat too | 472 | * providing it only in verbose mode not to bloat too |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 29acb894e035..a32f411faeac 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -369,11 +369,6 @@ static void | |||
369 | process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) | 369 | process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) |
370 | { | 370 | { |
371 | int ret = 0; | 371 | int ret = 0; |
372 | u64 now; | ||
373 | long long delta; | ||
374 | |||
375 | now = get_nsecs(); | ||
376 | delta = start_time + atom->timestamp - now; | ||
377 | 372 | ||
378 | switch (atom->type) { | 373 | switch (atom->type) { |
379 | case SCHED_EVENT_RUN: | 374 | case SCHED_EVENT_RUN: |
@@ -562,7 +557,7 @@ static void wait_for_tasks(void) | |||
562 | 557 | ||
563 | static void run_one_test(void) | 558 | static void run_one_test(void) |
564 | { | 559 | { |
565 | u64 T0, T1, delta, avg_delta, fluct, std_dev; | 560 | u64 T0, T1, delta, avg_delta, fluct; |
566 | 561 | ||
567 | T0 = get_nsecs(); | 562 | T0 = get_nsecs(); |
568 | wait_for_tasks(); | 563 | wait_for_tasks(); |
@@ -578,7 +573,6 @@ static void run_one_test(void) | |||
578 | else | 573 | else |
579 | fluct = delta - avg_delta; | 574 | fluct = delta - avg_delta; |
580 | sum_fluct += fluct; | 575 | sum_fluct += fluct; |
581 | std_dev = sum_fluct / nr_runs / sqrt(nr_runs); | ||
582 | if (!run_avg) | 576 | if (!run_avg) |
583 | run_avg = delta; | 577 | run_avg = delta; |
584 | run_avg = (run_avg*9 + delta)/10; | 578 | run_avg = (run_avg*9 + delta)/10; |
@@ -799,7 +793,7 @@ replay_switch_event(struct trace_switch_event *switch_event, | |||
799 | u64 timestamp, | 793 | u64 timestamp, |
800 | struct thread *thread __used) | 794 | struct thread *thread __used) |
801 | { | 795 | { |
802 | struct task_desc *prev, *next; | 796 | struct task_desc *prev, __used *next; |
803 | u64 timestamp0; | 797 | u64 timestamp0; |
804 | s64 delta; | 798 | s64 delta; |
805 | 799 | ||
@@ -1404,7 +1398,7 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
1404 | u64 timestamp, | 1398 | u64 timestamp, |
1405 | struct thread *thread __used) | 1399 | struct thread *thread __used) |
1406 | { | 1400 | { |
1407 | struct thread *sched_out, *sched_in; | 1401 | struct thread *sched_out __used, *sched_in; |
1408 | int new_shortname; | 1402 | int new_shortname; |
1409 | u64 timestamp0; | 1403 | u64 timestamp0; |
1410 | s64 delta; | 1404 | s64 delta; |
@@ -1580,9 +1574,9 @@ process_sched_migrate_task_event(void *data, struct perf_session *session, | |||
1580 | event, cpu, timestamp, thread); | 1574 | event, cpu, timestamp, thread); |
1581 | } | 1575 | } |
1582 | 1576 | ||
1583 | static void | 1577 | static void process_raw_event(union perf_event *raw_event __used, |
1584 | process_raw_event(event_t *raw_event __used, struct perf_session *session, | 1578 | struct perf_session *session, void *data, int cpu, |
1585 | void *data, int cpu, u64 timestamp, struct thread *thread) | 1579 | u64 timestamp, struct thread *thread) |
1586 | { | 1580 | { |
1587 | struct event *event; | 1581 | struct event *event; |
1588 | int type; | 1582 | int type; |
@@ -1607,7 +1601,8 @@ process_raw_event(event_t *raw_event __used, struct perf_session *session, | |||
1607 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); | 1601 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); |
1608 | } | 1602 | } |
1609 | 1603 | ||
1610 | static int process_sample_event(event_t *event, struct sample_data *sample, | 1604 | static int process_sample_event(union perf_event *event, |
1605 | struct perf_sample *sample, | ||
1611 | struct perf_session *session) | 1606 | struct perf_session *session) |
1612 | { | 1607 | { |
1613 | struct thread *thread; | 1608 | struct thread *thread; |
@@ -1635,9 +1630,9 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
1635 | 1630 | ||
1636 | static struct perf_event_ops event_ops = { | 1631 | static struct perf_event_ops event_ops = { |
1637 | .sample = process_sample_event, | 1632 | .sample = process_sample_event, |
1638 | .comm = event__process_comm, | 1633 | .comm = perf_event__process_comm, |
1639 | .lost = event__process_lost, | 1634 | .lost = perf_event__process_lost, |
1640 | .fork = event__process_task, | 1635 | .fork = perf_event__process_task, |
1641 | .ordered_samples = true, | 1636 | .ordered_samples = true, |
1642 | }; | 1637 | }; |
1643 | 1638 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b766c2a9ac97..9f5fc5492141 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -12,6 +12,8 @@ | |||
12 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
13 | #include "util/parse-options.h" | 13 | #include "util/parse-options.h" |
14 | #include "util/util.h" | 14 | #include "util/util.h" |
15 | #include "util/evlist.h" | ||
16 | #include "util/evsel.h" | ||
15 | 17 | ||
16 | static char const *script_name; | 18 | static char const *script_name; |
17 | static char const *generate_script_lang; | 19 | static char const *generate_script_lang; |
@@ -19,6 +21,183 @@ static bool debug_mode; | |||
19 | static u64 last_timestamp; | 21 | static u64 last_timestamp; |
20 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
21 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
24 | static bool no_callchain; | ||
25 | |||
26 | enum perf_output_field { | ||
27 | PERF_OUTPUT_COMM = 1U << 0, | ||
28 | PERF_OUTPUT_TID = 1U << 1, | ||
29 | PERF_OUTPUT_PID = 1U << 2, | ||
30 | PERF_OUTPUT_TIME = 1U << 3, | ||
31 | PERF_OUTPUT_CPU = 1U << 4, | ||
32 | PERF_OUTPUT_EVNAME = 1U << 5, | ||
33 | PERF_OUTPUT_TRACE = 1U << 6, | ||
34 | PERF_OUTPUT_SYM = 1U << 7, | ||
35 | }; | ||
36 | |||
37 | struct output_option { | ||
38 | const char *str; | ||
39 | enum perf_output_field field; | ||
40 | } all_output_options[] = { | ||
41 | {.str = "comm", .field = PERF_OUTPUT_COMM}, | ||
42 | {.str = "tid", .field = PERF_OUTPUT_TID}, | ||
43 | {.str = "pid", .field = PERF_OUTPUT_PID}, | ||
44 | {.str = "time", .field = PERF_OUTPUT_TIME}, | ||
45 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, | ||
46 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, | ||
47 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, | ||
48 | {.str = "sym", .field = PERF_OUTPUT_SYM}, | ||
49 | }; | ||
50 | |||
51 | /* default set to maintain compatibility with current format */ | ||
52 | static u64 output_fields[PERF_TYPE_MAX] = { | ||
53 | [PERF_TYPE_HARDWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | ||
54 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | ||
55 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
56 | |||
57 | [PERF_TYPE_SOFTWARE] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | ||
58 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | ||
59 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | ||
60 | |||
61 | [PERF_TYPE_TRACEPOINT] = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | \ | ||
62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | \ | ||
63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | ||
64 | }; | ||
65 | |||
66 | static bool output_set_by_user; | ||
67 | |||
68 | #define PRINT_FIELD(x) (output_fields[attr->type] & PERF_OUTPUT_##x) | ||
69 | |||
70 | static int perf_session__check_attr(struct perf_session *session, | ||
71 | struct perf_event_attr *attr) | ||
72 | { | ||
73 | if (PRINT_FIELD(TRACE) && | ||
74 | !perf_session__has_traces(session, "record -R")) | ||
75 | return -EINVAL; | ||
76 | |||
77 | if (PRINT_FIELD(SYM)) { | ||
78 | if (!(session->sample_type & PERF_SAMPLE_IP)) { | ||
79 | pr_err("Samples do not contain IP data.\n"); | ||
80 | return -EINVAL; | ||
81 | } | ||
82 | if (!no_callchain && | ||
83 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | ||
84 | symbol_conf.use_callchain = false; | ||
85 | } | ||
86 | |||
87 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | ||
88 | !(session->sample_type & PERF_SAMPLE_TID)) { | ||
89 | pr_err("Samples do not contain TID/PID data.\n"); | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | if (PRINT_FIELD(TIME) && | ||
94 | !(session->sample_type & PERF_SAMPLE_TIME)) { | ||
95 | pr_err("Samples do not contain timestamps.\n"); | ||
96 | return -EINVAL; | ||
97 | } | ||
98 | |||
99 | if (PRINT_FIELD(CPU) && | ||
100 | !(session->sample_type & PERF_SAMPLE_CPU)) { | ||
101 | pr_err("Samples do not contain cpu.\n"); | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static void print_sample_start(struct perf_sample *sample, | ||
109 | struct thread *thread, | ||
110 | struct perf_event_attr *attr) | ||
111 | { | ||
112 | int type; | ||
113 | struct event *event; | ||
114 | const char *evname = NULL; | ||
115 | unsigned long secs; | ||
116 | unsigned long usecs; | ||
117 | unsigned long long nsecs; | ||
118 | |||
119 | if (PRINT_FIELD(COMM)) { | ||
120 | if (latency_format) | ||
121 | printf("%8.8s ", thread->comm); | ||
122 | else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) | ||
123 | printf("%s ", thread->comm); | ||
124 | else | ||
125 | printf("%16s ", thread->comm); | ||
126 | } | ||
127 | |||
128 | if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) | ||
129 | printf("%5d/%-5d ", sample->pid, sample->tid); | ||
130 | else if (PRINT_FIELD(PID)) | ||
131 | printf("%5d ", sample->pid); | ||
132 | else if (PRINT_FIELD(TID)) | ||
133 | printf("%5d ", sample->tid); | ||
134 | |||
135 | if (PRINT_FIELD(CPU)) { | ||
136 | if (latency_format) | ||
137 | printf("%3d ", sample->cpu); | ||
138 | else | ||
139 | printf("[%03d] ", sample->cpu); | ||
140 | } | ||
141 | |||
142 | if (PRINT_FIELD(TIME)) { | ||
143 | nsecs = sample->time; | ||
144 | secs = nsecs / NSECS_PER_SEC; | ||
145 | nsecs -= secs * NSECS_PER_SEC; | ||
146 | usecs = nsecs / NSECS_PER_USEC; | ||
147 | printf("%5lu.%06lu: ", secs, usecs); | ||
148 | } | ||
149 | |||
150 | if (PRINT_FIELD(EVNAME)) { | ||
151 | if (attr->type == PERF_TYPE_TRACEPOINT) { | ||
152 | type = trace_parse_common_type(sample->raw_data); | ||
153 | event = trace_find_event(type); | ||
154 | if (event) | ||
155 | evname = event->name; | ||
156 | } else | ||
157 | evname = __event_name(attr->type, attr->config); | ||
158 | |||
159 | printf("%s: ", evname ? evname : "(unknown)"); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | static void process_event(union perf_event *event __unused, | ||
164 | struct perf_sample *sample, | ||
165 | struct perf_session *session, | ||
166 | struct thread *thread) | ||
167 | { | ||
168 | struct perf_event_attr *attr; | ||
169 | struct perf_evsel *evsel; | ||
170 | |||
171 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
172 | if (evsel == NULL) { | ||
173 | pr_err("Invalid data. Contains samples with id not in " | ||
174 | "its header!\n"); | ||
175 | return; | ||
176 | } | ||
177 | attr = &evsel->attr; | ||
178 | |||
179 | if (output_fields[attr->type] == 0) | ||
180 | return; | ||
181 | |||
182 | if (perf_session__check_attr(session, attr) < 0) | ||
183 | return; | ||
184 | |||
185 | print_sample_start(sample, thread, attr); | ||
186 | |||
187 | if (PRINT_FIELD(TRACE)) | ||
188 | print_trace_event(sample->cpu, sample->raw_data, | ||
189 | sample->raw_size); | ||
190 | |||
191 | if (PRINT_FIELD(SYM)) { | ||
192 | if (!symbol_conf.use_callchain) | ||
193 | printf(" "); | ||
194 | else | ||
195 | printf("\n"); | ||
196 | perf_session__print_symbols(event, sample, session); | ||
197 | } | ||
198 | |||
199 | printf("\n"); | ||
200 | } | ||
22 | 201 | ||
23 | static int default_start_script(const char *script __unused, | 202 | static int default_start_script(const char *script __unused, |
24 | int argc __unused, | 203 | int argc __unused, |
@@ -40,7 +219,7 @@ static int default_generate_script(const char *outfile __unused) | |||
40 | static struct scripting_ops default_scripting_ops = { | 219 | static struct scripting_ops default_scripting_ops = { |
41 | .start_script = default_start_script, | 220 | .start_script = default_start_script, |
42 | .stop_script = default_stop_script, | 221 | .stop_script = default_stop_script, |
43 | .process_event = print_event, | 222 | .process_event = process_event, |
44 | .generate_script = default_generate_script, | 223 | .generate_script = default_generate_script, |
45 | }; | 224 | }; |
46 | 225 | ||
@@ -63,7 +242,8 @@ static int cleanup_scripting(void) | |||
63 | 242 | ||
64 | static char const *input_name = "perf.data"; | 243 | static char const *input_name = "perf.data"; |
65 | 244 | ||
66 | static int process_sample_event(event_t *event, struct sample_data *sample, | 245 | static int process_sample_event(union perf_event *event, |
246 | struct perf_sample *sample, | ||
67 | struct perf_session *session) | 247 | struct perf_session *session) |
68 | { | 248 | { |
69 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | 249 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
@@ -74,40 +254,34 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
74 | return -1; | 254 | return -1; |
75 | } | 255 | } |
76 | 256 | ||
77 | if (session->sample_type & PERF_SAMPLE_RAW) { | 257 | if (debug_mode) { |
78 | if (debug_mode) { | 258 | if (sample->time < last_timestamp) { |
79 | if (sample->time < last_timestamp) { | 259 | pr_err("Samples misordered, previous: %" PRIu64 |
80 | pr_err("Samples misordered, previous: %" PRIu64 | 260 | " this: %" PRIu64 "\n", last_timestamp, |
81 | " this: %" PRIu64 "\n", last_timestamp, | 261 | sample->time); |
82 | sample->time); | 262 | nr_unordered++; |
83 | nr_unordered++; | ||
84 | } | ||
85 | last_timestamp = sample->time; | ||
86 | return 0; | ||
87 | } | 263 | } |
88 | /* | 264 | last_timestamp = sample->time; |
89 | * FIXME: better resolve from pid from the struct trace_entry | 265 | return 0; |
90 | * field, although it should be the same than this perf | ||
91 | * event pid | ||
92 | */ | ||
93 | scripting_ops->process_event(sample->cpu, sample->raw_data, | ||
94 | sample->raw_size, | ||
95 | sample->time, thread->comm); | ||
96 | } | 266 | } |
267 | scripting_ops->process_event(event, sample, session, thread); | ||
97 | 268 | ||
98 | session->hists.stats.total_period += sample->period; | 269 | session->hists.stats.total_period += sample->period; |
99 | return 0; | 270 | return 0; |
100 | } | 271 | } |
101 | 272 | ||
102 | static struct perf_event_ops event_ops = { | 273 | static struct perf_event_ops event_ops = { |
103 | .sample = process_sample_event, | 274 | .sample = process_sample_event, |
104 | .comm = event__process_comm, | 275 | .mmap = perf_event__process_mmap, |
105 | .attr = event__process_attr, | 276 | .comm = perf_event__process_comm, |
106 | .event_type = event__process_event_type, | 277 | .exit = perf_event__process_task, |
107 | .tracing_data = event__process_tracing_data, | 278 | .fork = perf_event__process_task, |
108 | .build_id = event__process_build_id, | 279 | .attr = perf_event__process_attr, |
109 | .ordering_requires_timestamps = true, | 280 | .event_type = perf_event__process_event_type, |
281 | .tracing_data = perf_event__process_tracing_data, | ||
282 | .build_id = perf_event__process_build_id, | ||
110 | .ordered_samples = true, | 283 | .ordered_samples = true, |
284 | .ordering_requires_timestamps = true, | ||
111 | }; | 285 | }; |
112 | 286 | ||
113 | extern volatile int session_done; | 287 | extern volatile int session_done; |
@@ -279,6 +453,68 @@ static int parse_scriptname(const struct option *opt __used, | |||
279 | return 0; | 453 | return 0; |
280 | } | 454 | } |
281 | 455 | ||
456 | static int parse_output_fields(const struct option *opt __used, | ||
457 | const char *arg, int unset __used) | ||
458 | { | ||
459 | char *tok; | ||
460 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | ||
461 | int rc = 0; | ||
462 | char *str = strdup(arg); | ||
463 | int type = -1; | ||
464 | |||
465 | if (!str) | ||
466 | return -ENOMEM; | ||
467 | |||
468 | tok = strtok(str, ":"); | ||
469 | if (!tok) { | ||
470 | fprintf(stderr, | ||
471 | "Invalid field string - not prepended with type."); | ||
472 | return -EINVAL; | ||
473 | } | ||
474 | |||
475 | /* first word should state which event type user | ||
476 | * is specifying the fields | ||
477 | */ | ||
478 | if (!strcmp(tok, "hw")) | ||
479 | type = PERF_TYPE_HARDWARE; | ||
480 | else if (!strcmp(tok, "sw")) | ||
481 | type = PERF_TYPE_SOFTWARE; | ||
482 | else if (!strcmp(tok, "trace")) | ||
483 | type = PERF_TYPE_TRACEPOINT; | ||
484 | else { | ||
485 | fprintf(stderr, "Invalid event type in field string."); | ||
486 | return -EINVAL; | ||
487 | } | ||
488 | |||
489 | output_fields[type] = 0; | ||
490 | while (1) { | ||
491 | tok = strtok(NULL, ","); | ||
492 | if (!tok) | ||
493 | break; | ||
494 | for (i = 0; i < imax; ++i) { | ||
495 | if (strcmp(tok, all_output_options[i].str) == 0) { | ||
496 | output_fields[type] |= all_output_options[i].field; | ||
497 | break; | ||
498 | } | ||
499 | } | ||
500 | if (i == imax) { | ||
501 | fprintf(stderr, "Invalid field requested."); | ||
502 | rc = -EINVAL; | ||
503 | break; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | if (output_fields[type] == 0) { | ||
508 | pr_debug("No fields requested for %s type. " | ||
509 | "Events will not be displayed\n", event_type(type)); | ||
510 | } | ||
511 | |||
512 | output_set_by_user = true; | ||
513 | |||
514 | free(str); | ||
515 | return rc; | ||
516 | } | ||
517 | |||
282 | /* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */ | 518 | /* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */ |
283 | static int is_directory(const char *base_path, const struct dirent *dent) | 519 | static int is_directory(const char *base_path, const struct dirent *dent) |
284 | { | 520 | { |
@@ -591,6 +827,17 @@ static const struct option options[] = { | |||
591 | "input file name"), | 827 | "input file name"), |
592 | OPT_BOOLEAN('d', "debug-mode", &debug_mode, | 828 | OPT_BOOLEAN('d', "debug-mode", &debug_mode, |
593 | "do various checks like samples ordering and lost events"), | 829 | "do various checks like samples ordering and lost events"), |
830 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | ||
831 | "file", "vmlinux pathname"), | ||
832 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, | ||
833 | "file", "kallsyms pathname"), | ||
834 | OPT_BOOLEAN('G', "hide-call-graph", &no_callchain, | ||
835 | "When printing symbols do not display call chain"), | ||
836 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
837 | "Look for files with symbols relative to this directory"), | ||
838 | OPT_CALLBACK('f', "fields", NULL, "str", | ||
839 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace. Fields: comm,tid,pid,time,cpu,event,trace,sym", | ||
840 | parse_output_fields), | ||
594 | 841 | ||
595 | OPT_END() | 842 | OPT_END() |
596 | }; | 843 | }; |
@@ -771,14 +1018,22 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
771 | if (session == NULL) | 1018 | if (session == NULL) |
772 | return -ENOMEM; | 1019 | return -ENOMEM; |
773 | 1020 | ||
774 | if (strcmp(input_name, "-") && | 1021 | if (!no_callchain) |
775 | !perf_session__has_traces(session, "record -R")) | 1022 | symbol_conf.use_callchain = true; |
776 | return -EINVAL; | 1023 | else |
1024 | symbol_conf.use_callchain = false; | ||
777 | 1025 | ||
778 | if (generate_script_lang) { | 1026 | if (generate_script_lang) { |
779 | struct stat perf_stat; | 1027 | struct stat perf_stat; |
1028 | int input; | ||
1029 | |||
1030 | if (output_set_by_user) { | ||
1031 | fprintf(stderr, | ||
1032 | "custom fields not supported for generated scripts"); | ||
1033 | return -1; | ||
1034 | } | ||
780 | 1035 | ||
781 | int input = open(input_name, O_RDONLY); | 1036 | input = open(input_name, O_RDONLY); |
782 | if (input < 0) { | 1037 | if (input < 0) { |
783 | perror("failed to open file"); | 1038 | perror("failed to open file"); |
784 | exit(-1); | 1039 | exit(-1); |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a482a191a0ca..e2109f9b43eb 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -43,11 +43,13 @@ | |||
43 | #include "util/parse-options.h" | 43 | #include "util/parse-options.h" |
44 | #include "util/parse-events.h" | 44 | #include "util/parse-events.h" |
45 | #include "util/event.h" | 45 | #include "util/event.h" |
46 | #include "util/evlist.h" | ||
46 | #include "util/evsel.h" | 47 | #include "util/evsel.h" |
47 | #include "util/debug.h" | 48 | #include "util/debug.h" |
48 | #include "util/header.h" | 49 | #include "util/header.h" |
49 | #include "util/cpumap.h" | 50 | #include "util/cpumap.h" |
50 | #include "util/thread.h" | 51 | #include "util/thread.h" |
52 | #include "util/thread_map.h" | ||
51 | 53 | ||
52 | #include <sys/prctl.h> | 54 | #include <sys/prctl.h> |
53 | #include <math.h> | 55 | #include <math.h> |
@@ -71,8 +73,9 @@ static struct perf_event_attr default_attrs[] = { | |||
71 | 73 | ||
72 | }; | 74 | }; |
73 | 75 | ||
76 | struct perf_evlist *evsel_list; | ||
77 | |||
74 | static bool system_wide = false; | 78 | static bool system_wide = false; |
75 | static struct cpu_map *cpus; | ||
76 | static int run_idx = 0; | 79 | static int run_idx = 0; |
77 | 80 | ||
78 | static int run_count = 1; | 81 | static int run_count = 1; |
@@ -81,7 +84,6 @@ static bool scale = true; | |||
81 | static bool no_aggr = false; | 84 | static bool no_aggr = false; |
82 | static pid_t target_pid = -1; | 85 | static pid_t target_pid = -1; |
83 | static pid_t target_tid = -1; | 86 | static pid_t target_tid = -1; |
84 | static struct thread_map *threads; | ||
85 | static pid_t child_pid = -1; | 87 | static pid_t child_pid = -1; |
86 | static bool null_run = false; | 88 | static bool null_run = false; |
87 | static bool big_num = true; | 89 | static bool big_num = true; |
@@ -166,7 +168,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
166 | PERF_FORMAT_TOTAL_TIME_RUNNING; | 168 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
167 | 169 | ||
168 | if (system_wide) | 170 | if (system_wide) |
169 | return perf_evsel__open_per_cpu(evsel, cpus); | 171 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false, false); |
170 | 172 | ||
171 | attr->inherit = !no_inherit; | 173 | attr->inherit = !no_inherit; |
172 | if (target_pid == -1 && target_tid == -1) { | 174 | if (target_pid == -1 && target_tid == -1) { |
@@ -174,7 +176,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
174 | attr->enable_on_exec = 1; | 176 | attr->enable_on_exec = 1; |
175 | } | 177 | } |
176 | 178 | ||
177 | return perf_evsel__open_per_thread(evsel, threads); | 179 | return perf_evsel__open_per_thread(evsel, evsel_list->threads, false, false); |
178 | } | 180 | } |
179 | 181 | ||
180 | /* | 182 | /* |
@@ -199,7 +201,8 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
199 | u64 *count = counter->counts->aggr.values; | 201 | u64 *count = counter->counts->aggr.values; |
200 | int i; | 202 | int i; |
201 | 203 | ||
202 | if (__perf_evsel__read(counter, cpus->nr, threads->nr, scale) < 0) | 204 | if (__perf_evsel__read(counter, evsel_list->cpus->nr, |
205 | evsel_list->threads->nr, scale) < 0) | ||
203 | return -1; | 206 | return -1; |
204 | 207 | ||
205 | for (i = 0; i < 3; i++) | 208 | for (i = 0; i < 3; i++) |
@@ -232,7 +235,7 @@ static int read_counter(struct perf_evsel *counter) | |||
232 | u64 *count; | 235 | u64 *count; |
233 | int cpu; | 236 | int cpu; |
234 | 237 | ||
235 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 238 | for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) { |
236 | if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0) | 239 | if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0) |
237 | return -1; | 240 | return -1; |
238 | 241 | ||
@@ -297,7 +300,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
297 | } | 300 | } |
298 | 301 | ||
299 | if (target_tid == -1 && target_pid == -1 && !system_wide) | 302 | if (target_tid == -1 && target_pid == -1 && !system_wide) |
300 | threads->map[0] = child_pid; | 303 | evsel_list->threads->map[0] = child_pid; |
301 | 304 | ||
302 | /* | 305 | /* |
303 | * Wait for the child to be ready to exec. | 306 | * Wait for the child to be ready to exec. |
@@ -309,7 +312,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
309 | close(child_ready_pipe[0]); | 312 | close(child_ready_pipe[0]); |
310 | } | 313 | } |
311 | 314 | ||
312 | list_for_each_entry(counter, &evsel_list, node) { | 315 | list_for_each_entry(counter, &evsel_list->entries, node) { |
313 | if (create_perf_stat_counter(counter) < 0) { | 316 | if (create_perf_stat_counter(counter) < 0) { |
314 | if (errno == -EPERM || errno == -EACCES) { | 317 | if (errno == -EPERM || errno == -EACCES) { |
315 | error("You may not have permission to collect %sstats.\n" | 318 | error("You may not have permission to collect %sstats.\n" |
@@ -330,6 +333,12 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
330 | } | 333 | } |
331 | } | 334 | } |
332 | 335 | ||
336 | if (perf_evlist__set_filters(evsel_list)) { | ||
337 | error("failed to set filter with %d (%s)\n", errno, | ||
338 | strerror(errno)); | ||
339 | return -1; | ||
340 | } | ||
341 | |||
333 | /* | 342 | /* |
334 | * Enable counters and exec the command: | 343 | * Enable counters and exec the command: |
335 | */ | 344 | */ |
@@ -347,14 +356,15 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
347 | update_stats(&walltime_nsecs_stats, t1 - t0); | 356 | update_stats(&walltime_nsecs_stats, t1 - t0); |
348 | 357 | ||
349 | if (no_aggr) { | 358 | if (no_aggr) { |
350 | list_for_each_entry(counter, &evsel_list, node) { | 359 | list_for_each_entry(counter, &evsel_list->entries, node) { |
351 | read_counter(counter); | 360 | read_counter(counter); |
352 | perf_evsel__close_fd(counter, cpus->nr, 1); | 361 | perf_evsel__close_fd(counter, evsel_list->cpus->nr, 1); |
353 | } | 362 | } |
354 | } else { | 363 | } else { |
355 | list_for_each_entry(counter, &evsel_list, node) { | 364 | list_for_each_entry(counter, &evsel_list->entries, node) { |
356 | read_counter_aggr(counter); | 365 | read_counter_aggr(counter); |
357 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); | 366 | perf_evsel__close_fd(counter, evsel_list->cpus->nr, |
367 | evsel_list->threads->nr); | ||
358 | } | 368 | } |
359 | } | 369 | } |
360 | 370 | ||
@@ -382,10 +392,13 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
382 | if (no_aggr) | 392 | if (no_aggr) |
383 | sprintf(cpustr, "CPU%*d%s", | 393 | sprintf(cpustr, "CPU%*d%s", |
384 | csv_output ? 0 : -4, | 394 | csv_output ? 0 : -4, |
385 | cpus->map[cpu], csv_sep); | 395 | evsel_list->cpus->map[cpu], csv_sep); |
386 | 396 | ||
387 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); | 397 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); |
388 | 398 | ||
399 | if (evsel->cgrp) | ||
400 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | ||
401 | |||
389 | if (csv_output) | 402 | if (csv_output) |
390 | return; | 403 | return; |
391 | 404 | ||
@@ -410,12 +423,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
410 | if (no_aggr) | 423 | if (no_aggr) |
411 | sprintf(cpustr, "CPU%*d%s", | 424 | sprintf(cpustr, "CPU%*d%s", |
412 | csv_output ? 0 : -4, | 425 | csv_output ? 0 : -4, |
413 | cpus->map[cpu], csv_sep); | 426 | evsel_list->cpus->map[cpu], csv_sep); |
414 | else | 427 | else |
415 | cpu = 0; | 428 | cpu = 0; |
416 | 429 | ||
417 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel)); | 430 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel)); |
418 | 431 | ||
432 | if (evsel->cgrp) | ||
433 | fprintf(stderr, "%s%s", csv_sep, evsel->cgrp->name); | ||
434 | |||
419 | if (csv_output) | 435 | if (csv_output) |
420 | return; | 436 | return; |
421 | 437 | ||
@@ -456,9 +472,17 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
456 | int scaled = counter->counts->scaled; | 472 | int scaled = counter->counts->scaled; |
457 | 473 | ||
458 | if (scaled == -1) { | 474 | if (scaled == -1) { |
459 | fprintf(stderr, "%*s%s%-24s\n", | 475 | fprintf(stderr, "%*s%s%*s", |
460 | csv_output ? 0 : 18, | 476 | csv_output ? 0 : 18, |
461 | "<not counted>", csv_sep, event_name(counter)); | 477 | "<not counted>", |
478 | csv_sep, | ||
479 | csv_output ? 0 : -24, | ||
480 | event_name(counter)); | ||
481 | |||
482 | if (counter->cgrp) | ||
483 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | ||
484 | |||
485 | fputc('\n', stderr); | ||
462 | return; | 486 | return; |
463 | } | 487 | } |
464 | 488 | ||
@@ -483,7 +507,6 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
483 | fprintf(stderr, " (scaled from %.2f%%)", | 507 | fprintf(stderr, " (scaled from %.2f%%)", |
484 | 100 * avg_running / avg_enabled); | 508 | 100 * avg_running / avg_enabled); |
485 | } | 509 | } |
486 | |||
487 | fprintf(stderr, "\n"); | 510 | fprintf(stderr, "\n"); |
488 | } | 511 | } |
489 | 512 | ||
@@ -496,19 +519,23 @@ static void print_counter(struct perf_evsel *counter) | |||
496 | u64 ena, run, val; | 519 | u64 ena, run, val; |
497 | int cpu; | 520 | int cpu; |
498 | 521 | ||
499 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 522 | for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) { |
500 | val = counter->counts->cpu[cpu].val; | 523 | val = counter->counts->cpu[cpu].val; |
501 | ena = counter->counts->cpu[cpu].ena; | 524 | ena = counter->counts->cpu[cpu].ena; |
502 | run = counter->counts->cpu[cpu].run; | 525 | run = counter->counts->cpu[cpu].run; |
503 | if (run == 0 || ena == 0) { | 526 | if (run == 0 || ena == 0) { |
504 | fprintf(stderr, "CPU%*d%s%*s%s%-24s", | 527 | fprintf(stderr, "CPU%*d%s%*s%s%*s", |
505 | csv_output ? 0 : -4, | 528 | csv_output ? 0 : -4, |
506 | cpus->map[cpu], csv_sep, | 529 | evsel_list->cpus->map[cpu], csv_sep, |
507 | csv_output ? 0 : 18, | 530 | csv_output ? 0 : 18, |
508 | "<not counted>", csv_sep, | 531 | "<not counted>", csv_sep, |
532 | csv_output ? 0 : -24, | ||
509 | event_name(counter)); | 533 | event_name(counter)); |
510 | 534 | ||
511 | fprintf(stderr, "\n"); | 535 | if (counter->cgrp) |
536 | fprintf(stderr, "%s%s", csv_sep, counter->cgrp->name); | ||
537 | |||
538 | fputc('\n', stderr); | ||
512 | continue; | 539 | continue; |
513 | } | 540 | } |
514 | 541 | ||
@@ -525,7 +552,7 @@ static void print_counter(struct perf_evsel *counter) | |||
525 | 100.0 * run / ena); | 552 | 100.0 * run / ena); |
526 | } | 553 | } |
527 | } | 554 | } |
528 | fprintf(stderr, "\n"); | 555 | fputc('\n', stderr); |
529 | } | 556 | } |
530 | } | 557 | } |
531 | 558 | ||
@@ -555,10 +582,10 @@ static void print_stat(int argc, const char **argv) | |||
555 | } | 582 | } |
556 | 583 | ||
557 | if (no_aggr) { | 584 | if (no_aggr) { |
558 | list_for_each_entry(counter, &evsel_list, node) | 585 | list_for_each_entry(counter, &evsel_list->entries, node) |
559 | print_counter(counter); | 586 | print_counter(counter); |
560 | } else { | 587 | } else { |
561 | list_for_each_entry(counter, &evsel_list, node) | 588 | list_for_each_entry(counter, &evsel_list->entries, node) |
562 | print_counter_aggr(counter); | 589 | print_counter_aggr(counter); |
563 | } | 590 | } |
564 | 591 | ||
@@ -610,9 +637,11 @@ static int stat__set_big_num(const struct option *opt __used, | |||
610 | } | 637 | } |
611 | 638 | ||
612 | static const struct option options[] = { | 639 | static const struct option options[] = { |
613 | OPT_CALLBACK('e', "event", NULL, "event", | 640 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
614 | "event selector. use 'perf list' to list available events", | 641 | "event selector. use 'perf list' to list available events", |
615 | parse_events), | 642 | parse_events), |
643 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | ||
644 | "event filter", parse_filter), | ||
616 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 645 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
617 | "child tasks do not inherit counters"), | 646 | "child tasks do not inherit counters"), |
618 | OPT_INTEGER('p', "pid", &target_pid, | 647 | OPT_INTEGER('p', "pid", &target_pid, |
@@ -638,6 +667,9 @@ static const struct option options[] = { | |||
638 | "disable CPU count aggregation"), | 667 | "disable CPU count aggregation"), |
639 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 668 | OPT_STRING('x', "field-separator", &csv_sep, "separator", |
640 | "print counts with custom separator"), | 669 | "print counts with custom separator"), |
670 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | ||
671 | "monitor event in cgroup name only", | ||
672 | parse_cgroups), | ||
641 | OPT_END() | 673 | OPT_END() |
642 | }; | 674 | }; |
643 | 675 | ||
@@ -648,6 +680,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
648 | 680 | ||
649 | setlocale(LC_ALL, ""); | 681 | setlocale(LC_ALL, ""); |
650 | 682 | ||
683 | evsel_list = perf_evlist__new(NULL, NULL); | ||
684 | if (evsel_list == NULL) | ||
685 | return -ENOMEM; | ||
686 | |||
651 | argc = parse_options(argc, argv, options, stat_usage, | 687 | argc = parse_options(argc, argv, options, stat_usage, |
652 | PARSE_OPT_STOP_AT_NON_OPTION); | 688 | PARSE_OPT_STOP_AT_NON_OPTION); |
653 | 689 | ||
@@ -674,49 +710,50 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
674 | if (run_count <= 0) | 710 | if (run_count <= 0) |
675 | usage_with_options(stat_usage, options); | 711 | usage_with_options(stat_usage, options); |
676 | 712 | ||
677 | /* no_aggr is for system-wide only */ | 713 | /* no_aggr, cgroup are for system-wide only */ |
678 | if (no_aggr && !system_wide) | 714 | if ((no_aggr || nr_cgroups) && !system_wide) { |
715 | fprintf(stderr, "both cgroup and no-aggregation " | ||
716 | "modes only available in system-wide mode\n"); | ||
717 | |||
679 | usage_with_options(stat_usage, options); | 718 | usage_with_options(stat_usage, options); |
719 | } | ||
680 | 720 | ||
681 | /* Set attrs and nr_counters if no event is selected and !null_run */ | 721 | /* Set attrs and nr_counters if no event is selected and !null_run */ |
682 | if (!null_run && !nr_counters) { | 722 | if (!null_run && !evsel_list->nr_entries) { |
683 | size_t c; | 723 | size_t c; |
684 | 724 | ||
685 | nr_counters = ARRAY_SIZE(default_attrs); | ||
686 | |||
687 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { | 725 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { |
688 | pos = perf_evsel__new(&default_attrs[c], | 726 | pos = perf_evsel__new(&default_attrs[c], c); |
689 | nr_counters); | ||
690 | if (pos == NULL) | 727 | if (pos == NULL) |
691 | goto out; | 728 | goto out; |
692 | list_add(&pos->node, &evsel_list); | 729 | perf_evlist__add(evsel_list, pos); |
693 | } | 730 | } |
694 | } | 731 | } |
695 | 732 | ||
696 | if (target_pid != -1) | 733 | if (target_pid != -1) |
697 | target_tid = target_pid; | 734 | target_tid = target_pid; |
698 | 735 | ||
699 | threads = thread_map__new(target_pid, target_tid); | 736 | evsel_list->threads = thread_map__new(target_pid, target_tid); |
700 | if (threads == NULL) { | 737 | if (evsel_list->threads == NULL) { |
701 | pr_err("Problems finding threads of monitor\n"); | 738 | pr_err("Problems finding threads of monitor\n"); |
702 | usage_with_options(stat_usage, options); | 739 | usage_with_options(stat_usage, options); |
703 | } | 740 | } |
704 | 741 | ||
705 | if (system_wide) | 742 | if (system_wide) |
706 | cpus = cpu_map__new(cpu_list); | 743 | evsel_list->cpus = cpu_map__new(cpu_list); |
707 | else | 744 | else |
708 | cpus = cpu_map__dummy_new(); | 745 | evsel_list->cpus = cpu_map__dummy_new(); |
709 | 746 | ||
710 | if (cpus == NULL) { | 747 | if (evsel_list->cpus == NULL) { |
711 | perror("failed to parse CPUs map"); | 748 | perror("failed to parse CPUs map"); |
712 | usage_with_options(stat_usage, options); | 749 | usage_with_options(stat_usage, options); |
713 | return -1; | 750 | return -1; |
714 | } | 751 | } |
715 | 752 | ||
716 | list_for_each_entry(pos, &evsel_list, node) { | 753 | list_for_each_entry(pos, &evsel_list->entries, node) { |
717 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 754 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
718 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || | 755 | perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0 || |
719 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 756 | perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, evsel_list->threads->nr) < 0) |
720 | goto out_free_fd; | 757 | goto out_free_fd; |
721 | } | 758 | } |
722 | 759 | ||
@@ -741,11 +778,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
741 | if (status != -1) | 778 | if (status != -1) |
742 | print_stat(argc, argv); | 779 | print_stat(argc, argv); |
743 | out_free_fd: | 780 | out_free_fd: |
744 | list_for_each_entry(pos, &evsel_list, node) | 781 | list_for_each_entry(pos, &evsel_list->entries, node) |
745 | perf_evsel__free_stat_priv(pos); | 782 | perf_evsel__free_stat_priv(pos); |
746 | perf_evsel_list__delete(); | 783 | perf_evlist__delete_maps(evsel_list); |
747 | out: | 784 | out: |
748 | thread_map__delete(threads); | 785 | perf_evlist__delete(evsel_list); |
749 | threads = NULL; | ||
750 | return status; | 786 | return status; |
751 | } | 787 | } |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 5dcdba653d70..1b2106c58f66 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -7,10 +7,11 @@ | |||
7 | 7 | ||
8 | #include "util/cache.h" | 8 | #include "util/cache.h" |
9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
10 | #include "util/evlist.h" | ||
10 | #include "util/parse-options.h" | 11 | #include "util/parse-options.h" |
11 | #include "util/session.h" | 12 | #include "util/parse-events.h" |
12 | #include "util/symbol.h" | 13 | #include "util/symbol.h" |
13 | #include "util/thread.h" | 14 | #include "util/thread_map.h" |
14 | 15 | ||
15 | static long page_size; | 16 | static long page_size; |
16 | 17 | ||
@@ -238,14 +239,14 @@ out: | |||
238 | #include "util/evsel.h" | 239 | #include "util/evsel.h" |
239 | #include <sys/types.h> | 240 | #include <sys/types.h> |
240 | 241 | ||
241 | static int trace_event__id(const char *event_name) | 242 | static int trace_event__id(const char *evname) |
242 | { | 243 | { |
243 | char *filename; | 244 | char *filename; |
244 | int err = -1, fd; | 245 | int err = -1, fd; |
245 | 246 | ||
246 | if (asprintf(&filename, | 247 | if (asprintf(&filename, |
247 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | 248 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", |
248 | event_name) < 0) | 249 | evname) < 0) |
249 | return -1; | 250 | return -1; |
250 | 251 | ||
251 | fd = open(filename, O_RDONLY); | 252 | fd = open(filename, O_RDONLY); |
@@ -289,7 +290,7 @@ static int test__open_syscall_event(void) | |||
289 | goto out_thread_map_delete; | 290 | goto out_thread_map_delete; |
290 | } | 291 | } |
291 | 292 | ||
292 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | 293 | if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) { |
293 | pr_debug("failed to open counter: %s, " | 294 | pr_debug("failed to open counter: %s, " |
294 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 295 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
295 | strerror(errno)); | 296 | strerror(errno)); |
@@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
347 | } | 348 | } |
348 | 349 | ||
349 | cpus = cpu_map__new(NULL); | 350 | cpus = cpu_map__new(NULL); |
350 | if (threads == NULL) { | 351 | if (cpus == NULL) { |
351 | pr_debug("thread_map__new\n"); | 352 | pr_debug("cpu_map__new\n"); |
352 | return -1; | 353 | goto out_thread_map_delete; |
353 | } | 354 | } |
354 | 355 | ||
355 | 356 | ||
@@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
364 | goto out_thread_map_delete; | 365 | goto out_thread_map_delete; |
365 | } | 366 | } |
366 | 367 | ||
367 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | 368 | if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) { |
368 | pr_debug("failed to open counter: %s, " | 369 | pr_debug("failed to open counter: %s, " |
369 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 370 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
370 | strerror(errno)); | 371 | strerror(errno)); |
@@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
408 | goto out_close_fd; | 409 | goto out_close_fd; |
409 | } | 410 | } |
410 | 411 | ||
412 | err = 0; | ||
413 | |||
411 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | 414 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
412 | unsigned int expected; | 415 | unsigned int expected; |
413 | 416 | ||
@@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
416 | 419 | ||
417 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | 420 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { |
418 | pr_debug("perf_evsel__open_read_on_cpu\n"); | 421 | pr_debug("perf_evsel__open_read_on_cpu\n"); |
419 | goto out_close_fd; | 422 | err = -1; |
423 | break; | ||
420 | } | 424 | } |
421 | 425 | ||
422 | expected = nr_open_calls + cpu; | 426 | expected = nr_open_calls + cpu; |
423 | if (evsel->counts->cpu[cpu].val != expected) { | 427 | if (evsel->counts->cpu[cpu].val != expected) { |
424 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | 428 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", |
425 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | 429 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); |
426 | goto out_close_fd; | 430 | err = -1; |
427 | } | 431 | } |
428 | } | 432 | } |
429 | 433 | ||
430 | err = 0; | ||
431 | out_close_fd: | 434 | out_close_fd: |
432 | perf_evsel__close_fd(evsel, 1, threads->nr); | 435 | perf_evsel__close_fd(evsel, 1, threads->nr); |
433 | out_evsel_delete: | 436 | out_evsel_delete: |
@@ -437,6 +440,159 @@ out_thread_map_delete: | |||
437 | return err; | 440 | return err; |
438 | } | 441 | } |
439 | 442 | ||
443 | /* | ||
444 | * This test will generate random numbers of calls to some getpid syscalls, | ||
445 | * then establish an mmap for a group of events that are created to monitor | ||
446 | * the syscalls. | ||
447 | * | ||
448 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
449 | * sample.id field to map back to its respective perf_evsel instance. | ||
450 | * | ||
451 | * Then it checks if the number of syscalls reported as perf events by | ||
452 | * the kernel corresponds to the number of syscalls made. | ||
453 | */ | ||
454 | static int test__basic_mmap(void) | ||
455 | { | ||
456 | int err = -1; | ||
457 | union perf_event *event; | ||
458 | struct thread_map *threads; | ||
459 | struct cpu_map *cpus; | ||
460 | struct perf_evlist *evlist; | ||
461 | struct perf_event_attr attr = { | ||
462 | .type = PERF_TYPE_TRACEPOINT, | ||
463 | .read_format = PERF_FORMAT_ID, | ||
464 | .sample_type = PERF_SAMPLE_ID, | ||
465 | .watermark = 0, | ||
466 | }; | ||
467 | cpu_set_t cpu_set; | ||
468 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
469 | "getpgid", }; | ||
470 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
471 | (void*)getpgid }; | ||
472 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
473 | int ids[nsyscalls]; | ||
474 | unsigned int nr_events[nsyscalls], | ||
475 | expected_nr_events[nsyscalls], i, j; | ||
476 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
477 | |||
478 | for (i = 0; i < nsyscalls; ++i) { | ||
479 | char name[64]; | ||
480 | |||
481 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
482 | ids[i] = trace_event__id(name); | ||
483 | if (ids[i] < 0) { | ||
484 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
485 | return -1; | ||
486 | } | ||
487 | nr_events[i] = 0; | ||
488 | expected_nr_events[i] = random() % 257; | ||
489 | } | ||
490 | |||
491 | threads = thread_map__new(-1, getpid()); | ||
492 | if (threads == NULL) { | ||
493 | pr_debug("thread_map__new\n"); | ||
494 | return -1; | ||
495 | } | ||
496 | |||
497 | cpus = cpu_map__new(NULL); | ||
498 | if (cpus == NULL) { | ||
499 | pr_debug("cpu_map__new\n"); | ||
500 | goto out_free_threads; | ||
501 | } | ||
502 | |||
503 | CPU_ZERO(&cpu_set); | ||
504 | CPU_SET(cpus->map[0], &cpu_set); | ||
505 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
506 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
507 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
508 | cpus->map[0], strerror(errno)); | ||
509 | goto out_free_cpus; | ||
510 | } | ||
511 | |||
512 | evlist = perf_evlist__new(cpus, threads); | ||
513 | if (evlist == NULL) { | ||
514 | pr_debug("perf_evlist__new\n"); | ||
515 | goto out_free_cpus; | ||
516 | } | ||
517 | |||
518 | /* anonymous union fields, can't be initialized above */ | ||
519 | attr.wakeup_events = 1; | ||
520 | attr.sample_period = 1; | ||
521 | |||
522 | for (i = 0; i < nsyscalls; ++i) { | ||
523 | attr.config = ids[i]; | ||
524 | evsels[i] = perf_evsel__new(&attr, i); | ||
525 | if (evsels[i] == NULL) { | ||
526 | pr_debug("perf_evsel__new\n"); | ||
527 | goto out_free_evlist; | ||
528 | } | ||
529 | |||
530 | perf_evlist__add(evlist, evsels[i]); | ||
531 | |||
532 | if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) { | ||
533 | pr_debug("failed to open counter: %s, " | ||
534 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
535 | strerror(errno)); | ||
536 | goto out_close_fd; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
541 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
542 | strerror(errno)); | ||
543 | goto out_close_fd; | ||
544 | } | ||
545 | |||
546 | for (i = 0; i < nsyscalls; ++i) | ||
547 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
548 | int foo = syscalls[i](); | ||
549 | ++foo; | ||
550 | } | ||
551 | |||
552 | while ((event = perf_evlist__read_on_cpu(evlist, 0)) != NULL) { | ||
553 | struct perf_sample sample; | ||
554 | |||
555 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
556 | pr_debug("unexpected %s event\n", | ||
557 | perf_event__name(event->header.type)); | ||
558 | goto out_munmap; | ||
559 | } | ||
560 | |||
561 | perf_event__parse_sample(event, attr.sample_type, false, &sample); | ||
562 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
563 | if (evsel == NULL) { | ||
564 | pr_debug("event with id %" PRIu64 | ||
565 | " doesn't map to an evsel\n", sample.id); | ||
566 | goto out_munmap; | ||
567 | } | ||
568 | nr_events[evsel->idx]++; | ||
569 | } | ||
570 | |||
571 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
572 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
573 | pr_debug("expected %d %s events, got %d\n", | ||
574 | expected_nr_events[evsel->idx], | ||
575 | event_name(evsel), nr_events[evsel->idx]); | ||
576 | goto out_munmap; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | err = 0; | ||
581 | out_munmap: | ||
582 | perf_evlist__munmap(evlist); | ||
583 | out_close_fd: | ||
584 | for (i = 0; i < nsyscalls; ++i) | ||
585 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
586 | out_free_evlist: | ||
587 | perf_evlist__delete(evlist); | ||
588 | out_free_cpus: | ||
589 | cpu_map__delete(cpus); | ||
590 | out_free_threads: | ||
591 | thread_map__delete(threads); | ||
592 | return err; | ||
593 | #undef nsyscalls | ||
594 | } | ||
595 | |||
440 | static struct test { | 596 | static struct test { |
441 | const char *desc; | 597 | const char *desc; |
442 | int (*func)(void); | 598 | int (*func)(void); |
@@ -454,6 +610,10 @@ static struct test { | |||
454 | .func = test__open_syscall_event_on_all_cpus, | 610 | .func = test__open_syscall_event_on_all_cpus, |
455 | }, | 611 | }, |
456 | { | 612 | { |
613 | .desc = "read samples using the mmap interface", | ||
614 | .func = test__basic_mmap, | ||
615 | }, | ||
616 | { | ||
457 | .func = NULL, | 617 | .func = NULL, |
458 | }, | 618 | }, |
459 | }; | 619 | }; |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 0ace786e83e0..67c0459dc325 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -273,21 +273,24 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
273 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 273 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
274 | static u64 cpus_pstate_state[MAX_CPUS]; | 274 | static u64 cpus_pstate_state[MAX_CPUS]; |
275 | 275 | ||
276 | static int process_comm_event(event_t *event, struct sample_data *sample __used, | 276 | static int process_comm_event(union perf_event *event, |
277 | struct perf_sample *sample __used, | ||
277 | struct perf_session *session __used) | 278 | struct perf_session *session __used) |
278 | { | 279 | { |
279 | pid_set_comm(event->comm.tid, event->comm.comm); | 280 | pid_set_comm(event->comm.tid, event->comm.comm); |
280 | return 0; | 281 | return 0; |
281 | } | 282 | } |
282 | 283 | ||
283 | static int process_fork_event(event_t *event, struct sample_data *sample __used, | 284 | static int process_fork_event(union perf_event *event, |
285 | struct perf_sample *sample __used, | ||
284 | struct perf_session *session __used) | 286 | struct perf_session *session __used) |
285 | { | 287 | { |
286 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 288 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
287 | return 0; | 289 | return 0; |
288 | } | 290 | } |
289 | 291 | ||
290 | static int process_exit_event(event_t *event, struct sample_data *sample __used, | 292 | static int process_exit_event(union perf_event *event, |
293 | struct perf_sample *sample __used, | ||
291 | struct perf_session *session __used) | 294 | struct perf_session *session __used) |
292 | { | 295 | { |
293 | pid_exit(event->fork.pid, event->fork.time); | 296 | pid_exit(event->fork.pid, event->fork.time); |
@@ -483,8 +486,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
483 | } | 486 | } |
484 | 487 | ||
485 | 488 | ||
486 | static int process_sample_event(event_t *event __used, | 489 | static int process_sample_event(union perf_event *event __used, |
487 | struct sample_data *sample, | 490 | struct perf_sample *sample, |
488 | struct perf_session *session) | 491 | struct perf_session *session) |
489 | { | 492 | { |
490 | struct trace_entry *te; | 493 | struct trace_entry *te; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5a29d9cd9486..70f1075cc5b0 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -20,11 +20,16 @@ | |||
20 | 20 | ||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/annotate.h" | ||
24 | #include "util/cache.h" | ||
23 | #include "util/color.h" | 25 | #include "util/color.h" |
26 | #include "util/evlist.h" | ||
24 | #include "util/evsel.h" | 27 | #include "util/evsel.h" |
25 | #include "util/session.h" | 28 | #include "util/session.h" |
26 | #include "util/symbol.h" | 29 | #include "util/symbol.h" |
27 | #include "util/thread.h" | 30 | #include "util/thread.h" |
31 | #include "util/thread_map.h" | ||
32 | #include "util/top.h" | ||
28 | #include "util/util.h" | 33 | #include "util/util.h" |
29 | #include <linux/rbtree.h> | 34 | #include <linux/rbtree.h> |
30 | #include "util/parse-options.h" | 35 | #include "util/parse-options.h" |
@@ -45,7 +50,6 @@ | |||
45 | #include <errno.h> | 50 | #include <errno.h> |
46 | #include <time.h> | 51 | #include <time.h> |
47 | #include <sched.h> | 52 | #include <sched.h> |
48 | #include <pthread.h> | ||
49 | 53 | ||
50 | #include <sys/syscall.h> | 54 | #include <sys/syscall.h> |
51 | #include <sys/ioctl.h> | 55 | #include <sys/ioctl.h> |
@@ -60,85 +64,42 @@ | |||
60 | 64 | ||
61 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 65 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
62 | 66 | ||
67 | static struct perf_top top = { | ||
68 | .count_filter = 5, | ||
69 | .delay_secs = 2, | ||
70 | .display_weighted = -1, | ||
71 | .target_pid = -1, | ||
72 | .target_tid = -1, | ||
73 | .active_symbols = LIST_HEAD_INIT(top.active_symbols), | ||
74 | .active_symbols_lock = PTHREAD_MUTEX_INITIALIZER, | ||
75 | .active_symbols_cond = PTHREAD_COND_INITIALIZER, | ||
76 | .freq = 1000, /* 1 KHz */ | ||
77 | }; | ||
78 | |||
63 | static bool system_wide = false; | 79 | static bool system_wide = false; |
64 | 80 | ||
65 | static int default_interval = 0; | 81 | static bool use_tui, use_stdio; |
66 | 82 | ||
67 | static int count_filter = 5; | 83 | static int default_interval = 0; |
68 | static int print_entries; | ||
69 | 84 | ||
70 | static int target_pid = -1; | ||
71 | static int target_tid = -1; | ||
72 | static struct thread_map *threads; | ||
73 | static bool inherit = false; | 85 | static bool inherit = false; |
74 | static struct cpu_map *cpus; | ||
75 | static int realtime_prio = 0; | 86 | static int realtime_prio = 0; |
76 | static bool group = false; | 87 | static bool group = false; |
77 | static unsigned int page_size; | 88 | static unsigned int page_size; |
78 | static unsigned int mmap_pages = 16; | 89 | static unsigned int mmap_pages = 128; |
79 | static int freq = 1000; /* 1 KHz */ | ||
80 | 90 | ||
81 | static int delay_secs = 2; | ||
82 | static bool zero = false; | ||
83 | static bool dump_symtab = false; | 91 | static bool dump_symtab = false; |
84 | 92 | ||
85 | static bool hide_kernel_symbols = false; | ||
86 | static bool hide_user_symbols = false; | ||
87 | static struct winsize winsize; | 93 | static struct winsize winsize; |
88 | 94 | ||
89 | /* | ||
90 | * Source | ||
91 | */ | ||
92 | |||
93 | struct source_line { | ||
94 | u64 eip; | ||
95 | unsigned long count[MAX_COUNTERS]; | ||
96 | char *line; | ||
97 | struct source_line *next; | ||
98 | }; | ||
99 | |||
100 | static const char *sym_filter = NULL; | 95 | static const char *sym_filter = NULL; |
101 | struct sym_entry *sym_filter_entry = NULL; | ||
102 | struct sym_entry *sym_filter_entry_sched = NULL; | 96 | struct sym_entry *sym_filter_entry_sched = NULL; |
103 | static int sym_pcnt_filter = 5; | 97 | static int sym_pcnt_filter = 5; |
104 | static int sym_counter = 0; | ||
105 | static struct perf_evsel *sym_evsel = NULL; | ||
106 | static int display_weighted = -1; | ||
107 | static const char *cpu_list; | ||
108 | |||
109 | /* | ||
110 | * Symbols | ||
111 | */ | ||
112 | |||
113 | struct sym_entry_source { | ||
114 | struct source_line *source; | ||
115 | struct source_line *lines; | ||
116 | struct source_line **lines_tail; | ||
117 | pthread_mutex_t lock; | ||
118 | }; | ||
119 | |||
120 | struct sym_entry { | ||
121 | struct rb_node rb_node; | ||
122 | struct list_head node; | ||
123 | unsigned long snap_count; | ||
124 | double weight; | ||
125 | int skip; | ||
126 | u16 name_len; | ||
127 | u8 origin; | ||
128 | struct map *map; | ||
129 | struct sym_entry_source *src; | ||
130 | unsigned long count[0]; | ||
131 | }; | ||
132 | 98 | ||
133 | /* | 99 | /* |
134 | * Source functions | 100 | * Source functions |
135 | */ | 101 | */ |
136 | 102 | ||
137 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
138 | { | ||
139 | return ((void *)self) + symbol_conf.priv_size; | ||
140 | } | ||
141 | |||
142 | void get_term_dimensions(struct winsize *ws) | 103 | void get_term_dimensions(struct winsize *ws) |
143 | { | 104 | { |
144 | char *s = getenv("LINES"); | 105 | char *s = getenv("LINES"); |
@@ -163,10 +124,10 @@ void get_term_dimensions(struct winsize *ws) | |||
163 | 124 | ||
164 | static void update_print_entries(struct winsize *ws) | 125 | static void update_print_entries(struct winsize *ws) |
165 | { | 126 | { |
166 | print_entries = ws->ws_row; | 127 | top.print_entries = ws->ws_row; |
167 | 128 | ||
168 | if (print_entries > 9) | 129 | if (top.print_entries > 9) |
169 | print_entries -= 9; | 130 | top.print_entries -= 9; |
170 | } | 131 | } |
171 | 132 | ||
172 | static void sig_winch_handler(int sig __used) | 133 | static void sig_winch_handler(int sig __used) |
@@ -178,12 +139,9 @@ static void sig_winch_handler(int sig __used) | |||
178 | static int parse_source(struct sym_entry *syme) | 139 | static int parse_source(struct sym_entry *syme) |
179 | { | 140 | { |
180 | struct symbol *sym; | 141 | struct symbol *sym; |
181 | struct sym_entry_source *source; | 142 | struct annotation *notes; |
182 | struct map *map; | 143 | struct map *map; |
183 | FILE *file; | 144 | int err = -1; |
184 | char command[PATH_MAX*2]; | ||
185 | const char *path; | ||
186 | u64 len; | ||
187 | 145 | ||
188 | if (!syme) | 146 | if (!syme) |
189 | return -1; | 147 | return -1; |
@@ -194,411 +152,137 @@ static int parse_source(struct sym_entry *syme) | |||
194 | /* | 152 | /* |
195 | * We can't annotate with just /proc/kallsyms | 153 | * We can't annotate with just /proc/kallsyms |
196 | */ | 154 | */ |
197 | if (map->dso->origin == DSO__ORIG_KERNEL) | 155 | if (map->dso->symtab_type == SYMTAB__KALLSYMS) { |
156 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
157 | "path\n", sym->name); | ||
158 | sleep(1); | ||
198 | return -1; | 159 | return -1; |
199 | |||
200 | if (syme->src == NULL) { | ||
201 | syme->src = zalloc(sizeof(*source)); | ||
202 | if (syme->src == NULL) | ||
203 | return -1; | ||
204 | pthread_mutex_init(&syme->src->lock, NULL); | ||
205 | } | 160 | } |
206 | 161 | ||
207 | source = syme->src; | 162 | notes = symbol__annotation(sym); |
208 | 163 | if (notes->src != NULL) { | |
209 | if (source->lines) { | 164 | pthread_mutex_lock(¬es->lock); |
210 | pthread_mutex_lock(&source->lock); | ||
211 | goto out_assign; | 165 | goto out_assign; |
212 | } | 166 | } |
213 | path = map->dso->long_name; | ||
214 | |||
215 | len = sym->end - sym->start; | ||
216 | |||
217 | sprintf(command, | ||
218 | "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", | ||
219 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), | ||
220 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); | ||
221 | |||
222 | file = popen(command, "r"); | ||
223 | if (!file) | ||
224 | return -1; | ||
225 | 167 | ||
226 | pthread_mutex_lock(&source->lock); | 168 | pthread_mutex_lock(¬es->lock); |
227 | source->lines_tail = &source->lines; | ||
228 | while (!feof(file)) { | ||
229 | struct source_line *src; | ||
230 | size_t dummy = 0; | ||
231 | char *c, *sep; | ||
232 | 169 | ||
233 | src = malloc(sizeof(struct source_line)); | 170 | if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) { |
234 | assert(src != NULL); | 171 | pthread_mutex_unlock(¬es->lock); |
235 | memset(src, 0, sizeof(struct source_line)); | 172 | pr_err("Not enough memory for annotating '%s' symbol!\n", |
236 | 173 | sym->name); | |
237 | if (getline(&src->line, &dummy, file) < 0) | 174 | sleep(1); |
238 | break; | 175 | return err; |
239 | if (!src->line) | ||
240 | break; | ||
241 | |||
242 | c = strchr(src->line, '\n'); | ||
243 | if (c) | ||
244 | *c = 0; | ||
245 | |||
246 | src->next = NULL; | ||
247 | *source->lines_tail = src; | ||
248 | source->lines_tail = &src->next; | ||
249 | |||
250 | src->eip = strtoull(src->line, &sep, 16); | ||
251 | if (*sep == ':') | ||
252 | src->eip = map__objdump_2ip(map, src->eip); | ||
253 | else /* this line has no ip info (e.g. source line) */ | ||
254 | src->eip = 0; | ||
255 | } | 176 | } |
256 | pclose(file); | 177 | |
178 | err = symbol__annotate(sym, syme->map, 0); | ||
179 | if (err == 0) { | ||
257 | out_assign: | 180 | out_assign: |
258 | sym_filter_entry = syme; | 181 | top.sym_filter_entry = syme; |
259 | pthread_mutex_unlock(&source->lock); | 182 | } |
260 | return 0; | 183 | |
184 | pthread_mutex_unlock(¬es->lock); | ||
185 | return err; | ||
261 | } | 186 | } |
262 | 187 | ||
263 | static void __zero_source_counters(struct sym_entry *syme) | 188 | static void __zero_source_counters(struct sym_entry *syme) |
264 | { | 189 | { |
265 | int i; | 190 | struct symbol *sym = sym_entry__symbol(syme); |
266 | struct source_line *line; | 191 | symbol__annotate_zero_histograms(sym); |
267 | |||
268 | line = syme->src->lines; | ||
269 | while (line) { | ||
270 | for (i = 0; i < nr_counters; i++) | ||
271 | line->count[i] = 0; | ||
272 | line = line->next; | ||
273 | } | ||
274 | } | 192 | } |
275 | 193 | ||
276 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | 194 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) |
277 | { | 195 | { |
278 | struct source_line *line; | 196 | struct annotation *notes; |
279 | 197 | struct symbol *sym; | |
280 | if (syme != sym_filter_entry) | ||
281 | return; | ||
282 | 198 | ||
283 | if (pthread_mutex_trylock(&syme->src->lock)) | 199 | if (syme != top.sym_filter_entry) |
284 | return; | 200 | return; |
285 | 201 | ||
286 | if (syme->src == NULL || syme->src->source == NULL) | 202 | sym = sym_entry__symbol(syme); |
287 | goto out_unlock; | 203 | notes = symbol__annotation(sym); |
288 | |||
289 | for (line = syme->src->lines; line; line = line->next) { | ||
290 | /* skip lines without IP info */ | ||
291 | if (line->eip == 0) | ||
292 | continue; | ||
293 | if (line->eip == ip) { | ||
294 | line->count[counter]++; | ||
295 | break; | ||
296 | } | ||
297 | if (line->eip > ip) | ||
298 | break; | ||
299 | } | ||
300 | out_unlock: | ||
301 | pthread_mutex_unlock(&syme->src->lock); | ||
302 | } | ||
303 | |||
304 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) | ||
305 | |||
306 | static void lookup_sym_source(struct sym_entry *syme) | ||
307 | { | ||
308 | struct symbol *symbol = sym_entry__symbol(syme); | ||
309 | struct source_line *line; | ||
310 | char pattern[PATTERN_LEN + 1]; | ||
311 | |||
312 | sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4, | ||
313 | map__rip_2objdump(syme->map, symbol->start)); | ||
314 | |||
315 | pthread_mutex_lock(&syme->src->lock); | ||
316 | for (line = syme->src->lines; line; line = line->next) { | ||
317 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { | ||
318 | syme->src->source = line; | ||
319 | break; | ||
320 | } | ||
321 | } | ||
322 | pthread_mutex_unlock(&syme->src->lock); | ||
323 | } | ||
324 | 204 | ||
325 | static void show_lines(struct source_line *queue, int count, int total) | 205 | if (pthread_mutex_trylock(¬es->lock)) |
326 | { | 206 | return; |
327 | int i; | ||
328 | struct source_line *line; | ||
329 | 207 | ||
330 | line = queue; | 208 | ip = syme->map->map_ip(syme->map, ip); |
331 | for (i = 0; i < count; i++) { | 209 | symbol__inc_addr_samples(sym, syme->map, counter, ip); |
332 | float pcnt = 100.0*(float)line->count[sym_counter]/(float)total; | ||
333 | 210 | ||
334 | printf("%8li %4.1f%%\t%s\n", line->count[sym_counter], pcnt, line->line); | 211 | pthread_mutex_unlock(¬es->lock); |
335 | line = line->next; | ||
336 | } | ||
337 | } | 212 | } |
338 | 213 | ||
339 | #define TRACE_COUNT 3 | ||
340 | |||
341 | static void show_details(struct sym_entry *syme) | 214 | static void show_details(struct sym_entry *syme) |
342 | { | 215 | { |
216 | struct annotation *notes; | ||
343 | struct symbol *symbol; | 217 | struct symbol *symbol; |
344 | struct source_line *line; | 218 | int more; |
345 | struct source_line *line_queue = NULL; | ||
346 | int displayed = 0; | ||
347 | int line_queue_count = 0, total = 0, more = 0; | ||
348 | 219 | ||
349 | if (!syme) | 220 | if (!syme) |
350 | return; | 221 | return; |
351 | 222 | ||
352 | if (!syme->src->source) | ||
353 | lookup_sym_source(syme); | ||
354 | |||
355 | if (!syme->src->source) | ||
356 | return; | ||
357 | |||
358 | symbol = sym_entry__symbol(syme); | 223 | symbol = sym_entry__symbol(syme); |
359 | printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name); | 224 | notes = symbol__annotation(symbol); |
360 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | ||
361 | |||
362 | pthread_mutex_lock(&syme->src->lock); | ||
363 | line = syme->src->source; | ||
364 | while (line) { | ||
365 | total += line->count[sym_counter]; | ||
366 | line = line->next; | ||
367 | } | ||
368 | |||
369 | line = syme->src->source; | ||
370 | while (line) { | ||
371 | float pcnt = 0.0; | ||
372 | |||
373 | if (!line_queue_count) | ||
374 | line_queue = line; | ||
375 | line_queue_count++; | ||
376 | |||
377 | if (line->count[sym_counter]) | ||
378 | pcnt = 100.0 * line->count[sym_counter] / (float)total; | ||
379 | if (pcnt >= (float)sym_pcnt_filter) { | ||
380 | if (displayed <= print_entries) | ||
381 | show_lines(line_queue, line_queue_count, total); | ||
382 | else more++; | ||
383 | displayed += line_queue_count; | ||
384 | line_queue_count = 0; | ||
385 | line_queue = NULL; | ||
386 | } else if (line_queue_count > TRACE_COUNT) { | ||
387 | line_queue = line_queue->next; | ||
388 | line_queue_count--; | ||
389 | } | ||
390 | |||
391 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | ||
392 | line = line->next; | ||
393 | } | ||
394 | pthread_mutex_unlock(&syme->src->lock); | ||
395 | if (more) | ||
396 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Symbols will be added here in event__process_sample and will get out | ||
401 | * after decayed. | ||
402 | */ | ||
403 | static LIST_HEAD(active_symbols); | ||
404 | static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER; | ||
405 | 225 | ||
406 | /* | 226 | pthread_mutex_lock(¬es->lock); |
407 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
408 | */ | ||
409 | static double sym_weight(const struct sym_entry *sym) | ||
410 | { | ||
411 | double weight = sym->snap_count; | ||
412 | int counter; | ||
413 | 227 | ||
414 | if (!display_weighted) | 228 | if (notes->src == NULL) |
415 | return weight; | 229 | goto out_unlock; |
416 | |||
417 | for (counter = 1; counter < nr_counters-1; counter++) | ||
418 | weight *= sym->count[counter]; | ||
419 | 230 | ||
420 | weight /= (sym->count[counter] + 1); | 231 | printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name); |
232 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | ||
421 | 233 | ||
422 | return weight; | 234 | more = symbol__annotate_printf(symbol, syme->map, top.sym_evsel->idx, |
235 | 0, sym_pcnt_filter, top.print_entries, 4); | ||
236 | if (top.zero) | ||
237 | symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx); | ||
238 | else | ||
239 | symbol__annotate_decay_histogram(symbol, top.sym_evsel->idx); | ||
240 | if (more != 0) | ||
241 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | ||
242 | out_unlock: | ||
243 | pthread_mutex_unlock(¬es->lock); | ||
423 | } | 244 | } |
424 | 245 | ||
425 | static long samples; | ||
426 | static long kernel_samples, us_samples; | ||
427 | static long exact_samples; | ||
428 | static long guest_us_samples, guest_kernel_samples; | ||
429 | static const char CONSOLE_CLEAR[] = "[H[2J"; | 246 | static const char CONSOLE_CLEAR[] = "[H[2J"; |
430 | 247 | ||
431 | static void __list_insert_active_sym(struct sym_entry *syme) | 248 | static void __list_insert_active_sym(struct sym_entry *syme) |
432 | { | 249 | { |
433 | list_add(&syme->node, &active_symbols); | 250 | list_add(&syme->node, &top.active_symbols); |
434 | } | 251 | } |
435 | 252 | ||
436 | static void list_remove_active_sym(struct sym_entry *syme) | 253 | static void print_sym_table(struct perf_session *session) |
437 | { | 254 | { |
438 | pthread_mutex_lock(&active_symbols_lock); | 255 | char bf[160]; |
439 | list_del_init(&syme->node); | 256 | int printed = 0; |
440 | pthread_mutex_unlock(&active_symbols_lock); | ||
441 | } | ||
442 | |||
443 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
444 | { | ||
445 | struct rb_node **p = &tree->rb_node; | ||
446 | struct rb_node *parent = NULL; | ||
447 | struct sym_entry *iter; | ||
448 | |||
449 | while (*p != NULL) { | ||
450 | parent = *p; | ||
451 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
452 | |||
453 | if (se->weight > iter->weight) | ||
454 | p = &(*p)->rb_left; | ||
455 | else | ||
456 | p = &(*p)->rb_right; | ||
457 | } | ||
458 | |||
459 | rb_link_node(&se->rb_node, parent, p); | ||
460 | rb_insert_color(&se->rb_node, tree); | ||
461 | } | ||
462 | |||
463 | static void print_sym_table(void) | ||
464 | { | ||
465 | int printed = 0, j; | ||
466 | struct perf_evsel *counter; | ||
467 | int snap = !display_weighted ? sym_counter : 0; | ||
468 | float samples_per_sec = samples/delay_secs; | ||
469 | float ksamples_per_sec = kernel_samples/delay_secs; | ||
470 | float us_samples_per_sec = (us_samples)/delay_secs; | ||
471 | float guest_kernel_samples_per_sec = (guest_kernel_samples)/delay_secs; | ||
472 | float guest_us_samples_per_sec = (guest_us_samples)/delay_secs; | ||
473 | float esamples_percent = (100.0*exact_samples)/samples; | ||
474 | float sum_ksamples = 0.0; | ||
475 | struct sym_entry *syme, *n; | ||
476 | struct rb_root tmp = RB_ROOT; | ||
477 | struct rb_node *nd; | 257 | struct rb_node *nd; |
478 | int sym_width = 0, dso_width = 0, dso_short_width = 0; | 258 | struct sym_entry *syme; |
259 | struct rb_root tmp = RB_ROOT; | ||
479 | const int win_width = winsize.ws_col - 1; | 260 | const int win_width = winsize.ws_col - 1; |
480 | 261 | int sym_width, dso_width, dso_short_width; | |
481 | samples = us_samples = kernel_samples = exact_samples = 0; | 262 | float sum_ksamples = perf_top__decay_samples(&top, &tmp); |
482 | guest_kernel_samples = guest_us_samples = 0; | ||
483 | |||
484 | /* Sort the active symbols */ | ||
485 | pthread_mutex_lock(&active_symbols_lock); | ||
486 | syme = list_entry(active_symbols.next, struct sym_entry, node); | ||
487 | pthread_mutex_unlock(&active_symbols_lock); | ||
488 | |||
489 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | ||
490 | syme->snap_count = syme->count[snap]; | ||
491 | if (syme->snap_count != 0) { | ||
492 | |||
493 | if ((hide_user_symbols && | ||
494 | syme->origin == PERF_RECORD_MISC_USER) || | ||
495 | (hide_kernel_symbols && | ||
496 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
497 | list_remove_active_sym(syme); | ||
498 | continue; | ||
499 | } | ||
500 | syme->weight = sym_weight(syme); | ||
501 | rb_insert_active_sym(&tmp, syme); | ||
502 | sum_ksamples += syme->snap_count; | ||
503 | |||
504 | for (j = 0; j < nr_counters; j++) | ||
505 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; | ||
506 | } else | ||
507 | list_remove_active_sym(syme); | ||
508 | } | ||
509 | 263 | ||
510 | puts(CONSOLE_CLEAR); | 264 | puts(CONSOLE_CLEAR); |
511 | 265 | ||
512 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 266 | perf_top__header_snprintf(&top, bf, sizeof(bf)); |
513 | if (!perf_guest) { | 267 | printf("%s\n", bf); |
514 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | ||
515 | " exact: %4.1f%% [", | ||
516 | samples_per_sec, | ||
517 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
518 | samples_per_sec)), | ||
519 | esamples_percent); | ||
520 | } else { | ||
521 | printf(" PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
522 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
523 | " exact: %4.1f%% [", | ||
524 | samples_per_sec, | ||
525 | 100.0 - (100.0 * ((samples_per_sec-ksamples_per_sec) / | ||
526 | samples_per_sec)), | ||
527 | 100.0 - (100.0 * ((samples_per_sec-us_samples_per_sec) / | ||
528 | samples_per_sec)), | ||
529 | 100.0 - (100.0 * ((samples_per_sec - | ||
530 | guest_kernel_samples_per_sec) / | ||
531 | samples_per_sec)), | ||
532 | 100.0 - (100.0 * ((samples_per_sec - | ||
533 | guest_us_samples_per_sec) / | ||
534 | samples_per_sec)), | ||
535 | esamples_percent); | ||
536 | } | ||
537 | |||
538 | if (nr_counters == 1 || !display_weighted) { | ||
539 | struct perf_evsel *first; | ||
540 | first = list_entry(evsel_list.next, struct perf_evsel, node); | ||
541 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); | ||
542 | if (freq) | ||
543 | printf("Hz "); | ||
544 | else | ||
545 | printf(" "); | ||
546 | } | ||
547 | |||
548 | if (!display_weighted) | ||
549 | printf("%s", event_name(sym_evsel)); | ||
550 | else list_for_each_entry(counter, &evsel_list, node) { | ||
551 | if (counter->idx) | ||
552 | printf("/"); | ||
553 | |||
554 | printf("%s", event_name(counter)); | ||
555 | } | ||
556 | 268 | ||
557 | printf( "], "); | 269 | perf_top__reset_sample_counters(&top); |
558 | |||
559 | if (target_pid != -1) | ||
560 | printf(" (target_pid: %d", target_pid); | ||
561 | else if (target_tid != -1) | ||
562 | printf(" (target_tid: %d", target_tid); | ||
563 | else | ||
564 | printf(" (all"); | ||
565 | |||
566 | if (cpu_list) | ||
567 | printf(", CPU%s: %s)\n", cpus->nr > 1 ? "s" : "", cpu_list); | ||
568 | else { | ||
569 | if (target_tid != -1) | ||
570 | printf(")\n"); | ||
571 | else | ||
572 | printf(", %d CPU%s)\n", cpus->nr, cpus->nr > 1 ? "s" : ""); | ||
573 | } | ||
574 | 270 | ||
575 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 271 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
576 | 272 | ||
577 | if (sym_filter_entry) { | 273 | if (session->hists.stats.total_lost != 0) { |
578 | show_details(sym_filter_entry); | 274 | color_fprintf(stdout, PERF_COLOR_RED, "WARNING:"); |
579 | return; | 275 | printf(" LOST %" PRIu64 " events, Check IO/CPU overload\n", |
276 | session->hists.stats.total_lost); | ||
580 | } | 277 | } |
581 | 278 | ||
582 | /* | 279 | if (top.sym_filter_entry) { |
583 | * Find the longest symbol name that will be displayed | 280 | show_details(top.sym_filter_entry); |
584 | */ | 281 | return; |
585 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
586 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
587 | if (++printed > print_entries || | ||
588 | (int)syme->snap_count < count_filter) | ||
589 | continue; | ||
590 | |||
591 | if (syme->map->dso->long_name_len > dso_width) | ||
592 | dso_width = syme->map->dso->long_name_len; | ||
593 | |||
594 | if (syme->map->dso->short_name_len > dso_short_width) | ||
595 | dso_short_width = syme->map->dso->short_name_len; | ||
596 | |||
597 | if (syme->name_len > sym_width) | ||
598 | sym_width = syme->name_len; | ||
599 | } | 282 | } |
600 | 283 | ||
601 | printed = 0; | 284 | perf_top__find_widths(&top, &tmp, &dso_width, &dso_short_width, |
285 | &sym_width); | ||
602 | 286 | ||
603 | if (sym_width + dso_width > winsize.ws_col - 29) { | 287 | if (sym_width + dso_width > winsize.ws_col - 29) { |
604 | dso_width = dso_short_width; | 288 | dso_width = dso_short_width; |
@@ -606,7 +290,7 @@ static void print_sym_table(void) | |||
606 | sym_width = winsize.ws_col - dso_width - 29; | 290 | sym_width = winsize.ws_col - dso_width - 29; |
607 | } | 291 | } |
608 | putchar('\n'); | 292 | putchar('\n'); |
609 | if (nr_counters == 1) | 293 | if (top.evlist->nr_entries == 1) |
610 | printf(" samples pcnt"); | 294 | printf(" samples pcnt"); |
611 | else | 295 | else |
612 | printf(" weight samples pcnt"); | 296 | printf(" weight samples pcnt"); |
@@ -615,7 +299,7 @@ static void print_sym_table(void) | |||
615 | printf(" RIP "); | 299 | printf(" RIP "); |
616 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | 300 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
617 | printf(" %s _______ _____", | 301 | printf(" %s _______ _____", |
618 | nr_counters == 1 ? " " : "______"); | 302 | top.evlist->nr_entries == 1 ? " " : "______"); |
619 | if (verbose) | 303 | if (verbose) |
620 | printf(" ________________"); | 304 | printf(" ________________"); |
621 | printf(" %-*.*s", sym_width, sym_width, graph_line); | 305 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
@@ -628,13 +312,14 @@ static void print_sym_table(void) | |||
628 | 312 | ||
629 | syme = rb_entry(nd, struct sym_entry, rb_node); | 313 | syme = rb_entry(nd, struct sym_entry, rb_node); |
630 | sym = sym_entry__symbol(syme); | 314 | sym = sym_entry__symbol(syme); |
631 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 315 | if (++printed > top.print_entries || |
316 | (int)syme->snap_count < top.count_filter) | ||
632 | continue; | 317 | continue; |
633 | 318 | ||
634 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 319 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
635 | sum_ksamples)); | 320 | sum_ksamples)); |
636 | 321 | ||
637 | if (nr_counters == 1 || !display_weighted) | 322 | if (top.evlist->nr_entries == 1 || !top.display_weighted) |
638 | printf("%20.2f ", syme->weight); | 323 | printf("%20.2f ", syme->weight); |
639 | else | 324 | else |
640 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | 325 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
@@ -693,10 +378,8 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
693 | 378 | ||
694 | /* zero counters of active symbol */ | 379 | /* zero counters of active symbol */ |
695 | if (syme) { | 380 | if (syme) { |
696 | pthread_mutex_lock(&syme->src->lock); | ||
697 | __zero_source_counters(syme); | 381 | __zero_source_counters(syme); |
698 | *target = NULL; | 382 | *target = NULL; |
699 | pthread_mutex_unlock(&syme->src->lock); | ||
700 | } | 383 | } |
701 | 384 | ||
702 | fprintf(stdout, "\n%s: ", msg); | 385 | fprintf(stdout, "\n%s: ", msg); |
@@ -707,11 +390,11 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
707 | if (p) | 390 | if (p) |
708 | *p = 0; | 391 | *p = 0; |
709 | 392 | ||
710 | pthread_mutex_lock(&active_symbols_lock); | 393 | pthread_mutex_lock(&top.active_symbols_lock); |
711 | syme = list_entry(active_symbols.next, struct sym_entry, node); | 394 | syme = list_entry(top.active_symbols.next, struct sym_entry, node); |
712 | pthread_mutex_unlock(&active_symbols_lock); | 395 | pthread_mutex_unlock(&top.active_symbols_lock); |
713 | 396 | ||
714 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 397 | list_for_each_entry_safe_from(syme, n, &top.active_symbols, node) { |
715 | struct symbol *sym = sym_entry__symbol(syme); | 398 | struct symbol *sym = sym_entry__symbol(syme); |
716 | 399 | ||
717 | if (!strcmp(buf, sym->name)) { | 400 | if (!strcmp(buf, sym->name)) { |
@@ -735,34 +418,34 @@ static void print_mapped_keys(void) | |||
735 | { | 418 | { |
736 | char *name = NULL; | 419 | char *name = NULL; |
737 | 420 | ||
738 | if (sym_filter_entry) { | 421 | if (top.sym_filter_entry) { |
739 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); | 422 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); |
740 | name = sym->name; | 423 | name = sym->name; |
741 | } | 424 | } |
742 | 425 | ||
743 | fprintf(stdout, "\nMapped keys:\n"); | 426 | fprintf(stdout, "\nMapped keys:\n"); |
744 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); | 427 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", top.delay_secs); |
745 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | 428 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", top.print_entries); |
746 | 429 | ||
747 | if (nr_counters > 1) | 430 | if (top.evlist->nr_entries > 1) |
748 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); | 431 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(top.sym_evsel)); |
749 | 432 | ||
750 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 433 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", top.count_filter); |
751 | 434 | ||
752 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 435 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
753 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 436 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
754 | fprintf(stdout, "\t[S] stop annotation.\n"); | 437 | fprintf(stdout, "\t[S] stop annotation.\n"); |
755 | 438 | ||
756 | if (nr_counters > 1) | 439 | if (top.evlist->nr_entries > 1) |
757 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 440 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", top.display_weighted ? 1 : 0); |
758 | 441 | ||
759 | fprintf(stdout, | 442 | fprintf(stdout, |
760 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | 443 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", |
761 | hide_kernel_symbols ? "yes" : "no"); | 444 | top.hide_kernel_symbols ? "yes" : "no"); |
762 | fprintf(stdout, | 445 | fprintf(stdout, |
763 | "\t[U] hide user symbols. \t(%s)\n", | 446 | "\t[U] hide user symbols. \t(%s)\n", |
764 | hide_user_symbols ? "yes" : "no"); | 447 | top.hide_user_symbols ? "yes" : "no"); |
765 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 448 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", top.zero ? 1 : 0); |
766 | fprintf(stdout, "\t[qQ] quit.\n"); | 449 | fprintf(stdout, "\t[qQ] quit.\n"); |
767 | } | 450 | } |
768 | 451 | ||
@@ -783,7 +466,7 @@ static int key_mapped(int c) | |||
783 | return 1; | 466 | return 1; |
784 | case 'E': | 467 | case 'E': |
785 | case 'w': | 468 | case 'w': |
786 | return nr_counters > 1 ? 1 : 0; | 469 | return top.evlist->nr_entries > 1 ? 1 : 0; |
787 | default: | 470 | default: |
788 | break; | 471 | break; |
789 | } | 472 | } |
@@ -818,47 +501,48 @@ static void handle_keypress(struct perf_session *session, int c) | |||
818 | 501 | ||
819 | switch (c) { | 502 | switch (c) { |
820 | case 'd': | 503 | case 'd': |
821 | prompt_integer(&delay_secs, "Enter display delay"); | 504 | prompt_integer(&top.delay_secs, "Enter display delay"); |
822 | if (delay_secs < 1) | 505 | if (top.delay_secs < 1) |
823 | delay_secs = 1; | 506 | top.delay_secs = 1; |
824 | break; | 507 | break; |
825 | case 'e': | 508 | case 'e': |
826 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 509 | prompt_integer(&top.print_entries, "Enter display entries (lines)"); |
827 | if (print_entries == 0) { | 510 | if (top.print_entries == 0) { |
828 | sig_winch_handler(SIGWINCH); | 511 | sig_winch_handler(SIGWINCH); |
829 | signal(SIGWINCH, sig_winch_handler); | 512 | signal(SIGWINCH, sig_winch_handler); |
830 | } else | 513 | } else |
831 | signal(SIGWINCH, SIG_DFL); | 514 | signal(SIGWINCH, SIG_DFL); |
832 | break; | 515 | break; |
833 | case 'E': | 516 | case 'E': |
834 | if (nr_counters > 1) { | 517 | if (top.evlist->nr_entries > 1) { |
518 | int counter; | ||
835 | fprintf(stderr, "\nAvailable events:"); | 519 | fprintf(stderr, "\nAvailable events:"); |
836 | 520 | ||
837 | list_for_each_entry(sym_evsel, &evsel_list, node) | 521 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) |
838 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); | 522 | fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel)); |
839 | 523 | ||
840 | prompt_integer(&sym_counter, "Enter details event counter"); | 524 | prompt_integer(&counter, "Enter details event counter"); |
841 | 525 | ||
842 | if (sym_counter >= nr_counters) { | 526 | if (counter >= top.evlist->nr_entries) { |
843 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 527 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); |
844 | sym_counter = 0; | 528 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel)); |
845 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); | ||
846 | sleep(1); | 529 | sleep(1); |
847 | break; | 530 | break; |
848 | } | 531 | } |
849 | list_for_each_entry(sym_evsel, &evsel_list, node) | 532 | list_for_each_entry(top.sym_evsel, &top.evlist->entries, node) |
850 | if (sym_evsel->idx == sym_counter) | 533 | if (top.sym_evsel->idx == counter) |
851 | break; | 534 | break; |
852 | } else sym_counter = 0; | 535 | } else |
536 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | ||
853 | break; | 537 | break; |
854 | case 'f': | 538 | case 'f': |
855 | prompt_integer(&count_filter, "Enter display event count filter"); | 539 | prompt_integer(&top.count_filter, "Enter display event count filter"); |
856 | break; | 540 | break; |
857 | case 'F': | 541 | case 'F': |
858 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 542 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
859 | break; | 543 | break; |
860 | case 'K': | 544 | case 'K': |
861 | hide_kernel_symbols = !hide_kernel_symbols; | 545 | top.hide_kernel_symbols = !top.hide_kernel_symbols; |
862 | break; | 546 | break; |
863 | case 'q': | 547 | case 'q': |
864 | case 'Q': | 548 | case 'Q': |
@@ -867,34 +551,50 @@ static void handle_keypress(struct perf_session *session, int c) | |||
867 | perf_session__fprintf_dsos(session, stderr); | 551 | perf_session__fprintf_dsos(session, stderr); |
868 | exit(0); | 552 | exit(0); |
869 | case 's': | 553 | case 's': |
870 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 554 | prompt_symbol(&top.sym_filter_entry, "Enter details symbol"); |
871 | break; | 555 | break; |
872 | case 'S': | 556 | case 'S': |
873 | if (!sym_filter_entry) | 557 | if (!top.sym_filter_entry) |
874 | break; | 558 | break; |
875 | else { | 559 | else { |
876 | struct sym_entry *syme = sym_filter_entry; | 560 | struct sym_entry *syme = top.sym_filter_entry; |
877 | 561 | ||
878 | pthread_mutex_lock(&syme->src->lock); | 562 | top.sym_filter_entry = NULL; |
879 | sym_filter_entry = NULL; | ||
880 | __zero_source_counters(syme); | 563 | __zero_source_counters(syme); |
881 | pthread_mutex_unlock(&syme->src->lock); | ||
882 | } | 564 | } |
883 | break; | 565 | break; |
884 | case 'U': | 566 | case 'U': |
885 | hide_user_symbols = !hide_user_symbols; | 567 | top.hide_user_symbols = !top.hide_user_symbols; |
886 | break; | 568 | break; |
887 | case 'w': | 569 | case 'w': |
888 | display_weighted = ~display_weighted; | 570 | top.display_weighted = ~top.display_weighted; |
889 | break; | 571 | break; |
890 | case 'z': | 572 | case 'z': |
891 | zero = !zero; | 573 | top.zero = !top.zero; |
892 | break; | 574 | break; |
893 | default: | 575 | default: |
894 | break; | 576 | break; |
895 | } | 577 | } |
896 | } | 578 | } |
897 | 579 | ||
580 | static void *display_thread_tui(void *arg __used) | ||
581 | { | ||
582 | int err = 0; | ||
583 | pthread_mutex_lock(&top.active_symbols_lock); | ||
584 | while (list_empty(&top.active_symbols)) { | ||
585 | err = pthread_cond_wait(&top.active_symbols_cond, | ||
586 | &top.active_symbols_lock); | ||
587 | if (err) | ||
588 | break; | ||
589 | } | ||
590 | pthread_mutex_unlock(&top.active_symbols_lock); | ||
591 | if (!err) | ||
592 | perf_top__tui_browser(&top); | ||
593 | exit_browser(0); | ||
594 | exit(0); | ||
595 | return NULL; | ||
596 | } | ||
597 | |||
898 | static void *display_thread(void *arg __used) | 598 | static void *display_thread(void *arg __used) |
899 | { | 599 | { |
900 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 600 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
@@ -909,13 +609,13 @@ static void *display_thread(void *arg __used) | |||
909 | tc.c_cc[VTIME] = 0; | 609 | tc.c_cc[VTIME] = 0; |
910 | 610 | ||
911 | repeat: | 611 | repeat: |
912 | delay_msecs = delay_secs * 1000; | 612 | delay_msecs = top.delay_secs * 1000; |
913 | tcsetattr(0, TCSANOW, &tc); | 613 | tcsetattr(0, TCSANOW, &tc); |
914 | /* trash return*/ | 614 | /* trash return*/ |
915 | getc(stdin); | 615 | getc(stdin); |
916 | 616 | ||
917 | do { | 617 | do { |
918 | print_sym_table(); | 618 | print_sym_table(session); |
919 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); | 619 | } while (!poll(&stdin_poll, 1, delay_msecs) == 1); |
920 | 620 | ||
921 | c = getc(stdin); | 621 | c = getc(stdin); |
@@ -930,6 +630,7 @@ repeat: | |||
930 | /* Tag samples to be skipped. */ | 630 | /* Tag samples to be skipped. */ |
931 | static const char *skip_symbols[] = { | 631 | static const char *skip_symbols[] = { |
932 | "default_idle", | 632 | "default_idle", |
633 | "native_safe_halt", | ||
933 | "cpu_idle", | 634 | "cpu_idle", |
934 | "enter_idle", | 635 | "enter_idle", |
935 | "exit_idle", | 636 | "exit_idle", |
@@ -965,9 +666,9 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
965 | 666 | ||
966 | syme = symbol__priv(sym); | 667 | syme = symbol__priv(sym); |
967 | syme->map = map; | 668 | syme->map = map; |
968 | syme->src = NULL; | 669 | symbol__annotate_init(map, sym); |
969 | 670 | ||
970 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { | 671 | if (!top.sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { |
971 | /* schedule initial sym_filter_entry setup */ | 672 | /* schedule initial sym_filter_entry setup */ |
972 | sym_filter_entry_sched = syme; | 673 | sym_filter_entry_sched = syme; |
973 | sym_filter = NULL; | 674 | sym_filter = NULL; |
@@ -975,49 +676,45 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
975 | 676 | ||
976 | for (i = 0; skip_symbols[i]; i++) { | 677 | for (i = 0; skip_symbols[i]; i++) { |
977 | if (!strcmp(skip_symbols[i], name)) { | 678 | if (!strcmp(skip_symbols[i], name)) { |
978 | syme->skip = 1; | 679 | sym->ignore = true; |
979 | break; | 680 | break; |
980 | } | 681 | } |
981 | } | 682 | } |
982 | 683 | ||
983 | if (!syme->skip) | ||
984 | syme->name_len = strlen(sym->name); | ||
985 | |||
986 | return 0; | 684 | return 0; |
987 | } | 685 | } |
988 | 686 | ||
989 | static void event__process_sample(const event_t *self, | 687 | static void perf_event__process_sample(const union perf_event *event, |
990 | struct sample_data *sample, | 688 | struct perf_sample *sample, |
991 | struct perf_session *session, | 689 | struct perf_session *session) |
992 | struct perf_evsel *evsel) | ||
993 | { | 690 | { |
994 | u64 ip = self->ip.ip; | 691 | u64 ip = event->ip.ip; |
995 | struct sym_entry *syme; | 692 | struct sym_entry *syme; |
996 | struct addr_location al; | 693 | struct addr_location al; |
997 | struct machine *machine; | 694 | struct machine *machine; |
998 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 695 | u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
999 | 696 | ||
1000 | ++samples; | 697 | ++top.samples; |
1001 | 698 | ||
1002 | switch (origin) { | 699 | switch (origin) { |
1003 | case PERF_RECORD_MISC_USER: | 700 | case PERF_RECORD_MISC_USER: |
1004 | ++us_samples; | 701 | ++top.us_samples; |
1005 | if (hide_user_symbols) | 702 | if (top.hide_user_symbols) |
1006 | return; | 703 | return; |
1007 | machine = perf_session__find_host_machine(session); | 704 | machine = perf_session__find_host_machine(session); |
1008 | break; | 705 | break; |
1009 | case PERF_RECORD_MISC_KERNEL: | 706 | case PERF_RECORD_MISC_KERNEL: |
1010 | ++kernel_samples; | 707 | ++top.kernel_samples; |
1011 | if (hide_kernel_symbols) | 708 | if (top.hide_kernel_symbols) |
1012 | return; | 709 | return; |
1013 | machine = perf_session__find_host_machine(session); | 710 | machine = perf_session__find_host_machine(session); |
1014 | break; | 711 | break; |
1015 | case PERF_RECORD_MISC_GUEST_KERNEL: | 712 | case PERF_RECORD_MISC_GUEST_KERNEL: |
1016 | ++guest_kernel_samples; | 713 | ++top.guest_kernel_samples; |
1017 | machine = perf_session__find_machine(session, self->ip.pid); | 714 | machine = perf_session__find_machine(session, event->ip.pid); |
1018 | break; | 715 | break; |
1019 | case PERF_RECORD_MISC_GUEST_USER: | 716 | case PERF_RECORD_MISC_GUEST_USER: |
1020 | ++guest_us_samples; | 717 | ++top.guest_us_samples; |
1021 | /* | 718 | /* |
1022 | * TODO: we don't process guest user from host side | 719 | * TODO: we don't process guest user from host side |
1023 | * except simple counting. | 720 | * except simple counting. |
@@ -1029,15 +726,15 @@ static void event__process_sample(const event_t *self, | |||
1029 | 726 | ||
1030 | if (!machine && perf_guest) { | 727 | if (!machine && perf_guest) { |
1031 | pr_err("Can't find guest [%d]'s kernel information\n", | 728 | pr_err("Can't find guest [%d]'s kernel information\n", |
1032 | self->ip.pid); | 729 | event->ip.pid); |
1033 | return; | 730 | return; |
1034 | } | 731 | } |
1035 | 732 | ||
1036 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) | 733 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
1037 | exact_samples++; | 734 | top.exact_samples++; |
1038 | 735 | ||
1039 | if (event__preprocess_sample(self, session, &al, sample, | 736 | if (perf_event__preprocess_sample(event, session, &al, sample, |
1040 | symbol_filter) < 0 || | 737 | symbol_filter) < 0 || |
1041 | al.filtered) | 738 | al.filtered) |
1042 | return; | 739 | return; |
1043 | 740 | ||
@@ -1055,8 +752,9 @@ static void event__process_sample(const event_t *self, | |||
1055 | */ | 752 | */ |
1056 | if (al.map == machine->vmlinux_maps[MAP__FUNCTION] && | 753 | if (al.map == machine->vmlinux_maps[MAP__FUNCTION] && |
1057 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | 754 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { |
1058 | pr_err("The %s file can't be used\n", | 755 | ui__warning("The %s file can't be used\n", |
1059 | symbol_conf.vmlinux_name); | 756 | symbol_conf.vmlinux_name); |
757 | exit_browser(0); | ||
1060 | exit(1); | 758 | exit(1); |
1061 | } | 759 | } |
1062 | 760 | ||
@@ -1065,13 +763,13 @@ static void event__process_sample(const event_t *self, | |||
1065 | 763 | ||
1066 | /* let's see, whether we need to install initial sym_filter_entry */ | 764 | /* let's see, whether we need to install initial sym_filter_entry */ |
1067 | if (sym_filter_entry_sched) { | 765 | if (sym_filter_entry_sched) { |
1068 | sym_filter_entry = sym_filter_entry_sched; | 766 | top.sym_filter_entry = sym_filter_entry_sched; |
1069 | sym_filter_entry_sched = NULL; | 767 | sym_filter_entry_sched = NULL; |
1070 | if (parse_source(sym_filter_entry) < 0) { | 768 | if (parse_source(top.sym_filter_entry) < 0) { |
1071 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); | 769 | struct symbol *sym = sym_entry__symbol(top.sym_filter_entry); |
1072 | 770 | ||
1073 | pr_err("Can't annotate %s", sym->name); | 771 | pr_err("Can't annotate %s", sym->name); |
1074 | if (sym_filter_entry->map->dso->origin == DSO__ORIG_KERNEL) { | 772 | if (top.sym_filter_entry->map->dso->symtab_type == SYMTAB__KALLSYMS) { |
1075 | pr_err(": No vmlinux file was found in the path:\n"); | 773 | pr_err(": No vmlinux file was found in the path:\n"); |
1076 | machine__fprintf_vmlinux_path(machine, stderr); | 774 | machine__fprintf_vmlinux_path(machine, stderr); |
1077 | } else | 775 | } else |
@@ -1081,167 +779,73 @@ static void event__process_sample(const event_t *self, | |||
1081 | } | 779 | } |
1082 | 780 | ||
1083 | syme = symbol__priv(al.sym); | 781 | syme = symbol__priv(al.sym); |
1084 | if (!syme->skip) { | 782 | if (!al.sym->ignore) { |
783 | struct perf_evsel *evsel; | ||
784 | |||
785 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | ||
786 | assert(evsel != NULL); | ||
1085 | syme->count[evsel->idx]++; | 787 | syme->count[evsel->idx]++; |
1086 | syme->origin = origin; | ||
1087 | record_precise_ip(syme, evsel->idx, ip); | 788 | record_precise_ip(syme, evsel->idx, ip); |
1088 | pthread_mutex_lock(&active_symbols_lock); | 789 | pthread_mutex_lock(&top.active_symbols_lock); |
1089 | if (list_empty(&syme->node) || !syme->node.next) | 790 | if (list_empty(&syme->node) || !syme->node.next) { |
791 | static bool first = true; | ||
1090 | __list_insert_active_sym(syme); | 792 | __list_insert_active_sym(syme); |
1091 | pthread_mutex_unlock(&active_symbols_lock); | 793 | if (first) { |
794 | pthread_cond_broadcast(&top.active_symbols_cond); | ||
795 | first = false; | ||
796 | } | ||
797 | } | ||
798 | pthread_mutex_unlock(&top.active_symbols_lock); | ||
1092 | } | 799 | } |
1093 | } | 800 | } |
1094 | 801 | ||
1095 | struct mmap_data { | 802 | static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) |
1096 | void *base; | ||
1097 | int mask; | ||
1098 | unsigned int prev; | ||
1099 | }; | ||
1100 | |||
1101 | static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel, | ||
1102 | int ncpus, int nthreads) | ||
1103 | { | ||
1104 | evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data)); | ||
1105 | return evsel->priv != NULL ? 0 : -ENOMEM; | ||
1106 | } | ||
1107 | |||
1108 | static void perf_evsel__free_mmap(struct perf_evsel *evsel) | ||
1109 | { | ||
1110 | xyarray__delete(evsel->priv); | ||
1111 | evsel->priv = NULL; | ||
1112 | } | ||
1113 | |||
1114 | static unsigned int mmap_read_head(struct mmap_data *md) | ||
1115 | { | ||
1116 | struct perf_event_mmap_page *pc = md->base; | ||
1117 | int head; | ||
1118 | |||
1119 | head = pc->data_head; | ||
1120 | rmb(); | ||
1121 | |||
1122 | return head; | ||
1123 | } | ||
1124 | |||
1125 | static void perf_session__mmap_read_counter(struct perf_session *self, | ||
1126 | struct perf_evsel *evsel, | ||
1127 | int cpu, int thread_idx) | ||
1128 | { | 803 | { |
1129 | struct xyarray *mmap_array = evsel->priv; | 804 | struct perf_sample sample; |
1130 | struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx); | 805 | union perf_event *event; |
1131 | unsigned int head = mmap_read_head(md); | ||
1132 | unsigned int old = md->prev; | ||
1133 | unsigned char *data = md->base + page_size; | ||
1134 | struct sample_data sample; | ||
1135 | int diff; | ||
1136 | |||
1137 | /* | ||
1138 | * If we're further behind than half the buffer, there's a chance | ||
1139 | * the writer will bite our tail and mess up the samples under us. | ||
1140 | * | ||
1141 | * If we somehow ended up ahead of the head, we got messed up. | ||
1142 | * | ||
1143 | * In either case, truncate and restart at head. | ||
1144 | */ | ||
1145 | diff = head - old; | ||
1146 | if (diff > md->mask / 2 || diff < 0) { | ||
1147 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
1148 | |||
1149 | /* | ||
1150 | * head points to a known good entry, start there. | ||
1151 | */ | ||
1152 | old = head; | ||
1153 | } | ||
1154 | |||
1155 | for (; old != head;) { | ||
1156 | event_t *event = (event_t *)&data[old & md->mask]; | ||
1157 | |||
1158 | event_t event_copy; | ||
1159 | 806 | ||
1160 | size_t size = event->header.size; | 807 | while ((event = perf_evlist__read_on_cpu(top.evlist, cpu)) != NULL) { |
808 | perf_session__parse_sample(self, event, &sample); | ||
1161 | 809 | ||
1162 | /* | ||
1163 | * Event straddles the mmap boundary -- header should always | ||
1164 | * be inside due to u64 alignment of output. | ||
1165 | */ | ||
1166 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
1167 | unsigned int offset = old; | ||
1168 | unsigned int len = min(sizeof(*event), size), cpy; | ||
1169 | void *dst = &event_copy; | ||
1170 | |||
1171 | do { | ||
1172 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
1173 | memcpy(dst, &data[offset & md->mask], cpy); | ||
1174 | offset += cpy; | ||
1175 | dst += cpy; | ||
1176 | len -= cpy; | ||
1177 | } while (len); | ||
1178 | |||
1179 | event = &event_copy; | ||
1180 | } | ||
1181 | |||
1182 | event__parse_sample(event, self, &sample); | ||
1183 | if (event->header.type == PERF_RECORD_SAMPLE) | 810 | if (event->header.type == PERF_RECORD_SAMPLE) |
1184 | event__process_sample(event, &sample, self, evsel); | 811 | perf_event__process_sample(event, &sample, self); |
1185 | else | 812 | else |
1186 | event__process(event, &sample, self); | 813 | perf_event__process(event, &sample, self); |
1187 | old += size; | ||
1188 | } | 814 | } |
1189 | |||
1190 | md->prev = old; | ||
1191 | } | 815 | } |
1192 | 816 | ||
1193 | static struct pollfd *event_array; | ||
1194 | |||
1195 | static void perf_session__mmap_read(struct perf_session *self) | 817 | static void perf_session__mmap_read(struct perf_session *self) |
1196 | { | 818 | { |
1197 | struct perf_evsel *counter; | 819 | int i; |
1198 | int i, thread_index; | ||
1199 | |||
1200 | for (i = 0; i < cpus->nr; i++) { | ||
1201 | list_for_each_entry(counter, &evsel_list, node) { | ||
1202 | for (thread_index = 0; | ||
1203 | thread_index < threads->nr; | ||
1204 | thread_index++) { | ||
1205 | perf_session__mmap_read_counter(self, | ||
1206 | counter, i, thread_index); | ||
1207 | } | ||
1208 | } | ||
1209 | } | ||
1210 | } | ||
1211 | 820 | ||
1212 | int nr_poll; | 821 | for (i = 0; i < top.evlist->cpus->nr; i++) |
1213 | int group_fd; | 822 | perf_session__mmap_read_cpu(self, i); |
823 | } | ||
1214 | 824 | ||
1215 | static void start_counter(int i, struct perf_evsel *evsel) | 825 | static void start_counters(struct perf_evlist *evlist) |
1216 | { | 826 | { |
1217 | struct xyarray *mmap_array = evsel->priv; | 827 | struct perf_evsel *counter; |
1218 | struct mmap_data *mm; | ||
1219 | struct perf_event_attr *attr; | ||
1220 | int cpu = -1; | ||
1221 | int thread_index; | ||
1222 | |||
1223 | if (target_tid == -1) | ||
1224 | cpu = cpus->map[i]; | ||
1225 | 828 | ||
1226 | attr = &evsel->attr; | 829 | list_for_each_entry(counter, &evlist->entries, node) { |
830 | struct perf_event_attr *attr = &counter->attr; | ||
1227 | 831 | ||
1228 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 832 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1229 | 833 | ||
1230 | if (freq) { | 834 | if (top.freq) { |
1231 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 835 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
1232 | attr->freq = 1; | 836 | attr->freq = 1; |
1233 | attr->sample_freq = freq; | 837 | attr->sample_freq = top.freq; |
1234 | } | 838 | } |
1235 | 839 | ||
1236 | attr->inherit = (cpu < 0) && inherit; | 840 | if (evlist->nr_entries > 1) { |
1237 | attr->mmap = 1; | 841 | attr->sample_type |= PERF_SAMPLE_ID; |
842 | attr->read_format |= PERF_FORMAT_ID; | ||
843 | } | ||
1238 | 844 | ||
1239 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 845 | attr->mmap = 1; |
1240 | try_again: | 846 | try_again: |
1241 | FD(evsel, i, thread_index) = sys_perf_event_open(attr, | 847 | if (perf_evsel__open(counter, top.evlist->cpus, |
1242 | threads->map[thread_index], cpu, group_fd, 0); | 848 | top.evlist->threads, group, inherit) < 0) { |
1243 | |||
1244 | if (FD(evsel, i, thread_index) < 0) { | ||
1245 | int err = errno; | 849 | int err = errno; |
1246 | 850 | ||
1247 | if (err == EPERM || err == EACCES) | 851 | if (err == EPERM || err == EACCES) |
@@ -1253,8 +857,8 @@ try_again: | |||
1253 | * based cpu-clock-tick sw counter, which | 857 | * based cpu-clock-tick sw counter, which |
1254 | * is always available even if no PMU support: | 858 | * is always available even if no PMU support: |
1255 | */ | 859 | */ |
1256 | if (attr->type == PERF_TYPE_HARDWARE | 860 | if (attr->type == PERF_TYPE_HARDWARE && |
1257 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 861 | attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
1258 | 862 | ||
1259 | if (verbose) | 863 | if (verbose) |
1260 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | 864 | warning(" ... trying to fall back to cpu-clock-ticks\n"); |
@@ -1264,39 +868,22 @@ try_again: | |||
1264 | goto try_again; | 868 | goto try_again; |
1265 | } | 869 | } |
1266 | printf("\n"); | 870 | printf("\n"); |
1267 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 871 | error("sys_perf_event_open() syscall returned with %d " |
1268 | FD(evsel, i, thread_index), strerror(err)); | 872 | "(%s). /bin/dmesg may provide additional information.\n", |
873 | err, strerror(err)); | ||
1269 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 874 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
1270 | exit(-1); | 875 | exit(-1); |
1271 | } | 876 | } |
1272 | assert(FD(evsel, i, thread_index) >= 0); | ||
1273 | fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK); | ||
1274 | |||
1275 | /* | ||
1276 | * First counter acts as the group leader: | ||
1277 | */ | ||
1278 | if (group && group_fd == -1) | ||
1279 | group_fd = FD(evsel, i, thread_index); | ||
1280 | |||
1281 | event_array[nr_poll].fd = FD(evsel, i, thread_index); | ||
1282 | event_array[nr_poll].events = POLLIN; | ||
1283 | nr_poll++; | ||
1284 | |||
1285 | mm = xyarray__entry(mmap_array, i, thread_index); | ||
1286 | mm->prev = 0; | ||
1287 | mm->mask = mmap_pages*page_size - 1; | ||
1288 | mm->base = mmap(NULL, (mmap_pages+1)*page_size, | ||
1289 | PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0); | ||
1290 | if (mm->base == MAP_FAILED) | ||
1291 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
1292 | } | 877 | } |
878 | |||
879 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) | ||
880 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
1293 | } | 881 | } |
1294 | 882 | ||
1295 | static int __cmd_top(void) | 883 | static int __cmd_top(void) |
1296 | { | 884 | { |
1297 | pthread_t thread; | 885 | pthread_t thread; |
1298 | struct perf_evsel *counter; | 886 | int ret __used; |
1299 | int i, ret; | ||
1300 | /* | 887 | /* |
1301 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 888 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
1302 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 889 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
@@ -1305,23 +892,23 @@ static int __cmd_top(void) | |||
1305 | if (session == NULL) | 892 | if (session == NULL) |
1306 | return -ENOMEM; | 893 | return -ENOMEM; |
1307 | 894 | ||
1308 | if (target_tid != -1) | 895 | if (top.target_tid != -1) |
1309 | event__synthesize_thread_map(threads, event__process, session); | 896 | perf_event__synthesize_thread_map(top.evlist->threads, |
897 | perf_event__process, session); | ||
1310 | else | 898 | else |
1311 | event__synthesize_threads(event__process, session); | 899 | perf_event__synthesize_threads(perf_event__process, session); |
1312 | 900 | ||
1313 | for (i = 0; i < cpus->nr; i++) { | 901 | start_counters(top.evlist); |
1314 | group_fd = -1; | 902 | session->evlist = top.evlist; |
1315 | list_for_each_entry(counter, &evsel_list, node) | 903 | perf_session__update_sample_type(session); |
1316 | start_counter(i, counter); | ||
1317 | } | ||
1318 | 904 | ||
1319 | /* Wait for a minimal set of events before starting the snapshot */ | 905 | /* Wait for a minimal set of events before starting the snapshot */ |
1320 | poll(&event_array[0], nr_poll, 100); | 906 | poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
1321 | 907 | ||
1322 | perf_session__mmap_read(session); | 908 | perf_session__mmap_read(session); |
1323 | 909 | ||
1324 | if (pthread_create(&thread, NULL, display_thread, session)) { | 910 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
911 | display_thread), session)) { | ||
1325 | printf("Could not create display thread.\n"); | 912 | printf("Could not create display thread.\n"); |
1326 | exit(-1); | 913 | exit(-1); |
1327 | } | 914 | } |
@@ -1337,12 +924,12 @@ static int __cmd_top(void) | |||
1337 | } | 924 | } |
1338 | 925 | ||
1339 | while (1) { | 926 | while (1) { |
1340 | int hits = samples; | 927 | u64 hits = top.samples; |
1341 | 928 | ||
1342 | perf_session__mmap_read(session); | 929 | perf_session__mmap_read(session); |
1343 | 930 | ||
1344 | if (hits == samples) | 931 | if (hits == top.samples) |
1345 | ret = poll(event_array, nr_poll, 100); | 932 | ret = poll(top.evlist->pollfd, top.evlist->nr_fds, 100); |
1346 | } | 933 | } |
1347 | 934 | ||
1348 | return 0; | 935 | return 0; |
@@ -1354,31 +941,31 @@ static const char * const top_usage[] = { | |||
1354 | }; | 941 | }; |
1355 | 942 | ||
1356 | static const struct option options[] = { | 943 | static const struct option options[] = { |
1357 | OPT_CALLBACK('e', "event", NULL, "event", | 944 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1358 | "event selector. use 'perf list' to list available events", | 945 | "event selector. use 'perf list' to list available events", |
1359 | parse_events), | 946 | parse_events), |
1360 | OPT_INTEGER('c', "count", &default_interval, | 947 | OPT_INTEGER('c', "count", &default_interval, |
1361 | "event period to sample"), | 948 | "event period to sample"), |
1362 | OPT_INTEGER('p', "pid", &target_pid, | 949 | OPT_INTEGER('p', "pid", &top.target_pid, |
1363 | "profile events on existing process id"), | 950 | "profile events on existing process id"), |
1364 | OPT_INTEGER('t', "tid", &target_tid, | 951 | OPT_INTEGER('t', "tid", &top.target_tid, |
1365 | "profile events on existing thread id"), | 952 | "profile events on existing thread id"), |
1366 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 953 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1367 | "system-wide collection from all CPUs"), | 954 | "system-wide collection from all CPUs"), |
1368 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | 955 | OPT_STRING('C', "cpu", &top.cpu_list, "cpu", |
1369 | "list of cpus to monitor"), | 956 | "list of cpus to monitor"), |
1370 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 957 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1371 | "file", "vmlinux pathname"), | 958 | "file", "vmlinux pathname"), |
1372 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | 959 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, |
1373 | "hide kernel symbols"), | 960 | "hide kernel symbols"), |
1374 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), | 961 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), |
1375 | OPT_INTEGER('r', "realtime", &realtime_prio, | 962 | OPT_INTEGER('r', "realtime", &realtime_prio, |
1376 | "collect data with this RT SCHED_FIFO priority"), | 963 | "collect data with this RT SCHED_FIFO priority"), |
1377 | OPT_INTEGER('d', "delay", &delay_secs, | 964 | OPT_INTEGER('d', "delay", &top.delay_secs, |
1378 | "number of seconds to delay between refreshes"), | 965 | "number of seconds to delay between refreshes"), |
1379 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, | 966 | OPT_BOOLEAN('D', "dump-symtab", &dump_symtab, |
1380 | "dump the symbol table used for profiling"), | 967 | "dump the symbol table used for profiling"), |
1381 | OPT_INTEGER('f', "count-filter", &count_filter, | 968 | OPT_INTEGER('f', "count-filter", &top.count_filter, |
1382 | "only display functions with more events than this"), | 969 | "only display functions with more events than this"), |
1383 | OPT_BOOLEAN('g', "group", &group, | 970 | OPT_BOOLEAN('g', "group", &group, |
1384 | "put the counters into a counter group"), | 971 | "put the counters into a counter group"), |
@@ -1386,14 +973,16 @@ static const struct option options[] = { | |||
1386 | "child tasks inherit counters"), | 973 | "child tasks inherit counters"), |
1387 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 974 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1388 | "symbol to annotate"), | 975 | "symbol to annotate"), |
1389 | OPT_BOOLEAN('z', "zero", &zero, | 976 | OPT_BOOLEAN('z', "zero", &top.zero, |
1390 | "zero history across updates"), | 977 | "zero history across updates"), |
1391 | OPT_INTEGER('F', "freq", &freq, | 978 | OPT_INTEGER('F', "freq", &top.freq, |
1392 | "profile at this frequency"), | 979 | "profile at this frequency"), |
1393 | OPT_INTEGER('E', "entries", &print_entries, | 980 | OPT_INTEGER('E', "entries", &top.print_entries, |
1394 | "display this many functions"), | 981 | "display this many functions"), |
1395 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | 982 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, |
1396 | "hide user symbols"), | 983 | "hide user symbols"), |
984 | OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), | ||
985 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | ||
1397 | OPT_INCR('v', "verbose", &verbose, | 986 | OPT_INCR('v', "verbose", &verbose, |
1398 | "be more verbose (show counter open errors, etc)"), | 987 | "be more verbose (show counter open errors, etc)"), |
1399 | OPT_END() | 988 | OPT_END() |
@@ -1404,64 +993,68 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1404 | struct perf_evsel *pos; | 993 | struct perf_evsel *pos; |
1405 | int status = -ENOMEM; | 994 | int status = -ENOMEM; |
1406 | 995 | ||
996 | top.evlist = perf_evlist__new(NULL, NULL); | ||
997 | if (top.evlist == NULL) | ||
998 | return -ENOMEM; | ||
999 | |||
1407 | page_size = sysconf(_SC_PAGE_SIZE); | 1000 | page_size = sysconf(_SC_PAGE_SIZE); |
1408 | 1001 | ||
1409 | argc = parse_options(argc, argv, options, top_usage, 0); | 1002 | argc = parse_options(argc, argv, options, top_usage, 0); |
1410 | if (argc) | 1003 | if (argc) |
1411 | usage_with_options(top_usage, options); | 1004 | usage_with_options(top_usage, options); |
1412 | 1005 | ||
1413 | if (target_pid != -1) | 1006 | /* |
1414 | target_tid = target_pid; | 1007 | * XXX For now start disabled, only using TUI if explicitely asked for. |
1008 | * Change that when handle_keys equivalent gets written, live annotation | ||
1009 | * done, etc. | ||
1010 | */ | ||
1011 | use_browser = 0; | ||
1415 | 1012 | ||
1416 | threads = thread_map__new(target_pid, target_tid); | 1013 | if (use_stdio) |
1417 | if (threads == NULL) { | 1014 | use_browser = 0; |
1418 | pr_err("Problems finding threads of monitor\n"); | 1015 | else if (use_tui) |
1419 | usage_with_options(top_usage, options); | 1016 | use_browser = 1; |
1420 | } | ||
1421 | 1017 | ||
1422 | event_array = malloc((sizeof(struct pollfd) * | 1018 | setup_browser(false); |
1423 | MAX_NR_CPUS * MAX_COUNTERS * threads->nr)); | ||
1424 | if (!event_array) | ||
1425 | return -ENOMEM; | ||
1426 | 1019 | ||
1427 | /* CPU and PID are mutually exclusive */ | 1020 | /* CPU and PID are mutually exclusive */ |
1428 | if (target_tid > 0 && cpu_list) { | 1021 | if (top.target_tid > 0 && top.cpu_list) { |
1429 | printf("WARNING: PID switch overriding CPU\n"); | 1022 | printf("WARNING: PID switch overriding CPU\n"); |
1430 | sleep(1); | 1023 | sleep(1); |
1431 | cpu_list = NULL; | 1024 | top.cpu_list = NULL; |
1432 | } | 1025 | } |
1433 | 1026 | ||
1434 | if (!nr_counters && perf_evsel_list__create_default() < 0) { | 1027 | if (top.target_pid != -1) |
1028 | top.target_tid = top.target_pid; | ||
1029 | |||
1030 | if (perf_evlist__create_maps(top.evlist, top.target_pid, | ||
1031 | top.target_tid, top.cpu_list) < 0) | ||
1032 | usage_with_options(top_usage, options); | ||
1033 | |||
1034 | if (!top.evlist->nr_entries && | ||
1035 | perf_evlist__add_default(top.evlist) < 0) { | ||
1435 | pr_err("Not enough memory for event selector list\n"); | 1036 | pr_err("Not enough memory for event selector list\n"); |
1436 | return -ENOMEM; | 1037 | return -ENOMEM; |
1437 | } | 1038 | } |
1438 | 1039 | ||
1439 | if (delay_secs < 1) | 1040 | if (top.delay_secs < 1) |
1440 | delay_secs = 1; | 1041 | top.delay_secs = 1; |
1441 | 1042 | ||
1442 | /* | 1043 | /* |
1443 | * User specified count overrides default frequency. | 1044 | * User specified count overrides default frequency. |
1444 | */ | 1045 | */ |
1445 | if (default_interval) | 1046 | if (default_interval) |
1446 | freq = 0; | 1047 | top.freq = 0; |
1447 | else if (freq) { | 1048 | else if (top.freq) { |
1448 | default_interval = freq; | 1049 | default_interval = top.freq; |
1449 | } else { | 1050 | } else { |
1450 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 1051 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
1451 | exit(EXIT_FAILURE); | 1052 | exit(EXIT_FAILURE); |
1452 | } | 1053 | } |
1453 | 1054 | ||
1454 | if (target_tid != -1) | 1055 | list_for_each_entry(pos, &top.evlist->entries, node) { |
1455 | cpus = cpu_map__dummy_new(); | 1056 | if (perf_evsel__alloc_fd(pos, top.evlist->cpus->nr, |
1456 | else | 1057 | top.evlist->threads->nr) < 0) |
1457 | cpus = cpu_map__new(cpu_list); | ||
1458 | |||
1459 | if (cpus == NULL) | ||
1460 | usage_with_options(top_usage, options); | ||
1461 | |||
1462 | list_for_each_entry(pos, &evsel_list, node) { | ||
1463 | if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || | ||
1464 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
1465 | goto out_free_fd; | 1058 | goto out_free_fd; |
1466 | /* | 1059 | /* |
1467 | * Fill in the ones not specifically initialized via -c: | 1060 | * Fill in the ones not specifically initialized via -c: |
@@ -1472,26 +1065,28 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1472 | pos->attr.sample_period = default_interval; | 1065 | pos->attr.sample_period = default_interval; |
1473 | } | 1066 | } |
1474 | 1067 | ||
1475 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 1068 | if (perf_evlist__alloc_pollfd(top.evlist) < 0 || |
1069 | perf_evlist__alloc_mmap(top.evlist) < 0) | ||
1070 | goto out_free_fd; | ||
1071 | |||
1072 | top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node); | ||
1476 | 1073 | ||
1477 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1074 | symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) + |
1478 | (nr_counters + 1) * sizeof(unsigned long)); | 1075 | (top.evlist->nr_entries + 1) * sizeof(unsigned long)); |
1479 | 1076 | ||
1480 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1077 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1481 | if (symbol__init() < 0) | 1078 | if (symbol__init() < 0) |
1482 | return -1; | 1079 | return -1; |
1483 | 1080 | ||
1484 | get_term_dimensions(&winsize); | 1081 | get_term_dimensions(&winsize); |
1485 | if (print_entries == 0) { | 1082 | if (top.print_entries == 0) { |
1486 | update_print_entries(&winsize); | 1083 | update_print_entries(&winsize); |
1487 | signal(SIGWINCH, sig_winch_handler); | 1084 | signal(SIGWINCH, sig_winch_handler); |
1488 | } | 1085 | } |
1489 | 1086 | ||
1490 | status = __cmd_top(); | 1087 | status = __cmd_top(); |
1491 | out_free_fd: | 1088 | out_free_fd: |
1492 | list_for_each_entry(pos, &evsel_list, node) | 1089 | perf_evlist__delete(top.evlist); |
1493 | perf_evsel__free_mmap(pos); | ||
1494 | perf_evsel_list__delete(); | ||
1495 | 1090 | ||
1496 | return status; | 1091 | return status; |
1497 | } | 1092 | } |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index c7798c7f24ed..4702e2443a8e 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -19,6 +19,7 @@ extern int cmd_bench(int argc, const char **argv, const char *prefix); | |||
19 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); | 19 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); |
20 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); | 20 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); |
21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); | 21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); |
22 | extern int cmd_evlist(int argc, const char **argv, const char *prefix); | ||
22 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 23 | extern int cmd_help(int argc, const char **argv, const char *prefix); |
23 | extern int cmd_sched(int argc, const char **argv, const char *prefix); | 24 | extern int cmd_sched(int argc, const char **argv, const char *prefix); |
24 | extern int cmd_list(int argc, const char **argv, const char *prefix); | 25 | extern int cmd_list(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 16b5088cf8f4..d695fe40fbff 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -8,6 +8,7 @@ perf-bench mainporcelain common | |||
8 | perf-buildid-cache mainporcelain common | 8 | perf-buildid-cache mainporcelain common |
9 | perf-buildid-list mainporcelain common | 9 | perf-buildid-list mainporcelain common |
10 | perf-diff mainporcelain common | 10 | perf-diff mainporcelain common |
11 | perf-evlist mainporcelain common | ||
11 | perf-inject mainporcelain common | 12 | perf-inject mainporcelain common |
12 | perf-list mainporcelain common | 13 | perf-list mainporcelain common |
13 | perf-sched mainporcelain common | 14 | perf-sched mainporcelain common |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 595d0f4a7103..ec635b7cc8ea 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -313,6 +313,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
313 | { "buildid-cache", cmd_buildid_cache, 0 }, | 313 | { "buildid-cache", cmd_buildid_cache, 0 }, |
314 | { "buildid-list", cmd_buildid_list, 0 }, | 314 | { "buildid-list", cmd_buildid_list, 0 }, |
315 | { "diff", cmd_diff, 0 }, | 315 | { "diff", cmd_diff, 0 }, |
316 | { "evlist", cmd_evlist, 0 }, | ||
316 | { "help", cmd_help, 0 }, | 317 | { "help", cmd_help, 0 }, |
317 | { "list", cmd_list, 0 }, | 318 | { "list", cmd_list, 0 }, |
318 | { "record", cmd_record, 0 }, | 319 | { "record", cmd_record, 0 }, |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 95aaf565c704..a5fc660c1f12 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws); | |||
94 | #include "util/types.h" | 94 | #include "util/types.h" |
95 | #include <stdbool.h> | 95 | #include <stdbool.h> |
96 | 96 | ||
97 | struct perf_mmap { | ||
98 | void *base; | ||
99 | int mask; | ||
100 | unsigned int prev; | ||
101 | }; | ||
102 | |||
103 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
104 | { | ||
105 | struct perf_event_mmap_page *pc = mm->base; | ||
106 | int head = pc->data_head; | ||
107 | rmb(); | ||
108 | return head; | ||
109 | } | ||
110 | |||
111 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
112 | unsigned long tail) | ||
113 | { | ||
114 | struct perf_event_mmap_page *pc = md->base; | ||
115 | |||
116 | /* | ||
117 | * ensure all reads are done before we write the tail out. | ||
118 | */ | ||
119 | /* mb(); */ | ||
120 | pc->data_tail = tail; | ||
121 | } | ||
122 | |||
97 | /* | 123 | /* |
98 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 124 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all |
99 | * counters in the current task. | 125 | * counters in the current task. |
diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py new file mode 100755 index 000000000000..df638c438a9f --- /dev/null +++ b/tools/perf/python/twatch.py | |||
@@ -0,0 +1,41 @@ | |||
1 | #! /usr/bin/python | ||
2 | # -*- python -*- | ||
3 | # -*- coding: utf-8 -*- | ||
4 | # twatch - Experimental use of the perf python interface | ||
5 | # Copyright (C) 2011 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
6 | # | ||
7 | # This application is free software; you can redistribute it and/or | ||
8 | # modify it under the terms of the GNU General Public License | ||
9 | # as published by the Free Software Foundation; version 2. | ||
10 | # | ||
11 | # This application is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | # General Public License for more details. | ||
15 | |||
16 | import perf | ||
17 | |||
18 | def main(): | ||
19 | cpus = perf.cpu_map() | ||
20 | threads = perf.thread_map() | ||
21 | evsel = perf.evsel(task = 1, comm = 1, mmap = 0, | ||
22 | wakeup_events = 1, sample_period = 1, | ||
23 | sample_id_all = 1, | ||
24 | sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID) | ||
25 | evsel.open(cpus = cpus, threads = threads); | ||
26 | evlist = perf.evlist(cpus, threads) | ||
27 | evlist.add(evsel) | ||
28 | evlist.mmap() | ||
29 | while True: | ||
30 | evlist.poll(timeout = -1) | ||
31 | for cpu in cpus: | ||
32 | event = evlist.read_on_cpu(cpu) | ||
33 | if not event: | ||
34 | continue | ||
35 | print "cpu: %2d, pid: %4d, tid: %4d" % (event.sample_cpu, | ||
36 | event.sample_pid, | ||
37 | event.sample_tid), | ||
38 | print event | ||
39 | |||
40 | if __name__ == '__main__': | ||
41 | main() | ||
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 97d76562a1a0..26d4d3fd6deb 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -23,10 +23,10 @@ if test -d ../../.git -o -f ../../.git && | |||
23 | then | 23 | then |
24 | VN=$(echo "$VN" | sed -e 's/-/./g'); | 24 | VN=$(echo "$VN" | sed -e 's/-/./g'); |
25 | else | 25 | else |
26 | eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '` | 26 | eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ') |
27 | eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '` | 27 | eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') |
28 | eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '` | 28 | eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') |
29 | eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '` | 29 | eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ') |
30 | 30 | ||
31 | VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" | 31 | VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" |
32 | fi | 32 | fi |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c new file mode 100644 index 000000000000..e01af2b1a469 --- /dev/null +++ b/tools/perf/util/annotate.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-annotate.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | |||
10 | #include "util.h" | ||
11 | #include "build-id.h" | ||
12 | #include "color.h" | ||
13 | #include "cache.h" | ||
14 | #include "symbol.h" | ||
15 | #include "debug.h" | ||
16 | #include "annotate.h" | ||
17 | #include <pthread.h> | ||
18 | |||
19 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | ||
20 | { | ||
21 | struct annotation *notes = symbol__annotation(sym); | ||
22 | pthread_mutex_init(¬es->lock, NULL); | ||
23 | return 0; | ||
24 | } | ||
25 | |||
26 | int symbol__alloc_hist(struct symbol *sym, int nevents) | ||
27 | { | ||
28 | struct annotation *notes = symbol__annotation(sym); | ||
29 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + | ||
30 | (sym->end - sym->start) * sizeof(u64)); | ||
31 | |||
32 | notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist); | ||
33 | if (notes->src == NULL) | ||
34 | return -1; | ||
35 | notes->src->sizeof_sym_hist = sizeof_sym_hist; | ||
36 | notes->src->nr_histograms = nevents; | ||
37 | INIT_LIST_HEAD(¬es->src->source); | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | void symbol__annotate_zero_histograms(struct symbol *sym) | ||
42 | { | ||
43 | struct annotation *notes = symbol__annotation(sym); | ||
44 | |||
45 | pthread_mutex_lock(¬es->lock); | ||
46 | if (notes->src != NULL) | ||
47 | memset(notes->src->histograms, 0, | ||
48 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); | ||
49 | pthread_mutex_unlock(¬es->lock); | ||
50 | } | ||
51 | |||
52 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||
53 | int evidx, u64 addr) | ||
54 | { | ||
55 | unsigned offset; | ||
56 | struct annotation *notes; | ||
57 | struct sym_hist *h; | ||
58 | |||
59 | notes = symbol__annotation(sym); | ||
60 | if (notes->src == NULL) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | ||
64 | |||
65 | if (addr >= sym->end) | ||
66 | return 0; | ||
67 | |||
68 | offset = addr - sym->start; | ||
69 | h = annotation__histogram(notes, evidx); | ||
70 | h->sum++; | ||
71 | h->addr[offset]++; | ||
72 | |||
73 | pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 | ||
74 | ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, | ||
75 | addr, addr - sym->start, evidx, h->addr[offset]); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
80 | { | ||
81 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
82 | |||
83 | if (self != NULL) { | ||
84 | self->offset = offset; | ||
85 | self->line = line; | ||
86 | } | ||
87 | |||
88 | return self; | ||
89 | } | ||
90 | |||
91 | void objdump_line__free(struct objdump_line *self) | ||
92 | { | ||
93 | free(self->line); | ||
94 | free(self); | ||
95 | } | ||
96 | |||
97 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
98 | { | ||
99 | list_add_tail(&line->node, head); | ||
100 | } | ||
101 | |||
102 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
103 | struct objdump_line *pos) | ||
104 | { | ||
105 | list_for_each_entry_continue(pos, head, node) | ||
106 | if (pos->offset >= 0) | ||
107 | return pos; | ||
108 | |||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, | ||
113 | int evidx, u64 len, int min_pcnt, | ||
114 | int printed, int max_lines, | ||
115 | struct objdump_line *queue) | ||
116 | { | ||
117 | static const char *prev_line; | ||
118 | static const char *prev_color; | ||
119 | |||
120 | if (oline->offset != -1) { | ||
121 | const char *path = NULL; | ||
122 | unsigned int hits = 0; | ||
123 | double percent = 0.0; | ||
124 | const char *color; | ||
125 | struct annotation *notes = symbol__annotation(sym); | ||
126 | struct source_line *src_line = notes->src->lines; | ||
127 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
128 | s64 offset = oline->offset; | ||
129 | struct objdump_line *next; | ||
130 | |||
131 | next = objdump__get_next_ip_line(¬es->src->source, oline); | ||
132 | |||
133 | while (offset < (s64)len && | ||
134 | (next == NULL || offset < next->offset)) { | ||
135 | if (src_line) { | ||
136 | if (path == NULL) | ||
137 | path = src_line[offset].path; | ||
138 | percent += src_line[offset].percent; | ||
139 | } else | ||
140 | hits += h->addr[offset]; | ||
141 | |||
142 | ++offset; | ||
143 | } | ||
144 | |||
145 | if (src_line == NULL && h->sum) | ||
146 | percent = 100.0 * hits / h->sum; | ||
147 | |||
148 | if (percent < min_pcnt) | ||
149 | return -1; | ||
150 | |||
151 | if (max_lines && printed >= max_lines) | ||
152 | return 1; | ||
153 | |||
154 | if (queue != NULL) { | ||
155 | list_for_each_entry_from(queue, ¬es->src->source, node) { | ||
156 | if (queue == oline) | ||
157 | break; | ||
158 | objdump_line__print(queue, sym, evidx, len, | ||
159 | 0, 0, 1, NULL); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | color = get_percent_color(percent); | ||
164 | |||
165 | /* | ||
166 | * Also color the filename and line if needed, with | ||
167 | * the same color than the percentage. Don't print it | ||
168 | * twice for close colored addr with the same filename:line | ||
169 | */ | ||
170 | if (path) { | ||
171 | if (!prev_line || strcmp(prev_line, path) | ||
172 | || color != prev_color) { | ||
173 | color_fprintf(stdout, color, " %s", path); | ||
174 | prev_line = path; | ||
175 | prev_color = color; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | color_fprintf(stdout, color, " %7.2f", percent); | ||
180 | printf(" : "); | ||
181 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); | ||
182 | } else if (max_lines && printed >= max_lines) | ||
183 | return 1; | ||
184 | else { | ||
185 | if (queue) | ||
186 | return -1; | ||
187 | |||
188 | if (!*oline->line) | ||
189 | printf(" :\n"); | ||
190 | else | ||
191 | printf(" : %s\n", oline->line); | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | ||
198 | FILE *file, size_t privsize) | ||
199 | { | ||
200 | struct annotation *notes = symbol__annotation(sym); | ||
201 | struct objdump_line *objdump_line; | ||
202 | char *line = NULL, *tmp, *tmp2, *c; | ||
203 | size_t line_len; | ||
204 | s64 line_ip, offset = -1; | ||
205 | |||
206 | if (getline(&line, &line_len, file) < 0) | ||
207 | return -1; | ||
208 | |||
209 | if (!line) | ||
210 | return -1; | ||
211 | |||
212 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
213 | line[--line_len] = '\0'; | ||
214 | |||
215 | c = strchr(line, '\n'); | ||
216 | if (c) | ||
217 | *c = 0; | ||
218 | |||
219 | line_ip = -1; | ||
220 | |||
221 | /* | ||
222 | * Strip leading spaces: | ||
223 | */ | ||
224 | tmp = line; | ||
225 | while (*tmp) { | ||
226 | if (*tmp != ' ') | ||
227 | break; | ||
228 | tmp++; | ||
229 | } | ||
230 | |||
231 | if (*tmp) { | ||
232 | /* | ||
233 | * Parse hexa addresses followed by ':' | ||
234 | */ | ||
235 | line_ip = strtoull(tmp, &tmp2, 16); | ||
236 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
237 | line_ip = -1; | ||
238 | } | ||
239 | |||
240 | if (line_ip != -1) { | ||
241 | u64 start = map__rip_2objdump(map, sym->start), | ||
242 | end = map__rip_2objdump(map, sym->end); | ||
243 | |||
244 | offset = line_ip - start; | ||
245 | if (offset < 0 || (u64)line_ip > end) | ||
246 | offset = -1; | ||
247 | } | ||
248 | |||
249 | objdump_line = objdump_line__new(offset, line, privsize); | ||
250 | if (objdump_line == NULL) { | ||
251 | free(line); | ||
252 | return -1; | ||
253 | } | ||
254 | objdump__add_line(¬es->src->source, objdump_line); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | ||
260 | { | ||
261 | struct dso *dso = map->dso; | ||
262 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
263 | bool free_filename = true; | ||
264 | char command[PATH_MAX * 2]; | ||
265 | FILE *file; | ||
266 | int err = 0; | ||
267 | char symfs_filename[PATH_MAX]; | ||
268 | |||
269 | if (filename) { | ||
270 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
271 | symbol_conf.symfs, filename); | ||
272 | } | ||
273 | |||
274 | if (filename == NULL) { | ||
275 | if (dso->has_build_id) { | ||
276 | pr_err("Can't annotate %s: not enough memory\n", | ||
277 | sym->name); | ||
278 | return -ENOMEM; | ||
279 | } | ||
280 | goto fallback; | ||
281 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
282 | strstr(command, "[kernel.kallsyms]") || | ||
283 | access(symfs_filename, R_OK)) { | ||
284 | free(filename); | ||
285 | fallback: | ||
286 | /* | ||
287 | * If we don't have build-ids or the build-id file isn't in the | ||
288 | * cache, or is just a kallsyms file, well, lets hope that this | ||
289 | * DSO is the same as when 'perf record' ran. | ||
290 | */ | ||
291 | filename = dso->long_name; | ||
292 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
293 | symbol_conf.symfs, filename); | ||
294 | free_filename = false; | ||
295 | } | ||
296 | |||
297 | if (dso->symtab_type == SYMTAB__KALLSYMS) { | ||
298 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; | ||
299 | char *build_id_msg = NULL; | ||
300 | |||
301 | if (dso->annotate_warned) | ||
302 | goto out_free_filename; | ||
303 | |||
304 | if (dso->has_build_id) { | ||
305 | build_id__sprintf(dso->build_id, | ||
306 | sizeof(dso->build_id), bf + 15); | ||
307 | build_id_msg = bf; | ||
308 | } | ||
309 | err = -ENOENT; | ||
310 | dso->annotate_warned = 1; | ||
311 | pr_err("Can't annotate %s: No vmlinux file%s was found in the " | ||
312 | "path.\nPlease use 'perf buildid-cache -av vmlinux' or " | ||
313 | "--vmlinux vmlinux.\n", | ||
314 | sym->name, build_id_msg ?: ""); | ||
315 | goto out_free_filename; | ||
316 | } | ||
317 | |||
318 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | ||
319 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
320 | map->unmap_ip(map, sym->end)); | ||
321 | |||
322 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
323 | dso, dso->long_name, sym, sym->name); | ||
324 | |||
325 | snprintf(command, sizeof(command), | ||
326 | "objdump --start-address=0x%016" PRIx64 | ||
327 | " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | ||
328 | map__rip_2objdump(map, sym->start), | ||
329 | map__rip_2objdump(map, sym->end), | ||
330 | symfs_filename, filename); | ||
331 | |||
332 | pr_debug("Executing: %s\n", command); | ||
333 | |||
334 | file = popen(command, "r"); | ||
335 | if (!file) | ||
336 | goto out_free_filename; | ||
337 | |||
338 | while (!feof(file)) | ||
339 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) | ||
340 | break; | ||
341 | |||
342 | pclose(file); | ||
343 | out_free_filename: | ||
344 | if (free_filename) | ||
345 | free(filename); | ||
346 | return err; | ||
347 | } | ||
348 | |||
349 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) | ||
350 | { | ||
351 | struct source_line *iter; | ||
352 | struct rb_node **p = &root->rb_node; | ||
353 | struct rb_node *parent = NULL; | ||
354 | |||
355 | while (*p != NULL) { | ||
356 | parent = *p; | ||
357 | iter = rb_entry(parent, struct source_line, node); | ||
358 | |||
359 | if (src_line->percent > iter->percent) | ||
360 | p = &(*p)->rb_left; | ||
361 | else | ||
362 | p = &(*p)->rb_right; | ||
363 | } | ||
364 | |||
365 | rb_link_node(&src_line->node, parent, p); | ||
366 | rb_insert_color(&src_line->node, root); | ||
367 | } | ||
368 | |||
369 | static void symbol__free_source_line(struct symbol *sym, int len) | ||
370 | { | ||
371 | struct annotation *notes = symbol__annotation(sym); | ||
372 | struct source_line *src_line = notes->src->lines; | ||
373 | int i; | ||
374 | |||
375 | for (i = 0; i < len; i++) | ||
376 | free(src_line[i].path); | ||
377 | |||
378 | free(src_line); | ||
379 | notes->src->lines = NULL; | ||
380 | } | ||
381 | |||
382 | /* Get the filename:line for the colored entries */ | ||
383 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | ||
384 | int evidx, struct rb_root *root, int len, | ||
385 | const char *filename) | ||
386 | { | ||
387 | u64 start; | ||
388 | int i; | ||
389 | char cmd[PATH_MAX * 2]; | ||
390 | struct source_line *src_line; | ||
391 | struct annotation *notes = symbol__annotation(sym); | ||
392 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
393 | |||
394 | if (!h->sum) | ||
395 | return 0; | ||
396 | |||
397 | src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); | ||
398 | if (!notes->src->lines) | ||
399 | return -1; | ||
400 | |||
401 | start = map->unmap_ip(map, sym->start); | ||
402 | |||
403 | for (i = 0; i < len; i++) { | ||
404 | char *path = NULL; | ||
405 | size_t line_len; | ||
406 | u64 offset; | ||
407 | FILE *fp; | ||
408 | |||
409 | src_line[i].percent = 100.0 * h->addr[i] / h->sum; | ||
410 | if (src_line[i].percent <= 0.5) | ||
411 | continue; | ||
412 | |||
413 | offset = start + i; | ||
414 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | ||
415 | fp = popen(cmd, "r"); | ||
416 | if (!fp) | ||
417 | continue; | ||
418 | |||
419 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
420 | goto next; | ||
421 | |||
422 | src_line[i].path = malloc(sizeof(char) * line_len + 1); | ||
423 | if (!src_line[i].path) | ||
424 | goto next; | ||
425 | |||
426 | strcpy(src_line[i].path, path); | ||
427 | insert_source_line(root, &src_line[i]); | ||
428 | |||
429 | next: | ||
430 | pclose(fp); | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static void print_summary(struct rb_root *root, const char *filename) | ||
437 | { | ||
438 | struct source_line *src_line; | ||
439 | struct rb_node *node; | ||
440 | |||
441 | printf("\nSorted summary for file %s\n", filename); | ||
442 | printf("----------------------------------------------\n\n"); | ||
443 | |||
444 | if (RB_EMPTY_ROOT(root)) { | ||
445 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | node = rb_first(root); | ||
450 | while (node) { | ||
451 | double percent; | ||
452 | const char *color; | ||
453 | char *path; | ||
454 | |||
455 | src_line = rb_entry(node, struct source_line, node); | ||
456 | percent = src_line->percent; | ||
457 | color = get_percent_color(percent); | ||
458 | path = src_line->path; | ||
459 | |||
460 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
461 | node = rb_next(node); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static void symbol__annotate_hits(struct symbol *sym, int evidx) | ||
466 | { | ||
467 | struct annotation *notes = symbol__annotation(sym); | ||
468 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
469 | u64 len = sym->end - sym->start, offset; | ||
470 | |||
471 | for (offset = 0; offset < len; ++offset) | ||
472 | if (h->addr[offset] != 0) | ||
473 | printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | ||
474 | sym->start + offset, h->addr[offset]); | ||
475 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||
476 | } | ||
477 | |||
478 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | ||
479 | bool full_paths, int min_pcnt, int max_lines, | ||
480 | int context) | ||
481 | { | ||
482 | struct dso *dso = map->dso; | ||
483 | const char *filename = dso->long_name, *d_filename; | ||
484 | struct annotation *notes = symbol__annotation(sym); | ||
485 | struct objdump_line *pos, *queue = NULL; | ||
486 | int printed = 2, queue_len = 0; | ||
487 | int more = 0; | ||
488 | u64 len; | ||
489 | |||
490 | if (full_paths) | ||
491 | d_filename = filename; | ||
492 | else | ||
493 | d_filename = basename(filename); | ||
494 | |||
495 | len = sym->end - sym->start; | ||
496 | |||
497 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | ||
498 | printf("------------------------------------------------\n"); | ||
499 | |||
500 | if (verbose) | ||
501 | symbol__annotate_hits(sym, evidx); | ||
502 | |||
503 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
504 | if (context && queue == NULL) { | ||
505 | queue = pos; | ||
506 | queue_len = 0; | ||
507 | } | ||
508 | |||
509 | switch (objdump_line__print(pos, sym, evidx, len, min_pcnt, | ||
510 | printed, max_lines, queue)) { | ||
511 | case 0: | ||
512 | ++printed; | ||
513 | if (context) { | ||
514 | printed += queue_len; | ||
515 | queue = NULL; | ||
516 | queue_len = 0; | ||
517 | } | ||
518 | break; | ||
519 | case 1: | ||
520 | /* filtered by max_lines */ | ||
521 | ++more; | ||
522 | break; | ||
523 | case -1: | ||
524 | default: | ||
525 | /* | ||
526 | * Filtered by min_pcnt or non IP lines when | ||
527 | * context != 0 | ||
528 | */ | ||
529 | if (!context) | ||
530 | break; | ||
531 | if (queue_len == context) | ||
532 | queue = list_entry(queue->node.next, typeof(*queue), node); | ||
533 | else | ||
534 | ++queue_len; | ||
535 | break; | ||
536 | } | ||
537 | } | ||
538 | |||
539 | return more; | ||
540 | } | ||
541 | |||
542 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | ||
543 | { | ||
544 | struct annotation *notes = symbol__annotation(sym); | ||
545 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
546 | |||
547 | memset(h, 0, notes->src->sizeof_sym_hist); | ||
548 | } | ||
549 | |||
550 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) | ||
551 | { | ||
552 | struct annotation *notes = symbol__annotation(sym); | ||
553 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
554 | struct objdump_line *pos; | ||
555 | int len = sym->end - sym->start; | ||
556 | |||
557 | h->sum = 0; | ||
558 | |||
559 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
560 | if (pos->offset != -1 && pos->offset < len) { | ||
561 | h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; | ||
562 | h->sum += h->addr[pos->offset]; | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | |||
567 | void objdump_line_list__purge(struct list_head *head) | ||
568 | { | ||
569 | struct objdump_line *pos, *n; | ||
570 | |||
571 | list_for_each_entry_safe(pos, n, head, node) { | ||
572 | list_del(&pos->node); | ||
573 | objdump_line__free(pos); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||
578 | bool print_lines, bool full_paths, int min_pcnt, | ||
579 | int max_lines) | ||
580 | { | ||
581 | struct dso *dso = map->dso; | ||
582 | const char *filename = dso->long_name; | ||
583 | struct rb_root source_line = RB_ROOT; | ||
584 | u64 len; | ||
585 | |||
586 | if (symbol__annotate(sym, map, 0) < 0) | ||
587 | return -1; | ||
588 | |||
589 | len = sym->end - sym->start; | ||
590 | |||
591 | if (print_lines) { | ||
592 | symbol__get_source_line(sym, map, evidx, &source_line, | ||
593 | len, filename); | ||
594 | print_summary(&source_line, filename); | ||
595 | } | ||
596 | |||
597 | symbol__annotate_printf(sym, map, evidx, full_paths, | ||
598 | min_pcnt, max_lines, 0); | ||
599 | if (print_lines) | ||
600 | symbol__free_source_line(sym, len); | ||
601 | |||
602 | objdump_line_list__purge(&symbol__annotation(sym)->src->source); | ||
603 | |||
604 | return 0; | ||
605 | } | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h new file mode 100644 index 000000000000..c2c286896801 --- /dev/null +++ b/tools/perf/util/annotate.h | |||
@@ -0,0 +1,103 @@ | |||
1 | #ifndef __PERF_ANNOTATE_H | ||
2 | #define __PERF_ANNOTATE_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "types.h" | ||
6 | #include "symbol.h" | ||
7 | #include <linux/list.h> | ||
8 | #include <linux/rbtree.h> | ||
9 | |||
10 | struct objdump_line { | ||
11 | struct list_head node; | ||
12 | s64 offset; | ||
13 | char *line; | ||
14 | }; | ||
15 | |||
16 | void objdump_line__free(struct objdump_line *self); | ||
17 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
18 | struct objdump_line *pos); | ||
19 | |||
20 | struct sym_hist { | ||
21 | u64 sum; | ||
22 | u64 addr[0]; | ||
23 | }; | ||
24 | |||
25 | struct source_line { | ||
26 | struct rb_node node; | ||
27 | double percent; | ||
28 | char *path; | ||
29 | }; | ||
30 | |||
31 | /** struct annotated_source - symbols with hits have this attached as in sannotation | ||
32 | * | ||
33 | * @histogram: Array of addr hit histograms per event being monitored | ||
34 | * @lines: If 'print_lines' is specified, per source code line percentages | ||
35 | * @source: source parsed from objdump -dS | ||
36 | * | ||
37 | * lines is allocated, percentages calculated and all sorted by percentage | ||
38 | * when the annotation is about to be presented, so the percentages are for | ||
39 | * one of the entries in the histogram array, i.e. for the event/counter being | ||
40 | * presented. It is deallocated right after symbol__{tui,tty,etc}_annotate | ||
41 | * returns. | ||
42 | */ | ||
43 | struct annotated_source { | ||
44 | struct list_head source; | ||
45 | struct source_line *lines; | ||
46 | int nr_histograms; | ||
47 | int sizeof_sym_hist; | ||
48 | struct sym_hist histograms[0]; | ||
49 | }; | ||
50 | |||
51 | struct annotation { | ||
52 | pthread_mutex_t lock; | ||
53 | struct annotated_source *src; | ||
54 | }; | ||
55 | |||
56 | struct sannotation { | ||
57 | struct annotation annotation; | ||
58 | struct symbol symbol; | ||
59 | }; | ||
60 | |||
61 | static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx) | ||
62 | { | ||
63 | return (((void *)¬es->src->histograms) + | ||
64 | (notes->src->sizeof_sym_hist * idx)); | ||
65 | } | ||
66 | |||
67 | static inline struct annotation *symbol__annotation(struct symbol *sym) | ||
68 | { | ||
69 | struct sannotation *a = container_of(sym, struct sannotation, symbol); | ||
70 | return &a->annotation; | ||
71 | } | ||
72 | |||
73 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||
74 | int evidx, u64 addr); | ||
75 | int symbol__alloc_hist(struct symbol *sym, int nevents); | ||
76 | void symbol__annotate_zero_histograms(struct symbol *sym); | ||
77 | |||
78 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | ||
79 | int symbol__annotate_init(struct map *map __used, struct symbol *sym); | ||
80 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | ||
81 | bool full_paths, int min_pcnt, int max_lines, | ||
82 | int context); | ||
83 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | ||
84 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); | ||
85 | void objdump_line_list__purge(struct list_head *head); | ||
86 | |||
87 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||
88 | bool print_lines, bool full_paths, int min_pcnt, | ||
89 | int max_lines); | ||
90 | |||
91 | #ifdef NO_NEWT_SUPPORT | ||
92 | static inline int symbol__tui_annotate(struct symbol *sym __used, | ||
93 | struct map *map __used, | ||
94 | int evidx __used, int refresh __used) | ||
95 | { | ||
96 | return 0; | ||
97 | } | ||
98 | #else | ||
99 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
100 | int refresh); | ||
101 | #endif | ||
102 | |||
103 | #endif /* __PERF_ANNOTATE_H */ | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index deffb8c96071..31f934af9861 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -14,8 +14,8 @@ | |||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | 16 | ||
17 | static int build_id__mark_dso_hit(event_t *event, | 17 | static int build_id__mark_dso_hit(union perf_event *event, |
18 | struct sample_data *sample __used, | 18 | struct perf_sample *sample __used, |
19 | struct perf_session *session) | 19 | struct perf_session *session) |
20 | { | 20 | { |
21 | struct addr_location al; | 21 | struct addr_location al; |
@@ -37,13 +37,14 @@ static int build_id__mark_dso_hit(event_t *event, | |||
37 | return 0; | 37 | return 0; |
38 | } | 38 | } |
39 | 39 | ||
40 | static int event__exit_del_thread(event_t *self, struct sample_data *sample __used, | 40 | static int perf_event__exit_del_thread(union perf_event *event, |
41 | struct perf_session *session) | 41 | struct perf_sample *sample __used, |
42 | struct perf_session *session) | ||
42 | { | 43 | { |
43 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 44 | struct thread *thread = perf_session__findnew(session, event->fork.tid); |
44 | 45 | ||
45 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 46 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
46 | self->fork.ppid, self->fork.ptid); | 47 | event->fork.ppid, event->fork.ptid); |
47 | 48 | ||
48 | if (thread) { | 49 | if (thread) { |
49 | rb_erase(&thread->rb_node, &session->threads); | 50 | rb_erase(&thread->rb_node, &session->threads); |
@@ -56,9 +57,9 @@ static int event__exit_del_thread(event_t *self, struct sample_data *sample __us | |||
56 | 57 | ||
57 | struct perf_event_ops build_id__mark_dso_hit_ops = { | 58 | struct perf_event_ops build_id__mark_dso_hit_ops = { |
58 | .sample = build_id__mark_dso_hit, | 59 | .sample = build_id__mark_dso_hit, |
59 | .mmap = event__process_mmap, | 60 | .mmap = perf_event__process_mmap, |
60 | .fork = event__process_task, | 61 | .fork = perf_event__process_task, |
61 | .exit = event__exit_del_thread, | 62 | .exit = perf_event__exit_del_thread, |
62 | }; | 63 | }; |
63 | 64 | ||
64 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 65 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index a7729797fd96..fc5e5a09d5b9 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -34,13 +34,14 @@ extern int pager_use_color; | |||
34 | extern int use_browser; | 34 | extern int use_browser; |
35 | 35 | ||
36 | #ifdef NO_NEWT_SUPPORT | 36 | #ifdef NO_NEWT_SUPPORT |
37 | static inline void setup_browser(void) | 37 | static inline void setup_browser(bool fallback_to_pager) |
38 | { | 38 | { |
39 | setup_pager(); | 39 | if (fallback_to_pager) |
40 | setup_pager(); | ||
40 | } | 41 | } |
41 | static inline void exit_browser(bool wait_for_ok __used) {} | 42 | static inline void exit_browser(bool wait_for_ok __used) {} |
42 | #else | 43 | #else |
43 | void setup_browser(void); | 44 | void setup_browser(bool fallback_to_pager); |
44 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
45 | #endif | 46 | #endif |
46 | 47 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b2..9f7106a8d9a4 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> |
3 | * | 3 | * |
4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then |
5 | * sort them in an rbtree. | 5 | * sort them in an rbtree. |
@@ -18,7 +18,8 @@ | |||
18 | #include "util.h" | 18 | #include "util.h" |
19 | #include "callchain.h" | 19 | #include "callchain.h" |
20 | 20 | ||
21 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | 21 | bool ip_callchain__valid(struct ip_callchain *chain, |
22 | const union perf_event *event) | ||
22 | { | 23 | { |
23 | unsigned int chain_size = event->header.size; | 24 | unsigned int chain_size = event->header.size; |
24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | 25 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; |
@@ -26,10 +27,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | |||
26 | } | 27 | } |
27 | 28 | ||
28 | #define chain_for_each_child(child, parent) \ | 29 | #define chain_for_each_child(child, parent) \ |
29 | list_for_each_entry(child, &parent->children, brothers) | 30 | list_for_each_entry(child, &parent->children, siblings) |
30 | 31 | ||
31 | #define chain_for_each_child_safe(child, next, parent) \ | 32 | #define chain_for_each_child_safe(child, next, parent) \ |
32 | list_for_each_entry_safe(child, next, &parent->children, brothers) | 33 | list_for_each_entry_safe(child, next, &parent->children, siblings) |
33 | 34 | ||
34 | static void | 35 | static void |
35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 36 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
@@ -38,14 +39,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
38 | struct rb_node **p = &root->rb_node; | 39 | struct rb_node **p = &root->rb_node; |
39 | struct rb_node *parent = NULL; | 40 | struct rb_node *parent = NULL; |
40 | struct callchain_node *rnode; | 41 | struct callchain_node *rnode; |
41 | u64 chain_cumul = cumul_hits(chain); | 42 | u64 chain_cumul = callchain_cumul_hits(chain); |
42 | 43 | ||
43 | while (*p) { | 44 | while (*p) { |
44 | u64 rnode_cumul; | 45 | u64 rnode_cumul; |
45 | 46 | ||
46 | parent = *p; | 47 | parent = *p; |
47 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 48 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
48 | rnode_cumul = cumul_hits(rnode); | 49 | rnode_cumul = callchain_cumul_hits(rnode); |
49 | 50 | ||
50 | switch (mode) { | 51 | switch (mode) { |
51 | case CHAIN_FLAT: | 52 | case CHAIN_FLAT: |
@@ -104,7 +105,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
104 | 105 | ||
105 | chain_for_each_child(child, node) { | 106 | chain_for_each_child(child, node) { |
106 | __sort_chain_graph_abs(child, min_hit); | 107 | __sort_chain_graph_abs(child, min_hit); |
107 | if (cumul_hits(child) >= min_hit) | 108 | if (callchain_cumul_hits(child) >= min_hit) |
108 | rb_insert_callchain(&node->rb_root, child, | 109 | rb_insert_callchain(&node->rb_root, child, |
109 | CHAIN_GRAPH_ABS); | 110 | CHAIN_GRAPH_ABS); |
110 | } | 111 | } |
@@ -129,7 +130,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
129 | 130 | ||
130 | chain_for_each_child(child, node) { | 131 | chain_for_each_child(child, node) { |
131 | __sort_chain_graph_rel(child, min_percent); | 132 | __sort_chain_graph_rel(child, min_percent); |
132 | if (cumul_hits(child) >= min_hit) | 133 | if (callchain_cumul_hits(child) >= min_hit) |
133 | rb_insert_callchain(&node->rb_root, child, | 134 | rb_insert_callchain(&node->rb_root, child, |
134 | CHAIN_GRAPH_REL); | 135 | CHAIN_GRAPH_REL); |
135 | } | 136 | } |
@@ -143,7 +144,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, | |||
143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 144 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
144 | } | 145 | } |
145 | 146 | ||
146 | int register_callchain_param(struct callchain_param *param) | 147 | int callchain_register_param(struct callchain_param *param) |
147 | { | 148 | { |
148 | switch (param->mode) { | 149 | switch (param->mode) { |
149 | case CHAIN_GRAPH_ABS: | 150 | case CHAIN_GRAPH_ABS: |
@@ -189,32 +190,27 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
189 | chain_for_each_child(next, new) | 190 | chain_for_each_child(next, new) |
190 | next->parent = new; | 191 | next->parent = new; |
191 | } | 192 | } |
192 | list_add_tail(&new->brothers, &parent->children); | 193 | list_add_tail(&new->siblings, &parent->children); |
193 | 194 | ||
194 | return new; | 195 | return new; |
195 | } | 196 | } |
196 | 197 | ||
197 | 198 | ||
198 | struct resolved_ip { | ||
199 | u64 ip; | ||
200 | struct map_symbol ms; | ||
201 | }; | ||
202 | |||
203 | struct resolved_chain { | ||
204 | u64 nr; | ||
205 | struct resolved_ip ips[0]; | ||
206 | }; | ||
207 | |||
208 | |||
209 | /* | 199 | /* |
210 | * Fill the node with callchain values | 200 | * Fill the node with callchain values |
211 | */ | 201 | */ |
212 | static void | 202 | static void |
213 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | 203 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
214 | { | 204 | { |
215 | unsigned int i; | 205 | struct callchain_cursor_node *cursor_node; |
206 | |||
207 | node->val_nr = cursor->nr - cursor->pos; | ||
208 | if (!node->val_nr) | ||
209 | pr_warning("Warning: empty node in callchain tree\n"); | ||
216 | 210 | ||
217 | for (i = start; i < chain->nr; i++) { | 211 | cursor_node = callchain_cursor_current(cursor); |
212 | |||
213 | while (cursor_node) { | ||
218 | struct callchain_list *call; | 214 | struct callchain_list *call; |
219 | 215 | ||
220 | call = zalloc(sizeof(*call)); | 216 | call = zalloc(sizeof(*call)); |
@@ -222,23 +218,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | |||
222 | perror("not enough memory for the code path tree"); | 218 | perror("not enough memory for the code path tree"); |
223 | return; | 219 | return; |
224 | } | 220 | } |
225 | call->ip = chain->ips[i].ip; | 221 | call->ip = cursor_node->ip; |
226 | call->ms = chain->ips[i].ms; | 222 | call->ms.sym = cursor_node->sym; |
223 | call->ms.map = cursor_node->map; | ||
227 | list_add_tail(&call->list, &node->val); | 224 | list_add_tail(&call->list, &node->val); |
225 | |||
226 | callchain_cursor_advance(cursor); | ||
227 | cursor_node = callchain_cursor_current(cursor); | ||
228 | } | 228 | } |
229 | node->val_nr = chain->nr - start; | ||
230 | if (!node->val_nr) | ||
231 | pr_warning("Warning: empty node in callchain tree\n"); | ||
232 | } | 229 | } |
233 | 230 | ||
234 | static void | 231 | static void |
235 | add_child(struct callchain_node *parent, struct resolved_chain *chain, | 232 | add_child(struct callchain_node *parent, |
236 | int start, u64 period) | 233 | struct callchain_cursor *cursor, |
234 | u64 period) | ||
237 | { | 235 | { |
238 | struct callchain_node *new; | 236 | struct callchain_node *new; |
239 | 237 | ||
240 | new = create_child(parent, false); | 238 | new = create_child(parent, false); |
241 | fill_node(new, chain, start); | 239 | fill_node(new, cursor); |
242 | 240 | ||
243 | new->children_hit = 0; | 241 | new->children_hit = 0; |
244 | new->hit = period; | 242 | new->hit = period; |
@@ -250,9 +248,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
250 | * Then create another child to host the given callchain of new branch | 248 | * Then create another child to host the given callchain of new branch |
251 | */ | 249 | */ |
252 | static void | 250 | static void |
253 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | 251 | split_add_child(struct callchain_node *parent, |
254 | struct callchain_list *to_split, int idx_parents, int idx_local, | 252 | struct callchain_cursor *cursor, |
255 | u64 period) | 253 | struct callchain_list *to_split, |
254 | u64 idx_parents, u64 idx_local, u64 period) | ||
256 | { | 255 | { |
257 | struct callchain_node *new; | 256 | struct callchain_node *new; |
258 | struct list_head *old_tail; | 257 | struct list_head *old_tail; |
@@ -272,14 +271,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
272 | /* split the hits */ | 271 | /* split the hits */ |
273 | new->hit = parent->hit; | 272 | new->hit = parent->hit; |
274 | new->children_hit = parent->children_hit; | 273 | new->children_hit = parent->children_hit; |
275 | parent->children_hit = cumul_hits(new); | 274 | parent->children_hit = callchain_cumul_hits(new); |
276 | new->val_nr = parent->val_nr - idx_local; | 275 | new->val_nr = parent->val_nr - idx_local; |
277 | parent->val_nr = idx_local; | 276 | parent->val_nr = idx_local; |
278 | 277 | ||
279 | /* create a new child for the new branch if any */ | 278 | /* create a new child for the new branch if any */ |
280 | if (idx_total < chain->nr) { | 279 | if (idx_total < cursor->nr) { |
281 | parent->hit = 0; | 280 | parent->hit = 0; |
282 | add_child(parent, chain, idx_total, period); | 281 | add_child(parent, cursor, period); |
283 | parent->children_hit += period; | 282 | parent->children_hit += period; |
284 | } else { | 283 | } else { |
285 | parent->hit = period; | 284 | parent->hit = period; |
@@ -287,36 +286,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
287 | } | 286 | } |
288 | 287 | ||
289 | static int | 288 | static int |
290 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 289 | append_chain(struct callchain_node *root, |
291 | unsigned int start, u64 period); | 290 | struct callchain_cursor *cursor, |
291 | u64 period); | ||
292 | 292 | ||
293 | static void | 293 | static void |
294 | append_chain_children(struct callchain_node *root, struct resolved_chain *chain, | 294 | append_chain_children(struct callchain_node *root, |
295 | unsigned int start, u64 period) | 295 | struct callchain_cursor *cursor, |
296 | u64 period) | ||
296 | { | 297 | { |
297 | struct callchain_node *rnode; | 298 | struct callchain_node *rnode; |
298 | 299 | ||
299 | /* lookup in childrens */ | 300 | /* lookup in childrens */ |
300 | chain_for_each_child(rnode, root) { | 301 | chain_for_each_child(rnode, root) { |
301 | unsigned int ret = append_chain(rnode, chain, start, period); | 302 | unsigned int ret = append_chain(rnode, cursor, period); |
302 | 303 | ||
303 | if (!ret) | 304 | if (!ret) |
304 | goto inc_children_hit; | 305 | goto inc_children_hit; |
305 | } | 306 | } |
306 | /* nothing in children, add to the current node */ | 307 | /* nothing in children, add to the current node */ |
307 | add_child(root, chain, start, period); | 308 | add_child(root, cursor, period); |
308 | 309 | ||
309 | inc_children_hit: | 310 | inc_children_hit: |
310 | root->children_hit += period; | 311 | root->children_hit += period; |
311 | } | 312 | } |
312 | 313 | ||
313 | static int | 314 | static int |
314 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 315 | append_chain(struct callchain_node *root, |
315 | unsigned int start, u64 period) | 316 | struct callchain_cursor *cursor, |
317 | u64 period) | ||
316 | { | 318 | { |
319 | struct callchain_cursor_node *curr_snap = cursor->curr; | ||
317 | struct callchain_list *cnode; | 320 | struct callchain_list *cnode; |
318 | unsigned int i = start; | 321 | u64 start = cursor->pos; |
319 | bool found = false; | 322 | bool found = false; |
323 | u64 matches; | ||
320 | 324 | ||
321 | /* | 325 | /* |
322 | * Lookup in the current node | 326 | * Lookup in the current node |
@@ -324,141 +328,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, | |||
324 | * anywhere inside a function. | 328 | * anywhere inside a function. |
325 | */ | 329 | */ |
326 | list_for_each_entry(cnode, &root->val, list) { | 330 | list_for_each_entry(cnode, &root->val, list) { |
331 | struct callchain_cursor_node *node; | ||
327 | struct symbol *sym; | 332 | struct symbol *sym; |
328 | 333 | ||
329 | if (i == chain->nr) | 334 | node = callchain_cursor_current(cursor); |
335 | if (!node) | ||
330 | break; | 336 | break; |
331 | 337 | ||
332 | sym = chain->ips[i].ms.sym; | 338 | sym = node->sym; |
333 | 339 | ||
334 | if (cnode->ms.sym && sym) { | 340 | if (cnode->ms.sym && sym) { |
335 | if (cnode->ms.sym->start != sym->start) | 341 | if (cnode->ms.sym->start != sym->start) |
336 | break; | 342 | break; |
337 | } else if (cnode->ip != chain->ips[i].ip) | 343 | } else if (cnode->ip != node->ip) |
338 | break; | 344 | break; |
339 | 345 | ||
340 | if (!found) | 346 | if (!found) |
341 | found = true; | 347 | found = true; |
342 | i++; | 348 | |
349 | callchain_cursor_advance(cursor); | ||
343 | } | 350 | } |
344 | 351 | ||
345 | /* matches not, relay on the parent */ | 352 | /* matches not, relay on the parent */ |
346 | if (!found) | 353 | if (!found) { |
354 | cursor->curr = curr_snap; | ||
355 | cursor->pos = start; | ||
347 | return -1; | 356 | return -1; |
357 | } | ||
358 | |||
359 | matches = cursor->pos - start; | ||
348 | 360 | ||
349 | /* we match only a part of the node. Split it and add the new chain */ | 361 | /* we match only a part of the node. Split it and add the new chain */ |
350 | if (i - start < root->val_nr) { | 362 | if (matches < root->val_nr) { |
351 | split_add_child(root, chain, cnode, start, i - start, period); | 363 | split_add_child(root, cursor, cnode, start, matches, period); |
352 | return 0; | 364 | return 0; |
353 | } | 365 | } |
354 | 366 | ||
355 | /* we match 100% of the path, increment the hit */ | 367 | /* we match 100% of the path, increment the hit */ |
356 | if (i - start == root->val_nr && i == chain->nr) { | 368 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
357 | root->hit += period; | 369 | root->hit += period; |
358 | return 0; | 370 | return 0; |
359 | } | 371 | } |
360 | 372 | ||
361 | /* We match the node and still have a part remaining */ | 373 | /* We match the node and still have a part remaining */ |
362 | append_chain_children(root, chain, i, period); | 374 | append_chain_children(root, cursor, period); |
363 | 375 | ||
364 | return 0; | 376 | return 0; |
365 | } | 377 | } |
366 | 378 | ||
367 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, | 379 | int callchain_append(struct callchain_root *root, |
368 | struct map_symbol *syms) | 380 | struct callchain_cursor *cursor, |
369 | { | 381 | u64 period) |
370 | int i, j = 0; | ||
371 | |||
372 | for (i = 0; i < (int)old->nr; i++) { | ||
373 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
374 | continue; | ||
375 | |||
376 | new->ips[j].ip = old->ips[i]; | ||
377 | new->ips[j].ms = syms[i]; | ||
378 | j++; | ||
379 | } | ||
380 | |||
381 | new->nr = j; | ||
382 | } | ||
383 | |||
384 | |||
385 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | ||
386 | struct map_symbol *syms, u64 period) | ||
387 | { | 382 | { |
388 | struct resolved_chain *filtered; | 383 | if (!cursor->nr) |
389 | |||
390 | if (!chain->nr) | ||
391 | return 0; | 384 | return 0; |
392 | 385 | ||
393 | filtered = zalloc(sizeof(*filtered) + | 386 | callchain_cursor_commit(cursor); |
394 | chain->nr * sizeof(struct resolved_ip)); | ||
395 | if (!filtered) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | filter_context(chain, filtered, syms); | ||
399 | |||
400 | if (!filtered->nr) | ||
401 | goto end; | ||
402 | 387 | ||
403 | append_chain_children(&root->node, filtered, 0, period); | 388 | append_chain_children(&root->node, cursor, period); |
404 | 389 | ||
405 | if (filtered->nr > root->max_depth) | 390 | if (cursor->nr > root->max_depth) |
406 | root->max_depth = filtered->nr; | 391 | root->max_depth = cursor->nr; |
407 | end: | ||
408 | free(filtered); | ||
409 | 392 | ||
410 | return 0; | 393 | return 0; |
411 | } | 394 | } |
412 | 395 | ||
413 | static int | 396 | static int |
414 | merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, | 397 | merge_chain_branch(struct callchain_cursor *cursor, |
415 | struct resolved_chain *chain) | 398 | struct callchain_node *dst, struct callchain_node *src) |
416 | { | 399 | { |
400 | struct callchain_cursor_node **old_last = cursor->last; | ||
417 | struct callchain_node *child, *next_child; | 401 | struct callchain_node *child, *next_child; |
418 | struct callchain_list *list, *next_list; | 402 | struct callchain_list *list, *next_list; |
419 | int old_pos = chain->nr; | 403 | int old_pos = cursor->nr; |
420 | int err = 0; | 404 | int err = 0; |
421 | 405 | ||
422 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 406 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
423 | chain->ips[chain->nr].ip = list->ip; | 407 | callchain_cursor_append(cursor, list->ip, |
424 | chain->ips[chain->nr].ms = list->ms; | 408 | list->ms.map, list->ms.sym); |
425 | chain->nr++; | ||
426 | list_del(&list->list); | 409 | list_del(&list->list); |
427 | free(list); | 410 | free(list); |
428 | } | 411 | } |
429 | 412 | ||
430 | if (src->hit) | 413 | if (src->hit) { |
431 | append_chain_children(dst, chain, 0, src->hit); | 414 | callchain_cursor_commit(cursor); |
415 | append_chain_children(dst, cursor, src->hit); | ||
416 | } | ||
432 | 417 | ||
433 | chain_for_each_child_safe(child, next_child, src) { | 418 | chain_for_each_child_safe(child, next_child, src) { |
434 | err = merge_chain_branch(dst, child, chain); | 419 | err = merge_chain_branch(cursor, dst, child); |
435 | if (err) | 420 | if (err) |
436 | break; | 421 | break; |
437 | 422 | ||
438 | list_del(&child->brothers); | 423 | list_del(&child->siblings); |
439 | free(child); | 424 | free(child); |
440 | } | 425 | } |
441 | 426 | ||
442 | chain->nr = old_pos; | 427 | cursor->nr = old_pos; |
428 | cursor->last = old_last; | ||
443 | 429 | ||
444 | return err; | 430 | return err; |
445 | } | 431 | } |
446 | 432 | ||
447 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src) | 433 | int callchain_merge(struct callchain_cursor *cursor, |
434 | struct callchain_root *dst, struct callchain_root *src) | ||
435 | { | ||
436 | return merge_chain_branch(cursor, &dst->node, &src->node); | ||
437 | } | ||
438 | |||
439 | int callchain_cursor_append(struct callchain_cursor *cursor, | ||
440 | u64 ip, struct map *map, struct symbol *sym) | ||
448 | { | 441 | { |
449 | struct resolved_chain *chain; | 442 | struct callchain_cursor_node *node = *cursor->last; |
450 | int err; | ||
451 | 443 | ||
452 | chain = malloc(sizeof(*chain) + | 444 | if (!node) { |
453 | src->max_depth * sizeof(struct resolved_ip)); | 445 | node = calloc(sizeof(*node), 1); |
454 | if (!chain) | 446 | if (!node) |
455 | return -ENOMEM; | 447 | return -ENOMEM; |
456 | 448 | ||
457 | chain->nr = 0; | 449 | *cursor->last = node; |
450 | } | ||
458 | 451 | ||
459 | err = merge_chain_branch(&dst->node, &src->node, chain); | 452 | node->ip = ip; |
453 | node->map = map; | ||
454 | node->sym = sym; | ||
460 | 455 | ||
461 | free(chain); | 456 | cursor->nr++; |
462 | 457 | ||
463 | return err; | 458 | cursor->last = &node->next; |
459 | |||
460 | return 0; | ||
464 | } | 461 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad2..1a79df9f739f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -16,7 +16,7 @@ enum chain_mode { | |||
16 | 16 | ||
17 | struct callchain_node { | 17 | struct callchain_node { |
18 | struct callchain_node *parent; | 18 | struct callchain_node *parent; |
19 | struct list_head brothers; | 19 | struct list_head siblings; |
20 | struct list_head children; | 20 | struct list_head children; |
21 | struct list_head val; | 21 | struct list_head val; |
22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ |
@@ -49,9 +49,30 @@ struct callchain_list { | |||
49 | struct list_head list; | 49 | struct list_head list; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | /* | ||
53 | * A callchain cursor is a single linked list that | ||
54 | * let one feed a callchain progressively. | ||
55 | * It keeps persitent allocated entries to minimize | ||
56 | * allocations. | ||
57 | */ | ||
58 | struct callchain_cursor_node { | ||
59 | u64 ip; | ||
60 | struct map *map; | ||
61 | struct symbol *sym; | ||
62 | struct callchain_cursor_node *next; | ||
63 | }; | ||
64 | |||
65 | struct callchain_cursor { | ||
66 | u64 nr; | ||
67 | struct callchain_cursor_node *first; | ||
68 | struct callchain_cursor_node **last; | ||
69 | u64 pos; | ||
70 | struct callchain_cursor_node *curr; | ||
71 | }; | ||
72 | |||
52 | static inline void callchain_init(struct callchain_root *root) | 73 | static inline void callchain_init(struct callchain_root *root) |
53 | { | 74 | { |
54 | INIT_LIST_HEAD(&root->node.brothers); | 75 | INIT_LIST_HEAD(&root->node.siblings); |
55 | INIT_LIST_HEAD(&root->node.children); | 76 | INIT_LIST_HEAD(&root->node.children); |
56 | INIT_LIST_HEAD(&root->node.val); | 77 | INIT_LIST_HEAD(&root->node.val); |
57 | 78 | ||
@@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) | |||
61 | root->max_depth = 0; | 82 | root->max_depth = 0; |
62 | } | 83 | } |
63 | 84 | ||
64 | static inline u64 cumul_hits(struct callchain_node *node) | 85 | static inline u64 callchain_cumul_hits(struct callchain_node *node) |
65 | { | 86 | { |
66 | return node->hit + node->children_hit; | 87 | return node->hit + node->children_hit; |
67 | } | 88 | } |
68 | 89 | ||
69 | int register_callchain_param(struct callchain_param *param); | 90 | int callchain_register_param(struct callchain_param *param); |
70 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | 91 | int callchain_append(struct callchain_root *root, |
71 | struct map_symbol *syms, u64 period); | 92 | struct callchain_cursor *cursor, |
72 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src); | 93 | u64 period); |
94 | |||
95 | int callchain_merge(struct callchain_cursor *cursor, | ||
96 | struct callchain_root *dst, struct callchain_root *src); | ||
97 | |||
98 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
99 | const union perf_event *event); | ||
100 | /* | ||
101 | * Initialize a cursor before adding entries inside, but keep | ||
102 | * the previously allocated entries as a cache. | ||
103 | */ | ||
104 | static inline void callchain_cursor_reset(struct callchain_cursor *cursor) | ||
105 | { | ||
106 | cursor->nr = 0; | ||
107 | cursor->last = &cursor->first; | ||
108 | } | ||
109 | |||
110 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, | ||
111 | struct map *map, struct symbol *sym); | ||
73 | 112 | ||
74 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); | 113 | /* Close a cursor writing session. Initialize for the reader */ |
114 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) | ||
115 | { | ||
116 | cursor->curr = cursor->first; | ||
117 | cursor->pos = 0; | ||
118 | } | ||
119 | |||
120 | /* Cursor reading iteration helpers */ | ||
121 | static inline struct callchain_cursor_node * | ||
122 | callchain_cursor_current(struct callchain_cursor *cursor) | ||
123 | { | ||
124 | if (cursor->pos == cursor->nr) | ||
125 | return NULL; | ||
126 | |||
127 | return cursor->curr; | ||
128 | } | ||
129 | |||
130 | static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | ||
131 | { | ||
132 | cursor->curr = cursor->curr->next; | ||
133 | cursor->pos++; | ||
134 | } | ||
75 | #endif /* __PERF_CALLCHAIN_H */ | 135 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c new file mode 100644 index 000000000000..9fea75535221 --- /dev/null +++ b/tools/perf/util/cgroup.c | |||
@@ -0,0 +1,178 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "parse-options.h" | ||
4 | #include "evsel.h" | ||
5 | #include "cgroup.h" | ||
6 | #include "debugfs.h" /* MAX_PATH, STR() */ | ||
7 | #include "evlist.h" | ||
8 | |||
9 | int nr_cgroups; | ||
10 | |||
11 | static int | ||
12 | cgroupfs_find_mountpoint(char *buf, size_t maxlen) | ||
13 | { | ||
14 | FILE *fp; | ||
15 | char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1]; | ||
16 | char *token, *saved_ptr; | ||
17 | int found = 0; | ||
18 | |||
19 | fp = fopen("/proc/mounts", "r"); | ||
20 | if (!fp) | ||
21 | return -1; | ||
22 | |||
23 | /* | ||
24 | * in order to handle split hierarchy, we need to scan /proc/mounts | ||
25 | * and inspect every cgroupfs mount point to find one that has | ||
26 | * perf_event subsystem | ||
27 | */ | ||
28 | while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %" | ||
29 | STR(MAX_PATH)"s %*d %*d\n", | ||
30 | mountpoint, type, tokens) == 3) { | ||
31 | |||
32 | if (!strcmp(type, "cgroup")) { | ||
33 | |||
34 | token = strtok_r(tokens, ",", &saved_ptr); | ||
35 | |||
36 | while (token != NULL) { | ||
37 | if (!strcmp(token, "perf_event")) { | ||
38 | found = 1; | ||
39 | break; | ||
40 | } | ||
41 | token = strtok_r(NULL, ",", &saved_ptr); | ||
42 | } | ||
43 | } | ||
44 | if (found) | ||
45 | break; | ||
46 | } | ||
47 | fclose(fp); | ||
48 | if (!found) | ||
49 | return -1; | ||
50 | |||
51 | if (strlen(mountpoint) < maxlen) { | ||
52 | strcpy(buf, mountpoint); | ||
53 | return 0; | ||
54 | } | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | static int open_cgroup(char *name) | ||
59 | { | ||
60 | char path[MAX_PATH+1]; | ||
61 | char mnt[MAX_PATH+1]; | ||
62 | int fd; | ||
63 | |||
64 | |||
65 | if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1)) | ||
66 | return -1; | ||
67 | |||
68 | snprintf(path, MAX_PATH, "%s/%s", mnt, name); | ||
69 | |||
70 | fd = open(path, O_RDONLY); | ||
71 | if (fd == -1) | ||
72 | fprintf(stderr, "no access to cgroup %s\n", path); | ||
73 | |||
74 | return fd; | ||
75 | } | ||
76 | |||
77 | static int add_cgroup(struct perf_evlist *evlist, char *str) | ||
78 | { | ||
79 | struct perf_evsel *counter; | ||
80 | struct cgroup_sel *cgrp = NULL; | ||
81 | int n; | ||
82 | /* | ||
83 | * check if cgrp is already defined, if so we reuse it | ||
84 | */ | ||
85 | list_for_each_entry(counter, &evlist->entries, node) { | ||
86 | cgrp = counter->cgrp; | ||
87 | if (!cgrp) | ||
88 | continue; | ||
89 | if (!strcmp(cgrp->name, str)) | ||
90 | break; | ||
91 | |||
92 | cgrp = NULL; | ||
93 | } | ||
94 | |||
95 | if (!cgrp) { | ||
96 | cgrp = zalloc(sizeof(*cgrp)); | ||
97 | if (!cgrp) | ||
98 | return -1; | ||
99 | |||
100 | cgrp->name = str; | ||
101 | |||
102 | cgrp->fd = open_cgroup(str); | ||
103 | if (cgrp->fd == -1) { | ||
104 | free(cgrp); | ||
105 | return -1; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * find corresponding event | ||
111 | * if add cgroup N, then need to find event N | ||
112 | */ | ||
113 | n = 0; | ||
114 | list_for_each_entry(counter, &evlist->entries, node) { | ||
115 | if (n == nr_cgroups) | ||
116 | goto found; | ||
117 | n++; | ||
118 | } | ||
119 | if (cgrp->refcnt == 0) | ||
120 | free(cgrp); | ||
121 | |||
122 | return -1; | ||
123 | found: | ||
124 | cgrp->refcnt++; | ||
125 | counter->cgrp = cgrp; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | void close_cgroup(struct cgroup_sel *cgrp) | ||
130 | { | ||
131 | if (!cgrp) | ||
132 | return; | ||
133 | |||
134 | /* XXX: not reentrant */ | ||
135 | if (--cgrp->refcnt == 0) { | ||
136 | close(cgrp->fd); | ||
137 | free(cgrp->name); | ||
138 | free(cgrp); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | int parse_cgroups(const struct option *opt __used, const char *str, | ||
143 | int unset __used) | ||
144 | { | ||
145 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
146 | const char *p, *e, *eos = str + strlen(str); | ||
147 | char *s; | ||
148 | int ret; | ||
149 | |||
150 | if (list_empty(&evlist->entries)) { | ||
151 | fprintf(stderr, "must define events before cgroups\n"); | ||
152 | return -1; | ||
153 | } | ||
154 | |||
155 | for (;;) { | ||
156 | p = strchr(str, ','); | ||
157 | e = p ? p : eos; | ||
158 | |||
159 | /* allow empty cgroups, i.e., skip */ | ||
160 | if (e - str) { | ||
161 | /* termination added */ | ||
162 | s = strndup(str, e - str); | ||
163 | if (!s) | ||
164 | return -1; | ||
165 | ret = add_cgroup(evlist, s); | ||
166 | if (ret) { | ||
167 | free(s); | ||
168 | return -1; | ||
169 | } | ||
170 | } | ||
171 | /* nr_cgroups is increased een for empty cgroups */ | ||
172 | nr_cgroups++; | ||
173 | if (!p) | ||
174 | break; | ||
175 | str = p+1; | ||
176 | } | ||
177 | return 0; | ||
178 | } | ||
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h new file mode 100644 index 000000000000..89acd6debdc5 --- /dev/null +++ b/tools/perf/util/cgroup.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef __CGROUP_H__ | ||
2 | #define __CGROUP_H__ | ||
3 | |||
4 | struct option; | ||
5 | |||
6 | struct cgroup_sel { | ||
7 | char *name; | ||
8 | int fd; | ||
9 | int refcnt; | ||
10 | }; | ||
11 | |||
12 | |||
13 | extern int nr_cgroups; /* number of explicit cgroups defined */ | ||
14 | extern void close_cgroup(struct cgroup_sel *cgrp); | ||
15 | extern int parse_cgroups(const struct option *opt, const char *str, int unset); | ||
16 | |||
17 | #endif /* __CGROUP_H__ */ | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 3ccaa1043383..6893eec693ab 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) | |||
177 | 177 | ||
178 | return cpus; | 178 | return cpus; |
179 | } | 179 | } |
180 | |||
181 | void cpu_map__delete(struct cpu_map *map) | ||
182 | { | ||
183 | free(map); | ||
184 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index f7a4f42f6307..072c0a374794 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -8,6 +8,6 @@ struct cpu_map { | |||
8 | 8 | ||
9 | struct cpu_map *cpu_map__new(const char *cpu_list); | 9 | struct cpu_map *cpu_map__new(const char *cpu_list); |
10 | struct cpu_map *cpu_map__dummy_new(void); | 10 | struct cpu_map *cpu_map__dummy_new(void); |
11 | void *cpu_map__delete(struct cpu_map *map); | 11 | void cpu_map__delete(struct cpu_map *map); |
12 | 12 | ||
13 | #endif /* __PERF_CPUMAP_H */ | 13 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 01bbe8ecec3f..d4536a9e0d8c 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -57,7 +57,7 @@ void ui__warning(const char *format, ...) | |||
57 | } | 57 | } |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | void trace_event(event_t *event) | 60 | void trace_event(union perf_event *event) |
61 | { | 61 | { |
62 | unsigned char *raw_event = (void *)event; | 62 | unsigned char *raw_event = (void *)event; |
63 | const char *color = PERF_COLOR_BLUE; | 63 | const char *color = PERF_COLOR_BLUE; |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index ca35fd66b5df..93516cf4682c 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -9,7 +9,7 @@ extern int verbose; | |||
9 | extern bool quiet, dump_trace; | 9 | extern bool quiet, dump_trace; |
10 | 10 | ||
11 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 11 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
12 | void trace_event(event_t *event); | 12 | void trace_event(union perf_event *event); |
13 | 13 | ||
14 | struct ui_progress; | 14 | struct ui_progress; |
15 | 15 | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 50d0a931497a..2b15c362ef56 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -6,8 +6,9 @@ | |||
6 | #include "string.h" | 6 | #include "string.h" |
7 | #include "strlist.h" | 7 | #include "strlist.h" |
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | #include "thread_map.h" | ||
9 | 10 | ||
10 | static const char *event__name[] = { | 11 | static const char *perf_event__names[] = { |
11 | [0] = "TOTAL", | 12 | [0] = "TOTAL", |
12 | [PERF_RECORD_MMAP] = "MMAP", | 13 | [PERF_RECORD_MMAP] = "MMAP", |
13 | [PERF_RECORD_LOST] = "LOST", | 14 | [PERF_RECORD_LOST] = "LOST", |
@@ -25,16 +26,16 @@ static const char *event__name[] = { | |||
25 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 26 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
26 | }; | 27 | }; |
27 | 28 | ||
28 | const char *event__get_event_name(unsigned int id) | 29 | const char *perf_event__name(unsigned int id) |
29 | { | 30 | { |
30 | if (id >= ARRAY_SIZE(event__name)) | 31 | if (id >= ARRAY_SIZE(perf_event__names)) |
31 | return "INVALID"; | 32 | return "INVALID"; |
32 | if (!event__name[id]) | 33 | if (!perf_event__names[id]) |
33 | return "UNKNOWN"; | 34 | return "UNKNOWN"; |
34 | return event__name[id]; | 35 | return perf_event__names[id]; |
35 | } | 36 | } |
36 | 37 | ||
37 | static struct sample_data synth_sample = { | 38 | static struct perf_sample synth_sample = { |
38 | .pid = -1, | 39 | .pid = -1, |
39 | .tid = -1, | 40 | .tid = -1, |
40 | .time = -1, | 41 | .time = -1, |
@@ -43,9 +44,9 @@ static struct sample_data synth_sample = { | |||
43 | .period = 1, | 44 | .period = 1, |
44 | }; | 45 | }; |
45 | 46 | ||
46 | static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, | 47 | static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid, |
47 | event__handler_t process, | 48 | int full, perf_event__handler_t process, |
48 | struct perf_session *session) | 49 | struct perf_session *session) |
49 | { | 50 | { |
50 | char filename[PATH_MAX]; | 51 | char filename[PATH_MAX]; |
51 | char bf[BUFSIZ]; | 52 | char bf[BUFSIZ]; |
@@ -126,9 +127,10 @@ out: | |||
126 | return tgid; | 127 | return tgid; |
127 | } | 128 | } |
128 | 129 | ||
129 | static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, | 130 | static int perf_event__synthesize_mmap_events(union perf_event *event, |
130 | event__handler_t process, | 131 | pid_t pid, pid_t tgid, |
131 | struct perf_session *session) | 132 | perf_event__handler_t process, |
133 | struct perf_session *session) | ||
132 | { | 134 | { |
133 | char filename[PATH_MAX]; | 135 | char filename[PATH_MAX]; |
134 | FILE *fp; | 136 | FILE *fp; |
@@ -199,14 +201,14 @@ static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, | |||
199 | return 0; | 201 | return 0; |
200 | } | 202 | } |
201 | 203 | ||
202 | int event__synthesize_modules(event__handler_t process, | 204 | int perf_event__synthesize_modules(perf_event__handler_t process, |
203 | struct perf_session *session, | 205 | struct perf_session *session, |
204 | struct machine *machine) | 206 | struct machine *machine) |
205 | { | 207 | { |
206 | struct rb_node *nd; | 208 | struct rb_node *nd; |
207 | struct map_groups *kmaps = &machine->kmaps; | 209 | struct map_groups *kmaps = &machine->kmaps; |
208 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | 210 | union perf_event *event = zalloc((sizeof(event->mmap) + |
209 | 211 | session->id_hdr_size)); | |
210 | if (event == NULL) { | 212 | if (event == NULL) { |
211 | pr_debug("Not enough memory synthesizing mmap event " | 213 | pr_debug("Not enough memory synthesizing mmap event " |
212 | "for kernel modules\n"); | 214 | "for kernel modules\n"); |
@@ -251,23 +253,24 @@ int event__synthesize_modules(event__handler_t process, | |||
251 | return 0; | 253 | return 0; |
252 | } | 254 | } |
253 | 255 | ||
254 | static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, | 256 | static int __event__synthesize_thread(union perf_event *comm_event, |
255 | pid_t pid, event__handler_t process, | 257 | union perf_event *mmap_event, |
258 | pid_t pid, perf_event__handler_t process, | ||
256 | struct perf_session *session) | 259 | struct perf_session *session) |
257 | { | 260 | { |
258 | pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, | 261 | pid_t tgid = perf_event__synthesize_comm(comm_event, pid, 1, process, |
259 | session); | 262 | session); |
260 | if (tgid == -1) | 263 | if (tgid == -1) |
261 | return -1; | 264 | return -1; |
262 | return event__synthesize_mmap_events(mmap_event, pid, tgid, | 265 | return perf_event__synthesize_mmap_events(mmap_event, pid, tgid, |
263 | process, session); | 266 | process, session); |
264 | } | 267 | } |
265 | 268 | ||
266 | int event__synthesize_thread_map(struct thread_map *threads, | 269 | int perf_event__synthesize_thread_map(struct thread_map *threads, |
267 | event__handler_t process, | 270 | perf_event__handler_t process, |
268 | struct perf_session *session) | 271 | struct perf_session *session) |
269 | { | 272 | { |
270 | event_t *comm_event, *mmap_event; | 273 | union perf_event *comm_event, *mmap_event; |
271 | int err = -1, thread; | 274 | int err = -1, thread; |
272 | 275 | ||
273 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | 276 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); |
@@ -294,12 +297,12 @@ out: | |||
294 | return err; | 297 | return err; |
295 | } | 298 | } |
296 | 299 | ||
297 | int event__synthesize_threads(event__handler_t process, | 300 | int perf_event__synthesize_threads(perf_event__handler_t process, |
298 | struct perf_session *session) | 301 | struct perf_session *session) |
299 | { | 302 | { |
300 | DIR *proc; | 303 | DIR *proc; |
301 | struct dirent dirent, *next; | 304 | struct dirent dirent, *next; |
302 | event_t *comm_event, *mmap_event; | 305 | union perf_event *comm_event, *mmap_event; |
303 | int err = -1; | 306 | int err = -1; |
304 | 307 | ||
305 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | 308 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); |
@@ -357,10 +360,10 @@ static int find_symbol_cb(void *arg, const char *name, char type, | |||
357 | return 1; | 360 | return 1; |
358 | } | 361 | } |
359 | 362 | ||
360 | int event__synthesize_kernel_mmap(event__handler_t process, | 363 | int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, |
361 | struct perf_session *session, | 364 | struct perf_session *session, |
362 | struct machine *machine, | 365 | struct machine *machine, |
363 | const char *symbol_name) | 366 | const char *symbol_name) |
364 | { | 367 | { |
365 | size_t size; | 368 | size_t size; |
366 | const char *filename, *mmap_name; | 369 | const char *filename, *mmap_name; |
@@ -374,8 +377,8 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
374 | * kernels. | 377 | * kernels. |
375 | */ | 378 | */ |
376 | struct process_symbol_args args = { .name = symbol_name, }; | 379 | struct process_symbol_args args = { .name = symbol_name, }; |
377 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | 380 | union perf_event *event = zalloc((sizeof(event->mmap) + |
378 | 381 | session->id_hdr_size)); | |
379 | if (event == NULL) { | 382 | if (event == NULL) { |
380 | pr_debug("Not enough memory synthesizing mmap event " | 383 | pr_debug("Not enough memory synthesizing mmap event " |
381 | "for kernel modules\n"); | 384 | "for kernel modules\n"); |
@@ -421,42 +424,15 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
421 | return err; | 424 | return err; |
422 | } | 425 | } |
423 | 426 | ||
424 | static void thread__comm_adjust(struct thread *self, struct hists *hists) | 427 | int perf_event__process_comm(union perf_event *event, |
425 | { | 428 | struct perf_sample *sample __used, |
426 | char *comm = self->comm; | 429 | struct perf_session *session) |
427 | |||
428 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
429 | (!symbol_conf.comm_list || | ||
430 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
431 | u16 slen = strlen(comm); | ||
432 | |||
433 | if (hists__new_col_len(hists, HISTC_COMM, slen)) | ||
434 | hists__set_col_len(hists, HISTC_THREAD, slen + 6); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | static int thread__set_comm_adjust(struct thread *self, const char *comm, | ||
439 | struct hists *hists) | ||
440 | { | 430 | { |
441 | int ret = thread__set_comm(self, comm); | 431 | struct thread *thread = perf_session__findnew(session, event->comm.tid); |
442 | |||
443 | if (ret) | ||
444 | return ret; | ||
445 | |||
446 | thread__comm_adjust(self, hists); | ||
447 | 432 | ||
448 | return 0; | 433 | dump_printf(": %s:%d\n", event->comm.comm, event->comm.tid); |
449 | } | ||
450 | 434 | ||
451 | int event__process_comm(event_t *self, struct sample_data *sample __used, | 435 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { |
452 | struct perf_session *session) | ||
453 | { | ||
454 | struct thread *thread = perf_session__findnew(session, self->comm.tid); | ||
455 | |||
456 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); | ||
457 | |||
458 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, | ||
459 | &session->hists)) { | ||
460 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 436 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
461 | return -1; | 437 | return -1; |
462 | } | 438 | } |
@@ -464,19 +440,21 @@ int event__process_comm(event_t *self, struct sample_data *sample __used, | |||
464 | return 0; | 440 | return 0; |
465 | } | 441 | } |
466 | 442 | ||
467 | int event__process_lost(event_t *self, struct sample_data *sample __used, | 443 | int perf_event__process_lost(union perf_event *event, |
468 | struct perf_session *session) | 444 | struct perf_sample *sample __used, |
445 | struct perf_session *session) | ||
469 | { | 446 | { |
470 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | 447 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", |
471 | self->lost.id, self->lost.lost); | 448 | event->lost.id, event->lost.lost); |
472 | session->hists.stats.total_lost += self->lost.lost; | 449 | session->hists.stats.total_lost += event->lost.lost; |
473 | return 0; | 450 | return 0; |
474 | } | 451 | } |
475 | 452 | ||
476 | static void event_set_kernel_mmap_len(struct map **maps, event_t *self) | 453 | static void perf_event__set_kernel_mmap_len(union perf_event *event, |
454 | struct map **maps) | ||
477 | { | 455 | { |
478 | maps[MAP__FUNCTION]->start = self->mmap.start; | 456 | maps[MAP__FUNCTION]->start = event->mmap.start; |
479 | maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | 457 | maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; |
480 | /* | 458 | /* |
481 | * Be a bit paranoid here, some perf.data file came with | 459 | * Be a bit paranoid here, some perf.data file came with |
482 | * a zero sized synthesized MMAP event for the kernel. | 460 | * a zero sized synthesized MMAP event for the kernel. |
@@ -485,8 +463,8 @@ static void event_set_kernel_mmap_len(struct map **maps, event_t *self) | |||
485 | maps[MAP__FUNCTION]->end = ~0ULL; | 463 | maps[MAP__FUNCTION]->end = ~0ULL; |
486 | } | 464 | } |
487 | 465 | ||
488 | static int event__process_kernel_mmap(event_t *self, | 466 | static int perf_event__process_kernel_mmap(union perf_event *event, |
489 | struct perf_session *session) | 467 | struct perf_session *session) |
490 | { | 468 | { |
491 | struct map *map; | 469 | struct map *map; |
492 | char kmmap_prefix[PATH_MAX]; | 470 | char kmmap_prefix[PATH_MAX]; |
@@ -494,9 +472,9 @@ static int event__process_kernel_mmap(event_t *self, | |||
494 | enum dso_kernel_type kernel_type; | 472 | enum dso_kernel_type kernel_type; |
495 | bool is_kernel_mmap; | 473 | bool is_kernel_mmap; |
496 | 474 | ||
497 | machine = perf_session__findnew_machine(session, self->mmap.pid); | 475 | machine = perf_session__findnew_machine(session, event->mmap.pid); |
498 | if (!machine) { | 476 | if (!machine) { |
499 | pr_err("Can't find id %d's machine\n", self->mmap.pid); | 477 | pr_err("Can't find id %d's machine\n", event->mmap.pid); |
500 | goto out_problem; | 478 | goto out_problem; |
501 | } | 479 | } |
502 | 480 | ||
@@ -506,17 +484,17 @@ static int event__process_kernel_mmap(event_t *self, | |||
506 | else | 484 | else |
507 | kernel_type = DSO_TYPE_GUEST_KERNEL; | 485 | kernel_type = DSO_TYPE_GUEST_KERNEL; |
508 | 486 | ||
509 | is_kernel_mmap = memcmp(self->mmap.filename, | 487 | is_kernel_mmap = memcmp(event->mmap.filename, |
510 | kmmap_prefix, | 488 | kmmap_prefix, |
511 | strlen(kmmap_prefix)) == 0; | 489 | strlen(kmmap_prefix)) == 0; |
512 | if (self->mmap.filename[0] == '/' || | 490 | if (event->mmap.filename[0] == '/' || |
513 | (!is_kernel_mmap && self->mmap.filename[0] == '[')) { | 491 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { |
514 | 492 | ||
515 | char short_module_name[1024]; | 493 | char short_module_name[1024]; |
516 | char *name, *dot; | 494 | char *name, *dot; |
517 | 495 | ||
518 | if (self->mmap.filename[0] == '/') { | 496 | if (event->mmap.filename[0] == '/') { |
519 | name = strrchr(self->mmap.filename, '/'); | 497 | name = strrchr(event->mmap.filename, '/'); |
520 | if (name == NULL) | 498 | if (name == NULL) |
521 | goto out_problem; | 499 | goto out_problem; |
522 | 500 | ||
@@ -528,10 +506,10 @@ static int event__process_kernel_mmap(event_t *self, | |||
528 | "[%.*s]", (int)(dot - name), name); | 506 | "[%.*s]", (int)(dot - name), name); |
529 | strxfrchar(short_module_name, '-', '_'); | 507 | strxfrchar(short_module_name, '-', '_'); |
530 | } else | 508 | } else |
531 | strcpy(short_module_name, self->mmap.filename); | 509 | strcpy(short_module_name, event->mmap.filename); |
532 | 510 | ||
533 | map = machine__new_module(machine, self->mmap.start, | 511 | map = machine__new_module(machine, event->mmap.start, |
534 | self->mmap.filename); | 512 | event->mmap.filename); |
535 | if (map == NULL) | 513 | if (map == NULL) |
536 | goto out_problem; | 514 | goto out_problem; |
537 | 515 | ||
@@ -541,9 +519,9 @@ static int event__process_kernel_mmap(event_t *self, | |||
541 | 519 | ||
542 | map->dso->short_name = name; | 520 | map->dso->short_name = name; |
543 | map->dso->sname_alloc = 1; | 521 | map->dso->sname_alloc = 1; |
544 | map->end = map->start + self->mmap.len; | 522 | map->end = map->start + event->mmap.len; |
545 | } else if (is_kernel_mmap) { | 523 | } else if (is_kernel_mmap) { |
546 | const char *symbol_name = (self->mmap.filename + | 524 | const char *symbol_name = (event->mmap.filename + |
547 | strlen(kmmap_prefix)); | 525 | strlen(kmmap_prefix)); |
548 | /* | 526 | /* |
549 | * Should be there already, from the build-id table in | 527 | * Should be there already, from the build-id table in |
@@ -558,10 +536,10 @@ static int event__process_kernel_mmap(event_t *self, | |||
558 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 536 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
559 | goto out_problem; | 537 | goto out_problem; |
560 | 538 | ||
561 | event_set_kernel_mmap_len(machine->vmlinux_maps, self); | 539 | perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); |
562 | perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | 540 | perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, |
563 | symbol_name, | 541 | symbol_name, |
564 | self->mmap.pgoff); | 542 | event->mmap.pgoff); |
565 | if (machine__is_default_guest(machine)) { | 543 | if (machine__is_default_guest(machine)) { |
566 | /* | 544 | /* |
567 | * preload dso of guest kernel and modules | 545 | * preload dso of guest kernel and modules |
@@ -575,22 +553,23 @@ out_problem: | |||
575 | return -1; | 553 | return -1; |
576 | } | 554 | } |
577 | 555 | ||
578 | int event__process_mmap(event_t *self, struct sample_data *sample __used, | 556 | int perf_event__process_mmap(union perf_event *event, |
579 | struct perf_session *session) | 557 | struct perf_sample *sample __used, |
558 | struct perf_session *session) | ||
580 | { | 559 | { |
581 | struct machine *machine; | 560 | struct machine *machine; |
582 | struct thread *thread; | 561 | struct thread *thread; |
583 | struct map *map; | 562 | struct map *map; |
584 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 563 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
585 | int ret = 0; | 564 | int ret = 0; |
586 | 565 | ||
587 | dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", | 566 | dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", |
588 | self->mmap.pid, self->mmap.tid, self->mmap.start, | 567 | event->mmap.pid, event->mmap.tid, event->mmap.start, |
589 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | 568 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
590 | 569 | ||
591 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | 570 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || |
592 | cpumode == PERF_RECORD_MISC_KERNEL) { | 571 | cpumode == PERF_RECORD_MISC_KERNEL) { |
593 | ret = event__process_kernel_mmap(self, session); | 572 | ret = perf_event__process_kernel_mmap(event, session); |
594 | if (ret < 0) | 573 | if (ret < 0) |
595 | goto out_problem; | 574 | goto out_problem; |
596 | return 0; | 575 | return 0; |
@@ -599,12 +578,12 @@ int event__process_mmap(event_t *self, struct sample_data *sample __used, | |||
599 | machine = perf_session__find_host_machine(session); | 578 | machine = perf_session__find_host_machine(session); |
600 | if (machine == NULL) | 579 | if (machine == NULL) |
601 | goto out_problem; | 580 | goto out_problem; |
602 | thread = perf_session__findnew(session, self->mmap.pid); | 581 | thread = perf_session__findnew(session, event->mmap.pid); |
603 | if (thread == NULL) | 582 | if (thread == NULL) |
604 | goto out_problem; | 583 | goto out_problem; |
605 | map = map__new(&machine->user_dsos, self->mmap.start, | 584 | map = map__new(&machine->user_dsos, event->mmap.start, |
606 | self->mmap.len, self->mmap.pgoff, | 585 | event->mmap.len, event->mmap.pgoff, |
607 | self->mmap.pid, self->mmap.filename, | 586 | event->mmap.pid, event->mmap.filename, |
608 | MAP__FUNCTION); | 587 | MAP__FUNCTION); |
609 | if (map == NULL) | 588 | if (map == NULL) |
610 | goto out_problem; | 589 | goto out_problem; |
@@ -617,16 +596,17 @@ out_problem: | |||
617 | return 0; | 596 | return 0; |
618 | } | 597 | } |
619 | 598 | ||
620 | int event__process_task(event_t *self, struct sample_data *sample __used, | 599 | int perf_event__process_task(union perf_event *event, |
621 | struct perf_session *session) | 600 | struct perf_sample *sample __used, |
601 | struct perf_session *session) | ||
622 | { | 602 | { |
623 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 603 | struct thread *thread = perf_session__findnew(session, event->fork.tid); |
624 | struct thread *parent = perf_session__findnew(session, self->fork.ptid); | 604 | struct thread *parent = perf_session__findnew(session, event->fork.ptid); |
625 | 605 | ||
626 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 606 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
627 | self->fork.ppid, self->fork.ptid); | 607 | event->fork.ppid, event->fork.ptid); |
628 | 608 | ||
629 | if (self->header.type == PERF_RECORD_EXIT) { | 609 | if (event->header.type == PERF_RECORD_EXIT) { |
630 | perf_session__remove_thread(session, thread); | 610 | perf_session__remove_thread(session, thread); |
631 | return 0; | 611 | return 0; |
632 | } | 612 | } |
@@ -640,20 +620,22 @@ int event__process_task(event_t *self, struct sample_data *sample __used, | |||
640 | return 0; | 620 | return 0; |
641 | } | 621 | } |
642 | 622 | ||
643 | int event__process(event_t *event, struct sample_data *sample, | 623 | int perf_event__process(union perf_event *event, struct perf_sample *sample, |
644 | struct perf_session *session) | 624 | struct perf_session *session) |
645 | { | 625 | { |
646 | switch (event->header.type) { | 626 | switch (event->header.type) { |
647 | case PERF_RECORD_COMM: | 627 | case PERF_RECORD_COMM: |
648 | event__process_comm(event, sample, session); | 628 | perf_event__process_comm(event, sample, session); |
649 | break; | 629 | break; |
650 | case PERF_RECORD_MMAP: | 630 | case PERF_RECORD_MMAP: |
651 | event__process_mmap(event, sample, session); | 631 | perf_event__process_mmap(event, sample, session); |
652 | break; | 632 | break; |
653 | case PERF_RECORD_FORK: | 633 | case PERF_RECORD_FORK: |
654 | case PERF_RECORD_EXIT: | 634 | case PERF_RECORD_EXIT: |
655 | event__process_task(event, sample, session); | 635 | perf_event__process_task(event, sample, session); |
656 | break; | 636 | break; |
637 | case PERF_RECORD_LOST: | ||
638 | perf_event__process_lost(event, sample, session); | ||
657 | default: | 639 | default: |
658 | break; | 640 | break; |
659 | } | 641 | } |
@@ -750,24 +732,14 @@ void thread__find_addr_location(struct thread *self, | |||
750 | al->sym = NULL; | 732 | al->sym = NULL; |
751 | } | 733 | } |
752 | 734 | ||
753 | static void dso__calc_col_width(struct dso *self, struct hists *hists) | 735 | int perf_event__preprocess_sample(const union perf_event *event, |
754 | { | 736 | struct perf_session *session, |
755 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 737 | struct addr_location *al, |
756 | (!symbol_conf.dso_list || | 738 | struct perf_sample *sample, |
757 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | 739 | symbol_filter_t filter) |
758 | u16 slen = dso__name_len(self); | ||
759 | hists__new_col_len(hists, HISTC_DSO, slen); | ||
760 | } | ||
761 | |||
762 | self->slen_calculated = 1; | ||
763 | } | ||
764 | |||
765 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
766 | struct addr_location *al, struct sample_data *data, | ||
767 | symbol_filter_t filter) | ||
768 | { | 740 | { |
769 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 741 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
770 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | 742 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
771 | 743 | ||
772 | if (thread == NULL) | 744 | if (thread == NULL) |
773 | return -1; | 745 | return -1; |
@@ -789,12 +761,12 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
789 | machine__create_kernel_maps(&session->host_machine); | 761 | machine__create_kernel_maps(&session->host_machine); |
790 | 762 | ||
791 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | 763 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
792 | self->ip.pid, self->ip.ip, al); | 764 | event->ip.pid, event->ip.ip, al); |
793 | dump_printf(" ...... dso: %s\n", | 765 | dump_printf(" ...... dso: %s\n", |
794 | al->map ? al->map->dso->long_name : | 766 | al->map ? al->map->dso->long_name : |
795 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 767 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
796 | al->sym = NULL; | 768 | al->sym = NULL; |
797 | al->cpu = data->cpu; | 769 | al->cpu = sample->cpu; |
798 | 770 | ||
799 | if (al->map) { | 771 | if (al->map) { |
800 | if (symbol_conf.dso_list && | 772 | if (symbol_conf.dso_list && |
@@ -805,23 +777,8 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
805 | strlist__has_entry(symbol_conf.dso_list, | 777 | strlist__has_entry(symbol_conf.dso_list, |
806 | al->map->dso->long_name))))) | 778 | al->map->dso->long_name))))) |
807 | goto out_filtered; | 779 | goto out_filtered; |
808 | /* | ||
809 | * We have to do this here as we may have a dso with no symbol | ||
810 | * hit that has a name longer than the ones with symbols | ||
811 | * sampled. | ||
812 | */ | ||
813 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | ||
814 | dso__calc_col_width(al->map->dso, &session->hists); | ||
815 | 780 | ||
816 | al->sym = map__find_symbol(al->map, al->addr, filter); | 781 | al->sym = map__find_symbol(al->map, al->addr, filter); |
817 | } else { | ||
818 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
819 | |||
820 | if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && | ||
821 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
822 | !symbol_conf.dso_list) | ||
823 | hists__set_col_len(&session->hists, HISTC_DSO, | ||
824 | unresolved_col_width); | ||
825 | } | 782 | } |
826 | 783 | ||
827 | if (symbol_conf.sym_list && al->sym && | 784 | if (symbol_conf.sym_list && al->sym && |
@@ -834,128 +791,3 @@ out_filtered: | |||
834 | al->filtered = true; | 791 | al->filtered = true; |
835 | return 0; | 792 | return 0; |
836 | } | 793 | } |
837 | |||
838 | static int event__parse_id_sample(const event_t *event, | ||
839 | struct perf_session *session, | ||
840 | struct sample_data *sample) | ||
841 | { | ||
842 | const u64 *array; | ||
843 | u64 type; | ||
844 | |||
845 | sample->cpu = sample->pid = sample->tid = -1; | ||
846 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
847 | |||
848 | if (!session->sample_id_all) | ||
849 | return 0; | ||
850 | |||
851 | array = event->sample.array; | ||
852 | array += ((event->header.size - | ||
853 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
854 | type = session->sample_type; | ||
855 | |||
856 | if (type & PERF_SAMPLE_CPU) { | ||
857 | u32 *p = (u32 *)array; | ||
858 | sample->cpu = *p; | ||
859 | array--; | ||
860 | } | ||
861 | |||
862 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
863 | sample->stream_id = *array; | ||
864 | array--; | ||
865 | } | ||
866 | |||
867 | if (type & PERF_SAMPLE_ID) { | ||
868 | sample->id = *array; | ||
869 | array--; | ||
870 | } | ||
871 | |||
872 | if (type & PERF_SAMPLE_TIME) { | ||
873 | sample->time = *array; | ||
874 | array--; | ||
875 | } | ||
876 | |||
877 | if (type & PERF_SAMPLE_TID) { | ||
878 | u32 *p = (u32 *)array; | ||
879 | sample->pid = p[0]; | ||
880 | sample->tid = p[1]; | ||
881 | } | ||
882 | |||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
887 | struct sample_data *data) | ||
888 | { | ||
889 | const u64 *array; | ||
890 | u64 type; | ||
891 | |||
892 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
893 | return event__parse_id_sample(event, session, data); | ||
894 | |||
895 | array = event->sample.array; | ||
896 | type = session->sample_type; | ||
897 | |||
898 | if (type & PERF_SAMPLE_IP) { | ||
899 | data->ip = event->ip.ip; | ||
900 | array++; | ||
901 | } | ||
902 | |||
903 | if (type & PERF_SAMPLE_TID) { | ||
904 | u32 *p = (u32 *)array; | ||
905 | data->pid = p[0]; | ||
906 | data->tid = p[1]; | ||
907 | array++; | ||
908 | } | ||
909 | |||
910 | if (type & PERF_SAMPLE_TIME) { | ||
911 | data->time = *array; | ||
912 | array++; | ||
913 | } | ||
914 | |||
915 | if (type & PERF_SAMPLE_ADDR) { | ||
916 | data->addr = *array; | ||
917 | array++; | ||
918 | } | ||
919 | |||
920 | data->id = -1ULL; | ||
921 | if (type & PERF_SAMPLE_ID) { | ||
922 | data->id = *array; | ||
923 | array++; | ||
924 | } | ||
925 | |||
926 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
927 | data->stream_id = *array; | ||
928 | array++; | ||
929 | } | ||
930 | |||
931 | if (type & PERF_SAMPLE_CPU) { | ||
932 | u32 *p = (u32 *)array; | ||
933 | data->cpu = *p; | ||
934 | array++; | ||
935 | } else | ||
936 | data->cpu = -1; | ||
937 | |||
938 | if (type & PERF_SAMPLE_PERIOD) { | ||
939 | data->period = *array; | ||
940 | array++; | ||
941 | } | ||
942 | |||
943 | if (type & PERF_SAMPLE_READ) { | ||
944 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
945 | return -1; | ||
946 | } | ||
947 | |||
948 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
949 | data->callchain = (struct ip_callchain *)array; | ||
950 | array += 1 + data->callchain->nr; | ||
951 | } | ||
952 | |||
953 | if (type & PERF_SAMPLE_RAW) { | ||
954 | u32 *p = (u32 *)array; | ||
955 | data->raw_size = *p; | ||
956 | p++; | ||
957 | data->raw_data = p; | ||
958 | } | ||
959 | |||
960 | return 0; | ||
961 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index cc7b52f9b492..9c35170fb379 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -61,7 +61,7 @@ struct sample_event { | |||
61 | u64 array[]; | 61 | u64 array[]; |
62 | }; | 62 | }; |
63 | 63 | ||
64 | struct sample_data { | 64 | struct perf_sample { |
65 | u64 ip; | 65 | u64 ip; |
66 | u32 pid, tid; | 66 | u32 pid, tid; |
67 | u64 time; | 67 | u64 time; |
@@ -117,7 +117,7 @@ struct tracing_data_event { | |||
117 | u32 size; | 117 | u32 size; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | typedef union event_union { | 120 | union perf_event { |
121 | struct perf_event_header header; | 121 | struct perf_event_header header; |
122 | struct ip_event ip; | 122 | struct ip_event ip; |
123 | struct mmap_event mmap; | 123 | struct mmap_event mmap; |
@@ -130,50 +130,54 @@ typedef union event_union { | |||
130 | struct event_type_event event_type; | 130 | struct event_type_event event_type; |
131 | struct tracing_data_event tracing_data; | 131 | struct tracing_data_event tracing_data; |
132 | struct build_id_event build_id; | 132 | struct build_id_event build_id; |
133 | } event_t; | 133 | }; |
134 | 134 | ||
135 | void event__print_totals(void); | 135 | void perf_event__print_totals(void); |
136 | 136 | ||
137 | struct perf_session; | 137 | struct perf_session; |
138 | struct thread_map; | 138 | struct thread_map; |
139 | 139 | ||
140 | typedef int (*event__handler_synth_t)(event_t *event, | 140 | typedef int (*perf_event__handler_synth_t)(union perf_event *event, |
141 | struct perf_session *session); | ||
142 | typedef int (*perf_event__handler_t)(union perf_event *event, | ||
143 | struct perf_sample *sample, | ||
141 | struct perf_session *session); | 144 | struct perf_session *session); |
142 | typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, | 145 | |
143 | struct perf_session *session); | 146 | int perf_event__synthesize_thread_map(struct thread_map *threads, |
144 | 147 | perf_event__handler_t process, | |
145 | int event__synthesize_thread_map(struct thread_map *threads, | 148 | struct perf_session *session); |
146 | event__handler_t process, | 149 | int perf_event__synthesize_threads(perf_event__handler_t process, |
147 | struct perf_session *session); | 150 | struct perf_session *session); |
148 | int event__synthesize_threads(event__handler_t process, | 151 | int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, |
149 | struct perf_session *session); | 152 | struct perf_session *session, |
150 | int event__synthesize_kernel_mmap(event__handler_t process, | 153 | struct machine *machine, |
151 | struct perf_session *session, | 154 | const char *symbol_name); |
152 | struct machine *machine, | 155 | |
153 | const char *symbol_name); | 156 | int perf_event__synthesize_modules(perf_event__handler_t process, |
154 | 157 | struct perf_session *session, | |
155 | int event__synthesize_modules(event__handler_t process, | 158 | struct machine *machine); |
156 | struct perf_session *session, | 159 | |
157 | struct machine *machine); | 160 | int perf_event__process_comm(union perf_event *event, struct perf_sample *sample, |
158 | 161 | struct perf_session *session); | |
159 | int event__process_comm(event_t *self, struct sample_data *sample, | 162 | int perf_event__process_lost(union perf_event *event, struct perf_sample *sample, |
160 | struct perf_session *session); | 163 | struct perf_session *session); |
161 | int event__process_lost(event_t *self, struct sample_data *sample, | 164 | int perf_event__process_mmap(union perf_event *event, struct perf_sample *sample, |
162 | struct perf_session *session); | 165 | struct perf_session *session); |
163 | int event__process_mmap(event_t *self, struct sample_data *sample, | 166 | int perf_event__process_task(union perf_event *event, struct perf_sample *sample, |
164 | struct perf_session *session); | 167 | struct perf_session *session); |
165 | int event__process_task(event_t *self, struct sample_data *sample, | 168 | int perf_event__process(union perf_event *event, struct perf_sample *sample, |
166 | struct perf_session *session); | 169 | struct perf_session *session); |
167 | int event__process(event_t *event, struct sample_data *sample, | ||
168 | struct perf_session *session); | ||
169 | 170 | ||
170 | struct addr_location; | 171 | struct addr_location; |
171 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 172 | int perf_event__preprocess_sample(const union perf_event *self, |
172 | struct addr_location *al, struct sample_data *data, | 173 | struct perf_session *session, |
173 | symbol_filter_t filter); | 174 | struct addr_location *al, |
174 | int event__parse_sample(const event_t *event, struct perf_session *session, | 175 | struct perf_sample *sample, |
175 | struct sample_data *sample); | 176 | symbol_filter_t filter); |
177 | |||
178 | const char *perf_event__name(unsigned int id); | ||
176 | 179 | ||
177 | const char *event__get_event_name(unsigned int id); | 180 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
181 | bool sample_id_all, struct perf_sample *sample); | ||
178 | 182 | ||
179 | #endif /* __PERF_RECORD_H */ | 183 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 000000000000..d852cefa20de --- /dev/null +++ b/tools/perf/util/evlist.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include <poll.h> | ||
10 | #include "cpumap.h" | ||
11 | #include "thread_map.h" | ||
12 | #include "evlist.h" | ||
13 | #include "evsel.h" | ||
14 | #include "util.h" | ||
15 | |||
16 | #include <sys/mman.h> | ||
17 | |||
18 | #include <linux/bitops.h> | ||
19 | #include <linux/hash.h> | ||
20 | |||
21 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
22 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | ||
23 | |||
24 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
25 | struct thread_map *threads) | ||
26 | { | ||
27 | int i; | ||
28 | |||
29 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | ||
30 | INIT_HLIST_HEAD(&evlist->heads[i]); | ||
31 | INIT_LIST_HEAD(&evlist->entries); | ||
32 | perf_evlist__set_maps(evlist, cpus, threads); | ||
33 | } | ||
34 | |||
35 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | ||
36 | struct thread_map *threads) | ||
37 | { | ||
38 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | ||
39 | |||
40 | if (evlist != NULL) | ||
41 | perf_evlist__init(evlist, cpus, threads); | ||
42 | |||
43 | return evlist; | ||
44 | } | ||
45 | |||
46 | static void perf_evlist__purge(struct perf_evlist *evlist) | ||
47 | { | ||
48 | struct perf_evsel *pos, *n; | ||
49 | |||
50 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | ||
51 | list_del_init(&pos->node); | ||
52 | perf_evsel__delete(pos); | ||
53 | } | ||
54 | |||
55 | evlist->nr_entries = 0; | ||
56 | } | ||
57 | |||
58 | void perf_evlist__exit(struct perf_evlist *evlist) | ||
59 | { | ||
60 | free(evlist->mmap); | ||
61 | free(evlist->pollfd); | ||
62 | evlist->mmap = NULL; | ||
63 | evlist->pollfd = NULL; | ||
64 | } | ||
65 | |||
66 | void perf_evlist__delete(struct perf_evlist *evlist) | ||
67 | { | ||
68 | perf_evlist__purge(evlist); | ||
69 | perf_evlist__exit(evlist); | ||
70 | free(evlist); | ||
71 | } | ||
72 | |||
73 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | ||
74 | { | ||
75 | list_add_tail(&entry->node, &evlist->entries); | ||
76 | ++evlist->nr_entries; | ||
77 | } | ||
78 | |||
79 | int perf_evlist__add_default(struct perf_evlist *evlist) | ||
80 | { | ||
81 | struct perf_event_attr attr = { | ||
82 | .type = PERF_TYPE_HARDWARE, | ||
83 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
84 | }; | ||
85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | ||
86 | |||
87 | if (evsel == NULL) | ||
88 | return -ENOMEM; | ||
89 | |||
90 | perf_evlist__add(evlist, evsel); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | ||
95 | { | ||
96 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | ||
97 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | ||
98 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | ||
99 | } | ||
100 | |||
101 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
102 | { | ||
103 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
104 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
105 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
106 | evlist->nr_fds++; | ||
107 | } | ||
108 | |||
109 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | ||
110 | struct perf_evsel *evsel, | ||
111 | int cpu, int thread, u64 id) | ||
112 | { | ||
113 | int hash; | ||
114 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | ||
115 | |||
116 | sid->id = id; | ||
117 | sid->evsel = evsel; | ||
118 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
119 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
120 | } | ||
121 | |||
122 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
123 | int cpu, int thread, u64 id) | ||
124 | { | ||
125 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); | ||
126 | evsel->id[evsel->ids++] = id; | ||
127 | } | ||
128 | |||
129 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | ||
130 | struct perf_evsel *evsel, | ||
131 | int cpu, int thread, int fd) | ||
132 | { | ||
133 | u64 read_data[4] = { 0, }; | ||
134 | int id_idx = 1; /* The first entry is the counter value */ | ||
135 | |||
136 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
137 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
138 | return -1; | ||
139 | |||
140 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
141 | ++id_idx; | ||
142 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
143 | ++id_idx; | ||
144 | |||
145 | perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
150 | { | ||
151 | struct hlist_head *head; | ||
152 | struct hlist_node *pos; | ||
153 | struct perf_sample_id *sid; | ||
154 | int hash; | ||
155 | |||
156 | if (evlist->nr_entries == 1) | ||
157 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
158 | |||
159 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
160 | head = &evlist->heads[hash]; | ||
161 | |||
162 | hlist_for_each_entry(sid, pos, head, node) | ||
163 | if (sid->id == id) | ||
164 | return sid->evsel; | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) | ||
169 | { | ||
170 | /* XXX Move this to perf.c, making it generally available */ | ||
171 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
172 | struct perf_mmap *md = &evlist->mmap[cpu]; | ||
173 | unsigned int head = perf_mmap__read_head(md); | ||
174 | unsigned int old = md->prev; | ||
175 | unsigned char *data = md->base + page_size; | ||
176 | union perf_event *event = NULL; | ||
177 | |||
178 | if (evlist->overwrite) { | ||
179 | /* | ||
180 | * If we're further behind than half the buffer, there's a chance | ||
181 | * the writer will bite our tail and mess up the samples under us. | ||
182 | * | ||
183 | * If we somehow ended up ahead of the head, we got messed up. | ||
184 | * | ||
185 | * In either case, truncate and restart at head. | ||
186 | */ | ||
187 | int diff = head - old; | ||
188 | if (diff > md->mask / 2 || diff < 0) { | ||
189 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
190 | |||
191 | /* | ||
192 | * head points to a known good entry, start there. | ||
193 | */ | ||
194 | old = head; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | if (old != head) { | ||
199 | size_t size; | ||
200 | |||
201 | event = (union perf_event *)&data[old & md->mask]; | ||
202 | size = event->header.size; | ||
203 | |||
204 | /* | ||
205 | * Event straddles the mmap boundary -- header should always | ||
206 | * be inside due to u64 alignment of output. | ||
207 | */ | ||
208 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
209 | unsigned int offset = old; | ||
210 | unsigned int len = min(sizeof(*event), size), cpy; | ||
211 | void *dst = &evlist->event_copy; | ||
212 | |||
213 | do { | ||
214 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
215 | memcpy(dst, &data[offset & md->mask], cpy); | ||
216 | offset += cpy; | ||
217 | dst += cpy; | ||
218 | len -= cpy; | ||
219 | } while (len); | ||
220 | |||
221 | event = &evlist->event_copy; | ||
222 | } | ||
223 | |||
224 | old += size; | ||
225 | } | ||
226 | |||
227 | md->prev = old; | ||
228 | |||
229 | if (!evlist->overwrite) | ||
230 | perf_mmap__write_tail(md, old); | ||
231 | |||
232 | return event; | ||
233 | } | ||
234 | |||
235 | void perf_evlist__munmap(struct perf_evlist *evlist) | ||
236 | { | ||
237 | int cpu; | ||
238 | |||
239 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
240 | if (evlist->mmap[cpu].base != NULL) { | ||
241 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
242 | evlist->mmap[cpu].base = NULL; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | ||
248 | { | ||
249 | evlist->mmap = zalloc(evlist->cpus->nr * sizeof(struct perf_mmap)); | ||
250 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
251 | } | ||
252 | |||
253 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, | ||
254 | int mask, int fd) | ||
255 | { | ||
256 | evlist->mmap[cpu].prev = 0; | ||
257 | evlist->mmap[cpu].mask = mask; | ||
258 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
259 | MAP_SHARED, fd, 0); | ||
260 | if (evlist->mmap[cpu].base == MAP_FAILED) | ||
261 | return -1; | ||
262 | |||
263 | perf_evlist__add_pollfd(evlist, fd); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
268 | * | ||
269 | * @evlist - list of events | ||
270 | * @pages - map length in pages | ||
271 | * @overwrite - overwrite older events? | ||
272 | * | ||
273 | * If overwrite is false the user needs to signal event consuption using: | ||
274 | * | ||
275 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
276 | * unsigned int head = perf_mmap__read_head(m); | ||
277 | * | ||
278 | * perf_mmap__write_tail(m, head) | ||
279 | * | ||
280 | * Using perf_evlist__read_on_cpu does this automatically. | ||
281 | */ | ||
282 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) | ||
283 | { | ||
284 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
285 | int mask = pages * page_size - 1, cpu; | ||
286 | struct perf_evsel *first_evsel, *evsel; | ||
287 | const struct cpu_map *cpus = evlist->cpus; | ||
288 | const struct thread_map *threads = evlist->threads; | ||
289 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
290 | |||
291 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | ||
292 | return -ENOMEM; | ||
293 | |||
294 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | ||
295 | return -ENOMEM; | ||
296 | |||
297 | evlist->overwrite = overwrite; | ||
298 | evlist->mmap_len = (pages + 1) * page_size; | ||
299 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
300 | |||
301 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
302 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
303 | evsel->sample_id == NULL && | ||
304 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
305 | return -ENOMEM; | ||
306 | |||
307 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
308 | for (thread = 0; thread < threads->nr; thread++) { | ||
309 | int fd = FD(evsel, cpu, thread); | ||
310 | |||
311 | if (evsel->idx || thread) { | ||
312 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
313 | FD(first_evsel, cpu, 0)) != 0) | ||
314 | goto out_unmap; | ||
315 | } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) | ||
316 | goto out_unmap; | ||
317 | |||
318 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
319 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
320 | goto out_unmap; | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | |||
325 | return 0; | ||
326 | |||
327 | out_unmap: | ||
328 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
329 | if (evlist->mmap[cpu].base != NULL) { | ||
330 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
331 | evlist->mmap[cpu].base = NULL; | ||
332 | } | ||
333 | } | ||
334 | return -1; | ||
335 | } | ||
336 | |||
337 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | ||
338 | pid_t target_tid, const char *cpu_list) | ||
339 | { | ||
340 | evlist->threads = thread_map__new(target_pid, target_tid); | ||
341 | |||
342 | if (evlist->threads == NULL) | ||
343 | return -1; | ||
344 | |||
345 | if (target_tid != -1) | ||
346 | evlist->cpus = cpu_map__dummy_new(); | ||
347 | else | ||
348 | evlist->cpus = cpu_map__new(cpu_list); | ||
349 | |||
350 | if (evlist->cpus == NULL) | ||
351 | goto out_delete_threads; | ||
352 | |||
353 | return 0; | ||
354 | |||
355 | out_delete_threads: | ||
356 | thread_map__delete(evlist->threads); | ||
357 | return -1; | ||
358 | } | ||
359 | |||
360 | void perf_evlist__delete_maps(struct perf_evlist *evlist) | ||
361 | { | ||
362 | cpu_map__delete(evlist->cpus); | ||
363 | thread_map__delete(evlist->threads); | ||
364 | evlist->cpus = NULL; | ||
365 | evlist->threads = NULL; | ||
366 | } | ||
367 | |||
368 | int perf_evlist__set_filters(struct perf_evlist *evlist) | ||
369 | { | ||
370 | const struct thread_map *threads = evlist->threads; | ||
371 | const struct cpu_map *cpus = evlist->cpus; | ||
372 | struct perf_evsel *evsel; | ||
373 | char *filter; | ||
374 | int thread; | ||
375 | int cpu; | ||
376 | int err; | ||
377 | int fd; | ||
378 | |||
379 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
380 | filter = evsel->filter; | ||
381 | if (!filter) | ||
382 | continue; | ||
383 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
384 | for (thread = 0; thread < threads->nr; thread++) { | ||
385 | fd = FD(evsel, cpu, thread); | ||
386 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | ||
387 | if (err) | ||
388 | return err; | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | |||
393 | return 0; | ||
394 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 000000000000..8b1cb7a4c5f1 --- /dev/null +++ b/tools/perf/util/evlist.h | |||
@@ -0,0 +1,68 @@ | |||
1 | #ifndef __PERF_EVLIST_H | ||
2 | #define __PERF_EVLIST_H 1 | ||
3 | |||
4 | #include <linux/list.h> | ||
5 | #include "../perf.h" | ||
6 | #include "event.h" | ||
7 | |||
8 | struct pollfd; | ||
9 | struct thread_map; | ||
10 | struct cpu_map; | ||
11 | |||
12 | #define PERF_EVLIST__HLIST_BITS 8 | ||
13 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | ||
14 | |||
15 | struct perf_evlist { | ||
16 | struct list_head entries; | ||
17 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | ||
18 | int nr_entries; | ||
19 | int nr_fds; | ||
20 | int mmap_len; | ||
21 | bool overwrite; | ||
22 | union perf_event event_copy; | ||
23 | struct perf_mmap *mmap; | ||
24 | struct pollfd *pollfd; | ||
25 | struct thread_map *threads; | ||
26 | struct cpu_map *cpus; | ||
27 | }; | ||
28 | |||
29 | struct perf_evsel; | ||
30 | |||
31 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | ||
32 | struct thread_map *threads); | ||
33 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
34 | struct thread_map *threads); | ||
35 | void perf_evlist__exit(struct perf_evlist *evlist); | ||
36 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
37 | |||
38 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | ||
39 | int perf_evlist__add_default(struct perf_evlist *evlist); | ||
40 | |||
41 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
42 | int cpu, int thread, u64 id); | ||
43 | |||
44 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | ||
45 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | ||
46 | |||
47 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | ||
48 | |||
49 | union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); | ||
50 | |||
51 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | ||
52 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | ||
53 | void perf_evlist__munmap(struct perf_evlist *evlist); | ||
54 | |||
55 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | ||
56 | struct cpu_map *cpus, | ||
57 | struct thread_map *threads) | ||
58 | { | ||
59 | evlist->cpus = cpus; | ||
60 | evlist->threads = threads; | ||
61 | } | ||
62 | |||
63 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | ||
64 | pid_t target_tid, const char *cpu_list); | ||
65 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | ||
66 | int perf_evlist__set_filters(struct perf_evlist *evlist); | ||
67 | |||
68 | #endif /* __PERF_EVLIST_H */ | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d8575d31ee6c..662596afd7f1 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | |||
1 | #include "evsel.h" | 10 | #include "evsel.h" |
2 | #include "../perf.h" | 11 | #include "evlist.h" |
3 | #include "util.h" | 12 | #include "util.h" |
4 | #include "cpumap.h" | 13 | #include "cpumap.h" |
5 | #include "thread.h" | 14 | #include "thread_map.h" |
6 | 15 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 16 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
8 | 17 | ||
18 | void perf_evsel__init(struct perf_evsel *evsel, | ||
19 | struct perf_event_attr *attr, int idx) | ||
20 | { | ||
21 | evsel->idx = idx; | ||
22 | evsel->attr = *attr; | ||
23 | INIT_LIST_HEAD(&evsel->node); | ||
24 | } | ||
25 | |||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 26 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 27 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 28 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 29 | ||
13 | if (evsel != NULL) { | 30 | if (evsel != NULL) |
14 | evsel->idx = idx; | 31 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 32 | ||
19 | return evsel; | 33 | return evsel; |
20 | } | 34 | } |
@@ -25,6 +39,22 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 39 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 40 | } |
27 | 41 | ||
42 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
43 | { | ||
44 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
45 | if (evsel->sample_id == NULL) | ||
46 | return -ENOMEM; | ||
47 | |||
48 | evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); | ||
49 | if (evsel->id == NULL) { | ||
50 | xyarray__delete(evsel->sample_id); | ||
51 | evsel->sample_id = NULL; | ||
52 | return -ENOMEM; | ||
53 | } | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 58 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 59 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 60 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +68,14 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 68 | evsel->fd = NULL; |
39 | } | 69 | } |
40 | 70 | ||
71 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
72 | { | ||
73 | xyarray__delete(evsel->sample_id); | ||
74 | evsel->sample_id = NULL; | ||
75 | free(evsel->id); | ||
76 | evsel->id = NULL; | ||
77 | } | ||
78 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 79 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 80 | { |
43 | int cpu, thread; | 81 | int cpu, thread; |
@@ -49,10 +87,19 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 87 | } |
50 | } | 88 | } |
51 | 89 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 90 | void perf_evsel__exit(struct perf_evsel *evsel) |
53 | { | 91 | { |
54 | assert(list_empty(&evsel->node)); | 92 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 93 | xyarray__delete(evsel->fd); |
94 | xyarray__delete(evsel->sample_id); | ||
95 | free(evsel->id); | ||
96 | } | ||
97 | |||
98 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
99 | { | ||
100 | perf_evsel__exit(evsel); | ||
101 | close_cgroup(evsel->cgrp); | ||
102 | free(evsel->name); | ||
56 | free(evsel); | 103 | free(evsel); |
57 | } | 104 | } |
58 | 105 | ||
@@ -128,21 +175,51 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 175 | } |
129 | 176 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 177 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 178 | struct thread_map *threads, bool group, bool inherit) |
132 | { | 179 | { |
133 | int cpu, thread; | 180 | int cpu, thread; |
181 | unsigned long flags = 0; | ||
182 | int pid = -1; | ||
134 | 183 | ||
135 | if (evsel->fd == NULL && | 184 | if (evsel->fd == NULL && |
136 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 185 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
137 | return -1; | 186 | return -1; |
138 | 187 | ||
188 | if (evsel->cgrp) { | ||
189 | flags = PERF_FLAG_PID_CGROUP; | ||
190 | pid = evsel->cgrp->fd; | ||
191 | } | ||
192 | |||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 193 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
194 | int group_fd = -1; | ||
195 | /* | ||
196 | * Don't allow mmap() of inherited per-task counters. This | ||
197 | * would create a performance issue due to all children writing | ||
198 | * to the same buffer. | ||
199 | * | ||
200 | * FIXME: | ||
201 | * Proper fix is not to pass 'inherit' to perf_evsel__open*, | ||
202 | * but a 'flags' parameter, with 'group' folded there as well, | ||
203 | * then introduce a PERF_O_{MMAP,GROUP,INHERIT} enum, and if | ||
204 | * O_MMAP is set, emit a warning if cpu < 0 and O_INHERIT is | ||
205 | * set. Lets go for the minimal fix first tho. | ||
206 | */ | ||
207 | evsel->attr.inherit = (cpus->map[cpu] >= 0) && inherit; | ||
208 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 209 | for (thread = 0; thread < threads->nr; thread++) { |
210 | |||
211 | if (!evsel->cgrp) | ||
212 | pid = threads->map[thread]; | ||
213 | |||
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 214 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 215 | pid, |
143 | cpus->map[cpu], -1, 0); | 216 | cpus->map[cpu], |
217 | group_fd, flags); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 218 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 219 | goto out_close; |
220 | |||
221 | if (group && group_fd == -1) | ||
222 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 223 | } |
147 | } | 224 | } |
148 | 225 | ||
@@ -175,10 +252,9 @@ static struct { | |||
175 | .threads = { -1, }, | 252 | .threads = { -1, }, |
176 | }; | 253 | }; |
177 | 254 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 255 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 256 | struct thread_map *threads, bool group, bool inherit) |
180 | { | 257 | { |
181 | |||
182 | if (cpus == NULL) { | 258 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 259 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 260 | cpus = &empty_cpu_map.map; |
@@ -187,15 +263,135 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 263 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 264 | threads = &empty_thread_map.map; |
189 | 265 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 266 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
191 | } | 267 | } |
192 | 268 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 269 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
270 | struct cpu_map *cpus, bool group, bool inherit) | ||
194 | { | 271 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 272 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); |
273 | } | ||
274 | |||
275 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
276 | struct thread_map *threads, bool group, bool inherit) | ||
277 | { | ||
278 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
279 | } | ||
280 | |||
281 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | ||
282 | struct perf_sample *sample) | ||
283 | { | ||
284 | const u64 *array = event->sample.array; | ||
285 | |||
286 | array += ((event->header.size - | ||
287 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
288 | |||
289 | if (type & PERF_SAMPLE_CPU) { | ||
290 | u32 *p = (u32 *)array; | ||
291 | sample->cpu = *p; | ||
292 | array--; | ||
293 | } | ||
294 | |||
295 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
296 | sample->stream_id = *array; | ||
297 | array--; | ||
298 | } | ||
299 | |||
300 | if (type & PERF_SAMPLE_ID) { | ||
301 | sample->id = *array; | ||
302 | array--; | ||
303 | } | ||
304 | |||
305 | if (type & PERF_SAMPLE_TIME) { | ||
306 | sample->time = *array; | ||
307 | array--; | ||
308 | } | ||
309 | |||
310 | if (type & PERF_SAMPLE_TID) { | ||
311 | u32 *p = (u32 *)array; | ||
312 | sample->pid = p[0]; | ||
313 | sample->tid = p[1]; | ||
314 | } | ||
315 | |||
316 | return 0; | ||
196 | } | 317 | } |
197 | 318 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 319 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
320 | bool sample_id_all, struct perf_sample *data) | ||
199 | { | 321 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 322 | const u64 *array; |
323 | |||
324 | data->cpu = data->pid = data->tid = -1; | ||
325 | data->stream_id = data->id = data->time = -1ULL; | ||
326 | |||
327 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
328 | if (!sample_id_all) | ||
329 | return 0; | ||
330 | return perf_event__parse_id_sample(event, type, data); | ||
331 | } | ||
332 | |||
333 | array = event->sample.array; | ||
334 | |||
335 | if (type & PERF_SAMPLE_IP) { | ||
336 | data->ip = event->ip.ip; | ||
337 | array++; | ||
338 | } | ||
339 | |||
340 | if (type & PERF_SAMPLE_TID) { | ||
341 | u32 *p = (u32 *)array; | ||
342 | data->pid = p[0]; | ||
343 | data->tid = p[1]; | ||
344 | array++; | ||
345 | } | ||
346 | |||
347 | if (type & PERF_SAMPLE_TIME) { | ||
348 | data->time = *array; | ||
349 | array++; | ||
350 | } | ||
351 | |||
352 | if (type & PERF_SAMPLE_ADDR) { | ||
353 | data->addr = *array; | ||
354 | array++; | ||
355 | } | ||
356 | |||
357 | data->id = -1ULL; | ||
358 | if (type & PERF_SAMPLE_ID) { | ||
359 | data->id = *array; | ||
360 | array++; | ||
361 | } | ||
362 | |||
363 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
364 | data->stream_id = *array; | ||
365 | array++; | ||
366 | } | ||
367 | |||
368 | if (type & PERF_SAMPLE_CPU) { | ||
369 | u32 *p = (u32 *)array; | ||
370 | data->cpu = *p; | ||
371 | array++; | ||
372 | } | ||
373 | |||
374 | if (type & PERF_SAMPLE_PERIOD) { | ||
375 | data->period = *array; | ||
376 | array++; | ||
377 | } | ||
378 | |||
379 | if (type & PERF_SAMPLE_READ) { | ||
380 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
381 | return -1; | ||
382 | } | ||
383 | |||
384 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
385 | data->callchain = (struct ip_callchain *)array; | ||
386 | array += 1 + data->callchain->nr; | ||
387 | } | ||
388 | |||
389 | if (type & PERF_SAMPLE_RAW) { | ||
390 | u32 *p = (u32 *)array; | ||
391 | data->raw_size = *p; | ||
392 | p++; | ||
393 | data->raw_data = p; | ||
394 | } | ||
395 | |||
396 | return 0; | ||
201 | } | 397 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a5..6710ab538342 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -6,6 +6,8 @@ | |||
6 | #include "../../../include/linux/perf_event.h" | 6 | #include "../../../include/linux/perf_event.h" |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "xyarray.h" | 8 | #include "xyarray.h" |
9 | #include "cgroup.h" | ||
10 | #include "hist.h" | ||
9 | 11 | ||
10 | struct perf_counts_values { | 12 | struct perf_counts_values { |
11 | union { | 13 | union { |
@@ -24,31 +26,66 @@ struct perf_counts { | |||
24 | struct perf_counts_values cpu[]; | 26 | struct perf_counts_values cpu[]; |
25 | }; | 27 | }; |
26 | 28 | ||
29 | struct perf_evsel; | ||
30 | |||
31 | /* | ||
32 | * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are | ||
33 | * more than one entry in the evlist. | ||
34 | */ | ||
35 | struct perf_sample_id { | ||
36 | struct hlist_node node; | ||
37 | u64 id; | ||
38 | struct perf_evsel *evsel; | ||
39 | }; | ||
40 | |||
41 | /** struct perf_evsel - event selector | ||
42 | * | ||
43 | * @name - Can be set to retain the original event name passed by the user, | ||
44 | * so that when showing results in tools such as 'perf stat', we | ||
45 | * show the name used, not some alias. | ||
46 | */ | ||
27 | struct perf_evsel { | 47 | struct perf_evsel { |
28 | struct list_head node; | 48 | struct list_head node; |
29 | struct perf_event_attr attr; | 49 | struct perf_event_attr attr; |
30 | char *filter; | 50 | char *filter; |
31 | struct xyarray *fd; | 51 | struct xyarray *fd; |
52 | struct xyarray *sample_id; | ||
53 | u64 *id; | ||
32 | struct perf_counts *counts; | 54 | struct perf_counts *counts; |
33 | int idx; | 55 | int idx; |
34 | void *priv; | 56 | int ids; |
57 | struct hists hists; | ||
58 | char *name; | ||
59 | union { | ||
60 | void *priv; | ||
61 | off_t id_offset; | ||
62 | }; | ||
63 | struct cgroup_sel *cgrp; | ||
35 | }; | 64 | }; |
36 | 65 | ||
37 | struct cpu_map; | 66 | struct cpu_map; |
38 | struct thread_map; | 67 | struct thread_map; |
68 | struct perf_evlist; | ||
39 | 69 | ||
40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 70 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
71 | void perf_evsel__init(struct perf_evsel *evsel, | ||
72 | struct perf_event_attr *attr, int idx); | ||
73 | void perf_evsel__exit(struct perf_evsel *evsel); | ||
41 | void perf_evsel__delete(struct perf_evsel *evsel); | 74 | void perf_evsel__delete(struct perf_evsel *evsel); |
42 | 75 | ||
43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 76 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
77 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
44 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 78 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 79 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
80 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 81 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
47 | 82 | ||
48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | 83 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
49 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); | 84 | struct cpu_map *cpus, bool group, bool inherit); |
50 | int perf_evsel__open(struct perf_evsel *evsel, | 85 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
51 | struct cpu_map *cpus, struct thread_map *threads); | 86 | struct thread_map *threads, bool group, bool inherit); |
87 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
88 | struct thread_map *threads, bool group, bool inherit); | ||
52 | 89 | ||
53 | #define perf_evsel__match(evsel, t, c) \ | 90 | #define perf_evsel__match(evsel, t, c) \ |
54 | (evsel->attr.type == PERF_TYPE_##t && \ | 91 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 67eeff571568..7adf4ad15d8f 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c | |||
@@ -11,31 +11,12 @@ static const char *argv0_path; | |||
11 | 11 | ||
12 | const char *system_path(const char *path) | 12 | const char *system_path(const char *path) |
13 | { | 13 | { |
14 | #ifdef RUNTIME_PREFIX | ||
15 | static const char *prefix; | ||
16 | #else | ||
17 | static const char *prefix = PREFIX; | 14 | static const char *prefix = PREFIX; |
18 | #endif | ||
19 | struct strbuf d = STRBUF_INIT; | 15 | struct strbuf d = STRBUF_INIT; |
20 | 16 | ||
21 | if (is_absolute_path(path)) | 17 | if (is_absolute_path(path)) |
22 | return path; | 18 | return path; |
23 | 19 | ||
24 | #ifdef RUNTIME_PREFIX | ||
25 | assert(argv0_path); | ||
26 | assert(is_absolute_path(argv0_path)); | ||
27 | |||
28 | if (!prefix && | ||
29 | !(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) && | ||
30 | !(prefix = strip_path_suffix(argv0_path, BINDIR)) && | ||
31 | !(prefix = strip_path_suffix(argv0_path, "perf"))) { | ||
32 | prefix = PREFIX; | ||
33 | fprintf(stderr, "RUNTIME_PREFIX requested, " | ||
34 | "but prefix computation failed. " | ||
35 | "Using static fallback '%s'.\n", prefix); | ||
36 | } | ||
37 | #endif | ||
38 | |||
39 | strbuf_addf(&d, "%s/%s", prefix, path); | 20 | strbuf_addf(&d, "%s/%s", prefix, path); |
40 | path = strbuf_detach(&d, NULL); | 21 | path = strbuf_detach(&d, NULL); |
41 | return path; | 22 | return path; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6a929e74981..e5230c0ef95b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -8,6 +8,8 @@ | |||
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | 10 | ||
11 | #include "evlist.h" | ||
12 | #include "evsel.h" | ||
11 | #include "util.h" | 13 | #include "util.h" |
12 | #include "header.h" | 14 | #include "header.h" |
13 | #include "../perf.h" | 15 | #include "../perf.h" |
@@ -18,89 +20,6 @@ | |||
18 | 20 | ||
19 | static bool no_buildid_cache = false; | 21 | static bool no_buildid_cache = false; |
20 | 22 | ||
21 | /* | ||
22 | * Create new perf.data header attribute: | ||
23 | */ | ||
24 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | ||
25 | { | ||
26 | struct perf_header_attr *self = malloc(sizeof(*self)); | ||
27 | |||
28 | if (self != NULL) { | ||
29 | self->attr = *attr; | ||
30 | self->ids = 0; | ||
31 | self->size = 1; | ||
32 | self->id = malloc(sizeof(u64)); | ||
33 | if (self->id == NULL) { | ||
34 | free(self); | ||
35 | self = NULL; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return self; | ||
40 | } | ||
41 | |||
42 | void perf_header_attr__delete(struct perf_header_attr *self) | ||
43 | { | ||
44 | free(self->id); | ||
45 | free(self); | ||
46 | } | ||
47 | |||
48 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
49 | { | ||
50 | int pos = self->ids; | ||
51 | |||
52 | self->ids++; | ||
53 | if (self->ids > self->size) { | ||
54 | int nsize = self->size * 2; | ||
55 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); | ||
56 | |||
57 | if (nid == NULL) | ||
58 | return -1; | ||
59 | |||
60 | self->size = nsize; | ||
61 | self->id = nid; | ||
62 | } | ||
63 | self->id[pos] = id; | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | int perf_header__init(struct perf_header *self) | ||
68 | { | ||
69 | self->size = 1; | ||
70 | self->attr = malloc(sizeof(void *)); | ||
71 | return self->attr == NULL ? -ENOMEM : 0; | ||
72 | } | ||
73 | |||
74 | void perf_header__exit(struct perf_header *self) | ||
75 | { | ||
76 | int i; | ||
77 | for (i = 0; i < self->attrs; ++i) | ||
78 | perf_header_attr__delete(self->attr[i]); | ||
79 | free(self->attr); | ||
80 | } | ||
81 | |||
82 | int perf_header__add_attr(struct perf_header *self, | ||
83 | struct perf_header_attr *attr) | ||
84 | { | ||
85 | if (self->frozen) | ||
86 | return -1; | ||
87 | |||
88 | if (self->attrs == self->size) { | ||
89 | int nsize = self->size * 2; | ||
90 | struct perf_header_attr **nattr; | ||
91 | |||
92 | nattr = realloc(self->attr, nsize * sizeof(void *)); | ||
93 | if (nattr == NULL) | ||
94 | return -1; | ||
95 | |||
96 | self->size = nsize; | ||
97 | self->attr = nattr; | ||
98 | } | ||
99 | |||
100 | self->attr[self->attrs++] = attr; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int event_count; | 23 | static int event_count; |
105 | static struct perf_trace_event_type *events; | 24 | static struct perf_trace_event_type *events; |
106 | 25 | ||
@@ -147,19 +66,19 @@ struct perf_file_attr { | |||
147 | struct perf_file_section ids; | 66 | struct perf_file_section ids; |
148 | }; | 67 | }; |
149 | 68 | ||
150 | void perf_header__set_feat(struct perf_header *self, int feat) | 69 | void perf_header__set_feat(struct perf_header *header, int feat) |
151 | { | 70 | { |
152 | set_bit(feat, self->adds_features); | 71 | set_bit(feat, header->adds_features); |
153 | } | 72 | } |
154 | 73 | ||
155 | void perf_header__clear_feat(struct perf_header *self, int feat) | 74 | void perf_header__clear_feat(struct perf_header *header, int feat) |
156 | { | 75 | { |
157 | clear_bit(feat, self->adds_features); | 76 | clear_bit(feat, header->adds_features); |
158 | } | 77 | } |
159 | 78 | ||
160 | bool perf_header__has_feat(const struct perf_header *self, int feat) | 79 | bool perf_header__has_feat(const struct perf_header *header, int feat) |
161 | { | 80 | { |
162 | return test_bit(feat, self->adds_features); | 81 | return test_bit(feat, header->adds_features); |
163 | } | 82 | } |
164 | 83 | ||
165 | static int do_write(int fd, const void *buf, size_t size) | 84 | static int do_write(int fd, const void *buf, size_t size) |
@@ -228,22 +147,22 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | |||
228 | return 0; | 147 | return 0; |
229 | } | 148 | } |
230 | 149 | ||
231 | static int machine__write_buildid_table(struct machine *self, int fd) | 150 | static int machine__write_buildid_table(struct machine *machine, int fd) |
232 | { | 151 | { |
233 | int err; | 152 | int err; |
234 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | 153 | u16 kmisc = PERF_RECORD_MISC_KERNEL, |
235 | umisc = PERF_RECORD_MISC_USER; | 154 | umisc = PERF_RECORD_MISC_USER; |
236 | 155 | ||
237 | if (!machine__is_host(self)) { | 156 | if (!machine__is_host(machine)) { |
238 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | 157 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; |
239 | umisc = PERF_RECORD_MISC_GUEST_USER; | 158 | umisc = PERF_RECORD_MISC_GUEST_USER; |
240 | } | 159 | } |
241 | 160 | ||
242 | err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid, | 161 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, |
243 | kmisc, fd); | 162 | kmisc, fd); |
244 | if (err == 0) | 163 | if (err == 0) |
245 | err = __dsos__write_buildid_table(&self->user_dsos, | 164 | err = __dsos__write_buildid_table(&machine->user_dsos, |
246 | self->pid, umisc, fd); | 165 | machine->pid, umisc, fd); |
247 | return err; | 166 | return err; |
248 | } | 167 | } |
249 | 168 | ||
@@ -270,11 +189,15 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
270 | const char *name, bool is_kallsyms) | 189 | const char *name, bool is_kallsyms) |
271 | { | 190 | { |
272 | const size_t size = PATH_MAX; | 191 | const size_t size = PATH_MAX; |
273 | char *realname = realpath(name, NULL), | 192 | char *realname, *filename = malloc(size), |
274 | *filename = malloc(size), | ||
275 | *linkname = malloc(size), *targetname; | 193 | *linkname = malloc(size), *targetname; |
276 | int len, err = -1; | 194 | int len, err = -1; |
277 | 195 | ||
196 | if (is_kallsyms) | ||
197 | realname = (char *)name; | ||
198 | else | ||
199 | realname = realpath(name, NULL); | ||
200 | |||
278 | if (realname == NULL || filename == NULL || linkname == NULL) | 201 | if (realname == NULL || filename == NULL || linkname == NULL) |
279 | goto out_free; | 202 | goto out_free; |
280 | 203 | ||
@@ -306,7 +229,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
306 | if (symlink(targetname, linkname) == 0) | 229 | if (symlink(targetname, linkname) == 0) |
307 | err = 0; | 230 | err = 0; |
308 | out_free: | 231 | out_free: |
309 | free(realname); | 232 | if (!is_kallsyms) |
233 | free(realname); | ||
310 | free(filename); | 234 | free(filename); |
311 | free(linkname); | 235 | free(linkname); |
312 | return err; | 236 | return err; |
@@ -361,12 +285,12 @@ out_free: | |||
361 | return err; | 285 | return err; |
362 | } | 286 | } |
363 | 287 | ||
364 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | 288 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) |
365 | { | 289 | { |
366 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | 290 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
367 | 291 | ||
368 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | 292 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), |
369 | self->long_name, debugdir, is_kallsyms); | 293 | dso->long_name, debugdir, is_kallsyms); |
370 | } | 294 | } |
371 | 295 | ||
372 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 296 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) |
@@ -381,14 +305,14 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | |||
381 | return err; | 305 | return err; |
382 | } | 306 | } |
383 | 307 | ||
384 | static int machine__cache_build_ids(struct machine *self, const char *debugdir) | 308 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
385 | { | 309 | { |
386 | int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir); | 310 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); |
387 | ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir); | 311 | ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); |
388 | return ret; | 312 | return ret; |
389 | } | 313 | } |
390 | 314 | ||
391 | static int perf_session__cache_build_ids(struct perf_session *self) | 315 | static int perf_session__cache_build_ids(struct perf_session *session) |
392 | { | 316 | { |
393 | struct rb_node *nd; | 317 | struct rb_node *nd; |
394 | int ret; | 318 | int ret; |
@@ -399,28 +323,28 @@ static int perf_session__cache_build_ids(struct perf_session *self) | |||
399 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 323 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
400 | return -1; | 324 | return -1; |
401 | 325 | ||
402 | ret = machine__cache_build_ids(&self->host_machine, debugdir); | 326 | ret = machine__cache_build_ids(&session->host_machine, debugdir); |
403 | 327 | ||
404 | for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { | 328 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
405 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 329 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
406 | ret |= machine__cache_build_ids(pos, debugdir); | 330 | ret |= machine__cache_build_ids(pos, debugdir); |
407 | } | 331 | } |
408 | return ret ? -1 : 0; | 332 | return ret ? -1 : 0; |
409 | } | 333 | } |
410 | 334 | ||
411 | static bool machine__read_build_ids(struct machine *self, bool with_hits) | 335 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) |
412 | { | 336 | { |
413 | bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits); | 337 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); |
414 | ret |= __dsos__read_build_ids(&self->user_dsos, with_hits); | 338 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); |
415 | return ret; | 339 | return ret; |
416 | } | 340 | } |
417 | 341 | ||
418 | static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits) | 342 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) |
419 | { | 343 | { |
420 | struct rb_node *nd; | 344 | struct rb_node *nd; |
421 | bool ret = machine__read_build_ids(&self->host_machine, with_hits); | 345 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); |
422 | 346 | ||
423 | for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { | 347 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
424 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 348 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
425 | ret |= machine__read_build_ids(pos, with_hits); | 349 | ret |= machine__read_build_ids(pos, with_hits); |
426 | } | 350 | } |
@@ -428,7 +352,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi | |||
428 | return ret; | 352 | return ret; |
429 | } | 353 | } |
430 | 354 | ||
431 | static int perf_header__adds_write(struct perf_header *self, int fd) | 355 | static int perf_header__adds_write(struct perf_header *header, |
356 | struct perf_evlist *evlist, int fd) | ||
432 | { | 357 | { |
433 | int nr_sections; | 358 | int nr_sections; |
434 | struct perf_session *session; | 359 | struct perf_session *session; |
@@ -437,13 +362,13 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
437 | u64 sec_start; | 362 | u64 sec_start; |
438 | int idx = 0, err; | 363 | int idx = 0, err; |
439 | 364 | ||
440 | session = container_of(self, struct perf_session, header); | 365 | session = container_of(header, struct perf_session, header); |
441 | 366 | ||
442 | if (perf_header__has_feat(self, HEADER_BUILD_ID && | 367 | if (perf_header__has_feat(header, HEADER_BUILD_ID && |
443 | !perf_session__read_build_ids(session, true))) | 368 | !perf_session__read_build_ids(session, true))) |
444 | perf_header__clear_feat(self, HEADER_BUILD_ID); | 369 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
445 | 370 | ||
446 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 371 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
447 | if (!nr_sections) | 372 | if (!nr_sections) |
448 | return 0; | 373 | return 0; |
449 | 374 | ||
@@ -453,28 +378,28 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
453 | 378 | ||
454 | sec_size = sizeof(*feat_sec) * nr_sections; | 379 | sec_size = sizeof(*feat_sec) * nr_sections; |
455 | 380 | ||
456 | sec_start = self->data_offset + self->data_size; | 381 | sec_start = header->data_offset + header->data_size; |
457 | lseek(fd, sec_start + sec_size, SEEK_SET); | 382 | lseek(fd, sec_start + sec_size, SEEK_SET); |
458 | 383 | ||
459 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | 384 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { |
460 | struct perf_file_section *trace_sec; | 385 | struct perf_file_section *trace_sec; |
461 | 386 | ||
462 | trace_sec = &feat_sec[idx++]; | 387 | trace_sec = &feat_sec[idx++]; |
463 | 388 | ||
464 | /* Write trace info */ | 389 | /* Write trace info */ |
465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 390 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
466 | read_tracing_data(fd, &evsel_list); | 391 | read_tracing_data(fd, &evlist->entries); |
467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 392 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
468 | } | 393 | } |
469 | 394 | ||
470 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | 395 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { |
471 | struct perf_file_section *buildid_sec; | 396 | struct perf_file_section *buildid_sec; |
472 | 397 | ||
473 | buildid_sec = &feat_sec[idx++]; | 398 | buildid_sec = &feat_sec[idx++]; |
474 | 399 | ||
475 | /* Write build-ids */ | 400 | /* Write build-ids */ |
476 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 401 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); |
477 | err = dsos__write_buildid_table(self, fd); | 402 | err = dsos__write_buildid_table(header, fd); |
478 | if (err < 0) { | 403 | if (err < 0) { |
479 | pr_debug("failed to write buildid table\n"); | 404 | pr_debug("failed to write buildid table\n"); |
480 | goto out_free; | 405 | goto out_free; |
@@ -513,32 +438,41 @@ int perf_header__write_pipe(int fd) | |||
513 | return 0; | 438 | return 0; |
514 | } | 439 | } |
515 | 440 | ||
516 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 441 | int perf_session__write_header(struct perf_session *session, |
442 | struct perf_evlist *evlist, | ||
443 | int fd, bool at_exit) | ||
517 | { | 444 | { |
518 | struct perf_file_header f_header; | 445 | struct perf_file_header f_header; |
519 | struct perf_file_attr f_attr; | 446 | struct perf_file_attr f_attr; |
520 | struct perf_header_attr *attr; | 447 | struct perf_header *header = &session->header; |
521 | int i, err; | 448 | struct perf_evsel *attr, *pair = NULL; |
449 | int err; | ||
522 | 450 | ||
523 | lseek(fd, sizeof(f_header), SEEK_SET); | 451 | lseek(fd, sizeof(f_header), SEEK_SET); |
524 | 452 | ||
525 | for (i = 0; i < self->attrs; i++) { | 453 | if (session->evlist != evlist) |
526 | attr = self->attr[i]; | 454 | pair = list_entry(session->evlist->entries.next, struct perf_evsel, node); |
527 | 455 | ||
456 | list_for_each_entry(attr, &evlist->entries, node) { | ||
528 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 457 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
529 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); | 458 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
530 | if (err < 0) { | 459 | if (err < 0) { |
460 | out_err_write: | ||
531 | pr_debug("failed to write perf header\n"); | 461 | pr_debug("failed to write perf header\n"); |
532 | return err; | 462 | return err; |
533 | } | 463 | } |
464 | if (session->evlist != evlist) { | ||
465 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); | ||
466 | if (err < 0) | ||
467 | goto out_err_write; | ||
468 | attr->ids += pair->ids; | ||
469 | pair = list_entry(pair->node.next, struct perf_evsel, node); | ||
470 | } | ||
534 | } | 471 | } |
535 | 472 | ||
473 | header->attr_offset = lseek(fd, 0, SEEK_CUR); | ||
536 | 474 | ||
537 | self->attr_offset = lseek(fd, 0, SEEK_CUR); | 475 | list_for_each_entry(attr, &evlist->entries, node) { |
538 | |||
539 | for (i = 0; i < self->attrs; i++) { | ||
540 | attr = self->attr[i]; | ||
541 | |||
542 | f_attr = (struct perf_file_attr){ | 476 | f_attr = (struct perf_file_attr){ |
543 | .attr = attr->attr, | 477 | .attr = attr->attr, |
544 | .ids = { | 478 | .ids = { |
@@ -553,20 +487,20 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
553 | } | 487 | } |
554 | } | 488 | } |
555 | 489 | ||
556 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 490 | header->event_offset = lseek(fd, 0, SEEK_CUR); |
557 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 491 | header->event_size = event_count * sizeof(struct perf_trace_event_type); |
558 | if (events) { | 492 | if (events) { |
559 | err = do_write(fd, events, self->event_size); | 493 | err = do_write(fd, events, header->event_size); |
560 | if (err < 0) { | 494 | if (err < 0) { |
561 | pr_debug("failed to write perf header events\n"); | 495 | pr_debug("failed to write perf header events\n"); |
562 | return err; | 496 | return err; |
563 | } | 497 | } |
564 | } | 498 | } |
565 | 499 | ||
566 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 500 | header->data_offset = lseek(fd, 0, SEEK_CUR); |
567 | 501 | ||
568 | if (at_exit) { | 502 | if (at_exit) { |
569 | err = perf_header__adds_write(self, fd); | 503 | err = perf_header__adds_write(header, evlist, fd); |
570 | if (err < 0) | 504 | if (err < 0) |
571 | return err; | 505 | return err; |
572 | } | 506 | } |
@@ -576,20 +510,20 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
576 | .size = sizeof(f_header), | 510 | .size = sizeof(f_header), |
577 | .attr_size = sizeof(f_attr), | 511 | .attr_size = sizeof(f_attr), |
578 | .attrs = { | 512 | .attrs = { |
579 | .offset = self->attr_offset, | 513 | .offset = header->attr_offset, |
580 | .size = self->attrs * sizeof(f_attr), | 514 | .size = evlist->nr_entries * sizeof(f_attr), |
581 | }, | 515 | }, |
582 | .data = { | 516 | .data = { |
583 | .offset = self->data_offset, | 517 | .offset = header->data_offset, |
584 | .size = self->data_size, | 518 | .size = header->data_size, |
585 | }, | 519 | }, |
586 | .event_types = { | 520 | .event_types = { |
587 | .offset = self->event_offset, | 521 | .offset = header->event_offset, |
588 | .size = self->event_size, | 522 | .size = header->event_size, |
589 | }, | 523 | }, |
590 | }; | 524 | }; |
591 | 525 | ||
592 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | 526 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); |
593 | 527 | ||
594 | lseek(fd, 0, SEEK_SET); | 528 | lseek(fd, 0, SEEK_SET); |
595 | err = do_write(fd, &f_header, sizeof(f_header)); | 529 | err = do_write(fd, &f_header, sizeof(f_header)); |
@@ -597,26 +531,26 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
597 | pr_debug("failed to write perf header\n"); | 531 | pr_debug("failed to write perf header\n"); |
598 | return err; | 532 | return err; |
599 | } | 533 | } |
600 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 534 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
601 | 535 | ||
602 | self->frozen = 1; | 536 | header->frozen = 1; |
603 | return 0; | 537 | return 0; |
604 | } | 538 | } |
605 | 539 | ||
606 | static int perf_header__getbuffer64(struct perf_header *self, | 540 | static int perf_header__getbuffer64(struct perf_header *header, |
607 | int fd, void *buf, size_t size) | 541 | int fd, void *buf, size_t size) |
608 | { | 542 | { |
609 | if (readn(fd, buf, size) <= 0) | 543 | if (readn(fd, buf, size) <= 0) |
610 | return -1; | 544 | return -1; |
611 | 545 | ||
612 | if (self->needs_swap) | 546 | if (header->needs_swap) |
613 | mem_bswap_64(buf, size); | 547 | mem_bswap_64(buf, size); |
614 | 548 | ||
615 | return 0; | 549 | return 0; |
616 | } | 550 | } |
617 | 551 | ||
618 | int perf_header__process_sections(struct perf_header *self, int fd, | 552 | int perf_header__process_sections(struct perf_header *header, int fd, |
619 | int (*process)(struct perf_file_section *self, | 553 | int (*process)(struct perf_file_section *section, |
620 | struct perf_header *ph, | 554 | struct perf_header *ph, |
621 | int feat, int fd)) | 555 | int feat, int fd)) |
622 | { | 556 | { |
@@ -626,7 +560,7 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
626 | int idx = 0; | 560 | int idx = 0; |
627 | int err = -1, feat = 1; | 561 | int err = -1, feat = 1; |
628 | 562 | ||
629 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 563 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
630 | if (!nr_sections) | 564 | if (!nr_sections) |
631 | return 0; | 565 | return 0; |
632 | 566 | ||
@@ -636,17 +570,17 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
636 | 570 | ||
637 | sec_size = sizeof(*feat_sec) * nr_sections; | 571 | sec_size = sizeof(*feat_sec) * nr_sections; |
638 | 572 | ||
639 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 573 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
640 | 574 | ||
641 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) | 575 | if (perf_header__getbuffer64(header, fd, feat_sec, sec_size)) |
642 | goto out_free; | 576 | goto out_free; |
643 | 577 | ||
644 | err = 0; | 578 | err = 0; |
645 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | 579 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { |
646 | if (perf_header__has_feat(self, feat)) { | 580 | if (perf_header__has_feat(header, feat)) { |
647 | struct perf_file_section *sec = &feat_sec[idx++]; | 581 | struct perf_file_section *sec = &feat_sec[idx++]; |
648 | 582 | ||
649 | err = process(sec, self, feat, fd); | 583 | err = process(sec, header, feat, fd); |
650 | if (err < 0) | 584 | if (err < 0) |
651 | break; | 585 | break; |
652 | } | 586 | } |
@@ -657,35 +591,35 @@ out_free: | |||
657 | return err; | 591 | return err; |
658 | } | 592 | } |
659 | 593 | ||
660 | int perf_file_header__read(struct perf_file_header *self, | 594 | int perf_file_header__read(struct perf_file_header *header, |
661 | struct perf_header *ph, int fd) | 595 | struct perf_header *ph, int fd) |
662 | { | 596 | { |
663 | lseek(fd, 0, SEEK_SET); | 597 | lseek(fd, 0, SEEK_SET); |
664 | 598 | ||
665 | if (readn(fd, self, sizeof(*self)) <= 0 || | 599 | if (readn(fd, header, sizeof(*header)) <= 0 || |
666 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 600 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
667 | return -1; | 601 | return -1; |
668 | 602 | ||
669 | if (self->attr_size != sizeof(struct perf_file_attr)) { | 603 | if (header->attr_size != sizeof(struct perf_file_attr)) { |
670 | u64 attr_size = bswap_64(self->attr_size); | 604 | u64 attr_size = bswap_64(header->attr_size); |
671 | 605 | ||
672 | if (attr_size != sizeof(struct perf_file_attr)) | 606 | if (attr_size != sizeof(struct perf_file_attr)) |
673 | return -1; | 607 | return -1; |
674 | 608 | ||
675 | mem_bswap_64(self, offsetof(struct perf_file_header, | 609 | mem_bswap_64(header, offsetof(struct perf_file_header, |
676 | adds_features)); | 610 | adds_features)); |
677 | ph->needs_swap = true; | 611 | ph->needs_swap = true; |
678 | } | 612 | } |
679 | 613 | ||
680 | if (self->size != sizeof(*self)) { | 614 | if (header->size != sizeof(*header)) { |
681 | /* Support the previous format */ | 615 | /* Support the previous format */ |
682 | if (self->size == offsetof(typeof(*self), adds_features)) | 616 | if (header->size == offsetof(typeof(*header), adds_features)) |
683 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | 617 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
684 | else | 618 | else |
685 | return -1; | 619 | return -1; |
686 | } | 620 | } |
687 | 621 | ||
688 | memcpy(&ph->adds_features, &self->adds_features, | 622 | memcpy(&ph->adds_features, &header->adds_features, |
689 | sizeof(ph->adds_features)); | 623 | sizeof(ph->adds_features)); |
690 | /* | 624 | /* |
691 | * FIXME: hack that assumes that if we need swap the perf.data file | 625 | * FIXME: hack that assumes that if we need swap the perf.data file |
@@ -699,10 +633,10 @@ int perf_file_header__read(struct perf_file_header *self, | |||
699 | perf_header__set_feat(ph, HEADER_BUILD_ID); | 633 | perf_header__set_feat(ph, HEADER_BUILD_ID); |
700 | } | 634 | } |
701 | 635 | ||
702 | ph->event_offset = self->event_types.offset; | 636 | ph->event_offset = header->event_types.offset; |
703 | ph->event_size = self->event_types.size; | 637 | ph->event_size = header->event_types.size; |
704 | ph->data_offset = self->data.offset; | 638 | ph->data_offset = header->data.offset; |
705 | ph->data_size = self->data.size; | 639 | ph->data_size = header->data.size; |
706 | return 0; | 640 | return 0; |
707 | } | 641 | } |
708 | 642 | ||
@@ -761,11 +695,10 @@ out: | |||
761 | return err; | 695 | return err; |
762 | } | 696 | } |
763 | 697 | ||
764 | static int perf_header__read_build_ids(struct perf_header *self, | 698 | static int perf_header__read_build_ids(struct perf_header *header, |
765 | int input, u64 offset, u64 size) | 699 | int input, u64 offset, u64 size) |
766 | { | 700 | { |
767 | struct perf_session *session = container_of(self, | 701 | struct perf_session *session = container_of(header, struct perf_session, header); |
768 | struct perf_session, header); | ||
769 | struct build_id_event bev; | 702 | struct build_id_event bev; |
770 | char filename[PATH_MAX]; | 703 | char filename[PATH_MAX]; |
771 | u64 limit = offset + size; | 704 | u64 limit = offset + size; |
@@ -777,7 +710,7 @@ static int perf_header__read_build_ids(struct perf_header *self, | |||
777 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 710 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) |
778 | goto out; | 711 | goto out; |
779 | 712 | ||
780 | if (self->needs_swap) | 713 | if (header->needs_swap) |
781 | perf_event_header__bswap(&bev.header); | 714 | perf_event_header__bswap(&bev.header); |
782 | 715 | ||
783 | len = bev.header.size - sizeof(bev); | 716 | len = bev.header.size - sizeof(bev); |
@@ -793,13 +726,13 @@ out: | |||
793 | return err; | 726 | return err; |
794 | } | 727 | } |
795 | 728 | ||
796 | static int perf_file_section__process(struct perf_file_section *self, | 729 | static int perf_file_section__process(struct perf_file_section *section, |
797 | struct perf_header *ph, | 730 | struct perf_header *ph, |
798 | int feat, int fd) | 731 | int feat, int fd) |
799 | { | 732 | { |
800 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { | 733 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
801 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 734 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
802 | "%d, continuing...\n", self->offset, feat); | 735 | "%d, continuing...\n", section->offset, feat); |
803 | return 0; | 736 | return 0; |
804 | } | 737 | } |
805 | 738 | ||
@@ -809,7 +742,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
809 | break; | 742 | break; |
810 | 743 | ||
811 | case HEADER_BUILD_ID: | 744 | case HEADER_BUILD_ID: |
812 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) | 745 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
813 | pr_debug("Failed to read buildids, continuing...\n"); | 746 | pr_debug("Failed to read buildids, continuing...\n"); |
814 | break; | 747 | break; |
815 | default: | 748 | default: |
@@ -819,21 +752,21 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
819 | return 0; | 752 | return 0; |
820 | } | 753 | } |
821 | 754 | ||
822 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, | 755 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
823 | struct perf_header *ph, int fd, | 756 | struct perf_header *ph, int fd, |
824 | bool repipe) | 757 | bool repipe) |
825 | { | 758 | { |
826 | if (readn(fd, self, sizeof(*self)) <= 0 || | 759 | if (readn(fd, header, sizeof(*header)) <= 0 || |
827 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 760 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
828 | return -1; | 761 | return -1; |
829 | 762 | ||
830 | if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) | 763 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) |
831 | return -1; | 764 | return -1; |
832 | 765 | ||
833 | if (self->size != sizeof(*self)) { | 766 | if (header->size != sizeof(*header)) { |
834 | u64 size = bswap_64(self->size); | 767 | u64 size = bswap_64(header->size); |
835 | 768 | ||
836 | if (size != sizeof(*self)) | 769 | if (size != sizeof(*header)) |
837 | return -1; | 770 | return -1; |
838 | 771 | ||
839 | ph->needs_swap = true; | 772 | ph->needs_swap = true; |
@@ -844,10 +777,10 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, | |||
844 | 777 | ||
845 | static int perf_header__read_pipe(struct perf_session *session, int fd) | 778 | static int perf_header__read_pipe(struct perf_session *session, int fd) |
846 | { | 779 | { |
847 | struct perf_header *self = &session->header; | 780 | struct perf_header *header = &session->header; |
848 | struct perf_pipe_file_header f_header; | 781 | struct perf_pipe_file_header f_header; |
849 | 782 | ||
850 | if (perf_file_header__read_pipe(&f_header, self, fd, | 783 | if (perf_file_header__read_pipe(&f_header, header, fd, |
851 | session->repipe) < 0) { | 784 | session->repipe) < 0) { |
852 | pr_debug("incompatible file format\n"); | 785 | pr_debug("incompatible file format\n"); |
853 | return -EINVAL; | 786 | return -EINVAL; |
@@ -858,18 +791,22 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) | |||
858 | return 0; | 791 | return 0; |
859 | } | 792 | } |
860 | 793 | ||
861 | int perf_header__read(struct perf_session *session, int fd) | 794 | int perf_session__read_header(struct perf_session *session, int fd) |
862 | { | 795 | { |
863 | struct perf_header *self = &session->header; | 796 | struct perf_header *header = &session->header; |
864 | struct perf_file_header f_header; | 797 | struct perf_file_header f_header; |
865 | struct perf_file_attr f_attr; | 798 | struct perf_file_attr f_attr; |
866 | u64 f_id; | 799 | u64 f_id; |
867 | int nr_attrs, nr_ids, i, j; | 800 | int nr_attrs, nr_ids, i, j; |
868 | 801 | ||
802 | session->evlist = perf_evlist__new(NULL, NULL); | ||
803 | if (session->evlist == NULL) | ||
804 | return -ENOMEM; | ||
805 | |||
869 | if (session->fd_pipe) | 806 | if (session->fd_pipe) |
870 | return perf_header__read_pipe(session, fd); | 807 | return perf_header__read_pipe(session, fd); |
871 | 808 | ||
872 | if (perf_file_header__read(&f_header, self, fd) < 0) { | 809 | if (perf_file_header__read(&f_header, header, fd) < 0) { |
873 | pr_debug("incompatible file format\n"); | 810 | pr_debug("incompatible file format\n"); |
874 | return -EINVAL; | 811 | return -EINVAL; |
875 | } | 812 | } |
@@ -878,33 +815,39 @@ int perf_header__read(struct perf_session *session, int fd) | |||
878 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 815 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
879 | 816 | ||
880 | for (i = 0; i < nr_attrs; i++) { | 817 | for (i = 0; i < nr_attrs; i++) { |
881 | struct perf_header_attr *attr; | 818 | struct perf_evsel *evsel; |
882 | off_t tmp; | 819 | off_t tmp; |
883 | 820 | ||
884 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) | 821 | if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) |
885 | goto out_errno; | 822 | goto out_errno; |
886 | 823 | ||
887 | tmp = lseek(fd, 0, SEEK_CUR); | 824 | tmp = lseek(fd, 0, SEEK_CUR); |
825 | evsel = perf_evsel__new(&f_attr.attr, i); | ||
888 | 826 | ||
889 | attr = perf_header_attr__new(&f_attr.attr); | 827 | if (evsel == NULL) |
890 | if (attr == NULL) | 828 | goto out_delete_evlist; |
891 | return -ENOMEM; | 829 | /* |
830 | * Do it before so that if perf_evsel__alloc_id fails, this | ||
831 | * entry gets purged too at perf_evlist__delete(). | ||
832 | */ | ||
833 | perf_evlist__add(session->evlist, evsel); | ||
892 | 834 | ||
893 | nr_ids = f_attr.ids.size / sizeof(u64); | 835 | nr_ids = f_attr.ids.size / sizeof(u64); |
836 | /* | ||
837 | * We don't have the cpu and thread maps on the header, so | ||
838 | * for allocating the perf_sample_id table we fake 1 cpu and | ||
839 | * hattr->ids threads. | ||
840 | */ | ||
841 | if (perf_evsel__alloc_id(evsel, 1, nr_ids)) | ||
842 | goto out_delete_evlist; | ||
843 | |||
894 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 844 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
895 | 845 | ||
896 | for (j = 0; j < nr_ids; j++) { | 846 | for (j = 0; j < nr_ids; j++) { |
897 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) | 847 | if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) |
898 | goto out_errno; | 848 | goto out_errno; |
899 | 849 | ||
900 | if (perf_header_attr__add_id(attr, f_id) < 0) { | 850 | perf_evlist__id_add(session->evlist, evsel, 0, j, f_id); |
901 | perf_header_attr__delete(attr); | ||
902 | return -ENOMEM; | ||
903 | } | ||
904 | } | ||
905 | if (perf_header__add_attr(self, attr) < 0) { | ||
906 | perf_header_attr__delete(attr); | ||
907 | return -ENOMEM; | ||
908 | } | 851 | } |
909 | 852 | ||
910 | lseek(fd, tmp, SEEK_SET); | 853 | lseek(fd, tmp, SEEK_SET); |
@@ -915,93 +858,63 @@ int perf_header__read(struct perf_session *session, int fd) | |||
915 | events = malloc(f_header.event_types.size); | 858 | events = malloc(f_header.event_types.size); |
916 | if (events == NULL) | 859 | if (events == NULL) |
917 | return -ENOMEM; | 860 | return -ENOMEM; |
918 | if (perf_header__getbuffer64(self, fd, events, | 861 | if (perf_header__getbuffer64(header, fd, events, |
919 | f_header.event_types.size)) | 862 | f_header.event_types.size)) |
920 | goto out_errno; | 863 | goto out_errno; |
921 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 864 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
922 | } | 865 | } |
923 | 866 | ||
924 | perf_header__process_sections(self, fd, perf_file_section__process); | 867 | perf_header__process_sections(header, fd, perf_file_section__process); |
925 | 868 | ||
926 | lseek(fd, self->data_offset, SEEK_SET); | 869 | lseek(fd, header->data_offset, SEEK_SET); |
927 | 870 | ||
928 | self->frozen = 1; | 871 | header->frozen = 1; |
929 | return 0; | 872 | return 0; |
930 | out_errno: | 873 | out_errno: |
931 | return -errno; | 874 | return -errno; |
875 | |||
876 | out_delete_evlist: | ||
877 | perf_evlist__delete(session->evlist); | ||
878 | session->evlist = NULL; | ||
879 | return -ENOMEM; | ||
932 | } | 880 | } |
933 | 881 | ||
934 | u64 perf_header__sample_type(struct perf_header *header) | 882 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) |
935 | { | 883 | { |
884 | struct perf_evsel *pos; | ||
936 | u64 type = 0; | 885 | u64 type = 0; |
937 | int i; | ||
938 | |||
939 | for (i = 0; i < header->attrs; i++) { | ||
940 | struct perf_header_attr *attr = header->attr[i]; | ||
941 | 886 | ||
887 | list_for_each_entry(pos, &evlist->entries, node) { | ||
942 | if (!type) | 888 | if (!type) |
943 | type = attr->attr.sample_type; | 889 | type = pos->attr.sample_type; |
944 | else if (type != attr->attr.sample_type) | 890 | else if (type != pos->attr.sample_type) |
945 | die("non matching sample_type"); | 891 | die("non matching sample_type"); |
946 | } | 892 | } |
947 | 893 | ||
948 | return type; | 894 | return type; |
949 | } | 895 | } |
950 | 896 | ||
951 | bool perf_header__sample_id_all(const struct perf_header *header) | 897 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) |
952 | { | 898 | { |
953 | bool value = false, first = true; | 899 | bool value = false, first = true; |
954 | int i; | 900 | struct perf_evsel *pos; |
955 | |||
956 | for (i = 0; i < header->attrs; i++) { | ||
957 | struct perf_header_attr *attr = header->attr[i]; | ||
958 | 901 | ||
902 | list_for_each_entry(pos, &evlist->entries, node) { | ||
959 | if (first) { | 903 | if (first) { |
960 | value = attr->attr.sample_id_all; | 904 | value = pos->attr.sample_id_all; |
961 | first = false; | 905 | first = false; |
962 | } else if (value != attr->attr.sample_id_all) | 906 | } else if (value != pos->attr.sample_id_all) |
963 | die("non matching sample_id_all"); | 907 | die("non matching sample_id_all"); |
964 | } | 908 | } |
965 | 909 | ||
966 | return value; | 910 | return value; |
967 | } | 911 | } |
968 | 912 | ||
969 | struct perf_event_attr * | 913 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
970 | perf_header__find_attr(u64 id, struct perf_header *header) | 914 | perf_event__handler_t process, |
971 | { | 915 | struct perf_session *session) |
972 | int i; | ||
973 | |||
974 | /* | ||
975 | * We set id to -1 if the data file doesn't contain sample | ||
976 | * ids. This can happen when the data file contains one type | ||
977 | * of event and in that case, the header can still store the | ||
978 | * event attribute information. Check for this and avoid | ||
979 | * walking through the entire list of ids which may be large. | ||
980 | */ | ||
981 | if (id == -1ULL) { | ||
982 | if (header->attrs > 0) | ||
983 | return &header->attr[0]->attr; | ||
984 | return NULL; | ||
985 | } | ||
986 | |||
987 | for (i = 0; i < header->attrs; i++) { | ||
988 | struct perf_header_attr *attr = header->attr[i]; | ||
989 | int j; | ||
990 | |||
991 | for (j = 0; j < attr->ids; j++) { | ||
992 | if (attr->id[j] == id) | ||
993 | return &attr->attr; | ||
994 | } | ||
995 | } | ||
996 | |||
997 | return NULL; | ||
998 | } | ||
999 | |||
1000 | int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | ||
1001 | event__handler_t process, | ||
1002 | struct perf_session *session) | ||
1003 | { | 916 | { |
1004 | event_t *ev; | 917 | union perf_event *ev; |
1005 | size_t size; | 918 | size_t size; |
1006 | int err; | 919 | int err; |
1007 | 920 | ||
@@ -1028,17 +941,15 @@ int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | |||
1028 | return err; | 941 | return err; |
1029 | } | 942 | } |
1030 | 943 | ||
1031 | int event__synthesize_attrs(struct perf_header *self, event__handler_t process, | 944 | int perf_session__synthesize_attrs(struct perf_session *session, |
1032 | struct perf_session *session) | 945 | perf_event__handler_t process) |
1033 | { | 946 | { |
1034 | struct perf_header_attr *attr; | 947 | struct perf_evsel *attr; |
1035 | int i, err = 0; | 948 | int err = 0; |
1036 | |||
1037 | for (i = 0; i < self->attrs; i++) { | ||
1038 | attr = self->attr[i]; | ||
1039 | 949 | ||
1040 | err = event__synthesize_attr(&attr->attr, attr->ids, attr->id, | 950 | list_for_each_entry(attr, &session->evlist->entries, node) { |
1041 | process, session); | 951 | err = perf_event__synthesize_attr(&attr->attr, attr->ids, |
952 | attr->id, process, session); | ||
1042 | if (err) { | 953 | if (err) { |
1043 | pr_debug("failed to create perf header attribute\n"); | 954 | pr_debug("failed to create perf header attribute\n"); |
1044 | return err; | 955 | return err; |
@@ -1048,29 +959,39 @@ int event__synthesize_attrs(struct perf_header *self, event__handler_t process, | |||
1048 | return err; | 959 | return err; |
1049 | } | 960 | } |
1050 | 961 | ||
1051 | int event__process_attr(event_t *self, struct perf_session *session) | 962 | int perf_event__process_attr(union perf_event *event, |
963 | struct perf_session *session) | ||
1052 | { | 964 | { |
1053 | struct perf_header_attr *attr; | ||
1054 | unsigned int i, ids, n_ids; | 965 | unsigned int i, ids, n_ids; |
966 | struct perf_evsel *evsel; | ||
967 | |||
968 | if (session->evlist == NULL) { | ||
969 | session->evlist = perf_evlist__new(NULL, NULL); | ||
970 | if (session->evlist == NULL) | ||
971 | return -ENOMEM; | ||
972 | } | ||
1055 | 973 | ||
1056 | attr = perf_header_attr__new(&self->attr.attr); | 974 | evsel = perf_evsel__new(&event->attr.attr, |
1057 | if (attr == NULL) | 975 | session->evlist->nr_entries); |
976 | if (evsel == NULL) | ||
1058 | return -ENOMEM; | 977 | return -ENOMEM; |
1059 | 978 | ||
1060 | ids = self->header.size; | 979 | perf_evlist__add(session->evlist, evsel); |
1061 | ids -= (void *)&self->attr.id - (void *)self; | 980 | |
981 | ids = event->header.size; | ||
982 | ids -= (void *)&event->attr.id - (void *)event; | ||
1062 | n_ids = ids / sizeof(u64); | 983 | n_ids = ids / sizeof(u64); |
984 | /* | ||
985 | * We don't have the cpu and thread maps on the header, so | ||
986 | * for allocating the perf_sample_id table we fake 1 cpu and | ||
987 | * hattr->ids threads. | ||
988 | */ | ||
989 | if (perf_evsel__alloc_id(evsel, 1, n_ids)) | ||
990 | return -ENOMEM; | ||
1063 | 991 | ||
1064 | for (i = 0; i < n_ids; i++) { | 992 | for (i = 0; i < n_ids; i++) { |
1065 | if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) { | 993 | perf_evlist__id_add(session->evlist, evsel, 0, i, |
1066 | perf_header_attr__delete(attr); | 994 | event->attr.id[i]); |
1067 | return -ENOMEM; | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | if (perf_header__add_attr(&session->header, attr) < 0) { | ||
1072 | perf_header_attr__delete(attr); | ||
1073 | return -ENOMEM; | ||
1074 | } | 995 | } |
1075 | 996 | ||
1076 | perf_session__update_sample_type(session); | 997 | perf_session__update_sample_type(session); |
@@ -1078,11 +999,11 @@ int event__process_attr(event_t *self, struct perf_session *session) | |||
1078 | return 0; | 999 | return 0; |
1079 | } | 1000 | } |
1080 | 1001 | ||
1081 | int event__synthesize_event_type(u64 event_id, char *name, | 1002 | int perf_event__synthesize_event_type(u64 event_id, char *name, |
1082 | event__handler_t process, | 1003 | perf_event__handler_t process, |
1083 | struct perf_session *session) | 1004 | struct perf_session *session) |
1084 | { | 1005 | { |
1085 | event_t ev; | 1006 | union perf_event ev; |
1086 | size_t size = 0; | 1007 | size_t size = 0; |
1087 | int err = 0; | 1008 | int err = 0; |
1088 | 1009 | ||
@@ -1103,8 +1024,8 @@ int event__synthesize_event_type(u64 event_id, char *name, | |||
1103 | return err; | 1024 | return err; |
1104 | } | 1025 | } |
1105 | 1026 | ||
1106 | int event__synthesize_event_types(event__handler_t process, | 1027 | int perf_event__synthesize_event_types(perf_event__handler_t process, |
1107 | struct perf_session *session) | 1028 | struct perf_session *session) |
1108 | { | 1029 | { |
1109 | struct perf_trace_event_type *type; | 1030 | struct perf_trace_event_type *type; |
1110 | int i, err = 0; | 1031 | int i, err = 0; |
@@ -1112,8 +1033,9 @@ int event__synthesize_event_types(event__handler_t process, | |||
1112 | for (i = 0; i < event_count; i++) { | 1033 | for (i = 0; i < event_count; i++) { |
1113 | type = &events[i]; | 1034 | type = &events[i]; |
1114 | 1035 | ||
1115 | err = event__synthesize_event_type(type->event_id, type->name, | 1036 | err = perf_event__synthesize_event_type(type->event_id, |
1116 | process, session); | 1037 | type->name, process, |
1038 | session); | ||
1117 | if (err) { | 1039 | if (err) { |
1118 | pr_debug("failed to create perf header event type\n"); | 1040 | pr_debug("failed to create perf header event type\n"); |
1119 | return err; | 1041 | return err; |
@@ -1123,28 +1045,28 @@ int event__synthesize_event_types(event__handler_t process, | |||
1123 | return err; | 1045 | return err; |
1124 | } | 1046 | } |
1125 | 1047 | ||
1126 | int event__process_event_type(event_t *self, | 1048 | int perf_event__process_event_type(union perf_event *event, |
1127 | struct perf_session *session __unused) | 1049 | struct perf_session *session __unused) |
1128 | { | 1050 | { |
1129 | if (perf_header__push_event(self->event_type.event_type.event_id, | 1051 | if (perf_header__push_event(event->event_type.event_type.event_id, |
1130 | self->event_type.event_type.name) < 0) | 1052 | event->event_type.event_type.name) < 0) |
1131 | return -ENOMEM; | 1053 | return -ENOMEM; |
1132 | 1054 | ||
1133 | return 0; | 1055 | return 0; |
1134 | } | 1056 | } |
1135 | 1057 | ||
1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 1058 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
1137 | event__handler_t process, | 1059 | perf_event__handler_t process, |
1138 | struct perf_session *session __unused) | 1060 | struct perf_session *session __unused) |
1139 | { | 1061 | { |
1140 | event_t ev; | 1062 | union perf_event ev; |
1141 | ssize_t size = 0, aligned_size = 0, padding; | 1063 | ssize_t size = 0, aligned_size = 0, padding; |
1142 | int err = 0; | 1064 | int err __used = 0; |
1143 | 1065 | ||
1144 | memset(&ev, 0, sizeof(ev)); | 1066 | memset(&ev, 0, sizeof(ev)); |
1145 | 1067 | ||
1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1068 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1147 | size = read_tracing_data_size(fd, pattrs); | 1069 | size = read_tracing_data_size(fd, &evlist->entries); |
1148 | if (size <= 0) | 1070 | if (size <= 0) |
1149 | return size; | 1071 | return size; |
1150 | aligned_size = ALIGN(size, sizeof(u64)); | 1072 | aligned_size = ALIGN(size, sizeof(u64)); |
@@ -1154,16 +1076,16 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1154 | 1076 | ||
1155 | process(&ev, NULL, session); | 1077 | process(&ev, NULL, session); |
1156 | 1078 | ||
1157 | err = read_tracing_data(fd, pattrs); | 1079 | err = read_tracing_data(fd, &evlist->entries); |
1158 | write_padded(fd, NULL, 0, padding); | 1080 | write_padded(fd, NULL, 0, padding); |
1159 | 1081 | ||
1160 | return aligned_size; | 1082 | return aligned_size; |
1161 | } | 1083 | } |
1162 | 1084 | ||
1163 | int event__process_tracing_data(event_t *self, | 1085 | int perf_event__process_tracing_data(union perf_event *event, |
1164 | struct perf_session *session) | 1086 | struct perf_session *session) |
1165 | { | 1087 | { |
1166 | ssize_t size_read, padding, size = self->tracing_data.size; | 1088 | ssize_t size_read, padding, size = event->tracing_data.size; |
1167 | off_t offset = lseek(session->fd, 0, SEEK_CUR); | 1089 | off_t offset = lseek(session->fd, 0, SEEK_CUR); |
1168 | char buf[BUFSIZ]; | 1090 | char buf[BUFSIZ]; |
1169 | 1091 | ||
@@ -1189,12 +1111,12 @@ int event__process_tracing_data(event_t *self, | |||
1189 | return size_read + padding; | 1111 | return size_read + padding; |
1190 | } | 1112 | } |
1191 | 1113 | ||
1192 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 1114 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, |
1193 | event__handler_t process, | 1115 | perf_event__handler_t process, |
1194 | struct machine *machine, | 1116 | struct machine *machine, |
1195 | struct perf_session *session) | 1117 | struct perf_session *session) |
1196 | { | 1118 | { |
1197 | event_t ev; | 1119 | union perf_event ev; |
1198 | size_t len; | 1120 | size_t len; |
1199 | int err = 0; | 1121 | int err = 0; |
1200 | 1122 | ||
@@ -1217,11 +1139,11 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, | |||
1217 | return err; | 1139 | return err; |
1218 | } | 1140 | } |
1219 | 1141 | ||
1220 | int event__process_build_id(event_t *self, | 1142 | int perf_event__process_build_id(union perf_event *event, |
1221 | struct perf_session *session) | 1143 | struct perf_session *session) |
1222 | { | 1144 | { |
1223 | __event_process_build_id(&self->build_id, | 1145 | __event_process_build_id(&event->build_id, |
1224 | self->build_id.filename, | 1146 | event->build_id.filename, |
1225 | session); | 1147 | session); |
1226 | return 0; | 1148 | return 0; |
1227 | } | 1149 | } |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72f..456661d7f10e 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -9,13 +9,6 @@ | |||
9 | 9 | ||
10 | #include <linux/bitmap.h> | 10 | #include <linux/bitmap.h> |
11 | 11 | ||
12 | struct perf_header_attr { | ||
13 | struct perf_event_attr attr; | ||
14 | int ids, size; | ||
15 | u64 *id; | ||
16 | off_t id_offset; | ||
17 | }; | ||
18 | |||
19 | enum { | 12 | enum { |
20 | HEADER_TRACE_INFO = 1, | 13 | HEADER_TRACE_INFO = 1, |
21 | HEADER_BUILD_ID, | 14 | HEADER_BUILD_ID, |
@@ -46,14 +39,12 @@ struct perf_pipe_file_header { | |||
46 | 39 | ||
47 | struct perf_header; | 40 | struct perf_header; |
48 | 41 | ||
49 | int perf_file_header__read(struct perf_file_header *self, | 42 | int perf_file_header__read(struct perf_file_header *header, |
50 | struct perf_header *ph, int fd); | 43 | struct perf_header *ph, int fd); |
51 | 44 | ||
52 | struct perf_header { | 45 | struct perf_header { |
53 | int frozen; | 46 | int frozen; |
54 | int attrs, size; | ||
55 | bool needs_swap; | 47 | bool needs_swap; |
56 | struct perf_header_attr **attr; | ||
57 | s64 attr_offset; | 48 | s64 attr_offset; |
58 | u64 data_offset; | 49 | u64 data_offset; |
59 | u64 data_size; | 50 | u64 data_size; |
@@ -62,34 +53,25 @@ struct perf_header { | |||
62 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 53 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
63 | }; | 54 | }; |
64 | 55 | ||
65 | int perf_header__init(struct perf_header *self); | 56 | struct perf_evlist; |
66 | void perf_header__exit(struct perf_header *self); | ||
67 | 57 | ||
68 | int perf_header__read(struct perf_session *session, int fd); | 58 | int perf_session__read_header(struct perf_session *session, int fd); |
69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 59 | int perf_session__write_header(struct perf_session *session, |
60 | struct perf_evlist *evlist, | ||
61 | int fd, bool at_exit); | ||
70 | int perf_header__write_pipe(int fd); | 62 | int perf_header__write_pipe(int fd); |
71 | 63 | ||
72 | int perf_header__add_attr(struct perf_header *self, | ||
73 | struct perf_header_attr *attr); | ||
74 | |||
75 | int perf_header__push_event(u64 id, const char *name); | 64 | int perf_header__push_event(u64 id, const char *name); |
76 | char *perf_header__find_event(u64 id); | 65 | char *perf_header__find_event(u64 id); |
77 | 66 | ||
78 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | 67 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); |
79 | void perf_header_attr__delete(struct perf_header_attr *self); | 68 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); |
69 | void perf_header__set_feat(struct perf_header *header, int feat); | ||
70 | void perf_header__clear_feat(struct perf_header *header, int feat); | ||
71 | bool perf_header__has_feat(const struct perf_header *header, int feat); | ||
80 | 72 | ||
81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | 73 | int perf_header__process_sections(struct perf_header *header, int fd, |
82 | 74 | int (*process)(struct perf_file_section *section, | |
83 | u64 perf_header__sample_type(struct perf_header *header); | ||
84 | bool perf_header__sample_id_all(const struct perf_header *header); | ||
85 | struct perf_event_attr * | ||
86 | perf_header__find_attr(u64 id, struct perf_header *header); | ||
87 | void perf_header__set_feat(struct perf_header *self, int feat); | ||
88 | void perf_header__clear_feat(struct perf_header *self, int feat); | ||
89 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
90 | |||
91 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
92 | int (*process)(struct perf_file_section *self, | ||
93 | struct perf_header *ph, | 75 | struct perf_header *ph, |
94 | int feat, int fd)); | 76 | int feat, int fd)); |
95 | 77 | ||
@@ -97,32 +79,31 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
97 | const char *name, bool is_kallsyms); | 79 | const char *name, bool is_kallsyms); |
98 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | 80 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); |
99 | 81 | ||
100 | int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 82 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
101 | event__handler_t process, | 83 | perf_event__handler_t process, |
102 | struct perf_session *session); | ||
103 | int event__synthesize_attrs(struct perf_header *self, | ||
104 | event__handler_t process, | ||
105 | struct perf_session *session); | ||
106 | int event__process_attr(event_t *self, struct perf_session *session); | ||
107 | |||
108 | int event__synthesize_event_type(u64 event_id, char *name, | ||
109 | event__handler_t process, | ||
110 | struct perf_session *session); | ||
111 | int event__synthesize_event_types(event__handler_t process, | ||
112 | struct perf_session *session); | ||
113 | int event__process_event_type(event_t *self, | ||
114 | struct perf_session *session); | ||
115 | |||
116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | ||
117 | event__handler_t process, | ||
118 | struct perf_session *session); | ||
119 | int event__process_tracing_data(event_t *self, | ||
120 | struct perf_session *session); | 84 | struct perf_session *session); |
85 | int perf_session__synthesize_attrs(struct perf_session *session, | ||
86 | perf_event__handler_t process); | ||
87 | int perf_event__process_attr(union perf_event *event, struct perf_session *session); | ||
88 | |||
89 | int perf_event__synthesize_event_type(u64 event_id, char *name, | ||
90 | perf_event__handler_t process, | ||
91 | struct perf_session *session); | ||
92 | int perf_event__synthesize_event_types(perf_event__handler_t process, | ||
93 | struct perf_session *session); | ||
94 | int perf_event__process_event_type(union perf_event *event, | ||
95 | struct perf_session *session); | ||
121 | 96 | ||
122 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 97 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
123 | event__handler_t process, | 98 | perf_event__handler_t process, |
124 | struct machine *machine, | 99 | struct perf_session *session); |
125 | struct perf_session *session); | 100 | int perf_event__process_tracing_data(union perf_event *event, |
126 | int event__process_build_id(event_t *self, struct perf_session *session); | 101 | struct perf_session *session); |
127 | 102 | ||
103 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | ||
104 | perf_event__handler_t process, | ||
105 | struct machine *machine, | ||
106 | struct perf_session *session); | ||
107 | int perf_event__process_build_id(union perf_event *event, | ||
108 | struct perf_session *session); | ||
128 | #endif /* __PERF_HEADER_H */ | 109 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index df51560f16f7..627a02e03c57 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include "annotate.h" | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include "build-id.h" | 3 | #include "build-id.h" |
3 | #include "hist.h" | 4 | #include "hist.h" |
@@ -49,6 +50,15 @@ static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | |||
49 | 50 | ||
50 | if (h->ms.sym) | 51 | if (h->ms.sym) |
51 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 52 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); |
53 | else { | ||
54 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
55 | |||
56 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | ||
57 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
58 | !symbol_conf.dso_list) | ||
59 | hists__set_col_len(self, HISTC_DSO, | ||
60 | unresolved_col_width); | ||
61 | } | ||
52 | 62 | ||
53 | len = thread__comm_len(h->thread); | 63 | len = thread__comm_len(h->thread); |
54 | if (hists__new_col_len(self, HISTC_COMM, len)) | 64 | if (hists__new_col_len(self, HISTC_COMM, len)) |
@@ -211,7 +221,9 @@ void hist_entry__free(struct hist_entry *he) | |||
211 | * collapse the histogram | 221 | * collapse the histogram |
212 | */ | 222 | */ |
213 | 223 | ||
214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 224 | static bool hists__collapse_insert_entry(struct hists *self, |
225 | struct rb_root *root, | ||
226 | struct hist_entry *he) | ||
215 | { | 227 | { |
216 | struct rb_node **p = &root->rb_node; | 228 | struct rb_node **p = &root->rb_node; |
217 | struct rb_node *parent = NULL; | 229 | struct rb_node *parent = NULL; |
@@ -226,8 +238,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
226 | 238 | ||
227 | if (!cmp) { | 239 | if (!cmp) { |
228 | iter->period += he->period; | 240 | iter->period += he->period; |
229 | if (symbol_conf.use_callchain) | 241 | if (symbol_conf.use_callchain) { |
230 | callchain_merge(iter->callchain, he->callchain); | 242 | callchain_cursor_reset(&self->callchain_cursor); |
243 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
244 | he->callchain); | ||
245 | } | ||
231 | hist_entry__free(he); | 246 | hist_entry__free(he); |
232 | return false; | 247 | return false; |
233 | } | 248 | } |
@@ -262,7 +277,7 @@ void hists__collapse_resort(struct hists *self) | |||
262 | next = rb_next(&n->rb_node); | 277 | next = rb_next(&n->rb_node); |
263 | 278 | ||
264 | rb_erase(&n->rb_node, &self->entries); | 279 | rb_erase(&n->rb_node, &self->entries); |
265 | if (collapse__insert_entry(&tmp, n)) | 280 | if (hists__collapse_insert_entry(self, &tmp, n)) |
266 | hists__inc_nr_entries(self, n); | 281 | hists__inc_nr_entries(self, n); |
267 | } | 282 | } |
268 | 283 | ||
@@ -425,7 +440,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
425 | u64 cumul; | 440 | u64 cumul; |
426 | 441 | ||
427 | child = rb_entry(node, struct callchain_node, rb_node); | 442 | child = rb_entry(node, struct callchain_node, rb_node); |
428 | cumul = cumul_hits(child); | 443 | cumul = callchain_cumul_hits(child); |
429 | remaining -= cumul; | 444 | remaining -= cumul; |
430 | 445 | ||
431 | /* | 446 | /* |
@@ -947,225 +962,14 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
947 | } | 962 | } |
948 | } | 963 | } |
949 | 964 | ||
950 | static int symbol__alloc_hist(struct symbol *self) | 965 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
951 | { | ||
952 | struct sym_priv *priv = symbol__priv(self); | ||
953 | const int size = (sizeof(*priv->hist) + | ||
954 | (self->end - self->start) * sizeof(u64)); | ||
955 | |||
956 | priv->hist = zalloc(size); | ||
957 | return priv->hist == NULL ? -1 : 0; | ||
958 | } | ||
959 | |||
960 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
961 | { | ||
962 | unsigned int sym_size, offset; | ||
963 | struct symbol *sym = self->ms.sym; | ||
964 | struct sym_priv *priv; | ||
965 | struct sym_hist *h; | ||
966 | |||
967 | if (!sym || !self->ms.map) | ||
968 | return 0; | ||
969 | |||
970 | priv = symbol__priv(sym); | ||
971 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
972 | return -ENOMEM; | ||
973 | |||
974 | sym_size = sym->end - sym->start; | ||
975 | offset = ip - sym->start; | ||
976 | |||
977 | pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
978 | |||
979 | if (offset >= sym_size) | ||
980 | return 0; | ||
981 | |||
982 | h = priv->hist; | ||
983 | h->sum++; | ||
984 | h->ip[offset]++; | ||
985 | |||
986 | pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64 | ||
987 | "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name, | ||
988 | ip, ip - self->ms.sym->start, h->ip[offset]); | ||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
993 | { | ||
994 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
995 | |||
996 | if (self != NULL) { | ||
997 | self->offset = offset; | ||
998 | self->line = line; | ||
999 | } | ||
1000 | |||
1001 | return self; | ||
1002 | } | ||
1003 | |||
1004 | void objdump_line__free(struct objdump_line *self) | ||
1005 | { | ||
1006 | free(self->line); | ||
1007 | free(self); | ||
1008 | } | ||
1009 | |||
1010 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
1011 | { | ||
1012 | list_add_tail(&line->node, head); | ||
1013 | } | ||
1014 | |||
1015 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
1016 | struct objdump_line *pos) | ||
1017 | { | ||
1018 | list_for_each_entry_continue(pos, head, node) | ||
1019 | if (pos->offset >= 0) | ||
1020 | return pos; | ||
1021 | |||
1022 | return NULL; | ||
1023 | } | ||
1024 | |||
1025 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
1026 | struct list_head *head, size_t privsize) | ||
1027 | { | 966 | { |
1028 | struct symbol *sym = self->ms.sym; | 967 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
1029 | struct objdump_line *objdump_line; | ||
1030 | char *line = NULL, *tmp, *tmp2, *c; | ||
1031 | size_t line_len; | ||
1032 | s64 line_ip, offset = -1; | ||
1033 | |||
1034 | if (getline(&line, &line_len, file) < 0) | ||
1035 | return -1; | ||
1036 | |||
1037 | if (!line) | ||
1038 | return -1; | ||
1039 | |||
1040 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
1041 | line[--line_len] = '\0'; | ||
1042 | |||
1043 | c = strchr(line, '\n'); | ||
1044 | if (c) | ||
1045 | *c = 0; | ||
1046 | |||
1047 | line_ip = -1; | ||
1048 | |||
1049 | /* | ||
1050 | * Strip leading spaces: | ||
1051 | */ | ||
1052 | tmp = line; | ||
1053 | while (*tmp) { | ||
1054 | if (*tmp != ' ') | ||
1055 | break; | ||
1056 | tmp++; | ||
1057 | } | ||
1058 | |||
1059 | if (*tmp) { | ||
1060 | /* | ||
1061 | * Parse hexa addresses followed by ':' | ||
1062 | */ | ||
1063 | line_ip = strtoull(tmp, &tmp2, 16); | ||
1064 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
1065 | line_ip = -1; | ||
1066 | } | ||
1067 | |||
1068 | if (line_ip != -1) { | ||
1069 | u64 start = map__rip_2objdump(self->ms.map, sym->start), | ||
1070 | end = map__rip_2objdump(self->ms.map, sym->end); | ||
1071 | |||
1072 | offset = line_ip - start; | ||
1073 | if (offset < 0 || (u64)line_ip > end) | ||
1074 | offset = -1; | ||
1075 | } | ||
1076 | |||
1077 | objdump_line = objdump_line__new(offset, line, privsize); | ||
1078 | if (objdump_line == NULL) { | ||
1079 | free(line); | ||
1080 | return -1; | ||
1081 | } | ||
1082 | objdump__add_line(head, objdump_line); | ||
1083 | |||
1084 | return 0; | ||
1085 | } | 968 | } |
1086 | 969 | ||
1087 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 970 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) |
1088 | size_t privsize) | ||
1089 | { | 971 | { |
1090 | struct symbol *sym = self->ms.sym; | 972 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
1091 | struct map *map = self->ms.map; | ||
1092 | struct dso *dso = map->dso; | ||
1093 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
1094 | bool free_filename = true; | ||
1095 | char command[PATH_MAX * 2]; | ||
1096 | FILE *file; | ||
1097 | int err = 0; | ||
1098 | u64 len; | ||
1099 | char symfs_filename[PATH_MAX]; | ||
1100 | |||
1101 | if (filename) { | ||
1102 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1103 | symbol_conf.symfs, filename); | ||
1104 | } | ||
1105 | |||
1106 | if (filename == NULL) { | ||
1107 | if (dso->has_build_id) { | ||
1108 | pr_err("Can't annotate %s: not enough memory\n", | ||
1109 | sym->name); | ||
1110 | return -ENOMEM; | ||
1111 | } | ||
1112 | goto fallback; | ||
1113 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
1114 | strstr(command, "[kernel.kallsyms]") || | ||
1115 | access(symfs_filename, R_OK)) { | ||
1116 | free(filename); | ||
1117 | fallback: | ||
1118 | /* | ||
1119 | * If we don't have build-ids or the build-id file isn't in the | ||
1120 | * cache, or is just a kallsyms file, well, lets hope that this | ||
1121 | * DSO is the same as when 'perf record' ran. | ||
1122 | */ | ||
1123 | filename = dso->long_name; | ||
1124 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1125 | symbol_conf.symfs, filename); | ||
1126 | free_filename = false; | ||
1127 | } | ||
1128 | |||
1129 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
1130 | if (dso->annotate_warned) | ||
1131 | goto out_free_filename; | ||
1132 | err = -ENOENT; | ||
1133 | dso->annotate_warned = 1; | ||
1134 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
1135 | "path\n", sym->name); | ||
1136 | goto out_free_filename; | ||
1137 | } | ||
1138 | |||
1139 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | ||
1140 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
1141 | map->unmap_ip(map, sym->end)); | ||
1142 | |||
1143 | len = sym->end - sym->start; | ||
1144 | |||
1145 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
1146 | dso, dso->long_name, sym, sym->name); | ||
1147 | |||
1148 | snprintf(command, sizeof(command), | ||
1149 | "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | ||
1150 | map__rip_2objdump(map, sym->start), | ||
1151 | map__rip_2objdump(map, sym->end), | ||
1152 | symfs_filename, filename); | ||
1153 | |||
1154 | pr_debug("Executing: %s\n", command); | ||
1155 | |||
1156 | file = popen(command, "r"); | ||
1157 | if (!file) | ||
1158 | goto out_free_filename; | ||
1159 | |||
1160 | while (!feof(file)) | ||
1161 | if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) | ||
1162 | break; | ||
1163 | |||
1164 | pclose(file); | ||
1165 | out_free_filename: | ||
1166 | if (free_filename) | ||
1167 | free(filename); | ||
1168 | return err; | ||
1169 | } | 973 | } |
1170 | 974 | ||
1171 | void hists__inc_nr_events(struct hists *self, u32 type) | 975 | void hists__inc_nr_events(struct hists *self, u32 type) |
@@ -1180,8 +984,12 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
1180 | size_t ret = 0; | 984 | size_t ret = 0; |
1181 | 985 | ||
1182 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
1183 | const char *name = event__get_event_name(i); | 987 | const char *name; |
988 | |||
989 | if (self->stats.nr_events[i] == 0) | ||
990 | continue; | ||
1184 | 991 | ||
992 | name = perf_event__name(i); | ||
1185 | if (!strcmp(name, "UNKNOWN")) | 993 | if (!strcmp(name, "UNKNOWN")) |
1186 | continue; | 994 | continue; |
1187 | 995 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c9..cb6858a2f9a3 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -9,33 +9,6 @@ extern struct callchain_param callchain_param; | |||
9 | struct hist_entry; | 9 | struct hist_entry; |
10 | struct addr_location; | 10 | struct addr_location; |
11 | struct symbol; | 11 | struct symbol; |
12 | struct rb_root; | ||
13 | |||
14 | struct objdump_line { | ||
15 | struct list_head node; | ||
16 | s64 offset; | ||
17 | char *line; | ||
18 | }; | ||
19 | |||
20 | void objdump_line__free(struct objdump_line *self); | ||
21 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
22 | struct objdump_line *pos); | ||
23 | |||
24 | struct sym_hist { | ||
25 | u64 sum; | ||
26 | u64 ip[0]; | ||
27 | }; | ||
28 | |||
29 | struct sym_ext { | ||
30 | struct rb_node node; | ||
31 | double percent; | ||
32 | char *path; | ||
33 | }; | ||
34 | |||
35 | struct sym_priv { | ||
36 | struct sym_hist *hist; | ||
37 | struct sym_ext *ext; | ||
38 | }; | ||
39 | 12 | ||
40 | /* | 13 | /* |
41 | * The kernel collects the number of events it couldn't send in a stretch and | 14 | * The kernel collects the number of events it couldn't send in a stretch and |
@@ -69,14 +42,13 @@ enum hist_column { | |||
69 | }; | 42 | }; |
70 | 43 | ||
71 | struct hists { | 44 | struct hists { |
72 | struct rb_node rb_node; | ||
73 | struct rb_root entries; | 45 | struct rb_root entries; |
74 | u64 nr_entries; | 46 | u64 nr_entries; |
75 | struct events_stats stats; | 47 | struct events_stats stats; |
76 | u64 config; | ||
77 | u64 event_stream; | 48 | u64 event_stream; |
78 | u32 type; | ||
79 | u16 col_len[HISTC_NR_COLS]; | 49 | u16 col_len[HISTC_NR_COLS]; |
50 | /* Best would be to reuse the session callchain cursor */ | ||
51 | struct callchain_cursor callchain_cursor; | ||
80 | }; | 52 | }; |
81 | 53 | ||
82 | struct hist_entry *__hists__add_entry(struct hists *self, | 54 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -102,9 +74,8 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | |||
102 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 74 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
103 | bool show_displacement, FILE *fp); | 75 | bool show_displacement, FILE *fp); |
104 | 76 | ||
105 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); | 77 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
106 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 78 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
107 | size_t privsize); | ||
108 | 79 | ||
109 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 80 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); |
110 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 81 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); |
@@ -113,21 +84,18 @@ u16 hists__col_len(struct hists *self, enum hist_column col); | |||
113 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 84 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
114 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 85 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
115 | 86 | ||
116 | #ifdef NO_NEWT_SUPPORT | 87 | struct perf_evlist; |
117 | static inline int hists__browse(struct hists *self __used, | ||
118 | const char *helpline __used, | ||
119 | const char *ev_name __used) | ||
120 | { | ||
121 | return 0; | ||
122 | } | ||
123 | 88 | ||
124 | static inline int hists__tui_browse_tree(struct rb_root *self __used, | 89 | #ifdef NO_NEWT_SUPPORT |
125 | const char *help __used) | 90 | static inline |
91 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | ||
92 | const char *help __used) | ||
126 | { | 93 | { |
127 | return 0; | 94 | return 0; |
128 | } | 95 | } |
129 | 96 | ||
130 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | 97 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
98 | int evidx __used) | ||
131 | { | 99 | { |
132 | return 0; | 100 | return 0; |
133 | } | 101 | } |
@@ -135,14 +103,12 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | |||
135 | #define KEY_RIGHT -2 | 103 | #define KEY_RIGHT -2 |
136 | #else | 104 | #else |
137 | #include <newt.h> | 105 | #include <newt.h> |
138 | int hists__browse(struct hists *self, const char *helpline, | 106 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); |
139 | const char *ev_name); | ||
140 | int hist_entry__tui_annotate(struct hist_entry *self); | ||
141 | 107 | ||
142 | #define KEY_LEFT NEWT_KEY_LEFT | 108 | #define KEY_LEFT NEWT_KEY_LEFT |
143 | #define KEY_RIGHT NEWT_KEY_RIGHT | 109 | #define KEY_RIGHT NEWT_KEY_RIGHT |
144 | 110 | ||
145 | int hists__tui_browse_tree(struct rb_root *self, const char *help); | 111 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); |
146 | #endif | 112 | #endif |
147 | 113 | ||
148 | unsigned int hists__sort_list_width(struct hists *self); | 114 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index f5ca26e53fbb..356c7e467b83 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <linux/kernel.h> | ||
1 | #include "../../../../include/linux/list.h" | 2 | #include "../../../../include/linux/list.h" |
2 | 3 | ||
3 | #ifndef PERF_LIST_H | 4 | #ifndef PERF_LIST_H |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 135f69baf966..952b4ae3d954 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include "../../../include/linux/hw_breakpoint.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "evlist.h" | ||
4 | #include "evsel.h" | 5 | #include "evsel.h" |
5 | #include "parse-options.h" | 6 | #include "parse-options.h" |
6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
@@ -11,10 +12,6 @@ | |||
11 | #include "header.h" | 12 | #include "header.h" |
12 | #include "debugfs.h" | 13 | #include "debugfs.h" |
13 | 14 | ||
14 | int nr_counters; | ||
15 | |||
16 | LIST_HEAD(evsel_list); | ||
17 | |||
18 | struct event_symbol { | 15 | struct event_symbol { |
19 | u8 type; | 16 | u8 type; |
20 | u64 config; | 17 | u64 config; |
@@ -266,11 +263,36 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) | |||
266 | return name; | 263 | return name; |
267 | } | 264 | } |
268 | 265 | ||
266 | const char *event_type(int type) | ||
267 | { | ||
268 | switch (type) { | ||
269 | case PERF_TYPE_HARDWARE: | ||
270 | return "hardware"; | ||
271 | |||
272 | case PERF_TYPE_SOFTWARE: | ||
273 | return "software"; | ||
274 | |||
275 | case PERF_TYPE_TRACEPOINT: | ||
276 | return "tracepoint"; | ||
277 | |||
278 | case PERF_TYPE_HW_CACHE: | ||
279 | return "hardware-cache"; | ||
280 | |||
281 | default: | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | return "unknown"; | ||
286 | } | ||
287 | |||
269 | const char *event_name(struct perf_evsel *evsel) | 288 | const char *event_name(struct perf_evsel *evsel) |
270 | { | 289 | { |
271 | u64 config = evsel->attr.config; | 290 | u64 config = evsel->attr.config; |
272 | int type = evsel->attr.type; | 291 | int type = evsel->attr.type; |
273 | 292 | ||
293 | if (evsel->name) | ||
294 | return evsel->name; | ||
295 | |||
274 | return __event_name(type, config); | 296 | return __event_name(type, config); |
275 | } | 297 | } |
276 | 298 | ||
@@ -449,8 +471,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
449 | /* sys + ':' + event + ':' + flags*/ | 471 | /* sys + ':' + event + ':' + flags*/ |
450 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 472 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
451 | static enum event_result | 473 | static enum event_result |
452 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | 474 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, |
453 | char *flags) | 475 | const char *evt_exp, char *flags) |
454 | { | 476 | { |
455 | char evt_path[MAXPATHLEN]; | 477 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 478 | struct dirent *evt_ent; |
@@ -483,15 +505,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
483 | if (len < 0) | 505 | if (len < 0) |
484 | return EVT_FAILED; | 506 | return EVT_FAILED; |
485 | 507 | ||
486 | if (parse_events(NULL, event_opt, 0)) | 508 | if (parse_events(opt, event_opt, 0)) |
487 | return EVT_FAILED; | 509 | return EVT_FAILED; |
488 | } | 510 | } |
489 | 511 | ||
490 | return EVT_HANDLED_ALL; | 512 | return EVT_HANDLED_ALL; |
491 | } | 513 | } |
492 | 514 | ||
493 | static enum event_result parse_tracepoint_event(const char **strp, | 515 | static enum event_result |
494 | struct perf_event_attr *attr) | 516 | parse_tracepoint_event(const struct option *opt, const char **strp, |
517 | struct perf_event_attr *attr) | ||
495 | { | 518 | { |
496 | const char *evt_name; | 519 | const char *evt_name; |
497 | char *flags = NULL, *comma_loc; | 520 | char *flags = NULL, *comma_loc; |
@@ -530,7 +553,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
530 | return EVT_FAILED; | 553 | return EVT_FAILED; |
531 | if (strpbrk(evt_name, "*?")) { | 554 | if (strpbrk(evt_name, "*?")) { |
532 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 555 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
533 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 556 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, |
534 | flags); | 557 | flags); |
535 | } else { | 558 | } else { |
536 | return parse_single_tracepoint_event(sys_name, evt_name, | 559 | return parse_single_tracepoint_event(sys_name, evt_name, |
@@ -740,11 +763,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
740 | * Symbolic names are (almost) exactly matched. | 763 | * Symbolic names are (almost) exactly matched. |
741 | */ | 764 | */ |
742 | static enum event_result | 765 | static enum event_result |
743 | parse_event_symbols(const char **str, struct perf_event_attr *attr) | 766 | parse_event_symbols(const struct option *opt, const char **str, |
767 | struct perf_event_attr *attr) | ||
744 | { | 768 | { |
745 | enum event_result ret; | 769 | enum event_result ret; |
746 | 770 | ||
747 | ret = parse_tracepoint_event(str, attr); | 771 | ret = parse_tracepoint_event(opt, str, attr); |
748 | if (ret != EVT_FAILED) | 772 | if (ret != EVT_FAILED) |
749 | goto modifier; | 773 | goto modifier; |
750 | 774 | ||
@@ -778,14 +802,17 @@ modifier: | |||
778 | return ret; | 802 | return ret; |
779 | } | 803 | } |
780 | 804 | ||
781 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 805 | int parse_events(const struct option *opt, const char *str, int unset __used) |
782 | { | 806 | { |
807 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
783 | struct perf_event_attr attr; | 808 | struct perf_event_attr attr; |
784 | enum event_result ret; | 809 | enum event_result ret; |
810 | const char *ostr; | ||
785 | 811 | ||
786 | for (;;) { | 812 | for (;;) { |
813 | ostr = str; | ||
787 | memset(&attr, 0, sizeof(attr)); | 814 | memset(&attr, 0, sizeof(attr)); |
788 | ret = parse_event_symbols(&str, &attr); | 815 | ret = parse_event_symbols(opt, &str, &attr); |
789 | if (ret == EVT_FAILED) | 816 | if (ret == EVT_FAILED) |
790 | return -1; | 817 | return -1; |
791 | 818 | ||
@@ -794,12 +821,15 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
794 | 821 | ||
795 | if (ret != EVT_HANDLED_ALL) { | 822 | if (ret != EVT_HANDLED_ALL) { |
796 | struct perf_evsel *evsel; | 823 | struct perf_evsel *evsel; |
797 | evsel = perf_evsel__new(&attr, | 824 | evsel = perf_evsel__new(&attr, evlist->nr_entries); |
798 | nr_counters); | ||
799 | if (evsel == NULL) | 825 | if (evsel == NULL) |
800 | return -1; | 826 | return -1; |
801 | list_add_tail(&evsel->node, &evsel_list); | 827 | perf_evlist__add(evlist, evsel); |
802 | ++nr_counters; | 828 | |
829 | evsel->name = calloc(str - ostr + 1, 1); | ||
830 | if (!evsel->name) | ||
831 | return -1; | ||
832 | strncpy(evsel->name, ostr, str - ostr); | ||
803 | } | 833 | } |
804 | 834 | ||
805 | if (*str == 0) | 835 | if (*str == 0) |
@@ -813,13 +843,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
813 | return 0; | 843 | return 0; |
814 | } | 844 | } |
815 | 845 | ||
816 | int parse_filter(const struct option *opt __used, const char *str, | 846 | int parse_filter(const struct option *opt, const char *str, |
817 | int unset __used) | 847 | int unset __used) |
818 | { | 848 | { |
849 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
819 | struct perf_evsel *last = NULL; | 850 | struct perf_evsel *last = NULL; |
820 | 851 | ||
821 | if (!list_empty(&evsel_list)) | 852 | if (evlist->nr_entries > 0) |
822 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | 853 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); |
823 | 854 | ||
824 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 855 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
825 | fprintf(stderr, | 856 | fprintf(stderr, |
@@ -849,7 +880,7 @@ static const char * const event_type_descriptors[] = { | |||
849 | * Print the events from <debugfs_mount_point>/tracing/events | 880 | * Print the events from <debugfs_mount_point>/tracing/events |
850 | */ | 881 | */ |
851 | 882 | ||
852 | static void print_tracepoint_events(void) | 883 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob) |
853 | { | 884 | { |
854 | DIR *sys_dir, *evt_dir; | 885 | DIR *sys_dir, *evt_dir; |
855 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 886 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
@@ -864,6 +895,9 @@ static void print_tracepoint_events(void) | |||
864 | return; | 895 | return; |
865 | 896 | ||
866 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | 897 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { |
898 | if (subsys_glob != NULL && | ||
899 | !strglobmatch(sys_dirent.d_name, subsys_glob)) | ||
900 | continue; | ||
867 | 901 | ||
868 | snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, | 902 | snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, |
869 | sys_dirent.d_name); | 903 | sys_dirent.d_name); |
@@ -872,6 +906,10 @@ static void print_tracepoint_events(void) | |||
872 | continue; | 906 | continue; |
873 | 907 | ||
874 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 908 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
909 | if (event_glob != NULL && | ||
910 | !strglobmatch(evt_dirent.d_name, event_glob)) | ||
911 | continue; | ||
912 | |||
875 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 913 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
876 | sys_dirent.d_name, evt_dirent.d_name); | 914 | sys_dirent.d_name, evt_dirent.d_name); |
877 | printf(" %-42s [%s]\n", evt_path, | 915 | printf(" %-42s [%s]\n", evt_path, |
@@ -923,13 +961,61 @@ int is_valid_tracepoint(const char *event_string) | |||
923 | return 0; | 961 | return 0; |
924 | } | 962 | } |
925 | 963 | ||
964 | void print_events_type(u8 type) | ||
965 | { | ||
966 | struct event_symbol *syms = event_symbols; | ||
967 | unsigned int i; | ||
968 | char name[64]; | ||
969 | |||
970 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | ||
971 | if (type != syms->type) | ||
972 | continue; | ||
973 | |||
974 | if (strlen(syms->alias)) | ||
975 | snprintf(name, sizeof(name), "%s OR %s", | ||
976 | syms->symbol, syms->alias); | ||
977 | else | ||
978 | snprintf(name, sizeof(name), "%s", syms->symbol); | ||
979 | |||
980 | printf(" %-42s [%s]\n", name, | ||
981 | event_type_descriptors[type]); | ||
982 | } | ||
983 | } | ||
984 | |||
985 | int print_hwcache_events(const char *event_glob) | ||
986 | { | ||
987 | unsigned int type, op, i, printed = 0; | ||
988 | |||
989 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
990 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
991 | /* skip invalid cache type */ | ||
992 | if (!is_cache_op_valid(type, op)) | ||
993 | continue; | ||
994 | |||
995 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
996 | char *name = event_cache_name(type, op, i); | ||
997 | |||
998 | if (event_glob != NULL && | ||
999 | !strglobmatch(name, event_glob)) | ||
1000 | continue; | ||
1001 | |||
1002 | printf(" %-42s [%s]\n", name, | ||
1003 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
1004 | ++printed; | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | return printed; | ||
1010 | } | ||
1011 | |||
926 | /* | 1012 | /* |
927 | * Print the help text for the event symbols: | 1013 | * Print the help text for the event symbols: |
928 | */ | 1014 | */ |
929 | void print_events(void) | 1015 | void print_events(const char *event_glob) |
930 | { | 1016 | { |
931 | struct event_symbol *syms = event_symbols; | 1017 | struct event_symbol *syms = event_symbols; |
932 | unsigned int i, type, op, prev_type = -1; | 1018 | unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0; |
933 | char name[40]; | 1019 | char name[40]; |
934 | 1020 | ||
935 | printf("\n"); | 1021 | printf("\n"); |
@@ -938,8 +1024,16 @@ void print_events(void) | |||
938 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 1024 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
939 | type = syms->type; | 1025 | type = syms->type; |
940 | 1026 | ||
941 | if (type != prev_type) | 1027 | if (type != prev_type && printed) { |
942 | printf("\n"); | 1028 | printf("\n"); |
1029 | printed = 0; | ||
1030 | ntypes_printed++; | ||
1031 | } | ||
1032 | |||
1033 | if (event_glob != NULL && | ||
1034 | !(strglobmatch(syms->symbol, event_glob) || | ||
1035 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | ||
1036 | continue; | ||
943 | 1037 | ||
944 | if (strlen(syms->alias)) | 1038 | if (strlen(syms->alias)) |
945 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 1039 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
@@ -949,22 +1043,17 @@ void print_events(void) | |||
949 | event_type_descriptors[type]); | 1043 | event_type_descriptors[type]); |
950 | 1044 | ||
951 | prev_type = type; | 1045 | prev_type = type; |
1046 | ++printed; | ||
952 | } | 1047 | } |
953 | 1048 | ||
954 | printf("\n"); | 1049 | if (ntypes_printed) { |
955 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 1050 | printed = 0; |
956 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 1051 | printf("\n"); |
957 | /* skip invalid cache type */ | ||
958 | if (!is_cache_op_valid(type, op)) | ||
959 | continue; | ||
960 | |||
961 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
962 | printf(" %-42s [%s]\n", | ||
963 | event_cache_name(type, op, i), | ||
964 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
965 | } | ||
966 | } | ||
967 | } | 1052 | } |
1053 | print_hwcache_events(event_glob); | ||
1054 | |||
1055 | if (event_glob != NULL) | ||
1056 | return; | ||
968 | 1057 | ||
969 | printf("\n"); | 1058 | printf("\n"); |
970 | printf(" %-42s [%s]\n", | 1059 | printf(" %-42s [%s]\n", |
@@ -977,37 +1066,7 @@ void print_events(void) | |||
977 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1066 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
978 | printf("\n"); | 1067 | printf("\n"); |
979 | 1068 | ||
980 | print_tracepoint_events(); | 1069 | print_tracepoint_events(NULL, NULL); |
981 | 1070 | ||
982 | exit(129); | 1071 | exit(129); |
983 | } | 1072 | } |
984 | |||
985 | int perf_evsel_list__create_default(void) | ||
986 | { | ||
987 | struct perf_evsel *evsel; | ||
988 | struct perf_event_attr attr; | ||
989 | |||
990 | memset(&attr, 0, sizeof(attr)); | ||
991 | attr.type = PERF_TYPE_HARDWARE; | ||
992 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
993 | |||
994 | evsel = perf_evsel__new(&attr, 0); | ||
995 | |||
996 | if (evsel == NULL) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | list_add(&evsel->node, &evsel_list); | ||
1000 | ++nr_counters; | ||
1001 | return 0; | ||
1002 | } | ||
1003 | |||
1004 | void perf_evsel_list__delete(void) | ||
1005 | { | ||
1006 | struct perf_evsel *pos, *n; | ||
1007 | |||
1008 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
1009 | list_del_init(&pos->node); | ||
1010 | perf_evsel__delete(pos); | ||
1011 | } | ||
1012 | nr_counters = 0; | ||
1013 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 458e3ecf17af..746d3fcbfc2a 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -9,11 +9,6 @@ | |||
9 | struct list_head; | 9 | struct list_head; |
10 | struct perf_evsel; | 10 | struct perf_evsel; |
11 | 11 | ||
12 | extern struct list_head evsel_list; | ||
13 | |||
14 | int perf_evsel_list__create_default(void); | ||
15 | void perf_evsel_list__delete(void); | ||
16 | |||
17 | struct option; | 12 | struct option; |
18 | 13 | ||
19 | struct tracepoint_path { | 14 | struct tracepoint_path { |
@@ -25,8 +20,7 @@ struct tracepoint_path { | |||
25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 20 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
26 | extern bool have_tracepoints(struct list_head *evlist); | 21 | extern bool have_tracepoints(struct list_head *evlist); |
27 | 22 | ||
28 | extern int nr_counters; | 23 | const char *event_type(int type); |
29 | |||
30 | const char *event_name(struct perf_evsel *event); | 24 | const char *event_name(struct perf_evsel *event); |
31 | extern const char *__event_name(int type, u64 config); | 25 | extern const char *__event_name(int type, u64 config); |
32 | 26 | ||
@@ -35,7 +29,10 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
35 | 29 | ||
36 | #define EVENTS_HELP_MAX (128*1024) | 30 | #define EVENTS_HELP_MAX (128*1024) |
37 | 31 | ||
38 | extern void print_events(void); | 32 | void print_events(const char *event_glob); |
33 | void print_events_type(u8 type); | ||
34 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | ||
35 | int print_hwcache_events(const char *event_glob); | ||
39 | extern int is_valid_tracepoint(const char *event_string); | 36 | extern int is_valid_tracepoint(const char *event_string); |
40 | 37 | ||
41 | extern char debugfs_path[]; | 38 | extern char debugfs_path[]; |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dccc..5ddee66020a7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | ||
34 | 35 | ||
35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
36 | #include "util.h" | 37 | #include "util.h" |
@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, | |||
111 | NULL); | 112 | NULL); |
112 | } | 113 | } |
113 | 114 | ||
114 | const char *kernel_get_module_path(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
116 | { | ||
117 | struct rb_node *nd; | ||
118 | struct map_groups *grp = &machine.kmaps; | ||
119 | |||
120 | if (!module) | ||
121 | module = "kernel"; | ||
122 | |||
123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
125 | if (strncmp(pos->dso->short_name + 1, module, | ||
126 | pos->dso->short_name_len - 2) == 0) { | ||
127 | return pos; | ||
128 | } | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dso *kernel_get_module_dso(const char *module) | ||
115 | { | 134 | { |
116 | struct dso *dso; | 135 | struct dso *dso; |
117 | struct map *map; | 136 | struct map *map; |
@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) | |||
141 | } | 160 | } |
142 | } | 161 | } |
143 | found: | 162 | found: |
144 | return dso->long_name; | 163 | return dso; |
164 | } | ||
165 | |||
166 | const char *kernel_get_module_path(const char *module) | ||
167 | { | ||
168 | struct dso *dso = kernel_get_module_dso(module); | ||
169 | return (dso) ? dso->long_name : NULL; | ||
145 | } | 170 | } |
146 | 171 | ||
147 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
@@ -384,7 +409,7 @@ int show_line_range(struct line_range *lr, const char *module) | |||
384 | setup_pager(); | 409 | setup_pager(); |
385 | 410 | ||
386 | if (lr->function) | 411 | if (lr->function) |
387 | fprintf(stdout, "<%s:%d>\n", lr->function, | 412 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
388 | lr->start - lr->offset); | 413 | lr->start - lr->offset); |
389 | else | 414 | else |
390 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 415 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
@@ -426,12 +451,14 @@ end: | |||
426 | } | 451 | } |
427 | 452 | ||
428 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 453 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, |
429 | int max_vls, bool externs) | 454 | int max_vls, struct strfilter *_filter, |
455 | bool externs) | ||
430 | { | 456 | { |
431 | char *buf; | 457 | char *buf; |
432 | int ret, i; | 458 | int ret, i, nvars; |
433 | struct str_node *node; | 459 | struct str_node *node; |
434 | struct variable_list *vls = NULL, *vl; | 460 | struct variable_list *vls = NULL, *vl; |
461 | const char *var; | ||
435 | 462 | ||
436 | buf = synthesize_perf_probe_point(&pev->point); | 463 | buf = synthesize_perf_probe_point(&pev->point); |
437 | if (!buf) | 464 | if (!buf) |
@@ -439,36 +466,45 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
439 | pr_debug("Searching variables at %s\n", buf); | 466 | pr_debug("Searching variables at %s\n", buf); |
440 | 467 | ||
441 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 468 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); |
442 | if (ret > 0) { | 469 | if (ret <= 0) { |
443 | /* Some variables were found */ | 470 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
444 | fprintf(stdout, "Available variables at %s\n", buf); | 471 | goto end; |
445 | for (i = 0; i < ret; i++) { | 472 | } |
446 | vl = &vls[i]; | 473 | /* Some variables are found */ |
447 | /* | 474 | fprintf(stdout, "Available variables at %s\n", buf); |
448 | * A probe point might be converted to | 475 | for (i = 0; i < ret; i++) { |
449 | * several trace points. | 476 | vl = &vls[i]; |
450 | */ | 477 | /* |
451 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 478 | * A probe point might be converted to |
452 | vl->point.offset); | 479 | * several trace points. |
453 | free(vl->point.symbol); | 480 | */ |
454 | if (vl->vars) { | 481 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
455 | strlist__for_each(node, vl->vars) | 482 | vl->point.offset); |
483 | free(vl->point.symbol); | ||
484 | nvars = 0; | ||
485 | if (vl->vars) { | ||
486 | strlist__for_each(node, vl->vars) { | ||
487 | var = strchr(node->s, '\t') + 1; | ||
488 | if (strfilter__compare(_filter, var)) { | ||
456 | fprintf(stdout, "\t\t%s\n", node->s); | 489 | fprintf(stdout, "\t\t%s\n", node->s); |
457 | strlist__delete(vl->vars); | 490 | nvars++; |
458 | } else | 491 | } |
459 | fprintf(stdout, "(No variables)\n"); | 492 | } |
493 | strlist__delete(vl->vars); | ||
460 | } | 494 | } |
461 | free(vls); | 495 | if (nvars == 0) |
462 | } else | 496 | fprintf(stdout, "\t\t(No matched variables)\n"); |
463 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 497 | } |
464 | 498 | free(vls); | |
499 | end: | ||
465 | free(buf); | 500 | free(buf); |
466 | return ret; | 501 | return ret; |
467 | } | 502 | } |
468 | 503 | ||
469 | /* Show available variables on given probe point */ | 504 | /* Show available variables on given probe point */ |
470 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 505 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
471 | int max_vls, const char *module, bool externs) | 506 | int max_vls, const char *module, |
507 | struct strfilter *_filter, bool externs) | ||
472 | { | 508 | { |
473 | int i, fd, ret = 0; | 509 | int i, fd, ret = 0; |
474 | 510 | ||
@@ -485,7 +521,8 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
485 | setup_pager(); | 521 | setup_pager(); |
486 | 522 | ||
487 | for (i = 0; i < npevs && ret >= 0; i++) | 523 | for (i = 0; i < npevs && ret >= 0; i++) |
488 | ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); | 524 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, |
525 | externs); | ||
489 | 526 | ||
490 | close(fd); | 527 | close(fd); |
491 | return ret; | 528 | return ret; |
@@ -531,7 +568,9 @@ int show_line_range(struct line_range *lr __unused, const char *module __unused) | |||
531 | 568 | ||
532 | int show_available_vars(struct perf_probe_event *pevs __unused, | 569 | int show_available_vars(struct perf_probe_event *pevs __unused, |
533 | int npevs __unused, int max_vls __unused, | 570 | int npevs __unused, int max_vls __unused, |
534 | const char *module __unused, bool externs __unused) | 571 | const char *module __unused, |
572 | struct strfilter *filter __unused, | ||
573 | bool externs __unused) | ||
535 | { | 574 | { |
536 | pr_warning("Debuginfo-analysis is not supported.\n"); | 575 | pr_warning("Debuginfo-analysis is not supported.\n"); |
537 | return -ENOSYS; | 576 | return -ENOSYS; |
@@ -556,11 +595,11 @@ static int parse_line_num(char **ptr, int *val, const char *what) | |||
556 | * The line range syntax is described by: | 595 | * The line range syntax is described by: |
557 | * | 596 | * |
558 | * SRC[:SLN[+NUM|-ELN]] | 597 | * SRC[:SLN[+NUM|-ELN]] |
559 | * FNC[:SLN[+NUM|-ELN]] | 598 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
560 | */ | 599 | */ |
561 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 600 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
562 | { | 601 | { |
563 | char *range, *name = strdup(arg); | 602 | char *range, *file, *name = strdup(arg); |
564 | int err; | 603 | int err; |
565 | 604 | ||
566 | if (!name) | 605 | if (!name) |
@@ -610,7 +649,16 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) | |||
610 | } | 649 | } |
611 | } | 650 | } |
612 | 651 | ||
613 | if (strchr(name, '.')) | 652 | file = strchr(name, '@'); |
653 | if (file) { | ||
654 | *file = '\0'; | ||
655 | lr->file = strdup(++file); | ||
656 | if (lr->file == NULL) { | ||
657 | err = -ENOMEM; | ||
658 | goto err; | ||
659 | } | ||
660 | lr->function = name; | ||
661 | } else if (strchr(name, '.')) | ||
614 | lr->file = name; | 662 | lr->file = name; |
615 | else | 663 | else |
616 | lr->function = name; | 664 | lr->function = name; |
@@ -1784,9 +1832,12 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1784 | } | 1832 | } |
1785 | 1833 | ||
1786 | /* Loop 2: add all events */ | 1834 | /* Loop 2: add all events */ |
1787 | for (i = 0; i < npevs && ret >= 0; i++) | 1835 | for (i = 0; i < npevs; i++) { |
1788 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1836 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1789 | pkgs[i].ntevs, force_add); | 1837 | pkgs[i].ntevs, force_add); |
1838 | if (ret < 0) | ||
1839 | break; | ||
1840 | } | ||
1790 | end: | 1841 | end: |
1791 | /* Loop 3: cleanup and free trace events */ | 1842 | /* Loop 3: cleanup and free trace events */ |
1792 | for (i = 0; i < npevs; i++) { | 1843 | for (i = 0; i < npevs; i++) { |
@@ -1912,4 +1963,46 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1912 | 1963 | ||
1913 | return ret; | 1964 | return ret; |
1914 | } | 1965 | } |
1966 | /* TODO: don't use a global variable for filter ... */ | ||
1967 | static struct strfilter *available_func_filter; | ||
1968 | |||
1969 | /* | ||
1970 | * If a symbol corresponds to a function with global binding and | ||
1971 | * matches filter return 0. For all others return 1. | ||
1972 | */ | ||
1973 | static int filter_available_functions(struct map *map __unused, | ||
1974 | struct symbol *sym) | ||
1975 | { | ||
1976 | if (sym->binding == STB_GLOBAL && | ||
1977 | strfilter__compare(available_func_filter, sym->name)) | ||
1978 | return 0; | ||
1979 | return 1; | ||
1980 | } | ||
1981 | |||
1982 | int show_available_funcs(const char *module, struct strfilter *_filter) | ||
1983 | { | ||
1984 | struct map *map; | ||
1985 | int ret; | ||
1986 | |||
1987 | setup_pager(); | ||
1988 | |||
1989 | ret = init_vmlinux(); | ||
1990 | if (ret < 0) | ||
1991 | return ret; | ||
1915 | 1992 | ||
1993 | map = kernel_get_module_map(module); | ||
1994 | if (!map) { | ||
1995 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1996 | return -EINVAL; | ||
1997 | } | ||
1998 | available_func_filter = _filter; | ||
1999 | if (map__load(map, filter_available_functions)) { | ||
2000 | pr_err("Failed to load map.\n"); | ||
2001 | return -EINVAL; | ||
2002 | } | ||
2003 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
2004 | dso__sort_by_name(map->dso, map->type); | ||
2005 | |||
2006 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
2007 | return 0; | ||
2008 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea37..3434fc9d79d5 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "strlist.h" | 5 | #include "strlist.h" |
6 | #include "strfilter.h" | ||
6 | 7 | ||
7 | extern bool probe_event_dry_run; | 8 | extern bool probe_event_dry_run; |
8 | 9 | ||
@@ -126,7 +127,8 @@ extern int show_perf_probe_events(void); | |||
126 | extern int show_line_range(struct line_range *lr, const char *module); | 127 | extern int show_line_range(struct line_range *lr, const char *module); |
127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 128 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
128 | int max_probe_points, const char *module, | 129 | int max_probe_points, const char *module, |
129 | bool externs); | 130 | struct strfilter *filter, bool externs); |
131 | extern int show_available_funcs(const char *module, struct strfilter *filter); | ||
130 | 132 | ||
131 | 133 | ||
132 | /* Maximum index number of event-name postfix */ | 134 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d65..194f9e2a3285 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <ctype.h> | 33 | #include <ctype.h> |
34 | #include <dwarf-regs.h> | 34 | #include <dwarf-regs.h> |
35 | 35 | ||
36 | #include <linux/bitops.h> | ||
36 | #include "event.h" | 37 | #include "event.h" |
37 | #include "debug.h" | 38 | #include "debug.h" |
38 | #include "util.h" | 39 | #include "util.h" |
@@ -280,6 +281,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
280 | return name ? (strcmp(tname, name) == 0) : false; | 281 | return name ? (strcmp(tname, name) == 0) : false; |
281 | } | 282 | } |
282 | 283 | ||
284 | /* Get callsite line number of inline-function instance */ | ||
285 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
286 | { | ||
287 | Dwarf_Attribute attr; | ||
288 | Dwarf_Word ret; | ||
289 | |||
290 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
291 | return -ENOENT; | ||
292 | |||
293 | dwarf_formudata(&attr, &ret); | ||
294 | return (int)ret; | ||
295 | } | ||
296 | |||
283 | /* Get type die */ | 297 | /* Get type die */ |
284 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 298 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
285 | { | 299 | { |
@@ -320,13 +334,23 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | |||
320 | return vr_die; | 334 | return vr_die; |
321 | } | 335 | } |
322 | 336 | ||
323 | static bool die_is_signed_type(Dwarf_Die *tp_die) | 337 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, |
338 | Dwarf_Word *result) | ||
324 | { | 339 | { |
325 | Dwarf_Attribute attr; | 340 | Dwarf_Attribute attr; |
341 | |||
342 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
343 | dwarf_formudata(&attr, result) != 0) | ||
344 | return -ENOENT; | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static bool die_is_signed_type(Dwarf_Die *tp_die) | ||
350 | { | ||
326 | Dwarf_Word ret; | 351 | Dwarf_Word ret; |
327 | 352 | ||
328 | if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || | 353 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) |
329 | dwarf_formudata(&attr, &ret) != 0) | ||
330 | return false; | 354 | return false; |
331 | 355 | ||
332 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | 356 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || |
@@ -335,11 +359,29 @@ static bool die_is_signed_type(Dwarf_Die *tp_die) | |||
335 | 359 | ||
336 | static int die_get_byte_size(Dwarf_Die *tp_die) | 360 | static int die_get_byte_size(Dwarf_Die *tp_die) |
337 | { | 361 | { |
338 | Dwarf_Attribute attr; | ||
339 | Dwarf_Word ret; | 362 | Dwarf_Word ret; |
340 | 363 | ||
341 | if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || | 364 | if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) |
342 | dwarf_formudata(&attr, &ret) != 0) | 365 | return 0; |
366 | |||
367 | return (int)ret; | ||
368 | } | ||
369 | |||
370 | static int die_get_bit_size(Dwarf_Die *tp_die) | ||
371 | { | ||
372 | Dwarf_Word ret; | ||
373 | |||
374 | if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) | ||
375 | return 0; | ||
376 | |||
377 | return (int)ret; | ||
378 | } | ||
379 | |||
380 | static int die_get_bit_offset(Dwarf_Die *tp_die) | ||
381 | { | ||
382 | Dwarf_Word ret; | ||
383 | |||
384 | if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) | ||
343 | return 0; | 385 | return 0; |
344 | 386 | ||
345 | return (int)ret; | 387 | return (int)ret; |
@@ -458,6 +500,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | |||
458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 500 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
459 | } | 501 | } |
460 | 502 | ||
503 | /* Walker on lines (Note: line number will not be sorted) */ | ||
504 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
505 | Dwarf_Addr addr, void *data); | ||
506 | |||
507 | struct __line_walk_param { | ||
508 | const char *fname; | ||
509 | line_walk_handler_t handler; | ||
510 | void *data; | ||
511 | int retval; | ||
512 | }; | ||
513 | |||
514 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
515 | { | ||
516 | struct __line_walk_param *lw = data; | ||
517 | Dwarf_Addr addr; | ||
518 | int lineno; | ||
519 | |||
520 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
521 | lineno = die_get_call_lineno(in_die); | ||
522 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
523 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
524 | lw->data); | ||
525 | if (lw->retval != 0) | ||
526 | return DIE_FIND_CB_FOUND; | ||
527 | } | ||
528 | } | ||
529 | return DIE_FIND_CB_SIBLING; | ||
530 | } | ||
531 | |||
532 | /* Walk on lines of blocks included in given DIE */ | ||
533 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
534 | line_walk_handler_t handler, void *data) | ||
535 | { | ||
536 | struct __line_walk_param lw = { | ||
537 | .handler = handler, | ||
538 | .data = data, | ||
539 | .retval = 0, | ||
540 | }; | ||
541 | Dwarf_Die die_mem; | ||
542 | Dwarf_Addr addr; | ||
543 | int lineno; | ||
544 | |||
545 | /* Handle function declaration line */ | ||
546 | lw.fname = dwarf_decl_file(sp_die); | ||
547 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
548 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
549 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
550 | if (lw.retval != 0) | ||
551 | goto done; | ||
552 | } | ||
553 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
554 | done: | ||
555 | return lw.retval; | ||
556 | } | ||
557 | |||
558 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
559 | { | ||
560 | struct __line_walk_param *lw = data; | ||
561 | |||
562 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
563 | if (lw->retval != 0) | ||
564 | return DWARF_CB_ABORT; | ||
565 | |||
566 | return DWARF_CB_OK; | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
571 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
572 | */ | ||
573 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
574 | void *data) | ||
575 | { | ||
576 | Dwarf_Lines *lines; | ||
577 | Dwarf_Line *line; | ||
578 | Dwarf_Addr addr; | ||
579 | const char *fname; | ||
580 | int lineno, ret = 0; | ||
581 | Dwarf_Die die_mem, *cu_die; | ||
582 | size_t nlines, i; | ||
583 | |||
584 | /* Get the CU die */ | ||
585 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
586 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
587 | else | ||
588 | cu_die = pdie; | ||
589 | if (!cu_die) { | ||
590 | pr_debug2("Failed to get CU from subprogram\n"); | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | /* Get lines list in the CU */ | ||
595 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
596 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
597 | return -ENOENT; | ||
598 | } | ||
599 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
600 | |||
601 | /* Walk on the lines on lines list */ | ||
602 | for (i = 0; i < nlines; i++) { | ||
603 | line = dwarf_onesrcline(lines, i); | ||
604 | if (line == NULL || | ||
605 | dwarf_lineno(line, &lineno) != 0 || | ||
606 | dwarf_lineaddr(line, &addr) != 0) { | ||
607 | pr_debug2("Failed to get line info. " | ||
608 | "Possible error in debuginfo.\n"); | ||
609 | continue; | ||
610 | } | ||
611 | /* Filter lines based on address */ | ||
612 | if (pdie != cu_die) | ||
613 | /* | ||
614 | * Address filtering | ||
615 | * The line is included in given function, and | ||
616 | * no inline block includes it. | ||
617 | */ | ||
618 | if (!dwarf_haspc(pdie, addr) || | ||
619 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
620 | continue; | ||
621 | /* Get source line */ | ||
622 | fname = dwarf_linesrc(line, NULL, NULL); | ||
623 | |||
624 | ret = handler(fname, lineno, addr, data); | ||
625 | if (ret != 0) | ||
626 | return ret; | ||
627 | } | ||
628 | |||
629 | /* | ||
630 | * Dwarf lines doesn't include function declarations and inlined | ||
631 | * subroutines. We have to check functions list or given function. | ||
632 | */ | ||
633 | if (pdie != cu_die) | ||
634 | ret = __die_walk_funclines(pdie, handler, data); | ||
635 | else { | ||
636 | struct __line_walk_param param = { | ||
637 | .handler = handler, | ||
638 | .data = data, | ||
639 | .retval = 0, | ||
640 | }; | ||
641 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
642 | ret = param.retval; | ||
643 | } | ||
644 | |||
645 | return ret; | ||
646 | } | ||
647 | |||
461 | struct __find_variable_param { | 648 | struct __find_variable_param { |
462 | const char *name; | 649 | const char *name; |
463 | Dwarf_Addr addr; | 650 | Dwarf_Addr addr; |
@@ -669,6 +856,8 @@ static_var: | |||
669 | return 0; | 856 | return 0; |
670 | } | 857 | } |
671 | 858 | ||
859 | #define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) | ||
860 | |||
672 | static int convert_variable_type(Dwarf_Die *vr_die, | 861 | static int convert_variable_type(Dwarf_Die *vr_die, |
673 | struct probe_trace_arg *tvar, | 862 | struct probe_trace_arg *tvar, |
674 | const char *cast) | 863 | const char *cast) |
@@ -685,6 +874,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
685 | return (tvar->type == NULL) ? -ENOMEM : 0; | 874 | return (tvar->type == NULL) ? -ENOMEM : 0; |
686 | } | 875 | } |
687 | 876 | ||
877 | if (die_get_bit_size(vr_die) != 0) { | ||
878 | /* This is a bitfield */ | ||
879 | ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), | ||
880 | die_get_bit_offset(vr_die), | ||
881 | BYTES_TO_BITS(die_get_byte_size(vr_die))); | ||
882 | goto formatted; | ||
883 | } | ||
884 | |||
688 | if (die_get_real_type(vr_die, &type) == NULL) { | 885 | if (die_get_real_type(vr_die, &type) == NULL) { |
689 | pr_warning("Failed to get a type information of %s.\n", | 886 | pr_warning("Failed to get a type information of %s.\n", |
690 | dwarf_diename(vr_die)); | 887 | dwarf_diename(vr_die)); |
@@ -729,29 +926,31 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
729 | return (tvar->type == NULL) ? -ENOMEM : 0; | 926 | return (tvar->type == NULL) ? -ENOMEM : 0; |
730 | } | 927 | } |
731 | 928 | ||
732 | ret = die_get_byte_size(&type) * 8; | 929 | ret = BYTES_TO_BITS(die_get_byte_size(&type)); |
733 | if (ret) { | 930 | if (!ret) |
734 | /* Check the bitwidth */ | 931 | /* No size ... try to use default type */ |
735 | if (ret > MAX_BASIC_TYPE_BITS) { | 932 | return 0; |
736 | pr_info("%s exceeds max-bitwidth." | ||
737 | " Cut down to %d bits.\n", | ||
738 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); | ||
739 | ret = MAX_BASIC_TYPE_BITS; | ||
740 | } | ||
741 | 933 | ||
742 | ret = snprintf(buf, 16, "%c%d", | 934 | /* Check the bitwidth */ |
743 | die_is_signed_type(&type) ? 's' : 'u', ret); | 935 | if (ret > MAX_BASIC_TYPE_BITS) { |
744 | if (ret < 0 || ret >= 16) { | 936 | pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", |
745 | if (ret >= 16) | 937 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); |
746 | ret = -E2BIG; | 938 | ret = MAX_BASIC_TYPE_BITS; |
747 | pr_warning("Failed to convert variable type: %s\n", | 939 | } |
748 | strerror(-ret)); | 940 | ret = snprintf(buf, 16, "%c%d", |
749 | return ret; | 941 | die_is_signed_type(&type) ? 's' : 'u', ret); |
750 | } | 942 | |
751 | tvar->type = strdup(buf); | 943 | formatted: |
752 | if (tvar->type == NULL) | 944 | if (ret < 0 || ret >= 16) { |
753 | return -ENOMEM; | 945 | if (ret >= 16) |
946 | ret = -E2BIG; | ||
947 | pr_warning("Failed to convert variable type: %s\n", | ||
948 | strerror(-ret)); | ||
949 | return ret; | ||
754 | } | 950 | } |
951 | tvar->type = strdup(buf); | ||
952 | if (tvar->type == NULL) | ||
953 | return -ENOMEM; | ||
755 | return 0; | 954 | return 0; |
756 | } | 955 | } |
757 | 956 | ||
@@ -1050,157 +1249,102 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1050 | return ret; | 1249 | return ret; |
1051 | } | 1250 | } |
1052 | 1251 | ||
1053 | /* Find probe point from its line number */ | 1252 | static int probe_point_line_walker(const char *fname, int lineno, |
1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1253 | Dwarf_Addr addr, void *data) |
1055 | { | 1254 | { |
1056 | Dwarf_Lines *lines; | 1255 | struct probe_finder *pf = data; |
1057 | Dwarf_Line *line; | 1256 | int ret; |
1058 | size_t nlines, i; | ||
1059 | Dwarf_Addr addr; | ||
1060 | int lineno; | ||
1061 | int ret = 0; | ||
1062 | |||
1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | ||
1064 | pr_warning("No source lines found.\n"); | ||
1065 | return -ENOENT; | ||
1066 | } | ||
1067 | 1257 | ||
1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1258 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1069 | line = dwarf_onesrcline(lines, i); | 1259 | return 0; |
1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1071 | lineno != pf->lno) | ||
1072 | continue; | ||
1073 | 1260 | ||
1074 | /* TODO: Get fileno from line, but how? */ | 1261 | pf->addr = addr; |
1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1262 | ret = call_probe_finder(NULL, pf); |
1076 | continue; | ||
1077 | 1263 | ||
1078 | if (dwarf_lineaddr(line, &addr) != 0) { | 1264 | /* Continue if no error, because the line will be in inline function */ |
1079 | pr_warning("Failed to get the address of the line.\n"); | 1265 | return ret < 0 ? ret : 0; |
1080 | return -ENOENT; | 1266 | } |
1081 | } | ||
1082 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
1083 | (int)i, lineno, (uintmax_t)addr); | ||
1084 | pf->addr = addr; | ||
1085 | 1267 | ||
1086 | ret = call_probe_finder(NULL, pf); | 1268 | /* Find probe point from its line number */ |
1087 | /* Continuing, because target line might be inlined. */ | 1269 | static int find_probe_point_by_line(struct probe_finder *pf) |
1088 | } | 1270 | { |
1089 | return ret; | 1271 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
1090 | } | 1272 | } |
1091 | 1273 | ||
1092 | /* Find lines which match lazy pattern */ | 1274 | /* Find lines which match lazy pattern */ |
1093 | static int find_lazy_match_lines(struct list_head *head, | 1275 | static int find_lazy_match_lines(struct list_head *head, |
1094 | const char *fname, const char *pat) | 1276 | const char *fname, const char *pat) |
1095 | { | 1277 | { |
1096 | char *fbuf, *p1, *p2; | 1278 | FILE *fp; |
1097 | int fd, line, nlines = -1; | 1279 | char *line = NULL; |
1098 | struct stat st; | 1280 | size_t line_len; |
1099 | 1281 | ssize_t len; | |
1100 | fd = open(fname, O_RDONLY); | 1282 | int count = 0, linenum = 1; |
1101 | if (fd < 0) { | 1283 | |
1102 | pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); | 1284 | fp = fopen(fname, "r"); |
1285 | if (!fp) { | ||
1286 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | ||
1103 | return -errno; | 1287 | return -errno; |
1104 | } | 1288 | } |
1105 | 1289 | ||
1106 | if (fstat(fd, &st) < 0) { | 1290 | while ((len = getline(&line, &line_len, fp)) > 0) { |
1107 | pr_warning("Failed to get the size of %s: %s\n", | 1291 | |
1108 | fname, strerror(errno)); | 1292 | if (line[len - 1] == '\n') |
1109 | nlines = -errno; | 1293 | line[len - 1] = '\0'; |
1110 | goto out_close; | 1294 | |
1111 | } | 1295 | if (strlazymatch(line, pat)) { |
1112 | 1296 | line_list__add_line(head, linenum); | |
1113 | nlines = -ENOMEM; | 1297 | count++; |
1114 | fbuf = malloc(st.st_size + 2); | ||
1115 | if (fbuf == NULL) | ||
1116 | goto out_close; | ||
1117 | if (read(fd, fbuf, st.st_size) < 0) { | ||
1118 | pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); | ||
1119 | nlines = -errno; | ||
1120 | goto out_free_fbuf; | ||
1121 | } | ||
1122 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
1123 | fbuf[st.st_size + 1] = '\0'; | ||
1124 | p1 = fbuf; | ||
1125 | line = 1; | ||
1126 | nlines = 0; | ||
1127 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
1128 | *p2 = '\0'; | ||
1129 | if (strlazymatch(p1, pat)) { | ||
1130 | line_list__add_line(head, line); | ||
1131 | nlines++; | ||
1132 | } | 1298 | } |
1133 | line++; | 1299 | linenum++; |
1134 | p1 = p2 + 1; | ||
1135 | } | 1300 | } |
1136 | out_free_fbuf: | 1301 | |
1137 | free(fbuf); | 1302 | if (ferror(fp)) |
1138 | out_close: | 1303 | count = -errno; |
1139 | close(fd); | 1304 | free(line); |
1140 | return nlines; | 1305 | fclose(fp); |
1306 | |||
1307 | if (count == 0) | ||
1308 | pr_debug("No matched lines found in %s.\n", fname); | ||
1309 | return count; | ||
1310 | } | ||
1311 | |||
1312 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
1313 | Dwarf_Addr addr, void *data) | ||
1314 | { | ||
1315 | struct probe_finder *pf = data; | ||
1316 | int ret; | ||
1317 | |||
1318 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
1319 | strtailcmp(fname, pf->fname) != 0) | ||
1320 | return 0; | ||
1321 | |||
1322 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
1323 | lineno, (unsigned long long)addr); | ||
1324 | pf->addr = addr; | ||
1325 | ret = call_probe_finder(NULL, pf); | ||
1326 | |||
1327 | /* | ||
1328 | * Continue if no error, because the lazy pattern will match | ||
1329 | * to other lines | ||
1330 | */ | ||
1331 | return ret < 0 ? ret : 0; | ||
1141 | } | 1332 | } |
1142 | 1333 | ||
1143 | /* Find probe points from lazy pattern */ | 1334 | /* Find probe points from lazy pattern */ |
1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1335 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
1145 | { | 1336 | { |
1146 | Dwarf_Lines *lines; | ||
1147 | Dwarf_Line *line; | ||
1148 | size_t nlines, i; | ||
1149 | Dwarf_Addr addr; | ||
1150 | Dwarf_Die die_mem; | ||
1151 | int lineno; | ||
1152 | int ret = 0; | 1337 | int ret = 0; |
1153 | 1338 | ||
1154 | if (list_empty(&pf->lcache)) { | 1339 | if (list_empty(&pf->lcache)) { |
1155 | /* Matching lazy line pattern */ | 1340 | /* Matching lazy line pattern */ |
1156 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | 1341 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, |
1157 | pf->pev->point.lazy_line); | 1342 | pf->pev->point.lazy_line); |
1158 | if (ret == 0) { | 1343 | if (ret <= 0) |
1159 | pr_debug("No matched lines found in %s.\n", pf->fname); | ||
1160 | return 0; | ||
1161 | } else if (ret < 0) | ||
1162 | return ret; | 1344 | return ret; |
1163 | } | 1345 | } |
1164 | 1346 | ||
1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1347 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
1166 | pr_warning("No source lines found.\n"); | ||
1167 | return -ENOENT; | ||
1168 | } | ||
1169 | |||
1170 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
1171 | line = dwarf_onesrcline(lines, i); | ||
1172 | |||
1173 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1174 | !line_list__has_line(&pf->lcache, lineno)) | ||
1175 | continue; | ||
1176 | |||
1177 | /* TODO: Get fileno from line, but how? */ | ||
1178 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
1179 | continue; | ||
1180 | |||
1181 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
1182 | pr_debug("Failed to get the address of line %d.\n", | ||
1183 | lineno); | ||
1184 | continue; | ||
1185 | } | ||
1186 | if (sp_die) { | ||
1187 | /* Address filtering 1: does sp_die include addr? */ | ||
1188 | if (!dwarf_haspc(sp_die, addr)) | ||
1189 | continue; | ||
1190 | /* Address filtering 2: No child include addr? */ | ||
1191 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1192 | continue; | ||
1193 | } | ||
1194 | |||
1195 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
1196 | (int)i, lineno, (unsigned long long)addr); | ||
1197 | pf->addr = addr; | ||
1198 | |||
1199 | ret = call_probe_finder(sp_die, pf); | ||
1200 | /* Continuing, because target line might be inlined. */ | ||
1201 | } | ||
1202 | /* TODO: deallocate lines, but how? */ | ||
1203 | return ret; | ||
1204 | } | 1348 | } |
1205 | 1349 | ||
1206 | /* Callback parameter with return value */ | 1350 | /* Callback parameter with return value */ |
@@ -1318,8 +1462,7 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1318 | off = 0; | 1462 | off = 0; |
1319 | line_list__init(&pf->lcache); | 1463 | line_list__init(&pf->lcache); |
1320 | /* Loop on CUs (Compilation Unit) */ | 1464 | /* Loop on CUs (Compilation Unit) */ |
1321 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && | 1465 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1322 | ret >= 0) { | ||
1323 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1466 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1324 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1467 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); |
1325 | if (!diep) | 1468 | if (!diep) |
@@ -1340,6 +1483,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1340 | pf->lno = pp->line; | 1483 | pf->lno = pp->line; |
1341 | ret = find_probe_point_by_line(pf); | 1484 | ret = find_probe_point_by_line(pf); |
1342 | } | 1485 | } |
1486 | if (ret < 0) | ||
1487 | break; | ||
1343 | } | 1488 | } |
1344 | off = noff; | 1489 | off = noff; |
1345 | } | 1490 | } |
@@ -1644,91 +1789,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
1644 | return line_list__add_line(&lr->line_list, lineno); | 1789 | return line_list__add_line(&lr->line_list, lineno); |
1645 | } | 1790 | } |
1646 | 1791 | ||
1647 | /* Search function declaration lines */ | 1792 | static int line_range_walk_cb(const char *fname, int lineno, |
1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1793 | Dwarf_Addr addr __used, |
1794 | void *data) | ||
1649 | { | 1795 | { |
1650 | struct dwarf_callback_param *param = data; | 1796 | struct line_finder *lf = data; |
1651 | struct line_finder *lf = param->data; | ||
1652 | const char *src; | ||
1653 | int lineno; | ||
1654 | 1797 | ||
1655 | src = dwarf_decl_file(sp_die); | 1798 | if ((strtailcmp(fname, lf->fname) != 0) || |
1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
1657 | return DWARF_CB_OK; | ||
1658 | |||
1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | ||
1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1799 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
1661 | return DWARF_CB_OK; | 1800 | return 0; |
1662 | 1801 | ||
1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1802 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
1664 | if (param->retval < 0) | 1803 | return -EINVAL; |
1665 | return DWARF_CB_ABORT; | ||
1666 | return DWARF_CB_OK; | ||
1667 | } | ||
1668 | 1804 | ||
1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1805 | return 0; |
1670 | { | ||
1671 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
1672 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
1673 | return param.retval; | ||
1674 | } | 1806 | } |
1675 | 1807 | ||
1676 | /* Find line range from its line number */ | 1808 | /* Find line range from its line number */ |
1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1809 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
1678 | { | 1810 | { |
1679 | Dwarf_Lines *lines; | 1811 | int ret; |
1680 | Dwarf_Line *line; | ||
1681 | size_t nlines, i; | ||
1682 | Dwarf_Addr addr; | ||
1683 | int lineno, ret = 0; | ||
1684 | const char *src; | ||
1685 | Dwarf_Die die_mem; | ||
1686 | |||
1687 | line_list__init(&lf->lr->line_list); | ||
1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | ||
1689 | pr_warning("No source lines found.\n"); | ||
1690 | return -ENOENT; | ||
1691 | } | ||
1692 | |||
1693 | /* Search probable lines on lines list */ | ||
1694 | for (i = 0; i < nlines; i++) { | ||
1695 | line = dwarf_onesrcline(lines, i); | ||
1696 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1697 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
1698 | continue; | ||
1699 | |||
1700 | if (sp_die) { | ||
1701 | /* Address filtering 1: does sp_die include addr? */ | ||
1702 | if (dwarf_lineaddr(line, &addr) != 0 || | ||
1703 | !dwarf_haspc(sp_die, addr)) | ||
1704 | continue; | ||
1705 | |||
1706 | /* Address filtering 2: No child include addr? */ | ||
1707 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1708 | continue; | ||
1709 | } | ||
1710 | |||
1711 | /* TODO: Get fileno from line, but how? */ | ||
1712 | src = dwarf_linesrc(line, NULL, NULL); | ||
1713 | if (strtailcmp(src, lf->fname) != 0) | ||
1714 | continue; | ||
1715 | |||
1716 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1717 | if (ret < 0) | ||
1718 | return ret; | ||
1719 | } | ||
1720 | 1812 | ||
1721 | /* | 1813 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
1722 | * Dwarf lines doesn't include function declarations. We have to | ||
1723 | * check functions list or given function. | ||
1724 | */ | ||
1725 | if (sp_die) { | ||
1726 | src = dwarf_decl_file(sp_die); | ||
1727 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
1728 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
1729 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1730 | } else | ||
1731 | ret = find_line_range_func_decl_lines(lf); | ||
1732 | 1814 | ||
1733 | /* Update status */ | 1815 | /* Update status */ |
1734 | if (ret >= 0) | 1816 | if (ret >= 0) |
@@ -1758,9 +1840,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1758 | struct line_finder *lf = param->data; | 1840 | struct line_finder *lf = param->data; |
1759 | struct line_range *lr = lf->lr; | 1841 | struct line_range *lr = lf->lr; |
1760 | 1842 | ||
1761 | pr_debug("find (%llx) %s\n", | ||
1762 | (unsigned long long)dwarf_dieoffset(sp_die), | ||
1763 | dwarf_diename(sp_die)); | ||
1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1843 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1765 | die_compare_name(sp_die, lr->function)) { | 1844 | die_compare_name(sp_die, lr->function)) { |
1766 | lf->fname = dwarf_decl_file(sp_die); | 1845 | lf->fname = dwarf_decl_file(sp_die); |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c new file mode 100644 index 000000000000..a9f2d7e1204d --- /dev/null +++ b/tools/perf/util/python.c | |||
@@ -0,0 +1,896 @@ | |||
1 | #include <Python.h> | ||
2 | #include <structmember.h> | ||
3 | #include <inttypes.h> | ||
4 | #include <poll.h> | ||
5 | #include "evlist.h" | ||
6 | #include "evsel.h" | ||
7 | #include "event.h" | ||
8 | #include "cpumap.h" | ||
9 | #include "thread_map.h" | ||
10 | |||
11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ | ||
12 | #ifndef PyVarObject_HEAD_INIT | ||
13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | ||
14 | #endif | ||
15 | |||
16 | struct throttle_event { | ||
17 | struct perf_event_header header; | ||
18 | u64 time; | ||
19 | u64 id; | ||
20 | u64 stream_id; | ||
21 | }; | ||
22 | |||
23 | PyMODINIT_FUNC initperf(void); | ||
24 | |||
25 | #define member_def(type, member, ptype, help) \ | ||
26 | { #member, ptype, \ | ||
27 | offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ | ||
28 | 0, help } | ||
29 | |||
30 | #define sample_member_def(name, member, ptype, help) \ | ||
31 | { #name, ptype, \ | ||
32 | offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \ | ||
33 | 0, help } | ||
34 | |||
35 | struct pyrf_event { | ||
36 | PyObject_HEAD | ||
37 | struct perf_sample sample; | ||
38 | union perf_event event; | ||
39 | }; | ||
40 | |||
41 | #define sample_members \ | ||
42 | sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ | ||
43 | sample_member_def(sample_pid, pid, T_INT, "event pid"), \ | ||
44 | sample_member_def(sample_tid, tid, T_INT, "event tid"), \ | ||
45 | sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ | ||
46 | sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ | ||
47 | sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ | ||
48 | sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ | ||
49 | sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ | ||
50 | sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), | ||
51 | |||
52 | static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); | ||
53 | |||
54 | static PyMemberDef pyrf_mmap_event__members[] = { | ||
55 | sample_members | ||
56 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
57 | member_def(mmap_event, pid, T_UINT, "event pid"), | ||
58 | member_def(mmap_event, tid, T_UINT, "event tid"), | ||
59 | member_def(mmap_event, start, T_ULONGLONG, "start of the map"), | ||
60 | member_def(mmap_event, len, T_ULONGLONG, "map length"), | ||
61 | member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), | ||
62 | member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), | ||
63 | { .name = NULL, }, | ||
64 | }; | ||
65 | |||
66 | static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) | ||
67 | { | ||
68 | PyObject *ret; | ||
69 | char *s; | ||
70 | |||
71 | if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " | ||
72 | "length: %#" PRIx64 ", offset: %#" PRIx64 ", " | ||
73 | "filename: %s }", | ||
74 | pevent->event.mmap.pid, pevent->event.mmap.tid, | ||
75 | pevent->event.mmap.start, pevent->event.mmap.len, | ||
76 | pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) { | ||
77 | ret = PyErr_NoMemory(); | ||
78 | } else { | ||
79 | ret = PyString_FromString(s); | ||
80 | free(s); | ||
81 | } | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static PyTypeObject pyrf_mmap_event__type = { | ||
86 | PyVarObject_HEAD_INIT(NULL, 0) | ||
87 | .tp_name = "perf.mmap_event", | ||
88 | .tp_basicsize = sizeof(struct pyrf_event), | ||
89 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
90 | .tp_doc = pyrf_mmap_event__doc, | ||
91 | .tp_members = pyrf_mmap_event__members, | ||
92 | .tp_repr = (reprfunc)pyrf_mmap_event__repr, | ||
93 | }; | ||
94 | |||
95 | static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); | ||
96 | |||
97 | static PyMemberDef pyrf_task_event__members[] = { | ||
98 | sample_members | ||
99 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
100 | member_def(fork_event, pid, T_UINT, "event pid"), | ||
101 | member_def(fork_event, ppid, T_UINT, "event ppid"), | ||
102 | member_def(fork_event, tid, T_UINT, "event tid"), | ||
103 | member_def(fork_event, ptid, T_UINT, "event ptid"), | ||
104 | member_def(fork_event, time, T_ULONGLONG, "timestamp"), | ||
105 | { .name = NULL, }, | ||
106 | }; | ||
107 | |||
108 | static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) | ||
109 | { | ||
110 | return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " | ||
111 | "ptid: %u, time: %" PRIu64 "}", | ||
112 | pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit", | ||
113 | pevent->event.fork.pid, | ||
114 | pevent->event.fork.ppid, | ||
115 | pevent->event.fork.tid, | ||
116 | pevent->event.fork.ptid, | ||
117 | pevent->event.fork.time); | ||
118 | } | ||
119 | |||
120 | static PyTypeObject pyrf_task_event__type = { | ||
121 | PyVarObject_HEAD_INIT(NULL, 0) | ||
122 | .tp_name = "perf.task_event", | ||
123 | .tp_basicsize = sizeof(struct pyrf_event), | ||
124 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
125 | .tp_doc = pyrf_task_event__doc, | ||
126 | .tp_members = pyrf_task_event__members, | ||
127 | .tp_repr = (reprfunc)pyrf_task_event__repr, | ||
128 | }; | ||
129 | |||
130 | static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); | ||
131 | |||
132 | static PyMemberDef pyrf_comm_event__members[] = { | ||
133 | sample_members | ||
134 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
135 | member_def(comm_event, pid, T_UINT, "event pid"), | ||
136 | member_def(comm_event, tid, T_UINT, "event tid"), | ||
137 | member_def(comm_event, comm, T_STRING_INPLACE, "process name"), | ||
138 | { .name = NULL, }, | ||
139 | }; | ||
140 | |||
141 | static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) | ||
142 | { | ||
143 | return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", | ||
144 | pevent->event.comm.pid, | ||
145 | pevent->event.comm.tid, | ||
146 | pevent->event.comm.comm); | ||
147 | } | ||
148 | |||
149 | static PyTypeObject pyrf_comm_event__type = { | ||
150 | PyVarObject_HEAD_INIT(NULL, 0) | ||
151 | .tp_name = "perf.comm_event", | ||
152 | .tp_basicsize = sizeof(struct pyrf_event), | ||
153 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
154 | .tp_doc = pyrf_comm_event__doc, | ||
155 | .tp_members = pyrf_comm_event__members, | ||
156 | .tp_repr = (reprfunc)pyrf_comm_event__repr, | ||
157 | }; | ||
158 | |||
159 | static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); | ||
160 | |||
161 | static PyMemberDef pyrf_throttle_event__members[] = { | ||
162 | sample_members | ||
163 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
164 | member_def(throttle_event, time, T_ULONGLONG, "timestamp"), | ||
165 | member_def(throttle_event, id, T_ULONGLONG, "event id"), | ||
166 | member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), | ||
167 | { .name = NULL, }, | ||
168 | }; | ||
169 | |||
170 | static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) | ||
171 | { | ||
172 | struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); | ||
173 | |||
174 | return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 | ||
175 | ", stream_id: %" PRIu64 " }", | ||
176 | pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", | ||
177 | te->time, te->id, te->stream_id); | ||
178 | } | ||
179 | |||
180 | static PyTypeObject pyrf_throttle_event__type = { | ||
181 | PyVarObject_HEAD_INIT(NULL, 0) | ||
182 | .tp_name = "perf.throttle_event", | ||
183 | .tp_basicsize = sizeof(struct pyrf_event), | ||
184 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
185 | .tp_doc = pyrf_throttle_event__doc, | ||
186 | .tp_members = pyrf_throttle_event__members, | ||
187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, | ||
188 | }; | ||
189 | |||
190 | static int pyrf_event__setup_types(void) | ||
191 | { | ||
192 | int err; | ||
193 | pyrf_mmap_event__type.tp_new = | ||
194 | pyrf_task_event__type.tp_new = | ||
195 | pyrf_comm_event__type.tp_new = | ||
196 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; | ||
197 | err = PyType_Ready(&pyrf_mmap_event__type); | ||
198 | if (err < 0) | ||
199 | goto out; | ||
200 | err = PyType_Ready(&pyrf_task_event__type); | ||
201 | if (err < 0) | ||
202 | goto out; | ||
203 | err = PyType_Ready(&pyrf_comm_event__type); | ||
204 | if (err < 0) | ||
205 | goto out; | ||
206 | err = PyType_Ready(&pyrf_throttle_event__type); | ||
207 | if (err < 0) | ||
208 | goto out; | ||
209 | out: | ||
210 | return err; | ||
211 | } | ||
212 | |||
213 | static PyTypeObject *pyrf_event__type[] = { | ||
214 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, | ||
215 | [PERF_RECORD_LOST] = &pyrf_mmap_event__type, | ||
216 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, | ||
217 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, | ||
218 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, | ||
219 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, | ||
220 | [PERF_RECORD_FORK] = &pyrf_task_event__type, | ||
221 | [PERF_RECORD_READ] = &pyrf_mmap_event__type, | ||
222 | [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, | ||
223 | }; | ||
224 | |||
225 | static PyObject *pyrf_event__new(union perf_event *event) | ||
226 | { | ||
227 | struct pyrf_event *pevent; | ||
228 | PyTypeObject *ptype; | ||
229 | |||
230 | if (event->header.type < PERF_RECORD_MMAP || | ||
231 | event->header.type > PERF_RECORD_SAMPLE) | ||
232 | return NULL; | ||
233 | |||
234 | ptype = pyrf_event__type[event->header.type]; | ||
235 | pevent = PyObject_New(struct pyrf_event, ptype); | ||
236 | if (pevent != NULL) | ||
237 | memcpy(&pevent->event, event, event->header.size); | ||
238 | return (PyObject *)pevent; | ||
239 | } | ||
240 | |||
241 | struct pyrf_cpu_map { | ||
242 | PyObject_HEAD | ||
243 | |||
244 | struct cpu_map *cpus; | ||
245 | }; | ||
246 | |||
247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | ||
248 | PyObject *args, PyObject *kwargs) | ||
249 | { | ||
250 | static char *kwlist[] = { "cpustr", NULL, NULL, }; | ||
251 | char *cpustr = NULL; | ||
252 | |||
253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | ||
254 | kwlist, &cpustr)) | ||
255 | return -1; | ||
256 | |||
257 | pcpus->cpus = cpu_map__new(cpustr); | ||
258 | if (pcpus->cpus == NULL) | ||
259 | return -1; | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) | ||
264 | { | ||
265 | cpu_map__delete(pcpus->cpus); | ||
266 | pcpus->ob_type->tp_free((PyObject*)pcpus); | ||
267 | } | ||
268 | |||
269 | static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) | ||
270 | { | ||
271 | struct pyrf_cpu_map *pcpus = (void *)obj; | ||
272 | |||
273 | return pcpus->cpus->nr; | ||
274 | } | ||
275 | |||
276 | static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) | ||
277 | { | ||
278 | struct pyrf_cpu_map *pcpus = (void *)obj; | ||
279 | |||
280 | if (i >= pcpus->cpus->nr) | ||
281 | return NULL; | ||
282 | |||
283 | return Py_BuildValue("i", pcpus->cpus->map[i]); | ||
284 | } | ||
285 | |||
286 | static PySequenceMethods pyrf_cpu_map__sequence_methods = { | ||
287 | .sq_length = pyrf_cpu_map__length, | ||
288 | .sq_item = pyrf_cpu_map__item, | ||
289 | }; | ||
290 | |||
291 | static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); | ||
292 | |||
293 | static PyTypeObject pyrf_cpu_map__type = { | ||
294 | PyVarObject_HEAD_INIT(NULL, 0) | ||
295 | .tp_name = "perf.cpu_map", | ||
296 | .tp_basicsize = sizeof(struct pyrf_cpu_map), | ||
297 | .tp_dealloc = (destructor)pyrf_cpu_map__delete, | ||
298 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
299 | .tp_doc = pyrf_cpu_map__doc, | ||
300 | .tp_as_sequence = &pyrf_cpu_map__sequence_methods, | ||
301 | .tp_init = (initproc)pyrf_cpu_map__init, | ||
302 | }; | ||
303 | |||
304 | static int pyrf_cpu_map__setup_types(void) | ||
305 | { | ||
306 | pyrf_cpu_map__type.tp_new = PyType_GenericNew; | ||
307 | return PyType_Ready(&pyrf_cpu_map__type); | ||
308 | } | ||
309 | |||
310 | struct pyrf_thread_map { | ||
311 | PyObject_HEAD | ||
312 | |||
313 | struct thread_map *threads; | ||
314 | }; | ||
315 | |||
316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | ||
317 | PyObject *args, PyObject *kwargs) | ||
318 | { | ||
319 | static char *kwlist[] = { "pid", "tid", NULL, NULL, }; | ||
320 | int pid = -1, tid = -1; | ||
321 | |||
322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | ||
323 | kwlist, &pid, &tid)) | ||
324 | return -1; | ||
325 | |||
326 | pthreads->threads = thread_map__new(pid, tid); | ||
327 | if (pthreads->threads == NULL) | ||
328 | return -1; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) | ||
333 | { | ||
334 | thread_map__delete(pthreads->threads); | ||
335 | pthreads->ob_type->tp_free((PyObject*)pthreads); | ||
336 | } | ||
337 | |||
338 | static Py_ssize_t pyrf_thread_map__length(PyObject *obj) | ||
339 | { | ||
340 | struct pyrf_thread_map *pthreads = (void *)obj; | ||
341 | |||
342 | return pthreads->threads->nr; | ||
343 | } | ||
344 | |||
345 | static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) | ||
346 | { | ||
347 | struct pyrf_thread_map *pthreads = (void *)obj; | ||
348 | |||
349 | if (i >= pthreads->threads->nr) | ||
350 | return NULL; | ||
351 | |||
352 | return Py_BuildValue("i", pthreads->threads->map[i]); | ||
353 | } | ||
354 | |||
355 | static PySequenceMethods pyrf_thread_map__sequence_methods = { | ||
356 | .sq_length = pyrf_thread_map__length, | ||
357 | .sq_item = pyrf_thread_map__item, | ||
358 | }; | ||
359 | |||
360 | static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); | ||
361 | |||
362 | static PyTypeObject pyrf_thread_map__type = { | ||
363 | PyVarObject_HEAD_INIT(NULL, 0) | ||
364 | .tp_name = "perf.thread_map", | ||
365 | .tp_basicsize = sizeof(struct pyrf_thread_map), | ||
366 | .tp_dealloc = (destructor)pyrf_thread_map__delete, | ||
367 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
368 | .tp_doc = pyrf_thread_map__doc, | ||
369 | .tp_as_sequence = &pyrf_thread_map__sequence_methods, | ||
370 | .tp_init = (initproc)pyrf_thread_map__init, | ||
371 | }; | ||
372 | |||
373 | static int pyrf_thread_map__setup_types(void) | ||
374 | { | ||
375 | pyrf_thread_map__type.tp_new = PyType_GenericNew; | ||
376 | return PyType_Ready(&pyrf_thread_map__type); | ||
377 | } | ||
378 | |||
379 | struct pyrf_evsel { | ||
380 | PyObject_HEAD | ||
381 | |||
382 | struct perf_evsel evsel; | ||
383 | }; | ||
384 | |||
385 | static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | ||
386 | PyObject *args, PyObject *kwargs) | ||
387 | { | ||
388 | struct perf_event_attr attr = { | ||
389 | .type = PERF_TYPE_HARDWARE, | ||
390 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
391 | .sample_type = PERF_SAMPLE_PERIOD | PERF_SAMPLE_TID, | ||
392 | }; | ||
393 | static char *kwlist[] = { | ||
394 | "type", | ||
395 | "config", | ||
396 | "sample_freq", | ||
397 | "sample_period", | ||
398 | "sample_type", | ||
399 | "read_format", | ||
400 | "disabled", | ||
401 | "inherit", | ||
402 | "pinned", | ||
403 | "exclusive", | ||
404 | "exclude_user", | ||
405 | "exclude_kernel", | ||
406 | "exclude_hv", | ||
407 | "exclude_idle", | ||
408 | "mmap", | ||
409 | "comm", | ||
410 | "freq", | ||
411 | "inherit_stat", | ||
412 | "enable_on_exec", | ||
413 | "task", | ||
414 | "watermark", | ||
415 | "precise_ip", | ||
416 | "mmap_data", | ||
417 | "sample_id_all", | ||
418 | "wakeup_events", | ||
419 | "bp_type", | ||
420 | "bp_addr", | ||
421 | "bp_len", NULL, NULL, }; | ||
422 | u64 sample_period = 0; | ||
423 | u32 disabled = 0, | ||
424 | inherit = 0, | ||
425 | pinned = 0, | ||
426 | exclusive = 0, | ||
427 | exclude_user = 0, | ||
428 | exclude_kernel = 0, | ||
429 | exclude_hv = 0, | ||
430 | exclude_idle = 0, | ||
431 | mmap = 0, | ||
432 | comm = 0, | ||
433 | freq = 1, | ||
434 | inherit_stat = 0, | ||
435 | enable_on_exec = 0, | ||
436 | task = 0, | ||
437 | watermark = 0, | ||
438 | precise_ip = 0, | ||
439 | mmap_data = 0, | ||
440 | sample_id_all = 1; | ||
441 | int idx = 0; | ||
442 | |||
443 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, | ||
444 | "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, | ||
445 | &attr.type, &attr.config, &attr.sample_freq, | ||
446 | &sample_period, &attr.sample_type, | ||
447 | &attr.read_format, &disabled, &inherit, | ||
448 | &pinned, &exclusive, &exclude_user, | ||
449 | &exclude_kernel, &exclude_hv, &exclude_idle, | ||
450 | &mmap, &comm, &freq, &inherit_stat, | ||
451 | &enable_on_exec, &task, &watermark, | ||
452 | &precise_ip, &mmap_data, &sample_id_all, | ||
453 | &attr.wakeup_events, &attr.bp_type, | ||
454 | &attr.bp_addr, &attr.bp_len, &idx)) | ||
455 | return -1; | ||
456 | |||
457 | /* union... */ | ||
458 | if (sample_period != 0) { | ||
459 | if (attr.sample_freq != 0) | ||
460 | return -1; /* FIXME: throw right exception */ | ||
461 | attr.sample_period = sample_period; | ||
462 | } | ||
463 | |||
464 | /* Bitfields */ | ||
465 | attr.disabled = disabled; | ||
466 | attr.inherit = inherit; | ||
467 | attr.pinned = pinned; | ||
468 | attr.exclusive = exclusive; | ||
469 | attr.exclude_user = exclude_user; | ||
470 | attr.exclude_kernel = exclude_kernel; | ||
471 | attr.exclude_hv = exclude_hv; | ||
472 | attr.exclude_idle = exclude_idle; | ||
473 | attr.mmap = mmap; | ||
474 | attr.comm = comm; | ||
475 | attr.freq = freq; | ||
476 | attr.inherit_stat = inherit_stat; | ||
477 | attr.enable_on_exec = enable_on_exec; | ||
478 | attr.task = task; | ||
479 | attr.watermark = watermark; | ||
480 | attr.precise_ip = precise_ip; | ||
481 | attr.mmap_data = mmap_data; | ||
482 | attr.sample_id_all = sample_id_all; | ||
483 | |||
484 | perf_evsel__init(&pevsel->evsel, &attr, idx); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) | ||
489 | { | ||
490 | perf_evsel__exit(&pevsel->evsel); | ||
491 | pevsel->ob_type->tp_free((PyObject*)pevsel); | ||
492 | } | ||
493 | |||
494 | static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | ||
495 | PyObject *args, PyObject *kwargs) | ||
496 | { | ||
497 | struct perf_evsel *evsel = &pevsel->evsel; | ||
498 | struct cpu_map *cpus = NULL; | ||
499 | struct thread_map *threads = NULL; | ||
500 | PyObject *pcpus = NULL, *pthreads = NULL; | ||
501 | int group = 0, overwrite = 0; | ||
502 | static char *kwlist[] = {"cpus", "threads", "group", "overwrite", NULL, NULL}; | ||
503 | |||
504 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | ||
505 | &pcpus, &pthreads, &group, &overwrite)) | ||
506 | return NULL; | ||
507 | |||
508 | if (pthreads != NULL) | ||
509 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | ||
510 | |||
511 | if (pcpus != NULL) | ||
512 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | ||
513 | |||
514 | if (perf_evsel__open(evsel, cpus, threads, group, overwrite) < 0) { | ||
515 | PyErr_SetFromErrno(PyExc_OSError); | ||
516 | return NULL; | ||
517 | } | ||
518 | |||
519 | Py_INCREF(Py_None); | ||
520 | return Py_None; | ||
521 | } | ||
522 | |||
523 | static PyMethodDef pyrf_evsel__methods[] = { | ||
524 | { | ||
525 | .ml_name = "open", | ||
526 | .ml_meth = (PyCFunction)pyrf_evsel__open, | ||
527 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
528 | .ml_doc = PyDoc_STR("open the event selector file descriptor table.") | ||
529 | }, | ||
530 | { .ml_name = NULL, } | ||
531 | }; | ||
532 | |||
533 | static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); | ||
534 | |||
535 | static PyTypeObject pyrf_evsel__type = { | ||
536 | PyVarObject_HEAD_INIT(NULL, 0) | ||
537 | .tp_name = "perf.evsel", | ||
538 | .tp_basicsize = sizeof(struct pyrf_evsel), | ||
539 | .tp_dealloc = (destructor)pyrf_evsel__delete, | ||
540 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
541 | .tp_doc = pyrf_evsel__doc, | ||
542 | .tp_methods = pyrf_evsel__methods, | ||
543 | .tp_init = (initproc)pyrf_evsel__init, | ||
544 | }; | ||
545 | |||
546 | static int pyrf_evsel__setup_types(void) | ||
547 | { | ||
548 | pyrf_evsel__type.tp_new = PyType_GenericNew; | ||
549 | return PyType_Ready(&pyrf_evsel__type); | ||
550 | } | ||
551 | |||
552 | struct pyrf_evlist { | ||
553 | PyObject_HEAD | ||
554 | |||
555 | struct perf_evlist evlist; | ||
556 | }; | ||
557 | |||
558 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, | ||
559 | PyObject *args, PyObject *kwargs __used) | ||
560 | { | ||
561 | PyObject *pcpus = NULL, *pthreads = NULL; | ||
562 | struct cpu_map *cpus; | ||
563 | struct thread_map *threads; | ||
564 | |||
565 | if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) | ||
566 | return -1; | ||
567 | |||
568 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | ||
569 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | ||
570 | perf_evlist__init(&pevlist->evlist, cpus, threads); | ||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) | ||
575 | { | ||
576 | perf_evlist__exit(&pevlist->evlist); | ||
577 | pevlist->ob_type->tp_free((PyObject*)pevlist); | ||
578 | } | ||
579 | |||
580 | static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | ||
581 | PyObject *args, PyObject *kwargs) | ||
582 | { | ||
583 | struct perf_evlist *evlist = &pevlist->evlist; | ||
584 | static char *kwlist[] = {"pages", "overwrite", | ||
585 | NULL, NULL}; | ||
586 | int pages = 128, overwrite = false; | ||
587 | |||
588 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | ||
589 | &pages, &overwrite)) | ||
590 | return NULL; | ||
591 | |||
592 | if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { | ||
593 | PyErr_SetFromErrno(PyExc_OSError); | ||
594 | return NULL; | ||
595 | } | ||
596 | |||
597 | Py_INCREF(Py_None); | ||
598 | return Py_None; | ||
599 | } | ||
600 | |||
601 | static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | ||
602 | PyObject *args, PyObject *kwargs) | ||
603 | { | ||
604 | struct perf_evlist *evlist = &pevlist->evlist; | ||
605 | static char *kwlist[] = {"timeout", NULL, NULL}; | ||
606 | int timeout = -1, n; | ||
607 | |||
608 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | ||
609 | return NULL; | ||
610 | |||
611 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | ||
612 | if (n < 0) { | ||
613 | PyErr_SetFromErrno(PyExc_OSError); | ||
614 | return NULL; | ||
615 | } | ||
616 | |||
617 | return Py_BuildValue("i", n); | ||
618 | } | ||
619 | |||
620 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | ||
621 | PyObject *args __used, PyObject *kwargs __used) | ||
622 | { | ||
623 | struct perf_evlist *evlist = &pevlist->evlist; | ||
624 | PyObject *list = PyList_New(0); | ||
625 | int i; | ||
626 | |||
627 | for (i = 0; i < evlist->nr_fds; ++i) { | ||
628 | PyObject *file; | ||
629 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | ||
630 | |||
631 | if (fp == NULL) | ||
632 | goto free_list; | ||
633 | |||
634 | file = PyFile_FromFile(fp, "perf", "r", NULL); | ||
635 | if (file == NULL) | ||
636 | goto free_list; | ||
637 | |||
638 | if (PyList_Append(list, file) != 0) { | ||
639 | Py_DECREF(file); | ||
640 | goto free_list; | ||
641 | } | ||
642 | |||
643 | Py_DECREF(file); | ||
644 | } | ||
645 | |||
646 | return list; | ||
647 | free_list: | ||
648 | return PyErr_NoMemory(); | ||
649 | } | ||
650 | |||
651 | |||
652 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, | ||
653 | PyObject *args, PyObject *kwargs __used) | ||
654 | { | ||
655 | struct perf_evlist *evlist = &pevlist->evlist; | ||
656 | PyObject *pevsel; | ||
657 | struct perf_evsel *evsel; | ||
658 | |||
659 | if (!PyArg_ParseTuple(args, "O", &pevsel)) | ||
660 | return NULL; | ||
661 | |||
662 | Py_INCREF(pevsel); | ||
663 | evsel = &((struct pyrf_evsel *)pevsel)->evsel; | ||
664 | evsel->idx = evlist->nr_entries; | ||
665 | perf_evlist__add(evlist, evsel); | ||
666 | |||
667 | return Py_BuildValue("i", evlist->nr_entries); | ||
668 | } | ||
669 | |||
670 | static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | ||
671 | PyObject *args, PyObject *kwargs) | ||
672 | { | ||
673 | struct perf_evlist *evlist = &pevlist->evlist; | ||
674 | union perf_event *event; | ||
675 | int sample_id_all = 1, cpu; | ||
676 | static char *kwlist[] = {"sample_id_all", NULL, NULL}; | ||
677 | |||
678 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | ||
679 | &cpu, &sample_id_all)) | ||
680 | return NULL; | ||
681 | |||
682 | event = perf_evlist__read_on_cpu(evlist, cpu); | ||
683 | if (event != NULL) { | ||
684 | struct perf_evsel *first; | ||
685 | PyObject *pyevent = pyrf_event__new(event); | ||
686 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; | ||
687 | |||
688 | if (pyevent == NULL) | ||
689 | return PyErr_NoMemory(); | ||
690 | |||
691 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
692 | perf_event__parse_sample(event, first->attr.sample_type, sample_id_all, | ||
693 | &pevent->sample); | ||
694 | return pyevent; | ||
695 | } | ||
696 | |||
697 | Py_INCREF(Py_None); | ||
698 | return Py_None; | ||
699 | } | ||
700 | |||
701 | static PyMethodDef pyrf_evlist__methods[] = { | ||
702 | { | ||
703 | .ml_name = "mmap", | ||
704 | .ml_meth = (PyCFunction)pyrf_evlist__mmap, | ||
705 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
706 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") | ||
707 | }, | ||
708 | { | ||
709 | .ml_name = "poll", | ||
710 | .ml_meth = (PyCFunction)pyrf_evlist__poll, | ||
711 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
712 | .ml_doc = PyDoc_STR("poll the file descriptor table.") | ||
713 | }, | ||
714 | { | ||
715 | .ml_name = "get_pollfd", | ||
716 | .ml_meth = (PyCFunction)pyrf_evlist__get_pollfd, | ||
717 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
718 | .ml_doc = PyDoc_STR("get the poll file descriptor table.") | ||
719 | }, | ||
720 | { | ||
721 | .ml_name = "add", | ||
722 | .ml_meth = (PyCFunction)pyrf_evlist__add, | ||
723 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
724 | .ml_doc = PyDoc_STR("adds an event selector to the list.") | ||
725 | }, | ||
726 | { | ||
727 | .ml_name = "read_on_cpu", | ||
728 | .ml_meth = (PyCFunction)pyrf_evlist__read_on_cpu, | ||
729 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
730 | .ml_doc = PyDoc_STR("reads an event.") | ||
731 | }, | ||
732 | { .ml_name = NULL, } | ||
733 | }; | ||
734 | |||
735 | static Py_ssize_t pyrf_evlist__length(PyObject *obj) | ||
736 | { | ||
737 | struct pyrf_evlist *pevlist = (void *)obj; | ||
738 | |||
739 | return pevlist->evlist.nr_entries; | ||
740 | } | ||
741 | |||
742 | static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) | ||
743 | { | ||
744 | struct pyrf_evlist *pevlist = (void *)obj; | ||
745 | struct perf_evsel *pos; | ||
746 | |||
747 | if (i >= pevlist->evlist.nr_entries) | ||
748 | return NULL; | ||
749 | |||
750 | list_for_each_entry(pos, &pevlist->evlist.entries, node) | ||
751 | if (i-- == 0) | ||
752 | break; | ||
753 | |||
754 | return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); | ||
755 | } | ||
756 | |||
757 | static PySequenceMethods pyrf_evlist__sequence_methods = { | ||
758 | .sq_length = pyrf_evlist__length, | ||
759 | .sq_item = pyrf_evlist__item, | ||
760 | }; | ||
761 | |||
762 | static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); | ||
763 | |||
764 | static PyTypeObject pyrf_evlist__type = { | ||
765 | PyVarObject_HEAD_INIT(NULL, 0) | ||
766 | .tp_name = "perf.evlist", | ||
767 | .tp_basicsize = sizeof(struct pyrf_evlist), | ||
768 | .tp_dealloc = (destructor)pyrf_evlist__delete, | ||
769 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
770 | .tp_as_sequence = &pyrf_evlist__sequence_methods, | ||
771 | .tp_doc = pyrf_evlist__doc, | ||
772 | .tp_methods = pyrf_evlist__methods, | ||
773 | .tp_init = (initproc)pyrf_evlist__init, | ||
774 | }; | ||
775 | |||
776 | static int pyrf_evlist__setup_types(void) | ||
777 | { | ||
778 | pyrf_evlist__type.tp_new = PyType_GenericNew; | ||
779 | return PyType_Ready(&pyrf_evlist__type); | ||
780 | } | ||
781 | |||
782 | static struct { | ||
783 | const char *name; | ||
784 | int value; | ||
785 | } perf__constants[] = { | ||
786 | { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, | ||
787 | { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, | ||
788 | { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, | ||
789 | { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, | ||
790 | { "TYPE_RAW", PERF_TYPE_RAW }, | ||
791 | { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, | ||
792 | |||
793 | { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, | ||
794 | { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, | ||
795 | { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, | ||
796 | { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, | ||
797 | { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, | ||
798 | { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, | ||
799 | { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, | ||
800 | { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, | ||
801 | { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, | ||
802 | { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, | ||
803 | { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, | ||
804 | { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, | ||
805 | { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, | ||
806 | { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, | ||
807 | { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, | ||
808 | { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, | ||
809 | { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, | ||
810 | { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, | ||
811 | |||
812 | { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, | ||
813 | { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, | ||
814 | { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, | ||
815 | { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, | ||
816 | { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, | ||
817 | { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, | ||
818 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, | ||
819 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, | ||
820 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, | ||
821 | |||
822 | { "SAMPLE_IP", PERF_SAMPLE_IP }, | ||
823 | { "SAMPLE_TID", PERF_SAMPLE_TID }, | ||
824 | { "SAMPLE_TIME", PERF_SAMPLE_TIME }, | ||
825 | { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, | ||
826 | { "SAMPLE_READ", PERF_SAMPLE_READ }, | ||
827 | { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, | ||
828 | { "SAMPLE_ID", PERF_SAMPLE_ID }, | ||
829 | { "SAMPLE_CPU", PERF_SAMPLE_CPU }, | ||
830 | { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, | ||
831 | { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, | ||
832 | { "SAMPLE_RAW", PERF_SAMPLE_RAW }, | ||
833 | |||
834 | { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, | ||
835 | { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, | ||
836 | { "FORMAT_ID", PERF_FORMAT_ID }, | ||
837 | { "FORMAT_GROUP", PERF_FORMAT_GROUP }, | ||
838 | |||
839 | { "RECORD_MMAP", PERF_RECORD_MMAP }, | ||
840 | { "RECORD_LOST", PERF_RECORD_LOST }, | ||
841 | { "RECORD_COMM", PERF_RECORD_COMM }, | ||
842 | { "RECORD_EXIT", PERF_RECORD_EXIT }, | ||
843 | { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, | ||
844 | { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, | ||
845 | { "RECORD_FORK", PERF_RECORD_FORK }, | ||
846 | { "RECORD_READ", PERF_RECORD_READ }, | ||
847 | { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, | ||
848 | { .name = NULL, }, | ||
849 | }; | ||
850 | |||
851 | static PyMethodDef perf__methods[] = { | ||
852 | { .ml_name = NULL, } | ||
853 | }; | ||
854 | |||
855 | PyMODINIT_FUNC initperf(void) | ||
856 | { | ||
857 | PyObject *obj; | ||
858 | int i; | ||
859 | PyObject *dict, *module = Py_InitModule("perf", perf__methods); | ||
860 | |||
861 | if (module == NULL || | ||
862 | pyrf_event__setup_types() < 0 || | ||
863 | pyrf_evlist__setup_types() < 0 || | ||
864 | pyrf_evsel__setup_types() < 0 || | ||
865 | pyrf_thread_map__setup_types() < 0 || | ||
866 | pyrf_cpu_map__setup_types() < 0) | ||
867 | return; | ||
868 | |||
869 | Py_INCREF(&pyrf_evlist__type); | ||
870 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); | ||
871 | |||
872 | Py_INCREF(&pyrf_evsel__type); | ||
873 | PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); | ||
874 | |||
875 | Py_INCREF(&pyrf_thread_map__type); | ||
876 | PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); | ||
877 | |||
878 | Py_INCREF(&pyrf_cpu_map__type); | ||
879 | PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); | ||
880 | |||
881 | dict = PyModule_GetDict(module); | ||
882 | if (dict == NULL) | ||
883 | goto error; | ||
884 | |||
885 | for (i = 0; perf__constants[i].name != NULL; i++) { | ||
886 | obj = PyInt_FromLong(perf__constants[i].value); | ||
887 | if (obj == NULL) | ||
888 | goto error; | ||
889 | PyDict_SetItemString(dict, perf__constants[i].name, obj); | ||
890 | Py_DECREF(obj); | ||
891 | } | ||
892 | |||
893 | error: | ||
894 | if (PyErr_Occurred()) | ||
895 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); | ||
896 | } | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 93680818e244..621427212e86 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -245,9 +245,10 @@ static inline struct event *find_cache_event(int type) | |||
245 | return event; | 245 | return event; |
246 | } | 246 | } |
247 | 247 | ||
248 | static void perl_process_event(int cpu, void *data, | 248 | static void perl_process_event(union perf_event *pevent __unused, |
249 | int size __unused, | 249 | struct perf_sample *sample, |
250 | unsigned long long nsecs, char *comm) | 250 | struct perf_session *session __unused, |
251 | struct thread *thread) | ||
251 | { | 252 | { |
252 | struct format_field *field; | 253 | struct format_field *field; |
253 | static char handler[256]; | 254 | static char handler[256]; |
@@ -256,6 +257,10 @@ static void perl_process_event(int cpu, void *data, | |||
256 | struct event *event; | 257 | struct event *event; |
257 | int type; | 258 | int type; |
258 | int pid; | 259 | int pid; |
260 | int cpu = sample->cpu; | ||
261 | void *data = sample->raw_data; | ||
262 | unsigned long long nsecs = sample->time; | ||
263 | char *comm = thread->comm; | ||
259 | 264 | ||
260 | dSP; | 265 | dSP; |
261 | 266 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c6d99334bdfa..1b85d6055159 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -204,9 +204,10 @@ static inline struct event *find_cache_event(int type) | |||
204 | return event; | 204 | return event; |
205 | } | 205 | } |
206 | 206 | ||
207 | static void python_process_event(int cpu, void *data, | 207 | static void python_process_event(union perf_event *pevent __unused, |
208 | int size __unused, | 208 | struct perf_sample *sample, |
209 | unsigned long long nsecs, char *comm) | 209 | struct perf_session *session __unused, |
210 | struct thread *thread) | ||
210 | { | 211 | { |
211 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; | 212 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; |
212 | static char handler_name[256]; | 213 | static char handler_name[256]; |
@@ -217,6 +218,10 @@ static void python_process_event(int cpu, void *data, | |||
217 | unsigned n = 0; | 218 | unsigned n = 0; |
218 | int type; | 219 | int type; |
219 | int pid; | 220 | int pid; |
221 | int cpu = sample->cpu; | ||
222 | void *data = sample->raw_data; | ||
223 | unsigned long long nsecs = sample->time; | ||
224 | char *comm = thread->comm; | ||
220 | 225 | ||
221 | t = PyTuple_New(MAX_FIELDS); | 226 | t = PyTuple_New(MAX_FIELDS); |
222 | if (!t) | 227 | if (!t) |
@@ -248,8 +253,7 @@ static void python_process_event(int cpu, void *data, | |||
248 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | 253 | context = PyCObject_FromVoidPtr(scripting_context, NULL); |
249 | 254 | ||
250 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); | 255 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); |
251 | PyTuple_SetItem(t, n++, | 256 | PyTuple_SetItem(t, n++, context); |
252 | PyCObject_FromVoidPtr(scripting_context, NULL)); | ||
253 | 257 | ||
254 | if (handler) { | 258 | if (handler) { |
255 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); | 259 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 105f00bfd555..c68cf40764f9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <sys/types.h> | 7 | #include <sys/types.h> |
8 | #include <sys/mman.h> | 8 | #include <sys/mman.h> |
9 | 9 | ||
10 | #include "evlist.h" | ||
11 | #include "evsel.h" | ||
10 | #include "session.h" | 12 | #include "session.h" |
11 | #include "sort.h" | 13 | #include "sort.h" |
12 | #include "util.h" | 14 | #include "util.h" |
@@ -19,7 +21,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
19 | self->fd_pipe = true; | 21 | self->fd_pipe = true; |
20 | self->fd = STDIN_FILENO; | 22 | self->fd = STDIN_FILENO; |
21 | 23 | ||
22 | if (perf_header__read(self, self->fd) < 0) | 24 | if (perf_session__read_header(self, self->fd) < 0) |
23 | pr_err("incompatible file format"); | 25 | pr_err("incompatible file format"); |
24 | 26 | ||
25 | return 0; | 27 | return 0; |
@@ -51,7 +53,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
51 | goto out_close; | 53 | goto out_close; |
52 | } | 54 | } |
53 | 55 | ||
54 | if (perf_header__read(self, self->fd) < 0) { | 56 | if (perf_session__read_header(self, self->fd) < 0) { |
55 | pr_err("incompatible file format"); | 57 | pr_err("incompatible file format"); |
56 | goto out_close; | 58 | goto out_close; |
57 | } | 59 | } |
@@ -67,7 +69,7 @@ out_close: | |||
67 | 69 | ||
68 | static void perf_session__id_header_size(struct perf_session *session) | 70 | static void perf_session__id_header_size(struct perf_session *session) |
69 | { | 71 | { |
70 | struct sample_data *data; | 72 | struct perf_sample *data; |
71 | u64 sample_type = session->sample_type; | 73 | u64 sample_type = session->sample_type; |
72 | u16 size = 0; | 74 | u16 size = 0; |
73 | 75 | ||
@@ -92,21 +94,10 @@ out: | |||
92 | session->id_hdr_size = size; | 94 | session->id_hdr_size = size; |
93 | } | 95 | } |
94 | 96 | ||
95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | ||
96 | { | ||
97 | session->sample_id_all = value; | ||
98 | perf_session__id_header_size(session); | ||
99 | } | ||
100 | |||
101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | ||
102 | { | ||
103 | session->sample_type = type; | ||
104 | } | ||
105 | |||
106 | void perf_session__update_sample_type(struct perf_session *self) | 97 | void perf_session__update_sample_type(struct perf_session *self) |
107 | { | 98 | { |
108 | self->sample_type = perf_header__sample_type(&self->header); | 99 | self->sample_type = perf_evlist__sample_type(self->evlist); |
109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | 100 | self->sample_id_all = perf_evlist__sample_id_all(self->evlist); |
110 | perf_session__id_header_size(self); | 101 | perf_session__id_header_size(self); |
111 | } | 102 | } |
112 | 103 | ||
@@ -135,13 +126,9 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
135 | if (self == NULL) | 126 | if (self == NULL) |
136 | goto out; | 127 | goto out; |
137 | 128 | ||
138 | if (perf_header__init(&self->header) < 0) | ||
139 | goto out_free; | ||
140 | |||
141 | memcpy(self->filename, filename, len); | 129 | memcpy(self->filename, filename, len); |
142 | self->threads = RB_ROOT; | 130 | self->threads = RB_ROOT; |
143 | INIT_LIST_HEAD(&self->dead_threads); | 131 | INIT_LIST_HEAD(&self->dead_threads); |
144 | self->hists_tree = RB_ROOT; | ||
145 | self->last_match = NULL; | 132 | self->last_match = NULL; |
146 | /* | 133 | /* |
147 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | 134 | * On 64bit we can mmap the data file in one go. No need for tiny mmap |
@@ -162,17 +149,16 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
162 | if (mode == O_RDONLY) { | 149 | if (mode == O_RDONLY) { |
163 | if (perf_session__open(self, force) < 0) | 150 | if (perf_session__open(self, force) < 0) |
164 | goto out_delete; | 151 | goto out_delete; |
152 | perf_session__update_sample_type(self); | ||
165 | } else if (mode == O_WRONLY) { | 153 | } else if (mode == O_WRONLY) { |
166 | /* | 154 | /* |
167 | * In O_RDONLY mode this will be performed when reading the | 155 | * In O_RDONLY mode this will be performed when reading the |
168 | * kernel MMAP event, in event__process_mmap(). | 156 | * kernel MMAP event, in perf_event__process_mmap(). |
169 | */ | 157 | */ |
170 | if (perf_session__create_kernel_maps(self) < 0) | 158 | if (perf_session__create_kernel_maps(self) < 0) |
171 | goto out_delete; | 159 | goto out_delete; |
172 | } | 160 | } |
173 | 161 | ||
174 | perf_session__update_sample_type(self); | ||
175 | |||
176 | if (ops && ops->ordering_requires_timestamps && | 162 | if (ops && ops->ordering_requires_timestamps && |
177 | ops->ordered_samples && !self->sample_id_all) { | 163 | ops->ordered_samples && !self->sample_id_all) { |
178 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 164 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
@@ -181,9 +167,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
181 | 167 | ||
182 | out: | 168 | out: |
183 | return self; | 169 | return self; |
184 | out_free: | ||
185 | free(self); | ||
186 | return NULL; | ||
187 | out_delete: | 170 | out_delete: |
188 | perf_session__delete(self); | 171 | perf_session__delete(self); |
189 | return NULL; | 172 | return NULL; |
@@ -214,7 +197,6 @@ static void perf_session__delete_threads(struct perf_session *self) | |||
214 | 197 | ||
215 | void perf_session__delete(struct perf_session *self) | 198 | void perf_session__delete(struct perf_session *self) |
216 | { | 199 | { |
217 | perf_header__exit(&self->header); | ||
218 | perf_session__destroy_kernel_maps(self); | 200 | perf_session__destroy_kernel_maps(self); |
219 | perf_session__delete_dead_threads(self); | 201 | perf_session__delete_dead_threads(self); |
220 | perf_session__delete_threads(self); | 202 | perf_session__delete_threads(self); |
@@ -242,17 +224,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
242 | return 0; | 224 | return 0; |
243 | } | 225 | } |
244 | 226 | ||
245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 227 | int perf_session__resolve_callchain(struct perf_session *self, |
246 | struct thread *thread, | 228 | struct thread *thread, |
247 | struct ip_callchain *chain, | 229 | struct ip_callchain *chain, |
248 | struct symbol **parent) | 230 | struct symbol **parent) |
249 | { | 231 | { |
250 | u8 cpumode = PERF_RECORD_MISC_USER; | 232 | u8 cpumode = PERF_RECORD_MISC_USER; |
251 | unsigned int i; | 233 | unsigned int i; |
252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 234 | int err; |
253 | 235 | ||
254 | if (!syms) | 236 | callchain_cursor_reset(&self->callchain_cursor); |
255 | return NULL; | ||
256 | 237 | ||
257 | for (i = 0; i < chain->nr; i++) { | 238 | for (i = 0; i < chain->nr; i++) { |
258 | u64 ip = chain->ips[i]; | 239 | u64 ip = chain->ips[i]; |
@@ -281,30 +262,33 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
281 | *parent = al.sym; | 262 | *parent = al.sym; |
282 | if (!symbol_conf.use_callchain) | 263 | if (!symbol_conf.use_callchain) |
283 | break; | 264 | break; |
284 | syms[i].map = al.map; | ||
285 | syms[i].sym = al.sym; | ||
286 | } | 265 | } |
266 | |||
267 | err = callchain_cursor_append(&self->callchain_cursor, | ||
268 | ip, al.map, al.sym); | ||
269 | if (err) | ||
270 | return err; | ||
287 | } | 271 | } |
288 | 272 | ||
289 | return syms; | 273 | return 0; |
290 | } | 274 | } |
291 | 275 | ||
292 | static int process_event_synth_stub(event_t *event __used, | 276 | static int process_event_synth_stub(union perf_event *event __used, |
293 | struct perf_session *session __used) | 277 | struct perf_session *session __used) |
294 | { | 278 | { |
295 | dump_printf(": unhandled!\n"); | 279 | dump_printf(": unhandled!\n"); |
296 | return 0; | 280 | return 0; |
297 | } | 281 | } |
298 | 282 | ||
299 | static int process_event_stub(event_t *event __used, | 283 | static int process_event_stub(union perf_event *event __used, |
300 | struct sample_data *sample __used, | 284 | struct perf_sample *sample __used, |
301 | struct perf_session *session __used) | 285 | struct perf_session *session __used) |
302 | { | 286 | { |
303 | dump_printf(": unhandled!\n"); | 287 | dump_printf(": unhandled!\n"); |
304 | return 0; | 288 | return 0; |
305 | } | 289 | } |
306 | 290 | ||
307 | static int process_finished_round_stub(event_t *event __used, | 291 | static int process_finished_round_stub(union perf_event *event __used, |
308 | struct perf_session *session __used, | 292 | struct perf_session *session __used, |
309 | struct perf_event_ops *ops __used) | 293 | struct perf_event_ops *ops __used) |
310 | { | 294 | { |
@@ -312,7 +296,7 @@ static int process_finished_round_stub(event_t *event __used, | |||
312 | return 0; | 296 | return 0; |
313 | } | 297 | } |
314 | 298 | ||
315 | static int process_finished_round(event_t *event, | 299 | static int process_finished_round(union perf_event *event, |
316 | struct perf_session *session, | 300 | struct perf_session *session, |
317 | struct perf_event_ops *ops); | 301 | struct perf_event_ops *ops); |
318 | 302 | ||
@@ -329,7 +313,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
329 | if (handler->exit == NULL) | 313 | if (handler->exit == NULL) |
330 | handler->exit = process_event_stub; | 314 | handler->exit = process_event_stub; |
331 | if (handler->lost == NULL) | 315 | if (handler->lost == NULL) |
332 | handler->lost = event__process_lost; | 316 | handler->lost = perf_event__process_lost; |
333 | if (handler->read == NULL) | 317 | if (handler->read == NULL) |
334 | handler->read = process_event_stub; | 318 | handler->read = process_event_stub; |
335 | if (handler->throttle == NULL) | 319 | if (handler->throttle == NULL) |
@@ -363,98 +347,98 @@ void mem_bswap_64(void *src, int byte_size) | |||
363 | } | 347 | } |
364 | } | 348 | } |
365 | 349 | ||
366 | static void event__all64_swap(event_t *self) | 350 | static void perf_event__all64_swap(union perf_event *event) |
367 | { | 351 | { |
368 | struct perf_event_header *hdr = &self->header; | 352 | struct perf_event_header *hdr = &event->header; |
369 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | 353 | mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); |
370 | } | 354 | } |
371 | 355 | ||
372 | static void event__comm_swap(event_t *self) | 356 | static void perf_event__comm_swap(union perf_event *event) |
373 | { | 357 | { |
374 | self->comm.pid = bswap_32(self->comm.pid); | 358 | event->comm.pid = bswap_32(event->comm.pid); |
375 | self->comm.tid = bswap_32(self->comm.tid); | 359 | event->comm.tid = bswap_32(event->comm.tid); |
376 | } | 360 | } |
377 | 361 | ||
378 | static void event__mmap_swap(event_t *self) | 362 | static void perf_event__mmap_swap(union perf_event *event) |
379 | { | 363 | { |
380 | self->mmap.pid = bswap_32(self->mmap.pid); | 364 | event->mmap.pid = bswap_32(event->mmap.pid); |
381 | self->mmap.tid = bswap_32(self->mmap.tid); | 365 | event->mmap.tid = bswap_32(event->mmap.tid); |
382 | self->mmap.start = bswap_64(self->mmap.start); | 366 | event->mmap.start = bswap_64(event->mmap.start); |
383 | self->mmap.len = bswap_64(self->mmap.len); | 367 | event->mmap.len = bswap_64(event->mmap.len); |
384 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | 368 | event->mmap.pgoff = bswap_64(event->mmap.pgoff); |
385 | } | 369 | } |
386 | 370 | ||
387 | static void event__task_swap(event_t *self) | 371 | static void perf_event__task_swap(union perf_event *event) |
388 | { | 372 | { |
389 | self->fork.pid = bswap_32(self->fork.pid); | 373 | event->fork.pid = bswap_32(event->fork.pid); |
390 | self->fork.tid = bswap_32(self->fork.tid); | 374 | event->fork.tid = bswap_32(event->fork.tid); |
391 | self->fork.ppid = bswap_32(self->fork.ppid); | 375 | event->fork.ppid = bswap_32(event->fork.ppid); |
392 | self->fork.ptid = bswap_32(self->fork.ptid); | 376 | event->fork.ptid = bswap_32(event->fork.ptid); |
393 | self->fork.time = bswap_64(self->fork.time); | 377 | event->fork.time = bswap_64(event->fork.time); |
394 | } | 378 | } |
395 | 379 | ||
396 | static void event__read_swap(event_t *self) | 380 | static void perf_event__read_swap(union perf_event *event) |
397 | { | 381 | { |
398 | self->read.pid = bswap_32(self->read.pid); | 382 | event->read.pid = bswap_32(event->read.pid); |
399 | self->read.tid = bswap_32(self->read.tid); | 383 | event->read.tid = bswap_32(event->read.tid); |
400 | self->read.value = bswap_64(self->read.value); | 384 | event->read.value = bswap_64(event->read.value); |
401 | self->read.time_enabled = bswap_64(self->read.time_enabled); | 385 | event->read.time_enabled = bswap_64(event->read.time_enabled); |
402 | self->read.time_running = bswap_64(self->read.time_running); | 386 | event->read.time_running = bswap_64(event->read.time_running); |
403 | self->read.id = bswap_64(self->read.id); | 387 | event->read.id = bswap_64(event->read.id); |
404 | } | 388 | } |
405 | 389 | ||
406 | static void event__attr_swap(event_t *self) | 390 | static void perf_event__attr_swap(union perf_event *event) |
407 | { | 391 | { |
408 | size_t size; | 392 | size_t size; |
409 | 393 | ||
410 | self->attr.attr.type = bswap_32(self->attr.attr.type); | 394 | event->attr.attr.type = bswap_32(event->attr.attr.type); |
411 | self->attr.attr.size = bswap_32(self->attr.attr.size); | 395 | event->attr.attr.size = bswap_32(event->attr.attr.size); |
412 | self->attr.attr.config = bswap_64(self->attr.attr.config); | 396 | event->attr.attr.config = bswap_64(event->attr.attr.config); |
413 | self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); | 397 | event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); |
414 | self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); | 398 | event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); |
415 | self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); | 399 | event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); |
416 | self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); | 400 | event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); |
417 | self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); | 401 | event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); |
418 | self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); | 402 | event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); |
419 | self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); | 403 | event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); |
420 | 404 | ||
421 | size = self->header.size; | 405 | size = event->header.size; |
422 | size -= (void *)&self->attr.id - (void *)self; | 406 | size -= (void *)&event->attr.id - (void *)event; |
423 | mem_bswap_64(self->attr.id, size); | 407 | mem_bswap_64(event->attr.id, size); |
424 | } | 408 | } |
425 | 409 | ||
426 | static void event__event_type_swap(event_t *self) | 410 | static void perf_event__event_type_swap(union perf_event *event) |
427 | { | 411 | { |
428 | self->event_type.event_type.event_id = | 412 | event->event_type.event_type.event_id = |
429 | bswap_64(self->event_type.event_type.event_id); | 413 | bswap_64(event->event_type.event_type.event_id); |
430 | } | 414 | } |
431 | 415 | ||
432 | static void event__tracing_data_swap(event_t *self) | 416 | static void perf_event__tracing_data_swap(union perf_event *event) |
433 | { | 417 | { |
434 | self->tracing_data.size = bswap_32(self->tracing_data.size); | 418 | event->tracing_data.size = bswap_32(event->tracing_data.size); |
435 | } | 419 | } |
436 | 420 | ||
437 | typedef void (*event__swap_op)(event_t *self); | 421 | typedef void (*perf_event__swap_op)(union perf_event *event); |
438 | 422 | ||
439 | static event__swap_op event__swap_ops[] = { | 423 | static perf_event__swap_op perf_event__swap_ops[] = { |
440 | [PERF_RECORD_MMAP] = event__mmap_swap, | 424 | [PERF_RECORD_MMAP] = perf_event__mmap_swap, |
441 | [PERF_RECORD_COMM] = event__comm_swap, | 425 | [PERF_RECORD_COMM] = perf_event__comm_swap, |
442 | [PERF_RECORD_FORK] = event__task_swap, | 426 | [PERF_RECORD_FORK] = perf_event__task_swap, |
443 | [PERF_RECORD_EXIT] = event__task_swap, | 427 | [PERF_RECORD_EXIT] = perf_event__task_swap, |
444 | [PERF_RECORD_LOST] = event__all64_swap, | 428 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
445 | [PERF_RECORD_READ] = event__read_swap, | 429 | [PERF_RECORD_READ] = perf_event__read_swap, |
446 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 430 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
447 | [PERF_RECORD_HEADER_ATTR] = event__attr_swap, | 431 | [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, |
448 | [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, | 432 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
449 | [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, | 433 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
450 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 434 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
451 | [PERF_RECORD_HEADER_MAX] = NULL, | 435 | [PERF_RECORD_HEADER_MAX] = NULL, |
452 | }; | 436 | }; |
453 | 437 | ||
454 | struct sample_queue { | 438 | struct sample_queue { |
455 | u64 timestamp; | 439 | u64 timestamp; |
456 | u64 file_offset; | 440 | u64 file_offset; |
457 | event_t *event; | 441 | union perf_event *event; |
458 | struct list_head list; | 442 | struct list_head list; |
459 | }; | 443 | }; |
460 | 444 | ||
@@ -472,8 +456,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session) | |||
472 | } | 456 | } |
473 | 457 | ||
474 | static int perf_session_deliver_event(struct perf_session *session, | 458 | static int perf_session_deliver_event(struct perf_session *session, |
475 | event_t *event, | 459 | union perf_event *event, |
476 | struct sample_data *sample, | 460 | struct perf_sample *sample, |
477 | struct perf_event_ops *ops, | 461 | struct perf_event_ops *ops, |
478 | u64 file_offset); | 462 | u64 file_offset); |
479 | 463 | ||
@@ -483,7 +467,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
483 | struct ordered_samples *os = &s->ordered_samples; | 467 | struct ordered_samples *os = &s->ordered_samples; |
484 | struct list_head *head = &os->samples; | 468 | struct list_head *head = &os->samples; |
485 | struct sample_queue *tmp, *iter; | 469 | struct sample_queue *tmp, *iter; |
486 | struct sample_data sample; | 470 | struct perf_sample sample; |
487 | u64 limit = os->next_flush; | 471 | u64 limit = os->next_flush; |
488 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 472 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
489 | 473 | ||
@@ -494,7 +478,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
494 | if (iter->timestamp > limit) | 478 | if (iter->timestamp > limit) |
495 | break; | 479 | break; |
496 | 480 | ||
497 | event__parse_sample(iter->event, s, &sample); | 481 | perf_session__parse_sample(s, iter->event, &sample); |
498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 482 | perf_session_deliver_event(s, iter->event, &sample, ops, |
499 | iter->file_offset); | 483 | iter->file_offset); |
500 | 484 | ||
@@ -550,7 +534,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
550 | * Flush every events below timestamp 7 | 534 | * Flush every events below timestamp 7 |
551 | * etc... | 535 | * etc... |
552 | */ | 536 | */ |
553 | static int process_finished_round(event_t *event __used, | 537 | static int process_finished_round(union perf_event *event __used, |
554 | struct perf_session *session, | 538 | struct perf_session *session, |
555 | struct perf_event_ops *ops) | 539 | struct perf_event_ops *ops) |
556 | { | 540 | { |
@@ -607,12 +591,12 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) | |||
607 | 591 | ||
608 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | 592 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) |
609 | 593 | ||
610 | static int perf_session_queue_event(struct perf_session *s, event_t *event, | 594 | static int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
611 | struct sample_data *data, u64 file_offset) | 595 | struct perf_sample *sample, u64 file_offset) |
612 | { | 596 | { |
613 | struct ordered_samples *os = &s->ordered_samples; | 597 | struct ordered_samples *os = &s->ordered_samples; |
614 | struct list_head *sc = &os->sample_cache; | 598 | struct list_head *sc = &os->sample_cache; |
615 | u64 timestamp = data->time; | 599 | u64 timestamp = sample->time; |
616 | struct sample_queue *new; | 600 | struct sample_queue *new; |
617 | 601 | ||
618 | if (!timestamp || timestamp == ~0ULL) | 602 | if (!timestamp || timestamp == ~0ULL) |
@@ -648,7 +632,7 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, | |||
648 | return 0; | 632 | return 0; |
649 | } | 633 | } |
650 | 634 | ||
651 | static void callchain__printf(struct sample_data *sample) | 635 | static void callchain__printf(struct perf_sample *sample) |
652 | { | 636 | { |
653 | unsigned int i; | 637 | unsigned int i; |
654 | 638 | ||
@@ -660,8 +644,8 @@ static void callchain__printf(struct sample_data *sample) | |||
660 | } | 644 | } |
661 | 645 | ||
662 | static void perf_session__print_tstamp(struct perf_session *session, | 646 | static void perf_session__print_tstamp(struct perf_session *session, |
663 | event_t *event, | 647 | union perf_event *event, |
664 | struct sample_data *sample) | 648 | struct perf_sample *sample) |
665 | { | 649 | { |
666 | if (event->header.type != PERF_RECORD_SAMPLE && | 650 | if (event->header.type != PERF_RECORD_SAMPLE && |
667 | !session->sample_id_all) { | 651 | !session->sample_id_all) { |
@@ -676,8 +660,8 @@ static void perf_session__print_tstamp(struct perf_session *session, | |||
676 | printf("%" PRIu64 " ", sample->time); | 660 | printf("%" PRIu64 " ", sample->time); |
677 | } | 661 | } |
678 | 662 | ||
679 | static void dump_event(struct perf_session *session, event_t *event, | 663 | static void dump_event(struct perf_session *session, union perf_event *event, |
680 | u64 file_offset, struct sample_data *sample) | 664 | u64 file_offset, struct perf_sample *sample) |
681 | { | 665 | { |
682 | if (!dump_trace) | 666 | if (!dump_trace) |
683 | return; | 667 | return; |
@@ -691,11 +675,11 @@ static void dump_event(struct perf_session *session, event_t *event, | |||
691 | perf_session__print_tstamp(session, event, sample); | 675 | perf_session__print_tstamp(session, event, sample); |
692 | 676 | ||
693 | printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, | 677 | printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, |
694 | event->header.size, event__get_event_name(event->header.type)); | 678 | event->header.size, perf_event__name(event->header.type)); |
695 | } | 679 | } |
696 | 680 | ||
697 | static void dump_sample(struct perf_session *session, event_t *event, | 681 | static void dump_sample(struct perf_session *session, union perf_event *event, |
698 | struct sample_data *sample) | 682 | struct perf_sample *sample) |
699 | { | 683 | { |
700 | if (!dump_trace) | 684 | if (!dump_trace) |
701 | return; | 685 | return; |
@@ -709,8 +693,8 @@ static void dump_sample(struct perf_session *session, event_t *event, | |||
709 | } | 693 | } |
710 | 694 | ||
711 | static int perf_session_deliver_event(struct perf_session *session, | 695 | static int perf_session_deliver_event(struct perf_session *session, |
712 | event_t *event, | 696 | union perf_event *event, |
713 | struct sample_data *sample, | 697 | struct perf_sample *sample, |
714 | struct perf_event_ops *ops, | 698 | struct perf_event_ops *ops, |
715 | u64 file_offset) | 699 | u64 file_offset) |
716 | { | 700 | { |
@@ -743,7 +727,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
743 | } | 727 | } |
744 | 728 | ||
745 | static int perf_session__preprocess_sample(struct perf_session *session, | 729 | static int perf_session__preprocess_sample(struct perf_session *session, |
746 | event_t *event, struct sample_data *sample) | 730 | union perf_event *event, struct perf_sample *sample) |
747 | { | 731 | { |
748 | if (event->header.type != PERF_RECORD_SAMPLE || | 732 | if (event->header.type != PERF_RECORD_SAMPLE || |
749 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | 733 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) |
@@ -758,7 +742,7 @@ static int perf_session__preprocess_sample(struct perf_session *session, | |||
758 | return 0; | 742 | return 0; |
759 | } | 743 | } |
760 | 744 | ||
761 | static int perf_session__process_user_event(struct perf_session *session, event_t *event, | 745 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
762 | struct perf_event_ops *ops, u64 file_offset) | 746 | struct perf_event_ops *ops, u64 file_offset) |
763 | { | 747 | { |
764 | dump_event(session, event, file_offset, NULL); | 748 | dump_event(session, event, file_offset, NULL); |
@@ -783,15 +767,16 @@ static int perf_session__process_user_event(struct perf_session *session, event_ | |||
783 | } | 767 | } |
784 | 768 | ||
785 | static int perf_session__process_event(struct perf_session *session, | 769 | static int perf_session__process_event(struct perf_session *session, |
786 | event_t *event, | 770 | union perf_event *event, |
787 | struct perf_event_ops *ops, | 771 | struct perf_event_ops *ops, |
788 | u64 file_offset) | 772 | u64 file_offset) |
789 | { | 773 | { |
790 | struct sample_data sample; | 774 | struct perf_sample sample; |
791 | int ret; | 775 | int ret; |
792 | 776 | ||
793 | if (session->header.needs_swap && event__swap_ops[event->header.type]) | 777 | if (session->header.needs_swap && |
794 | event__swap_ops[event->header.type](event); | 778 | perf_event__swap_ops[event->header.type]) |
779 | perf_event__swap_ops[event->header.type](event); | ||
795 | 780 | ||
796 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | 781 | if (event->header.type >= PERF_RECORD_HEADER_MAX) |
797 | return -EINVAL; | 782 | return -EINVAL; |
@@ -804,7 +789,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
804 | /* | 789 | /* |
805 | * For all kernel events we get the sample data | 790 | * For all kernel events we get the sample data |
806 | */ | 791 | */ |
807 | event__parse_sample(event, session, &sample); | 792 | perf_session__parse_sample(session, event, &sample); |
808 | 793 | ||
809 | /* Preprocess sample records - precheck callchains */ | 794 | /* Preprocess sample records - precheck callchains */ |
810 | if (perf_session__preprocess_sample(session, event, &sample)) | 795 | if (perf_session__preprocess_sample(session, event, &sample)) |
@@ -843,7 +828,7 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
843 | static void perf_session__warn_about_errors(const struct perf_session *session, | 828 | static void perf_session__warn_about_errors(const struct perf_session *session, |
844 | const struct perf_event_ops *ops) | 829 | const struct perf_event_ops *ops) |
845 | { | 830 | { |
846 | if (ops->lost == event__process_lost && | 831 | if (ops->lost == perf_event__process_lost && |
847 | session->hists.stats.total_lost != 0) { | 832 | session->hists.stats.total_lost != 0) { |
848 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 | 833 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 |
849 | "!\n\nCheck IO/CPU overload!\n\n", | 834 | "!\n\nCheck IO/CPU overload!\n\n", |
@@ -875,7 +860,7 @@ volatile int session_done; | |||
875 | static int __perf_session__process_pipe_events(struct perf_session *self, | 860 | static int __perf_session__process_pipe_events(struct perf_session *self, |
876 | struct perf_event_ops *ops) | 861 | struct perf_event_ops *ops) |
877 | { | 862 | { |
878 | event_t event; | 863 | union perf_event event; |
879 | uint32_t size; | 864 | uint32_t size; |
880 | int skip = 0; | 865 | int skip = 0; |
881 | u64 head; | 866 | u64 head; |
@@ -956,7 +941,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
956 | struct ui_progress *progress; | 941 | struct ui_progress *progress; |
957 | size_t page_size, mmap_size; | 942 | size_t page_size, mmap_size; |
958 | char *buf, *mmaps[8]; | 943 | char *buf, *mmaps[8]; |
959 | event_t *event; | 944 | union perf_event *event; |
960 | uint32_t size; | 945 | uint32_t size; |
961 | 946 | ||
962 | perf_event_ops__fill_defaults(ops); | 947 | perf_event_ops__fill_defaults(ops); |
@@ -1001,7 +986,7 @@ remap: | |||
1001 | file_pos = file_offset + head; | 986 | file_pos = file_offset + head; |
1002 | 987 | ||
1003 | more: | 988 | more: |
1004 | event = (event_t *)(buf + head); | 989 | event = (union perf_event *)(buf + head); |
1005 | 990 | ||
1006 | if (session->header.needs_swap) | 991 | if (session->header.needs_swap) |
1007 | perf_event_header__bswap(&event->header); | 992 | perf_event_header__bswap(&event->header); |
@@ -1134,3 +1119,79 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | |||
1134 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 1119 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); |
1135 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | 1120 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); |
1136 | } | 1121 | } |
1122 | |||
1123 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | ||
1124 | { | ||
1125 | struct perf_evsel *pos; | ||
1126 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | ||
1127 | |||
1128 | ret += hists__fprintf_nr_events(&session->hists, fp); | ||
1129 | |||
1130 | list_for_each_entry(pos, &session->evlist->entries, node) { | ||
1131 | ret += fprintf(fp, "%s stats:\n", event_name(pos)); | ||
1132 | ret += hists__fprintf_nr_events(&pos->hists, fp); | ||
1133 | } | ||
1134 | |||
1135 | return ret; | ||
1136 | } | ||
1137 | |||
1138 | void perf_session__print_symbols(union perf_event *event, | ||
1139 | struct perf_sample *sample, | ||
1140 | struct perf_session *session) | ||
1141 | { | ||
1142 | struct addr_location al; | ||
1143 | const char *symname, *dsoname; | ||
1144 | struct callchain_cursor *cursor = &session->callchain_cursor; | ||
1145 | struct callchain_cursor_node *node; | ||
1146 | |||
1147 | if (perf_event__preprocess_sample(event, session, &al, sample, | ||
1148 | NULL) < 0) { | ||
1149 | error("problem processing %d event, skipping it.\n", | ||
1150 | event->header.type); | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | if (symbol_conf.use_callchain && sample->callchain) { | ||
1155 | |||
1156 | if (perf_session__resolve_callchain(session, al.thread, | ||
1157 | sample->callchain, NULL) != 0) { | ||
1158 | if (verbose) | ||
1159 | error("Failed to resolve callchain. Skipping\n"); | ||
1160 | return; | ||
1161 | } | ||
1162 | callchain_cursor_commit(cursor); | ||
1163 | |||
1164 | while (1) { | ||
1165 | node = callchain_cursor_current(cursor); | ||
1166 | if (!node) | ||
1167 | break; | ||
1168 | |||
1169 | if (node->sym && node->sym->name) | ||
1170 | symname = node->sym->name; | ||
1171 | else | ||
1172 | symname = ""; | ||
1173 | |||
1174 | if (node->map && node->map->dso && node->map->dso->name) | ||
1175 | dsoname = node->map->dso->name; | ||
1176 | else | ||
1177 | dsoname = ""; | ||
1178 | |||
1179 | printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); | ||
1180 | |||
1181 | callchain_cursor_advance(cursor); | ||
1182 | } | ||
1183 | |||
1184 | } else { | ||
1185 | if (al.sym && al.sym->name) | ||
1186 | symname = al.sym->name; | ||
1187 | else | ||
1188 | symname = ""; | ||
1189 | |||
1190 | if (al.map && al.map->dso && al.map->dso->name) | ||
1191 | dsoname = al.map->dso->name; | ||
1192 | else | ||
1193 | dsoname = ""; | ||
1194 | |||
1195 | printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); | ||
1196 | } | ||
1197 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274fd..0b3c9afecaa9 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -34,12 +34,12 @@ struct perf_session { | |||
34 | struct thread *last_match; | 34 | struct thread *last_match; |
35 | struct machine host_machine; | 35 | struct machine host_machine; |
36 | struct rb_root machines; | 36 | struct rb_root machines; |
37 | struct rb_root hists_tree; | 37 | struct perf_evlist *evlist; |
38 | /* | 38 | /* |
39 | * FIXME: should point to the first entry in hists_tree and | 39 | * FIXME: Need to split this up further, we need global |
40 | * be a hists instance. Right now its only 'report' | 40 | * stats + per event stats. 'perf diff' also needs |
41 | * that is using ->hists_tree while all the rest use | 41 | * to properly support multiple events in a single |
42 | * ->hists. | 42 | * perf.data file. |
43 | */ | 43 | */ |
44 | struct hists hists; | 44 | struct hists hists; |
45 | u64 sample_type; | 45 | u64 sample_type; |
@@ -51,15 +51,17 @@ struct perf_session { | |||
51 | int cwdlen; | 51 | int cwdlen; |
52 | char *cwd; | 52 | char *cwd; |
53 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
54 | char filename[0]; | 54 | struct callchain_cursor callchain_cursor; |
55 | char filename[0]; | ||
55 | }; | 56 | }; |
56 | 57 | ||
57 | struct perf_event_ops; | 58 | struct perf_event_ops; |
58 | 59 | ||
59 | typedef int (*event_op)(event_t *self, struct sample_data *sample, | 60 | typedef int (*event_op)(union perf_event *self, struct perf_sample *sample, |
60 | struct perf_session *session); | 61 | struct perf_session *session); |
61 | typedef int (*event_synth_op)(event_t *self, struct perf_session *session); | 62 | typedef int (*event_synth_op)(union perf_event *self, |
62 | typedef int (*event_op2)(event_t *self, struct perf_session *session, | 63 | struct perf_session *session); |
64 | typedef int (*event_op2)(union perf_event *self, struct perf_session *session, | ||
63 | struct perf_event_ops *ops); | 65 | struct perf_event_ops *ops); |
64 | 66 | ||
65 | struct perf_event_ops { | 67 | struct perf_event_ops { |
@@ -94,10 +96,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
94 | int perf_session__process_events(struct perf_session *self, | 96 | int perf_session__process_events(struct perf_session *self, |
95 | struct perf_event_ops *event_ops); | 97 | struct perf_event_ops *event_ops); |
96 | 98 | ||
97 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 99 | int perf_session__resolve_callchain(struct perf_session *self, |
98 | struct thread *thread, | 100 | struct thread *thread, |
99 | struct ip_callchain *chain, | 101 | struct ip_callchain *chain, |
100 | struct symbol **parent); | 102 | struct symbol **parent); |
101 | 103 | ||
102 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 104 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
103 | 105 | ||
@@ -110,8 +112,6 @@ void mem_bswap_64(void *src, int byte_size); | |||
110 | int perf_session__create_kernel_maps(struct perf_session *self); | 112 | int perf_session__create_kernel_maps(struct perf_session *self); |
111 | 113 | ||
112 | void perf_session__update_sample_type(struct perf_session *self); | 114 | void perf_session__update_sample_type(struct perf_session *self); |
113 | void perf_session__set_sample_id_all(struct perf_session *session, bool value); | ||
114 | void perf_session__set_sample_type(struct perf_session *session, u64 type); | ||
115 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | 115 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); |
116 | 116 | ||
117 | static inline | 117 | static inline |
@@ -149,9 +149,18 @@ size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | |||
149 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 149 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, |
150 | FILE *fp, bool with_hits); | 150 | FILE *fp, bool with_hits); |
151 | 151 | ||
152 | static inline | 152 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
153 | size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | 153 | |
154 | static inline int perf_session__parse_sample(struct perf_session *session, | ||
155 | const union perf_event *event, | ||
156 | struct perf_sample *sample) | ||
154 | { | 157 | { |
155 | return hists__fprintf_nr_events(&self->hists, fp); | 158 | return perf_event__parse_sample(event, session->sample_type, |
159 | session->sample_id_all, sample); | ||
156 | } | 160 | } |
161 | |||
162 | void perf_session__print_symbols(union perf_event *event, | ||
163 | struct perf_sample *sample, | ||
164 | struct perf_session *session); | ||
165 | |||
157 | #endif /* __PERF_SESSION_H */ | 166 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py new file mode 100644 index 000000000000..e24ffadb20b2 --- /dev/null +++ b/tools/perf/util/setup.py | |||
@@ -0,0 +1,19 @@ | |||
1 | #!/usr/bin/python2 | ||
2 | |||
3 | from distutils.core import setup, Extension | ||
4 | |||
5 | perf = Extension('perf', | ||
6 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | ||
7 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | ||
8 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c'], | ||
9 | include_dirs = ['util/include'], | ||
10 | extra_compile_args = ['-fno-strict-aliasing', '-Wno-write-strings']) | ||
11 | |||
12 | setup(name='perf', | ||
13 | version='0.1', | ||
14 | description='Interface with the Linux profiling infrastructure', | ||
15 | author='Arnaldo Carvalho de Melo', | ||
16 | author_email='acme@redhat.com', | ||
17 | license='GPLv2', | ||
18 | url='http://perf.wiki.kernel.org', | ||
19 | ext_modules=[perf]) | ||
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c new file mode 100644 index 000000000000..834c8ebfe38e --- /dev/null +++ b/tools/perf/util/strfilter.c | |||
@@ -0,0 +1,199 @@ | |||
1 | #include "util.h" | ||
2 | #include "string.h" | ||
3 | #include "strfilter.h" | ||
4 | |||
5 | /* Operators */ | ||
6 | static const char *OP_and = "&"; /* Logical AND */ | ||
7 | static const char *OP_or = "|"; /* Logical OR */ | ||
8 | static const char *OP_not = "!"; /* Logical NOT */ | ||
9 | |||
10 | #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') | ||
11 | #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') | ||
12 | |||
13 | static void strfilter_node__delete(struct strfilter_node *self) | ||
14 | { | ||
15 | if (self) { | ||
16 | if (self->p && !is_operator(*self->p)) | ||
17 | free((char *)self->p); | ||
18 | strfilter_node__delete(self->l); | ||
19 | strfilter_node__delete(self->r); | ||
20 | free(self); | ||
21 | } | ||
22 | } | ||
23 | |||
24 | void strfilter__delete(struct strfilter *self) | ||
25 | { | ||
26 | if (self) { | ||
27 | strfilter_node__delete(self->root); | ||
28 | free(self); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static const char *get_token(const char *s, const char **e) | ||
33 | { | ||
34 | const char *p; | ||
35 | |||
36 | while (isspace(*s)) /* Skip spaces */ | ||
37 | s++; | ||
38 | |||
39 | if (*s == '\0') { | ||
40 | p = s; | ||
41 | goto end; | ||
42 | } | ||
43 | |||
44 | p = s + 1; | ||
45 | if (!is_separator(*s)) { | ||
46 | /* End search */ | ||
47 | retry: | ||
48 | while (*p && !is_separator(*p) && !isspace(*p)) | ||
49 | p++; | ||
50 | /* Escape and special case: '!' is also used in glob pattern */ | ||
51 | if (*(p - 1) == '\\' || (*p == '!' && *(p - 1) == '[')) { | ||
52 | p++; | ||
53 | goto retry; | ||
54 | } | ||
55 | } | ||
56 | end: | ||
57 | *e = p; | ||
58 | return s; | ||
59 | } | ||
60 | |||
61 | static struct strfilter_node *strfilter_node__alloc(const char *op, | ||
62 | struct strfilter_node *l, | ||
63 | struct strfilter_node *r) | ||
64 | { | ||
65 | struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); | ||
66 | |||
67 | if (ret) { | ||
68 | ret->p = op; | ||
69 | ret->l = l; | ||
70 | ret->r = r; | ||
71 | } | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static struct strfilter_node *strfilter_node__new(const char *s, | ||
77 | const char **ep) | ||
78 | { | ||
79 | struct strfilter_node root, *cur, *last_op; | ||
80 | const char *e; | ||
81 | |||
82 | if (!s) | ||
83 | return NULL; | ||
84 | |||
85 | memset(&root, 0, sizeof(root)); | ||
86 | last_op = cur = &root; | ||
87 | |||
88 | s = get_token(s, &e); | ||
89 | while (*s != '\0' && *s != ')') { | ||
90 | switch (*s) { | ||
91 | case '&': /* Exchg last OP->r with AND */ | ||
92 | if (!cur->r || !last_op->r) | ||
93 | goto error; | ||
94 | cur = strfilter_node__alloc(OP_and, last_op->r, NULL); | ||
95 | if (!cur) | ||
96 | goto nomem; | ||
97 | last_op->r = cur; | ||
98 | last_op = cur; | ||
99 | break; | ||
100 | case '|': /* Exchg the root with OR */ | ||
101 | if (!cur->r || !root.r) | ||
102 | goto error; | ||
103 | cur = strfilter_node__alloc(OP_or, root.r, NULL); | ||
104 | if (!cur) | ||
105 | goto nomem; | ||
106 | root.r = cur; | ||
107 | last_op = cur; | ||
108 | break; | ||
109 | case '!': /* Add NOT as a leaf node */ | ||
110 | if (cur->r) | ||
111 | goto error; | ||
112 | cur->r = strfilter_node__alloc(OP_not, NULL, NULL); | ||
113 | if (!cur->r) | ||
114 | goto nomem; | ||
115 | cur = cur->r; | ||
116 | break; | ||
117 | case '(': /* Recursively parses inside the parenthesis */ | ||
118 | if (cur->r) | ||
119 | goto error; | ||
120 | cur->r = strfilter_node__new(s + 1, &s); | ||
121 | if (!s) | ||
122 | goto nomem; | ||
123 | if (!cur->r || *s != ')') | ||
124 | goto error; | ||
125 | e = s + 1; | ||
126 | break; | ||
127 | default: | ||
128 | if (cur->r) | ||
129 | goto error; | ||
130 | cur->r = strfilter_node__alloc(NULL, NULL, NULL); | ||
131 | if (!cur->r) | ||
132 | goto nomem; | ||
133 | cur->r->p = strndup(s, e - s); | ||
134 | if (!cur->r->p) | ||
135 | goto nomem; | ||
136 | } | ||
137 | s = get_token(e, &e); | ||
138 | } | ||
139 | if (!cur->r) | ||
140 | goto error; | ||
141 | *ep = s; | ||
142 | return root.r; | ||
143 | nomem: | ||
144 | s = NULL; | ||
145 | error: | ||
146 | *ep = s; | ||
147 | strfilter_node__delete(root.r); | ||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Parse filter rule and return new strfilter. | ||
153 | * Return NULL if fail, and *ep == NULL if memory allocation failed. | ||
154 | */ | ||
155 | struct strfilter *strfilter__new(const char *rules, const char **err) | ||
156 | { | ||
157 | struct strfilter *ret = zalloc(sizeof(struct strfilter)); | ||
158 | const char *ep = NULL; | ||
159 | |||
160 | if (ret) | ||
161 | ret->root = strfilter_node__new(rules, &ep); | ||
162 | |||
163 | if (!ret || !ret->root || *ep != '\0') { | ||
164 | if (err) | ||
165 | *err = ep; | ||
166 | strfilter__delete(ret); | ||
167 | ret = NULL; | ||
168 | } | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static bool strfilter_node__compare(struct strfilter_node *self, | ||
174 | const char *str) | ||
175 | { | ||
176 | if (!self || !self->p) | ||
177 | return false; | ||
178 | |||
179 | switch (*self->p) { | ||
180 | case '|': /* OR */ | ||
181 | return strfilter_node__compare(self->l, str) || | ||
182 | strfilter_node__compare(self->r, str); | ||
183 | case '&': /* AND */ | ||
184 | return strfilter_node__compare(self->l, str) && | ||
185 | strfilter_node__compare(self->r, str); | ||
186 | case '!': /* NOT */ | ||
187 | return !strfilter_node__compare(self->r, str); | ||
188 | default: | ||
189 | return strglobmatch(str, self->p); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /* Return true if STR matches the filter rules */ | ||
194 | bool strfilter__compare(struct strfilter *self, const char *str) | ||
195 | { | ||
196 | if (!self) | ||
197 | return false; | ||
198 | return strfilter_node__compare(self->root, str); | ||
199 | } | ||
diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h new file mode 100644 index 000000000000..00f58a7506de --- /dev/null +++ b/tools/perf/util/strfilter.h | |||
@@ -0,0 +1,48 @@ | |||
1 | #ifndef __PERF_STRFILTER_H | ||
2 | #define __PERF_STRFILTER_H | ||
3 | /* General purpose glob matching filter */ | ||
4 | |||
5 | #include <linux/list.h> | ||
6 | #include <stdbool.h> | ||
7 | |||
8 | /* A node of string filter */ | ||
9 | struct strfilter_node { | ||
10 | struct strfilter_node *l; /* Tree left branche (for &,|) */ | ||
11 | struct strfilter_node *r; /* Tree right branche (for !,&,|) */ | ||
12 | const char *p; /* Operator or rule */ | ||
13 | }; | ||
14 | |||
15 | /* String filter */ | ||
16 | struct strfilter { | ||
17 | struct strfilter_node *root; | ||
18 | }; | ||
19 | |||
20 | /** | ||
21 | * strfilter__new - Create a new string filter | ||
22 | * @rules: Filter rule, which is a combination of glob expressions. | ||
23 | * @err: Pointer which points an error detected on @rules | ||
24 | * | ||
25 | * Parse @rules and return new strfilter. Return NULL if an error detected. | ||
26 | * In that case, *@err will indicate where it is detected, and *@err is NULL | ||
27 | * if a memory allocation is failed. | ||
28 | */ | ||
29 | struct strfilter *strfilter__new(const char *rules, const char **err); | ||
30 | |||
31 | /** | ||
32 | * strfilter__compare - compare given string and a string filter | ||
33 | * @self: String filter | ||
34 | * @str: target string | ||
35 | * | ||
36 | * Compare @str and @self. Return true if the str match the rule | ||
37 | */ | ||
38 | bool strfilter__compare(struct strfilter *self, const char *str); | ||
39 | |||
40 | /** | ||
41 | * strfilter__delete - delete a string filter | ||
42 | * @self: String filter to delete | ||
43 | * | ||
44 | * Delete @self. | ||
45 | */ | ||
46 | void strfilter__delete(struct strfilter *self); | ||
47 | |||
48 | #endif | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 7821d0e6866f..651dbfe7f4f3 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -207,8 +207,7 @@ struct dso *dso__new(const char *name) | |||
207 | dso__set_short_name(self, self->name); | 207 | dso__set_short_name(self, self->name); |
208 | for (i = 0; i < MAP__NR_TYPES; ++i) | 208 | for (i = 0; i < MAP__NR_TYPES; ++i) |
209 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | 209 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; |
210 | self->slen_calculated = 0; | 210 | self->symtab_type = SYMTAB__NOT_FOUND; |
211 | self->origin = DSO__ORIG_NOT_FOUND; | ||
212 | self->loaded = 0; | 211 | self->loaded = 0; |
213 | self->sorted_by_name = 0; | 212 | self->sorted_by_name = 0; |
214 | self->has_build_id = 0; | 213 | self->has_build_id = 0; |
@@ -681,9 +680,9 @@ int dso__load_kallsyms(struct dso *self, const char *filename, | |||
681 | return -1; | 680 | return -1; |
682 | 681 | ||
683 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) | 682 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) |
684 | self->origin = DSO__ORIG_GUEST_KERNEL; | 683 | self->symtab_type = SYMTAB__GUEST_KALLSYMS; |
685 | else | 684 | else |
686 | self->origin = DSO__ORIG_KERNEL; | 685 | self->symtab_type = SYMTAB__KALLSYMS; |
687 | 686 | ||
688 | return dso__split_kallsyms(self, map, filter); | 687 | return dso__split_kallsyms(self, map, filter); |
689 | } | 688 | } |
@@ -1205,7 +1204,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1205 | } | 1204 | } |
1206 | curr_map->map_ip = identity__map_ip; | 1205 | curr_map->map_ip = identity__map_ip; |
1207 | curr_map->unmap_ip = identity__map_ip; | 1206 | curr_map->unmap_ip = identity__map_ip; |
1208 | curr_dso->origin = self->origin; | 1207 | curr_dso->symtab_type = self->symtab_type; |
1209 | map_groups__insert(kmap->kmaps, curr_map); | 1208 | map_groups__insert(kmap->kmaps, curr_map); |
1210 | dsos__add(&self->node, curr_dso); | 1209 | dsos__add(&self->node, curr_dso); |
1211 | dso__set_loaded(curr_dso, map->type); | 1210 | dso__set_loaded(curr_dso, map->type); |
@@ -1431,21 +1430,21 @@ out: | |||
1431 | char dso__symtab_origin(const struct dso *self) | 1430 | char dso__symtab_origin(const struct dso *self) |
1432 | { | 1431 | { |
1433 | static const char origin[] = { | 1432 | static const char origin[] = { |
1434 | [DSO__ORIG_KERNEL] = 'k', | 1433 | [SYMTAB__KALLSYMS] = 'k', |
1435 | [DSO__ORIG_JAVA_JIT] = 'j', | 1434 | [SYMTAB__JAVA_JIT] = 'j', |
1436 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | 1435 | [SYMTAB__BUILD_ID_CACHE] = 'B', |
1437 | [DSO__ORIG_FEDORA] = 'f', | 1436 | [SYMTAB__FEDORA_DEBUGINFO] = 'f', |
1438 | [DSO__ORIG_UBUNTU] = 'u', | 1437 | [SYMTAB__UBUNTU_DEBUGINFO] = 'u', |
1439 | [DSO__ORIG_BUILDID] = 'b', | 1438 | [SYMTAB__BUILDID_DEBUGINFO] = 'b', |
1440 | [DSO__ORIG_DSO] = 'd', | 1439 | [SYMTAB__SYSTEM_PATH_DSO] = 'd', |
1441 | [DSO__ORIG_KMODULE] = 'K', | 1440 | [SYMTAB__SYSTEM_PATH_KMODULE] = 'K', |
1442 | [DSO__ORIG_GUEST_KERNEL] = 'g', | 1441 | [SYMTAB__GUEST_KALLSYMS] = 'g', |
1443 | [DSO__ORIG_GUEST_KMODULE] = 'G', | 1442 | [SYMTAB__GUEST_KMODULE] = 'G', |
1444 | }; | 1443 | }; |
1445 | 1444 | ||
1446 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1445 | if (self == NULL || self->symtab_type == SYMTAB__NOT_FOUND) |
1447 | return '!'; | 1446 | return '!'; |
1448 | return origin[self->origin]; | 1447 | return origin[self->symtab_type]; |
1449 | } | 1448 | } |
1450 | 1449 | ||
1451 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | 1450 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) |
@@ -1478,8 +1477,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1478 | 1477 | ||
1479 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 1478 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
1480 | ret = dso__load_perf_map(self, map, filter); | 1479 | ret = dso__load_perf_map(self, map, filter); |
1481 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 1480 | self->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : |
1482 | DSO__ORIG_NOT_FOUND; | 1481 | SYMTAB__NOT_FOUND; |
1483 | return ret; | 1482 | return ret; |
1484 | } | 1483 | } |
1485 | 1484 | ||
@@ -1487,26 +1486,26 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1487 | * On the first pass, only load images if they have a full symtab. | 1486 | * On the first pass, only load images if they have a full symtab. |
1488 | * Failing that, do a second pass where we accept .dynsym also | 1487 | * Failing that, do a second pass where we accept .dynsym also |
1489 | */ | 1488 | */ |
1490 | for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; | 1489 | for (self->symtab_type = SYMTAB__BUILD_ID_CACHE, want_symtab = 1; |
1491 | self->origin != DSO__ORIG_NOT_FOUND; | 1490 | self->symtab_type != SYMTAB__NOT_FOUND; |
1492 | self->origin++) { | 1491 | self->symtab_type++) { |
1493 | switch (self->origin) { | 1492 | switch (self->symtab_type) { |
1494 | case DSO__ORIG_BUILD_ID_CACHE: | 1493 | case SYMTAB__BUILD_ID_CACHE: |
1495 | /* skip the locally configured cache if a symfs is given */ | 1494 | /* skip the locally configured cache if a symfs is given */ |
1496 | if (symbol_conf.symfs[0] || | 1495 | if (symbol_conf.symfs[0] || |
1497 | (dso__build_id_filename(self, name, size) == NULL)) { | 1496 | (dso__build_id_filename(self, name, size) == NULL)) { |
1498 | continue; | 1497 | continue; |
1499 | } | 1498 | } |
1500 | break; | 1499 | break; |
1501 | case DSO__ORIG_FEDORA: | 1500 | case SYMTAB__FEDORA_DEBUGINFO: |
1502 | snprintf(name, size, "%s/usr/lib/debug%s.debug", | 1501 | snprintf(name, size, "%s/usr/lib/debug%s.debug", |
1503 | symbol_conf.symfs, self->long_name); | 1502 | symbol_conf.symfs, self->long_name); |
1504 | break; | 1503 | break; |
1505 | case DSO__ORIG_UBUNTU: | 1504 | case SYMTAB__UBUNTU_DEBUGINFO: |
1506 | snprintf(name, size, "%s/usr/lib/debug%s", | 1505 | snprintf(name, size, "%s/usr/lib/debug%s", |
1507 | symbol_conf.symfs, self->long_name); | 1506 | symbol_conf.symfs, self->long_name); |
1508 | break; | 1507 | break; |
1509 | case DSO__ORIG_BUILDID: { | 1508 | case SYMTAB__BUILDID_DEBUGINFO: { |
1510 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1509 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1511 | 1510 | ||
1512 | if (!self->has_build_id) | 1511 | if (!self->has_build_id) |
@@ -1520,20 +1519,20 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1520 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 1519 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); |
1521 | } | 1520 | } |
1522 | break; | 1521 | break; |
1523 | case DSO__ORIG_DSO: | 1522 | case SYMTAB__SYSTEM_PATH_DSO: |
1524 | snprintf(name, size, "%s%s", | 1523 | snprintf(name, size, "%s%s", |
1525 | symbol_conf.symfs, self->long_name); | 1524 | symbol_conf.symfs, self->long_name); |
1526 | break; | 1525 | break; |
1527 | case DSO__ORIG_GUEST_KMODULE: | 1526 | case SYMTAB__GUEST_KMODULE: |
1528 | if (map->groups && map->groups->machine) | 1527 | if (map->groups && machine) |
1529 | root_dir = map->groups->machine->root_dir; | 1528 | root_dir = machine->root_dir; |
1530 | else | 1529 | else |
1531 | root_dir = ""; | 1530 | root_dir = ""; |
1532 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, | 1531 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, |
1533 | root_dir, self->long_name); | 1532 | root_dir, self->long_name); |
1534 | break; | 1533 | break; |
1535 | 1534 | ||
1536 | case DSO__ORIG_KMODULE: | 1535 | case SYMTAB__SYSTEM_PATH_KMODULE: |
1537 | snprintf(name, size, "%s%s", symbol_conf.symfs, | 1536 | snprintf(name, size, "%s%s", symbol_conf.symfs, |
1538 | self->long_name); | 1537 | self->long_name); |
1539 | break; | 1538 | break; |
@@ -1545,7 +1544,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1545 | */ | 1544 | */ |
1546 | if (want_symtab) { | 1545 | if (want_symtab) { |
1547 | want_symtab = 0; | 1546 | want_symtab = 0; |
1548 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | 1547 | self->symtab_type = SYMTAB__BUILD_ID_CACHE; |
1549 | } else | 1548 | } else |
1550 | continue; | 1549 | continue; |
1551 | } | 1550 | } |
@@ -1758,9 +1757,9 @@ struct map *machine__new_module(struct machine *self, u64 start, | |||
1758 | return NULL; | 1757 | return NULL; |
1759 | 1758 | ||
1760 | if (machine__is_host(self)) | 1759 | if (machine__is_host(self)) |
1761 | dso->origin = DSO__ORIG_KMODULE; | 1760 | dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE; |
1762 | else | 1761 | else |
1763 | dso->origin = DSO__ORIG_GUEST_KMODULE; | 1762 | dso->symtab_type = SYMTAB__GUEST_KMODULE; |
1764 | map_groups__insert(&self->kmaps, map); | 1763 | map_groups__insert(&self->kmaps, map); |
1765 | return map; | 1764 | return map; |
1766 | } | 1765 | } |
@@ -1836,7 +1835,7 @@ int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1836 | int err = -1, fd; | 1835 | int err = -1, fd; |
1837 | char symfs_vmlinux[PATH_MAX]; | 1836 | char symfs_vmlinux[PATH_MAX]; |
1838 | 1837 | ||
1839 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s", | 1838 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", |
1840 | symbol_conf.symfs, vmlinux); | 1839 | symbol_conf.symfs, vmlinux); |
1841 | fd = open(symfs_vmlinux, O_RDONLY); | 1840 | fd = open(symfs_vmlinux, O_RDONLY); |
1842 | if (fd < 0) | 1841 | if (fd < 0) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 670cd1c88f54..713b0b40cc4a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -48,12 +48,17 @@ char *strxfrchar(char *s, char from, char to); | |||
48 | 48 | ||
49 | #define BUILD_ID_SIZE 20 | 49 | #define BUILD_ID_SIZE 20 |
50 | 50 | ||
51 | /** struct symbol - symtab entry | ||
52 | * | ||
53 | * @ignore - resolvable but tools ignore it (e.g. idle routines) | ||
54 | */ | ||
51 | struct symbol { | 55 | struct symbol { |
52 | struct rb_node rb_node; | 56 | struct rb_node rb_node; |
53 | u64 start; | 57 | u64 start; |
54 | u64 end; | 58 | u64 end; |
55 | u16 namelen; | 59 | u16 namelen; |
56 | u8 binding; | 60 | u8 binding; |
61 | bool ignore; | ||
57 | char name[0]; | 62 | char name[0]; |
58 | }; | 63 | }; |
59 | 64 | ||
@@ -132,13 +137,12 @@ struct dso { | |||
132 | struct rb_root symbol_names[MAP__NR_TYPES]; | 137 | struct rb_root symbol_names[MAP__NR_TYPES]; |
133 | enum dso_kernel_type kernel; | 138 | enum dso_kernel_type kernel; |
134 | u8 adjust_symbols:1; | 139 | u8 adjust_symbols:1; |
135 | u8 slen_calculated:1; | ||
136 | u8 has_build_id:1; | 140 | u8 has_build_id:1; |
137 | u8 hit:1; | 141 | u8 hit:1; |
138 | u8 annotate_warned:1; | 142 | u8 annotate_warned:1; |
139 | u8 sname_alloc:1; | 143 | u8 sname_alloc:1; |
140 | u8 lname_alloc:1; | 144 | u8 lname_alloc:1; |
141 | unsigned char origin; | 145 | unsigned char symtab_type; |
142 | u8 sorted_by_name; | 146 | u8 sorted_by_name; |
143 | u8 loaded; | 147 | u8 loaded; |
144 | u8 build_id[BUILD_ID_SIZE]; | 148 | u8 build_id[BUILD_ID_SIZE]; |
@@ -189,18 +193,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | |||
189 | size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp); | 193 | size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp); |
190 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 194 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
191 | 195 | ||
192 | enum dso_origin { | 196 | enum symtab_type { |
193 | DSO__ORIG_KERNEL = 0, | 197 | SYMTAB__KALLSYMS = 0, |
194 | DSO__ORIG_GUEST_KERNEL, | 198 | SYMTAB__GUEST_KALLSYMS, |
195 | DSO__ORIG_JAVA_JIT, | 199 | SYMTAB__JAVA_JIT, |
196 | DSO__ORIG_BUILD_ID_CACHE, | 200 | SYMTAB__BUILD_ID_CACHE, |
197 | DSO__ORIG_FEDORA, | 201 | SYMTAB__FEDORA_DEBUGINFO, |
198 | DSO__ORIG_UBUNTU, | 202 | SYMTAB__UBUNTU_DEBUGINFO, |
199 | DSO__ORIG_BUILDID, | 203 | SYMTAB__BUILDID_DEBUGINFO, |
200 | DSO__ORIG_DSO, | 204 | SYMTAB__SYSTEM_PATH_DSO, |
201 | DSO__ORIG_GUEST_KMODULE, | 205 | SYMTAB__GUEST_KMODULE, |
202 | DSO__ORIG_KMODULE, | 206 | SYMTAB__SYSTEM_PATH_KMODULE, |
203 | DSO__ORIG_NOT_FOUND, | 207 | SYMTAB__NOT_FOUND, |
204 | }; | 208 | }; |
205 | 209 | ||
206 | char dso__symtab_origin(const struct dso *self); | 210 | char dso__symtab_origin(const struct dso *self); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 00f4eade2e3e..d5d3b22250f3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,61 +7,6 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | /* Skip "." and ".." directories */ | ||
11 | static int filter(const struct dirent *dir) | ||
12 | { | ||
13 | if (dir->d_name[0] == '.') | ||
14 | return 0; | ||
15 | else | ||
16 | return 1; | ||
17 | } | ||
18 | |||
19 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
20 | { | ||
21 | struct thread_map *threads; | ||
22 | char name[256]; | ||
23 | int items; | ||
24 | struct dirent **namelist = NULL; | ||
25 | int i; | ||
26 | |||
27 | sprintf(name, "/proc/%d/task", pid); | ||
28 | items = scandir(name, &namelist, filter, NULL); | ||
29 | if (items <= 0) | ||
30 | return NULL; | ||
31 | |||
32 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
33 | if (threads != NULL) { | ||
34 | for (i = 0; i < items; i++) | ||
35 | threads->map[i] = atoi(namelist[i]->d_name); | ||
36 | threads->nr = items; | ||
37 | } | ||
38 | |||
39 | for (i=0; i<items; i++) | ||
40 | free(namelist[i]); | ||
41 | free(namelist); | ||
42 | |||
43 | return threads; | ||
44 | } | ||
45 | |||
46 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
47 | { | ||
48 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
49 | |||
50 | if (threads != NULL) { | ||
51 | threads->map[0] = tid; | ||
52 | threads->nr = 1; | ||
53 | } | ||
54 | |||
55 | return threads; | ||
56 | } | ||
57 | |||
58 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
59 | { | ||
60 | if (pid != -1) | ||
61 | return thread_map__new_by_pid(pid); | ||
62 | return thread_map__new_by_tid(tid); | ||
63 | } | ||
64 | |||
65 | static struct thread *thread__new(pid_t pid) | 10 | static struct thread *thread__new(pid_t pid) |
66 | { | 11 | { |
67 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d7574101054a..e5f2401c1b5e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -18,24 +18,10 @@ struct thread { | |||
18 | int comm_len; | 18 | int comm_len; |
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct thread_map { | ||
22 | int nr; | ||
23 | int map[]; | ||
24 | }; | ||
25 | |||
26 | struct perf_session; | 21 | struct perf_session; |
27 | 22 | ||
28 | void thread__delete(struct thread *self); | 23 | void thread__delete(struct thread *self); |
29 | 24 | ||
30 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
31 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
32 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
33 | |||
34 | static inline void thread_map__delete(struct thread_map *threads) | ||
35 | { | ||
36 | free(threads); | ||
37 | } | ||
38 | |||
39 | int thread__set_comm(struct thread *self, const char *comm); | 25 | int thread__set_comm(struct thread *self, const char *comm); |
40 | int thread__comm_len(struct thread *self); | 26 | int thread__comm_len(struct thread *self); |
41 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 27 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c new file mode 100644 index 000000000000..a5df131b77c3 --- /dev/null +++ b/tools/perf/util/thread_map.c | |||
@@ -0,0 +1,64 @@ | |||
1 | #include <dirent.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include "thread_map.h" | ||
5 | |||
6 | /* Skip "." and ".." directories */ | ||
7 | static int filter(const struct dirent *dir) | ||
8 | { | ||
9 | if (dir->d_name[0] == '.') | ||
10 | return 0; | ||
11 | else | ||
12 | return 1; | ||
13 | } | ||
14 | |||
15 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
16 | { | ||
17 | struct thread_map *threads; | ||
18 | char name[256]; | ||
19 | int items; | ||
20 | struct dirent **namelist = NULL; | ||
21 | int i; | ||
22 | |||
23 | sprintf(name, "/proc/%d/task", pid); | ||
24 | items = scandir(name, &namelist, filter, NULL); | ||
25 | if (items <= 0) | ||
26 | return NULL; | ||
27 | |||
28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
29 | if (threads != NULL) { | ||
30 | for (i = 0; i < items; i++) | ||
31 | threads->map[i] = atoi(namelist[i]->d_name); | ||
32 | threads->nr = items; | ||
33 | } | ||
34 | |||
35 | for (i=0; i<items; i++) | ||
36 | free(namelist[i]); | ||
37 | free(namelist); | ||
38 | |||
39 | return threads; | ||
40 | } | ||
41 | |||
42 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
43 | { | ||
44 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
45 | |||
46 | if (threads != NULL) { | ||
47 | threads->map[0] = tid; | ||
48 | threads->nr = 1; | ||
49 | } | ||
50 | |||
51 | return threads; | ||
52 | } | ||
53 | |||
54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
55 | { | ||
56 | if (pid != -1) | ||
57 | return thread_map__new_by_pid(pid); | ||
58 | return thread_map__new_by_tid(tid); | ||
59 | } | ||
60 | |||
61 | void thread_map__delete(struct thread_map *threads) | ||
62 | { | ||
63 | free(threads); | ||
64 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h new file mode 100644 index 000000000000..3cb907311409 --- /dev/null +++ b/tools/perf/util/thread_map.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef __PERF_THREAD_MAP_H | ||
2 | #define __PERF_THREAD_MAP_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | |||
6 | struct thread_map { | ||
7 | int nr; | ||
8 | int map[]; | ||
9 | }; | ||
10 | |||
11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
14 | void thread_map__delete(struct thread_map *threads); | ||
15 | #endif /* __PERF_THREAD_MAP_H */ | ||
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c new file mode 100644 index 000000000000..a11f60735a18 --- /dev/null +++ b/tools/perf/util/top.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Refactored from builtin-top.c, see that files for further copyright notes. | ||
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | ||
8 | |||
9 | #include "cpumap.h" | ||
10 | #include "event.h" | ||
11 | #include "evlist.h" | ||
12 | #include "evsel.h" | ||
13 | #include "parse-events.h" | ||
14 | #include "symbol.h" | ||
15 | #include "top.h" | ||
16 | #include <inttypes.h> | ||
17 | |||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | #define SNPRINTF(buf, size, fmt, args...) \ | ||
65 | ({ \ | ||
66 | size_t r = snprintf(buf, size, fmt, ## args); \ | ||
67 | r > size ? size : r; \ | ||
68 | }) | ||
69 | |||
70 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | ||
71 | { | ||
72 | struct perf_evsel *counter; | ||
73 | float samples_per_sec = top->samples / top->delay_secs; | ||
74 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | ||
75 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | ||
76 | size_t ret = 0; | ||
77 | |||
78 | if (!perf_guest) { | ||
79 | ret = SNPRINTF(bf, size, | ||
80 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | ||
81 | " exact: %4.1f%% [", samples_per_sec, | ||
82 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
83 | samples_per_sec)), | ||
84 | esamples_percent); | ||
85 | } else { | ||
86 | float us_samples_per_sec = top->us_samples / top->delay_secs; | ||
87 | float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; | ||
88 | float guest_us_samples_per_sec = top->guest_us_samples / top->delay_secs; | ||
89 | |||
90 | ret = SNPRINTF(bf, size, | ||
91 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
92 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
93 | " exact: %4.1f%% [", samples_per_sec, | ||
94 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
95 | samples_per_sec)), | ||
96 | 100.0 - (100.0 * ((samples_per_sec - us_samples_per_sec) / | ||
97 | samples_per_sec)), | ||
98 | 100.0 - (100.0 * ((samples_per_sec - | ||
99 | guest_kernel_samples_per_sec) / | ||
100 | samples_per_sec)), | ||
101 | 100.0 - (100.0 * ((samples_per_sec - | ||
102 | guest_us_samples_per_sec) / | ||
103 | samples_per_sec)), | ||
104 | esamples_percent); | ||
105 | } | ||
106 | |||
107 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
108 | struct perf_evsel *first; | ||
109 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | ||
110 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | ||
111 | (uint64_t)first->attr.sample_period, | ||
112 | top->freq ? "Hz" : ""); | ||
113 | } | ||
114 | |||
115 | if (!top->display_weighted) { | ||
116 | ret += SNPRINTF(bf + ret, size - ret, "%s", | ||
117 | event_name(top->sym_evsel)); | ||
118 | } else { | ||
119 | /* | ||
120 | * Don't let events eat all the space. Leaving 30 bytes | ||
121 | * for the rest should be enough. | ||
122 | */ | ||
123 | size_t last_pos = size - 30; | ||
124 | |||
125 | list_for_each_entry(counter, &top->evlist->entries, node) { | ||
126 | ret += SNPRINTF(bf + ret, size - ret, "%s%s", | ||
127 | counter->idx ? "/" : "", | ||
128 | event_name(counter)); | ||
129 | if (ret > last_pos) { | ||
130 | sprintf(bf + last_pos - 3, ".."); | ||
131 | ret = last_pos - 1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | ret += SNPRINTF(bf + ret, size - ret, "], "); | ||
138 | |||
139 | if (top->target_pid != -1) | ||
140 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", | ||
141 | top->target_pid); | ||
142 | else if (top->target_tid != -1) | ||
143 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", | ||
144 | top->target_tid); | ||
145 | else | ||
146 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | ||
147 | |||
148 | if (top->cpu_list) | ||
149 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | ||
150 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | ||
151 | else { | ||
152 | if (top->target_tid != -1) | ||
153 | ret += SNPRINTF(bf + ret, size - ret, ")"); | ||
154 | else | ||
155 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | ||
156 | top->evlist->cpus->nr, | ||
157 | top->evlist->cpus->nr > 1 ? "s" : ""); | ||
158 | } | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | void perf_top__reset_sample_counters(struct perf_top *top) | ||
164 | { | ||
165 | top->samples = top->us_samples = top->kernel_samples = | ||
166 | top->exact_samples = top->guest_kernel_samples = | ||
167 | top->guest_us_samples = 0; | ||
168 | } | ||
169 | |||
170 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
171 | { | ||
172 | struct sym_entry *syme, *n; | ||
173 | float sum_ksamples = 0.0; | ||
174 | int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; | ||
175 | |||
176 | /* Sort the active symbols */ | ||
177 | pthread_mutex_lock(&top->active_symbols_lock); | ||
178 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
179 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
180 | |||
181 | top->rb_entries = 0; | ||
182 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
183 | syme->snap_count = syme->count[snap]; | ||
184 | if (syme->snap_count != 0) { | ||
185 | |||
186 | if ((top->hide_user_symbols && | ||
187 | syme->map->dso->kernel == DSO_TYPE_USER) || | ||
188 | (top->hide_kernel_symbols && | ||
189 | syme->map->dso->kernel == DSO_TYPE_KERNEL)) { | ||
190 | perf_top__remove_active_sym(top, syme); | ||
191 | continue; | ||
192 | } | ||
193 | syme->weight = sym_weight(syme, top); | ||
194 | |||
195 | if ((int)syme->snap_count >= top->count_filter) { | ||
196 | rb_insert_active_sym(root, syme); | ||
197 | ++top->rb_entries; | ||
198 | } | ||
199 | sum_ksamples += syme->snap_count; | ||
200 | |||
201 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
202 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
203 | } else | ||
204 | perf_top__remove_active_sym(top, syme); | ||
205 | } | ||
206 | |||
207 | return sum_ksamples; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the longest symbol name that will be displayed | ||
212 | */ | ||
213 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
214 | int *dso_width, int *dso_short_width, int *sym_width) | ||
215 | { | ||
216 | struct rb_node *nd; | ||
217 | int printed = 0; | ||
218 | |||
219 | *sym_width = *dso_width = *dso_short_width = 0; | ||
220 | |||
221 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
222 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
223 | struct symbol *sym = sym_entry__symbol(syme); | ||
224 | |||
225 | if (++printed > top->print_entries || | ||
226 | (int)syme->snap_count < top->count_filter) | ||
227 | continue; | ||
228 | |||
229 | if (syme->map->dso->long_name_len > *dso_width) | ||
230 | *dso_width = syme->map->dso->long_name_len; | ||
231 | |||
232 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
233 | *dso_short_width = syme->map->dso->short_name_len; | ||
234 | |||
235 | if (sym->namelen > *sym_width) | ||
236 | *sym_width = sym->namelen; | ||
237 | } | ||
238 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h new file mode 100644 index 000000000000..bfbf95bcc603 --- /dev/null +++ b/tools/perf/util/top.h | |||
@@ -0,0 +1,64 @@ | |||
1 | #ifndef __PERF_TOP_H | ||
2 | #define __PERF_TOP_H 1 | ||
3 | |||
4 | #include "types.h" | ||
5 | #include "../perf.h" | ||
6 | #include <stddef.h> | ||
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | |||
11 | struct perf_evlist; | ||
12 | struct perf_evsel; | ||
13 | |||
14 | struct sym_entry { | ||
15 | struct rb_node rb_node; | ||
16 | struct list_head node; | ||
17 | unsigned long snap_count; | ||
18 | double weight; | ||
19 | struct map *map; | ||
20 | unsigned long count[0]; | ||
21 | }; | ||
22 | |||
23 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
24 | { | ||
25 | return ((void *)self) + symbol_conf.priv_size; | ||
26 | } | ||
27 | |||
28 | struct perf_top { | ||
29 | struct perf_evlist *evlist; | ||
30 | /* | ||
31 | * Symbols will be added here in perf_event__process_sample and will | ||
32 | * get out after decayed. | ||
33 | */ | ||
34 | struct list_head active_symbols; | ||
35 | pthread_mutex_t active_symbols_lock; | ||
36 | pthread_cond_t active_symbols_cond; | ||
37 | u64 samples; | ||
38 | u64 kernel_samples, us_samples; | ||
39 | u64 exact_samples; | ||
40 | u64 guest_us_samples, guest_kernel_samples; | ||
41 | int print_entries, count_filter, delay_secs; | ||
42 | int display_weighted, freq, rb_entries; | ||
43 | pid_t target_pid, target_tid; | ||
44 | bool hide_kernel_symbols, hide_user_symbols, zero; | ||
45 | const char *cpu_list; | ||
46 | struct sym_entry *sym_filter_entry; | ||
47 | struct perf_evsel *sym_evsel; | ||
48 | }; | ||
49 | |||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | ||
51 | void perf_top__reset_sample_counters(struct perf_top *top); | ||
52 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
53 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
54 | int *dso_width, int *dso_short_width, int *sym_width); | ||
55 | |||
56 | #ifdef NO_NEWT_SUPPORT | ||
57 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | #else | ||
62 | int perf_top__tui_browser(struct perf_top *top); | ||
63 | #endif | ||
64 | #endif /* __PERF_TOP_H */ | ||
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 73a02223c629..0a7ed5b5e281 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -153,7 +153,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
153 | char *next = NULL; | 153 | char *next = NULL; |
154 | char *addr_str; | 154 | char *addr_str; |
155 | char ch; | 155 | char ch; |
156 | int ret; | 156 | int ret __used; |
157 | int i; | 157 | int i; |
158 | 158 | ||
159 | line = strtok_r(file, "\n", &next); | 159 | line = strtok_r(file, "\n", &next); |
@@ -2643,68 +2643,13 @@ static void print_lat_fmt(void *data, int size __unused) | |||
2643 | printf("."); | 2643 | printf("."); |
2644 | 2644 | ||
2645 | if (lock_depth < 0) | 2645 | if (lock_depth < 0) |
2646 | printf("."); | 2646 | printf(". "); |
2647 | else | 2647 | else |
2648 | printf("%d", lock_depth); | 2648 | printf("%d ", lock_depth); |
2649 | } | ||
2650 | |||
2651 | /* taken from Linux, written by Frederic Weisbecker */ | ||
2652 | static void print_graph_cpu(int cpu) | ||
2653 | { | ||
2654 | int i; | ||
2655 | int log10_this = log10_cpu(cpu); | ||
2656 | int log10_all = log10_cpu(cpus); | ||
2657 | |||
2658 | |||
2659 | /* | ||
2660 | * Start with a space character - to make it stand out | ||
2661 | * to the right a bit when trace output is pasted into | ||
2662 | * email: | ||
2663 | */ | ||
2664 | printf(" "); | ||
2665 | |||
2666 | /* | ||
2667 | * Tricky - we space the CPU field according to the max | ||
2668 | * number of online CPUs. On a 2-cpu system it would take | ||
2669 | * a maximum of 1 digit - on a 128 cpu system it would | ||
2670 | * take up to 3 digits: | ||
2671 | */ | ||
2672 | for (i = 0; i < log10_all - log10_this; i++) | ||
2673 | printf(" "); | ||
2674 | |||
2675 | printf("%d) ", cpu); | ||
2676 | } | 2649 | } |
2677 | 2650 | ||
2678 | #define TRACE_GRAPH_PROCINFO_LENGTH 14 | ||
2679 | #define TRACE_GRAPH_INDENT 2 | 2651 | #define TRACE_GRAPH_INDENT 2 |
2680 | 2652 | ||
2681 | static void print_graph_proc(int pid, const char *comm) | ||
2682 | { | ||
2683 | /* sign + log10(MAX_INT) + '\0' */ | ||
2684 | char pid_str[11]; | ||
2685 | int spaces = 0; | ||
2686 | int len; | ||
2687 | int i; | ||
2688 | |||
2689 | sprintf(pid_str, "%d", pid); | ||
2690 | |||
2691 | /* 1 stands for the "-" character */ | ||
2692 | len = strlen(comm) + strlen(pid_str) + 1; | ||
2693 | |||
2694 | if (len < TRACE_GRAPH_PROCINFO_LENGTH) | ||
2695 | spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; | ||
2696 | |||
2697 | /* First spaces to align center */ | ||
2698 | for (i = 0; i < spaces / 2; i++) | ||
2699 | printf(" "); | ||
2700 | |||
2701 | printf("%s-%s", comm, pid_str); | ||
2702 | |||
2703 | /* Last spaces to align center */ | ||
2704 | for (i = 0; i < spaces - (spaces / 2); i++) | ||
2705 | printf(" "); | ||
2706 | } | ||
2707 | |||
2708 | static struct record * | 2653 | static struct record * |
2709 | get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | 2654 | get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, |
2710 | struct record *next) | 2655 | struct record *next) |
@@ -2876,21 +2821,13 @@ static void print_graph_nested(struct event *event, void *data) | |||
2876 | 2821 | ||
2877 | static void | 2822 | static void |
2878 | pretty_print_func_ent(void *data, int size, struct event *event, | 2823 | pretty_print_func_ent(void *data, int size, struct event *event, |
2879 | int cpu, int pid, const char *comm, | 2824 | int cpu, int pid) |
2880 | unsigned long secs, unsigned long usecs) | ||
2881 | { | 2825 | { |
2882 | struct format_field *field; | 2826 | struct format_field *field; |
2883 | struct record *rec; | 2827 | struct record *rec; |
2884 | void *copy_data; | 2828 | void *copy_data; |
2885 | unsigned long val; | 2829 | unsigned long val; |
2886 | 2830 | ||
2887 | printf("%5lu.%06lu | ", secs, usecs); | ||
2888 | |||
2889 | print_graph_cpu(cpu); | ||
2890 | print_graph_proc(pid, comm); | ||
2891 | |||
2892 | printf(" | "); | ||
2893 | |||
2894 | if (latency_format) { | 2831 | if (latency_format) { |
2895 | print_lat_fmt(data, size); | 2832 | print_lat_fmt(data, size); |
2896 | printf(" | "); | 2833 | printf(" | "); |
@@ -2923,22 +2860,13 @@ out_free: | |||
2923 | } | 2860 | } |
2924 | 2861 | ||
2925 | static void | 2862 | static void |
2926 | pretty_print_func_ret(void *data, int size __unused, struct event *event, | 2863 | pretty_print_func_ret(void *data, int size __unused, struct event *event) |
2927 | int cpu, int pid, const char *comm, | ||
2928 | unsigned long secs, unsigned long usecs) | ||
2929 | { | 2864 | { |
2930 | unsigned long long rettime, calltime; | 2865 | unsigned long long rettime, calltime; |
2931 | unsigned long long duration, depth; | 2866 | unsigned long long duration, depth; |
2932 | struct format_field *field; | 2867 | struct format_field *field; |
2933 | int i; | 2868 | int i; |
2934 | 2869 | ||
2935 | printf("%5lu.%06lu | ", secs, usecs); | ||
2936 | |||
2937 | print_graph_cpu(cpu); | ||
2938 | print_graph_proc(pid, comm); | ||
2939 | |||
2940 | printf(" | "); | ||
2941 | |||
2942 | if (latency_format) { | 2870 | if (latency_format) { |
2943 | print_lat_fmt(data, size); | 2871 | print_lat_fmt(data, size); |
2944 | printf(" | "); | 2872 | printf(" | "); |
@@ -2976,31 +2904,21 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, | |||
2976 | 2904 | ||
2977 | static void | 2905 | static void |
2978 | pretty_print_func_graph(void *data, int size, struct event *event, | 2906 | pretty_print_func_graph(void *data, int size, struct event *event, |
2979 | int cpu, int pid, const char *comm, | 2907 | int cpu, int pid) |
2980 | unsigned long secs, unsigned long usecs) | ||
2981 | { | 2908 | { |
2982 | if (event->flags & EVENT_FL_ISFUNCENT) | 2909 | if (event->flags & EVENT_FL_ISFUNCENT) |
2983 | pretty_print_func_ent(data, size, event, | 2910 | pretty_print_func_ent(data, size, event, cpu, pid); |
2984 | cpu, pid, comm, secs, usecs); | ||
2985 | else if (event->flags & EVENT_FL_ISFUNCRET) | 2911 | else if (event->flags & EVENT_FL_ISFUNCRET) |
2986 | pretty_print_func_ret(data, size, event, | 2912 | pretty_print_func_ret(data, size, event); |
2987 | cpu, pid, comm, secs, usecs); | ||
2988 | printf("\n"); | 2913 | printf("\n"); |
2989 | } | 2914 | } |
2990 | 2915 | ||
2991 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 2916 | void print_trace_event(int cpu, void *data, int size) |
2992 | char *comm) | ||
2993 | { | 2917 | { |
2994 | struct event *event; | 2918 | struct event *event; |
2995 | unsigned long secs; | ||
2996 | unsigned long usecs; | ||
2997 | int type; | 2919 | int type; |
2998 | int pid; | 2920 | int pid; |
2999 | 2921 | ||
3000 | secs = nsecs / NSECS_PER_SEC; | ||
3001 | nsecs -= secs * NSECS_PER_SEC; | ||
3002 | usecs = nsecs / NSECS_PER_USEC; | ||
3003 | |||
3004 | type = trace_parse_common_type(data); | 2922 | type = trace_parse_common_type(data); |
3005 | 2923 | ||
3006 | event = trace_find_event(type); | 2924 | event = trace_find_event(type); |
@@ -3012,17 +2930,10 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
3012 | pid = trace_parse_common_pid(data); | 2930 | pid = trace_parse_common_pid(data); |
3013 | 2931 | ||
3014 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | 2932 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) |
3015 | return pretty_print_func_graph(data, size, event, cpu, | 2933 | return pretty_print_func_graph(data, size, event, cpu, pid); |
3016 | pid, comm, secs, usecs); | ||
3017 | 2934 | ||
3018 | if (latency_format) { | 2935 | if (latency_format) |
3019 | printf("%8.8s-%-5d %3d", | ||
3020 | comm, pid, cpu); | ||
3021 | print_lat_fmt(data, size); | 2936 | print_lat_fmt(data, size); |
3022 | } else | ||
3023 | printf("%16s-%-5d [%03d]", comm, pid, cpu); | ||
3024 | |||
3025 | printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); | ||
3026 | 2937 | ||
3027 | if (event->flags & EVENT_FL_FAILED) { | 2938 | if (event->flags & EVENT_FL_FAILED) { |
3028 | printf("EVENT '%s' FAILED TO PARSE\n", | 2939 | printf("EVENT '%s' FAILED TO PARSE\n", |
@@ -3031,7 +2942,6 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
3031 | } | 2942 | } |
3032 | 2943 | ||
3033 | pretty_print(data, size, event); | 2944 | pretty_print(data, size, event); |
3034 | printf("\n"); | ||
3035 | } | 2945 | } |
3036 | 2946 | ||
3037 | static void print_fields(struct print_flag_sym *field) | 2947 | static void print_fields(struct print_flag_sym *field) |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index f7af2fca965d..66f4b78737ab 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -36,11 +36,10 @@ static int stop_script_unsupported(void) | |||
36 | return 0; | 36 | return 0; |
37 | } | 37 | } |
38 | 38 | ||
39 | static void process_event_unsupported(int cpu __unused, | 39 | static void process_event_unsupported(union perf_event *event __unused, |
40 | void *data __unused, | 40 | struct perf_sample *sample __unused, |
41 | int size __unused, | 41 | struct perf_session *session __unused, |
42 | unsigned long long nsecs __unused, | 42 | struct thread *thread __unused) |
43 | char *comm __unused) | ||
44 | { | 43 | { |
45 | } | 44 | } |
46 | 45 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b5f12ca24d99..b04da5722437 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
6 | #include "session.h" | ||
6 | 7 | ||
7 | #define __unused __attribute__((unused)) | 8 | #define __unused __attribute__((unused)) |
8 | 9 | ||
@@ -176,8 +177,7 @@ void print_printk(void); | |||
176 | 177 | ||
177 | int parse_ftrace_file(char *buf, unsigned long size); | 178 | int parse_ftrace_file(char *buf, unsigned long size); |
178 | int parse_event_file(char *buf, unsigned long size, char *sys); | 179 | int parse_event_file(char *buf, unsigned long size, char *sys); |
179 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 180 | void print_trace_event(int cpu, void *data, int size); |
180 | char *comm); | ||
181 | 181 | ||
182 | extern int file_bigendian; | 182 | extern int file_bigendian; |
183 | extern int host_bigendian; | 183 | extern int host_bigendian; |
@@ -278,8 +278,10 @@ struct scripting_ops { | |||
278 | const char *name; | 278 | const char *name; |
279 | int (*start_script) (const char *script, int argc, const char **argv); | 279 | int (*start_script) (const char *script, int argc, const char **argv); |
280 | int (*stop_script) (void); | 280 | int (*stop_script) (void); |
281 | void (*process_event) (int cpu, void *data, int size, | 281 | void (*process_event) (union perf_event *event, |
282 | unsigned long long nsecs, char *comm); | 282 | struct perf_sample *sample, |
283 | struct perf_session *session, | ||
284 | struct thread *thread); | ||
283 | int (*generate_script) (const char *outfile); | 285 | int (*generate_script) (const char *outfile); |
284 | }; | 286 | }; |
285 | 287 | ||
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 8bc010edca25..611219f80680 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "libslang.h" | 1 | #include "libslang.h" |
2 | #include "ui.h" | ||
2 | #include <linux/compiler.h> | 3 | #include <linux/compiler.h> |
3 | #include <linux/list.h> | 4 | #include <linux/list.h> |
4 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
@@ -156,6 +157,20 @@ void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | |||
156 | } | 157 | } |
157 | } | 158 | } |
158 | 159 | ||
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
161 | { | ||
162 | SLsmg_gotorc(0, 0); | ||
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | ||
164 | slsmg_write_nstring(title, browser->width); | ||
165 | } | ||
166 | |||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
168 | { | ||
169 | pthread_mutex_lock(&ui__lock); | ||
170 | __ui_browser__show_title(browser, title); | ||
171 | pthread_mutex_unlock(&ui__lock); | ||
172 | } | ||
173 | |||
159 | int ui_browser__show(struct ui_browser *self, const char *title, | 174 | int ui_browser__show(struct ui_browser *self, const char *title, |
160 | const char *helpline, ...) | 175 | const char *helpline, ...) |
161 | { | 176 | { |
@@ -178,9 +193,8 @@ int ui_browser__show(struct ui_browser *self, const char *title, | |||
178 | if (self->sb == NULL) | 193 | if (self->sb == NULL) |
179 | return -1; | 194 | return -1; |
180 | 195 | ||
181 | SLsmg_gotorc(0, 0); | 196 | pthread_mutex_lock(&ui__lock); |
182 | ui_browser__set_color(self, NEWT_COLORSET_ROOT); | 197 | __ui_browser__show_title(self, title); |
183 | slsmg_write_nstring(title, self->width); | ||
184 | 198 | ||
185 | ui_browser__add_exit_keys(self, keys); | 199 | ui_browser__add_exit_keys(self, keys); |
186 | newtFormAddComponent(self->form, self->sb); | 200 | newtFormAddComponent(self->form, self->sb); |
@@ -188,25 +202,30 @@ int ui_browser__show(struct ui_browser *self, const char *title, | |||
188 | va_start(ap, helpline); | 202 | va_start(ap, helpline); |
189 | ui_helpline__vpush(helpline, ap); | 203 | ui_helpline__vpush(helpline, ap); |
190 | va_end(ap); | 204 | va_end(ap); |
205 | pthread_mutex_unlock(&ui__lock); | ||
191 | return 0; | 206 | return 0; |
192 | } | 207 | } |
193 | 208 | ||
194 | void ui_browser__hide(struct ui_browser *self) | 209 | void ui_browser__hide(struct ui_browser *self) |
195 | { | 210 | { |
211 | pthread_mutex_lock(&ui__lock); | ||
196 | newtFormDestroy(self->form); | 212 | newtFormDestroy(self->form); |
197 | self->form = NULL; | 213 | self->form = NULL; |
198 | ui_helpline__pop(); | 214 | ui_helpline__pop(); |
215 | pthread_mutex_unlock(&ui__lock); | ||
199 | } | 216 | } |
200 | 217 | ||
201 | int ui_browser__refresh(struct ui_browser *self) | 218 | int ui_browser__refresh(struct ui_browser *self) |
202 | { | 219 | { |
203 | int row; | 220 | int row; |
204 | 221 | ||
222 | pthread_mutex_lock(&ui__lock); | ||
205 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); |
206 | row = self->refresh(self); | 224 | row = self->refresh(self); |
207 | ui_browser__set_color(self, HE_COLORSET_NORMAL); | 225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); |
208 | SLsmg_fill_region(self->y + row, self->x, | 226 | SLsmg_fill_region(self->y + row, self->x, |
209 | self->height - row, self->width, ' '); | 227 | self->height - row, self->width, ' '); |
228 | pthread_mutex_unlock(&ui__lock); | ||
210 | 229 | ||
211 | return 0; | 230 | return 0; |
212 | } | 231 | } |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 0dc7e4da36f5..fc63dda10910 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -24,7 +24,6 @@ struct ui_browser { | |||
24 | u32 nr_entries; | 24 | u32 nr_entries; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | |||
28 | void ui_browser__set_color(struct ui_browser *self, int color); | 27 | void ui_browser__set_color(struct ui_browser *self, int color); |
29 | void ui_browser__set_percent_color(struct ui_browser *self, | 28 | void ui_browser__set_percent_color(struct ui_browser *self, |
30 | double percent, bool current); | 29 | double percent, bool current); |
@@ -35,6 +34,8 @@ void ui_browser__reset_index(struct ui_browser *self); | |||
35 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | 34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); |
36 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | 35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); |
37 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | 36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); |
37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
38 | int ui_browser__show(struct ui_browser *self, const char *title, | 39 | int ui_browser__show(struct ui_browser *self, const char *title, |
39 | const char *helpline, ...); | 40 | const char *helpline, ...); |
40 | void ui_browser__hide(struct ui_browser *self); | 41 | void ui_browser__hide(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 82b78f99251b..8c17a8730e4a 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -1,9 +1,12 @@ | |||
1 | #include "../browser.h" | 1 | #include "../browser.h" |
2 | #include "../helpline.h" | 2 | #include "../helpline.h" |
3 | #include "../libslang.h" | 3 | #include "../libslang.h" |
4 | #include "../../annotate.h" | ||
4 | #include "../../hist.h" | 5 | #include "../../hist.h" |
5 | #include "../../sort.h" | 6 | #include "../../sort.h" |
6 | #include "../../symbol.h" | 7 | #include "../../symbol.h" |
8 | #include "../../annotate.h" | ||
9 | #include <pthread.h> | ||
7 | 10 | ||
8 | static void ui__error_window(const char *fmt, ...) | 11 | static void ui__error_window(const char *fmt, ...) |
9 | { | 12 | { |
@@ -42,8 +45,6 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
42 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); | 45 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); |
43 | ui_browser__set_percent_color(self, olrb->percent, current_entry); | 46 | ui_browser__set_percent_color(self, olrb->percent, current_entry); |
44 | slsmg_printf(" %7.2f ", olrb->percent); | 47 | slsmg_printf(" %7.2f ", olrb->percent); |
45 | if (!current_entry) | ||
46 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
47 | } else { | 48 | } else { |
48 | ui_browser__set_percent_color(self, 0, current_entry); | 49 | ui_browser__set_percent_color(self, 0, current_entry); |
49 | slsmg_write_nstring(" ", 9); | 50 | slsmg_write_nstring(" ", 9); |
@@ -55,35 +56,40 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
55 | slsmg_write_nstring(" ", width - 18); | 56 | slsmg_write_nstring(" ", width - 18); |
56 | else | 57 | else |
57 | slsmg_write_nstring(ol->line, width - 18); | 58 | slsmg_write_nstring(ol->line, width - 18); |
59 | |||
60 | if (!current_entry) | ||
61 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
58 | } | 62 | } |
59 | 63 | ||
60 | static double objdump_line__calc_percent(struct objdump_line *self, | 64 | static double objdump_line__calc_percent(struct objdump_line *self, |
61 | struct list_head *head, | 65 | struct symbol *sym, int evidx) |
62 | struct symbol *sym) | ||
63 | { | 66 | { |
64 | double percent = 0.0; | 67 | double percent = 0.0; |
65 | 68 | ||
66 | if (self->offset != -1) { | 69 | if (self->offset != -1) { |
67 | int len = sym->end - sym->start; | 70 | int len = sym->end - sym->start; |
68 | unsigned int hits = 0; | 71 | unsigned int hits = 0; |
69 | struct sym_priv *priv = symbol__priv(sym); | 72 | struct annotation *notes = symbol__annotation(sym); |
70 | struct sym_ext *sym_ext = priv->ext; | 73 | struct source_line *src_line = notes->src->lines; |
71 | struct sym_hist *h = priv->hist; | 74 | struct sym_hist *h = annotation__histogram(notes, evidx); |
72 | s64 offset = self->offset; | 75 | s64 offset = self->offset; |
73 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | 76 | struct objdump_line *next; |
74 | |||
75 | 77 | ||
78 | next = objdump__get_next_ip_line(¬es->src->source, self); | ||
76 | while (offset < (s64)len && | 79 | while (offset < (s64)len && |
77 | (next == NULL || offset < next->offset)) { | 80 | (next == NULL || offset < next->offset)) { |
78 | if (sym_ext) { | 81 | if (src_line) { |
79 | percent += sym_ext[offset].percent; | 82 | percent += src_line[offset].percent; |
80 | } else | 83 | } else |
81 | hits += h->ip[offset]; | 84 | hits += h->addr[offset]; |
82 | 85 | ||
83 | ++offset; | 86 | ++offset; |
84 | } | 87 | } |
85 | 88 | /* | |
86 | if (sym_ext == NULL && h->sum) | 89 | * If the percentage wasn't already calculated in |
90 | * symbol__get_source_line, do it now: | ||
91 | */ | ||
92 | if (src_line == NULL && h->sum) | ||
87 | percent = 100.0 * hits / h->sum; | 93 | percent = 100.0 * hits / h->sum; |
88 | } | 94 | } |
89 | 95 | ||
@@ -133,103 +139,161 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
133 | self->curr_hot = nd; | 139 | self->curr_hot = nd; |
134 | } | 140 | } |
135 | 141 | ||
136 | static int annotate_browser__run(struct annotate_browser *self) | 142 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
143 | int evidx) | ||
137 | { | 144 | { |
138 | struct rb_node *nd; | 145 | struct symbol *sym = browser->b.priv; |
139 | struct hist_entry *he = self->b.priv; | 146 | struct annotation *notes = symbol__annotation(sym); |
140 | int key; | 147 | struct objdump_line *pos; |
141 | 148 | ||
142 | if (ui_browser__show(&self->b, he->ms.sym->name, | 149 | browser->entries = RB_ROOT; |
143 | "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) | 150 | |
144 | return -1; | 151 | pthread_mutex_lock(¬es->lock); |
152 | |||
153 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
154 | struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); | ||
155 | rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); | ||
156 | if (rbpos->percent < 0.01) { | ||
157 | RB_CLEAR_NODE(&rbpos->rb_node); | ||
158 | continue; | ||
159 | } | ||
160 | objdump__insert_line(&browser->entries, rbpos); | ||
161 | } | ||
162 | pthread_mutex_unlock(¬es->lock); | ||
163 | |||
164 | browser->curr_hot = rb_last(&browser->entries); | ||
165 | } | ||
166 | |||
167 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | ||
168 | int refresh) | ||
169 | { | ||
170 | struct rb_node *nd = NULL; | ||
171 | struct symbol *sym = self->b.priv; | ||
145 | /* | 172 | /* |
146 | * To allow builtin-annotate to cycle thru multiple symbols by | 173 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by |
147 | * examining the exit key for this function. | 174 | * examining the exit key for this function. |
148 | */ | 175 | */ |
149 | ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT); | 176 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, |
177 | NEWT_KEY_RIGHT, 0 }; | ||
178 | int key; | ||
179 | |||
180 | if (ui_browser__show(&self->b, sym->name, | ||
181 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
182 | "cycle hottest lines, H: Hottest") < 0) | ||
183 | return -1; | ||
184 | |||
185 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
186 | annotate_browser__calc_percent(self, evidx); | ||
187 | |||
188 | if (self->curr_hot) | ||
189 | annotate_browser__set_top(self, self->curr_hot); | ||
150 | 190 | ||
151 | nd = self->curr_hot; | 191 | nd = self->curr_hot; |
152 | if (nd) { | 192 | |
153 | int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 }; | 193 | if (refresh != 0) |
154 | ui_browser__add_exit_keys(&self->b, tabs); | 194 | newtFormSetTimer(self->b.form, refresh); |
155 | } | ||
156 | 195 | ||
157 | while (1) { | 196 | while (1) { |
158 | key = ui_browser__run(&self->b); | 197 | key = ui_browser__run(&self->b); |
159 | 198 | ||
199 | if (refresh != 0) { | ||
200 | annotate_browser__calc_percent(self, evidx); | ||
201 | /* | ||
202 | * Current line focus got out of the list of most active | ||
203 | * lines, NULL it so that if TAB|UNTAB is pressed, we | ||
204 | * move to curr_hot (current hottest line). | ||
205 | */ | ||
206 | if (nd != NULL && RB_EMPTY_NODE(nd)) | ||
207 | nd = NULL; | ||
208 | } | ||
209 | |||
160 | switch (key) { | 210 | switch (key) { |
211 | case -1: | ||
212 | /* | ||
213 | * FIXME we need to check if it was | ||
214 | * es.reason == NEWT_EXIT_TIMER | ||
215 | */ | ||
216 | if (refresh != 0) | ||
217 | symbol__annotate_decay_histogram(sym, evidx); | ||
218 | continue; | ||
161 | case NEWT_KEY_TAB: | 219 | case NEWT_KEY_TAB: |
162 | nd = rb_prev(nd); | 220 | if (nd != NULL) { |
163 | if (nd == NULL) | 221 | nd = rb_prev(nd); |
164 | nd = rb_last(&self->entries); | 222 | if (nd == NULL) |
165 | annotate_browser__set_top(self, nd); | 223 | nd = rb_last(&self->entries); |
224 | } else | ||
225 | nd = self->curr_hot; | ||
166 | break; | 226 | break; |
167 | case NEWT_KEY_UNTAB: | 227 | case NEWT_KEY_UNTAB: |
168 | nd = rb_next(nd); | 228 | if (nd != NULL) |
169 | if (nd == NULL) | 229 | nd = rb_next(nd); |
170 | nd = rb_first(&self->entries); | 230 | if (nd == NULL) |
171 | annotate_browser__set_top(self, nd); | 231 | nd = rb_first(&self->entries); |
232 | else | ||
233 | nd = self->curr_hot; | ||
234 | break; | ||
235 | case 'H': | ||
236 | nd = self->curr_hot; | ||
172 | break; | 237 | break; |
173 | default: | 238 | default: |
174 | goto out; | 239 | goto out; |
175 | } | 240 | } |
241 | |||
242 | if (nd != NULL) | ||
243 | annotate_browser__set_top(self, nd); | ||
176 | } | 244 | } |
177 | out: | 245 | out: |
178 | ui_browser__hide(&self->b); | 246 | ui_browser__hide(&self->b); |
179 | return key; | 247 | return key; |
180 | } | 248 | } |
181 | 249 | ||
182 | int hist_entry__tui_annotate(struct hist_entry *self) | 250 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) |
251 | { | ||
252 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | ||
253 | } | ||
254 | |||
255 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
256 | int refresh) | ||
183 | { | 257 | { |
184 | struct objdump_line *pos, *n; | 258 | struct objdump_line *pos, *n; |
185 | struct objdump_line_rb_node *rbpos; | 259 | struct annotation *notes = symbol__annotation(sym); |
186 | LIST_HEAD(head); | ||
187 | struct annotate_browser browser = { | 260 | struct annotate_browser browser = { |
188 | .b = { | 261 | .b = { |
189 | .entries = &head, | 262 | .entries = ¬es->src->source, |
190 | .refresh = ui_browser__list_head_refresh, | 263 | .refresh = ui_browser__list_head_refresh, |
191 | .seek = ui_browser__list_head_seek, | 264 | .seek = ui_browser__list_head_seek, |
192 | .write = annotate_browser__write, | 265 | .write = annotate_browser__write, |
193 | .priv = self, | 266 | .priv = sym, |
194 | }, | 267 | }, |
195 | }; | 268 | }; |
196 | int ret; | 269 | int ret; |
197 | 270 | ||
198 | if (self->ms.sym == NULL) | 271 | if (sym == NULL) |
199 | return -1; | 272 | return -1; |
200 | 273 | ||
201 | if (self->ms.map->dso->annotate_warned) | 274 | if (map->dso->annotate_warned) |
202 | return -1; | 275 | return -1; |
203 | 276 | ||
204 | if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { | 277 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
205 | ui__error_window(ui_helpline__last_msg); | 278 | ui__error_window(ui_helpline__last_msg); |
206 | return -1; | 279 | return -1; |
207 | } | 280 | } |
208 | 281 | ||
209 | ui_helpline__push("Press <- or ESC to exit"); | 282 | ui_helpline__push("Press <- or ESC to exit"); |
210 | 283 | ||
211 | list_for_each_entry(pos, &head, node) { | 284 | list_for_each_entry(pos, ¬es->src->source, node) { |
285 | struct objdump_line_rb_node *rbpos; | ||
212 | size_t line_len = strlen(pos->line); | 286 | size_t line_len = strlen(pos->line); |
287 | |||
213 | if (browser.b.width < line_len) | 288 | if (browser.b.width < line_len) |
214 | browser.b.width = line_len; | 289 | browser.b.width = line_len; |
215 | rbpos = objdump_line__rb(pos); | 290 | rbpos = objdump_line__rb(pos); |
216 | rbpos->idx = browser.b.nr_entries++; | 291 | rbpos->idx = browser.b.nr_entries++; |
217 | rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); | ||
218 | if (rbpos->percent < 0.01) | ||
219 | continue; | ||
220 | objdump__insert_line(&browser.entries, rbpos); | ||
221 | } | 292 | } |
222 | 293 | ||
223 | /* | ||
224 | * Position the browser at the hottest line. | ||
225 | */ | ||
226 | browser.curr_hot = rb_last(&browser.entries); | ||
227 | if (browser.curr_hot) | ||
228 | annotate_browser__set_top(&browser, browser.curr_hot); | ||
229 | |||
230 | browser.b.width += 18; /* Percentage */ | 294 | browser.b.width += 18; /* Percentage */ |
231 | ret = annotate_browser__run(&browser); | 295 | ret = annotate_browser__run(&browser, evidx, refresh); |
232 | list_for_each_entry_safe(pos, n, &head, node) { | 296 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
233 | list_del(&pos->node); | 297 | list_del(&pos->node); |
234 | objdump_line__free(pos); | 298 | objdump_line__free(pos); |
235 | } | 299 | } |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 60c463c16028..798efdca3ead 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <newt.h> | 7 | #include <newt.h> |
8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
9 | 9 | ||
10 | #include "../../evsel.h" | ||
11 | #include "../../evlist.h" | ||
10 | #include "../../hist.h" | 12 | #include "../../hist.h" |
11 | #include "../../pstack.h" | 13 | #include "../../pstack.h" |
12 | #include "../../sort.h" | 14 | #include "../../sort.h" |
@@ -292,7 +294,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
292 | { | 294 | { |
293 | int key; | 295 | int key; |
294 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', |
295 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, }; | 297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, |
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
296 | 299 | ||
297 | self->b.entries = &self->hists->entries; | 300 | self->b.entries = &self->hists->entries; |
298 | self->b.nr_entries = self->hists->nr_entries; | 301 | self->b.nr_entries = self->hists->nr_entries; |
@@ -377,7 +380,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
377 | while (node) { | 380 | while (node) { |
378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 381 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
379 | struct rb_node *next = rb_next(node); | 382 | struct rb_node *next = rb_next(node); |
380 | u64 cumul = cumul_hits(child); | 383 | u64 cumul = callchain_cumul_hits(child); |
381 | struct callchain_list *chain; | 384 | struct callchain_list *chain; |
382 | char folded_sign = ' '; | 385 | char folded_sign = ' '; |
383 | int first = true; | 386 | int first = true; |
@@ -638,6 +641,9 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
638 | struct rb_node *nd; | 641 | struct rb_node *nd; |
639 | bool first = true; | 642 | bool first = true; |
640 | 643 | ||
644 | if (self->nr_entries == 0) | ||
645 | return; | ||
646 | |||
641 | switch (whence) { | 647 | switch (whence) { |
642 | case SEEK_SET: | 648 | case SEEK_SET: |
643 | nd = hists__filter_entries(rb_first(self->entries)); | 649 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -797,8 +803,11 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
797 | return printed; | 803 | return printed; |
798 | } | 804 | } |
799 | 805 | ||
800 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | 806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, |
807 | const char *helpline, const char *ev_name, | ||
808 | bool left_exits) | ||
801 | { | 809 | { |
810 | struct hists *self = &evsel->hists; | ||
802 | struct hist_browser *browser = hist_browser__new(self); | 811 | struct hist_browser *browser = hist_browser__new(self); |
803 | struct pstack *fstack; | 812 | struct pstack *fstack; |
804 | const struct thread *thread_filter = NULL; | 813 | const struct thread *thread_filter = NULL; |
@@ -818,8 +827,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
818 | hists__browser_title(self, msg, sizeof(msg), ev_name, | 827 | hists__browser_title(self, msg, sizeof(msg), ev_name, |
819 | dso_filter, thread_filter); | 828 | dso_filter, thread_filter); |
820 | while (1) { | 829 | while (1) { |
821 | const struct thread *thread; | 830 | const struct thread *thread = NULL; |
822 | const struct dso *dso; | 831 | const struct dso *dso = NULL; |
823 | char *options[16]; | 832 | char *options[16]; |
824 | int nr_options = 0, choice = 0, i, | 833 | int nr_options = 0, choice = 0, i, |
825 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
@@ -827,8 +836,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
827 | 836 | ||
828 | key = hist_browser__run(browser, msg); | 837 | key = hist_browser__run(browser, msg); |
829 | 838 | ||
830 | thread = hist_browser__selected_thread(browser); | 839 | if (browser->he_selection != NULL) { |
831 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 840 | thread = hist_browser__selected_thread(browser); |
841 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | ||
842 | } | ||
832 | 843 | ||
833 | switch (key) { | 844 | switch (key) { |
834 | case NEWT_KEY_TAB: | 845 | case NEWT_KEY_TAB: |
@@ -839,7 +850,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
839 | */ | 850 | */ |
840 | goto out_free_stack; | 851 | goto out_free_stack; |
841 | case 'a': | 852 | case 'a': |
842 | if (browser->selection->map == NULL && | 853 | if (browser->selection == NULL || |
854 | browser->selection->map == NULL || | ||
843 | browser->selection->map->dso->annotate_warned) | 855 | browser->selection->map->dso->annotate_warned) |
844 | continue; | 856 | continue; |
845 | goto do_annotate; | 857 | goto do_annotate; |
@@ -858,6 +870,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
858 | "E Expand all callchains\n" | 870 | "E Expand all callchains\n" |
859 | "d Zoom into current DSO\n" | 871 | "d Zoom into current DSO\n" |
860 | "t Zoom into current Thread\n" | 872 | "t Zoom into current Thread\n" |
873 | "TAB/UNTAB Switch events\n" | ||
861 | "q/CTRL+C Exit browser"); | 874 | "q/CTRL+C Exit browser"); |
862 | continue; | 875 | continue; |
863 | case NEWT_KEY_ENTER: | 876 | case NEWT_KEY_ENTER: |
@@ -867,8 +880,14 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
867 | case NEWT_KEY_LEFT: { | 880 | case NEWT_KEY_LEFT: { |
868 | const void *top; | 881 | const void *top; |
869 | 882 | ||
870 | if (pstack__empty(fstack)) | 883 | if (pstack__empty(fstack)) { |
884 | /* | ||
885 | * Go back to the perf_evsel_menu__run or other user | ||
886 | */ | ||
887 | if (left_exits) | ||
888 | goto out_free_stack; | ||
871 | continue; | 889 | continue; |
890 | } | ||
872 | top = pstack__pop(fstack); | 891 | top = pstack__pop(fstack); |
873 | if (top == &dso_filter) | 892 | if (top == &dso_filter) |
874 | goto zoom_out_dso; | 893 | goto zoom_out_dso; |
@@ -877,14 +896,16 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
877 | continue; | 896 | continue; |
878 | } | 897 | } |
879 | case NEWT_KEY_ESCAPE: | 898 | case NEWT_KEY_ESCAPE: |
880 | if (!ui__dialog_yesno("Do you really want to exit?")) | 899 | if (!left_exits && |
900 | !ui__dialog_yesno("Do you really want to exit?")) | ||
881 | continue; | 901 | continue; |
882 | /* Fall thru */ | 902 | /* Fall thru */ |
883 | default: | 903 | default: |
884 | goto out_free_stack; | 904 | goto out_free_stack; |
885 | } | 905 | } |
886 | 906 | ||
887 | if (browser->selection->sym != NULL && | 907 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | ||
888 | !browser->selection->map->dso->annotate_warned && | 909 | !browser->selection->map->dso->annotate_warned && |
889 | asprintf(&options[nr_options], "Annotate %s", | 910 | asprintf(&options[nr_options], "Annotate %s", |
890 | browser->selection->sym->name) > 0) | 911 | browser->selection->sym->name) > 0) |
@@ -903,7 +924,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
903 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
904 | zoom_dso = nr_options++; | 925 | zoom_dso = nr_options++; |
905 | 926 | ||
906 | if (browser->selection->map != NULL && | 927 | if (browser->selection != NULL && |
928 | browser->selection->map != NULL && | ||
907 | asprintf(&options[nr_options], "Browse map details") > 0) | 929 | asprintf(&options[nr_options], "Browse map details") > 0) |
908 | browse_map = nr_options++; | 930 | browse_map = nr_options++; |
909 | 931 | ||
@@ -923,19 +945,11 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
923 | if (choice == annotate) { | 945 | if (choice == annotate) { |
924 | struct hist_entry *he; | 946 | struct hist_entry *he; |
925 | do_annotate: | 947 | do_annotate: |
926 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { | ||
927 | browser->selection->map->dso->annotate_warned = 1; | ||
928 | ui_helpline__puts("No vmlinux file found, can't " | ||
929 | "annotate with just a " | ||
930 | "kallsyms file"); | ||
931 | continue; | ||
932 | } | ||
933 | |||
934 | he = hist_browser__selected_entry(browser); | 948 | he = hist_browser__selected_entry(browser); |
935 | if (he == NULL) | 949 | if (he == NULL) |
936 | continue; | 950 | continue; |
937 | 951 | ||
938 | hist_entry__tui_annotate(he); | 952 | hist_entry__tui_annotate(he, evsel->idx); |
939 | } else if (choice == browse_map) | 953 | } else if (choice == browse_map) |
940 | map__browse(browser->selection->map); | 954 | map__browse(browser->selection->map); |
941 | else if (choice == zoom_dso) { | 955 | else if (choice == zoom_dso) { |
@@ -984,30 +998,141 @@ out: | |||
984 | return key; | 998 | return key; |
985 | } | 999 | } |
986 | 1000 | ||
987 | int hists__tui_browse_tree(struct rb_root *self, const char *help) | 1001 | struct perf_evsel_menu { |
1002 | struct ui_browser b; | ||
1003 | struct perf_evsel *selection; | ||
1004 | }; | ||
1005 | |||
1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | ||
1007 | void *entry, int row) | ||
1008 | { | ||
1009 | struct perf_evsel_menu *menu = container_of(browser, | ||
1010 | struct perf_evsel_menu, b); | ||
1011 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1012 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1014 | const char *ev_name = event_name(evsel); | ||
1015 | char bf[256], unit; | ||
1016 | |||
1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | ||
1018 | HE_COLORSET_NORMAL); | ||
1019 | |||
1020 | nr_events = convert_unit(nr_events, &unit); | ||
1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | ||
1022 | unit, unit == ' ' ? "" : " ", ev_name); | ||
1023 | slsmg_write_nstring(bf, browser->width); | ||
1024 | |||
1025 | if (current_entry) | ||
1026 | menu->selection = evsel; | ||
1027 | } | ||
1028 | |||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | ||
988 | { | 1030 | { |
989 | struct rb_node *first = rb_first(self), *nd = first, *next; | 1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; |
990 | int key = 0; | 1032 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | ||
1034 | const char *ev_name, *title = "Available samples"; | ||
1035 | int key; | ||
1036 | |||
1037 | if (ui_browser__show(&menu->b, title, | ||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | ||
1039 | return -1; | ||
1040 | |||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
991 | 1042 | ||
992 | while (nd) { | 1043 | while (1) { |
993 | struct hists *hists = rb_entry(nd, struct hists, rb_node); | 1044 | key = ui_browser__run(&menu->b); |
994 | const char *ev_name = __event_name(hists->type, hists->config); | ||
995 | 1045 | ||
996 | key = hists__browse(hists, help, ev_name); | ||
997 | switch (key) { | 1046 | switch (key) { |
998 | case NEWT_KEY_TAB: | 1047 | case NEWT_KEY_RIGHT: |
999 | next = rb_next(nd); | 1048 | case NEWT_KEY_ENTER: |
1000 | if (next) | 1049 | if (!menu->selection) |
1001 | nd = next; | 1050 | continue; |
1051 | pos = menu->selection; | ||
1052 | browse_hists: | ||
1053 | ev_name = event_name(pos); | ||
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | ||
1055 | ui_browser__show_title(&menu->b, title); | ||
1002 | break; | 1056 | break; |
1003 | case NEWT_KEY_UNTAB: | 1057 | case NEWT_KEY_LEFT: |
1004 | if (nd == first) | 1058 | continue; |
1059 | case NEWT_KEY_ESCAPE: | ||
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
1005 | continue; | 1061 | continue; |
1006 | nd = rb_prev(nd); | 1062 | /* Fall thru */ |
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
1067 | switch (key) { | ||
1068 | case NEWT_KEY_TAB: | ||
1069 | if (pos->node.next == &evlist->entries) | ||
1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1071 | else | ||
1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1073 | goto browse_hists; | ||
1074 | case NEWT_KEY_UNTAB: | ||
1075 | if (pos->node.prev == &evlist->entries) | ||
1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1077 | else | ||
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1079 | goto browse_hists; | ||
1080 | case 'q': | ||
1081 | case CTRL('c'): | ||
1082 | goto out; | ||
1007 | default: | 1083 | default: |
1008 | return key; | 1084 | break; |
1009 | } | 1085 | } |
1010 | } | 1086 | } |
1011 | 1087 | ||
1088 | out: | ||
1089 | ui_browser__hide(&menu->b); | ||
1012 | return key; | 1090 | return key; |
1013 | } | 1091 | } |
1092 | |||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | ||
1094 | const char *help) | ||
1095 | { | ||
1096 | struct perf_evsel *pos; | ||
1097 | struct perf_evsel_menu menu = { | ||
1098 | .b = { | ||
1099 | .entries = &evlist->entries, | ||
1100 | .refresh = ui_browser__list_head_refresh, | ||
1101 | .seek = ui_browser__list_head_seek, | ||
1102 | .write = perf_evsel_menu__write, | ||
1103 | .nr_entries = evlist->nr_entries, | ||
1104 | .priv = evlist, | ||
1105 | }, | ||
1106 | }; | ||
1107 | |||
1108 | ui_helpline__push("Press ESC to exit"); | ||
1109 | |||
1110 | list_for_each_entry(pos, &evlist->entries, node) { | ||
1111 | const char *ev_name = event_name(pos); | ||
1112 | size_t line_len = strlen(ev_name) + 7; | ||
1113 | |||
1114 | if (menu.b.width < line_len) | ||
1115 | menu.b.width = line_len; | ||
1116 | /* | ||
1117 | * Cache the evsel name, tracepoints have a _high_ cost per | ||
1118 | * event_name() call. | ||
1119 | */ | ||
1120 | if (pos->name == NULL) | ||
1121 | pos->name = strdup(ev_name); | ||
1122 | } | ||
1123 | |||
1124 | return perf_evsel_menu__run(&menu, help); | ||
1125 | } | ||
1126 | |||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | ||
1128 | { | ||
1129 | |||
1130 | if (evlist->nr_entries == 1) { | ||
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | ||
1132 | struct perf_evsel, node); | ||
1133 | const char *ev_name = event_name(first); | ||
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | ||
1135 | } | ||
1136 | |||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | ||
1138 | } | ||
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index e5158369106e..8462bffe20bc 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -41,7 +41,7 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) | |||
41 | out_free_form: | 41 | out_free_form: |
42 | newtPopWindow(); | 42 | newtPopWindow(); |
43 | newtFormDestroy(form); | 43 | newtFormDestroy(form); |
44 | return 0; | 44 | return err; |
45 | } | 45 | } |
46 | 46 | ||
47 | struct map_browser { | 47 | struct map_browser { |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c new file mode 100644 index 000000000000..5a06538532af --- /dev/null +++ b/tools/perf/util/ui/browsers/top.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../../evlist.h" | ||
15 | #include "../../hist.h" | ||
16 | #include "../../sort.h" | ||
17 | #include "../../symbol.h" | ||
18 | #include "../../top.h" | ||
19 | |||
20 | struct perf_top_browser { | ||
21 | struct ui_browser b; | ||
22 | struct rb_root root; | ||
23 | struct sym_entry *selection; | ||
24 | float sum_ksamples; | ||
25 | int dso_width; | ||
26 | int dso_short_width; | ||
27 | int sym_width; | ||
28 | }; | ||
29 | |||
30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
31 | { | ||
32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
36 | struct perf_top *top = browser->priv; | ||
37 | int width = browser->width; | ||
38 | double pcnt; | ||
39 | |||
40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
41 | top_browser->sum_ksamples)); | ||
42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
43 | |||
44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
45 | slsmg_printf("%20.2f ", syme->weight); | ||
46 | width -= 24; | ||
47 | } else { | ||
48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
49 | width -= 23; | ||
50 | } | ||
51 | |||
52 | slsmg_printf("%4.1f%%", pcnt); | ||
53 | width -= 7; | ||
54 | |||
55 | if (verbose) { | ||
56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
57 | width -= 17; | ||
58 | } | ||
59 | |||
60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
61 | symbol->name); | ||
62 | width -= top_browser->sym_width; | ||
63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
64 | syme->map->dso->long_name : | ||
65 | syme->map->dso->short_name, width); | ||
66 | |||
67 | if (current_entry) | ||
68 | top_browser->selection = syme; | ||
69 | } | ||
70 | |||
71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
72 | { | ||
73 | struct perf_top *top = browser->b.priv; | ||
74 | u64 top_idx = browser->b.top_idx; | ||
75 | |||
76 | browser->root = RB_ROOT; | ||
77 | browser->b.top = NULL; | ||
78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
79 | /* | ||
80 | * No active symbols | ||
81 | */ | ||
82 | if (top->rb_entries == 0) | ||
83 | return; | ||
84 | |||
85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
86 | &browser->dso_short_width, | ||
87 | &browser->sym_width); | ||
88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
89 | browser->dso_width = browser->dso_short_width; | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
97 | * possible new top of the screen. | ||
98 | */ | ||
99 | browser->b.nr_entries = top->rb_entries; | ||
100 | |||
101 | if (top_idx >= browser->b.nr_entries) { | ||
102 | if (browser->b.height >= browser->b.nr_entries) | ||
103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
104 | else | ||
105 | top_idx = 0; | ||
106 | } | ||
107 | |||
108 | if (browser->b.index >= top_idx + browser->b.height) | ||
109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
110 | |||
111 | if (browser->b.index >= browser->b.nr_entries) | ||
112 | browser->b.index = browser->b.nr_entries - 1; | ||
113 | |||
114 | browser->b.top_idx = top_idx; | ||
115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
116 | } | ||
117 | |||
118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
119 | { | ||
120 | struct sym_entry *syme = browser->selection; | ||
121 | struct symbol *sym = sym_entry__symbol(syme); | ||
122 | struct annotation *notes = symbol__annotation(sym); | ||
123 | struct perf_top *top = browser->b.priv; | ||
124 | |||
125 | if (notes->src != NULL) | ||
126 | goto do_annotation; | ||
127 | |||
128 | pthread_mutex_lock(¬es->lock); | ||
129 | |||
130 | top->sym_filter_entry = NULL; | ||
131 | |||
132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
134 | sym->name); | ||
135 | pthread_mutex_unlock(¬es->lock); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | top->sym_filter_entry = syme; | ||
140 | |||
141 | pthread_mutex_unlock(¬es->lock); | ||
142 | do_annotation: | ||
143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
144 | } | ||
145 | |||
146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
147 | { | ||
148 | int key; | ||
149 | char title[160]; | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | int delay_msecs = top->delay_secs * 1000; | ||
152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
153 | |||
154 | perf_top_browser__update_rb_tree(browser); | ||
155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
156 | perf_top__reset_sample_counters(top); | ||
157 | |||
158 | if (ui_browser__show(&browser->b, title, | ||
159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
160 | return -1; | ||
161 | |||
162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
164 | |||
165 | while (1) { | ||
166 | key = ui_browser__run(&browser->b); | ||
167 | |||
168 | switch (key) { | ||
169 | case -1: | ||
170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
171 | perf_top_browser__update_rb_tree(browser); | ||
172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
173 | perf_top__reset_sample_counters(top); | ||
174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
175 | SLsmg_gotorc(0, 0); | ||
176 | slsmg_write_nstring(title, browser->b.width); | ||
177 | break; | ||
178 | case 'a': | ||
179 | case NEWT_KEY_RIGHT: | ||
180 | case NEWT_KEY_ENTER: | ||
181 | if (browser->selection) | ||
182 | perf_top_browser__annotate(browser); | ||
183 | break; | ||
184 | case NEWT_KEY_LEFT: | ||
185 | continue; | ||
186 | case NEWT_KEY_ESCAPE: | ||
187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
188 | continue; | ||
189 | /* Fall thru */ | ||
190 | default: | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | ui_browser__hide(&browser->b); | ||
196 | return key; | ||
197 | } | ||
198 | |||
199 | int perf_top__tui_browser(struct perf_top *top) | ||
200 | { | ||
201 | struct perf_top_browser browser = { | ||
202 | .b = { | ||
203 | .entries = &browser.root, | ||
204 | .refresh = ui_browser__rb_tree_refresh, | ||
205 | .seek = ui_browser__rb_tree_seek, | ||
206 | .write = perf_top_browser__write, | ||
207 | .priv = top, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | ui_helpline__push("Press <- or ESC to exit"); | ||
212 | return perf_top_browser__run(&browser); | ||
213 | } | ||
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 8d79daa4458a..f36d2ff509ed 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "helpline.h" | 7 | #include "helpline.h" |
8 | #include "ui.h" | ||
8 | 9 | ||
9 | void ui_helpline__pop(void) | 10 | void ui_helpline__pop(void) |
10 | { | 11 | { |
@@ -55,7 +56,8 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
55 | int ret; | 56 | int ret; |
56 | static int backlog; | 57 | static int backlog; |
57 | 58 | ||
58 | ret = vsnprintf(ui_helpline__last_msg + backlog, | 59 | pthread_mutex_lock(&ui__lock); |
60 | ret = vsnprintf(ui_helpline__last_msg + backlog, | ||
59 | sizeof(ui_helpline__last_msg) - backlog, format, ap); | 61 | sizeof(ui_helpline__last_msg) - backlog, format, ap); |
60 | backlog += ret; | 62 | backlog += ret; |
61 | 63 | ||
@@ -64,6 +66,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
64 | newtRefresh(); | 66 | newtRefresh(); |
65 | backlog = 0; | 67 | backlog = 0; |
66 | } | 68 | } |
69 | pthread_mutex_unlock(&ui__lock); | ||
67 | 70 | ||
68 | return ret; | 71 | return ret; |
69 | } | 72 | } |
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 5623da8e8080..2b63e1c9b181 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
@@ -13,11 +13,11 @@ | |||
13 | 13 | ||
14 | #if SLANG_VERSION < 20104 | 14 | #if SLANG_VERSION < 20104 |
15 | #define slsmg_printf(msg, args...) \ | 15 | #define slsmg_printf(msg, args...) \ |
16 | SLsmg_printf((char *)msg, ##args) | 16 | SLsmg_printf((char *)(msg), ##args) |
17 | #define slsmg_write_nstring(msg, len) \ | 17 | #define slsmg_write_nstring(msg, len) \ |
18 | SLsmg_write_nstring((char *)msg, len) | 18 | SLsmg_write_nstring((char *)(msg), len) |
19 | #define sltt_set_color(obj, name, fg, bg) \ | 19 | #define sltt_set_color(obj, name, fg, bg) \ |
20 | SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) | 20 | SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg)) |
21 | #else | 21 | #else |
22 | #define slsmg_printf SLsmg_printf | 22 | #define slsmg_printf SLsmg_printf |
23 | #define slsmg_write_nstring SLsmg_write_nstring | 23 | #define slsmg_write_nstring SLsmg_write_nstring |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 662085032eb7..ee46d671db59 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
@@ -6,6 +6,9 @@ | |||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "browser.h" | 7 | #include "browser.h" |
8 | #include "helpline.h" | 8 | #include "helpline.h" |
9 | #include "ui.h" | ||
10 | |||
11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | ||
9 | 12 | ||
10 | static void newt_suspend(void *d __used) | 13 | static void newt_suspend(void *d __used) |
11 | { | 14 | { |
@@ -14,11 +17,12 @@ static void newt_suspend(void *d __used) | |||
14 | newtResume(); | 17 | newtResume(); |
15 | } | 18 | } |
16 | 19 | ||
17 | void setup_browser(void) | 20 | void setup_browser(bool fallback_to_pager) |
18 | { | 21 | { |
19 | if (!isatty(1) || !use_browser || dump_trace) { | 22 | if (!isatty(1) || !use_browser || dump_trace) { |
20 | use_browser = 0; | 23 | use_browser = 0; |
21 | setup_pager(); | 24 | if (fallback_to_pager) |
25 | setup_pager(); | ||
22 | return; | 26 | return; |
23 | } | 27 | } |
24 | 28 | ||
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h new file mode 100644 index 000000000000..d264e059c829 --- /dev/null +++ b/tools/perf/util/ui/ui.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_UI_H_ | ||
2 | #define _PERF_UI_H_ 1 | ||
3 | |||
4 | #include <pthread.h> | ||
5 | |||
6 | extern pthread_mutex_t ui__lock; | ||
7 | |||
8 | #endif /* _PERF_UI_H_ */ | ||
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 7b5a8926624e..fdf1fc8f08bc 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "../debug.h" | 9 | #include "../debug.h" |
10 | #include "browser.h" | 10 | #include "browser.h" |
11 | #include "helpline.h" | 11 | #include "helpline.h" |
12 | #include "ui.h" | ||
12 | #include "util.h" | 13 | #include "util.h" |
13 | 14 | ||
14 | static void newt_form__set_exit_keys(newtComponent self) | 15 | static void newt_form__set_exit_keys(newtComponent self) |
@@ -118,10 +119,12 @@ void ui__warning(const char *format, ...) | |||
118 | va_list args; | 119 | va_list args; |
119 | 120 | ||
120 | va_start(args, format); | 121 | va_start(args, format); |
121 | if (use_browser > 0) | 122 | if (use_browser > 0) { |
123 | pthread_mutex_lock(&ui__lock); | ||
122 | newtWinMessagev((char *)warning_str, (char *)ok, | 124 | newtWinMessagev((char *)warning_str, (char *)ok, |
123 | (char *)format, args); | 125 | (char *)format, args); |
124 | else | 126 | pthread_mutex_unlock(&ui__lock); |
127 | } else | ||
125 | vfprintf(stderr, format, args); | 128 | vfprintf(stderr, format, args); |
126 | va_end(args); | 129 | va_end(args); |
127 | } | 130 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index e833f26f3bfc..fc784284ac8b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -70,9 +70,7 @@ | |||
70 | #include <sys/poll.h> | 70 | #include <sys/poll.h> |
71 | #include <sys/socket.h> | 71 | #include <sys/socket.h> |
72 | #include <sys/ioctl.h> | 72 | #include <sys/ioctl.h> |
73 | #ifndef NO_SYS_SELECT_H | ||
74 | #include <sys/select.h> | 73 | #include <sys/select.h> |
75 | #endif | ||
76 | #include <netinet/in.h> | 74 | #include <netinet/in.h> |
77 | #include <netinet/tcp.h> | 75 | #include <netinet/tcp.h> |
78 | #include <arpa/inet.h> | 76 | #include <arpa/inet.h> |
@@ -83,10 +81,6 @@ | |||
83 | #include "types.h" | 81 | #include "types.h" |
84 | #include <sys/ttydefaults.h> | 82 | #include <sys/ttydefaults.h> |
85 | 83 | ||
86 | #ifndef NO_ICONV | ||
87 | #include <iconv.h> | ||
88 | #endif | ||
89 | |||
90 | extern const char *graph_line; | 84 | extern const char *graph_line; |
91 | extern const char *graph_dotted_line; | 85 | extern const char *graph_dotted_line; |
92 | extern char buildid_dir[]; | 86 | extern char buildid_dir[]; |
@@ -236,26 +230,6 @@ static inline int sane_case(int x, int high) | |||
236 | return x; | 230 | return x; |
237 | } | 231 | } |
238 | 232 | ||
239 | #ifndef DIR_HAS_BSD_GROUP_SEMANTICS | ||
240 | # define FORCE_DIR_SET_GID S_ISGID | ||
241 | #else | ||
242 | # define FORCE_DIR_SET_GID 0 | ||
243 | #endif | ||
244 | |||
245 | #ifdef NO_NSEC | ||
246 | #undef USE_NSEC | ||
247 | #define ST_CTIME_NSEC(st) 0 | ||
248 | #define ST_MTIME_NSEC(st) 0 | ||
249 | #else | ||
250 | #ifdef USE_ST_TIMESPEC | ||
251 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) | ||
252 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) | ||
253 | #else | ||
254 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) | ||
255 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) | ||
256 | #endif | ||
257 | #endif | ||
258 | |||
259 | int mkdir_p(char *path, mode_t mode); | 233 | int mkdir_p(char *path, mode_t mode); |
260 | int copyfile(const char *from, const char *to); | 234 | int copyfile(const char *from, const char *to); |
261 | 235 | ||
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index a8b3292d0639..8ce792ea08e9 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl | |||
@@ -1,6 +1,6 @@ | |||
1 | #!/usr/bin/perl -w | 1 | #!/usr/bin/perl -w |
2 | # | 2 | # |
3 | # Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. | 3 | # Copyright 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. |
4 | # Licensed under the terms of the GNU GPL License version 2 | 4 | # Licensed under the terms of the GNU GPL License version 2 |
5 | # | 5 | # |
6 | 6 | ||
diff --git a/tools/usb/Makefile b/tools/usb/Makefile new file mode 100644 index 000000000000..8b704af14349 --- /dev/null +++ b/tools/usb/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | # Makefile for USB tools | ||
2 | |||
3 | CC = $(CROSS_COMPILE)gcc | ||
4 | PTHREAD_LIBS = -lpthread | ||
5 | WARNINGS = -Wall -Wextra | ||
6 | CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) | ||
7 | |||
8 | all: testusb ffs-test | ||
9 | %: %.c | ||
10 | $(CC) $(CFLAGS) -o $@ $^ | ||
11 | |||
12 | clean: | ||
13 | $(RM) testusb ffs-test | ||
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index bbe2e3a2ea62..b9c798631699 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c | |||
@@ -37,7 +37,7 @@ | |||
37 | #include <sys/types.h> | 37 | #include <sys/types.h> |
38 | #include <unistd.h> | 38 | #include <unistd.h> |
39 | 39 | ||
40 | #include <linux/usb/functionfs.h> | 40 | #include "../../include/linux/usb/functionfs.h" |
41 | 41 | ||
42 | 42 | ||
43 | /******************** Little Endian Handling ********************************/ | 43 | /******************** Little Endian Handling ********************************/ |
@@ -450,7 +450,7 @@ invalid: | |||
450 | len, expected, *p); | 450 | len, expected, *p); |
451 | for (p = buf, len = 0; len < nbytes; ++p, ++len) { | 451 | for (p = buf, len = 0; len < nbytes; ++p, ++len) { |
452 | if (0 == (len % 32)) | 452 | if (0 == (len % 32)) |
453 | fprintf(stderr, "%4d:", len); | 453 | fprintf(stderr, "%4zd:", len); |
454 | fprintf(stderr, " %02x", *p); | 454 | fprintf(stderr, " %02x", *p); |
455 | if (31 == (len % 32)) | 455 | if (31 == (len % 32)) |
456 | fprintf(stderr, "\n"); | 456 | fprintf(stderr, "\n"); |
diff --git a/tools/usb/hcd-tests.sh b/tools/usb/hcd-tests.sh new file mode 100644 index 000000000000..b30b3dc4c788 --- /dev/null +++ b/tools/usb/hcd-tests.sh | |||
@@ -0,0 +1,275 @@ | |||
1 | #!/bin/sh | ||
2 | # | ||
3 | # test types can be passed on the command line: | ||
4 | # | ||
5 | # - control: any device can do this | ||
6 | # - out, in: out needs 'bulk sink' firmware, in needs 'bulk src' | ||
7 | # - iso-out, iso-in: out needs 'iso sink' firmware, in needs 'iso src' | ||
8 | # - halt: needs bulk sink+src, tests halt set/clear from host | ||
9 | # - unlink: needs bulk sink and/or src, test HCD unlink processing | ||
10 | # - loop: needs firmware that will buffer N transfers | ||
11 | # | ||
12 | # run it for hours, days, weeks. | ||
13 | # | ||
14 | |||
15 | # | ||
16 | # this default provides a steady test load for a bulk device | ||
17 | # | ||
18 | TYPES='control out in' | ||
19 | #TYPES='control out in halt' | ||
20 | |||
21 | # | ||
22 | # to test HCD code | ||
23 | # | ||
24 | # - include unlink tests | ||
25 | # - add some ${RANDOM}ness | ||
26 | # - connect several devices concurrently (same HC) | ||
27 | # - keep HC's IRQ lines busy with unrelated traffic (IDE, net, ...) | ||
28 | # - add other concurrent system loads | ||
29 | # | ||
30 | |||
31 | declare -i COUNT BUFLEN | ||
32 | |||
33 | COUNT=50000 | ||
34 | BUFLEN=2048 | ||
35 | |||
36 | # NOTE: the 'in' and 'out' cases are usually bulk, but can be | ||
37 | # set up to use interrupt transfers by 'usbtest' module options | ||
38 | |||
39 | |||
40 | if [ "$DEVICE" = "" ]; then | ||
41 | echo "testing ALL recognized usbtest devices" | ||
42 | echo "" | ||
43 | TEST_ARGS="-a" | ||
44 | else | ||
45 | TEST_ARGS="" | ||
46 | fi | ||
47 | |||
48 | do_test () | ||
49 | { | ||
50 | if ! ./testusb $TEST_ARGS -s $BUFLEN -c $COUNT $* 2>/dev/null | ||
51 | then | ||
52 | echo "FAIL" | ||
53 | exit 1 | ||
54 | fi | ||
55 | } | ||
56 | |||
57 | ARGS="$*" | ||
58 | |||
59 | if [ "$ARGS" = "" ]; | ||
60 | then | ||
61 | ARGS="$TYPES" | ||
62 | fi | ||
63 | |||
64 | # FIXME use /sys/bus/usb/device/$THIS/bConfigurationValue to | ||
65 | # check and change configs | ||
66 | |||
67 | CONFIG='' | ||
68 | |||
69 | check_config () | ||
70 | { | ||
71 | if [ "$CONFIG" = "" ]; then | ||
72 | CONFIG=$1 | ||
73 | echo "assuming $CONFIG configuration" | ||
74 | return | ||
75 | fi | ||
76 | if [ "$CONFIG" = $1 ]; then | ||
77 | return | ||
78 | fi | ||
79 | |||
80 | echo "** device must be in $1 config, but it's $CONFIG instead" | ||
81 | exit 1 | ||
82 | } | ||
83 | |||
84 | |||
85 | echo "TESTING: $ARGS" | ||
86 | |||
87 | while : true | ||
88 | do | ||
89 | echo $(date) | ||
90 | |||
91 | for TYPE in $ARGS | ||
92 | do | ||
93 | # restore defaults | ||
94 | COUNT=5000 | ||
95 | BUFLEN=2048 | ||
96 | |||
97 | # FIXME automatically multiply COUNT by 10 when | ||
98 | # /sys/bus/usb/device/$THIS/speed == "480" | ||
99 | |||
100 | # COUNT=50000 | ||
101 | |||
102 | case $TYPE in | ||
103 | control) | ||
104 | # any device, in any configuration, can use this. | ||
105 | echo '** Control test cases:' | ||
106 | |||
107 | echo "test 9: ch9 postconfig" | ||
108 | do_test -t 9 -c 5000 | ||
109 | echo "test 10: control queueing" | ||
110 | do_test -t 10 -c 5000 | ||
111 | |||
112 | # this relies on some vendor-specific commands | ||
113 | echo "test 14: control writes" | ||
114 | do_test -t 14 -c 15000 -s 256 -v 1 | ||
115 | |||
116 | echo "test 21: control writes, unaligned" | ||
117 | do_test -t 21 -c 100 -s 256 -v 1 | ||
118 | |||
119 | ;; | ||
120 | |||
121 | out) | ||
122 | check_config sink-src | ||
123 | echo '** Host Write (OUT) test cases:' | ||
124 | |||
125 | echo "test 1: $COUNT transfers, same size" | ||
126 | do_test -t 1 | ||
127 | echo "test 3: $COUNT transfers, variable/short size" | ||
128 | do_test -t 3 -v 421 | ||
129 | |||
130 | COUNT=100 | ||
131 | echo "test 17: $COUNT transfers, unaligned DMA map by core" | ||
132 | do_test -t 17 | ||
133 | |||
134 | echo "test 19: $COUNT transfers, unaligned DMA map by usb_alloc_coherent" | ||
135 | do_test -t 19 | ||
136 | |||
137 | COUNT=2000 | ||
138 | echo "test 5: $COUNT scatterlists, same size entries" | ||
139 | do_test -t 5 | ||
140 | |||
141 | # try to trigger short OUT processing bugs | ||
142 | echo "test 7a: $COUNT scatterlists, variable size/short entries" | ||
143 | do_test -t 7 -v 579 | ||
144 | BUFLEN=4096 | ||
145 | echo "test 7b: $COUNT scatterlists, variable size/bigger entries" | ||
146 | do_test -t 7 -v 41 | ||
147 | BUFLEN=64 | ||
148 | echo "test 7c: $COUNT scatterlists, variable size/micro entries" | ||
149 | do_test -t 7 -v 63 | ||
150 | ;; | ||
151 | |||
152 | iso-out) | ||
153 | check_config sink-src | ||
154 | echo '** Host ISOCHRONOUS Write (OUT) test cases:' | ||
155 | |||
156 | # at peak iso transfer rates: | ||
157 | # - usb 2.0 high bandwidth, this is one frame. | ||
158 | # - usb 1.1, it's twenty-four frames. | ||
159 | BUFLEN=24500 | ||
160 | |||
161 | COUNT=1000 | ||
162 | |||
163 | # COUNT=10000 | ||
164 | |||
165 | echo "test 15: $COUNT transfers, same size" | ||
166 | # do_test -t 15 -g 3 -v 0 | ||
167 | BUFLEN=32768 | ||
168 | do_test -t 15 -g 8 -v 0 | ||
169 | |||
170 | # FIXME it'd make sense to have an iso OUT test issuing | ||
171 | # short writes on more packets than the last one | ||
172 | |||
173 | COUNT=100 | ||
174 | echo "test 22: $COUNT transfers, non aligned" | ||
175 | do_test -t 22 -g 8 -v 0 | ||
176 | |||
177 | ;; | ||
178 | |||
179 | in) | ||
180 | check_config sink-src | ||
181 | echo '** Host Read (IN) test cases:' | ||
182 | |||
183 | # NOTE: these "variable size" reads are just multiples | ||
184 | # of 512 bytes, no EOVERFLOW testing is done yet | ||
185 | |||
186 | echo "test 2: $COUNT transfers, same size" | ||
187 | do_test -t 2 | ||
188 | echo "test 4: $COUNT transfers, variable size" | ||
189 | do_test -t 4 | ||
190 | |||
191 | COUNT=100 | ||
192 | echo "test 18: $COUNT transfers, unaligned DMA map by core" | ||
193 | do_test -t 18 | ||
194 | |||
195 | echo "test 20: $COUNT transfers, unaligned DMA map by usb_alloc_coherent" | ||
196 | do_test -t 20 | ||
197 | |||
198 | COUNT=2000 | ||
199 | echo "test 6: $COUNT scatterlists, same size entries" | ||
200 | do_test -t 6 | ||
201 | echo "test 8: $COUNT scatterlists, variable size entries" | ||
202 | do_test -t 8 | ||
203 | ;; | ||
204 | |||
205 | iso-in) | ||
206 | check_config sink-src | ||
207 | echo '** Host ISOCHRONOUS Read (IN) test cases:' | ||
208 | |||
209 | # at peak iso transfer rates: | ||
210 | # - usb 2.0 high bandwidth, this is one frame. | ||
211 | # - usb 1.1, it's twenty-four frames. | ||
212 | BUFLEN=24500 | ||
213 | |||
214 | COUNT=1000 | ||
215 | |||
216 | # COUNT=10000 | ||
217 | |||
218 | echo "test 16: $COUNT transfers, same size" | ||
219 | # do_test -t 16 -g 3 -v 0 | ||
220 | BUFLEN=32768 | ||
221 | do_test -t 16 -g 8 -v 0 | ||
222 | |||
223 | # FIXME since iso expects faults, it'd make sense | ||
224 | # to have an iso IN test issuing short reads ... | ||
225 | |||
226 | COUNT=100 | ||
227 | echo "test 23: $COUNT transfers, unaligned" | ||
228 | do_test -t 23 -g 8 -v 0 | ||
229 | |||
230 | ;; | ||
231 | |||
232 | halt) | ||
233 | # NOTE: sometimes hardware doesn't cooperate well with halting | ||
234 | # endpoints from the host side. so long as mass-storage class | ||
235 | # firmware can halt them from the device, don't worry much if | ||
236 | # you can't make this test work on your device. | ||
237 | COUNT=2000 | ||
238 | echo "test 13: $COUNT halt set/clear" | ||
239 | do_test -t 13 | ||
240 | ;; | ||
241 | |||
242 | unlink) | ||
243 | COUNT=2000 | ||
244 | echo "test 11: $COUNT read unlinks" | ||
245 | do_test -t 11 | ||
246 | |||
247 | echo "test 12: $COUNT write unlinks" | ||
248 | do_test -t 12 | ||
249 | ;; | ||
250 | |||
251 | loop) | ||
252 | # defaults need too much buffering for ez-usb devices | ||
253 | BUFLEN=2048 | ||
254 | COUNT=32 | ||
255 | |||
256 | # modprobe g_zero qlen=$COUNT buflen=$BUFLEN loopdefault | ||
257 | check_config loopback | ||
258 | |||
259 | # FIXME someone needs to write and merge a version of this | ||
260 | |||
261 | echo "write $COUNT buffers of $BUFLEN bytes, read them back" | ||
262 | |||
263 | echo "write $COUNT variable size buffers, read them back" | ||
264 | |||
265 | ;; | ||
266 | |||
267 | *) | ||
268 | echo "Don't understand test type $TYPE" | ||
269 | exit 1; | ||
270 | esac | ||
271 | echo '' | ||
272 | done | ||
273 | done | ||
274 | |||
275 | # vim: sw=4 | ||