diff options
Diffstat (limited to 'tools')
141 files changed, 7570 insertions, 3599 deletions
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 04d959fa0226..a20e32033431 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile | |||
@@ -253,7 +253,7 @@ all_deps := $(all_objs:%.o=.%.d) | |||
253 | # let .d file also depends on the source and header files | 253 | # let .d file also depends on the source and header files |
254 | define check_deps | 254 | define check_deps |
255 | @set -e; $(RM) $@; \ | 255 | @set -e; $(RM) $@; \ |
256 | $(CC) -M $(CFLAGS) $< > $@.$$$$; \ | 256 | $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ |
257 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ | 257 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ |
258 | $(RM) $@.$$$$ | 258 | $(RM) $@.$$$$ |
259 | endef | 259 | endef |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index f2989c525e48..5a824e355d04 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -174,7 +174,7 @@ static int cmdline_init(struct pevent *pevent) | |||
174 | return 0; | 174 | return 0; |
175 | } | 175 | } |
176 | 176 | ||
177 | static char *find_cmdline(struct pevent *pevent, int pid) | 177 | static const char *find_cmdline(struct pevent *pevent, int pid) |
178 | { | 178 | { |
179 | const struct cmdline *comm; | 179 | const struct cmdline *comm; |
180 | struct cmdline key; | 180 | struct cmdline key; |
@@ -2637,7 +2637,7 @@ process_func_handler(struct event_format *event, struct pevent_function_handler | |||
2637 | struct print_arg *farg; | 2637 | struct print_arg *farg; |
2638 | enum event_type type; | 2638 | enum event_type type; |
2639 | char *token; | 2639 | char *token; |
2640 | char *test; | 2640 | const char *test; |
2641 | int i; | 2641 | int i; |
2642 | 2642 | ||
2643 | arg->type = PRINT_FUNC; | 2643 | arg->type = PRINT_FUNC; |
@@ -3889,7 +3889,7 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, | |||
3889 | struct event_format *event, struct print_arg *arg) | 3889 | struct event_format *event, struct print_arg *arg) |
3890 | { | 3890 | { |
3891 | unsigned char *buf; | 3891 | unsigned char *buf; |
3892 | char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; | 3892 | const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; |
3893 | 3893 | ||
3894 | if (arg->type == PRINT_FUNC) { | 3894 | if (arg->type == PRINT_FUNC) { |
3895 | process_defined_func(s, data, size, event, arg); | 3895 | process_defined_func(s, data, size, event, arg); |
@@ -3931,7 +3931,8 @@ static int is_printable_array(char *p, unsigned int len) | |||
3931 | return 1; | 3931 | return 1; |
3932 | } | 3932 | } |
3933 | 3933 | ||
3934 | static void print_event_fields(struct trace_seq *s, void *data, int size, | 3934 | static void print_event_fields(struct trace_seq *s, void *data, |
3935 | int size __maybe_unused, | ||
3935 | struct event_format *event) | 3936 | struct event_format *event) |
3936 | { | 3937 | { |
3937 | struct format_field *field; | 3938 | struct format_field *field; |
@@ -4408,7 +4409,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, | |||
4408 | void pevent_print_event(struct pevent *pevent, struct trace_seq *s, | 4409 | void pevent_print_event(struct pevent *pevent, struct trace_seq *s, |
4409 | struct pevent_record *record) | 4410 | struct pevent_record *record) |
4410 | { | 4411 | { |
4411 | static char *spaces = " "; /* 20 spaces */ | 4412 | static const char *spaces = " "; /* 20 spaces */ |
4412 | struct event_format *event; | 4413 | struct event_format *event; |
4413 | unsigned long secs; | 4414 | unsigned long secs; |
4414 | unsigned long usecs; | 4415 | unsigned long usecs; |
@@ -5070,8 +5071,8 @@ static const char * const pevent_error_str[] = { | |||
5070 | }; | 5071 | }; |
5071 | #undef _PE | 5072 | #undef _PE |
5072 | 5073 | ||
5073 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | 5074 | int pevent_strerror(struct pevent *pevent __maybe_unused, |
5074 | char *buf, size_t buflen) | 5075 | enum pevent_errno errnum, char *buf, size_t buflen) |
5075 | { | 5076 | { |
5076 | int idx; | 5077 | int idx; |
5077 | const char *msg; | 5078 | const char *msg; |
@@ -5100,6 +5101,7 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | |||
5100 | case PEVENT_ERRNO__READ_FORMAT_FAILED: | 5101 | case PEVENT_ERRNO__READ_FORMAT_FAILED: |
5101 | case PEVENT_ERRNO__READ_PRINT_FAILED: | 5102 | case PEVENT_ERRNO__READ_PRINT_FAILED: |
5102 | case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: | 5103 | case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: |
5104 | case PEVENT_ERRNO__INVALID_ARG_TYPE: | ||
5103 | snprintf(buf, buflen, "%s", msg); | 5105 | snprintf(buf, buflen, "%s", msg); |
5104 | break; | 5106 | break; |
5105 | 5107 | ||
@@ -5362,7 +5364,7 @@ int pevent_register_print_function(struct pevent *pevent, | |||
5362 | if (type == PEVENT_FUNC_ARG_VOID) | 5364 | if (type == PEVENT_FUNC_ARG_VOID) |
5363 | break; | 5365 | break; |
5364 | 5366 | ||
5365 | if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { | 5367 | if (type >= PEVENT_FUNC_ARG_MAX_TYPES) { |
5366 | do_warning("Invalid argument type %d", type); | 5368 | do_warning("Invalid argument type %d", type); |
5367 | ret = PEVENT_ERRNO__INVALID_ARG_TYPE; | 5369 | ret = PEVENT_ERRNO__INVALID_ARG_TYPE; |
5368 | goto out_free; | 5370 | goto out_free; |
@@ -5560,7 +5562,7 @@ void pevent_free(struct pevent *pevent) | |||
5560 | } | 5562 | } |
5561 | 5563 | ||
5562 | if (pevent->func_map) { | 5564 | if (pevent->func_map) { |
5563 | for (i = 0; i < pevent->func_count; i++) { | 5565 | for (i = 0; i < (int)pevent->func_count; i++) { |
5564 | free(pevent->func_map[i].func); | 5566 | free(pevent->func_map[i].func); |
5565 | free(pevent->func_map[i].mod); | 5567 | free(pevent->func_map[i].mod); |
5566 | } | 5568 | } |
@@ -5582,7 +5584,7 @@ void pevent_free(struct pevent *pevent) | |||
5582 | } | 5584 | } |
5583 | 5585 | ||
5584 | if (pevent->printk_map) { | 5586 | if (pevent->printk_map) { |
5585 | for (i = 0; i < pevent->printk_count; i++) | 5587 | for (i = 0; i < (int)pevent->printk_count; i++) |
5586 | free(pevent->printk_map[i].printk); | 5588 | free(pevent->printk_map[i].printk); |
5587 | free(pevent->printk_map); | 5589 | free(pevent->printk_map); |
5588 | } | 5590 | } |
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 9f2e44f2b17a..ef6d22e879eb 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -1,3 +1,5 @@ | |||
1 | include ../config/utilities.mak | ||
2 | |||
1 | OUTPUT := ./ | 3 | OUTPUT := ./ |
2 | ifeq ("$(origin O)", "command line") | 4 | ifeq ("$(origin O)", "command line") |
3 | ifneq ($(O),) | 5 | ifneq ($(O),) |
@@ -64,6 +66,7 @@ MAKEINFO=makeinfo | |||
64 | INSTALL_INFO=install-info | 66 | INSTALL_INFO=install-info |
65 | DOCBOOK2X_TEXI=docbook2x-texi | 67 | DOCBOOK2X_TEXI=docbook2x-texi |
66 | DBLATEX=dblatex | 68 | DBLATEX=dblatex |
69 | XMLTO=xmlto | ||
67 | ifndef PERL_PATH | 70 | ifndef PERL_PATH |
68 | PERL_PATH = /usr/bin/perl | 71 | PERL_PATH = /usr/bin/perl |
69 | endif | 72 | endif |
@@ -71,6 +74,16 @@ endif | |||
71 | -include ../config.mak.autogen | 74 | -include ../config.mak.autogen |
72 | -include ../config.mak | 75 | -include ../config.mak |
73 | 76 | ||
77 | _tmp_tool_path := $(call get-executable,$(ASCIIDOC)) | ||
78 | ifeq ($(_tmp_tool_path),) | ||
79 | missing_tools = $(ASCIIDOC) | ||
80 | endif | ||
81 | |||
82 | _tmp_tool_path := $(call get-executable,$(XMLTO)) | ||
83 | ifeq ($(_tmp_tool_path),) | ||
84 | missing_tools += $(XMLTO) | ||
85 | endif | ||
86 | |||
74 | # | 87 | # |
75 | # For asciidoc ... | 88 | # For asciidoc ... |
76 | # -7.1.2, no extra settings are needed. | 89 | # -7.1.2, no extra settings are needed. |
@@ -170,7 +183,12 @@ pdf: $(OUTPUT)user-manual.pdf | |||
170 | 183 | ||
171 | install: install-man | 184 | install: install-man |
172 | 185 | ||
173 | install-man: man | 186 | check-man-tools: |
187 | ifdef missing_tools | ||
188 | $(error "You need to install $(missing_tools) for man pages") | ||
189 | endif | ||
190 | |||
191 | do-install-man: man | ||
174 | $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) | 192 | $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) |
175 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) | 193 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) |
176 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) | 194 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) |
@@ -178,6 +196,15 @@ install-man: man | |||
178 | # $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) | 196 | # $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) |
179 | # $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) | 197 | # $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) |
180 | 198 | ||
199 | install-man: check-man-tools man | ||
200 | |||
201 | try-install-man: | ||
202 | ifdef missing_tools | ||
203 | $(warning Please install $(missing_tools) to have the man pages installed) | ||
204 | else | ||
205 | $(MAKE) do-install-man | ||
206 | endif | ||
207 | |||
181 | install-info: info | 208 | install-info: info |
182 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) | 209 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) |
183 | $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) | 210 | $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) |
@@ -246,7 +273,7 @@ $(MAN_HTML): $(OUTPUT)%.html : %.txt | |||
246 | 273 | ||
247 | $(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml | 274 | $(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml |
248 | $(QUIET_XMLTO)$(RM) $@ && \ | 275 | $(QUIET_XMLTO)$(RM) $@ && \ |
249 | xmlto -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< | 276 | $(XMLTO) -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< |
250 | 277 | ||
251 | $(OUTPUT)%.xml : %.txt | 278 | $(OUTPUT)%.xml : %.txt |
252 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ | 279 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ |
diff --git a/tools/perf/Documentation/android.txt b/tools/perf/Documentation/android.txt new file mode 100644 index 000000000000..8484c3a04a6a --- /dev/null +++ b/tools/perf/Documentation/android.txt | |||
@@ -0,0 +1,78 @@ | |||
1 | How to compile perf for Android | ||
2 | ========================================= | ||
3 | |||
4 | I. Set the Android NDK environment | ||
5 | ------------------------------------------------ | ||
6 | |||
7 | (a). Use the Android NDK | ||
8 | ------------------------------------------------ | ||
9 | 1. You need to download and install the Android Native Development Kit (NDK). | ||
10 | Set the NDK variable to point to the path where you installed the NDK: | ||
11 | export NDK=/path/to/android-ndk | ||
12 | |||
13 | 2. Set cross-compiling environment variables for NDK toolchain and sysroot. | ||
14 | For arm: | ||
15 | export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi- | ||
16 | export NDK_SYSROOT=${NDK}/platforms/android-9/arch-arm | ||
17 | For x86: | ||
18 | export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android- | ||
19 | export NDK_SYSROOT=${NDK}/platforms/android-9/arch-x86 | ||
20 | |||
21 | This method is not working for Android NDK versions up to Revision 8b. | ||
22 | perf uses some bionic enhancements that are not included in these NDK versions. | ||
23 | You can use method (b) described below instead. | ||
24 | |||
25 | (b). Use the Android source tree | ||
26 | ----------------------------------------------- | ||
27 | 1. Download the master branch of the Android source tree. | ||
28 | Set the environment for the target you want using: | ||
29 | source build/envsetup.sh | ||
30 | lunch | ||
31 | |||
32 | 2. Build your own NDK sysroot to contain latest bionic changes and set the | ||
33 | NDK sysroot environment variable. | ||
34 | cd ${ANDROID_BUILD_TOP}/ndk | ||
35 | For arm: | ||
36 | ./build/tools/build-ndk-sysroot.sh --abi=arm | ||
37 | export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-arm | ||
38 | For x86: | ||
39 | ./build/tools/build-ndk-sysroot.sh --abi=x86 | ||
40 | export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-x86 | ||
41 | |||
42 | 3. Set the NDK toolchain environment variable. | ||
43 | For arm: | ||
44 | export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/arm-linux-androideabi- | ||
45 | For x86: | ||
46 | export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/i686-linux-android- | ||
47 | |||
48 | II. Compile perf for Android | ||
49 | ------------------------------------------------ | ||
50 | You need to run make with the NDK toolchain and sysroot defined above: | ||
51 | For arm: | ||
52 | make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}" | ||
53 | For x86: | ||
54 | make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}" | ||
55 | |||
56 | III. Install perf | ||
57 | ----------------------------------------------- | ||
58 | You need to connect to your Android device/emulator using adb. | ||
59 | Install perf using: | ||
60 | adb push perf /data/perf | ||
61 | |||
62 | If you also want to use perf-archive you need busybox tools for Android. | ||
63 | For installing perf-archive, you first need to replace #!/bin/bash with #!/system/bin/sh: | ||
64 | sed 's/#!\/bin\/bash/#!\/system\/bin\/sh/g' perf-archive >> /tmp/perf-archive | ||
65 | chmod +x /tmp/perf-archive | ||
66 | adb push /tmp/perf-archive /data/perf-archive | ||
67 | |||
68 | IV. Environment settings for running perf | ||
69 | ------------------------------------------------ | ||
70 | Some perf features need environment variables to run properly. | ||
71 | You need to set these before running perf on the target: | ||
72 | adb shell | ||
73 | # PERF_PAGER=cat | ||
74 | |||
75 | IV. Run perf | ||
76 | ------------------------------------------------ | ||
77 | Run perf on your device/emulator to which you previously connected using adb: | ||
78 | # ./data/perf | ||
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index ab7f667de1b1..194f37d635df 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -72,6 +72,66 @@ OPTIONS | |||
72 | --symfs=<directory>:: | 72 | --symfs=<directory>:: |
73 | Look for files with symbols relative to this directory. | 73 | Look for files with symbols relative to this directory. |
74 | 74 | ||
75 | -b:: | ||
76 | --baseline-only:: | ||
77 | Show only items with match in baseline. | ||
78 | |||
79 | -c:: | ||
80 | --compute:: | ||
81 | Differential computation selection - delta,ratio,wdiff (default is delta). | ||
82 | If '+' is specified as a first character, the output is sorted based | ||
83 | on the computation results. | ||
84 | See COMPARISON METHODS section for more info. | ||
85 | |||
86 | -p:: | ||
87 | --period:: | ||
88 | Show period values for both compared hist entries. | ||
89 | |||
90 | -F:: | ||
91 | --formula:: | ||
92 | Show formula for given computation. | ||
93 | |||
94 | COMPARISON METHODS | ||
95 | ------------------ | ||
96 | delta | ||
97 | ~~~~~ | ||
98 | If specified the 'Delta' column is displayed with value 'd' computed as: | ||
99 | |||
100 | d = A->period_percent - B->period_percent | ||
101 | |||
102 | with: | ||
103 | - A/B being matching hist entry from first/second file specified | ||
104 | (or perf.data/perf.data.old) respectively. | ||
105 | |||
106 | - period_percent being the % of the hist entry period value within | ||
107 | single data file | ||
108 | |||
109 | ratio | ||
110 | ~~~~~ | ||
111 | If specified the 'Ratio' column is displayed with value 'r' computed as: | ||
112 | |||
113 | r = A->period / B->period | ||
114 | |||
115 | with: | ||
116 | - A/B being matching hist entry from first/second file specified | ||
117 | (or perf.data/perf.data.old) respectively. | ||
118 | |||
119 | - period being the hist entry period value | ||
120 | |||
121 | wdiff | ||
122 | ~~~~~ | ||
123 | If specified the 'Weighted diff' column is displayed with value 'd' computed as: | ||
124 | |||
125 | d = B->period * WEIGHT-A - A->period * WEIGHT-B | ||
126 | |||
127 | - A/B being matching hist entry from first/second file specified | ||
128 | (or perf.data/perf.data.old) respectively. | ||
129 | |||
130 | - period being the hist entry period value | ||
131 | |||
132 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option | ||
133 | behind ':' separator like '-c wdiff:1,2'. | ||
134 | |||
75 | SEE ALSO | 135 | SEE ALSO |
76 | -------- | 136 | -------- |
77 | linkperf:perf-record[1] | 137 | linkperf:perf-record[1] |
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index 025630d43cd2..a00a34276c54 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt | |||
@@ -29,6 +29,17 @@ OPTIONS | |||
29 | -v:: | 29 | -v:: |
30 | --verbose:: | 30 | --verbose:: |
31 | Be more verbose. | 31 | Be more verbose. |
32 | -i:: | ||
33 | --input=:: | ||
34 | Input file name. (default: stdin) | ||
35 | -o:: | ||
36 | --output=:: | ||
37 | Output file name. (default: stdout) | ||
38 | -s:: | ||
39 | --sched-stat:: | ||
40 | Merge sched_stat and sched_switch for getting events where and how long | ||
41 | tasks slept. sched_switch contains a callchain where a task slept and | ||
42 | sched_stat contains a timeslice how long a task slept. | ||
32 | 43 | ||
33 | SEE ALSO | 44 | SEE ALSO |
34 | -------- | 45 | -------- |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 2fa173b51970..cf0c3107e06e 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -108,6 +108,11 @@ with it. --append may be used here. Examples: | |||
108 | 3>results perf stat --log-fd 3 -- $cmd | 108 | 3>results perf stat --log-fd 3 -- $cmd |
109 | 3>>results perf stat --log-fd 3 --append -- $cmd | 109 | 3>>results perf stat --log-fd 3 --append -- $cmd |
110 | 110 | ||
111 | --pre:: | ||
112 | --post:: | ||
113 | Pre and post measurement hooks, e.g.: | ||
114 | |||
115 | perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage | ||
111 | 116 | ||
112 | 117 | ||
113 | EXAMPLES | 118 | EXAMPLES |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3a2ae37310a9..68718ccdd178 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -48,6 +48,12 @@ comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0- | |||
48 | In per-thread mode with inheritance mode on (default), Events are captured only when | 48 | In per-thread mode with inheritance mode on (default), Events are captured only when |
49 | the thread executes on the designated CPUs. Default is to monitor all CPUs. | 49 | the thread executes on the designated CPUs. Default is to monitor all CPUs. |
50 | 50 | ||
51 | --duration: | ||
52 | Show only events that had a duration greater than N.M ms. | ||
53 | |||
54 | --sched: | ||
55 | Accrue thread runtime and provide a summary at the end of the session. | ||
56 | |||
51 | SEE ALSO | 57 | SEE ALSO |
52 | -------- | 58 | -------- |
53 | linkperf:perf-record[1], linkperf:perf-script[1] | 59 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0a619af5be43..891bc77bdb2c 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -155,15 +155,15 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | |||
155 | 155 | ||
156 | -include config/feature-tests.mak | 156 | -include config/feature-tests.mak |
157 | 157 | ||
158 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) | 158 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) |
159 | CFLAGS := $(CFLAGS) -fstack-protector-all | 159 | CFLAGS := $(CFLAGS) -fstack-protector-all |
160 | endif | 160 | endif |
161 | 161 | ||
162 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y) | 162 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) |
163 | CFLAGS := $(CFLAGS) -Wstack-protector | 163 | CFLAGS := $(CFLAGS) -Wstack-protector |
164 | endif | 164 | endif |
165 | 165 | ||
166 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y) | 166 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) |
167 | CFLAGS := $(CFLAGS) -Wvolatile-register-var | 167 | CFLAGS := $(CFLAGS) -Wvolatile-register-var |
168 | endif | 168 | endif |
169 | 169 | ||
@@ -197,8 +197,16 @@ BASIC_CFLAGS = \ | |||
197 | -I. \ | 197 | -I. \ |
198 | -I$(TRACE_EVENT_DIR) \ | 198 | -I$(TRACE_EVENT_DIR) \ |
199 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 199 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
200 | |||
200 | BASIC_LDFLAGS = | 201 | BASIC_LDFLAGS = |
201 | 202 | ||
203 | ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) | ||
204 | BIONIC := 1 | ||
205 | EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) | ||
206 | EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) | ||
207 | BASIC_CFLAGS += -I. | ||
208 | endif | ||
209 | |||
202 | # Guard against environment variables | 210 | # Guard against environment variables |
203 | BUILTIN_OBJS = | 211 | BUILTIN_OBJS = |
204 | LIB_H = | 212 | LIB_H = |
@@ -330,6 +338,7 @@ LIB_H += util/evlist.h | |||
330 | LIB_H += util/exec_cmd.h | 338 | LIB_H += util/exec_cmd.h |
331 | LIB_H += util/types.h | 339 | LIB_H += util/types.h |
332 | LIB_H += util/levenshtein.h | 340 | LIB_H += util/levenshtein.h |
341 | LIB_H += util/machine.h | ||
333 | LIB_H += util/map.h | 342 | LIB_H += util/map.h |
334 | LIB_H += util/parse-options.h | 343 | LIB_H += util/parse-options.h |
335 | LIB_H += util/parse-events.h | 344 | LIB_H += util/parse-events.h |
@@ -346,6 +355,7 @@ LIB_H += util/svghelper.h | |||
346 | LIB_H += util/tool.h | 355 | LIB_H += util/tool.h |
347 | LIB_H += util/run-command.h | 356 | LIB_H += util/run-command.h |
348 | LIB_H += util/sigchain.h | 357 | LIB_H += util/sigchain.h |
358 | LIB_H += util/dso.h | ||
349 | LIB_H += util/symbol.h | 359 | LIB_H += util/symbol.h |
350 | LIB_H += util/color.h | 360 | LIB_H += util/color.h |
351 | LIB_H += util/values.h | 361 | LIB_H += util/values.h |
@@ -389,7 +399,6 @@ LIB_OBJS += $(OUTPUT)util/help.o | |||
389 | LIB_OBJS += $(OUTPUT)util/levenshtein.o | 399 | LIB_OBJS += $(OUTPUT)util/levenshtein.o |
390 | LIB_OBJS += $(OUTPUT)util/parse-options.o | 400 | LIB_OBJS += $(OUTPUT)util/parse-options.o |
391 | LIB_OBJS += $(OUTPUT)util/parse-events.o | 401 | LIB_OBJS += $(OUTPUT)util/parse-events.o |
392 | LIB_OBJS += $(OUTPUT)util/parse-events-test.o | ||
393 | LIB_OBJS += $(OUTPUT)util/path.o | 402 | LIB_OBJS += $(OUTPUT)util/path.o |
394 | LIB_OBJS += $(OUTPUT)util/rbtree.o | 403 | LIB_OBJS += $(OUTPUT)util/rbtree.o |
395 | LIB_OBJS += $(OUTPUT)util/bitmap.o | 404 | LIB_OBJS += $(OUTPUT)util/bitmap.o |
@@ -404,15 +413,16 @@ LIB_OBJS += $(OUTPUT)util/top.o | |||
404 | LIB_OBJS += $(OUTPUT)util/usage.o | 413 | LIB_OBJS += $(OUTPUT)util/usage.o |
405 | LIB_OBJS += $(OUTPUT)util/wrapper.o | 414 | LIB_OBJS += $(OUTPUT)util/wrapper.o |
406 | LIB_OBJS += $(OUTPUT)util/sigchain.o | 415 | LIB_OBJS += $(OUTPUT)util/sigchain.o |
416 | LIB_OBJS += $(OUTPUT)util/dso.o | ||
407 | LIB_OBJS += $(OUTPUT)util/symbol.o | 417 | LIB_OBJS += $(OUTPUT)util/symbol.o |
408 | LIB_OBJS += $(OUTPUT)util/symbol-elf.o | 418 | LIB_OBJS += $(OUTPUT)util/symbol-elf.o |
409 | LIB_OBJS += $(OUTPUT)util/dso-test-data.o | ||
410 | LIB_OBJS += $(OUTPUT)util/color.o | 419 | LIB_OBJS += $(OUTPUT)util/color.o |
411 | LIB_OBJS += $(OUTPUT)util/pager.o | 420 | LIB_OBJS += $(OUTPUT)util/pager.o |
412 | LIB_OBJS += $(OUTPUT)util/header.o | 421 | LIB_OBJS += $(OUTPUT)util/header.o |
413 | LIB_OBJS += $(OUTPUT)util/callchain.o | 422 | LIB_OBJS += $(OUTPUT)util/callchain.o |
414 | LIB_OBJS += $(OUTPUT)util/values.o | 423 | LIB_OBJS += $(OUTPUT)util/values.o |
415 | LIB_OBJS += $(OUTPUT)util/debug.o | 424 | LIB_OBJS += $(OUTPUT)util/debug.o |
425 | LIB_OBJS += $(OUTPUT)util/machine.o | ||
416 | LIB_OBJS += $(OUTPUT)util/map.o | 426 | LIB_OBJS += $(OUTPUT)util/map.o |
417 | LIB_OBJS += $(OUTPUT)util/pstack.o | 427 | LIB_OBJS += $(OUTPUT)util/pstack.o |
418 | LIB_OBJS += $(OUTPUT)util/session.o | 428 | LIB_OBJS += $(OUTPUT)util/session.o |
@@ -440,10 +450,29 @@ LIB_OBJS += $(OUTPUT)util/intlist.o | |||
440 | LIB_OBJS += $(OUTPUT)util/vdso.o | 450 | LIB_OBJS += $(OUTPUT)util/vdso.o |
441 | LIB_OBJS += $(OUTPUT)util/stat.o | 451 | LIB_OBJS += $(OUTPUT)util/stat.o |
442 | 452 | ||
453 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
443 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 454 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
455 | LIB_OBJS += $(OUTPUT)ui/progress.o | ||
444 | LIB_OBJS += $(OUTPUT)ui/hist.o | 456 | LIB_OBJS += $(OUTPUT)ui/hist.o |
445 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o | 457 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o |
446 | 458 | ||
459 | LIB_OBJS += $(OUTPUT)arch/common.o | ||
460 | |||
461 | LIB_OBJS += $(OUTPUT)tests/parse-events.o | ||
462 | LIB_OBJS += $(OUTPUT)tests/dso-data.o | ||
463 | LIB_OBJS += $(OUTPUT)tests/attr.o | ||
464 | LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o | ||
465 | LIB_OBJS += $(OUTPUT)tests/open-syscall.o | ||
466 | LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o | ||
467 | LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o | ||
468 | LIB_OBJS += $(OUTPUT)tests/mmap-basic.o | ||
469 | LIB_OBJS += $(OUTPUT)tests/perf-record.o | ||
470 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o | ||
471 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | ||
472 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | ||
473 | LIB_OBJS += $(OUTPUT)tests/pmu.o | ||
474 | LIB_OBJS += $(OUTPUT)tests/util.o | ||
475 | |||
447 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 476 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
448 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 477 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
449 | # Benchmark modules | 478 | # Benchmark modules |
@@ -473,8 +502,8 @@ BUILTIN_OBJS += $(OUTPUT)builtin-probe.o | |||
473 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o | 502 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o |
474 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | 503 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o |
475 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | 504 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o |
476 | BUILTIN_OBJS += $(OUTPUT)builtin-test.o | ||
477 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | 505 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o |
506 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o | ||
478 | 507 | ||
479 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) | 508 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) |
480 | 509 | ||
@@ -495,18 +524,33 @@ ifdef NO_LIBELF | |||
495 | NO_LIBUNWIND := 1 | 524 | NO_LIBUNWIND := 1 |
496 | else | 525 | else |
497 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | 526 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
498 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) | 527 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) |
499 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) | 528 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) |
500 | ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) | 529 | ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) |
501 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | 530 | LIBC_SUPPORT := 1 |
502 | else | 531 | endif |
532 | ifeq ($(BIONIC),1) | ||
533 | LIBC_SUPPORT := 1 | ||
534 | endif | ||
535 | ifeq ($(LIBC_SUPPORT),1) | ||
536 | msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); | ||
537 | |||
503 | NO_LIBELF := 1 | 538 | NO_LIBELF := 1 |
504 | NO_DWARF := 1 | 539 | NO_DWARF := 1 |
505 | NO_DEMANGLE := 1 | 540 | NO_DEMANGLE := 1 |
541 | else | ||
542 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
506 | endif | 543 | endif |
507 | else | 544 | else |
508 | FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) | 545 | # for linking with debug library, run like: |
509 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) | 546 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ |
547 | ifdef LIBDW_DIR | ||
548 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include | ||
549 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib | ||
550 | endif | ||
551 | |||
552 | FLAGS_DWARF=$(ALL_CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | ||
553 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) | ||
510 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | 554 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); |
511 | NO_DWARF := 1 | 555 | NO_DWARF := 1 |
512 | endif # Dwarf support | 556 | endif # Dwarf support |
@@ -522,7 +566,7 @@ ifdef LIBUNWIND_DIR | |||
522 | endif | 566 | endif |
523 | 567 | ||
524 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) | 568 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) |
525 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y) | 569 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) |
526 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); | 570 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); |
527 | NO_LIBUNWIND := 1 | 571 | NO_LIBUNWIND := 1 |
528 | endif # Libunwind support | 572 | endif # Libunwind support |
@@ -551,7 +595,8 @@ LIB_OBJS += $(OUTPUT)util/symbol-minimal.o | |||
551 | else # NO_LIBELF | 595 | else # NO_LIBELF |
552 | BASIC_CFLAGS += -DLIBELF_SUPPORT | 596 | BASIC_CFLAGS += -DLIBELF_SUPPORT |
553 | 597 | ||
554 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) | 598 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
599 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | ||
555 | BASIC_CFLAGS += -DLIBELF_MMAP | 600 | BASIC_CFLAGS += -DLIBELF_MMAP |
556 | endif | 601 | endif |
557 | 602 | ||
@@ -559,7 +604,8 @@ ifndef NO_DWARF | |||
559 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | 604 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) |
560 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | 605 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); |
561 | else | 606 | else |
562 | BASIC_CFLAGS += -DDWARF_SUPPORT | 607 | BASIC_CFLAGS := -DDWARF_SUPPORT $(LIBDW_CFLAGS) $(BASIC_CFLAGS) |
608 | BASIC_LDFLAGS := $(LIBDW_LDFLAGS) $(BASIC_LDFLAGS) | ||
563 | EXTLIBS += -lelf -ldw | 609 | EXTLIBS += -lelf -ldw |
564 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 610 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
565 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | 611 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o |
@@ -577,7 +623,7 @@ endif | |||
577 | 623 | ||
578 | ifndef NO_LIBAUDIT | 624 | ifndef NO_LIBAUDIT |
579 | FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit | 625 | FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit |
580 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y) | 626 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) |
581 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); | 627 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); |
582 | else | 628 | else |
583 | BASIC_CFLAGS += -DLIBAUDIT_SUPPORT | 629 | BASIC_CFLAGS += -DLIBAUDIT_SUPPORT |
@@ -588,23 +634,23 @@ endif | |||
588 | 634 | ||
589 | ifndef NO_NEWT | 635 | ifndef NO_NEWT |
590 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt | 636 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt |
591 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) | 637 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y) |
592 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 638 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
593 | else | 639 | else |
594 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 640 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h |
595 | BASIC_CFLAGS += -I/usr/include/slang | 641 | BASIC_CFLAGS += -I/usr/include/slang |
596 | BASIC_CFLAGS += -DNEWT_SUPPORT | 642 | BASIC_CFLAGS += -DNEWT_SUPPORT |
597 | EXTLIBS += -lnewt -lslang | 643 | EXTLIBS += -lnewt -lslang |
598 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
599 | LIB_OBJS += $(OUTPUT)ui/browser.o | 644 | LIB_OBJS += $(OUTPUT)ui/browser.o |
600 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o | 645 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o |
601 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o | 646 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o |
602 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o | 647 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o |
603 | LIB_OBJS += $(OUTPUT)ui/progress.o | 648 | LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o |
604 | LIB_OBJS += $(OUTPUT)ui/util.o | 649 | LIB_OBJS += $(OUTPUT)ui/util.o |
605 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o | 650 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o |
606 | LIB_OBJS += $(OUTPUT)ui/tui/util.o | 651 | LIB_OBJS += $(OUTPUT)ui/tui/util.o |
607 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o | 652 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o |
653 | LIB_OBJS += $(OUTPUT)ui/tui/progress.o | ||
608 | LIB_H += ui/browser.h | 654 | LIB_H += ui/browser.h |
609 | LIB_H += ui/browsers/map.h | 655 | LIB_H += ui/browsers/map.h |
610 | LIB_H += ui/keysyms.h | 656 | LIB_H += ui/keysyms.h |
@@ -617,10 +663,10 @@ endif | |||
617 | 663 | ||
618 | ifndef NO_GTK2 | 664 | ifndef NO_GTK2 |
619 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) | 665 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) |
620 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) | 666 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) |
621 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | 667 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); |
622 | else | 668 | else |
623 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y) | 669 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) |
624 | BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR | 670 | BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR |
625 | endif | 671 | endif |
626 | BASIC_CFLAGS += -DGTK2_SUPPORT | 672 | BASIC_CFLAGS += -DGTK2_SUPPORT |
@@ -630,9 +676,9 @@ ifndef NO_GTK2 | |||
630 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o | 676 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o |
631 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o | 677 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o |
632 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o | 678 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o |
679 | LIB_OBJS += $(OUTPUT)ui/gtk/progress.o | ||
633 | # Make sure that it'd be included only once. | 680 | # Make sure that it'd be included only once. |
634 | ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),) | 681 | ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),) |
635 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
636 | LIB_OBJS += $(OUTPUT)ui/util.o | 682 | LIB_OBJS += $(OUTPUT)ui/util.o |
637 | endif | 683 | endif |
638 | endif | 684 | endif |
@@ -647,7 +693,7 @@ else | |||
647 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | 693 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` |
648 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | 694 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) |
649 | 695 | ||
650 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) | 696 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) |
651 | BASIC_CFLAGS += -DNO_LIBPERL | 697 | BASIC_CFLAGS += -DNO_LIBPERL |
652 | else | 698 | else |
653 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) | 699 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) |
@@ -701,11 +747,11 @@ else | |||
701 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | 747 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) |
702 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | 748 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) |
703 | 749 | ||
704 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) | 750 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) |
705 | $(call disable-python,Python.h (for Python 2.x)) | 751 | $(call disable-python,Python.h (for Python 2.x)) |
706 | else | 752 | else |
707 | 753 | ||
708 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED)),y) | 754 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) |
709 | $(warning Python 3 is not yet supported; please set) | 755 | $(warning Python 3 is not yet supported; please set) |
710 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) | 756 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) |
711 | $(warning If you also have Python 2 installed, then) | 757 | $(warning If you also have Python 2 installed, then) |
@@ -739,22 +785,22 @@ else | |||
739 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 785 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
740 | else | 786 | else |
741 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd | 787 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd |
742 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) | 788 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) |
743 | ifeq ($(has_bfd),y) | 789 | ifeq ($(has_bfd),y) |
744 | EXTLIBS += -lbfd | 790 | EXTLIBS += -lbfd |
745 | else | 791 | else |
746 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | 792 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty |
747 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) | 793 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) |
748 | ifeq ($(has_bfd_iberty),y) | 794 | ifeq ($(has_bfd_iberty),y) |
749 | EXTLIBS += -lbfd -liberty | 795 | EXTLIBS += -lbfd -liberty |
750 | else | 796 | else |
751 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | 797 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz |
752 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) | 798 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) |
753 | ifeq ($(has_bfd_iberty_z),y) | 799 | ifeq ($(has_bfd_iberty_z),y) |
754 | EXTLIBS += -lbfd -liberty -lz | 800 | EXTLIBS += -lbfd -liberty -lz |
755 | else | 801 | else |
756 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty | 802 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty |
757 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) | 803 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) |
758 | ifeq ($(has_cplus_demangle),y) | 804 | ifeq ($(has_cplus_demangle),y) |
759 | EXTLIBS += -liberty | 805 | EXTLIBS += -liberty |
760 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 806 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
@@ -776,13 +822,19 @@ ifeq ($(NO_PERF_REGS),0) | |||
776 | endif | 822 | endif |
777 | 823 | ||
778 | ifndef NO_STRLCPY | 824 | ifndef NO_STRLCPY |
779 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),),y) | 825 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) |
780 | BASIC_CFLAGS += -DHAVE_STRLCPY | 826 | BASIC_CFLAGS += -DHAVE_STRLCPY |
781 | endif | 827 | endif |
782 | endif | 828 | endif |
783 | 829 | ||
830 | ifndef NO_ON_EXIT | ||
831 | ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) | ||
832 | BASIC_CFLAGS += -DHAVE_ON_EXIT | ||
833 | endif | ||
834 | endif | ||
835 | |||
784 | ifndef NO_BACKTRACE | 836 | ifndef NO_BACKTRACE |
785 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),),y) | 837 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) |
786 | BASIC_CFLAGS += -DBACKTRACE_SUPPORT | 838 | BASIC_CFLAGS += -DBACKTRACE_SUPPORT |
787 | endif | 839 | endif |
788 | endif | 840 | endif |
@@ -891,10 +943,14 @@ $(OUTPUT)%.s: %.S | |||
891 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 943 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
892 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 944 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
893 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ | 945 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ |
894 | '-DBINDIR="$(bindir_relative_SQ)"' \ | ||
895 | '-DPREFIX="$(prefix_SQ)"' \ | 946 | '-DPREFIX="$(prefix_SQ)"' \ |
896 | $< | 947 | $< |
897 | 948 | ||
949 | $(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS | ||
950 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | ||
951 | '-DBINDIR="$(bindir_SQ)"' \ | ||
952 | $< | ||
953 | |||
898 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 954 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
899 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 955 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
900 | 956 | ||
@@ -910,6 +966,9 @@ $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | |||
910 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS | 966 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS |
911 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 967 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
912 | 968 | ||
969 | $(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS | ||
970 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
971 | |||
913 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 972 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
914 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 973 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
915 | 974 | ||
@@ -981,20 +1040,15 @@ help: | |||
981 | @echo 'Perf maintainer targets:' | 1040 | @echo 'Perf maintainer targets:' |
982 | @echo ' clean - clean all binary objects and build output' | 1041 | @echo ' clean - clean all binary objects and build output' |
983 | 1042 | ||
984 | doc: | ||
985 | $(MAKE) -C Documentation all | ||
986 | |||
987 | man: | ||
988 | $(MAKE) -C Documentation man | ||
989 | 1043 | ||
990 | html: | 1044 | DOC_TARGETS := doc man html info pdf |
991 | $(MAKE) -C Documentation html | ||
992 | 1045 | ||
993 | info: | 1046 | INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man |
994 | $(MAKE) -C Documentation info | 1047 | INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html |
995 | 1048 | ||
996 | pdf: | 1049 | # 'make doc' should call 'make -C Documentation all' |
997 | $(MAKE) -C Documentation pdf | 1050 | $(DOC_TARGETS): |
1051 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) | ||
998 | 1052 | ||
999 | TAGS: | 1053 | TAGS: |
1000 | $(RM) TAGS | 1054 | $(RM) TAGS |
@@ -1045,7 +1099,7 @@ perfexec_instdir = $(prefix)/$(perfexecdir) | |||
1045 | endif | 1099 | endif |
1046 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | 1100 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) |
1047 | 1101 | ||
1048 | install: all | 1102 | install: all try-install-man |
1049 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 1103 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
1050 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 1104 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
1051 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 1105 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
@@ -1061,33 +1115,17 @@ install: all | |||
1061 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 1115 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
1062 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' | 1116 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' |
1063 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' | 1117 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' |
1118 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | ||
1119 | $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | ||
1120 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | ||
1121 | $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | ||
1064 | 1122 | ||
1065 | install-python_ext: | 1123 | install-python_ext: |
1066 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' | 1124 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' |
1067 | 1125 | ||
1068 | install-doc: | 1126 | # 'make install-doc' should call 'make -C Documentation install' |
1069 | $(MAKE) -C Documentation install | 1127 | $(INSTALL_DOC_TARGETS): |
1070 | 1128 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=) | |
1071 | install-man: | ||
1072 | $(MAKE) -C Documentation install-man | ||
1073 | |||
1074 | install-html: | ||
1075 | $(MAKE) -C Documentation install-html | ||
1076 | |||
1077 | install-info: | ||
1078 | $(MAKE) -C Documentation install-info | ||
1079 | |||
1080 | install-pdf: | ||
1081 | $(MAKE) -C Documentation install-pdf | ||
1082 | |||
1083 | quick-install-doc: | ||
1084 | $(MAKE) -C Documentation quick-install | ||
1085 | |||
1086 | quick-install-man: | ||
1087 | $(MAKE) -C Documentation quick-install-man | ||
1088 | |||
1089 | quick-install-html: | ||
1090 | $(MAKE) -C Documentation quick-install-html | ||
1091 | 1129 | ||
1092 | ### Cleaning rules | 1130 | ### Cleaning rules |
1093 | 1131 | ||
@@ -1095,7 +1133,7 @@ clean: $(LIBTRACEEVENT)-clean | |||
1095 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) | 1133 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
1096 | $(RM) $(ALL_PROGRAMS) perf | 1134 | $(RM) $(ALL_PROGRAMS) perf |
1097 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 1135 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
1098 | $(MAKE) -C Documentation/ clean | 1136 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
1099 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS | 1137 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
1100 | $(RM) $(OUTPUT)util/*-bison* | 1138 | $(RM) $(OUTPUT)util/*-bison* |
1101 | $(RM) $(OUTPUT)util/*-flex* | 1139 | $(RM) $(OUTPUT)util/*-flex* |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c new file mode 100644 index 000000000000..3e975cb6232e --- /dev/null +++ b/tools/perf/arch/common.c | |||
@@ -0,0 +1,211 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/utsname.h> | ||
3 | #include "common.h" | ||
4 | #include "../util/debug.h" | ||
5 | |||
6 | const char *const arm_triplets[] = { | ||
7 | "arm-eabi-", | ||
8 | "arm-linux-androideabi-", | ||
9 | "arm-unknown-linux-", | ||
10 | "arm-unknown-linux-gnu-", | ||
11 | "arm-unknown-linux-gnueabi-", | ||
12 | NULL | ||
13 | }; | ||
14 | |||
15 | const char *const powerpc_triplets[] = { | ||
16 | "powerpc-unknown-linux-gnu-", | ||
17 | "powerpc64-unknown-linux-gnu-", | ||
18 | NULL | ||
19 | }; | ||
20 | |||
21 | const char *const s390_triplets[] = { | ||
22 | "s390-ibm-linux-", | ||
23 | NULL | ||
24 | }; | ||
25 | |||
26 | const char *const sh_triplets[] = { | ||
27 | "sh-unknown-linux-gnu-", | ||
28 | "sh64-unknown-linux-gnu-", | ||
29 | NULL | ||
30 | }; | ||
31 | |||
32 | const char *const sparc_triplets[] = { | ||
33 | "sparc-unknown-linux-gnu-", | ||
34 | "sparc64-unknown-linux-gnu-", | ||
35 | NULL | ||
36 | }; | ||
37 | |||
38 | const char *const x86_triplets[] = { | ||
39 | "x86_64-pc-linux-gnu-", | ||
40 | "x86_64-unknown-linux-gnu-", | ||
41 | "i686-pc-linux-gnu-", | ||
42 | "i586-pc-linux-gnu-", | ||
43 | "i486-pc-linux-gnu-", | ||
44 | "i386-pc-linux-gnu-", | ||
45 | "i686-linux-android-", | ||
46 | "i686-android-linux-", | ||
47 | NULL | ||
48 | }; | ||
49 | |||
50 | const char *const mips_triplets[] = { | ||
51 | "mips-unknown-linux-gnu-", | ||
52 | "mipsel-linux-android-", | ||
53 | NULL | ||
54 | }; | ||
55 | |||
56 | static bool lookup_path(char *name) | ||
57 | { | ||
58 | bool found = false; | ||
59 | char *path, *tmp; | ||
60 | char buf[PATH_MAX]; | ||
61 | char *env = getenv("PATH"); | ||
62 | |||
63 | if (!env) | ||
64 | return false; | ||
65 | |||
66 | env = strdup(env); | ||
67 | if (!env) | ||
68 | return false; | ||
69 | |||
70 | path = strtok_r(env, ":", &tmp); | ||
71 | while (path) { | ||
72 | scnprintf(buf, sizeof(buf), "%s/%s", path, name); | ||
73 | if (access(buf, F_OK) == 0) { | ||
74 | found = true; | ||
75 | break; | ||
76 | } | ||
77 | path = strtok_r(NULL, ":", &tmp); | ||
78 | } | ||
79 | free(env); | ||
80 | return found; | ||
81 | } | ||
82 | |||
83 | static int lookup_triplets(const char *const *triplets, const char *name) | ||
84 | { | ||
85 | int i; | ||
86 | char buf[PATH_MAX]; | ||
87 | |||
88 | for (i = 0; triplets[i] != NULL; i++) { | ||
89 | scnprintf(buf, sizeof(buf), "%s%s", triplets[i], name); | ||
90 | if (lookup_path(buf)) | ||
91 | return i; | ||
92 | } | ||
93 | return -1; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Return architecture name in a normalized form. | ||
98 | * The conversion logic comes from the Makefile. | ||
99 | */ | ||
100 | static const char *normalize_arch(char *arch) | ||
101 | { | ||
102 | if (!strcmp(arch, "x86_64")) | ||
103 | return "x86"; | ||
104 | if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6') | ||
105 | return "x86"; | ||
106 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) | ||
107 | return "sparc"; | ||
108 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) | ||
109 | return "arm"; | ||
110 | if (!strncmp(arch, "s390", 4)) | ||
111 | return "s390"; | ||
112 | if (!strncmp(arch, "parisc", 6)) | ||
113 | return "parisc"; | ||
114 | if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3)) | ||
115 | return "powerpc"; | ||
116 | if (!strncmp(arch, "mips", 4)) | ||
117 | return "mips"; | ||
118 | if (!strncmp(arch, "sh", 2) && isdigit(arch[2])) | ||
119 | return "sh"; | ||
120 | |||
121 | return arch; | ||
122 | } | ||
123 | |||
124 | static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | ||
125 | const char *name, | ||
126 | const char **path) | ||
127 | { | ||
128 | int idx; | ||
129 | const char *arch, *cross_env; | ||
130 | struct utsname uts; | ||
131 | const char *const *path_list; | ||
132 | char *buf = NULL; | ||
133 | |||
134 | arch = normalize_arch(env->arch); | ||
135 | |||
136 | if (uname(&uts) < 0) | ||
137 | goto out; | ||
138 | |||
139 | /* | ||
140 | * We don't need to try to find objdump path for native system. | ||
141 | * Just use default binutils path (e.g.: "objdump"). | ||
142 | */ | ||
143 | if (!strcmp(normalize_arch(uts.machine), arch)) | ||
144 | goto out; | ||
145 | |||
146 | cross_env = getenv("CROSS_COMPILE"); | ||
147 | if (cross_env) { | ||
148 | if (asprintf(&buf, "%s%s", cross_env, name) < 0) | ||
149 | goto out_error; | ||
150 | if (buf[0] == '/') { | ||
151 | if (access(buf, F_OK) == 0) | ||
152 | goto out; | ||
153 | goto out_error; | ||
154 | } | ||
155 | if (lookup_path(buf)) | ||
156 | goto out; | ||
157 | free(buf); | ||
158 | } | ||
159 | |||
160 | if (!strcmp(arch, "arm")) | ||
161 | path_list = arm_triplets; | ||
162 | else if (!strcmp(arch, "powerpc")) | ||
163 | path_list = powerpc_triplets; | ||
164 | else if (!strcmp(arch, "sh")) | ||
165 | path_list = sh_triplets; | ||
166 | else if (!strcmp(arch, "s390")) | ||
167 | path_list = s390_triplets; | ||
168 | else if (!strcmp(arch, "sparc")) | ||
169 | path_list = sparc_triplets; | ||
170 | else if (!strcmp(arch, "x86")) | ||
171 | path_list = x86_triplets; | ||
172 | else if (!strcmp(arch, "mips")) | ||
173 | path_list = mips_triplets; | ||
174 | else { | ||
175 | ui__error("binutils for %s not supported.\n", arch); | ||
176 | goto out_error; | ||
177 | } | ||
178 | |||
179 | idx = lookup_triplets(path_list, name); | ||
180 | if (idx < 0) { | ||
181 | ui__error("Please install %s for %s.\n" | ||
182 | "You can add it to PATH, set CROSS_COMPILE or " | ||
183 | "override the default using --%s.\n", | ||
184 | name, arch, name); | ||
185 | goto out_error; | ||
186 | } | ||
187 | |||
188 | if (asprintf(&buf, "%s%s", path_list[idx], name) < 0) | ||
189 | goto out_error; | ||
190 | |||
191 | out: | ||
192 | *path = buf; | ||
193 | return 0; | ||
194 | out_error: | ||
195 | free(buf); | ||
196 | *path = NULL; | ||
197 | return -1; | ||
198 | } | ||
199 | |||
200 | int perf_session_env__lookup_objdump(struct perf_session_env *env) | ||
201 | { | ||
202 | /* | ||
203 | * For live mode, env->arch will be NULL and we can use | ||
204 | * the native objdump tool. | ||
205 | */ | ||
206 | if (env->arch == NULL) | ||
207 | return 0; | ||
208 | |||
209 | return perf_session_env__lookup_binutils_path(env, "objdump", | ||
210 | &objdump_path); | ||
211 | } | ||
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h new file mode 100644 index 000000000000..ede246eda9be --- /dev/null +++ b/tools/perf/arch/common.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef ARCH_PERF_COMMON_H | ||
2 | #define ARCH_PERF_COMMON_H | ||
3 | |||
4 | #include "../util/session.h" | ||
5 | |||
6 | extern const char *objdump_path; | ||
7 | |||
8 | int perf_session_env__lookup_objdump(struct perf_session_env *env); | ||
9 | |||
10 | #endif /* ARCH_PERF_COMMON_H */ | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9ea38540b873..dc870cf31b79 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -28,12 +28,12 @@ | |||
28 | #include "util/hist.h" | 28 | #include "util/hist.h" |
29 | #include "util/session.h" | 29 | #include "util/session.h" |
30 | #include "util/tool.h" | 30 | #include "util/tool.h" |
31 | #include "arch/common.h" | ||
31 | 32 | ||
32 | #include <linux/bitmap.h> | 33 | #include <linux/bitmap.h> |
33 | 34 | ||
34 | struct perf_annotate { | 35 | struct perf_annotate { |
35 | struct perf_tool tool; | 36 | struct perf_tool tool; |
36 | char const *input_name; | ||
37 | bool force, use_tui, use_stdio; | 37 | bool force, use_tui, use_stdio; |
38 | bool full_paths; | 38 | bool full_paths; |
39 | bool print_line; | 39 | bool print_line; |
@@ -139,7 +139,7 @@ find_next: | |||
139 | } | 139 | } |
140 | 140 | ||
141 | if (use_browser > 0) { | 141 | if (use_browser > 0) { |
142 | key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0); | 142 | key = hist_entry__tui_annotate(he, evidx, NULL); |
143 | switch (key) { | 143 | switch (key) { |
144 | case K_RIGHT: | 144 | case K_RIGHT: |
145 | next = rb_next(nd); | 145 | next = rb_next(nd); |
@@ -174,7 +174,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
174 | struct perf_evsel *pos; | 174 | struct perf_evsel *pos; |
175 | u64 total_nr_samples; | 175 | u64 total_nr_samples; |
176 | 176 | ||
177 | session = perf_session__new(ann->input_name, O_RDONLY, | 177 | session = perf_session__new(input_name, O_RDONLY, |
178 | ann->force, false, &ann->tool); | 178 | ann->force, false, &ann->tool); |
179 | if (session == NULL) | 179 | if (session == NULL) |
180 | return -ENOMEM; | 180 | return -ENOMEM; |
@@ -186,6 +186,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
186 | goto out_delete; | 186 | goto out_delete; |
187 | } | 187 | } |
188 | 188 | ||
189 | if (!objdump_path) { | ||
190 | ret = perf_session_env__lookup_objdump(&session->header.env); | ||
191 | if (ret) | ||
192 | goto out_delete; | ||
193 | } | ||
194 | |||
189 | ret = perf_session__process_events(session, &ann->tool); | 195 | ret = perf_session__process_events(session, &ann->tool); |
190 | if (ret) | 196 | if (ret) |
191 | goto out_delete; | 197 | goto out_delete; |
@@ -246,13 +252,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
246 | .sample = process_sample_event, | 252 | .sample = process_sample_event, |
247 | .mmap = perf_event__process_mmap, | 253 | .mmap = perf_event__process_mmap, |
248 | .comm = perf_event__process_comm, | 254 | .comm = perf_event__process_comm, |
249 | .fork = perf_event__process_task, | 255 | .exit = perf_event__process_exit, |
256 | .fork = perf_event__process_fork, | ||
250 | .ordered_samples = true, | 257 | .ordered_samples = true, |
251 | .ordering_requires_timestamps = true, | 258 | .ordering_requires_timestamps = true, |
252 | }, | 259 | }, |
253 | }; | 260 | }; |
254 | const struct option options[] = { | 261 | const struct option options[] = { |
255 | OPT_STRING('i', "input", &annotate.input_name, "file", | 262 | OPT_STRING('i', "input", &input_name, "file", |
256 | "input file name"), | 263 | "input file name"), |
257 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 264 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
258 | "only consider symbols in these dsos"), | 265 | "only consider symbols in these dsos"), |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index d37e077f4b14..fae8b250b2ca 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "util/header.h" | 13 | #include "util/header.h" |
14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
15 | #include "util/strlist.h" | 15 | #include "util/strlist.h" |
16 | #include "util/build-id.h" | ||
16 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
17 | 18 | ||
18 | static int build_id_cache__add_file(const char *filename, const char *debugdir) | 19 | static int build_id_cache__add_file(const char *filename, const char *debugdir) |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a0e94fffa03e..a82d99fec83e 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -44,8 +44,7 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) | |||
44 | return fprintf(fp, "%s\n", sbuild_id); | 44 | return fprintf(fp, "%s\n", sbuild_id); |
45 | } | 45 | } |
46 | 46 | ||
47 | static int perf_session__list_build_ids(const char *input_name, | 47 | static int perf_session__list_build_ids(bool force, bool with_hits) |
48 | bool force, bool with_hits) | ||
49 | { | 48 | { |
50 | struct perf_session *session; | 49 | struct perf_session *session; |
51 | 50 | ||
@@ -81,7 +80,6 @@ int cmd_buildid_list(int argc, const char **argv, | |||
81 | bool show_kernel = false; | 80 | bool show_kernel = false; |
82 | bool with_hits = false; | 81 | bool with_hits = false; |
83 | bool force = false; | 82 | bool force = false; |
84 | const char *input_name = NULL; | ||
85 | const struct option options[] = { | 83 | const struct option options[] = { |
86 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), | 84 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), |
87 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 85 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
@@ -101,5 +99,5 @@ int cmd_buildid_list(int argc, const char **argv, | |||
101 | if (show_kernel) | 99 | if (show_kernel) |
102 | return sysfs__fprintf_build_id(stdout); | 100 | return sysfs__fprintf_build_id(stdout); |
103 | 101 | ||
104 | return perf_session__list_build_ids(input_name, force, with_hits); | 102 | return perf_session__list_build_ids(force, with_hits); |
105 | } | 103 | } |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index a0b531c14b97..93b852f8a5d5 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -24,6 +24,228 @@ static char const *input_old = "perf.data.old", | |||
24 | static char diff__default_sort_order[] = "dso,symbol"; | 24 | static char diff__default_sort_order[] = "dso,symbol"; |
25 | static bool force; | 25 | static bool force; |
26 | static bool show_displacement; | 26 | static bool show_displacement; |
27 | static bool show_period; | ||
28 | static bool show_formula; | ||
29 | static bool show_baseline_only; | ||
30 | static bool sort_compute; | ||
31 | |||
32 | static s64 compute_wdiff_w1; | ||
33 | static s64 compute_wdiff_w2; | ||
34 | |||
35 | enum { | ||
36 | COMPUTE_DELTA, | ||
37 | COMPUTE_RATIO, | ||
38 | COMPUTE_WEIGHTED_DIFF, | ||
39 | COMPUTE_MAX, | ||
40 | }; | ||
41 | |||
42 | const char *compute_names[COMPUTE_MAX] = { | ||
43 | [COMPUTE_DELTA] = "delta", | ||
44 | [COMPUTE_RATIO] = "ratio", | ||
45 | [COMPUTE_WEIGHTED_DIFF] = "wdiff", | ||
46 | }; | ||
47 | |||
48 | static int compute; | ||
49 | |||
50 | static int setup_compute_opt_wdiff(char *opt) | ||
51 | { | ||
52 | char *w1_str = opt; | ||
53 | char *w2_str; | ||
54 | |||
55 | int ret = -EINVAL; | ||
56 | |||
57 | if (!opt) | ||
58 | goto out; | ||
59 | |||
60 | w2_str = strchr(opt, ','); | ||
61 | if (!w2_str) | ||
62 | goto out; | ||
63 | |||
64 | *w2_str++ = 0x0; | ||
65 | if (!*w2_str) | ||
66 | goto out; | ||
67 | |||
68 | compute_wdiff_w1 = strtol(w1_str, NULL, 10); | ||
69 | compute_wdiff_w2 = strtol(w2_str, NULL, 10); | ||
70 | |||
71 | if (!compute_wdiff_w1 || !compute_wdiff_w2) | ||
72 | goto out; | ||
73 | |||
74 | pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", | ||
75 | compute_wdiff_w1, compute_wdiff_w2); | ||
76 | |||
77 | ret = 0; | ||
78 | |||
79 | out: | ||
80 | if (ret) | ||
81 | pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | static int setup_compute_opt(char *opt) | ||
87 | { | ||
88 | if (compute == COMPUTE_WEIGHTED_DIFF) | ||
89 | return setup_compute_opt_wdiff(opt); | ||
90 | |||
91 | if (opt) { | ||
92 | pr_err("Failed: extra option specified '%s'", opt); | ||
93 | return -EINVAL; | ||
94 | } | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int setup_compute(const struct option *opt, const char *str, | ||
100 | int unset __maybe_unused) | ||
101 | { | ||
102 | int *cp = (int *) opt->value; | ||
103 | char *cstr = (char *) str; | ||
104 | char buf[50]; | ||
105 | unsigned i; | ||
106 | char *option; | ||
107 | |||
108 | if (!str) { | ||
109 | *cp = COMPUTE_DELTA; | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | if (*str == '+') { | ||
114 | sort_compute = true; | ||
115 | cstr = (char *) ++str; | ||
116 | if (!*str) | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | option = strchr(str, ':'); | ||
121 | if (option) { | ||
122 | unsigned len = option++ - str; | ||
123 | |||
124 | /* | ||
125 | * The str data are not writeable, so we need | ||
126 | * to use another buffer. | ||
127 | */ | ||
128 | |||
129 | /* No option value is longer. */ | ||
130 | if (len >= sizeof(buf)) | ||
131 | return -EINVAL; | ||
132 | |||
133 | strncpy(buf, str, len); | ||
134 | buf[len] = 0x0; | ||
135 | cstr = buf; | ||
136 | } | ||
137 | |||
138 | for (i = 0; i < COMPUTE_MAX; i++) | ||
139 | if (!strcmp(cstr, compute_names[i])) { | ||
140 | *cp = i; | ||
141 | return setup_compute_opt(option); | ||
142 | } | ||
143 | |||
144 | pr_err("Failed: '%s' is not computation method " | ||
145 | "(use 'delta','ratio' or 'wdiff')\n", str); | ||
146 | return -EINVAL; | ||
147 | } | ||
148 | |||
149 | static double get_period_percent(struct hist_entry *he, u64 period) | ||
150 | { | ||
151 | u64 total = he->hists->stats.total_period; | ||
152 | return (period * 100.0) / total; | ||
153 | } | ||
154 | |||
155 | double perf_diff__compute_delta(struct hist_entry *he) | ||
156 | { | ||
157 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
158 | double new_percent = get_period_percent(he, he->stat.period); | ||
159 | double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; | ||
160 | |||
161 | he->diff.period_ratio_delta = new_percent - old_percent; | ||
162 | he->diff.computed = true; | ||
163 | return he->diff.period_ratio_delta; | ||
164 | } | ||
165 | |||
166 | double perf_diff__compute_ratio(struct hist_entry *he) | ||
167 | { | ||
168 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
169 | double new_period = he->stat.period; | ||
170 | double old_period = pair ? pair->stat.period : 0; | ||
171 | |||
172 | he->diff.computed = true; | ||
173 | he->diff.period_ratio = pair ? (new_period / old_period) : 0; | ||
174 | return he->diff.period_ratio; | ||
175 | } | ||
176 | |||
177 | s64 perf_diff__compute_wdiff(struct hist_entry *he) | ||
178 | { | ||
179 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
180 | u64 new_period = he->stat.period; | ||
181 | u64 old_period = pair ? pair->stat.period : 0; | ||
182 | |||
183 | he->diff.computed = true; | ||
184 | |||
185 | if (!pair) | ||
186 | he->diff.wdiff = 0; | ||
187 | else | ||
188 | he->diff.wdiff = new_period * compute_wdiff_w2 - | ||
189 | old_period * compute_wdiff_w1; | ||
190 | |||
191 | return he->diff.wdiff; | ||
192 | } | ||
193 | |||
194 | static int formula_delta(struct hist_entry *he, char *buf, size_t size) | ||
195 | { | ||
196 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
197 | |||
198 | if (!pair) | ||
199 | return -1; | ||
200 | |||
201 | return scnprintf(buf, size, | ||
202 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | ||
203 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | ||
204 | he->stat.period, he->hists->stats.total_period, | ||
205 | pair->stat.period, pair->hists->stats.total_period); | ||
206 | } | ||
207 | |||
208 | static int formula_ratio(struct hist_entry *he, char *buf, size_t size) | ||
209 | { | ||
210 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
211 | double new_period = he->stat.period; | ||
212 | double old_period = pair ? pair->stat.period : 0; | ||
213 | |||
214 | if (!pair) | ||
215 | return -1; | ||
216 | |||
217 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); | ||
218 | } | ||
219 | |||
220 | static int formula_wdiff(struct hist_entry *he, char *buf, size_t size) | ||
221 | { | ||
222 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
223 | u64 new_period = he->stat.period; | ||
224 | u64 old_period = pair ? pair->stat.period : 0; | ||
225 | |||
226 | if (!pair) | ||
227 | return -1; | ||
228 | |||
229 | return scnprintf(buf, size, | ||
230 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", | ||
231 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); | ||
232 | } | ||
233 | |||
234 | int perf_diff__formula(char *buf, size_t size, struct hist_entry *he) | ||
235 | { | ||
236 | switch (compute) { | ||
237 | case COMPUTE_DELTA: | ||
238 | return formula_delta(he, buf, size); | ||
239 | case COMPUTE_RATIO: | ||
240 | return formula_ratio(he, buf, size); | ||
241 | case COMPUTE_WEIGHTED_DIFF: | ||
242 | return formula_wdiff(he, buf, size); | ||
243 | default: | ||
244 | BUG_ON(1); | ||
245 | } | ||
246 | |||
247 | return -1; | ||
248 | } | ||
27 | 249 | ||
28 | static int hists__add_entry(struct hists *self, | 250 | static int hists__add_entry(struct hists *self, |
29 | struct addr_location *al, u64 period) | 251 | struct addr_location *al, u64 period) |
@@ -47,7 +269,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
47 | return -1; | 269 | return -1; |
48 | } | 270 | } |
49 | 271 | ||
50 | if (al.filtered || al.sym == NULL) | 272 | if (al.filtered) |
51 | return 0; | 273 | return 0; |
52 | 274 | ||
53 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 275 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { |
@@ -63,8 +285,8 @@ static struct perf_tool tool = { | |||
63 | .sample = diff__process_sample_event, | 285 | .sample = diff__process_sample_event, |
64 | .mmap = perf_event__process_mmap, | 286 | .mmap = perf_event__process_mmap, |
65 | .comm = perf_event__process_comm, | 287 | .comm = perf_event__process_comm, |
66 | .exit = perf_event__process_task, | 288 | .exit = perf_event__process_exit, |
67 | .fork = perf_event__process_task, | 289 | .fork = perf_event__process_fork, |
68 | .lost = perf_event__process_lost, | 290 | .lost = perf_event__process_lost, |
69 | .ordered_samples = true, | 291 | .ordered_samples = true, |
70 | .ordering_requires_timestamps = true, | 292 | .ordering_requires_timestamps = true, |
@@ -112,36 +334,6 @@ static void hists__name_resort(struct hists *self, bool sort) | |||
112 | self->entries = tmp; | 334 | self->entries = tmp; |
113 | } | 335 | } |
114 | 336 | ||
115 | static struct hist_entry *hists__find_entry(struct hists *self, | ||
116 | struct hist_entry *he) | ||
117 | { | ||
118 | struct rb_node *n = self->entries.rb_node; | ||
119 | |||
120 | while (n) { | ||
121 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | ||
122 | int64_t cmp = hist_entry__cmp(he, iter); | ||
123 | |||
124 | if (cmp < 0) | ||
125 | n = n->rb_left; | ||
126 | else if (cmp > 0) | ||
127 | n = n->rb_right; | ||
128 | else | ||
129 | return iter; | ||
130 | } | ||
131 | |||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static void hists__match(struct hists *older, struct hists *newer) | ||
136 | { | ||
137 | struct rb_node *nd; | ||
138 | |||
139 | for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) { | ||
140 | struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); | ||
141 | pos->pair = hists__find_entry(older, pos); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | 337 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, |
146 | struct perf_evlist *evlist) | 338 | struct perf_evlist *evlist) |
147 | { | 339 | { |
@@ -172,6 +364,144 @@ static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) | |||
172 | } | 364 | } |
173 | } | 365 | } |
174 | 366 | ||
367 | static void hists__baseline_only(struct hists *hists) | ||
368 | { | ||
369 | struct rb_node *next = rb_first(&hists->entries); | ||
370 | |||
371 | while (next != NULL) { | ||
372 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | ||
373 | |||
374 | next = rb_next(&he->rb_node); | ||
375 | if (!hist_entry__next_pair(he)) { | ||
376 | rb_erase(&he->rb_node, &hists->entries); | ||
377 | hist_entry__free(he); | ||
378 | } | ||
379 | } | ||
380 | } | ||
381 | |||
382 | static void hists__precompute(struct hists *hists) | ||
383 | { | ||
384 | struct rb_node *next = rb_first(&hists->entries); | ||
385 | |||
386 | while (next != NULL) { | ||
387 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | ||
388 | |||
389 | next = rb_next(&he->rb_node); | ||
390 | |||
391 | switch (compute) { | ||
392 | case COMPUTE_DELTA: | ||
393 | perf_diff__compute_delta(he); | ||
394 | break; | ||
395 | case COMPUTE_RATIO: | ||
396 | perf_diff__compute_ratio(he); | ||
397 | break; | ||
398 | case COMPUTE_WEIGHTED_DIFF: | ||
399 | perf_diff__compute_wdiff(he); | ||
400 | break; | ||
401 | default: | ||
402 | BUG_ON(1); | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | |||
407 | static int64_t cmp_doubles(double l, double r) | ||
408 | { | ||
409 | if (l > r) | ||
410 | return -1; | ||
411 | else if (l < r) | ||
412 | return 1; | ||
413 | else | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static int64_t | ||
418 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | ||
419 | int c) | ||
420 | { | ||
421 | switch (c) { | ||
422 | case COMPUTE_DELTA: | ||
423 | { | ||
424 | double l = left->diff.period_ratio_delta; | ||
425 | double r = right->diff.period_ratio_delta; | ||
426 | |||
427 | return cmp_doubles(l, r); | ||
428 | } | ||
429 | case COMPUTE_RATIO: | ||
430 | { | ||
431 | double l = left->diff.period_ratio; | ||
432 | double r = right->diff.period_ratio; | ||
433 | |||
434 | return cmp_doubles(l, r); | ||
435 | } | ||
436 | case COMPUTE_WEIGHTED_DIFF: | ||
437 | { | ||
438 | s64 l = left->diff.wdiff; | ||
439 | s64 r = right->diff.wdiff; | ||
440 | |||
441 | return r - l; | ||
442 | } | ||
443 | default: | ||
444 | BUG_ON(1); | ||
445 | } | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static void insert_hist_entry_by_compute(struct rb_root *root, | ||
451 | struct hist_entry *he, | ||
452 | int c) | ||
453 | { | ||
454 | struct rb_node **p = &root->rb_node; | ||
455 | struct rb_node *parent = NULL; | ||
456 | struct hist_entry *iter; | ||
457 | |||
458 | while (*p != NULL) { | ||
459 | parent = *p; | ||
460 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
461 | if (hist_entry__cmp_compute(he, iter, c) < 0) | ||
462 | p = &(*p)->rb_left; | ||
463 | else | ||
464 | p = &(*p)->rb_right; | ||
465 | } | ||
466 | |||
467 | rb_link_node(&he->rb_node, parent, p); | ||
468 | rb_insert_color(&he->rb_node, root); | ||
469 | } | ||
470 | |||
471 | static void hists__compute_resort(struct hists *hists) | ||
472 | { | ||
473 | struct rb_root tmp = RB_ROOT; | ||
474 | struct rb_node *next = rb_first(&hists->entries); | ||
475 | |||
476 | while (next != NULL) { | ||
477 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); | ||
478 | |||
479 | next = rb_next(&he->rb_node); | ||
480 | |||
481 | rb_erase(&he->rb_node, &hists->entries); | ||
482 | insert_hist_entry_by_compute(&tmp, he, compute); | ||
483 | } | ||
484 | |||
485 | hists->entries = tmp; | ||
486 | } | ||
487 | |||
488 | static void hists__process(struct hists *old, struct hists *new) | ||
489 | { | ||
490 | hists__match(new, old); | ||
491 | |||
492 | if (show_baseline_only) | ||
493 | hists__baseline_only(new); | ||
494 | else | ||
495 | hists__link(new, old); | ||
496 | |||
497 | if (sort_compute) { | ||
498 | hists__precompute(new); | ||
499 | hists__compute_resort(new); | ||
500 | } | ||
501 | |||
502 | hists__fprintf(new, true, 0, 0, stdout); | ||
503 | } | ||
504 | |||
175 | static int __cmd_diff(void) | 505 | static int __cmd_diff(void) |
176 | { | 506 | { |
177 | int ret, i; | 507 | int ret, i; |
@@ -213,8 +543,7 @@ static int __cmd_diff(void) | |||
213 | 543 | ||
214 | first = false; | 544 | first = false; |
215 | 545 | ||
216 | hists__match(&evsel_old->hists, &evsel->hists); | 546 | hists__process(&evsel_old->hists, &evsel->hists); |
217 | hists__fprintf(&evsel->hists, true, 0, 0, stdout); | ||
218 | } | 547 | } |
219 | 548 | ||
220 | out_delete: | 549 | out_delete: |
@@ -235,6 +564,16 @@ static const struct option options[] = { | |||
235 | "be more verbose (show symbol address, etc)"), | 564 | "be more verbose (show symbol address, etc)"), |
236 | OPT_BOOLEAN('M', "displacement", &show_displacement, | 565 | OPT_BOOLEAN('M', "displacement", &show_displacement, |
237 | "Show position displacement relative to baseline"), | 566 | "Show position displacement relative to baseline"), |
567 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, | ||
568 | "Show only items with match in baseline"), | ||
569 | OPT_CALLBACK('c', "compute", &compute, | ||
570 | "delta,ratio,wdiff:w1,w2 (default delta)", | ||
571 | "Entries differential computation selection", | ||
572 | setup_compute), | ||
573 | OPT_BOOLEAN('p', "period", &show_period, | ||
574 | "Show period values."), | ||
575 | OPT_BOOLEAN('F', "formula", &show_formula, | ||
576 | "Show formula."), | ||
238 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 577 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
239 | "dump raw trace in ASCII"), | 578 | "dump raw trace in ASCII"), |
240 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 579 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
@@ -263,12 +602,36 @@ static void ui_init(void) | |||
263 | /* No overhead column. */ | 602 | /* No overhead column. */ |
264 | perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); | 603 | perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); |
265 | 604 | ||
266 | /* Display baseline/delta/displacement columns. */ | 605 | /* |
606 | * Display baseline/delta/ratio/displacement/ | ||
607 | * formula/periods columns. | ||
608 | */ | ||
267 | perf_hpp__column_enable(PERF_HPP__BASELINE, true); | 609 | perf_hpp__column_enable(PERF_HPP__BASELINE, true); |
268 | perf_hpp__column_enable(PERF_HPP__DELTA, true); | 610 | |
611 | switch (compute) { | ||
612 | case COMPUTE_DELTA: | ||
613 | perf_hpp__column_enable(PERF_HPP__DELTA, true); | ||
614 | break; | ||
615 | case COMPUTE_RATIO: | ||
616 | perf_hpp__column_enable(PERF_HPP__RATIO, true); | ||
617 | break; | ||
618 | case COMPUTE_WEIGHTED_DIFF: | ||
619 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF, true); | ||
620 | break; | ||
621 | default: | ||
622 | BUG_ON(1); | ||
623 | }; | ||
269 | 624 | ||
270 | if (show_displacement) | 625 | if (show_displacement) |
271 | perf_hpp__column_enable(PERF_HPP__DISPL, true); | 626 | perf_hpp__column_enable(PERF_HPP__DISPL, true); |
627 | |||
628 | if (show_formula) | ||
629 | perf_hpp__column_enable(PERF_HPP__FORMULA, true); | ||
630 | |||
631 | if (show_period) { | ||
632 | perf_hpp__column_enable(PERF_HPP__PERIOD, true); | ||
633 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE, true); | ||
634 | } | ||
272 | } | 635 | } |
273 | 636 | ||
274 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 637 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 997afb82691b..c20f1dcfb7e2 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
@@ -48,12 +48,12 @@ static int __if_print(bool *first, const char *field, u64 value) | |||
48 | 48 | ||
49 | #define if_print(field) __if_print(&first, #field, pos->attr.field) | 49 | #define if_print(field) __if_print(&first, #field, pos->attr.field) |
50 | 50 | ||
51 | static int __cmd_evlist(const char *input_name, struct perf_attr_details *details) | 51 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) |
52 | { | 52 | { |
53 | struct perf_session *session; | 53 | struct perf_session *session; |
54 | struct perf_evsel *pos; | 54 | struct perf_evsel *pos; |
55 | 55 | ||
56 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | 56 | session = perf_session__new(file_name, O_RDONLY, 0, false, NULL); |
57 | if (session == NULL) | 57 | if (session == NULL) |
58 | return -ENOMEM; | 58 | return -ENOMEM; |
59 | 59 | ||
@@ -111,7 +111,6 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail | |||
111 | int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) | 111 | int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) |
112 | { | 112 | { |
113 | struct perf_attr_details details = { .verbose = false, }; | 113 | struct perf_attr_details details = { .verbose = false, }; |
114 | const char *input_name = NULL; | ||
115 | const struct option options[] = { | 114 | const struct option options[] = { |
116 | OPT_STRING('i', "input", &input_name, "file", "Input file name"), | 115 | OPT_STRING('i', "input", &input_name, "file", "Input file name"), |
117 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), | 116 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 4688bea95c12..84ad6abe4258 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -8,33 +8,53 @@ | |||
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "perf.h" | 10 | #include "perf.h" |
11 | #include "util/color.h" | ||
12 | #include "util/evlist.h" | ||
13 | #include "util/evsel.h" | ||
11 | #include "util/session.h" | 14 | #include "util/session.h" |
12 | #include "util/tool.h" | 15 | #include "util/tool.h" |
13 | #include "util/debug.h" | 16 | #include "util/debug.h" |
17 | #include "util/build-id.h" | ||
14 | 18 | ||
15 | #include "util/parse-options.h" | 19 | #include "util/parse-options.h" |
16 | 20 | ||
21 | #include <linux/list.h> | ||
22 | |||
17 | struct perf_inject { | 23 | struct perf_inject { |
18 | struct perf_tool tool; | 24 | struct perf_tool tool; |
19 | bool build_ids; | 25 | bool build_ids; |
26 | bool sched_stat; | ||
27 | const char *input_name; | ||
28 | int pipe_output, | ||
29 | output; | ||
30 | u64 bytes_written; | ||
31 | struct list_head samples; | ||
32 | }; | ||
33 | |||
34 | struct event_entry { | ||
35 | struct list_head node; | ||
36 | u32 tid; | ||
37 | union perf_event event[0]; | ||
20 | }; | 38 | }; |
21 | 39 | ||
22 | static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused, | 40 | static int perf_event__repipe_synth(struct perf_tool *tool, |
23 | union perf_event *event, | 41 | union perf_event *event, |
24 | struct machine *machine __maybe_unused) | 42 | struct machine *machine __maybe_unused) |
25 | { | 43 | { |
44 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
26 | uint32_t size; | 45 | uint32_t size; |
27 | void *buf = event; | 46 | void *buf = event; |
28 | 47 | ||
29 | size = event->header.size; | 48 | size = event->header.size; |
30 | 49 | ||
31 | while (size) { | 50 | while (size) { |
32 | int ret = write(STDOUT_FILENO, buf, size); | 51 | int ret = write(inject->output, buf, size); |
33 | if (ret < 0) | 52 | if (ret < 0) |
34 | return -errno; | 53 | return -errno; |
35 | 54 | ||
36 | size -= ret; | 55 | size -= ret; |
37 | buf += ret; | 56 | buf += ret; |
57 | inject->bytes_written += ret; | ||
38 | } | 58 | } |
39 | 59 | ||
40 | return 0; | 60 | return 0; |
@@ -80,12 +100,25 @@ static int perf_event__repipe(struct perf_tool *tool, | |||
80 | return perf_event__repipe_synth(tool, event, machine); | 100 | return perf_event__repipe_synth(tool, event, machine); |
81 | } | 101 | } |
82 | 102 | ||
103 | typedef int (*inject_handler)(struct perf_tool *tool, | ||
104 | union perf_event *event, | ||
105 | struct perf_sample *sample, | ||
106 | struct perf_evsel *evsel, | ||
107 | struct machine *machine); | ||
108 | |||
83 | static int perf_event__repipe_sample(struct perf_tool *tool, | 109 | static int perf_event__repipe_sample(struct perf_tool *tool, |
84 | union perf_event *event, | 110 | union perf_event *event, |
85 | struct perf_sample *sample __maybe_unused, | 111 | struct perf_sample *sample, |
86 | struct perf_evsel *evsel __maybe_unused, | 112 | struct perf_evsel *evsel, |
87 | struct machine *machine) | 113 | struct machine *machine) |
88 | { | 114 | { |
115 | if (evsel->handler.func) { | ||
116 | inject_handler f = evsel->handler.func; | ||
117 | return f(tool, event, sample, evsel, machine); | ||
118 | } | ||
119 | |||
120 | build_id__mark_dso_hit(tool, event, sample, evsel, machine); | ||
121 | |||
89 | return perf_event__repipe_synth(tool, event, machine); | 122 | return perf_event__repipe_synth(tool, event, machine); |
90 | } | 123 | } |
91 | 124 | ||
@@ -102,14 +135,14 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, | |||
102 | return err; | 135 | return err; |
103 | } | 136 | } |
104 | 137 | ||
105 | static int perf_event__repipe_task(struct perf_tool *tool, | 138 | static int perf_event__repipe_fork(struct perf_tool *tool, |
106 | union perf_event *event, | 139 | union perf_event *event, |
107 | struct perf_sample *sample, | 140 | struct perf_sample *sample, |
108 | struct machine *machine) | 141 | struct machine *machine) |
109 | { | 142 | { |
110 | int err; | 143 | int err; |
111 | 144 | ||
112 | err = perf_event__process_task(tool, event, sample, machine); | 145 | err = perf_event__process_fork(tool, event, sample, machine); |
113 | perf_event__repipe(tool, event, sample, machine); | 146 | perf_event__repipe(tool, event, sample, machine); |
114 | 147 | ||
115 | return err; | 148 | return err; |
@@ -210,6 +243,80 @@ repipe: | |||
210 | return 0; | 243 | return 0; |
211 | } | 244 | } |
212 | 245 | ||
246 | static int perf_inject__sched_process_exit(struct perf_tool *tool, | ||
247 | union perf_event *event __maybe_unused, | ||
248 | struct perf_sample *sample, | ||
249 | struct perf_evsel *evsel __maybe_unused, | ||
250 | struct machine *machine __maybe_unused) | ||
251 | { | ||
252 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
253 | struct event_entry *ent; | ||
254 | |||
255 | list_for_each_entry(ent, &inject->samples, node) { | ||
256 | if (sample->tid == ent->tid) { | ||
257 | list_del_init(&ent->node); | ||
258 | free(ent); | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int perf_inject__sched_switch(struct perf_tool *tool, | ||
267 | union perf_event *event, | ||
268 | struct perf_sample *sample, | ||
269 | struct perf_evsel *evsel, | ||
270 | struct machine *machine) | ||
271 | { | ||
272 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
273 | struct event_entry *ent; | ||
274 | |||
275 | perf_inject__sched_process_exit(tool, event, sample, evsel, machine); | ||
276 | |||
277 | ent = malloc(event->header.size + sizeof(struct event_entry)); | ||
278 | if (ent == NULL) { | ||
279 | color_fprintf(stderr, PERF_COLOR_RED, | ||
280 | "Not enough memory to process sched switch event!"); | ||
281 | return -1; | ||
282 | } | ||
283 | |||
284 | ent->tid = sample->tid; | ||
285 | memcpy(&ent->event, event, event->header.size); | ||
286 | list_add(&ent->node, &inject->samples); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int perf_inject__sched_stat(struct perf_tool *tool, | ||
291 | union perf_event *event __maybe_unused, | ||
292 | struct perf_sample *sample, | ||
293 | struct perf_evsel *evsel, | ||
294 | struct machine *machine) | ||
295 | { | ||
296 | struct event_entry *ent; | ||
297 | union perf_event *event_sw; | ||
298 | struct perf_sample sample_sw; | ||
299 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
300 | u32 pid = perf_evsel__intval(evsel, sample, "pid"); | ||
301 | |||
302 | list_for_each_entry(ent, &inject->samples, node) { | ||
303 | if (pid == ent->tid) | ||
304 | goto found; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | found: | ||
309 | event_sw = &ent->event[0]; | ||
310 | perf_evsel__parse_sample(evsel, event_sw, &sample_sw); | ||
311 | |||
312 | sample_sw.period = sample->period; | ||
313 | sample_sw.time = sample->time; | ||
314 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, | ||
315 | &sample_sw, false); | ||
316 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); | ||
317 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); | ||
318 | } | ||
319 | |||
213 | extern volatile int session_done; | 320 | extern volatile int session_done; |
214 | 321 | ||
215 | static void sig_handler(int sig __maybe_unused) | 322 | static void sig_handler(int sig __maybe_unused) |
@@ -217,6 +324,21 @@ static void sig_handler(int sig __maybe_unused) | |||
217 | session_done = 1; | 324 | session_done = 1; |
218 | } | 325 | } |
219 | 326 | ||
327 | static int perf_evsel__check_stype(struct perf_evsel *evsel, | ||
328 | u64 sample_type, const char *sample_msg) | ||
329 | { | ||
330 | struct perf_event_attr *attr = &evsel->attr; | ||
331 | const char *name = perf_evsel__name(evsel); | ||
332 | |||
333 | if (!(attr->sample_type & sample_type)) { | ||
334 | pr_err("Samples for %s event do not have %s attribute set.", | ||
335 | name, sample_msg); | ||
336 | return -EINVAL; | ||
337 | } | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
220 | static int __cmd_inject(struct perf_inject *inject) | 342 | static int __cmd_inject(struct perf_inject *inject) |
221 | { | 343 | { |
222 | struct perf_session *session; | 344 | struct perf_session *session; |
@@ -224,19 +346,48 @@ static int __cmd_inject(struct perf_inject *inject) | |||
224 | 346 | ||
225 | signal(SIGINT, sig_handler); | 347 | signal(SIGINT, sig_handler); |
226 | 348 | ||
227 | if (inject->build_ids) { | 349 | if (inject->build_ids || inject->sched_stat) { |
228 | inject->tool.sample = perf_event__inject_buildid; | ||
229 | inject->tool.mmap = perf_event__repipe_mmap; | 350 | inject->tool.mmap = perf_event__repipe_mmap; |
230 | inject->tool.fork = perf_event__repipe_task; | 351 | inject->tool.fork = perf_event__repipe_fork; |
231 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 352 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
232 | } | 353 | } |
233 | 354 | ||
234 | session = perf_session__new("-", O_RDONLY, false, true, &inject->tool); | 355 | session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); |
235 | if (session == NULL) | 356 | if (session == NULL) |
236 | return -ENOMEM; | 357 | return -ENOMEM; |
237 | 358 | ||
359 | if (inject->build_ids) { | ||
360 | inject->tool.sample = perf_event__inject_buildid; | ||
361 | } else if (inject->sched_stat) { | ||
362 | struct perf_evsel *evsel; | ||
363 | |||
364 | inject->tool.ordered_samples = true; | ||
365 | |||
366 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
367 | const char *name = perf_evsel__name(evsel); | ||
368 | |||
369 | if (!strcmp(name, "sched:sched_switch")) { | ||
370 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) | ||
371 | return -EINVAL; | ||
372 | |||
373 | evsel->handler.func = perf_inject__sched_switch; | ||
374 | } else if (!strcmp(name, "sched:sched_process_exit")) | ||
375 | evsel->handler.func = perf_inject__sched_process_exit; | ||
376 | else if (!strncmp(name, "sched:sched_stat_", 17)) | ||
377 | evsel->handler.func = perf_inject__sched_stat; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | if (!inject->pipe_output) | ||
382 | lseek(inject->output, session->header.data_offset, SEEK_SET); | ||
383 | |||
238 | ret = perf_session__process_events(session, &inject->tool); | 384 | ret = perf_session__process_events(session, &inject->tool); |
239 | 385 | ||
386 | if (!inject->pipe_output) { | ||
387 | session->header.data_size = inject->bytes_written; | ||
388 | perf_session__write_header(session, session->evlist, inject->output, true); | ||
389 | } | ||
390 | |||
240 | perf_session__delete(session); | 391 | perf_session__delete(session); |
241 | 392 | ||
242 | return ret; | 393 | return ret; |
@@ -260,10 +411,20 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
260 | .tracing_data = perf_event__repipe_tracing_data_synth, | 411 | .tracing_data = perf_event__repipe_tracing_data_synth, |
261 | .build_id = perf_event__repipe_op2_synth, | 412 | .build_id = perf_event__repipe_op2_synth, |
262 | }, | 413 | }, |
414 | .input_name = "-", | ||
415 | .samples = LIST_HEAD_INIT(inject.samples), | ||
263 | }; | 416 | }; |
417 | const char *output_name = "-"; | ||
264 | const struct option options[] = { | 418 | const struct option options[] = { |
265 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 419 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
266 | "Inject build-ids into the output stream"), | 420 | "Inject build-ids into the output stream"), |
421 | OPT_STRING('i', "input", &inject.input_name, "file", | ||
422 | "input file name"), | ||
423 | OPT_STRING('o', "output", &output_name, "file", | ||
424 | "output file name"), | ||
425 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, | ||
426 | "Merge sched-stat and sched-switch for getting events " | ||
427 | "where and how long tasks slept"), | ||
267 | OPT_INCR('v', "verbose", &verbose, | 428 | OPT_INCR('v', "verbose", &verbose, |
268 | "be more verbose (show build ids, etc)"), | 429 | "be more verbose (show build ids, etc)"), |
269 | OPT_END() | 430 | OPT_END() |
@@ -281,6 +442,18 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
281 | if (argc) | 442 | if (argc) |
282 | usage_with_options(inject_usage, options); | 443 | usage_with_options(inject_usage, options); |
283 | 444 | ||
445 | if (!strcmp(output_name, "-")) { | ||
446 | inject.pipe_output = 1; | ||
447 | inject.output = STDOUT_FILENO; | ||
448 | } else { | ||
449 | inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, | ||
450 | S_IRUSR | S_IWUSR); | ||
451 | if (inject.output < 0) { | ||
452 | perror("failed to create output file"); | ||
453 | return -1; | ||
454 | } | ||
455 | } | ||
456 | |||
284 | if (symbol__init() < 0) | 457 | if (symbol__init() < 0) |
285 | return -1; | 458 | return -1; |
286 | 459 | ||
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 14bf82f63659..0b4b796167be 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -477,7 +477,7 @@ static void sort_result(void) | |||
477 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); | 477 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); |
478 | } | 478 | } |
479 | 479 | ||
480 | static int __cmd_kmem(const char *input_name) | 480 | static int __cmd_kmem(void) |
481 | { | 481 | { |
482 | int err = -EINVAL; | 482 | int err = -EINVAL; |
483 | struct perf_session *session; | 483 | struct perf_session *session; |
@@ -743,7 +743,6 @@ static int __cmd_record(int argc, const char **argv) | |||
743 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | 743 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) |
744 | { | 744 | { |
745 | const char * const default_sort_order = "frag,hit,bytes"; | 745 | const char * const default_sort_order = "frag,hit,bytes"; |
746 | const char *input_name = NULL; | ||
747 | const struct option kmem_options[] = { | 746 | const struct option kmem_options[] = { |
748 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 747 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
749 | OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, | 748 | OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, |
@@ -779,7 +778,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
779 | if (list_empty(&alloc_sort)) | 778 | if (list_empty(&alloc_sort)) |
780 | setup_sorting(&alloc_sort, default_sort_order); | 779 | setup_sorting(&alloc_sort, default_sort_order); |
781 | 780 | ||
782 | return __cmd_kmem(input_name); | 781 | return __cmd_kmem(); |
783 | } else | 782 | } else |
784 | usage_with_options(kmem_usage, kmem_options); | 783 | usage_with_options(kmem_usage, kmem_options); |
785 | 784 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 283b4397e397..ca3f80ebc100 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -314,9 +314,9 @@ struct vcpu_event_record { | |||
314 | 314 | ||
315 | static void init_kvm_event_record(struct perf_kvm_stat *kvm) | 315 | static void init_kvm_event_record(struct perf_kvm_stat *kvm) |
316 | { | 316 | { |
317 | int i; | 317 | unsigned int i; |
318 | 318 | ||
319 | for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++) | 319 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) |
320 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); | 320 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); |
321 | } | 321 | } |
322 | 322 | ||
@@ -370,9 +370,10 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm_stat *kvm, | |||
370 | BUG_ON(key->key == INVALID_KEY); | 370 | BUG_ON(key->key == INVALID_KEY); |
371 | 371 | ||
372 | head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; | 372 | head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; |
373 | list_for_each_entry(event, head, hash_entry) | 373 | list_for_each_entry(event, head, hash_entry) { |
374 | if (event->key.key == key->key && event->key.info == key->info) | 374 | if (event->key.key == key->key && event->key.info == key->info) |
375 | return event; | 375 | return event; |
376 | } | ||
376 | 377 | ||
377 | event = kvm_alloc_init_event(key); | 378 | event = kvm_alloc_init_event(key); |
378 | if (!event) | 379 | if (!event) |
@@ -417,7 +418,10 @@ static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event) | |||
417 | static bool update_kvm_event(struct kvm_event *event, int vcpu_id, | 418 | static bool update_kvm_event(struct kvm_event *event, int vcpu_id, |
418 | u64 time_diff) | 419 | u64 time_diff) |
419 | { | 420 | { |
420 | kvm_update_event_stats(&event->total, time_diff); | 421 | if (vcpu_id == -1) { |
422 | kvm_update_event_stats(&event->total, time_diff); | ||
423 | return true; | ||
424 | } | ||
421 | 425 | ||
422 | if (!kvm_event_expand(event, vcpu_id)) | 426 | if (!kvm_event_expand(event, vcpu_id)) |
423 | return false; | 427 | return false; |
@@ -433,6 +437,12 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, | |||
433 | { | 437 | { |
434 | struct kvm_event *event; | 438 | struct kvm_event *event; |
435 | u64 time_begin, time_diff; | 439 | u64 time_begin, time_diff; |
440 | int vcpu; | ||
441 | |||
442 | if (kvm->trace_vcpu == -1) | ||
443 | vcpu = -1; | ||
444 | else | ||
445 | vcpu = vcpu_record->vcpu_id; | ||
436 | 446 | ||
437 | event = vcpu_record->last_event; | 447 | event = vcpu_record->last_event; |
438 | time_begin = vcpu_record->start_time; | 448 | time_begin = vcpu_record->start_time; |
@@ -462,7 +472,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, | |||
462 | BUG_ON(timestamp < time_begin); | 472 | BUG_ON(timestamp < time_begin); |
463 | 473 | ||
464 | time_diff = timestamp - time_begin; | 474 | time_diff = timestamp - time_begin; |
465 | return update_kvm_event(event, vcpu_record->vcpu_id, time_diff); | 475 | return update_kvm_event(event, vcpu, time_diff); |
466 | } | 476 | } |
467 | 477 | ||
468 | static | 478 | static |
@@ -499,6 +509,11 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, | |||
499 | if (!vcpu_record) | 509 | if (!vcpu_record) |
500 | return true; | 510 | return true; |
501 | 511 | ||
512 | /* only process events for vcpus user cares about */ | ||
513 | if ((kvm->trace_vcpu != -1) && | ||
514 | (kvm->trace_vcpu != vcpu_record->vcpu_id)) | ||
515 | return true; | ||
516 | |||
502 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) | 517 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) |
503 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); | 518 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); |
504 | 519 | ||
@@ -598,13 +613,15 @@ static void sort_result(struct perf_kvm_stat *kvm) | |||
598 | int vcpu = kvm->trace_vcpu; | 613 | int vcpu = kvm->trace_vcpu; |
599 | struct kvm_event *event; | 614 | struct kvm_event *event; |
600 | 615 | ||
601 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) | 616 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) { |
602 | list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) | 617 | list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) { |
603 | if (event_is_valid(event, vcpu)) { | 618 | if (event_is_valid(event, vcpu)) { |
604 | update_total_count(kvm, event); | 619 | update_total_count(kvm, event); |
605 | insert_to_result(&kvm->result, event, | 620 | insert_to_result(&kvm->result, event, |
606 | kvm->compare, vcpu); | 621 | kvm->compare, vcpu); |
607 | } | 622 | } |
623 | } | ||
624 | } | ||
608 | } | 625 | } |
609 | 626 | ||
610 | /* returns left most element of result, and erase it */ | 627 | /* returns left most element of result, and erase it */ |
@@ -661,8 +678,8 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
661 | pr_info("\n"); | 678 | pr_info("\n"); |
662 | } | 679 | } |
663 | 680 | ||
664 | pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n", | 681 | pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n", |
665 | (unsigned long long)kvm->total_count, kvm->total_time / 1e3); | 682 | kvm->total_count, kvm->total_time / 1e3); |
666 | } | 683 | } |
667 | 684 | ||
668 | static int process_sample_event(struct perf_tool *tool, | 685 | static int process_sample_event(struct perf_tool *tool, |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6f5f328157aa..425830069749 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -335,8 +335,6 @@ alloc_failed: | |||
335 | return NULL; | 335 | return NULL; |
336 | } | 336 | } |
337 | 337 | ||
338 | static const char *input_name; | ||
339 | |||
340 | struct trace_lock_handler { | 338 | struct trace_lock_handler { |
341 | int (*acquire_event)(struct perf_evsel *evsel, | 339 | int (*acquire_event)(struct perf_evsel *evsel, |
342 | struct perf_sample *sample); | 340 | struct perf_sample *sample); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e9231659754d..f3151d3c70ce 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -31,6 +31,38 @@ | |||
31 | #include <sched.h> | 31 | #include <sched.h> |
32 | #include <sys/mman.h> | 32 | #include <sys/mman.h> |
33 | 33 | ||
34 | #ifndef HAVE_ON_EXIT | ||
35 | #ifndef ATEXIT_MAX | ||
36 | #define ATEXIT_MAX 32 | ||
37 | #endif | ||
38 | static int __on_exit_count = 0; | ||
39 | typedef void (*on_exit_func_t) (int, void *); | ||
40 | static on_exit_func_t __on_exit_funcs[ATEXIT_MAX]; | ||
41 | static void *__on_exit_args[ATEXIT_MAX]; | ||
42 | static int __exitcode = 0; | ||
43 | static void __handle_on_exit_funcs(void); | ||
44 | static int on_exit(on_exit_func_t function, void *arg); | ||
45 | #define exit(x) (exit)(__exitcode = (x)) | ||
46 | |||
47 | static int on_exit(on_exit_func_t function, void *arg) | ||
48 | { | ||
49 | if (__on_exit_count == ATEXIT_MAX) | ||
50 | return -ENOMEM; | ||
51 | else if (__on_exit_count == 0) | ||
52 | atexit(__handle_on_exit_funcs); | ||
53 | __on_exit_funcs[__on_exit_count] = function; | ||
54 | __on_exit_args[__on_exit_count++] = arg; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static void __handle_on_exit_funcs(void) | ||
59 | { | ||
60 | int i; | ||
61 | for (i = 0; i < __on_exit_count; i++) | ||
62 | __on_exit_funcs[i] (__exitcode, __on_exit_args[i]); | ||
63 | } | ||
64 | #endif | ||
65 | |||
34 | enum write_mode_t { | 66 | enum write_mode_t { |
35 | WRITE_FORCE, | 67 | WRITE_FORCE, |
36 | WRITE_APPEND | 68 | WRITE_APPEND |
@@ -198,11 +230,15 @@ static int perf_record__open(struct perf_record *rec) | |||
198 | struct perf_record_opts *opts = &rec->opts; | 230 | struct perf_record_opts *opts = &rec->opts; |
199 | int rc = 0; | 231 | int rc = 0; |
200 | 232 | ||
201 | perf_evlist__config_attrs(evlist, opts); | 233 | /* |
202 | 234 | * Set the evsel leader links before we configure attributes, | |
235 | * since some might depend on this info. | ||
236 | */ | ||
203 | if (opts->group) | 237 | if (opts->group) |
204 | perf_evlist__set_leader(evlist); | 238 | perf_evlist__set_leader(evlist); |
205 | 239 | ||
240 | perf_evlist__config_attrs(evlist, opts); | ||
241 | |||
206 | list_for_each_entry(pos, &evlist->entries, node) { | 242 | list_for_each_entry(pos, &evlist->entries, node) { |
207 | struct perf_event_attr *attr = &pos->attr; | 243 | struct perf_event_attr *attr = &pos->attr; |
208 | /* | 244 | /* |
@@ -285,6 +321,11 @@ try_again: | |||
285 | perf_evsel__name(pos)); | 321 | perf_evsel__name(pos)); |
286 | rc = -err; | 322 | rc = -err; |
287 | goto out; | 323 | goto out; |
324 | } else if ((err == EOPNOTSUPP) && (attr->precise_ip)) { | ||
325 | ui__error("\'precise\' request may not be supported. " | ||
326 | "Try removing 'p' modifier\n"); | ||
327 | rc = -err; | ||
328 | goto out; | ||
288 | } | 329 | } |
289 | 330 | ||
290 | printf("\n"); | 331 | printf("\n"); |
@@ -326,7 +367,8 @@ try_again: | |||
326 | "or try again with a smaller value of -m/--mmap_pages.\n" | 367 | "or try again with a smaller value of -m/--mmap_pages.\n" |
327 | "(current value: %d)\n", opts->mmap_pages); | 368 | "(current value: %d)\n", opts->mmap_pages); |
328 | rc = -errno; | 369 | rc = -errno; |
329 | } else if (!is_power_of_2(opts->mmap_pages)) { | 370 | } else if (!is_power_of_2(opts->mmap_pages) && |
371 | (opts->mmap_pages != UINT_MAX)) { | ||
330 | pr_err("--mmap_pages/-m value must be a power of two."); | 372 | pr_err("--mmap_pages/-m value must be a power of two."); |
331 | rc = -EINVAL; | 373 | rc = -EINVAL; |
332 | } else { | 374 | } else { |
@@ -460,6 +502,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
460 | struct perf_evlist *evsel_list = rec->evlist; | 502 | struct perf_evlist *evsel_list = rec->evlist; |
461 | const char *output_name = rec->output_name; | 503 | const char *output_name = rec->output_name; |
462 | struct perf_session *session; | 504 | struct perf_session *session; |
505 | bool disabled = false; | ||
463 | 506 | ||
464 | rec->progname = argv[0]; | 507 | rec->progname = argv[0]; |
465 | 508 | ||
@@ -659,7 +702,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
659 | } | 702 | } |
660 | } | 703 | } |
661 | 704 | ||
662 | perf_evlist__enable(evsel_list); | 705 | /* |
706 | * When perf is starting the traced process, all the events | ||
707 | * (apart from group members) have enable_on_exec=1 set, | ||
708 | * so don't spoil it by prematurely enabling them. | ||
709 | */ | ||
710 | if (!perf_target__none(&opts->target)) | ||
711 | perf_evlist__enable(evsel_list); | ||
663 | 712 | ||
664 | /* | 713 | /* |
665 | * Let the child rip | 714 | * Let the child rip |
@@ -682,8 +731,15 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
682 | waking++; | 731 | waking++; |
683 | } | 732 | } |
684 | 733 | ||
685 | if (done) | 734 | /* |
735 | * When perf is starting the traced process, at the end events | ||
736 | * die with the process and we wait for that. Thus no need to | ||
737 | * disable events in this case. | ||
738 | */ | ||
739 | if (done && !disabled && !perf_target__none(&opts->target)) { | ||
686 | perf_evlist__disable(evsel_list); | 740 | perf_evlist__disable(evsel_list); |
741 | disabled = true; | ||
742 | } | ||
687 | } | 743 | } |
688 | 744 | ||
689 | if (quiet || signr == SIGUSR1) | 745 | if (quiet || signr == SIGUSR1) |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index a61725d89d3e..fc251005dd3d 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -33,13 +33,13 @@ | |||
33 | #include "util/thread.h" | 33 | #include "util/thread.h" |
34 | #include "util/sort.h" | 34 | #include "util/sort.h" |
35 | #include "util/hist.h" | 35 | #include "util/hist.h" |
36 | #include "arch/common.h" | ||
36 | 37 | ||
37 | #include <linux/bitmap.h> | 38 | #include <linux/bitmap.h> |
38 | 39 | ||
39 | struct perf_report { | 40 | struct perf_report { |
40 | struct perf_tool tool; | 41 | struct perf_tool tool; |
41 | struct perf_session *session; | 42 | struct perf_session *session; |
42 | char const *input_name; | ||
43 | bool force, use_tui, use_gtk, use_stdio; | 43 | bool force, use_tui, use_gtk, use_stdio; |
44 | bool hide_unresolved; | 44 | bool hide_unresolved; |
45 | bool dont_use_callchains; | 45 | bool dont_use_callchains; |
@@ -428,10 +428,11 @@ static int __cmd_report(struct perf_report *rep) | |||
428 | if (use_browser > 0) { | 428 | if (use_browser > 0) { |
429 | if (use_browser == 1) { | 429 | if (use_browser == 1) { |
430 | perf_evlist__tui_browse_hists(session->evlist, help, | 430 | perf_evlist__tui_browse_hists(session->evlist, help, |
431 | NULL, NULL, 0); | 431 | NULL, |
432 | &session->header.env); | ||
432 | } else if (use_browser == 2) { | 433 | } else if (use_browser == 2) { |
433 | perf_evlist__gtk_browse_hists(session->evlist, help, | 434 | perf_evlist__gtk_browse_hists(session->evlist, help, |
434 | NULL, NULL, 0); | 435 | NULL); |
435 | } | 436 | } |
436 | } else | 437 | } else |
437 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 438 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
@@ -556,8 +557,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
556 | .sample = process_sample_event, | 557 | .sample = process_sample_event, |
557 | .mmap = perf_event__process_mmap, | 558 | .mmap = perf_event__process_mmap, |
558 | .comm = perf_event__process_comm, | 559 | .comm = perf_event__process_comm, |
559 | .exit = perf_event__process_task, | 560 | .exit = perf_event__process_exit, |
560 | .fork = perf_event__process_task, | 561 | .fork = perf_event__process_fork, |
561 | .lost = perf_event__process_lost, | 562 | .lost = perf_event__process_lost, |
562 | .read = process_read_event, | 563 | .read = process_read_event, |
563 | .attr = perf_event__process_attr, | 564 | .attr = perf_event__process_attr, |
@@ -570,7 +571,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
570 | .pretty_printing_style = "normal", | 571 | .pretty_printing_style = "normal", |
571 | }; | 572 | }; |
572 | const struct option options[] = { | 573 | const struct option options[] = { |
573 | OPT_STRING('i', "input", &report.input_name, "file", | 574 | OPT_STRING('i', "input", &input_name, "file", |
574 | "input file name"), | 575 | "input file name"), |
575 | OPT_INCR('v', "verbose", &verbose, | 576 | OPT_INCR('v', "verbose", &verbose, |
576 | "be more verbose (show symbol address, etc)"), | 577 | "be more verbose (show symbol address, etc)"), |
@@ -656,13 +657,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
656 | if (report.inverted_callchain) | 657 | if (report.inverted_callchain) |
657 | callchain_param.order = ORDER_CALLER; | 658 | callchain_param.order = ORDER_CALLER; |
658 | 659 | ||
659 | if (!report.input_name || !strlen(report.input_name)) { | 660 | if (!input_name || !strlen(input_name)) { |
660 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | 661 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) |
661 | report.input_name = "-"; | 662 | input_name = "-"; |
662 | else | 663 | else |
663 | report.input_name = "perf.data"; | 664 | input_name = "perf.data"; |
664 | } | 665 | } |
665 | session = perf_session__new(report.input_name, O_RDONLY, | 666 | session = perf_session__new(input_name, O_RDONLY, |
666 | report.force, false, &report.tool); | 667 | report.force, false, &report.tool); |
667 | if (session == NULL) | 668 | if (session == NULL) |
668 | return -ENOMEM; | 669 | return -ENOMEM; |
@@ -687,7 +688,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
687 | 688 | ||
688 | } | 689 | } |
689 | 690 | ||
690 | if (strcmp(report.input_name, "-") != 0) | 691 | if (strcmp(input_name, "-") != 0) |
691 | setup_browser(true); | 692 | setup_browser(true); |
692 | else { | 693 | else { |
693 | use_browser = 0; | 694 | use_browser = 0; |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3488ead3b60c..cc28b85dabd5 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -120,7 +120,6 @@ struct trace_sched_handler { | |||
120 | 120 | ||
121 | struct perf_sched { | 121 | struct perf_sched { |
122 | struct perf_tool tool; | 122 | struct perf_tool tool; |
123 | const char *input_name; | ||
124 | const char *sort_order; | 123 | const char *sort_order; |
125 | unsigned long nr_tasks; | 124 | unsigned long nr_tasks; |
126 | struct task_desc *pid_to_task[MAX_PID]; | 125 | struct task_desc *pid_to_task[MAX_PID]; |
@@ -1460,7 +1459,7 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1460 | }; | 1459 | }; |
1461 | struct perf_session *session; | 1460 | struct perf_session *session; |
1462 | 1461 | ||
1463 | session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool); | 1462 | session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); |
1464 | if (session == NULL) { | 1463 | if (session == NULL) { |
1465 | pr_debug("No Memory for session\n"); | 1464 | pr_debug("No Memory for session\n"); |
1466 | return -1; | 1465 | return -1; |
@@ -1672,7 +1671,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1672 | .sample = perf_sched__process_tracepoint_sample, | 1671 | .sample = perf_sched__process_tracepoint_sample, |
1673 | .comm = perf_event__process_comm, | 1672 | .comm = perf_event__process_comm, |
1674 | .lost = perf_event__process_lost, | 1673 | .lost = perf_event__process_lost, |
1675 | .fork = perf_event__process_task, | 1674 | .exit = perf_event__process_exit, |
1675 | .fork = perf_event__process_fork, | ||
1676 | .ordered_samples = true, | 1676 | .ordered_samples = true, |
1677 | }, | 1677 | }, |
1678 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | 1678 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), |
@@ -1707,7 +1707,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1707 | OPT_END() | 1707 | OPT_END() |
1708 | }; | 1708 | }; |
1709 | const struct option sched_options[] = { | 1709 | const struct option sched_options[] = { |
1710 | OPT_STRING('i', "input", &sched.input_name, "file", | 1710 | OPT_STRING('i', "input", &input_name, "file", |
1711 | "input file name"), | 1711 | "input file name"), |
1712 | OPT_INCR('v', "verbose", &verbose, | 1712 | OPT_INCR('v', "verbose", &verbose, |
1713 | "be more verbose (show symbol address, etc)"), | 1713 | "be more verbose (show symbol address, etc)"), |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index fb9625083a2e..b363e7b292b2 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -520,8 +520,8 @@ static struct perf_tool perf_script = { | |||
520 | .sample = process_sample_event, | 520 | .sample = process_sample_event, |
521 | .mmap = perf_event__process_mmap, | 521 | .mmap = perf_event__process_mmap, |
522 | .comm = perf_event__process_comm, | 522 | .comm = perf_event__process_comm, |
523 | .exit = perf_event__process_task, | 523 | .exit = perf_event__process_exit, |
524 | .fork = perf_event__process_task, | 524 | .fork = perf_event__process_fork, |
525 | .attr = perf_event__process_attr, | 525 | .attr = perf_event__process_attr, |
526 | .event_type = perf_event__process_event_type, | 526 | .event_type = perf_event__process_event_type, |
527 | .tracing_data = perf_event__process_tracing_data, | 527 | .tracing_data = perf_event__process_tracing_data, |
@@ -1030,6 +1030,68 @@ static int list_available_scripts(const struct option *opt __maybe_unused, | |||
1030 | } | 1030 | } |
1031 | 1031 | ||
1032 | /* | 1032 | /* |
1033 | * Some scripts specify the required events in their "xxx-record" file, | ||
1034 | * this function will check if the events in perf.data match those | ||
1035 | * mentioned in the "xxx-record". | ||
1036 | * | ||
1037 | * Fixme: All existing "xxx-record" are all in good formats "-e event ", | ||
1038 | * which is covered well now. And new parsing code should be added to | ||
1039 | * cover the future complexing formats like event groups etc. | ||
1040 | */ | ||
1041 | static int check_ev_match(char *dir_name, char *scriptname, | ||
1042 | struct perf_session *session) | ||
1043 | { | ||
1044 | char filename[MAXPATHLEN], evname[128]; | ||
1045 | char line[BUFSIZ], *p; | ||
1046 | struct perf_evsel *pos; | ||
1047 | int match, len; | ||
1048 | FILE *fp; | ||
1049 | |||
1050 | sprintf(filename, "%s/bin/%s-record", dir_name, scriptname); | ||
1051 | |||
1052 | fp = fopen(filename, "r"); | ||
1053 | if (!fp) | ||
1054 | return -1; | ||
1055 | |||
1056 | while (fgets(line, sizeof(line), fp)) { | ||
1057 | p = ltrim(line); | ||
1058 | if (*p == '#') | ||
1059 | continue; | ||
1060 | |||
1061 | while (strlen(p)) { | ||
1062 | p = strstr(p, "-e"); | ||
1063 | if (!p) | ||
1064 | break; | ||
1065 | |||
1066 | p += 2; | ||
1067 | p = ltrim(p); | ||
1068 | len = strcspn(p, " \t"); | ||
1069 | if (!len) | ||
1070 | break; | ||
1071 | |||
1072 | snprintf(evname, len + 1, "%s", p); | ||
1073 | |||
1074 | match = 0; | ||
1075 | list_for_each_entry(pos, | ||
1076 | &session->evlist->entries, node) { | ||
1077 | if (!strcmp(perf_evsel__name(pos), evname)) { | ||
1078 | match = 1; | ||
1079 | break; | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | if (!match) { | ||
1084 | fclose(fp); | ||
1085 | return -1; | ||
1086 | } | ||
1087 | } | ||
1088 | } | ||
1089 | |||
1090 | fclose(fp); | ||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | /* | ||
1033 | * Return -1 if none is found, otherwise the actual scripts number. | 1095 | * Return -1 if none is found, otherwise the actual scripts number. |
1034 | * | 1096 | * |
1035 | * Currently the only user of this function is the script browser, which | 1097 | * Currently the only user of this function is the script browser, which |
@@ -1039,17 +1101,23 @@ static int list_available_scripts(const struct option *opt __maybe_unused, | |||
1039 | int find_scripts(char **scripts_array, char **scripts_path_array) | 1101 | int find_scripts(char **scripts_array, char **scripts_path_array) |
1040 | { | 1102 | { |
1041 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | 1103 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; |
1042 | char scripts_path[MAXPATHLEN]; | 1104 | char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; |
1043 | DIR *scripts_dir, *lang_dir; | 1105 | DIR *scripts_dir, *lang_dir; |
1044 | char lang_path[MAXPATHLEN]; | 1106 | struct perf_session *session; |
1045 | char *temp; | 1107 | char *temp; |
1046 | int i = 0; | 1108 | int i = 0; |
1047 | 1109 | ||
1110 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | ||
1111 | if (!session) | ||
1112 | return -1; | ||
1113 | |||
1048 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | 1114 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); |
1049 | 1115 | ||
1050 | scripts_dir = opendir(scripts_path); | 1116 | scripts_dir = opendir(scripts_path); |
1051 | if (!scripts_dir) | 1117 | if (!scripts_dir) { |
1118 | perf_session__delete(session); | ||
1052 | return -1; | 1119 | return -1; |
1120 | } | ||
1053 | 1121 | ||
1054 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { | 1122 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { |
1055 | snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, | 1123 | snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, |
@@ -1077,10 +1145,18 @@ int find_scripts(char **scripts_array, char **scripts_path_array) | |||
1077 | snprintf(scripts_array[i], | 1145 | snprintf(scripts_array[i], |
1078 | (temp - script_dirent.d_name) + 1, | 1146 | (temp - script_dirent.d_name) + 1, |
1079 | "%s", script_dirent.d_name); | 1147 | "%s", script_dirent.d_name); |
1148 | |||
1149 | if (check_ev_match(lang_path, | ||
1150 | scripts_array[i], session)) | ||
1151 | continue; | ||
1152 | |||
1080 | i++; | 1153 | i++; |
1081 | } | 1154 | } |
1155 | closedir(lang_dir); | ||
1082 | } | 1156 | } |
1083 | 1157 | ||
1158 | closedir(scripts_dir); | ||
1159 | perf_session__delete(session); | ||
1084 | return i; | 1160 | return i; |
1085 | } | 1161 | } |
1086 | 1162 | ||
@@ -1175,7 +1251,6 @@ static int have_cmd(int argc, const char **argv) | |||
1175 | int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | 1251 | int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) |
1176 | { | 1252 | { |
1177 | bool show_full_info = false; | 1253 | bool show_full_info = false; |
1178 | const char *input_name = NULL; | ||
1179 | char *rec_script_path = NULL; | 1254 | char *rec_script_path = NULL; |
1180 | char *rep_script_path = NULL; | 1255 | char *rep_script_path = NULL; |
1181 | struct perf_session *session; | 1256 | struct perf_session *session; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 93b9011fa3e2..c247faca7127 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include "util/thread.h" | 57 | #include "util/thread.h" |
58 | #include "util/thread_map.h" | 58 | #include "util/thread_map.h" |
59 | 59 | ||
60 | #include <stdlib.h> | ||
60 | #include <sys/prctl.h> | 61 | #include <sys/prctl.h> |
61 | #include <locale.h> | 62 | #include <locale.h> |
62 | 63 | ||
@@ -83,6 +84,9 @@ static const char *csv_sep = NULL; | |||
83 | static bool csv_output = false; | 84 | static bool csv_output = false; |
84 | static bool group = false; | 85 | static bool group = false; |
85 | static FILE *output = NULL; | 86 | static FILE *output = NULL; |
87 | static const char *pre_cmd = NULL; | ||
88 | static const char *post_cmd = NULL; | ||
89 | static bool sync_run = false; | ||
86 | 90 | ||
87 | static volatile int done = 0; | 91 | static volatile int done = 0; |
88 | 92 | ||
@@ -125,8 +129,7 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | |||
125 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 129 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; |
126 | static struct stats walltime_nsecs_stats; | 130 | static struct stats walltime_nsecs_stats; |
127 | 131 | ||
128 | static int create_perf_stat_counter(struct perf_evsel *evsel, | 132 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
129 | struct perf_evsel *first) | ||
130 | { | 133 | { |
131 | struct perf_event_attr *attr = &evsel->attr; | 134 | struct perf_event_attr *attr = &evsel->attr; |
132 | bool exclude_guest_missing = false; | 135 | bool exclude_guest_missing = false; |
@@ -149,7 +152,8 @@ retry: | |||
149 | return 0; | 152 | return 0; |
150 | } | 153 | } |
151 | 154 | ||
152 | if (!perf_target__has_task(&target) && (!group || evsel == first)) { | 155 | if (!perf_target__has_task(&target) && |
156 | !perf_evsel__is_group_member(evsel)) { | ||
153 | attr->disabled = 1; | 157 | attr->disabled = 1; |
154 | attr->enable_on_exec = 1; | 158 | attr->enable_on_exec = 1; |
155 | } | 159 | } |
@@ -265,10 +269,10 @@ static int read_counter(struct perf_evsel *counter) | |||
265 | return 0; | 269 | return 0; |
266 | } | 270 | } |
267 | 271 | ||
268 | static int run_perf_stat(int argc __maybe_unused, const char **argv) | 272 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) |
269 | { | 273 | { |
270 | unsigned long long t0, t1; | 274 | unsigned long long t0, t1; |
271 | struct perf_evsel *counter, *first; | 275 | struct perf_evsel *counter; |
272 | int status = 0; | 276 | int status = 0; |
273 | int child_ready_pipe[2], go_pipe[2]; | 277 | int child_ready_pipe[2], go_pipe[2]; |
274 | const bool forks = (argc > 0); | 278 | const bool forks = (argc > 0); |
@@ -328,10 +332,8 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
328 | if (group) | 332 | if (group) |
329 | perf_evlist__set_leader(evsel_list); | 333 | perf_evlist__set_leader(evsel_list); |
330 | 334 | ||
331 | first = perf_evlist__first(evsel_list); | ||
332 | |||
333 | list_for_each_entry(counter, &evsel_list->entries, node) { | 335 | list_for_each_entry(counter, &evsel_list->entries, node) { |
334 | if (create_perf_stat_counter(counter, first) < 0) { | 336 | if (create_perf_stat_counter(counter) < 0) { |
335 | /* | 337 | /* |
336 | * PPC returns ENXIO for HW counters until 2.6.37 | 338 | * PPC returns ENXIO for HW counters until 2.6.37 |
337 | * (behavior changed with commit b0a873e). | 339 | * (behavior changed with commit b0a873e). |
@@ -405,6 +407,32 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
405 | return WEXITSTATUS(status); | 407 | return WEXITSTATUS(status); |
406 | } | 408 | } |
407 | 409 | ||
410 | static int run_perf_stat(int argc __maybe_unused, const char **argv) | ||
411 | { | ||
412 | int ret; | ||
413 | |||
414 | if (pre_cmd) { | ||
415 | ret = system(pre_cmd); | ||
416 | if (ret) | ||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | if (sync_run) | ||
421 | sync(); | ||
422 | |||
423 | ret = __run_perf_stat(argc, argv); | ||
424 | if (ret) | ||
425 | return ret; | ||
426 | |||
427 | if (post_cmd) { | ||
428 | ret = system(post_cmd); | ||
429 | if (ret) | ||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | return ret; | ||
434 | } | ||
435 | |||
408 | static void print_noise_pct(double total, double avg) | 436 | static void print_noise_pct(double total, double avg) |
409 | { | 437 | { |
410 | double pct = rel_stddev_stats(total, avg); | 438 | double pct = rel_stddev_stats(total, avg); |
@@ -1069,8 +1097,7 @@ static int add_default_attributes(void) | |||
1069 | 1097 | ||
1070 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | 1098 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) |
1071 | { | 1099 | { |
1072 | bool append_file = false, | 1100 | bool append_file = false; |
1073 | sync_run = false; | ||
1074 | int output_fd = 0; | 1101 | int output_fd = 0; |
1075 | const char *output_name = NULL; | 1102 | const char *output_name = NULL; |
1076 | const struct option options[] = { | 1103 | const struct option options[] = { |
@@ -1114,6 +1141,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1114 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | 1141 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), |
1115 | OPT_INTEGER(0, "log-fd", &output_fd, | 1142 | OPT_INTEGER(0, "log-fd", &output_fd, |
1116 | "log output to fd, instead of stderr"), | 1143 | "log output to fd, instead of stderr"), |
1144 | OPT_STRING(0, "pre", &pre_cmd, "command", | ||
1145 | "command to run prior to the measured command"), | ||
1146 | OPT_STRING(0, "post", &post_cmd, "command", | ||
1147 | "command to run after to the measured command"), | ||
1117 | OPT_END() | 1148 | OPT_END() |
1118 | }; | 1149 | }; |
1119 | const char * const stat_usage[] = { | 1150 | const char * const stat_usage[] = { |
@@ -1238,9 +1269,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1238 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1269 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
1239 | run_idx + 1); | 1270 | run_idx + 1); |
1240 | 1271 | ||
1241 | if (sync_run) | ||
1242 | sync(); | ||
1243 | |||
1244 | status = run_perf_stat(argc, argv); | 1272 | status = run_perf_stat(argc, argv); |
1245 | } | 1273 | } |
1246 | 1274 | ||
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c deleted file mode 100644 index 5acd6e8e658b..000000000000 --- a/tools/perf/builtin-test.c +++ /dev/null | |||
@@ -1,1547 +0,0 @@ | |||
1 | /* | ||
2 | * builtin-test.c | ||
3 | * | ||
4 | * Builtin regression testing command: ever growing number of sanity tests | ||
5 | */ | ||
6 | #include "builtin.h" | ||
7 | |||
8 | #include "util/cache.h" | ||
9 | #include "util/debug.h" | ||
10 | #include "util/debugfs.h" | ||
11 | #include "util/evlist.h" | ||
12 | #include "util/parse-options.h" | ||
13 | #include "util/parse-events.h" | ||
14 | #include "util/symbol.h" | ||
15 | #include "util/thread_map.h" | ||
16 | #include "util/pmu.h" | ||
17 | #include "event-parse.h" | ||
18 | #include <linux/hw_breakpoint.h> | ||
19 | |||
20 | #include <sys/mman.h> | ||
21 | |||
22 | static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | ||
23 | struct symbol *sym) | ||
24 | { | ||
25 | bool *visited = symbol__priv(sym); | ||
26 | *visited = true; | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static int test__vmlinux_matches_kallsyms(void) | ||
31 | { | ||
32 | int err = -1; | ||
33 | struct rb_node *nd; | ||
34 | struct symbol *sym; | ||
35 | struct map *kallsyms_map, *vmlinux_map; | ||
36 | struct machine kallsyms, vmlinux; | ||
37 | enum map_type type = MAP__FUNCTION; | ||
38 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
39 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | ||
40 | |||
41 | /* | ||
42 | * Step 1: | ||
43 | * | ||
44 | * Init the machines that will hold kernel, modules obtained from | ||
45 | * both vmlinux + .ko files and from /proc/kallsyms split by modules. | ||
46 | */ | ||
47 | machine__init(&kallsyms, "", HOST_KERNEL_ID); | ||
48 | machine__init(&vmlinux, "", HOST_KERNEL_ID); | ||
49 | |||
50 | /* | ||
51 | * Step 2: | ||
52 | * | ||
53 | * Create the kernel maps for kallsyms and the DSO where we will then | ||
54 | * load /proc/kallsyms. Also create the modules maps from /proc/modules | ||
55 | * and find the .ko files that match them in /lib/modules/`uname -r`/. | ||
56 | */ | ||
57 | if (machine__create_kernel_maps(&kallsyms) < 0) { | ||
58 | pr_debug("machine__create_kernel_maps "); | ||
59 | return -1; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Step 3: | ||
64 | * | ||
65 | * Load and split /proc/kallsyms into multiple maps, one per module. | ||
66 | */ | ||
67 | if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { | ||
68 | pr_debug("dso__load_kallsyms "); | ||
69 | goto out; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Step 4: | ||
74 | * | ||
75 | * kallsyms will be internally on demand sorted by name so that we can | ||
76 | * find the reference relocation * symbol, i.e. the symbol we will use | ||
77 | * to see if the running kernel was relocated by checking if it has the | ||
78 | * same value in the vmlinux file we load. | ||
79 | */ | ||
80 | kallsyms_map = machine__kernel_map(&kallsyms, type); | ||
81 | |||
82 | sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); | ||
83 | if (sym == NULL) { | ||
84 | pr_debug("dso__find_symbol_by_name "); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | ref_reloc_sym.addr = sym->start; | ||
89 | |||
90 | /* | ||
91 | * Step 5: | ||
92 | * | ||
93 | * Now repeat step 2, this time for the vmlinux file we'll auto-locate. | ||
94 | */ | ||
95 | if (machine__create_kernel_maps(&vmlinux) < 0) { | ||
96 | pr_debug("machine__create_kernel_maps "); | ||
97 | goto out; | ||
98 | } | ||
99 | |||
100 | vmlinux_map = machine__kernel_map(&vmlinux, type); | ||
101 | map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; | ||
102 | |||
103 | /* | ||
104 | * Step 6: | ||
105 | * | ||
106 | * Locate a vmlinux file in the vmlinux path that has a buildid that | ||
107 | * matches the one of the running kernel. | ||
108 | * | ||
109 | * While doing that look if we find the ref reloc symbol, if we find it | ||
110 | * we'll have its ref_reloc_symbol.unrelocated_addr and then | ||
111 | * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines | ||
112 | * to fixup the symbols. | ||
113 | */ | ||
114 | if (machine__load_vmlinux_path(&vmlinux, type, | ||
115 | vmlinux_matches_kallsyms_filter) <= 0) { | ||
116 | pr_debug("machine__load_vmlinux_path "); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | err = 0; | ||
121 | /* | ||
122 | * Step 7: | ||
123 | * | ||
124 | * Now look at the symbols in the vmlinux DSO and check if we find all of them | ||
125 | * in the kallsyms dso. For the ones that are in both, check its names and | ||
126 | * end addresses too. | ||
127 | */ | ||
128 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
129 | struct symbol *pair, *first_pair; | ||
130 | bool backwards = true; | ||
131 | |||
132 | sym = rb_entry(nd, struct symbol, rb_node); | ||
133 | |||
134 | if (sym->start == sym->end) | ||
135 | continue; | ||
136 | |||
137 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
138 | pair = first_pair; | ||
139 | |||
140 | if (pair && pair->start == sym->start) { | ||
141 | next_pair: | ||
142 | if (strcmp(sym->name, pair->name) == 0) { | ||
143 | /* | ||
144 | * kallsyms don't have the symbol end, so we | ||
145 | * set that by using the next symbol start - 1, | ||
146 | * in some cases we get this up to a page | ||
147 | * wrong, trace_kmalloc when I was developing | ||
148 | * this code was one such example, 2106 bytes | ||
149 | * off the real size. More than that and we | ||
150 | * _really_ have a problem. | ||
151 | */ | ||
152 | s64 skew = sym->end - pair->end; | ||
153 | if (llabs(skew) < page_size) | ||
154 | continue; | ||
155 | |||
156 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", | ||
157 | sym->start, sym->name, sym->end, pair->end); | ||
158 | } else { | ||
159 | struct rb_node *nnd; | ||
160 | detour: | ||
161 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
162 | rb_next(&pair->rb_node); | ||
163 | if (nnd) { | ||
164 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
165 | |||
166 | if (next->start == sym->start) { | ||
167 | pair = next; | ||
168 | goto next_pair; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | if (backwards) { | ||
173 | backwards = false; | ||
174 | pair = first_pair; | ||
175 | goto detour; | ||
176 | } | ||
177 | |||
178 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
179 | sym->start, sym->name, pair->name); | ||
180 | } | ||
181 | } else | ||
182 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | ||
183 | |||
184 | err = -1; | ||
185 | } | ||
186 | |||
187 | if (!verbose) | ||
188 | goto out; | ||
189 | |||
190 | pr_info("Maps only in vmlinux:\n"); | ||
191 | |||
192 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
193 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
194 | /* | ||
195 | * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while | ||
196 | * the kernel will have the path for the vmlinux file being used, | ||
197 | * so use the short name, less descriptive but the same ("[kernel]" in | ||
198 | * both cases. | ||
199 | */ | ||
200 | pair = map_groups__find_by_name(&kallsyms.kmaps, type, | ||
201 | (pos->dso->kernel ? | ||
202 | pos->dso->short_name : | ||
203 | pos->dso->name)); | ||
204 | if (pair) | ||
205 | pair->priv = 1; | ||
206 | else | ||
207 | map__fprintf(pos, stderr); | ||
208 | } | ||
209 | |||
210 | pr_info("Maps in vmlinux with a different name in kallsyms:\n"); | ||
211 | |||
212 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
213 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
214 | |||
215 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | ||
216 | if (pair == NULL || pair->priv) | ||
217 | continue; | ||
218 | |||
219 | if (pair->start == pos->start) { | ||
220 | pair->priv = 1; | ||
221 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | ||
222 | pos->start, pos->end, pos->pgoff, pos->dso->name); | ||
223 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | ||
224 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | ||
225 | pair->start, pair->end, pair->pgoff); | ||
226 | pr_info(" %s\n", pair->dso->name); | ||
227 | pair->priv = 1; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | pr_info("Maps only in kallsyms:\n"); | ||
232 | |||
233 | for (nd = rb_first(&kallsyms.kmaps.maps[type]); | ||
234 | nd; nd = rb_next(nd)) { | ||
235 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
236 | |||
237 | if (!pos->priv) | ||
238 | map__fprintf(pos, stderr); | ||
239 | } | ||
240 | out: | ||
241 | return err; | ||
242 | } | ||
243 | |||
244 | #include "util/cpumap.h" | ||
245 | #include "util/evsel.h" | ||
246 | #include <sys/types.h> | ||
247 | |||
248 | static int trace_event__id(const char *evname) | ||
249 | { | ||
250 | char *filename; | ||
251 | int err = -1, fd; | ||
252 | |||
253 | if (asprintf(&filename, | ||
254 | "%s/syscalls/%s/id", | ||
255 | tracing_events_path, evname) < 0) | ||
256 | return -1; | ||
257 | |||
258 | fd = open(filename, O_RDONLY); | ||
259 | if (fd >= 0) { | ||
260 | char id[16]; | ||
261 | if (read(fd, id, sizeof(id)) > 0) | ||
262 | err = atoi(id); | ||
263 | close(fd); | ||
264 | } | ||
265 | |||
266 | free(filename); | ||
267 | return err; | ||
268 | } | ||
269 | |||
270 | static int test__open_syscall_event(void) | ||
271 | { | ||
272 | int err = -1, fd; | ||
273 | struct thread_map *threads; | ||
274 | struct perf_evsel *evsel; | ||
275 | struct perf_event_attr attr; | ||
276 | unsigned int nr_open_calls = 111, i; | ||
277 | int id = trace_event__id("sys_enter_open"); | ||
278 | |||
279 | if (id < 0) { | ||
280 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
281 | return -1; | ||
282 | } | ||
283 | |||
284 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
285 | if (threads == NULL) { | ||
286 | pr_debug("thread_map__new\n"); | ||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | memset(&attr, 0, sizeof(attr)); | ||
291 | attr.type = PERF_TYPE_TRACEPOINT; | ||
292 | attr.config = id; | ||
293 | evsel = perf_evsel__new(&attr, 0); | ||
294 | if (evsel == NULL) { | ||
295 | pr_debug("perf_evsel__new\n"); | ||
296 | goto out_thread_map_delete; | ||
297 | } | ||
298 | |||
299 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | ||
300 | pr_debug("failed to open counter: %s, " | ||
301 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
302 | strerror(errno)); | ||
303 | goto out_evsel_delete; | ||
304 | } | ||
305 | |||
306 | for (i = 0; i < nr_open_calls; ++i) { | ||
307 | fd = open("/etc/passwd", O_RDONLY); | ||
308 | close(fd); | ||
309 | } | ||
310 | |||
311 | if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) { | ||
312 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
313 | goto out_close_fd; | ||
314 | } | ||
315 | |||
316 | if (evsel->counts->cpu[0].val != nr_open_calls) { | ||
317 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", | ||
318 | nr_open_calls, evsel->counts->cpu[0].val); | ||
319 | goto out_close_fd; | ||
320 | } | ||
321 | |||
322 | err = 0; | ||
323 | out_close_fd: | ||
324 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
325 | out_evsel_delete: | ||
326 | perf_evsel__delete(evsel); | ||
327 | out_thread_map_delete: | ||
328 | thread_map__delete(threads); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | #include <sched.h> | ||
333 | |||
334 | static int test__open_syscall_event_on_all_cpus(void) | ||
335 | { | ||
336 | int err = -1, fd, cpu; | ||
337 | struct thread_map *threads; | ||
338 | struct cpu_map *cpus; | ||
339 | struct perf_evsel *evsel; | ||
340 | struct perf_event_attr attr; | ||
341 | unsigned int nr_open_calls = 111, i; | ||
342 | cpu_set_t cpu_set; | ||
343 | int id = trace_event__id("sys_enter_open"); | ||
344 | |||
345 | if (id < 0) { | ||
346 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
351 | if (threads == NULL) { | ||
352 | pr_debug("thread_map__new\n"); | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | cpus = cpu_map__new(NULL); | ||
357 | if (cpus == NULL) { | ||
358 | pr_debug("cpu_map__new\n"); | ||
359 | goto out_thread_map_delete; | ||
360 | } | ||
361 | |||
362 | |||
363 | CPU_ZERO(&cpu_set); | ||
364 | |||
365 | memset(&attr, 0, sizeof(attr)); | ||
366 | attr.type = PERF_TYPE_TRACEPOINT; | ||
367 | attr.config = id; | ||
368 | evsel = perf_evsel__new(&attr, 0); | ||
369 | if (evsel == NULL) { | ||
370 | pr_debug("perf_evsel__new\n"); | ||
371 | goto out_thread_map_delete; | ||
372 | } | ||
373 | |||
374 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | ||
375 | pr_debug("failed to open counter: %s, " | ||
376 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
377 | strerror(errno)); | ||
378 | goto out_evsel_delete; | ||
379 | } | ||
380 | |||
381 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
382 | unsigned int ncalls = nr_open_calls + cpu; | ||
383 | /* | ||
384 | * XXX eventually lift this restriction in a way that | ||
385 | * keeps perf building on older glibc installations | ||
386 | * without CPU_ALLOC. 1024 cpus in 2010 still seems | ||
387 | * a reasonable upper limit tho :-) | ||
388 | */ | ||
389 | if (cpus->map[cpu] >= CPU_SETSIZE) { | ||
390 | pr_debug("Ignoring CPU %d\n", cpus->map[cpu]); | ||
391 | continue; | ||
392 | } | ||
393 | |||
394 | CPU_SET(cpus->map[cpu], &cpu_set); | ||
395 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
396 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
397 | cpus->map[cpu], | ||
398 | strerror(errno)); | ||
399 | goto out_close_fd; | ||
400 | } | ||
401 | for (i = 0; i < ncalls; ++i) { | ||
402 | fd = open("/etc/passwd", O_RDONLY); | ||
403 | close(fd); | ||
404 | } | ||
405 | CPU_CLR(cpus->map[cpu], &cpu_set); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * Here we need to explicitely preallocate the counts, as if | ||
410 | * we use the auto allocation it will allocate just for 1 cpu, | ||
411 | * as we start by cpu 0. | ||
412 | */ | ||
413 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | ||
414 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | ||
415 | goto out_close_fd; | ||
416 | } | ||
417 | |||
418 | err = 0; | ||
419 | |||
420 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
421 | unsigned int expected; | ||
422 | |||
423 | if (cpus->map[cpu] >= CPU_SETSIZE) | ||
424 | continue; | ||
425 | |||
426 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | ||
427 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
428 | err = -1; | ||
429 | break; | ||
430 | } | ||
431 | |||
432 | expected = nr_open_calls + cpu; | ||
433 | if (evsel->counts->cpu[cpu].val != expected) { | ||
434 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | ||
435 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | ||
436 | err = -1; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | out_close_fd: | ||
441 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
442 | out_evsel_delete: | ||
443 | perf_evsel__delete(evsel); | ||
444 | out_thread_map_delete: | ||
445 | thread_map__delete(threads); | ||
446 | return err; | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * This test will generate random numbers of calls to some getpid syscalls, | ||
451 | * then establish an mmap for a group of events that are created to monitor | ||
452 | * the syscalls. | ||
453 | * | ||
454 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
455 | * sample.id field to map back to its respective perf_evsel instance. | ||
456 | * | ||
457 | * Then it checks if the number of syscalls reported as perf events by | ||
458 | * the kernel corresponds to the number of syscalls made. | ||
459 | */ | ||
460 | static int test__basic_mmap(void) | ||
461 | { | ||
462 | int err = -1; | ||
463 | union perf_event *event; | ||
464 | struct thread_map *threads; | ||
465 | struct cpu_map *cpus; | ||
466 | struct perf_evlist *evlist; | ||
467 | struct perf_event_attr attr = { | ||
468 | .type = PERF_TYPE_TRACEPOINT, | ||
469 | .read_format = PERF_FORMAT_ID, | ||
470 | .sample_type = PERF_SAMPLE_ID, | ||
471 | .watermark = 0, | ||
472 | }; | ||
473 | cpu_set_t cpu_set; | ||
474 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
475 | "getpgid", }; | ||
476 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
477 | (void*)getpgid }; | ||
478 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
479 | int ids[nsyscalls]; | ||
480 | unsigned int nr_events[nsyscalls], | ||
481 | expected_nr_events[nsyscalls], i, j; | ||
482 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
483 | |||
484 | for (i = 0; i < nsyscalls; ++i) { | ||
485 | char name[64]; | ||
486 | |||
487 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
488 | ids[i] = trace_event__id(name); | ||
489 | if (ids[i] < 0) { | ||
490 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
491 | return -1; | ||
492 | } | ||
493 | nr_events[i] = 0; | ||
494 | expected_nr_events[i] = random() % 257; | ||
495 | } | ||
496 | |||
497 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
498 | if (threads == NULL) { | ||
499 | pr_debug("thread_map__new\n"); | ||
500 | return -1; | ||
501 | } | ||
502 | |||
503 | cpus = cpu_map__new(NULL); | ||
504 | if (cpus == NULL) { | ||
505 | pr_debug("cpu_map__new\n"); | ||
506 | goto out_free_threads; | ||
507 | } | ||
508 | |||
509 | CPU_ZERO(&cpu_set); | ||
510 | CPU_SET(cpus->map[0], &cpu_set); | ||
511 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
512 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
513 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
514 | cpus->map[0], strerror(errno)); | ||
515 | goto out_free_cpus; | ||
516 | } | ||
517 | |||
518 | evlist = perf_evlist__new(cpus, threads); | ||
519 | if (evlist == NULL) { | ||
520 | pr_debug("perf_evlist__new\n"); | ||
521 | goto out_free_cpus; | ||
522 | } | ||
523 | |||
524 | /* anonymous union fields, can't be initialized above */ | ||
525 | attr.wakeup_events = 1; | ||
526 | attr.sample_period = 1; | ||
527 | |||
528 | for (i = 0; i < nsyscalls; ++i) { | ||
529 | attr.config = ids[i]; | ||
530 | evsels[i] = perf_evsel__new(&attr, i); | ||
531 | if (evsels[i] == NULL) { | ||
532 | pr_debug("perf_evsel__new\n"); | ||
533 | goto out_free_evlist; | ||
534 | } | ||
535 | |||
536 | perf_evlist__add(evlist, evsels[i]); | ||
537 | |||
538 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | ||
539 | pr_debug("failed to open counter: %s, " | ||
540 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
541 | strerror(errno)); | ||
542 | goto out_close_fd; | ||
543 | } | ||
544 | } | ||
545 | |||
546 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
547 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
548 | strerror(errno)); | ||
549 | goto out_close_fd; | ||
550 | } | ||
551 | |||
552 | for (i = 0; i < nsyscalls; ++i) | ||
553 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
554 | int foo = syscalls[i](); | ||
555 | ++foo; | ||
556 | } | ||
557 | |||
558 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
559 | struct perf_sample sample; | ||
560 | |||
561 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
562 | pr_debug("unexpected %s event\n", | ||
563 | perf_event__name(event->header.type)); | ||
564 | goto out_munmap; | ||
565 | } | ||
566 | |||
567 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
568 | if (err) { | ||
569 | pr_err("Can't parse sample, err = %d\n", err); | ||
570 | goto out_munmap; | ||
571 | } | ||
572 | |||
573 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
574 | if (evsel == NULL) { | ||
575 | pr_debug("event with id %" PRIu64 | ||
576 | " doesn't map to an evsel\n", sample.id); | ||
577 | goto out_munmap; | ||
578 | } | ||
579 | nr_events[evsel->idx]++; | ||
580 | } | ||
581 | |||
582 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
583 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
584 | pr_debug("expected %d %s events, got %d\n", | ||
585 | expected_nr_events[evsel->idx], | ||
586 | perf_evsel__name(evsel), nr_events[evsel->idx]); | ||
587 | goto out_munmap; | ||
588 | } | ||
589 | } | ||
590 | |||
591 | err = 0; | ||
592 | out_munmap: | ||
593 | perf_evlist__munmap(evlist); | ||
594 | out_close_fd: | ||
595 | for (i = 0; i < nsyscalls; ++i) | ||
596 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
597 | out_free_evlist: | ||
598 | perf_evlist__delete(evlist); | ||
599 | out_free_cpus: | ||
600 | cpu_map__delete(cpus); | ||
601 | out_free_threads: | ||
602 | thread_map__delete(threads); | ||
603 | return err; | ||
604 | #undef nsyscalls | ||
605 | } | ||
606 | |||
607 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, | ||
608 | size_t *sizep) | ||
609 | { | ||
610 | cpu_set_t *mask; | ||
611 | size_t size; | ||
612 | int i, cpu = -1, nrcpus = 1024; | ||
613 | realloc: | ||
614 | mask = CPU_ALLOC(nrcpus); | ||
615 | size = CPU_ALLOC_SIZE(nrcpus); | ||
616 | CPU_ZERO_S(size, mask); | ||
617 | |||
618 | if (sched_getaffinity(pid, size, mask) == -1) { | ||
619 | CPU_FREE(mask); | ||
620 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
621 | nrcpus = nrcpus << 2; | ||
622 | goto realloc; | ||
623 | } | ||
624 | perror("sched_getaffinity"); | ||
625 | return -1; | ||
626 | } | ||
627 | |||
628 | for (i = 0; i < nrcpus; i++) { | ||
629 | if (CPU_ISSET_S(i, size, mask)) { | ||
630 | if (cpu == -1) { | ||
631 | cpu = i; | ||
632 | *maskp = mask; | ||
633 | *sizep = size; | ||
634 | } else | ||
635 | CPU_CLR_S(i, size, mask); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | if (cpu == -1) | ||
640 | CPU_FREE(mask); | ||
641 | |||
642 | return cpu; | ||
643 | } | ||
644 | |||
645 | static int test__PERF_RECORD(void) | ||
646 | { | ||
647 | struct perf_record_opts opts = { | ||
648 | .target = { | ||
649 | .uid = UINT_MAX, | ||
650 | .uses_mmap = true, | ||
651 | }, | ||
652 | .no_delay = true, | ||
653 | .freq = 10, | ||
654 | .mmap_pages = 256, | ||
655 | }; | ||
656 | cpu_set_t *cpu_mask = NULL; | ||
657 | size_t cpu_mask_size = 0; | ||
658 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
659 | struct perf_evsel *evsel; | ||
660 | struct perf_sample sample; | ||
661 | const char *cmd = "sleep"; | ||
662 | const char *argv[] = { cmd, "1", NULL, }; | ||
663 | char *bname; | ||
664 | u64 prev_time = 0; | ||
665 | bool found_cmd_mmap = false, | ||
666 | found_libc_mmap = false, | ||
667 | found_vdso_mmap = false, | ||
668 | found_ld_mmap = false; | ||
669 | int err = -1, errs = 0, i, wakeups = 0; | ||
670 | u32 cpu; | ||
671 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
672 | |||
673 | if (evlist == NULL || argv == NULL) { | ||
674 | pr_debug("Not enough memory to create evlist\n"); | ||
675 | goto out; | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * We need at least one evsel in the evlist, use the default | ||
680 | * one: "cycles". | ||
681 | */ | ||
682 | err = perf_evlist__add_default(evlist); | ||
683 | if (err < 0) { | ||
684 | pr_debug("Not enough memory to create evsel\n"); | ||
685 | goto out_delete_evlist; | ||
686 | } | ||
687 | |||
688 | /* | ||
689 | * Create maps of threads and cpus to monitor. In this case | ||
690 | * we start with all threads and cpus (-1, -1) but then in | ||
691 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
692 | * we're monitoring, the one forked there. | ||
693 | */ | ||
694 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
695 | if (err < 0) { | ||
696 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
697 | goto out_delete_evlist; | ||
698 | } | ||
699 | |||
700 | /* | ||
701 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
702 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
703 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
704 | * on all the fds) and then mmap them. | ||
705 | */ | ||
706 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
707 | if (err < 0) { | ||
708 | pr_debug("Couldn't run the workload!\n"); | ||
709 | goto out_delete_evlist; | ||
710 | } | ||
711 | |||
712 | /* | ||
713 | * Config the evsels, setting attr->comm on the first one, etc. | ||
714 | */ | ||
715 | evsel = perf_evlist__first(evlist); | ||
716 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | ||
717 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | ||
718 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | ||
719 | perf_evlist__config_attrs(evlist, &opts); | ||
720 | |||
721 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask, | ||
722 | &cpu_mask_size); | ||
723 | if (err < 0) { | ||
724 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
725 | goto out_delete_evlist; | ||
726 | } | ||
727 | |||
728 | cpu = err; | ||
729 | |||
730 | /* | ||
731 | * So that we can check perf_sample.cpu on all the samples. | ||
732 | */ | ||
733 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) { | ||
734 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
735 | goto out_free_cpu_mask; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
740 | * grouping them if asked to. | ||
741 | */ | ||
742 | err = perf_evlist__open(evlist); | ||
743 | if (err < 0) { | ||
744 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
745 | goto out_delete_evlist; | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * mmap the first fd on a given CPU and ask for events for the other | ||
750 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
751 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
752 | */ | ||
753 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
754 | if (err < 0) { | ||
755 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
756 | goto out_delete_evlist; | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * Now that all is properly set up, enable the events, they will | ||
761 | * count just on workload.pid, which will start... | ||
762 | */ | ||
763 | perf_evlist__enable(evlist); | ||
764 | |||
765 | /* | ||
766 | * Now! | ||
767 | */ | ||
768 | perf_evlist__start_workload(evlist); | ||
769 | |||
770 | while (1) { | ||
771 | int before = total_events; | ||
772 | |||
773 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
774 | union perf_event *event; | ||
775 | |||
776 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
777 | const u32 type = event->header.type; | ||
778 | const char *name = perf_event__name(type); | ||
779 | |||
780 | ++total_events; | ||
781 | if (type < PERF_RECORD_MAX) | ||
782 | nr_events[type]++; | ||
783 | |||
784 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
785 | if (err < 0) { | ||
786 | if (verbose) | ||
787 | perf_event__fprintf(event, stderr); | ||
788 | pr_debug("Couldn't parse sample\n"); | ||
789 | goto out_err; | ||
790 | } | ||
791 | |||
792 | if (verbose) { | ||
793 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
794 | perf_event__fprintf(event, stderr); | ||
795 | } | ||
796 | |||
797 | if (prev_time > sample.time) { | ||
798 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
799 | name, prev_time, sample.time); | ||
800 | ++errs; | ||
801 | } | ||
802 | |||
803 | prev_time = sample.time; | ||
804 | |||
805 | if (sample.cpu != cpu) { | ||
806 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
807 | name, cpu, sample.cpu); | ||
808 | ++errs; | ||
809 | } | ||
810 | |||
811 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
812 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
813 | name, evlist->workload.pid, sample.pid); | ||
814 | ++errs; | ||
815 | } | ||
816 | |||
817 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
818 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
819 | name, evlist->workload.pid, sample.tid); | ||
820 | ++errs; | ||
821 | } | ||
822 | |||
823 | if ((type == PERF_RECORD_COMM || | ||
824 | type == PERF_RECORD_MMAP || | ||
825 | type == PERF_RECORD_FORK || | ||
826 | type == PERF_RECORD_EXIT) && | ||
827 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
828 | pr_debug("%s with unexpected pid/tid\n", name); | ||
829 | ++errs; | ||
830 | } | ||
831 | |||
832 | if ((type == PERF_RECORD_COMM || | ||
833 | type == PERF_RECORD_MMAP) && | ||
834 | event->comm.pid != event->comm.tid) { | ||
835 | pr_debug("%s with different pid/tid!\n", name); | ||
836 | ++errs; | ||
837 | } | ||
838 | |||
839 | switch (type) { | ||
840 | case PERF_RECORD_COMM: | ||
841 | if (strcmp(event->comm.comm, cmd)) { | ||
842 | pr_debug("%s with unexpected comm!\n", name); | ||
843 | ++errs; | ||
844 | } | ||
845 | break; | ||
846 | case PERF_RECORD_EXIT: | ||
847 | goto found_exit; | ||
848 | case PERF_RECORD_MMAP: | ||
849 | bname = strrchr(event->mmap.filename, '/'); | ||
850 | if (bname != NULL) { | ||
851 | if (!found_cmd_mmap) | ||
852 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
853 | if (!found_libc_mmap) | ||
854 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
855 | if (!found_ld_mmap) | ||
856 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
857 | } else if (!found_vdso_mmap) | ||
858 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
859 | break; | ||
860 | |||
861 | case PERF_RECORD_SAMPLE: | ||
862 | /* Just ignore samples for now */ | ||
863 | break; | ||
864 | default: | ||
865 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
866 | type); | ||
867 | ++errs; | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * We don't use poll here because at least at 3.1 times the | ||
874 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
875 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
876 | */ | ||
877 | if (total_events == before && false) | ||
878 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
879 | |||
880 | sleep(1); | ||
881 | if (++wakeups > 5) { | ||
882 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
883 | break; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | found_exit: | ||
888 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
889 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
890 | ++errs; | ||
891 | } | ||
892 | |||
893 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
894 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
895 | ++errs; | ||
896 | } | ||
897 | |||
898 | if (!found_cmd_mmap) { | ||
899 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
900 | ++errs; | ||
901 | } | ||
902 | |||
903 | if (!found_libc_mmap) { | ||
904 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
905 | ++errs; | ||
906 | } | ||
907 | |||
908 | if (!found_ld_mmap) { | ||
909 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
910 | ++errs; | ||
911 | } | ||
912 | |||
913 | if (!found_vdso_mmap) { | ||
914 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
915 | ++errs; | ||
916 | } | ||
917 | out_err: | ||
918 | perf_evlist__munmap(evlist); | ||
919 | out_free_cpu_mask: | ||
920 | CPU_FREE(cpu_mask); | ||
921 | out_delete_evlist: | ||
922 | perf_evlist__delete(evlist); | ||
923 | out: | ||
924 | return (err < 0 || errs > 0) ? -1 : 0; | ||
925 | } | ||
926 | |||
927 | |||
928 | #if defined(__x86_64__) || defined(__i386__) | ||
929 | |||
930 | #define barrier() asm volatile("" ::: "memory") | ||
931 | |||
932 | static u64 rdpmc(unsigned int counter) | ||
933 | { | ||
934 | unsigned int low, high; | ||
935 | |||
936 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
937 | |||
938 | return low | ((u64)high) << 32; | ||
939 | } | ||
940 | |||
941 | static u64 rdtsc(void) | ||
942 | { | ||
943 | unsigned int low, high; | ||
944 | |||
945 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
946 | |||
947 | return low | ((u64)high) << 32; | ||
948 | } | ||
949 | |||
950 | static u64 mmap_read_self(void *addr) | ||
951 | { | ||
952 | struct perf_event_mmap_page *pc = addr; | ||
953 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
954 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
955 | |||
956 | do { | ||
957 | seq = pc->lock; | ||
958 | barrier(); | ||
959 | |||
960 | enabled = pc->time_enabled; | ||
961 | running = pc->time_running; | ||
962 | |||
963 | if (enabled != running) { | ||
964 | cyc = rdtsc(); | ||
965 | time_mult = pc->time_mult; | ||
966 | time_shift = pc->time_shift; | ||
967 | time_offset = pc->time_offset; | ||
968 | } | ||
969 | |||
970 | idx = pc->index; | ||
971 | count = pc->offset; | ||
972 | if (idx) | ||
973 | count += rdpmc(idx - 1); | ||
974 | |||
975 | barrier(); | ||
976 | } while (pc->lock != seq); | ||
977 | |||
978 | if (enabled != running) { | ||
979 | u64 quot, rem; | ||
980 | |||
981 | quot = (cyc >> time_shift); | ||
982 | rem = cyc & ((1 << time_shift) - 1); | ||
983 | delta = time_offset + quot * time_mult + | ||
984 | ((rem * time_mult) >> time_shift); | ||
985 | |||
986 | enabled += delta; | ||
987 | if (idx) | ||
988 | running += delta; | ||
989 | |||
990 | quot = count / running; | ||
991 | rem = count % running; | ||
992 | count = quot * enabled + (rem * enabled) / running; | ||
993 | } | ||
994 | |||
995 | return count; | ||
996 | } | ||
997 | |||
998 | /* | ||
999 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
1000 | */ | ||
1001 | static void segfault_handler(int sig __maybe_unused, | ||
1002 | siginfo_t *info __maybe_unused, | ||
1003 | void *uc __maybe_unused) | ||
1004 | { | ||
1005 | exit(-1); | ||
1006 | } | ||
1007 | |||
1008 | static int __test__rdpmc(void) | ||
1009 | { | ||
1010 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
1011 | volatile int tmp = 0; | ||
1012 | u64 i, loops = 1000; | ||
1013 | int n; | ||
1014 | int fd; | ||
1015 | void *addr; | ||
1016 | struct perf_event_attr attr = { | ||
1017 | .type = PERF_TYPE_HARDWARE, | ||
1018 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
1019 | .exclude_kernel = 1, | ||
1020 | }; | ||
1021 | u64 delta_sum = 0; | ||
1022 | struct sigaction sa; | ||
1023 | |||
1024 | sigfillset(&sa.sa_mask); | ||
1025 | sa.sa_sigaction = segfault_handler; | ||
1026 | sigaction(SIGSEGV, &sa, NULL); | ||
1027 | |||
1028 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
1029 | if (fd < 0) { | ||
1030 | pr_err("Error: sys_perf_event_open() syscall returned " | ||
1031 | "with %d (%s)\n", fd, strerror(errno)); | ||
1032 | return -1; | ||
1033 | } | ||
1034 | |||
1035 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
1036 | if (addr == (void *)(-1)) { | ||
1037 | pr_err("Error: mmap() syscall returned with (%s)\n", | ||
1038 | strerror(errno)); | ||
1039 | goto out_close; | ||
1040 | } | ||
1041 | |||
1042 | for (n = 0; n < 6; n++) { | ||
1043 | u64 stamp, now, delta; | ||
1044 | |||
1045 | stamp = mmap_read_self(addr); | ||
1046 | |||
1047 | for (i = 0; i < loops; i++) | ||
1048 | tmp++; | ||
1049 | |||
1050 | now = mmap_read_self(addr); | ||
1051 | loops *= 10; | ||
1052 | |||
1053 | delta = now - stamp; | ||
1054 | pr_debug("%14d: %14Lu\n", n, (long long)delta); | ||
1055 | |||
1056 | delta_sum += delta; | ||
1057 | } | ||
1058 | |||
1059 | munmap(addr, page_size); | ||
1060 | pr_debug(" "); | ||
1061 | out_close: | ||
1062 | close(fd); | ||
1063 | |||
1064 | if (!delta_sum) | ||
1065 | return -1; | ||
1066 | |||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | static int test__rdpmc(void) | ||
1071 | { | ||
1072 | int status = 0; | ||
1073 | int wret = 0; | ||
1074 | int ret; | ||
1075 | int pid; | ||
1076 | |||
1077 | pid = fork(); | ||
1078 | if (pid < 0) | ||
1079 | return -1; | ||
1080 | |||
1081 | if (!pid) { | ||
1082 | ret = __test__rdpmc(); | ||
1083 | |||
1084 | exit(ret); | ||
1085 | } | ||
1086 | |||
1087 | wret = waitpid(pid, &status, 0); | ||
1088 | if (wret < 0 || status) | ||
1089 | return -1; | ||
1090 | |||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | #endif | ||
1095 | |||
1096 | static int test__perf_pmu(void) | ||
1097 | { | ||
1098 | return perf_pmu__test(); | ||
1099 | } | ||
1100 | |||
1101 | static int perf_evsel__roundtrip_cache_name_test(void) | ||
1102 | { | ||
1103 | char name[128]; | ||
1104 | int type, op, err = 0, ret = 0, i, idx; | ||
1105 | struct perf_evsel *evsel; | ||
1106 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1107 | |||
1108 | if (evlist == NULL) | ||
1109 | return -ENOMEM; | ||
1110 | |||
1111 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
1112 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
1113 | /* skip invalid cache type */ | ||
1114 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
1115 | continue; | ||
1116 | |||
1117 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
1118 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
1119 | name, sizeof(name)); | ||
1120 | err = parse_events(evlist, name, 0); | ||
1121 | if (err) | ||
1122 | ret = err; | ||
1123 | } | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 | idx = 0; | ||
1128 | evsel = perf_evlist__first(evlist); | ||
1129 | |||
1130 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
1131 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
1132 | /* skip invalid cache type */ | ||
1133 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
1134 | continue; | ||
1135 | |||
1136 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
1137 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
1138 | name, sizeof(name)); | ||
1139 | if (evsel->idx != idx) | ||
1140 | continue; | ||
1141 | |||
1142 | ++idx; | ||
1143 | |||
1144 | if (strcmp(perf_evsel__name(evsel), name)) { | ||
1145 | pr_debug("%s != %s\n", perf_evsel__name(evsel), name); | ||
1146 | ret = -1; | ||
1147 | } | ||
1148 | |||
1149 | evsel = perf_evsel__next(evsel); | ||
1150 | } | ||
1151 | } | ||
1152 | } | ||
1153 | |||
1154 | perf_evlist__delete(evlist); | ||
1155 | return ret; | ||
1156 | } | ||
1157 | |||
1158 | static int __perf_evsel__name_array_test(const char *names[], int nr_names) | ||
1159 | { | ||
1160 | int i, err; | ||
1161 | struct perf_evsel *evsel; | ||
1162 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1163 | |||
1164 | if (evlist == NULL) | ||
1165 | return -ENOMEM; | ||
1166 | |||
1167 | for (i = 0; i < nr_names; ++i) { | ||
1168 | err = parse_events(evlist, names[i], 0); | ||
1169 | if (err) { | ||
1170 | pr_debug("failed to parse event '%s', err %d\n", | ||
1171 | names[i], err); | ||
1172 | goto out_delete_evlist; | ||
1173 | } | ||
1174 | } | ||
1175 | |||
1176 | err = 0; | ||
1177 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1178 | if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { | ||
1179 | --err; | ||
1180 | pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | out_delete_evlist: | ||
1185 | perf_evlist__delete(evlist); | ||
1186 | return err; | ||
1187 | } | ||
1188 | |||
1189 | #define perf_evsel__name_array_test(names) \ | ||
1190 | __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) | ||
1191 | |||
1192 | static int perf_evsel__roundtrip_name_test(void) | ||
1193 | { | ||
1194 | int err = 0, ret = 0; | ||
1195 | |||
1196 | err = perf_evsel__name_array_test(perf_evsel__hw_names); | ||
1197 | if (err) | ||
1198 | ret = err; | ||
1199 | |||
1200 | err = perf_evsel__name_array_test(perf_evsel__sw_names); | ||
1201 | if (err) | ||
1202 | ret = err; | ||
1203 | |||
1204 | err = perf_evsel__roundtrip_cache_name_test(); | ||
1205 | if (err) | ||
1206 | ret = err; | ||
1207 | |||
1208 | return ret; | ||
1209 | } | ||
1210 | |||
1211 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | ||
1212 | int size, bool should_be_signed) | ||
1213 | { | ||
1214 | struct format_field *field = perf_evsel__field(evsel, name); | ||
1215 | int is_signed; | ||
1216 | int ret = 0; | ||
1217 | |||
1218 | if (field == NULL) { | ||
1219 | pr_debug("%s: \"%s\" field not found!\n", evsel->name, name); | ||
1220 | return -1; | ||
1221 | } | ||
1222 | |||
1223 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | ||
1224 | if (should_be_signed && !is_signed) { | ||
1225 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | ||
1226 | evsel->name, name, is_signed, should_be_signed); | ||
1227 | ret = -1; | ||
1228 | } | ||
1229 | |||
1230 | if (field->size != size) { | ||
1231 | pr_debug("%s: \"%s\" size (%d) should be %d!\n", | ||
1232 | evsel->name, name, field->size, size); | ||
1233 | ret = -1; | ||
1234 | } | ||
1235 | |||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | static int perf_evsel__tp_sched_test(void) | ||
1240 | { | ||
1241 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | ||
1242 | int ret = 0; | ||
1243 | |||
1244 | if (evsel == NULL) { | ||
1245 | pr_debug("perf_evsel__new\n"); | ||
1246 | return -1; | ||
1247 | } | ||
1248 | |||
1249 | if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) | ||
1250 | ret = -1; | ||
1251 | |||
1252 | if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) | ||
1253 | ret = -1; | ||
1254 | |||
1255 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | ||
1256 | ret = -1; | ||
1257 | |||
1258 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | ||
1259 | ret = -1; | ||
1260 | |||
1261 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | ||
1262 | ret = -1; | ||
1263 | |||
1264 | if (perf_evsel__test_field(evsel, "next_pid", 4, true)) | ||
1265 | ret = -1; | ||
1266 | |||
1267 | if (perf_evsel__test_field(evsel, "next_prio", 4, true)) | ||
1268 | ret = -1; | ||
1269 | |||
1270 | perf_evsel__delete(evsel); | ||
1271 | |||
1272 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | ||
1273 | |||
1274 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | ||
1275 | ret = -1; | ||
1276 | |||
1277 | if (perf_evsel__test_field(evsel, "pid", 4, true)) | ||
1278 | ret = -1; | ||
1279 | |||
1280 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | ||
1281 | ret = -1; | ||
1282 | |||
1283 | if (perf_evsel__test_field(evsel, "success", 4, true)) | ||
1284 | ret = -1; | ||
1285 | |||
1286 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | ||
1287 | ret = -1; | ||
1288 | |||
1289 | return ret; | ||
1290 | } | ||
1291 | |||
1292 | static int test__syscall_open_tp_fields(void) | ||
1293 | { | ||
1294 | struct perf_record_opts opts = { | ||
1295 | .target = { | ||
1296 | .uid = UINT_MAX, | ||
1297 | .uses_mmap = true, | ||
1298 | }, | ||
1299 | .no_delay = true, | ||
1300 | .freq = 1, | ||
1301 | .mmap_pages = 256, | ||
1302 | .raw_samples = true, | ||
1303 | }; | ||
1304 | const char *filename = "/etc/passwd"; | ||
1305 | int flags = O_RDONLY | O_DIRECTORY; | ||
1306 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1307 | struct perf_evsel *evsel; | ||
1308 | int err = -1, i, nr_events = 0, nr_polls = 0; | ||
1309 | |||
1310 | if (evlist == NULL) { | ||
1311 | pr_debug("%s: perf_evlist__new\n", __func__); | ||
1312 | goto out; | ||
1313 | } | ||
1314 | |||
1315 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
1316 | if (evsel == NULL) { | ||
1317 | pr_debug("%s: perf_evsel__newtp\n", __func__); | ||
1318 | goto out_delete_evlist; | ||
1319 | } | ||
1320 | |||
1321 | perf_evlist__add(evlist, evsel); | ||
1322 | |||
1323 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
1324 | if (err < 0) { | ||
1325 | pr_debug("%s: perf_evlist__create_maps\n", __func__); | ||
1326 | goto out_delete_evlist; | ||
1327 | } | ||
1328 | |||
1329 | perf_evsel__config(evsel, &opts, evsel); | ||
1330 | |||
1331 | evlist->threads->map[0] = getpid(); | ||
1332 | |||
1333 | err = perf_evlist__open(evlist); | ||
1334 | if (err < 0) { | ||
1335 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
1336 | goto out_delete_evlist; | ||
1337 | } | ||
1338 | |||
1339 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
1340 | if (err < 0) { | ||
1341 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
1342 | goto out_delete_evlist; | ||
1343 | } | ||
1344 | |||
1345 | perf_evlist__enable(evlist); | ||
1346 | |||
1347 | /* | ||
1348 | * Generate the event: | ||
1349 | */ | ||
1350 | open(filename, flags); | ||
1351 | |||
1352 | while (1) { | ||
1353 | int before = nr_events; | ||
1354 | |||
1355 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
1356 | union perf_event *event; | ||
1357 | |||
1358 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
1359 | const u32 type = event->header.type; | ||
1360 | int tp_flags; | ||
1361 | struct perf_sample sample; | ||
1362 | |||
1363 | ++nr_events; | ||
1364 | |||
1365 | if (type != PERF_RECORD_SAMPLE) | ||
1366 | continue; | ||
1367 | |||
1368 | err = perf_evsel__parse_sample(evsel, event, &sample); | ||
1369 | if (err) { | ||
1370 | pr_err("Can't parse sample, err = %d\n", err); | ||
1371 | goto out_munmap; | ||
1372 | } | ||
1373 | |||
1374 | tp_flags = perf_evsel__intval(evsel, &sample, "flags"); | ||
1375 | |||
1376 | if (flags != tp_flags) { | ||
1377 | pr_debug("%s: Expected flags=%#x, got %#x\n", | ||
1378 | __func__, flags, tp_flags); | ||
1379 | goto out_munmap; | ||
1380 | } | ||
1381 | |||
1382 | goto out_ok; | ||
1383 | } | ||
1384 | } | ||
1385 | |||
1386 | if (nr_events == before) | ||
1387 | poll(evlist->pollfd, evlist->nr_fds, 10); | ||
1388 | |||
1389 | if (++nr_polls > 5) { | ||
1390 | pr_debug("%s: no events!\n", __func__); | ||
1391 | goto out_munmap; | ||
1392 | } | ||
1393 | } | ||
1394 | out_ok: | ||
1395 | err = 0; | ||
1396 | out_munmap: | ||
1397 | perf_evlist__munmap(evlist); | ||
1398 | out_delete_evlist: | ||
1399 | perf_evlist__delete(evlist); | ||
1400 | out: | ||
1401 | return err; | ||
1402 | } | ||
1403 | |||
1404 | static struct test { | ||
1405 | const char *desc; | ||
1406 | int (*func)(void); | ||
1407 | } tests[] = { | ||
1408 | { | ||
1409 | .desc = "vmlinux symtab matches kallsyms", | ||
1410 | .func = test__vmlinux_matches_kallsyms, | ||
1411 | }, | ||
1412 | { | ||
1413 | .desc = "detect open syscall event", | ||
1414 | .func = test__open_syscall_event, | ||
1415 | }, | ||
1416 | { | ||
1417 | .desc = "detect open syscall event on all cpus", | ||
1418 | .func = test__open_syscall_event_on_all_cpus, | ||
1419 | }, | ||
1420 | { | ||
1421 | .desc = "read samples using the mmap interface", | ||
1422 | .func = test__basic_mmap, | ||
1423 | }, | ||
1424 | { | ||
1425 | .desc = "parse events tests", | ||
1426 | .func = parse_events__test, | ||
1427 | }, | ||
1428 | #if defined(__x86_64__) || defined(__i386__) | ||
1429 | { | ||
1430 | .desc = "x86 rdpmc test", | ||
1431 | .func = test__rdpmc, | ||
1432 | }, | ||
1433 | #endif | ||
1434 | { | ||
1435 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
1436 | .func = test__PERF_RECORD, | ||
1437 | }, | ||
1438 | { | ||
1439 | .desc = "Test perf pmu format parsing", | ||
1440 | .func = test__perf_pmu, | ||
1441 | }, | ||
1442 | { | ||
1443 | .desc = "Test dso data interface", | ||
1444 | .func = dso__test_data, | ||
1445 | }, | ||
1446 | { | ||
1447 | .desc = "roundtrip evsel->name check", | ||
1448 | .func = perf_evsel__roundtrip_name_test, | ||
1449 | }, | ||
1450 | { | ||
1451 | .desc = "Check parsing of sched tracepoints fields", | ||
1452 | .func = perf_evsel__tp_sched_test, | ||
1453 | }, | ||
1454 | { | ||
1455 | .desc = "Generate and check syscalls:sys_enter_open event fields", | ||
1456 | .func = test__syscall_open_tp_fields, | ||
1457 | }, | ||
1458 | { | ||
1459 | .func = NULL, | ||
1460 | }, | ||
1461 | }; | ||
1462 | |||
1463 | static bool perf_test__matches(int curr, int argc, const char *argv[]) | ||
1464 | { | ||
1465 | int i; | ||
1466 | |||
1467 | if (argc == 0) | ||
1468 | return true; | ||
1469 | |||
1470 | for (i = 0; i < argc; ++i) { | ||
1471 | char *end; | ||
1472 | long nr = strtoul(argv[i], &end, 10); | ||
1473 | |||
1474 | if (*end == '\0') { | ||
1475 | if (nr == curr + 1) | ||
1476 | return true; | ||
1477 | continue; | ||
1478 | } | ||
1479 | |||
1480 | if (strstr(tests[curr].desc, argv[i])) | ||
1481 | return true; | ||
1482 | } | ||
1483 | |||
1484 | return false; | ||
1485 | } | ||
1486 | |||
1487 | static int __cmd_test(int argc, const char *argv[]) | ||
1488 | { | ||
1489 | int i = 0; | ||
1490 | |||
1491 | while (tests[i].func) { | ||
1492 | int curr = i++, err; | ||
1493 | |||
1494 | if (!perf_test__matches(curr, argc, argv)) | ||
1495 | continue; | ||
1496 | |||
1497 | pr_info("%2d: %s:", i, tests[curr].desc); | ||
1498 | pr_debug("\n--- start ---\n"); | ||
1499 | err = tests[curr].func(); | ||
1500 | pr_debug("---- end ----\n%s:", tests[curr].desc); | ||
1501 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); | ||
1502 | } | ||
1503 | |||
1504 | return 0; | ||
1505 | } | ||
1506 | |||
1507 | static int perf_test__list(int argc, const char **argv) | ||
1508 | { | ||
1509 | int i = 0; | ||
1510 | |||
1511 | while (tests[i].func) { | ||
1512 | int curr = i++; | ||
1513 | |||
1514 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | ||
1515 | continue; | ||
1516 | |||
1517 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
1518 | } | ||
1519 | |||
1520 | return 0; | ||
1521 | } | ||
1522 | |||
1523 | int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | ||
1524 | { | ||
1525 | const char * const test_usage[] = { | ||
1526 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
1527 | NULL, | ||
1528 | }; | ||
1529 | const struct option test_options[] = { | ||
1530 | OPT_INCR('v', "verbose", &verbose, | ||
1531 | "be more verbose (show symbol address, etc)"), | ||
1532 | OPT_END() | ||
1533 | }; | ||
1534 | |||
1535 | argc = parse_options(argc, argv, test_options, test_usage, 0); | ||
1536 | if (argc >= 1 && !strcmp(argv[0], "list")) | ||
1537 | return perf_test__list(argc, argv); | ||
1538 | |||
1539 | symbol_conf.priv_size = sizeof(int); | ||
1540 | symbol_conf.sort_by_name = true; | ||
1541 | symbol_conf.try_vmlinux_path = true; | ||
1542 | |||
1543 | if (symbol__init() < 0) | ||
1544 | return -1; | ||
1545 | |||
1546 | return __cmd_test(argc, argv); | ||
1547 | } | ||
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index f251b613b2f3..ab4cf232b852 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -965,7 +965,7 @@ static void write_svg_file(const char *filename) | |||
965 | svg_close(); | 965 | svg_close(); |
966 | } | 966 | } |
967 | 967 | ||
968 | static int __cmd_timechart(const char *input_name, const char *output_name) | 968 | static int __cmd_timechart(const char *output_name) |
969 | { | 969 | { |
970 | struct perf_tool perf_timechart = { | 970 | struct perf_tool perf_timechart = { |
971 | .comm = process_comm_event, | 971 | .comm = process_comm_event, |
@@ -1061,7 +1061,6 @@ parse_process(const struct option *opt __maybe_unused, const char *arg, | |||
1061 | int cmd_timechart(int argc, const char **argv, | 1061 | int cmd_timechart(int argc, const char **argv, |
1062 | const char *prefix __maybe_unused) | 1062 | const char *prefix __maybe_unused) |
1063 | { | 1063 | { |
1064 | const char *input_name; | ||
1065 | const char *output_name = "output.svg"; | 1064 | const char *output_name = "output.svg"; |
1066 | const struct option options[] = { | 1065 | const struct option options[] = { |
1067 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 1066 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
@@ -1092,5 +1091,5 @@ int cmd_timechart(int argc, const char **argv, | |||
1092 | 1091 | ||
1093 | setup_pager(); | 1092 | setup_pager(); |
1094 | 1093 | ||
1095 | return __cmd_timechart(input_name, output_name); | 1094 | return __cmd_timechart(output_name); |
1096 | } | 1095 | } |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ff6db8086805..c9ff3950cd4b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "util/color.h" | 26 | #include "util/color.h" |
27 | #include "util/evlist.h" | 27 | #include "util/evlist.h" |
28 | #include "util/evsel.h" | 28 | #include "util/evsel.h" |
29 | #include "util/machine.h" | ||
29 | #include "util/session.h" | 30 | #include "util/session.h" |
30 | #include "util/symbol.h" | 31 | #include "util/symbol.h" |
31 | #include "util/thread.h" | 32 | #include "util/thread.h" |
@@ -581,6 +582,11 @@ static void *display_thread_tui(void *arg) | |||
581 | struct perf_evsel *pos; | 582 | struct perf_evsel *pos; |
582 | struct perf_top *top = arg; | 583 | struct perf_top *top = arg; |
583 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; | 584 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
585 | struct hist_browser_timer hbt = { | ||
586 | .timer = perf_top__sort_new_samples, | ||
587 | .arg = top, | ||
588 | .refresh = top->delay_secs, | ||
589 | }; | ||
584 | 590 | ||
585 | perf_top__sort_new_samples(top); | 591 | perf_top__sort_new_samples(top); |
586 | 592 | ||
@@ -592,9 +598,8 @@ static void *display_thread_tui(void *arg) | |||
592 | list_for_each_entry(pos, &top->evlist->entries, node) | 598 | list_for_each_entry(pos, &top->evlist->entries, node) |
593 | pos->hists.uid_filter_str = top->target.uid_str; | 599 | pos->hists.uid_filter_str = top->target.uid_str; |
594 | 600 | ||
595 | perf_evlist__tui_browse_hists(top->evlist, help, | 601 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, |
596 | perf_top__sort_new_samples, | 602 | &top->session->header.env); |
597 | top, top->delay_secs); | ||
598 | 603 | ||
599 | exit_browser(0); | 604 | exit_browser(0); |
600 | exit(0); | 605 | exit(0); |
@@ -871,7 +876,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
871 | &sample, machine); | 876 | &sample, machine); |
872 | } else if (event->header.type < PERF_RECORD_MAX) { | 877 | } else if (event->header.type < PERF_RECORD_MAX) { |
873 | hists__inc_nr_events(&evsel->hists, event->header.type); | 878 | hists__inc_nr_events(&evsel->hists, event->header.type); |
874 | perf_event__process(&top->tool, event, &sample, machine); | 879 | machine__process_event(machine, event); |
875 | } else | 880 | } else |
876 | ++session->hists.stats.nr_unknown_events; | 881 | ++session->hists.stats.nr_unknown_events; |
877 | } | 882 | } |
@@ -976,6 +981,10 @@ try_again: | |||
976 | ui__error("Too many events are opened.\n" | 981 | ui__error("Too many events are opened.\n" |
977 | "Try again after reducing the number of events\n"); | 982 | "Try again after reducing the number of events\n"); |
978 | goto out_err; | 983 | goto out_err; |
984 | } else if ((err == EOPNOTSUPP) && (attr->precise_ip)) { | ||
985 | ui__error("\'precise\' request may not be supported. " | ||
986 | "Try removing 'p' modifier\n"); | ||
987 | goto out_err; | ||
979 | } | 988 | } |
980 | 989 | ||
981 | ui__error("The sys_perf_event_open() syscall " | 990 | ui__error("The sys_perf_event_open() syscall " |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7aaee39f6774..7932ffa29889 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "util/color.h" | ||
2 | #include "util/evlist.h" | 3 | #include "util/evlist.h" |
4 | #include "util/machine.h" | ||
5 | #include "util/thread.h" | ||
3 | #include "util/parse-options.h" | 6 | #include "util/parse-options.h" |
4 | #include "util/thread_map.h" | 7 | #include "util/thread_map.h" |
5 | #include "event-parse.h" | 8 | #include "event-parse.h" |
@@ -13,15 +16,18 @@ static struct syscall_fmt { | |||
13 | bool errmsg; | 16 | bool errmsg; |
14 | bool timeout; | 17 | bool timeout; |
15 | } syscall_fmts[] = { | 18 | } syscall_fmts[] = { |
19 | { .name = "access", .errmsg = true, }, | ||
16 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 20 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
17 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 21 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
18 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 22 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
19 | { .name = "futex", .errmsg = true, }, | 23 | { .name = "futex", .errmsg = true, }, |
24 | { .name = "open", .errmsg = true, }, | ||
20 | { .name = "poll", .errmsg = true, .timeout = true, }, | 25 | { .name = "poll", .errmsg = true, .timeout = true, }, |
21 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 26 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
22 | { .name = "read", .errmsg = true, }, | 27 | { .name = "read", .errmsg = true, }, |
23 | { .name = "recvfrom", .errmsg = true, }, | 28 | { .name = "recvfrom", .errmsg = true, }, |
24 | { .name = "select", .errmsg = true, .timeout = true, }, | 29 | { .name = "select", .errmsg = true, .timeout = true, }, |
30 | { .name = "socket", .errmsg = true, }, | ||
25 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 31 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
26 | }; | 32 | }; |
27 | 33 | ||
@@ -43,6 +49,57 @@ struct syscall { | |||
43 | struct syscall_fmt *fmt; | 49 | struct syscall_fmt *fmt; |
44 | }; | 50 | }; |
45 | 51 | ||
52 | static size_t fprintf_duration(unsigned long t, FILE *fp) | ||
53 | { | ||
54 | double duration = (double)t / NSEC_PER_MSEC; | ||
55 | size_t printed = fprintf(fp, "("); | ||
56 | |||
57 | if (duration >= 1.0) | ||
58 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); | ||
59 | else if (duration >= 0.01) | ||
60 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | ||
61 | else | ||
62 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | ||
63 | return printed + fprintf(stdout, "): "); | ||
64 | } | ||
65 | |||
66 | struct thread_trace { | ||
67 | u64 entry_time; | ||
68 | u64 exit_time; | ||
69 | bool entry_pending; | ||
70 | unsigned long nr_events; | ||
71 | char *entry_str; | ||
72 | double runtime_ms; | ||
73 | }; | ||
74 | |||
75 | static struct thread_trace *thread_trace__new(void) | ||
76 | { | ||
77 | return zalloc(sizeof(struct thread_trace)); | ||
78 | } | ||
79 | |||
80 | static struct thread_trace *thread__trace(struct thread *thread) | ||
81 | { | ||
82 | struct thread_trace *ttrace; | ||
83 | |||
84 | if (thread == NULL) | ||
85 | goto fail; | ||
86 | |||
87 | if (thread->priv == NULL) | ||
88 | thread->priv = thread_trace__new(); | ||
89 | |||
90 | if (thread->priv == NULL) | ||
91 | goto fail; | ||
92 | |||
93 | ttrace = thread->priv; | ||
94 | ++ttrace->nr_events; | ||
95 | |||
96 | return ttrace; | ||
97 | fail: | ||
98 | color_fprintf(stdout, PERF_COLOR_RED, | ||
99 | "WARNING: not enough memory, dropping samples!\n"); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
46 | struct trace { | 103 | struct trace { |
47 | int audit_machine; | 104 | int audit_machine; |
48 | struct { | 105 | struct { |
@@ -50,8 +107,96 @@ struct trace { | |||
50 | struct syscall *table; | 107 | struct syscall *table; |
51 | } syscalls; | 108 | } syscalls; |
52 | struct perf_record_opts opts; | 109 | struct perf_record_opts opts; |
110 | struct machine host; | ||
111 | u64 base_time; | ||
112 | unsigned long nr_events; | ||
113 | bool sched; | ||
114 | bool multiple_threads; | ||
115 | double duration_filter; | ||
116 | double runtime_ms; | ||
53 | }; | 117 | }; |
54 | 118 | ||
119 | static bool trace__filter_duration(struct trace *trace, double t) | ||
120 | { | ||
121 | return t < (trace->duration_filter * NSEC_PER_MSEC); | ||
122 | } | ||
123 | |||
124 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) | ||
125 | { | ||
126 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; | ||
127 | |||
128 | return fprintf(fp, "%10.3f ", ts); | ||
129 | } | ||
130 | |||
131 | static bool done = false; | ||
132 | |||
133 | static void sig_handler(int sig __maybe_unused) | ||
134 | { | ||
135 | done = true; | ||
136 | } | ||
137 | |||
138 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
139 | u64 duration, u64 tstamp, FILE *fp) | ||
140 | { | ||
141 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
142 | printed += fprintf_duration(duration, fp); | ||
143 | |||
144 | if (trace->multiple_threads) | ||
145 | printed += fprintf(fp, "%d ", thread->pid); | ||
146 | |||
147 | return printed; | ||
148 | } | ||
149 | |||
150 | static int trace__process_event(struct machine *machine, union perf_event *event) | ||
151 | { | ||
152 | int ret = 0; | ||
153 | |||
154 | switch (event->header.type) { | ||
155 | case PERF_RECORD_LOST: | ||
156 | color_fprintf(stdout, PERF_COLOR_RED, | ||
157 | "LOST %" PRIu64 " events!\n", event->lost.lost); | ||
158 | ret = machine__process_lost_event(machine, event); | ||
159 | default: | ||
160 | ret = machine__process_event(machine, event); | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | ||
168 | union perf_event *event, | ||
169 | struct perf_sample *sample __maybe_unused, | ||
170 | struct machine *machine) | ||
171 | { | ||
172 | return trace__process_event(machine, event); | ||
173 | } | ||
174 | |||
175 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | ||
176 | { | ||
177 | int err = symbol__init(); | ||
178 | |||
179 | if (err) | ||
180 | return err; | ||
181 | |||
182 | machine__init(&trace->host, "", HOST_KERNEL_ID); | ||
183 | machine__create_kernel_maps(&trace->host); | ||
184 | |||
185 | if (perf_target__has_task(&trace->opts.target)) { | ||
186 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | ||
187 | trace__tool_process, | ||
188 | &trace->host); | ||
189 | } else { | ||
190 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | ||
191 | &trace->host); | ||
192 | } | ||
193 | |||
194 | if (err) | ||
195 | symbol__exit(); | ||
196 | |||
197 | return err; | ||
198 | } | ||
199 | |||
55 | static int trace__read_syscall_info(struct trace *trace, int id) | 200 | static int trace__read_syscall_info(struct trace *trace, int id) |
56 | { | 201 | { |
57 | char tp_name[128]; | 202 | char tp_name[128]; |
@@ -93,7 +238,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
93 | return sc->tp_format != NULL ? 0 : -1; | 238 | return sc->tp_format != NULL ? 0 : -1; |
94 | } | 239 | } |
95 | 240 | ||
96 | static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) | 241 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
242 | unsigned long *args) | ||
97 | { | 243 | { |
98 | int i = 0; | 244 | int i = 0; |
99 | size_t printed = 0; | 245 | size_t printed = 0; |
@@ -102,12 +248,15 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL | |||
102 | struct format_field *field; | 248 | struct format_field *field; |
103 | 249 | ||
104 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 250 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
105 | printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", | 251 | printed += scnprintf(bf + printed, size - printed, |
106 | field->name, args[i++]); | 252 | "%s%s: %ld", printed ? ", " : "", |
253 | field->name, args[i++]); | ||
107 | } | 254 | } |
108 | } else { | 255 | } else { |
109 | while (i < 6) { | 256 | while (i < 6) { |
110 | printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); | 257 | printed += scnprintf(bf + printed, size - printed, |
258 | "%sarg%d: %ld", | ||
259 | printed ? ", " : "", i, args[i]); | ||
111 | ++i; | 260 | ++i; |
112 | } | 261 | } |
113 | } | 262 | } |
@@ -139,17 +288,24 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
139 | return &trace->syscalls.table[id]; | 288 | return &trace->syscalls.table[id]; |
140 | 289 | ||
141 | out_cant_read: | 290 | out_cant_read: |
142 | printf("Problems reading syscall %d information\n", id); | 291 | printf("Problems reading syscall %d", id); |
292 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | ||
293 | printf("(%s)", trace->syscalls.table[id].name); | ||
294 | puts(" information"); | ||
143 | return NULL; | 295 | return NULL; |
144 | } | 296 | } |
145 | 297 | ||
146 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 298 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
147 | struct perf_sample *sample) | 299 | struct perf_sample *sample) |
148 | { | 300 | { |
301 | char *msg; | ||
149 | void *args; | 302 | void *args; |
303 | size_t printed = 0; | ||
304 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
150 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 305 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
306 | struct thread_trace *ttrace = thread__trace(thread); | ||
151 | 307 | ||
152 | if (sc == NULL) | 308 | if (ttrace == NULL || sc == NULL) |
153 | return -1; | 309 | return -1; |
154 | 310 | ||
155 | args = perf_evsel__rawptr(evsel, sample, "args"); | 311 | args = perf_evsel__rawptr(evsel, sample, "args"); |
@@ -158,8 +314,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
158 | return -1; | 314 | return -1; |
159 | } | 315 | } |
160 | 316 | ||
161 | printf("%s(", sc->name); | 317 | ttrace = thread->priv; |
162 | syscall__fprintf_args(sc, args, stdout); | 318 | |
319 | if (ttrace->entry_str == NULL) { | ||
320 | ttrace->entry_str = malloc(1024); | ||
321 | if (!ttrace->entry_str) | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | ttrace->entry_time = sample->time; | ||
326 | msg = ttrace->entry_str; | ||
327 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); | ||
328 | |||
329 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); | ||
330 | |||
331 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | ||
332 | if (!trace->duration_filter) { | ||
333 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | ||
334 | printf("%-70s\n", ttrace->entry_str); | ||
335 | } | ||
336 | } else | ||
337 | ttrace->entry_pending = true; | ||
163 | 338 | ||
164 | return 0; | 339 | return 0; |
165 | } | 340 | } |
@@ -168,13 +343,37 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
168 | struct perf_sample *sample) | 343 | struct perf_sample *sample) |
169 | { | 344 | { |
170 | int ret; | 345 | int ret; |
346 | u64 duration = 0; | ||
347 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
348 | struct thread_trace *ttrace = thread__trace(thread); | ||
171 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 349 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
172 | 350 | ||
173 | if (sc == NULL) | 351 | if (ttrace == NULL || sc == NULL) |
174 | return -1; | 352 | return -1; |
175 | 353 | ||
176 | ret = perf_evsel__intval(evsel, sample, "ret"); | 354 | ret = perf_evsel__intval(evsel, sample, "ret"); |
177 | 355 | ||
356 | ttrace = thread->priv; | ||
357 | |||
358 | ttrace->exit_time = sample->time; | ||
359 | |||
360 | if (ttrace->entry_time) { | ||
361 | duration = sample->time - ttrace->entry_time; | ||
362 | if (trace__filter_duration(trace, duration)) | ||
363 | goto out; | ||
364 | } else if (trace->duration_filter) | ||
365 | goto out; | ||
366 | |||
367 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | ||
368 | |||
369 | if (ttrace->entry_pending) { | ||
370 | printf("%-70s", ttrace->entry_str); | ||
371 | } else { | ||
372 | printf(" ... ["); | ||
373 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | ||
374 | printf("]: %s()", sc->name); | ||
375 | } | ||
376 | |||
178 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 377 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { |
179 | char bf[256]; | 378 | char bf[256]; |
180 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 379 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
@@ -187,14 +386,44 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
187 | printf(") = %d", ret); | 386 | printf(") = %d", ret); |
188 | 387 | ||
189 | putchar('\n'); | 388 | putchar('\n'); |
389 | out: | ||
390 | ttrace->entry_pending = false; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | ||
396 | struct perf_sample *sample) | ||
397 | { | ||
398 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | ||
399 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | ||
400 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
401 | struct thread_trace *ttrace = thread__trace(thread); | ||
402 | |||
403 | if (ttrace == NULL) | ||
404 | goto out_dump; | ||
405 | |||
406 | ttrace->runtime_ms += runtime_ms; | ||
407 | trace->runtime_ms += runtime_ms; | ||
408 | return 0; | ||
409 | |||
410 | out_dump: | ||
411 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | ||
412 | evsel->name, | ||
413 | perf_evsel__strval(evsel, sample, "comm"), | ||
414 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | ||
415 | runtime, | ||
416 | perf_evsel__intval(evsel, sample, "vruntime")); | ||
190 | return 0; | 417 | return 0; |
191 | } | 418 | } |
192 | 419 | ||
193 | static int trace__run(struct trace *trace) | 420 | static int trace__run(struct trace *trace, int argc, const char **argv) |
194 | { | 421 | { |
195 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 422 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); |
196 | struct perf_evsel *evsel; | 423 | struct perf_evsel *evsel; |
197 | int err = -1, i, nr_events = 0, before; | 424 | int err = -1, i; |
425 | unsigned long before; | ||
426 | const bool forks = argc > 0; | ||
198 | 427 | ||
199 | if (evlist == NULL) { | 428 | if (evlist == NULL) { |
200 | printf("Not enough memory to run!\n"); | 429 | printf("Not enough memory to run!\n"); |
@@ -207,14 +436,38 @@ static int trace__run(struct trace *trace) | |||
207 | goto out_delete_evlist; | 436 | goto out_delete_evlist; |
208 | } | 437 | } |
209 | 438 | ||
439 | if (trace->sched && | ||
440 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | ||
441 | trace__sched_stat_runtime)) { | ||
442 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | ||
443 | goto out_delete_evlist; | ||
444 | } | ||
445 | |||
210 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 446 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
211 | if (err < 0) { | 447 | if (err < 0) { |
212 | printf("Problems parsing the target to trace, check your options!\n"); | 448 | printf("Problems parsing the target to trace, check your options!\n"); |
213 | goto out_delete_evlist; | 449 | goto out_delete_evlist; |
214 | } | 450 | } |
215 | 451 | ||
452 | err = trace__symbols_init(trace, evlist); | ||
453 | if (err < 0) { | ||
454 | printf("Problems initializing symbol libraries!\n"); | ||
455 | goto out_delete_evlist; | ||
456 | } | ||
457 | |||
216 | perf_evlist__config_attrs(evlist, &trace->opts); | 458 | perf_evlist__config_attrs(evlist, &trace->opts); |
217 | 459 | ||
460 | signal(SIGCHLD, sig_handler); | ||
461 | signal(SIGINT, sig_handler); | ||
462 | |||
463 | if (forks) { | ||
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); | ||
465 | if (err < 0) { | ||
466 | printf("Couldn't run the workload!\n"); | ||
467 | goto out_delete_evlist; | ||
468 | } | ||
469 | } | ||
470 | |||
218 | err = perf_evlist__open(evlist); | 471 | err = perf_evlist__open(evlist); |
219 | if (err < 0) { | 472 | if (err < 0) { |
220 | printf("Couldn't create the events: %s\n", strerror(errno)); | 473 | printf("Couldn't create the events: %s\n", strerror(errno)); |
@@ -228,8 +481,13 @@ static int trace__run(struct trace *trace) | |||
228 | } | 481 | } |
229 | 482 | ||
230 | perf_evlist__enable(evlist); | 483 | perf_evlist__enable(evlist); |
484 | |||
485 | if (forks) | ||
486 | perf_evlist__start_workload(evlist); | ||
487 | |||
488 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; | ||
231 | again: | 489 | again: |
232 | before = nr_events; | 490 | before = trace->nr_events; |
233 | 491 | ||
234 | for (i = 0; i < evlist->nr_mmaps; i++) { | 492 | for (i = 0; i < evlist->nr_mmaps; i++) { |
235 | union perf_event *event; | 493 | union perf_event *event; |
@@ -239,19 +497,7 @@ again: | |||
239 | tracepoint_handler handler; | 497 | tracepoint_handler handler; |
240 | struct perf_sample sample; | 498 | struct perf_sample sample; |
241 | 499 | ||
242 | ++nr_events; | 500 | ++trace->nr_events; |
243 | |||
244 | switch (type) { | ||
245 | case PERF_RECORD_SAMPLE: | ||
246 | break; | ||
247 | case PERF_RECORD_LOST: | ||
248 | printf("LOST %" PRIu64 " events!\n", event->lost.lost); | ||
249 | continue; | ||
250 | default: | ||
251 | printf("Unexpected %s event, skipping...\n", | ||
252 | perf_event__name(type)); | ||
253 | continue; | ||
254 | } | ||
255 | 501 | ||
256 | err = perf_evlist__parse_sample(evlist, event, &sample); | 502 | err = perf_evlist__parse_sample(evlist, event, &sample); |
257 | if (err) { | 503 | if (err) { |
@@ -259,14 +505,26 @@ again: | |||
259 | continue; | 505 | continue; |
260 | } | 506 | } |
261 | 507 | ||
508 | if (trace->base_time == 0) | ||
509 | trace->base_time = sample.time; | ||
510 | |||
511 | if (type != PERF_RECORD_SAMPLE) { | ||
512 | trace__process_event(&trace->host, event); | ||
513 | continue; | ||
514 | } | ||
515 | |||
262 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 516 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
263 | if (evsel == NULL) { | 517 | if (evsel == NULL) { |
264 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 518 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
265 | continue; | 519 | continue; |
266 | } | 520 | } |
267 | 521 | ||
268 | if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) | 522 | if (sample.raw_data == NULL) { |
269 | printf("%d ", sample.tid); | 523 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
524 | perf_evsel__name(evsel), sample.tid, | ||
525 | sample.cpu, sample.raw_size); | ||
526 | continue; | ||
527 | } | ||
270 | 528 | ||
271 | if (sample.raw_data == NULL) { | 529 | if (sample.raw_data == NULL) { |
272 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 530 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
@@ -280,8 +538,15 @@ again: | |||
280 | } | 538 | } |
281 | } | 539 | } |
282 | 540 | ||
283 | if (nr_events == before) | 541 | if (trace->nr_events == before) { |
542 | if (done) | ||
543 | goto out_delete_evlist; | ||
544 | |||
284 | poll(evlist->pollfd, evlist->nr_fds, -1); | 545 | poll(evlist->pollfd, evlist->nr_fds, -1); |
546 | } | ||
547 | |||
548 | if (done) | ||
549 | perf_evlist__disable(evlist); | ||
285 | 550 | ||
286 | goto again; | 551 | goto again; |
287 | 552 | ||
@@ -291,10 +556,65 @@ out: | |||
291 | return err; | 556 | return err; |
292 | } | 557 | } |
293 | 558 | ||
559 | static size_t trace__fprintf_threads_header(FILE *fp) | ||
560 | { | ||
561 | size_t printed; | ||
562 | |||
563 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); | ||
564 | printed += fprintf(fp," __) Summary of events (__\n\n"); | ||
565 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | ||
566 | printed += fprintf(fp," _____________________________________________________________________\n\n"); | ||
567 | |||
568 | return printed; | ||
569 | } | ||
570 | |||
571 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) | ||
572 | { | ||
573 | size_t printed = trace__fprintf_threads_header(fp); | ||
574 | struct rb_node *nd; | ||
575 | |||
576 | for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { | ||
577 | struct thread *thread = rb_entry(nd, struct thread, rb_node); | ||
578 | struct thread_trace *ttrace = thread->priv; | ||
579 | const char *color; | ||
580 | double ratio; | ||
581 | |||
582 | if (ttrace == NULL) | ||
583 | continue; | ||
584 | |||
585 | ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; | ||
586 | |||
587 | color = PERF_COLOR_NORMAL; | ||
588 | if (ratio > 50.0) | ||
589 | color = PERF_COLOR_RED; | ||
590 | else if (ratio > 25.0) | ||
591 | color = PERF_COLOR_GREEN; | ||
592 | else if (ratio > 5.0) | ||
593 | color = PERF_COLOR_YELLOW; | ||
594 | |||
595 | printed += color_fprintf(fp, color, "%20s", thread->comm); | ||
596 | printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events); | ||
597 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | ||
598 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | ||
599 | } | ||
600 | |||
601 | return printed; | ||
602 | } | ||
603 | |||
604 | static int trace__set_duration(const struct option *opt, const char *str, | ||
605 | int unset __maybe_unused) | ||
606 | { | ||
607 | struct trace *trace = opt->value; | ||
608 | |||
609 | trace->duration_filter = atof(str); | ||
610 | return 0; | ||
611 | } | ||
612 | |||
294 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 613 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
295 | { | 614 | { |
296 | const char * const trace_usage[] = { | 615 | const char * const trace_usage[] = { |
297 | "perf trace [<options>]", | 616 | "perf trace [<options>] [<command>]", |
617 | "perf trace [<options>] -- <command> [<options>]", | ||
298 | NULL | 618 | NULL |
299 | }; | 619 | }; |
300 | struct trace trace = { | 620 | struct trace trace = { |
@@ -328,21 +648,38 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
328 | "number of mmap data pages"), | 648 | "number of mmap data pages"), |
329 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", | 649 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", |
330 | "user to profile"), | 650 | "user to profile"), |
651 | OPT_CALLBACK(0, "duration", &trace, "float", | ||
652 | "show only events with duration > N.M ms", | ||
653 | trace__set_duration), | ||
654 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | ||
331 | OPT_END() | 655 | OPT_END() |
332 | }; | 656 | }; |
333 | int err; | 657 | int err; |
658 | char bf[BUFSIZ]; | ||
334 | 659 | ||
335 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 660 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
336 | if (argc) | 661 | |
337 | usage_with_options(trace_usage, trace_options); | 662 | err = perf_target__validate(&trace.opts.target); |
663 | if (err) { | ||
664 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | ||
665 | printf("%s", bf); | ||
666 | return err; | ||
667 | } | ||
338 | 668 | ||
339 | err = perf_target__parse_uid(&trace.opts.target); | 669 | err = perf_target__parse_uid(&trace.opts.target); |
340 | if (err) { | 670 | if (err) { |
341 | char bf[BUFSIZ]; | ||
342 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 671 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
343 | printf("%s", bf); | 672 | printf("%s", bf); |
344 | return err; | 673 | return err; |
345 | } | 674 | } |
346 | 675 | ||
347 | return trace__run(&trace); | 676 | if (!argc && perf_target__none(&trace.opts.target)) |
677 | trace.opts.target.system_wide = true; | ||
678 | |||
679 | err = trace__run(&trace, argc, argv); | ||
680 | |||
681 | if (trace.sched && !err) | ||
682 | trace__fprintf_thread_summary(&trace, stdout); | ||
683 | |||
684 | return err; | ||
348 | } | 685 | } |
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index 4add41bb0c7e..f5ac77485a4f 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -43,6 +43,15 @@ int main(void) | |||
43 | } | 43 | } |
44 | endef | 44 | endef |
45 | 45 | ||
46 | define SOURCE_BIONIC | ||
47 | #include <android/api-level.h> | ||
48 | |||
49 | int main(void) | ||
50 | { | ||
51 | return __ANDROID_API__; | ||
52 | } | ||
53 | endef | ||
54 | |||
46 | define SOURCE_ELF_MMAP | 55 | define SOURCE_ELF_MMAP |
47 | #include <libelf.h> | 56 | #include <libelf.h> |
48 | int main(void) | 57 | int main(void) |
@@ -112,7 +121,10 @@ define SOURCE_PYTHON_VERSION | |||
112 | #if PY_VERSION_HEX >= 0x03000000 | 121 | #if PY_VERSION_HEX >= 0x03000000 |
113 | #error | 122 | #error |
114 | #endif | 123 | #endif |
115 | int main(void){} | 124 | int main(void) |
125 | { | ||
126 | return 0; | ||
127 | } | ||
116 | endef | 128 | endef |
117 | define SOURCE_PYTHON_EMBED | 129 | define SOURCE_PYTHON_EMBED |
118 | #include <Python.h> | 130 | #include <Python.h> |
@@ -203,4 +215,13 @@ int main(void) | |||
203 | return audit_open(); | 215 | return audit_open(); |
204 | } | 216 | } |
205 | endef | 217 | endef |
206 | endif \ No newline at end of file | 218 | endif |
219 | |||
220 | define SOURCE_ON_EXIT | ||
221 | #include <stdio.h> | ||
222 | |||
223 | int main(void) | ||
224 | { | ||
225 | return on_exit(NULL, NULL); | ||
226 | } | ||
227 | endef | ||
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 8046182a19eb..e5413125e6bb 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak | |||
@@ -180,9 +180,15 @@ _gea_warn = $(warning The path '$(1)' is not executable.) | |||
180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) | 180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) |
181 | 181 | ||
182 | # try-cc | 182 | # try-cc |
183 | # Usage: option = $(call try-cc, source-to-build, cc-options) | 183 | # Usage: option = $(call try-cc, source-to-build, cc-options, msg) |
184 | ifndef V | ||
185 | TRY_CC_OUTPUT= > /dev/null 2>&1 | ||
186 | endif | ||
187 | TRY_CC_MSG=echo " CHK $(3)" 1>&2; | ||
188 | |||
184 | try-cc = $(shell sh -c \ | 189 | try-cc = $(shell sh -c \ |
185 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ | 190 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ |
191 | $(TRY_CC_MSG) \ | ||
186 | echo "$(1)" | \ | 192 | echo "$(1)" | \ |
187 | $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ | 193 | $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \ |
188 | rm -f "$$TMP"') | 194 | rm -f "$$TMP"') |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6d50eb0b4251..0f661fbce6a8 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -24,6 +24,7 @@ const char perf_more_info_string[] = | |||
24 | 24 | ||
25 | int use_browser = -1; | 25 | int use_browser = -1; |
26 | static int use_pager = -1; | 26 | static int use_pager = -1; |
27 | const char *input_name; | ||
27 | 28 | ||
28 | struct cmd_struct { | 29 | struct cmd_struct { |
29 | const char *cmd; | 30 | const char *cmd; |
@@ -84,21 +85,26 @@ int check_pager_config(const char *cmd) | |||
84 | return c.val; | 85 | return c.val; |
85 | } | 86 | } |
86 | 87 | ||
87 | static int tui_command_config(const char *var, const char *value, void *data) | 88 | static int browser_command_config(const char *var, const char *value, void *data) |
88 | { | 89 | { |
89 | struct pager_config *c = data; | 90 | struct pager_config *c = data; |
90 | if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd)) | 91 | if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd)) |
91 | c->val = perf_config_bool(var, value); | 92 | c->val = perf_config_bool(var, value); |
93 | if (!prefixcmp(var, "gtk.") && !strcmp(var + 4, c->cmd)) | ||
94 | c->val = perf_config_bool(var, value) ? 2 : 0; | ||
92 | return 0; | 95 | return 0; |
93 | } | 96 | } |
94 | 97 | ||
95 | /* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */ | 98 | /* |
96 | static int check_tui_config(const char *cmd) | 99 | * returns 0 for "no tui", 1 for "use tui", 2 for "use gtk", |
100 | * and -1 for "not specified" | ||
101 | */ | ||
102 | static int check_browser_config(const char *cmd) | ||
97 | { | 103 | { |
98 | struct pager_config c; | 104 | struct pager_config c; |
99 | c.cmd = cmd; | 105 | c.cmd = cmd; |
100 | c.val = -1; | 106 | c.val = -1; |
101 | perf_config(tui_command_config, &c); | 107 | perf_config(browser_command_config, &c); |
102 | return c.val; | 108 | return c.val; |
103 | } | 109 | } |
104 | 110 | ||
@@ -301,7 +307,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
301 | prefix = NULL; /* setup_perf_directory(); */ | 307 | prefix = NULL; /* setup_perf_directory(); */ |
302 | 308 | ||
303 | if (use_browser == -1) | 309 | if (use_browser == -1) |
304 | use_browser = check_tui_config(p->cmd); | 310 | use_browser = check_browser_config(p->cmd); |
305 | 311 | ||
306 | if (use_pager == -1 && p->option & RUN_SETUP) | 312 | if (use_pager == -1 && p->option & RUN_SETUP) |
307 | use_pager = check_pager_config(p->cmd); | 313 | use_pager = check_pager_config(p->cmd); |
@@ -440,6 +446,8 @@ int main(int argc, const char **argv) | |||
440 | { | 446 | { |
441 | const char *cmd; | 447 | const char *cmd; |
442 | 448 | ||
449 | page_size = sysconf(_SC_PAGE_SIZE); | ||
450 | |||
443 | cmd = perf_extract_argv0_path(argv[0]); | 451 | cmd = perf_extract_argv0_path(argv[0]); |
444 | if (!cmd) | 452 | if (!cmd) |
445 | cmd = "perf-help"; | 453 | cmd = "perf-help"; |
@@ -481,6 +489,8 @@ int main(int argc, const char **argv) | |||
481 | } | 489 | } |
482 | cmd = argv[0]; | 490 | cmd = argv[0]; |
483 | 491 | ||
492 | test_attr__init(); | ||
493 | |||
484 | /* | 494 | /* |
485 | * We use PATH to find perf commands, but we prepend some higher | 495 | * We use PATH to find perf commands, but we prepend some higher |
486 | * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH | 496 | * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 238f923f2218..2c340e7da458 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -26,6 +26,7 @@ void get_term_dimensions(struct winsize *ws); | |||
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | #ifdef __powerpc__ | 28 | #ifdef __powerpc__ |
29 | #include "../../arch/powerpc/include/uapi/asm/unistd.h" | ||
29 | #define rmb() asm volatile ("sync" ::: "memory") | 30 | #define rmb() asm volatile ("sync" ::: "memory") |
30 | #define cpu_relax() asm volatile ("" ::: "memory"); | 31 | #define cpu_relax() asm volatile ("" ::: "memory"); |
31 | #define CPUINFO_PROC "cpu" | 32 | #define CPUINFO_PROC "cpu" |
@@ -164,13 +165,25 @@ static inline unsigned long long rdclock(void) | |||
164 | (void) (&_min1 == &_min2); \ | 165 | (void) (&_min1 == &_min2); \ |
165 | _min1 < _min2 ? _min1 : _min2; }) | 166 | _min1 < _min2 ? _min1 : _min2; }) |
166 | 167 | ||
168 | extern bool test_attr__enabled; | ||
169 | void test_attr__init(void); | ||
170 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
171 | int fd, int group_fd, unsigned long flags); | ||
172 | |||
167 | static inline int | 173 | static inline int |
168 | sys_perf_event_open(struct perf_event_attr *attr, | 174 | sys_perf_event_open(struct perf_event_attr *attr, |
169 | pid_t pid, int cpu, int group_fd, | 175 | pid_t pid, int cpu, int group_fd, |
170 | unsigned long flags) | 176 | unsigned long flags) |
171 | { | 177 | { |
172 | return syscall(__NR_perf_event_open, attr, pid, cpu, | 178 | int fd; |
173 | group_fd, flags); | 179 | |
180 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, | ||
181 | group_fd, flags); | ||
182 | |||
183 | if (unlikely(test_attr__enabled)) | ||
184 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); | ||
185 | |||
186 | return fd; | ||
174 | } | 187 | } |
175 | 188 | ||
176 | #define MAX_COUNTERS 256 | 189 | #define MAX_COUNTERS 256 |
@@ -198,6 +211,7 @@ struct branch_stack { | |||
198 | struct branch_entry entries[0]; | 211 | struct branch_entry entries[0]; |
199 | }; | 212 | }; |
200 | 213 | ||
214 | extern const char *input_name; | ||
201 | extern bool perf_host, perf_guest; | 215 | extern bool perf_host, perf_guest; |
202 | extern const char perf_version_string[]; | 216 | extern const char perf_version_string[]; |
203 | 217 | ||
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c new file mode 100644 index 000000000000..25638a986257 --- /dev/null +++ b/tools/perf/tests/attr.c | |||
@@ -0,0 +1,175 @@ | |||
1 | |||
2 | /* | ||
3 | * The struct perf_event_attr test support. | ||
4 | * | ||
5 | * This test is embedded inside into perf directly and is governed | ||
6 | * by the PERF_TEST_ATTR environment variable and hook inside | ||
7 | * sys_perf_event_open function. | ||
8 | * | ||
9 | * The general idea is to store 'struct perf_event_attr' details for | ||
10 | * each event created within single perf command. Each event details | ||
11 | * are stored into separate text file. Once perf command is finished | ||
12 | * these files can be checked for values we expect for command. | ||
13 | * | ||
14 | * Besides 'struct perf_event_attr' values we also store 'fd' and | ||
15 | * 'group_fd' values to allow checking for groups created. | ||
16 | * | ||
17 | * This all is triggered by setting PERF_TEST_ATTR environment variable. | ||
18 | * It must contain name of existing directory with access and write | ||
19 | * permissions. All the event text files are stored there. | ||
20 | */ | ||
21 | |||
22 | #include <stdlib.h> | ||
23 | #include <stdio.h> | ||
24 | #include <inttypes.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include "../perf.h" | ||
28 | #include "util.h" | ||
29 | #include "exec_cmd.h" | ||
30 | #include "tests.h" | ||
31 | |||
32 | #define ENV "PERF_TEST_ATTR" | ||
33 | |||
34 | extern int verbose; | ||
35 | |||
36 | bool test_attr__enabled; | ||
37 | |||
38 | static char *dir; | ||
39 | |||
40 | void test_attr__init(void) | ||
41 | { | ||
42 | dir = getenv(ENV); | ||
43 | test_attr__enabled = (dir != NULL); | ||
44 | } | ||
45 | |||
46 | #define BUFSIZE 1024 | ||
47 | |||
48 | #define __WRITE_ASS(str, fmt, data) \ | ||
49 | do { \ | ||
50 | char buf[BUFSIZE]; \ | ||
51 | size_t size; \ | ||
52 | \ | ||
53 | size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \ | ||
54 | if (1 != fwrite(buf, size, 1, file)) { \ | ||
55 | perror("test attr - failed to write event file"); \ | ||
56 | fclose(file); \ | ||
57 | return -1; \ | ||
58 | } \ | ||
59 | \ | ||
60 | } while (0) | ||
61 | |||
62 | #define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field) | ||
63 | |||
64 | static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
65 | int fd, int group_fd, unsigned long flags) | ||
66 | { | ||
67 | FILE *file; | ||
68 | char path[PATH_MAX]; | ||
69 | |||
70 | snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, | ||
71 | attr->type, attr->config, fd); | ||
72 | |||
73 | file = fopen(path, "w+"); | ||
74 | if (!file) { | ||
75 | perror("test attr - failed to open event file"); | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | if (fprintf(file, "[event-%d-%llu-%d]\n", | ||
80 | attr->type, attr->config, fd) < 0) { | ||
81 | perror("test attr - failed to write event file"); | ||
82 | fclose(file); | ||
83 | return -1; | ||
84 | } | ||
85 | |||
86 | /* syscall arguments */ | ||
87 | __WRITE_ASS(fd, "d", fd); | ||
88 | __WRITE_ASS(group_fd, "d", group_fd); | ||
89 | __WRITE_ASS(cpu, "d", cpu); | ||
90 | __WRITE_ASS(pid, "d", pid); | ||
91 | __WRITE_ASS(flags, "lu", flags); | ||
92 | |||
93 | /* struct perf_event_attr */ | ||
94 | WRITE_ASS(type, PRIu32); | ||
95 | WRITE_ASS(size, PRIu32); | ||
96 | WRITE_ASS(config, "llu"); | ||
97 | WRITE_ASS(sample_period, "llu"); | ||
98 | WRITE_ASS(sample_type, "llu"); | ||
99 | WRITE_ASS(read_format, "llu"); | ||
100 | WRITE_ASS(disabled, "d"); | ||
101 | WRITE_ASS(inherit, "d"); | ||
102 | WRITE_ASS(pinned, "d"); | ||
103 | WRITE_ASS(exclusive, "d"); | ||
104 | WRITE_ASS(exclude_user, "d"); | ||
105 | WRITE_ASS(exclude_kernel, "d"); | ||
106 | WRITE_ASS(exclude_hv, "d"); | ||
107 | WRITE_ASS(exclude_idle, "d"); | ||
108 | WRITE_ASS(mmap, "d"); | ||
109 | WRITE_ASS(comm, "d"); | ||
110 | WRITE_ASS(freq, "d"); | ||
111 | WRITE_ASS(inherit_stat, "d"); | ||
112 | WRITE_ASS(enable_on_exec, "d"); | ||
113 | WRITE_ASS(task, "d"); | ||
114 | WRITE_ASS(watermark, "d"); | ||
115 | WRITE_ASS(precise_ip, "d"); | ||
116 | WRITE_ASS(mmap_data, "d"); | ||
117 | WRITE_ASS(sample_id_all, "d"); | ||
118 | WRITE_ASS(exclude_host, "d"); | ||
119 | WRITE_ASS(exclude_guest, "d"); | ||
120 | WRITE_ASS(exclude_callchain_kernel, "d"); | ||
121 | WRITE_ASS(exclude_callchain_user, "d"); | ||
122 | WRITE_ASS(wakeup_events, PRIu32); | ||
123 | WRITE_ASS(bp_type, PRIu32); | ||
124 | WRITE_ASS(config1, "llu"); | ||
125 | WRITE_ASS(config2, "llu"); | ||
126 | WRITE_ASS(branch_sample_type, "llu"); | ||
127 | WRITE_ASS(sample_regs_user, "llu"); | ||
128 | WRITE_ASS(sample_stack_user, PRIu32); | ||
129 | |||
130 | fclose(file); | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
135 | int fd, int group_fd, unsigned long flags) | ||
136 | { | ||
137 | int errno_saved = errno; | ||
138 | |||
139 | if (store_event(attr, pid, cpu, fd, group_fd, flags)) | ||
140 | die("test attr FAILED"); | ||
141 | |||
142 | errno = errno_saved; | ||
143 | } | ||
144 | |||
145 | static int run_dir(const char *d, const char *perf) | ||
146 | { | ||
147 | char cmd[3*PATH_MAX]; | ||
148 | |||
149 | snprintf(cmd, 3*PATH_MAX, "python %s/attr.py -d %s/attr/ -p %s %s", | ||
150 | d, d, perf, verbose ? "-v" : ""); | ||
151 | |||
152 | return system(cmd); | ||
153 | } | ||
154 | |||
155 | int test__attr(void) | ||
156 | { | ||
157 | struct stat st; | ||
158 | char path_perf[PATH_MAX]; | ||
159 | char path_dir[PATH_MAX]; | ||
160 | |||
161 | /* First try developement tree tests. */ | ||
162 | if (!lstat("./tests", &st)) | ||
163 | return run_dir("./tests", "./perf"); | ||
164 | |||
165 | /* Then installed path. */ | ||
166 | snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path()); | ||
167 | snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR); | ||
168 | |||
169 | if (!lstat(path_dir, &st) && | ||
170 | !lstat(path_perf, &st)) | ||
171 | return run_dir(path_dir, path_perf); | ||
172 | |||
173 | fprintf(stderr, " (ommitted)"); | ||
174 | return 0; | ||
175 | } | ||
diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py new file mode 100644 index 000000000000..e702b82dcb86 --- /dev/null +++ b/tools/perf/tests/attr.py | |||
@@ -0,0 +1,322 @@ | |||
1 | #! /usr/bin/python | ||
2 | |||
3 | import os | ||
4 | import sys | ||
5 | import glob | ||
6 | import optparse | ||
7 | import tempfile | ||
8 | import logging | ||
9 | import shutil | ||
10 | import ConfigParser | ||
11 | |||
12 | class Fail(Exception): | ||
13 | def __init__(self, test, msg): | ||
14 | self.msg = msg | ||
15 | self.test = test | ||
16 | def getMsg(self): | ||
17 | return '\'%s\' - %s' % (self.test.path, self.msg) | ||
18 | |||
19 | class Unsup(Exception): | ||
20 | def __init__(self, test): | ||
21 | self.test = test | ||
22 | def getMsg(self): | ||
23 | return '\'%s\'' % self.test.path | ||
24 | |||
25 | class Event(dict): | ||
26 | terms = [ | ||
27 | 'flags', | ||
28 | 'type', | ||
29 | 'size', | ||
30 | 'config', | ||
31 | 'sample_period', | ||
32 | 'sample_type', | ||
33 | 'read_format', | ||
34 | 'disabled', | ||
35 | 'inherit', | ||
36 | 'pinned', | ||
37 | 'exclusive', | ||
38 | 'exclude_user', | ||
39 | 'exclude_kernel', | ||
40 | 'exclude_hv', | ||
41 | 'exclude_idle', | ||
42 | 'mmap', | ||
43 | 'comm', | ||
44 | 'freq', | ||
45 | 'inherit_stat', | ||
46 | 'enable_on_exec', | ||
47 | 'task', | ||
48 | 'watermark', | ||
49 | 'precise_ip', | ||
50 | 'mmap_data', | ||
51 | 'sample_id_all', | ||
52 | 'exclude_host', | ||
53 | 'exclude_guest', | ||
54 | 'exclude_callchain_kernel', | ||
55 | 'exclude_callchain_user', | ||
56 | 'wakeup_events', | ||
57 | 'bp_type', | ||
58 | 'config1', | ||
59 | 'config2', | ||
60 | 'branch_sample_type', | ||
61 | 'sample_regs_user', | ||
62 | 'sample_stack_user', | ||
63 | ] | ||
64 | |||
65 | def add(self, data): | ||
66 | for key, val in data: | ||
67 | log.debug(" %s = %s" % (key, val)) | ||
68 | self[key] = val | ||
69 | |||
70 | def __init__(self, name, data, base): | ||
71 | log.info(" Event %s" % name); | ||
72 | self.name = name; | ||
73 | self.group = '' | ||
74 | self.add(base) | ||
75 | self.add(data) | ||
76 | |||
77 | def compare_data(self, a, b): | ||
78 | # Allow multiple values in assignment separated by '|' | ||
79 | a_list = a.split('|') | ||
80 | b_list = b.split('|') | ||
81 | |||
82 | for a_item in a_list: | ||
83 | for b_item in b_list: | ||
84 | if (a_item == b_item): | ||
85 | return True | ||
86 | elif (a_item == '*') or (b_item == '*'): | ||
87 | return True | ||
88 | |||
89 | return False | ||
90 | |||
91 | def equal(self, other): | ||
92 | for t in Event.terms: | ||
93 | log.debug(" [%s] %s %s" % (t, self[t], other[t])); | ||
94 | if not self.has_key(t) or not other.has_key(t): | ||
95 | return False | ||
96 | if not self.compare_data(self[t], other[t]): | ||
97 | return False | ||
98 | return True | ||
99 | |||
100 | # Test file description needs to have following sections: | ||
101 | # [config] | ||
102 | # - just single instance in file | ||
103 | # - needs to specify: | ||
104 | # 'command' - perf command name | ||
105 | # 'args' - special command arguments | ||
106 | # 'ret' - expected command return value (0 by default) | ||
107 | # | ||
108 | # [eventX:base] | ||
109 | # - one or multiple instances in file | ||
110 | # - expected values assignments | ||
111 | class Test(object): | ||
112 | def __init__(self, path, options): | ||
113 | parser = ConfigParser.SafeConfigParser() | ||
114 | parser.read(path) | ||
115 | |||
116 | log.warning("running '%s'" % path) | ||
117 | |||
118 | self.path = path | ||
119 | self.test_dir = options.test_dir | ||
120 | self.perf = options.perf | ||
121 | self.command = parser.get('config', 'command') | ||
122 | self.args = parser.get('config', 'args') | ||
123 | |||
124 | try: | ||
125 | self.ret = parser.get('config', 'ret') | ||
126 | except: | ||
127 | self.ret = 0 | ||
128 | |||
129 | self.expect = {} | ||
130 | self.result = {} | ||
131 | log.info(" loading expected events"); | ||
132 | self.load_events(path, self.expect) | ||
133 | |||
134 | def is_event(self, name): | ||
135 | if name.find("event") == -1: | ||
136 | return False | ||
137 | else: | ||
138 | return True | ||
139 | |||
140 | def load_events(self, path, events): | ||
141 | parser_event = ConfigParser.SafeConfigParser() | ||
142 | parser_event.read(path) | ||
143 | |||
144 | # The event record section header contains 'event' word, | ||
145 | # optionaly followed by ':' allowing to load 'parent | ||
146 | # event' first as a base | ||
147 | for section in filter(self.is_event, parser_event.sections()): | ||
148 | |||
149 | parser_items = parser_event.items(section); | ||
150 | base_items = {} | ||
151 | |||
152 | # Read parent event if there's any | ||
153 | if (':' in section): | ||
154 | base = section[section.index(':') + 1:] | ||
155 | parser_base = ConfigParser.SafeConfigParser() | ||
156 | parser_base.read(self.test_dir + '/' + base) | ||
157 | base_items = parser_base.items('event') | ||
158 | |||
159 | e = Event(section, parser_items, base_items) | ||
160 | events[section] = e | ||
161 | |||
162 | def run_cmd(self, tempdir): | ||
163 | cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir, | ||
164 | self.perf, self.command, tempdir, self.args) | ||
165 | ret = os.WEXITSTATUS(os.system(cmd)) | ||
166 | |||
167 | log.info(" running '%s' ret %d " % (cmd, ret)) | ||
168 | |||
169 | if ret != int(self.ret): | ||
170 | raise Unsup(self) | ||
171 | |||
172 | def compare(self, expect, result): | ||
173 | match = {} | ||
174 | |||
175 | log.info(" compare"); | ||
176 | |||
177 | # For each expected event find all matching | ||
178 | # events in result. Fail if there's not any. | ||
179 | for exp_name, exp_event in expect.items(): | ||
180 | exp_list = [] | ||
181 | log.debug(" matching [%s]" % exp_name) | ||
182 | for res_name, res_event in result.items(): | ||
183 | log.debug(" to [%s]" % res_name) | ||
184 | if (exp_event.equal(res_event)): | ||
185 | exp_list.append(res_name) | ||
186 | log.debug(" ->OK") | ||
187 | else: | ||
188 | log.debug(" ->FAIL"); | ||
189 | |||
190 | log.info(" match: [%s] matches %s" % (exp_name, str(exp_list))) | ||
191 | |||
192 | # we did not any matching event - fail | ||
193 | if (not exp_list): | ||
194 | raise Fail(self, 'match failure'); | ||
195 | |||
196 | match[exp_name] = exp_list | ||
197 | |||
198 | # For each defined group in the expected events | ||
199 | # check we match the same group in the result. | ||
200 | for exp_name, exp_event in expect.items(): | ||
201 | group = exp_event.group | ||
202 | |||
203 | if (group == ''): | ||
204 | continue | ||
205 | |||
206 | for res_name in match[exp_name]: | ||
207 | res_group = result[res_name].group | ||
208 | if res_group not in match[group]: | ||
209 | raise Fail(self, 'group failure') | ||
210 | |||
211 | log.info(" group: [%s] matches group leader %s" % | ||
212 | (exp_name, str(match[group]))) | ||
213 | |||
214 | log.info(" matched") | ||
215 | |||
216 | def resolve_groups(self, events): | ||
217 | for name, event in events.items(): | ||
218 | group_fd = event['group_fd']; | ||
219 | if group_fd == '-1': | ||
220 | continue; | ||
221 | |||
222 | for iname, ievent in events.items(): | ||
223 | if (ievent['fd'] == group_fd): | ||
224 | event.group = iname | ||
225 | log.debug('[%s] has group leader [%s]' % (name, iname)) | ||
226 | break; | ||
227 | |||
228 | def run(self): | ||
229 | tempdir = tempfile.mkdtemp(); | ||
230 | |||
231 | try: | ||
232 | # run the test script | ||
233 | self.run_cmd(tempdir); | ||
234 | |||
235 | # load events expectation for the test | ||
236 | log.info(" loading result events"); | ||
237 | for f in glob.glob(tempdir + '/event*'): | ||
238 | self.load_events(f, self.result); | ||
239 | |||
240 | # resolve group_fd to event names | ||
241 | self.resolve_groups(self.expect); | ||
242 | self.resolve_groups(self.result); | ||
243 | |||
244 | # do the expectation - results matching - both ways | ||
245 | self.compare(self.expect, self.result) | ||
246 | self.compare(self.result, self.expect) | ||
247 | |||
248 | finally: | ||
249 | # cleanup | ||
250 | shutil.rmtree(tempdir) | ||
251 | |||
252 | |||
253 | def run_tests(options): | ||
254 | for f in glob.glob(options.test_dir + '/' + options.test): | ||
255 | try: | ||
256 | Test(f, options).run() | ||
257 | except Unsup, obj: | ||
258 | log.warning("unsupp %s" % obj.getMsg()) | ||
259 | |||
260 | def setup_log(verbose): | ||
261 | global log | ||
262 | level = logging.CRITICAL | ||
263 | |||
264 | if verbose == 1: | ||
265 | level = logging.WARNING | ||
266 | if verbose == 2: | ||
267 | level = logging.INFO | ||
268 | if verbose >= 3: | ||
269 | level = logging.DEBUG | ||
270 | |||
271 | log = logging.getLogger('test') | ||
272 | log.setLevel(level) | ||
273 | ch = logging.StreamHandler() | ||
274 | ch.setLevel(level) | ||
275 | formatter = logging.Formatter('%(message)s') | ||
276 | ch.setFormatter(formatter) | ||
277 | log.addHandler(ch) | ||
278 | |||
279 | USAGE = '''%s [OPTIONS] | ||
280 | -d dir # tests dir | ||
281 | -p path # perf binary | ||
282 | -t test # single test | ||
283 | -v # verbose level | ||
284 | ''' % sys.argv[0] | ||
285 | |||
286 | def main(): | ||
287 | parser = optparse.OptionParser(usage=USAGE) | ||
288 | |||
289 | parser.add_option("-t", "--test", | ||
290 | action="store", type="string", dest="test") | ||
291 | parser.add_option("-d", "--test-dir", | ||
292 | action="store", type="string", dest="test_dir") | ||
293 | parser.add_option("-p", "--perf", | ||
294 | action="store", type="string", dest="perf") | ||
295 | parser.add_option("-v", "--verbose", | ||
296 | action="count", dest="verbose") | ||
297 | |||
298 | options, args = parser.parse_args() | ||
299 | if args: | ||
300 | parser.error('FAILED wrong arguments %s' % ' '.join(args)) | ||
301 | return -1 | ||
302 | |||
303 | setup_log(options.verbose) | ||
304 | |||
305 | if not options.test_dir: | ||
306 | print 'FAILED no -d option specified' | ||
307 | sys.exit(-1) | ||
308 | |||
309 | if not options.test: | ||
310 | options.test = 'test*' | ||
311 | |||
312 | try: | ||
313 | run_tests(options) | ||
314 | |||
315 | except Fail, obj: | ||
316 | print "FAILED %s" % obj.getMsg(); | ||
317 | sys.exit(-1) | ||
318 | |||
319 | sys.exit(0) | ||
320 | |||
321 | if __name__ == '__main__': | ||
322 | main() | ||
diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/attr/README new file mode 100644 index 000000000000..d102957cd59a --- /dev/null +++ b/tools/perf/tests/attr/README | |||
@@ -0,0 +1,64 @@ | |||
1 | The struct perf_event_attr test (attr tests) support | ||
2 | ==================================================== | ||
3 | This testing support is embedded into perf directly and is governed | ||
4 | by the PERF_TEST_ATTR environment variable and hook inside the | ||
5 | sys_perf_event_open function. | ||
6 | |||
7 | The general idea is to store 'struct perf_event_attr' details for | ||
8 | each event created within single perf command. Each event details | ||
9 | are stored into separate text file. Once perf command is finished | ||
10 | these files are checked for values we expect for command. | ||
11 | |||
12 | The attr tests consist of following parts: | ||
13 | |||
14 | tests/attr.c | ||
15 | ------------ | ||
16 | This is the sys_perf_event_open hook implementation. The hook | ||
17 | is triggered when the PERF_TEST_ATTR environment variable is | ||
18 | defined. It must contain name of existing directory with access | ||
19 | and write permissions. | ||
20 | |||
21 | For each sys_perf_event_open call event details are stored in | ||
22 | separate file. Besides 'struct perf_event_attr' values we also | ||
23 | store 'fd' and 'group_fd' values to allow checking for groups. | ||
24 | |||
25 | tests/attr.py | ||
26 | ------------- | ||
27 | This is the python script that does all the hard work. It reads | ||
28 | the test definition, executes it and checks results. | ||
29 | |||
30 | tests/attr/ | ||
31 | ----------- | ||
32 | Directory containing all attr test definitions. | ||
33 | Following tests are defined (with perf commands): | ||
34 | |||
35 | perf record kill (test-record-basic) | ||
36 | perf record -b kill (test-record-branch-any) | ||
37 | perf record -j any kill (test-record-branch-filter-any) | ||
38 | perf record -j any_call kill (test-record-branch-filter-any_call) | ||
39 | perf record -j any_ret kill (test-record-branch-filter-any_ret) | ||
40 | perf record -j hv kill (test-record-branch-filter-hv) | ||
41 | perf record -j ind_call kill (test-record-branch-filter-ind_call) | ||
42 | perf record -j k kill (test-record-branch-filter-k) | ||
43 | perf record -j u kill (test-record-branch-filter-u) | ||
44 | perf record -c 123 kill (test-record-count) | ||
45 | perf record -d kill (test-record-data) | ||
46 | perf record -F 100 kill (test-record-freq) | ||
47 | perf record -g -- kill (test-record-graph-default) | ||
48 | perf record -g dwarf -- kill (test-record-graph-dwarf) | ||
49 | perf record -g fp kill (test-record-graph-fp) | ||
50 | perf record --group -e cycles,instructions kill (test-record-group) | ||
51 | perf record -e '{cycles,instructions}' kill (test-record-group1) | ||
52 | perf record -D kill (test-record-no-delay) | ||
53 | perf record -i kill (test-record-no-inherit) | ||
54 | perf record -n kill (test-record-no-samples) | ||
55 | perf record -c 100 -P kill (test-record-period) | ||
56 | perf record -R kill (test-record-raw) | ||
57 | perf stat -e cycles kill (test-stat-basic) | ||
58 | perf stat kill (test-stat-default) | ||
59 | perf stat -d kill (test-stat-detailed-1) | ||
60 | perf stat -dd kill (test-stat-detailed-2) | ||
61 | perf stat -ddd kill (test-stat-detailed-3) | ||
62 | perf stat --group -e cycles,instructions kill (test-stat-group) | ||
63 | perf stat -e '{cycles,instructions}' kill (test-stat-group1) | ||
64 | perf stat -i -e cycles kill (test-stat-no-inherit) | ||
diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record new file mode 100644 index 000000000000..f1485d8e6a0b --- /dev/null +++ b/tools/perf/tests/attr/base-record | |||
@@ -0,0 +1,39 @@ | |||
1 | [event] | ||
2 | fd=1 | ||
3 | group_fd=-1 | ||
4 | flags=0 | ||
5 | type=0|1 | ||
6 | size=96 | ||
7 | config=0 | ||
8 | sample_period=4000 | ||
9 | sample_type=263 | ||
10 | read_format=7 | ||
11 | disabled=1 | ||
12 | inherit=1 | ||
13 | pinned=0 | ||
14 | exclusive=0 | ||
15 | exclude_user=0 | ||
16 | exclude_kernel=0 | ||
17 | exclude_hv=0 | ||
18 | exclude_idle=0 | ||
19 | mmap=1 | ||
20 | comm=1 | ||
21 | freq=1 | ||
22 | inherit_stat=0 | ||
23 | enable_on_exec=1 | ||
24 | task=0 | ||
25 | watermark=0 | ||
26 | precise_ip=0 | ||
27 | mmap_data=0 | ||
28 | sample_id_all=1 | ||
29 | exclude_host=0 | ||
30 | exclude_guest=1 | ||
31 | exclude_callchain_kernel=0 | ||
32 | exclude_callchain_user=0 | ||
33 | wakeup_events=0 | ||
34 | bp_type=0 | ||
35 | config1=0 | ||
36 | config2=0 | ||
37 | branch_sample_type=0 | ||
38 | sample_regs_user=0 | ||
39 | sample_stack_user=0 | ||
diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat new file mode 100644 index 000000000000..4bd79a82784f --- /dev/null +++ b/tools/perf/tests/attr/base-stat | |||
@@ -0,0 +1,39 @@ | |||
1 | [event] | ||
2 | fd=1 | ||
3 | group_fd=-1 | ||
4 | flags=0 | ||
5 | type=0 | ||
6 | size=96 | ||
7 | config=0 | ||
8 | sample_period=0 | ||
9 | sample_type=0 | ||
10 | read_format=3 | ||
11 | disabled=1 | ||
12 | inherit=1 | ||
13 | pinned=0 | ||
14 | exclusive=0 | ||
15 | exclude_user=0 | ||
16 | exclude_kernel=0 | ||
17 | exclude_hv=0 | ||
18 | exclude_idle=0 | ||
19 | mmap=0 | ||
20 | comm=0 | ||
21 | freq=0 | ||
22 | inherit_stat=0 | ||
23 | enable_on_exec=1 | ||
24 | task=0 | ||
25 | watermark=0 | ||
26 | precise_ip=0 | ||
27 | mmap_data=0 | ||
28 | sample_id_all=0 | ||
29 | exclude_host=0 | ||
30 | exclude_guest=1 | ||
31 | exclude_callchain_kernel=0 | ||
32 | exclude_callchain_user=0 | ||
33 | wakeup_events=0 | ||
34 | bp_type=0 | ||
35 | config1=0 | ||
36 | config2=0 | ||
37 | branch_sample_type=0 | ||
38 | sample_regs_user=0 | ||
39 | sample_stack_user=0 | ||
diff --git a/tools/perf/tests/attr/test-record-basic b/tools/perf/tests/attr/test-record-basic new file mode 100644 index 000000000000..55c0428370ca --- /dev/null +++ b/tools/perf/tests/attr/test-record-basic | |||
@@ -0,0 +1,5 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
diff --git a/tools/perf/tests/attr/test-record-branch-any b/tools/perf/tests/attr/test-record-branch-any new file mode 100644 index 000000000000..1421960ed4e9 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-any | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -b kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any b/tools/perf/tests/attr/test-record-branch-filter-any new file mode 100644 index 000000000000..915c4df0e0c2 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_call b/tools/perf/tests/attr/test-record-branch-filter-any_call new file mode 100644 index 000000000000..8708dbd4f373 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any_call | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any_call kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=16 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_ret b/tools/perf/tests/attr/test-record-branch-filter-any_ret new file mode 100644 index 000000000000..0d3607a6dcbe --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any_ret | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any_ret kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=32 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-hv b/tools/perf/tests/attr/test-record-branch-filter-hv new file mode 100644 index 000000000000..f25526740cec --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-hv | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j hv kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-ind_call b/tools/perf/tests/attr/test-record-branch-filter-ind_call new file mode 100644 index 000000000000..e862dd179128 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-ind_call | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j ind_call kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=64 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-k b/tools/perf/tests/attr/test-record-branch-filter-k new file mode 100644 index 000000000000..182971e898f5 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-k | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j k kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-u b/tools/perf/tests/attr/test-record-branch-filter-u new file mode 100644 index 000000000000..83449ef9e687 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-u | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j u kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-count b/tools/perf/tests/attr/test-record-count new file mode 100644 index 000000000000..2f841de56f6b --- /dev/null +++ b/tools/perf/tests/attr/test-record-count | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -c 123 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=123 | ||
7 | sample_type=7 | ||
8 | freq=0 | ||
diff --git a/tools/perf/tests/attr/test-record-data b/tools/perf/tests/attr/test-record-data new file mode 100644 index 000000000000..6627c3e7534a --- /dev/null +++ b/tools/perf/tests/attr/test-record-data | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -d kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=271 | ||
8 | mmap_data=1 | ||
diff --git a/tools/perf/tests/attr/test-record-freq b/tools/perf/tests/attr/test-record-freq new file mode 100644 index 000000000000..600d0f8f2583 --- /dev/null +++ b/tools/perf/tests/attr/test-record-freq | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -F 100 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=100 | ||
diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/attr/test-record-graph-default new file mode 100644 index 000000000000..833d1849d767 --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-default | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g -- kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=295 | ||
diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/attr/test-record-graph-dwarf new file mode 100644 index 000000000000..e93e082f5208 --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-dwarf | |||
@@ -0,0 +1,10 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g dwarf -- kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=12583 | ||
7 | exclude_callchain_user=1 | ||
8 | sample_stack_user=8192 | ||
9 | # TODO different for each arch, no support for that now | ||
10 | sample_regs_user=* | ||
diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/attr/test-record-graph-fp new file mode 100644 index 000000000000..7cef3743f03f --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-fp | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g fp kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=295 | ||
diff --git a/tools/perf/tests/attr/test-record-group b/tools/perf/tests/attr/test-record-group new file mode 100644 index 000000000000..a6599e9a19d3 --- /dev/null +++ b/tools/perf/tests/attr/test-record-group | |||
@@ -0,0 +1,18 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = --group -e cycles,instructions kill >/dev/null 2>&1 | ||
4 | |||
5 | [event-1:base-record] | ||
6 | fd=1 | ||
7 | group_fd=-1 | ||
8 | sample_type=327 | ||
9 | |||
10 | [event-2:base-record] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | config=1 | ||
14 | sample_type=327 | ||
15 | mmap=0 | ||
16 | comm=0 | ||
17 | enable_on_exec=0 | ||
18 | disabled=0 | ||
diff --git a/tools/perf/tests/attr/test-record-group1 b/tools/perf/tests/attr/test-record-group1 new file mode 100644 index 000000000000..5a8359da38af --- /dev/null +++ b/tools/perf/tests/attr/test-record-group1 | |||
@@ -0,0 +1,19 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -e '{cycles,instructions}' kill >/tmp/krava 2>&1 | ||
4 | |||
5 | [event-1:base-record] | ||
6 | fd=1 | ||
7 | group_fd=-1 | ||
8 | sample_type=327 | ||
9 | |||
10 | [event-2:base-record] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | type=0 | ||
14 | config=1 | ||
15 | sample_type=327 | ||
16 | mmap=0 | ||
17 | comm=0 | ||
18 | enable_on_exec=0 | ||
19 | disabled=0 | ||
diff --git a/tools/perf/tests/attr/test-record-no-delay b/tools/perf/tests/attr/test-record-no-delay new file mode 100644 index 000000000000..f253b78cdbf2 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-delay | |||
@@ -0,0 +1,9 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -D kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=263 | ||
8 | watermark=0 | ||
9 | wakeup_events=1 | ||
diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/attr/test-record-no-inherit new file mode 100644 index 000000000000..9079a25cd643 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-inherit | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -i kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=259 | ||
7 | inherit=0 | ||
diff --git a/tools/perf/tests/attr/test-record-no-samples b/tools/perf/tests/attr/test-record-no-samples new file mode 100644 index 000000000000..d0141b2418b5 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-samples | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -n kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=0 | ||
diff --git a/tools/perf/tests/attr/test-record-period b/tools/perf/tests/attr/test-record-period new file mode 100644 index 000000000000..8abc5314fc52 --- /dev/null +++ b/tools/perf/tests/attr/test-record-period | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -c 100 -P kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=100 | ||
7 | freq=0 | ||
diff --git a/tools/perf/tests/attr/test-record-raw b/tools/perf/tests/attr/test-record-raw new file mode 100644 index 000000000000..4a8ef25b5f49 --- /dev/null +++ b/tools/perf/tests/attr/test-record-raw | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -R kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=1415 | ||
diff --git a/tools/perf/tests/attr/test-stat-basic b/tools/perf/tests/attr/test-stat-basic new file mode 100644 index 000000000000..74e17881f2ba --- /dev/null +++ b/tools/perf/tests/attr/test-stat-basic | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e cycles kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
diff --git a/tools/perf/tests/attr/test-stat-default b/tools/perf/tests/attr/test-stat-default new file mode 100644 index 000000000000..19270f54c96e --- /dev/null +++ b/tools/perf/tests/attr/test-stat-default | |||
@@ -0,0 +1,64 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
7 | [event1:base-stat] | ||
8 | fd=1 | ||
9 | type=1 | ||
10 | config=1 | ||
11 | |||
12 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
13 | [event2:base-stat] | ||
14 | fd=2 | ||
15 | type=1 | ||
16 | config=3 | ||
17 | |||
18 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
19 | [event3:base-stat] | ||
20 | fd=3 | ||
21 | type=1 | ||
22 | config=4 | ||
23 | |||
24 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
25 | [event4:base-stat] | ||
26 | fd=4 | ||
27 | type=1 | ||
28 | config=2 | ||
29 | |||
30 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
31 | [event5:base-stat] | ||
32 | fd=5 | ||
33 | type=0 | ||
34 | config=0 | ||
35 | |||
36 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
37 | [event6:base-stat] | ||
38 | fd=6 | ||
39 | type=0 | ||
40 | config=7 | ||
41 | |||
42 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
43 | [event7:base-stat] | ||
44 | fd=7 | ||
45 | type=0 | ||
46 | config=8 | ||
47 | |||
48 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
49 | [event8:base-stat] | ||
50 | fd=8 | ||
51 | type=0 | ||
52 | config=1 | ||
53 | |||
54 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
55 | [event9:base-stat] | ||
56 | fd=9 | ||
57 | type=0 | ||
58 | config=4 | ||
59 | |||
60 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
61 | [event10:base-stat] | ||
62 | fd=10 | ||
63 | type=0 | ||
64 | config=5 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-1 b/tools/perf/tests/attr/test-stat-detailed-1 new file mode 100644 index 000000000000..51426b87153b --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-1 | |||
@@ -0,0 +1,101 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -d kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-2 b/tools/perf/tests/attr/test-stat-detailed-2 new file mode 100644 index 000000000000..8de5acc31c27 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-2 | |||
@@ -0,0 +1,155 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -dd kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
102 | |||
103 | # PERF_TYPE_HW_CACHE, | ||
104 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
105 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
106 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
107 | [event15:base-stat] | ||
108 | fd=15 | ||
109 | type=3 | ||
110 | config=1 | ||
111 | |||
112 | # PERF_TYPE_HW_CACHE, | ||
113 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
114 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
115 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
116 | [event16:base-stat] | ||
117 | fd=16 | ||
118 | type=3 | ||
119 | config=65537 | ||
120 | |||
121 | # PERF_TYPE_HW_CACHE, | ||
122 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
123 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
124 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
125 | [event17:base-stat] | ||
126 | fd=17 | ||
127 | type=3 | ||
128 | config=3 | ||
129 | |||
130 | # PERF_TYPE_HW_CACHE, | ||
131 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
132 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
133 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
134 | [event18:base-stat] | ||
135 | fd=18 | ||
136 | type=3 | ||
137 | config=65539 | ||
138 | |||
139 | # PERF_TYPE_HW_CACHE, | ||
140 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
141 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
142 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
143 | [event19:base-stat] | ||
144 | fd=19 | ||
145 | type=3 | ||
146 | config=4 | ||
147 | |||
148 | # PERF_TYPE_HW_CACHE, | ||
149 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
150 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
151 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
152 | [event20:base-stat] | ||
153 | fd=20 | ||
154 | type=3 | ||
155 | config=65540 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-3 b/tools/perf/tests/attr/test-stat-detailed-3 new file mode 100644 index 000000000000..0a1f45bf7d79 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-3 | |||
@@ -0,0 +1,173 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -ddd kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
102 | |||
103 | # PERF_TYPE_HW_CACHE, | ||
104 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
105 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
106 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
107 | [event15:base-stat] | ||
108 | fd=15 | ||
109 | type=3 | ||
110 | config=1 | ||
111 | |||
112 | # PERF_TYPE_HW_CACHE, | ||
113 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
114 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
115 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
116 | [event16:base-stat] | ||
117 | fd=16 | ||
118 | type=3 | ||
119 | config=65537 | ||
120 | |||
121 | # PERF_TYPE_HW_CACHE, | ||
122 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
123 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
124 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
125 | [event17:base-stat] | ||
126 | fd=17 | ||
127 | type=3 | ||
128 | config=3 | ||
129 | |||
130 | # PERF_TYPE_HW_CACHE, | ||
131 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
132 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
133 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
134 | [event18:base-stat] | ||
135 | fd=18 | ||
136 | type=3 | ||
137 | config=65539 | ||
138 | |||
139 | # PERF_TYPE_HW_CACHE, | ||
140 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
141 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
142 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
143 | [event19:base-stat] | ||
144 | fd=19 | ||
145 | type=3 | ||
146 | config=4 | ||
147 | |||
148 | # PERF_TYPE_HW_CACHE, | ||
149 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
150 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
151 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
152 | [event20:base-stat] | ||
153 | fd=20 | ||
154 | type=3 | ||
155 | config=65540 | ||
156 | |||
157 | # PERF_TYPE_HW_CACHE, | ||
158 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
159 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | | ||
160 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
161 | [event21:base-stat] | ||
162 | fd=21 | ||
163 | type=3 | ||
164 | config=512 | ||
165 | |||
166 | # PERF_TYPE_HW_CACHE, | ||
167 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
168 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | | ||
169 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
170 | [event22:base-stat] | ||
171 | fd=22 | ||
172 | type=3 | ||
173 | config=66048 | ||
diff --git a/tools/perf/tests/attr/test-stat-group b/tools/perf/tests/attr/test-stat-group new file mode 100644 index 000000000000..fdc1596a8862 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-group | |||
@@ -0,0 +1,15 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = --group -e cycles,instructions kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event-1:base-stat] | ||
7 | fd=1 | ||
8 | group_fd=-1 | ||
9 | |||
10 | [event-2:base-stat] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | config=1 | ||
14 | disabled=0 | ||
15 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/attr/test-stat-group1 b/tools/perf/tests/attr/test-stat-group1 new file mode 100644 index 000000000000..2a1f86e4a904 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-group1 | |||
@@ -0,0 +1,15 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e '{cycles,instructions}' kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event-1:base-stat] | ||
7 | fd=1 | ||
8 | group_fd=-1 | ||
9 | |||
10 | [event-2:base-stat] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | config=1 | ||
14 | disabled=0 | ||
15 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/attr/test-stat-no-inherit b/tools/perf/tests/attr/test-stat-no-inherit new file mode 100644 index 000000000000..d54b2a1e3e28 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-no-inherit | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -i -e cycles kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
7 | inherit=0 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c new file mode 100644 index 000000000000..186f67535494 --- /dev/null +++ b/tools/perf/tests/builtin-test.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * builtin-test.c | ||
3 | * | ||
4 | * Builtin regression testing command: ever growing number of sanity tests | ||
5 | */ | ||
6 | #include "builtin.h" | ||
7 | #include "tests.h" | ||
8 | #include "debug.h" | ||
9 | #include "color.h" | ||
10 | #include "parse-options.h" | ||
11 | #include "symbol.h" | ||
12 | |||
13 | static struct test { | ||
14 | const char *desc; | ||
15 | int (*func)(void); | ||
16 | } tests[] = { | ||
17 | { | ||
18 | .desc = "vmlinux symtab matches kallsyms", | ||
19 | .func = test__vmlinux_matches_kallsyms, | ||
20 | }, | ||
21 | { | ||
22 | .desc = "detect open syscall event", | ||
23 | .func = test__open_syscall_event, | ||
24 | }, | ||
25 | { | ||
26 | .desc = "detect open syscall event on all cpus", | ||
27 | .func = test__open_syscall_event_on_all_cpus, | ||
28 | }, | ||
29 | { | ||
30 | .desc = "read samples using the mmap interface", | ||
31 | .func = test__basic_mmap, | ||
32 | }, | ||
33 | { | ||
34 | .desc = "parse events tests", | ||
35 | .func = test__parse_events, | ||
36 | }, | ||
37 | #if defined(__x86_64__) || defined(__i386__) | ||
38 | { | ||
39 | .desc = "x86 rdpmc test", | ||
40 | .func = test__rdpmc, | ||
41 | }, | ||
42 | #endif | ||
43 | { | ||
44 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
45 | .func = test__PERF_RECORD, | ||
46 | }, | ||
47 | { | ||
48 | .desc = "Test perf pmu format parsing", | ||
49 | .func = test__pmu, | ||
50 | }, | ||
51 | { | ||
52 | .desc = "Test dso data interface", | ||
53 | .func = test__dso_data, | ||
54 | }, | ||
55 | { | ||
56 | .desc = "roundtrip evsel->name check", | ||
57 | .func = test__perf_evsel__roundtrip_name_test, | ||
58 | }, | ||
59 | { | ||
60 | .desc = "Check parsing of sched tracepoints fields", | ||
61 | .func = test__perf_evsel__tp_sched_test, | ||
62 | }, | ||
63 | { | ||
64 | .desc = "Generate and check syscalls:sys_enter_open event fields", | ||
65 | .func = test__syscall_open_tp_fields, | ||
66 | }, | ||
67 | { | ||
68 | .desc = "struct perf_event_attr setup", | ||
69 | .func = test__attr, | ||
70 | }, | ||
71 | { | ||
72 | .func = NULL, | ||
73 | }, | ||
74 | }; | ||
75 | |||
76 | static bool perf_test__matches(int curr, int argc, const char *argv[]) | ||
77 | { | ||
78 | int i; | ||
79 | |||
80 | if (argc == 0) | ||
81 | return true; | ||
82 | |||
83 | for (i = 0; i < argc; ++i) { | ||
84 | char *end; | ||
85 | long nr = strtoul(argv[i], &end, 10); | ||
86 | |||
87 | if (*end == '\0') { | ||
88 | if (nr == curr + 1) | ||
89 | return true; | ||
90 | continue; | ||
91 | } | ||
92 | |||
93 | if (strstr(tests[curr].desc, argv[i])) | ||
94 | return true; | ||
95 | } | ||
96 | |||
97 | return false; | ||
98 | } | ||
99 | |||
100 | static int __cmd_test(int argc, const char *argv[]) | ||
101 | { | ||
102 | int i = 0; | ||
103 | int width = 0; | ||
104 | |||
105 | while (tests[i].func) { | ||
106 | int len = strlen(tests[i].desc); | ||
107 | |||
108 | if (width < len) | ||
109 | width = len; | ||
110 | ++i; | ||
111 | } | ||
112 | |||
113 | i = 0; | ||
114 | while (tests[i].func) { | ||
115 | int curr = i++, err; | ||
116 | |||
117 | if (!perf_test__matches(curr, argc, argv)) | ||
118 | continue; | ||
119 | |||
120 | pr_info("%2d: %-*s:", i, width, tests[curr].desc); | ||
121 | pr_debug("\n--- start ---\n"); | ||
122 | err = tests[curr].func(); | ||
123 | pr_debug("---- end ----\n%s:", tests[curr].desc); | ||
124 | if (err) | ||
125 | color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); | ||
126 | else | ||
127 | pr_info(" Ok\n"); | ||
128 | } | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int perf_test__list(int argc, const char **argv) | ||
134 | { | ||
135 | int i = 0; | ||
136 | |||
137 | while (tests[i].func) { | ||
138 | int curr = i++; | ||
139 | |||
140 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | ||
141 | continue; | ||
142 | |||
143 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | ||
150 | { | ||
151 | const char * const test_usage[] = { | ||
152 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
153 | NULL, | ||
154 | }; | ||
155 | const struct option test_options[] = { | ||
156 | OPT_INCR('v', "verbose", &verbose, | ||
157 | "be more verbose (show symbol address, etc)"), | ||
158 | OPT_END() | ||
159 | }; | ||
160 | |||
161 | argc = parse_options(argc, argv, test_options, test_usage, 0); | ||
162 | if (argc >= 1 && !strcmp(argv[0], "list")) | ||
163 | return perf_test__list(argc, argv); | ||
164 | |||
165 | symbol_conf.priv_size = sizeof(int); | ||
166 | symbol_conf.sort_by_name = true; | ||
167 | symbol_conf.try_vmlinux_path = true; | ||
168 | |||
169 | if (symbol__init() < 0) | ||
170 | return -1; | ||
171 | |||
172 | return __cmd_test(argc, argv); | ||
173 | } | ||
diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/tests/dso-data.c index c6caedeb1d6b..5eaffa2de9c5 100644 --- a/tools/perf/util/dso-test-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -6,7 +6,9 @@ | |||
6 | #include <fcntl.h> | 6 | #include <fcntl.h> |
7 | #include <string.h> | 7 | #include <string.h> |
8 | 8 | ||
9 | #include "machine.h" | ||
9 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "tests.h" | ||
10 | 12 | ||
11 | #define TEST_ASSERT_VAL(text, cond) \ | 13 | #define TEST_ASSERT_VAL(text, cond) \ |
12 | do { \ | 14 | do { \ |
@@ -24,6 +26,10 @@ static char *test_file(int size) | |||
24 | unsigned char *buf; | 26 | unsigned char *buf; |
25 | 27 | ||
26 | fd = mkstemp(templ); | 28 | fd = mkstemp(templ); |
29 | if (fd < 0) { | ||
30 | perror("mkstemp failed"); | ||
31 | return NULL; | ||
32 | } | ||
27 | 33 | ||
28 | buf = malloc(size); | 34 | buf = malloc(size); |
29 | if (!buf) { | 35 | if (!buf) { |
@@ -94,7 +100,7 @@ struct test_data_offset offsets[] = { | |||
94 | }, | 100 | }, |
95 | }; | 101 | }; |
96 | 102 | ||
97 | int dso__test_data(void) | 103 | int test__dso_data(void) |
98 | { | 104 | { |
99 | struct machine machine; | 105 | struct machine machine; |
100 | struct dso *dso; | 106 | struct dso *dso; |
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c new file mode 100644 index 000000000000..e61fc828a158 --- /dev/null +++ b/tools/perf/tests/evsel-roundtrip-name.c | |||
@@ -0,0 +1,114 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "parse-events.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | static int perf_evsel__roundtrip_cache_name_test(void) | ||
7 | { | ||
8 | char name[128]; | ||
9 | int type, op, err = 0, ret = 0, i, idx; | ||
10 | struct perf_evsel *evsel; | ||
11 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
12 | |||
13 | if (evlist == NULL) | ||
14 | return -ENOMEM; | ||
15 | |||
16 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
17 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
18 | /* skip invalid cache type */ | ||
19 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
20 | continue; | ||
21 | |||
22 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
23 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
24 | name, sizeof(name)); | ||
25 | err = parse_events(evlist, name, 0); | ||
26 | if (err) | ||
27 | ret = err; | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | idx = 0; | ||
33 | evsel = perf_evlist__first(evlist); | ||
34 | |||
35 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
36 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
37 | /* skip invalid cache type */ | ||
38 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
39 | continue; | ||
40 | |||
41 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
42 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
43 | name, sizeof(name)); | ||
44 | if (evsel->idx != idx) | ||
45 | continue; | ||
46 | |||
47 | ++idx; | ||
48 | |||
49 | if (strcmp(perf_evsel__name(evsel), name)) { | ||
50 | pr_debug("%s != %s\n", perf_evsel__name(evsel), name); | ||
51 | ret = -1; | ||
52 | } | ||
53 | |||
54 | evsel = perf_evsel__next(evsel); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | perf_evlist__delete(evlist); | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static int __perf_evsel__name_array_test(const char *names[], int nr_names) | ||
64 | { | ||
65 | int i, err; | ||
66 | struct perf_evsel *evsel; | ||
67 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
68 | |||
69 | if (evlist == NULL) | ||
70 | return -ENOMEM; | ||
71 | |||
72 | for (i = 0; i < nr_names; ++i) { | ||
73 | err = parse_events(evlist, names[i], 0); | ||
74 | if (err) { | ||
75 | pr_debug("failed to parse event '%s', err %d\n", | ||
76 | names[i], err); | ||
77 | goto out_delete_evlist; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | err = 0; | ||
82 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
83 | if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { | ||
84 | --err; | ||
85 | pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | out_delete_evlist: | ||
90 | perf_evlist__delete(evlist); | ||
91 | return err; | ||
92 | } | ||
93 | |||
94 | #define perf_evsel__name_array_test(names) \ | ||
95 | __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) | ||
96 | |||
97 | int test__perf_evsel__roundtrip_name_test(void) | ||
98 | { | ||
99 | int err = 0, ret = 0; | ||
100 | |||
101 | err = perf_evsel__name_array_test(perf_evsel__hw_names); | ||
102 | if (err) | ||
103 | ret = err; | ||
104 | |||
105 | err = perf_evsel__name_array_test(perf_evsel__sw_names); | ||
106 | if (err) | ||
107 | ret = err; | ||
108 | |||
109 | err = perf_evsel__roundtrip_cache_name_test(); | ||
110 | if (err) | ||
111 | ret = err; | ||
112 | |||
113 | return ret; | ||
114 | } | ||
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c new file mode 100644 index 000000000000..a5d2fcc5ae35 --- /dev/null +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -0,0 +1,84 @@ | |||
1 | #include "evsel.h" | ||
2 | #include "tests.h" | ||
3 | #include "event-parse.h" | ||
4 | |||
5 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | ||
6 | int size, bool should_be_signed) | ||
7 | { | ||
8 | struct format_field *field = perf_evsel__field(evsel, name); | ||
9 | int is_signed; | ||
10 | int ret = 0; | ||
11 | |||
12 | if (field == NULL) { | ||
13 | pr_debug("%s: \"%s\" field not found!\n", evsel->name, name); | ||
14 | return -1; | ||
15 | } | ||
16 | |||
17 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | ||
18 | if (should_be_signed && !is_signed) { | ||
19 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | ||
20 | evsel->name, name, is_signed, should_be_signed); | ||
21 | ret = -1; | ||
22 | } | ||
23 | |||
24 | if (field->size != size) { | ||
25 | pr_debug("%s: \"%s\" size (%d) should be %d!\n", | ||
26 | evsel->name, name, field->size, size); | ||
27 | ret = -1; | ||
28 | } | ||
29 | |||
30 | return ret; | ||
31 | } | ||
32 | |||
33 | int test__perf_evsel__tp_sched_test(void) | ||
34 | { | ||
35 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | ||
36 | int ret = 0; | ||
37 | |||
38 | if (evsel == NULL) { | ||
39 | pr_debug("perf_evsel__new\n"); | ||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) | ||
44 | ret = -1; | ||
45 | |||
46 | if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) | ||
47 | ret = -1; | ||
48 | |||
49 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | ||
50 | ret = -1; | ||
51 | |||
52 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | ||
53 | ret = -1; | ||
54 | |||
55 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | ||
56 | ret = -1; | ||
57 | |||
58 | if (perf_evsel__test_field(evsel, "next_pid", 4, true)) | ||
59 | ret = -1; | ||
60 | |||
61 | if (perf_evsel__test_field(evsel, "next_prio", 4, true)) | ||
62 | ret = -1; | ||
63 | |||
64 | perf_evsel__delete(evsel); | ||
65 | |||
66 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | ||
67 | |||
68 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | ||
69 | ret = -1; | ||
70 | |||
71 | if (perf_evsel__test_field(evsel, "pid", 4, true)) | ||
72 | ret = -1; | ||
73 | |||
74 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | ||
75 | ret = -1; | ||
76 | |||
77 | if (perf_evsel__test_field(evsel, "success", 4, true)) | ||
78 | ret = -1; | ||
79 | |||
80 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | ||
81 | ret = -1; | ||
82 | |||
83 | return ret; | ||
84 | } | ||
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c new file mode 100644 index 000000000000..e1746811e14b --- /dev/null +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -0,0 +1,162 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | /* | ||
8 | * This test will generate random numbers of calls to some getpid syscalls, | ||
9 | * then establish an mmap for a group of events that are created to monitor | ||
10 | * the syscalls. | ||
11 | * | ||
12 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
13 | * sample.id field to map back to its respective perf_evsel instance. | ||
14 | * | ||
15 | * Then it checks if the number of syscalls reported as perf events by | ||
16 | * the kernel corresponds to the number of syscalls made. | ||
17 | */ | ||
18 | int test__basic_mmap(void) | ||
19 | { | ||
20 | int err = -1; | ||
21 | union perf_event *event; | ||
22 | struct thread_map *threads; | ||
23 | struct cpu_map *cpus; | ||
24 | struct perf_evlist *evlist; | ||
25 | struct perf_event_attr attr = { | ||
26 | .type = PERF_TYPE_TRACEPOINT, | ||
27 | .read_format = PERF_FORMAT_ID, | ||
28 | .sample_type = PERF_SAMPLE_ID, | ||
29 | .watermark = 0, | ||
30 | }; | ||
31 | cpu_set_t cpu_set; | ||
32 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
33 | "getpgid", }; | ||
34 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
35 | (void*)getpgid }; | ||
36 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
37 | int ids[nsyscalls]; | ||
38 | unsigned int nr_events[nsyscalls], | ||
39 | expected_nr_events[nsyscalls], i, j; | ||
40 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
41 | |||
42 | for (i = 0; i < nsyscalls; ++i) { | ||
43 | char name[64]; | ||
44 | |||
45 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
46 | ids[i] = trace_event__id(name); | ||
47 | if (ids[i] < 0) { | ||
48 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
49 | return -1; | ||
50 | } | ||
51 | nr_events[i] = 0; | ||
52 | expected_nr_events[i] = random() % 257; | ||
53 | } | ||
54 | |||
55 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
56 | if (threads == NULL) { | ||
57 | pr_debug("thread_map__new\n"); | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | cpus = cpu_map__new(NULL); | ||
62 | if (cpus == NULL) { | ||
63 | pr_debug("cpu_map__new\n"); | ||
64 | goto out_free_threads; | ||
65 | } | ||
66 | |||
67 | CPU_ZERO(&cpu_set); | ||
68 | CPU_SET(cpus->map[0], &cpu_set); | ||
69 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
70 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
71 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
72 | cpus->map[0], strerror(errno)); | ||
73 | goto out_free_cpus; | ||
74 | } | ||
75 | |||
76 | evlist = perf_evlist__new(cpus, threads); | ||
77 | if (evlist == NULL) { | ||
78 | pr_debug("perf_evlist__new\n"); | ||
79 | goto out_free_cpus; | ||
80 | } | ||
81 | |||
82 | /* anonymous union fields, can't be initialized above */ | ||
83 | attr.wakeup_events = 1; | ||
84 | attr.sample_period = 1; | ||
85 | |||
86 | for (i = 0; i < nsyscalls; ++i) { | ||
87 | attr.config = ids[i]; | ||
88 | evsels[i] = perf_evsel__new(&attr, i); | ||
89 | if (evsels[i] == NULL) { | ||
90 | pr_debug("perf_evsel__new\n"); | ||
91 | goto out_free_evlist; | ||
92 | } | ||
93 | |||
94 | perf_evlist__add(evlist, evsels[i]); | ||
95 | |||
96 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | ||
97 | pr_debug("failed to open counter: %s, " | ||
98 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
99 | strerror(errno)); | ||
100 | goto out_close_fd; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
105 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
106 | strerror(errno)); | ||
107 | goto out_close_fd; | ||
108 | } | ||
109 | |||
110 | for (i = 0; i < nsyscalls; ++i) | ||
111 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
112 | int foo = syscalls[i](); | ||
113 | ++foo; | ||
114 | } | ||
115 | |||
116 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
117 | struct perf_sample sample; | ||
118 | |||
119 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
120 | pr_debug("unexpected %s event\n", | ||
121 | perf_event__name(event->header.type)); | ||
122 | goto out_munmap; | ||
123 | } | ||
124 | |||
125 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
126 | if (err) { | ||
127 | pr_err("Can't parse sample, err = %d\n", err); | ||
128 | goto out_munmap; | ||
129 | } | ||
130 | |||
131 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
132 | if (evsel == NULL) { | ||
133 | pr_debug("event with id %" PRIu64 | ||
134 | " doesn't map to an evsel\n", sample.id); | ||
135 | goto out_munmap; | ||
136 | } | ||
137 | nr_events[evsel->idx]++; | ||
138 | } | ||
139 | |||
140 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
141 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
142 | pr_debug("expected %d %s events, got %d\n", | ||
143 | expected_nr_events[evsel->idx], | ||
144 | perf_evsel__name(evsel), nr_events[evsel->idx]); | ||
145 | goto out_munmap; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | err = 0; | ||
150 | out_munmap: | ||
151 | perf_evlist__munmap(evlist); | ||
152 | out_close_fd: | ||
153 | for (i = 0; i < nsyscalls; ++i) | ||
154 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
155 | out_free_evlist: | ||
156 | perf_evlist__delete(evlist); | ||
157 | out_free_cpus: | ||
158 | cpu_map__delete(cpus); | ||
159 | out_free_threads: | ||
160 | thread_map__delete(threads); | ||
161 | return err; | ||
162 | } | ||
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c new file mode 100644 index 000000000000..31072aba0d54 --- /dev/null +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
@@ -0,0 +1,120 @@ | |||
1 | #include "evsel.h" | ||
2 | #include "tests.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "debug.h" | ||
6 | |||
7 | int test__open_syscall_event_on_all_cpus(void) | ||
8 | { | ||
9 | int err = -1, fd, cpu; | ||
10 | struct thread_map *threads; | ||
11 | struct cpu_map *cpus; | ||
12 | struct perf_evsel *evsel; | ||
13 | struct perf_event_attr attr; | ||
14 | unsigned int nr_open_calls = 111, i; | ||
15 | cpu_set_t cpu_set; | ||
16 | int id = trace_event__id("sys_enter_open"); | ||
17 | |||
18 | if (id < 0) { | ||
19 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
20 | return -1; | ||
21 | } | ||
22 | |||
23 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
24 | if (threads == NULL) { | ||
25 | pr_debug("thread_map__new\n"); | ||
26 | return -1; | ||
27 | } | ||
28 | |||
29 | cpus = cpu_map__new(NULL); | ||
30 | if (cpus == NULL) { | ||
31 | pr_debug("cpu_map__new\n"); | ||
32 | goto out_thread_map_delete; | ||
33 | } | ||
34 | |||
35 | |||
36 | CPU_ZERO(&cpu_set); | ||
37 | |||
38 | memset(&attr, 0, sizeof(attr)); | ||
39 | attr.type = PERF_TYPE_TRACEPOINT; | ||
40 | attr.config = id; | ||
41 | evsel = perf_evsel__new(&attr, 0); | ||
42 | if (evsel == NULL) { | ||
43 | pr_debug("perf_evsel__new\n"); | ||
44 | goto out_thread_map_delete; | ||
45 | } | ||
46 | |||
47 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | ||
48 | pr_debug("failed to open counter: %s, " | ||
49 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
50 | strerror(errno)); | ||
51 | goto out_evsel_delete; | ||
52 | } | ||
53 | |||
54 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
55 | unsigned int ncalls = nr_open_calls + cpu; | ||
56 | /* | ||
57 | * XXX eventually lift this restriction in a way that | ||
58 | * keeps perf building on older glibc installations | ||
59 | * without CPU_ALLOC. 1024 cpus in 2010 still seems | ||
60 | * a reasonable upper limit tho :-) | ||
61 | */ | ||
62 | if (cpus->map[cpu] >= CPU_SETSIZE) { | ||
63 | pr_debug("Ignoring CPU %d\n", cpus->map[cpu]); | ||
64 | continue; | ||
65 | } | ||
66 | |||
67 | CPU_SET(cpus->map[cpu], &cpu_set); | ||
68 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
69 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
70 | cpus->map[cpu], | ||
71 | strerror(errno)); | ||
72 | goto out_close_fd; | ||
73 | } | ||
74 | for (i = 0; i < ncalls; ++i) { | ||
75 | fd = open("/etc/passwd", O_RDONLY); | ||
76 | close(fd); | ||
77 | } | ||
78 | CPU_CLR(cpus->map[cpu], &cpu_set); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * Here we need to explicitely preallocate the counts, as if | ||
83 | * we use the auto allocation it will allocate just for 1 cpu, | ||
84 | * as we start by cpu 0. | ||
85 | */ | ||
86 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | ||
87 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | ||
88 | goto out_close_fd; | ||
89 | } | ||
90 | |||
91 | err = 0; | ||
92 | |||
93 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
94 | unsigned int expected; | ||
95 | |||
96 | if (cpus->map[cpu] >= CPU_SETSIZE) | ||
97 | continue; | ||
98 | |||
99 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | ||
100 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
101 | err = -1; | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | expected = nr_open_calls + cpu; | ||
106 | if (evsel->counts->cpu[cpu].val != expected) { | ||
107 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | ||
108 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | ||
109 | err = -1; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | out_close_fd: | ||
114 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
115 | out_evsel_delete: | ||
116 | perf_evsel__delete(evsel); | ||
117 | out_thread_map_delete: | ||
118 | thread_map__delete(threads); | ||
119 | return err; | ||
120 | } | ||
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c new file mode 100644 index 000000000000..1c52fdc1164e --- /dev/null +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -0,0 +1,117 @@ | |||
1 | #include "perf.h" | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "thread_map.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | int test__syscall_open_tp_fields(void) | ||
8 | { | ||
9 | struct perf_record_opts opts = { | ||
10 | .target = { | ||
11 | .uid = UINT_MAX, | ||
12 | .uses_mmap = true, | ||
13 | }, | ||
14 | .no_delay = true, | ||
15 | .freq = 1, | ||
16 | .mmap_pages = 256, | ||
17 | .raw_samples = true, | ||
18 | }; | ||
19 | const char *filename = "/etc/passwd"; | ||
20 | int flags = O_RDONLY | O_DIRECTORY; | ||
21 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
22 | struct perf_evsel *evsel; | ||
23 | int err = -1, i, nr_events = 0, nr_polls = 0; | ||
24 | |||
25 | if (evlist == NULL) { | ||
26 | pr_debug("%s: perf_evlist__new\n", __func__); | ||
27 | goto out; | ||
28 | } | ||
29 | |||
30 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
31 | if (evsel == NULL) { | ||
32 | pr_debug("%s: perf_evsel__newtp\n", __func__); | ||
33 | goto out_delete_evlist; | ||
34 | } | ||
35 | |||
36 | perf_evlist__add(evlist, evsel); | ||
37 | |||
38 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
39 | if (err < 0) { | ||
40 | pr_debug("%s: perf_evlist__create_maps\n", __func__); | ||
41 | goto out_delete_evlist; | ||
42 | } | ||
43 | |||
44 | perf_evsel__config(evsel, &opts); | ||
45 | |||
46 | evlist->threads->map[0] = getpid(); | ||
47 | |||
48 | err = perf_evlist__open(evlist); | ||
49 | if (err < 0) { | ||
50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
51 | goto out_delete_evlist; | ||
52 | } | ||
53 | |||
54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
55 | if (err < 0) { | ||
56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
57 | goto out_delete_evlist; | ||
58 | } | ||
59 | |||
60 | perf_evlist__enable(evlist); | ||
61 | |||
62 | /* | ||
63 | * Generate the event: | ||
64 | */ | ||
65 | open(filename, flags); | ||
66 | |||
67 | while (1) { | ||
68 | int before = nr_events; | ||
69 | |||
70 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
71 | union perf_event *event; | ||
72 | |||
73 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
74 | const u32 type = event->header.type; | ||
75 | int tp_flags; | ||
76 | struct perf_sample sample; | ||
77 | |||
78 | ++nr_events; | ||
79 | |||
80 | if (type != PERF_RECORD_SAMPLE) | ||
81 | continue; | ||
82 | |||
83 | err = perf_evsel__parse_sample(evsel, event, &sample); | ||
84 | if (err) { | ||
85 | pr_err("Can't parse sample, err = %d\n", err); | ||
86 | goto out_munmap; | ||
87 | } | ||
88 | |||
89 | tp_flags = perf_evsel__intval(evsel, &sample, "flags"); | ||
90 | |||
91 | if (flags != tp_flags) { | ||
92 | pr_debug("%s: Expected flags=%#x, got %#x\n", | ||
93 | __func__, flags, tp_flags); | ||
94 | goto out_munmap; | ||
95 | } | ||
96 | |||
97 | goto out_ok; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | if (nr_events == before) | ||
102 | poll(evlist->pollfd, evlist->nr_fds, 10); | ||
103 | |||
104 | if (++nr_polls > 5) { | ||
105 | pr_debug("%s: no events!\n", __func__); | ||
106 | goto out_munmap; | ||
107 | } | ||
108 | } | ||
109 | out_ok: | ||
110 | err = 0; | ||
111 | out_munmap: | ||
112 | perf_evlist__munmap(evlist); | ||
113 | out_delete_evlist: | ||
114 | perf_evlist__delete(evlist); | ||
115 | out: | ||
116 | return err; | ||
117 | } | ||
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c new file mode 100644 index 000000000000..98be8b518b4f --- /dev/null +++ b/tools/perf/tests/open-syscall.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include "thread_map.h" | ||
2 | #include "evsel.h" | ||
3 | #include "debug.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | int test__open_syscall_event(void) | ||
7 | { | ||
8 | int err = -1, fd; | ||
9 | struct thread_map *threads; | ||
10 | struct perf_evsel *evsel; | ||
11 | struct perf_event_attr attr; | ||
12 | unsigned int nr_open_calls = 111, i; | ||
13 | int id = trace_event__id("sys_enter_open"); | ||
14 | |||
15 | if (id < 0) { | ||
16 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
17 | return -1; | ||
18 | } | ||
19 | |||
20 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
21 | if (threads == NULL) { | ||
22 | pr_debug("thread_map__new\n"); | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | memset(&attr, 0, sizeof(attr)); | ||
27 | attr.type = PERF_TYPE_TRACEPOINT; | ||
28 | attr.config = id; | ||
29 | evsel = perf_evsel__new(&attr, 0); | ||
30 | if (evsel == NULL) { | ||
31 | pr_debug("perf_evsel__new\n"); | ||
32 | goto out_thread_map_delete; | ||
33 | } | ||
34 | |||
35 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | ||
36 | pr_debug("failed to open counter: %s, " | ||
37 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
38 | strerror(errno)); | ||
39 | goto out_evsel_delete; | ||
40 | } | ||
41 | |||
42 | for (i = 0; i < nr_open_calls; ++i) { | ||
43 | fd = open("/etc/passwd", O_RDONLY); | ||
44 | close(fd); | ||
45 | } | ||
46 | |||
47 | if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) { | ||
48 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
49 | goto out_close_fd; | ||
50 | } | ||
51 | |||
52 | if (evsel->counts->cpu[0].val != nr_open_calls) { | ||
53 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", | ||
54 | nr_open_calls, evsel->counts->cpu[0].val); | ||
55 | goto out_close_fd; | ||
56 | } | ||
57 | |||
58 | err = 0; | ||
59 | out_close_fd: | ||
60 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
61 | out_evsel_delete: | ||
62 | perf_evsel__delete(evsel); | ||
63 | out_thread_map_delete: | ||
64 | thread_map__delete(threads); | ||
65 | return err; | ||
66 | } | ||
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/tests/parse-events.c index 6ef213b35ecd..32ee478905eb 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "evsel.h" | 3 | #include "evsel.h" |
4 | #include "evlist.h" | 4 | #include "evlist.h" |
5 | #include "sysfs.h" | 5 | #include "sysfs.h" |
6 | #include "tests.h" | ||
6 | #include <linux/hw_breakpoint.h> | 7 | #include <linux/hw_breakpoint.h> |
7 | 8 | ||
8 | #define TEST_ASSERT_VAL(text, cond) \ | 9 | #define TEST_ASSERT_VAL(text, cond) \ |
@@ -443,6 +444,23 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) | |||
443 | return 0; | 444 | return 0; |
444 | } | 445 | } |
445 | 446 | ||
447 | static int test__checkevent_pmu_events(struct perf_evlist *evlist) | ||
448 | { | ||
449 | struct perf_evsel *evsel; | ||
450 | |||
451 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
452 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
453 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
454 | TEST_ASSERT_VAL("wrong exclude_user", | ||
455 | !evsel->attr.exclude_user); | ||
456 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
457 | evsel->attr.exclude_kernel); | ||
458 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
459 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
446 | static int test__checkterms_simple(struct list_head *terms) | 464 | static int test__checkterms_simple(struct list_head *terms) |
447 | { | 465 | { |
448 | struct parse_events__term *term; | 466 | struct parse_events__term *term; |
@@ -503,7 +521,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
503 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 521 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
504 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 522 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
505 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 523 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
506 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 524 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
507 | 525 | ||
508 | /* cycles:upp */ | 526 | /* cycles:upp */ |
509 | evsel = perf_evsel__next(evsel); | 527 | evsel = perf_evsel__next(evsel); |
@@ -539,7 +557,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
539 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 557 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
540 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 558 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
541 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 559 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
542 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 560 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
543 | 561 | ||
544 | /* cache-references + :u modifier */ | 562 | /* cache-references + :u modifier */ |
545 | evsel = perf_evsel__next(evsel); | 563 | evsel = perf_evsel__next(evsel); |
@@ -565,7 +583,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
565 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 583 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
566 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 584 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
567 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 585 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
568 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 586 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
569 | 587 | ||
570 | return 0; | 588 | return 0; |
571 | } | 589 | } |
@@ -588,7 +606,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
588 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 606 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
589 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 607 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
590 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 608 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
591 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 609 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
592 | TEST_ASSERT_VAL("wrong group name", | 610 | TEST_ASSERT_VAL("wrong group name", |
593 | !strcmp(leader->group_name, "group1")); | 611 | !strcmp(leader->group_name, "group1")); |
594 | 612 | ||
@@ -618,7 +636,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
618 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 636 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
619 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 637 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
620 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 638 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
621 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 639 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
622 | TEST_ASSERT_VAL("wrong group name", | 640 | TEST_ASSERT_VAL("wrong group name", |
623 | !strcmp(leader->group_name, "group2")); | 641 | !strcmp(leader->group_name, "group2")); |
624 | 642 | ||
@@ -645,7 +663,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
645 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 663 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
646 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 664 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
647 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 665 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
648 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 666 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
649 | 667 | ||
650 | return 0; | 668 | return 0; |
651 | } | 669 | } |
@@ -669,7 +687,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
669 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 687 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
670 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); | 688 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); |
671 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 689 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
672 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 690 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
673 | 691 | ||
674 | /* instructions:kp + p */ | 692 | /* instructions:kp + p */ |
675 | evsel = perf_evsel__next(evsel); | 693 | evsel = perf_evsel__next(evsel); |
@@ -706,7 +724,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
706 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 724 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
707 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 725 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
708 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 726 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
709 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 727 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
710 | 728 | ||
711 | /* instructions + G */ | 729 | /* instructions + G */ |
712 | evsel = perf_evsel__next(evsel); | 730 | evsel = perf_evsel__next(evsel); |
@@ -733,7 +751,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
733 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 751 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
734 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 752 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
735 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 753 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
736 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 754 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
737 | 755 | ||
738 | /* instructions:G */ | 756 | /* instructions:G */ |
739 | evsel = perf_evsel__next(evsel); | 757 | evsel = perf_evsel__next(evsel); |
@@ -759,7 +777,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
759 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 777 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
760 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 778 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
761 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 779 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
762 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 780 | TEST_ASSERT_VAL("wrong leader", !perf_evsel__is_group_member(evsel)); |
763 | 781 | ||
764 | return 0; | 782 | return 0; |
765 | } | 783 | } |
@@ -1024,7 +1042,52 @@ static int test_pmu(void) | |||
1024 | return !ret; | 1042 | return !ret; |
1025 | } | 1043 | } |
1026 | 1044 | ||
1027 | int parse_events__test(void) | 1045 | static int test_pmu_events(void) |
1046 | { | ||
1047 | struct stat st; | ||
1048 | char path[PATH_MAX]; | ||
1049 | struct dirent *ent; | ||
1050 | DIR *dir; | ||
1051 | int ret; | ||
1052 | |||
1053 | snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/", | ||
1054 | sysfs_find_mountpoint()); | ||
1055 | |||
1056 | ret = stat(path, &st); | ||
1057 | if (ret) { | ||
1058 | pr_debug("ommiting PMU cpu events tests\n"); | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | dir = opendir(path); | ||
1063 | if (!dir) { | ||
1064 | pr_debug("can't open pmu event dir"); | ||
1065 | return -1; | ||
1066 | } | ||
1067 | |||
1068 | while (!ret && (ent = readdir(dir))) { | ||
1069 | #define MAX_NAME 100 | ||
1070 | struct test__event_st e; | ||
1071 | char name[MAX_NAME]; | ||
1072 | |||
1073 | if (!strcmp(ent->d_name, ".") || | ||
1074 | !strcmp(ent->d_name, "..")) | ||
1075 | continue; | ||
1076 | |||
1077 | snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name); | ||
1078 | |||
1079 | e.name = name; | ||
1080 | e.check = test__checkevent_pmu_events; | ||
1081 | |||
1082 | ret = test_event(&e); | ||
1083 | #undef MAX_NAME | ||
1084 | } | ||
1085 | |||
1086 | closedir(dir); | ||
1087 | return ret; | ||
1088 | } | ||
1089 | |||
1090 | int test__parse_events(void) | ||
1028 | { | 1091 | { |
1029 | int ret1, ret2 = 0; | 1092 | int ret1, ret2 = 0; |
1030 | 1093 | ||
@@ -1040,6 +1103,12 @@ do { \ | |||
1040 | if (test_pmu()) | 1103 | if (test_pmu()) |
1041 | TEST_EVENTS(test__events_pmu); | 1104 | TEST_EVENTS(test__events_pmu); |
1042 | 1105 | ||
1106 | if (test_pmu()) { | ||
1107 | int ret = test_pmu_events(); | ||
1108 | if (ret) | ||
1109 | return ret; | ||
1110 | } | ||
1111 | |||
1043 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); | 1112 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); |
1044 | if (!ret2) | 1113 | if (!ret2) |
1045 | ret2 = ret1; | 1114 | ret2 = ret1; |
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c new file mode 100644 index 000000000000..70e0d4421df8 --- /dev/null +++ b/tools/perf/tests/perf-record.c | |||
@@ -0,0 +1,312 @@ | |||
1 | #include <sched.h> | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "perf.h" | ||
5 | #include "debug.h" | ||
6 | #include "tests.h" | ||
7 | |||
8 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp) | ||
9 | { | ||
10 | int i, cpu = -1, nrcpus = 1024; | ||
11 | realloc: | ||
12 | CPU_ZERO(maskp); | ||
13 | |||
14 | if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) { | ||
15 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
16 | nrcpus = nrcpus << 2; | ||
17 | goto realloc; | ||
18 | } | ||
19 | perror("sched_getaffinity"); | ||
20 | return -1; | ||
21 | } | ||
22 | |||
23 | for (i = 0; i < nrcpus; i++) { | ||
24 | if (CPU_ISSET(i, maskp)) { | ||
25 | if (cpu == -1) | ||
26 | cpu = i; | ||
27 | else | ||
28 | CPU_CLR(i, maskp); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | return cpu; | ||
33 | } | ||
34 | |||
35 | int test__PERF_RECORD(void) | ||
36 | { | ||
37 | struct perf_record_opts opts = { | ||
38 | .target = { | ||
39 | .uid = UINT_MAX, | ||
40 | .uses_mmap = true, | ||
41 | }, | ||
42 | .no_delay = true, | ||
43 | .freq = 10, | ||
44 | .mmap_pages = 256, | ||
45 | }; | ||
46 | cpu_set_t cpu_mask; | ||
47 | size_t cpu_mask_size = sizeof(cpu_mask); | ||
48 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
49 | struct perf_evsel *evsel; | ||
50 | struct perf_sample sample; | ||
51 | const char *cmd = "sleep"; | ||
52 | const char *argv[] = { cmd, "1", NULL, }; | ||
53 | char *bname; | ||
54 | u64 prev_time = 0; | ||
55 | bool found_cmd_mmap = false, | ||
56 | found_libc_mmap = false, | ||
57 | found_vdso_mmap = false, | ||
58 | found_ld_mmap = false; | ||
59 | int err = -1, errs = 0, i, wakeups = 0; | ||
60 | u32 cpu; | ||
61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
62 | |||
63 | if (evlist == NULL || argv == NULL) { | ||
64 | pr_debug("Not enough memory to create evlist\n"); | ||
65 | goto out; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * We need at least one evsel in the evlist, use the default | ||
70 | * one: "cycles". | ||
71 | */ | ||
72 | err = perf_evlist__add_default(evlist); | ||
73 | if (err < 0) { | ||
74 | pr_debug("Not enough memory to create evsel\n"); | ||
75 | goto out_delete_evlist; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Create maps of threads and cpus to monitor. In this case | ||
80 | * we start with all threads and cpus (-1, -1) but then in | ||
81 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
82 | * we're monitoring, the one forked there. | ||
83 | */ | ||
84 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
85 | if (err < 0) { | ||
86 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
87 | goto out_delete_evlist; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
92 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
93 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
94 | * on all the fds) and then mmap them. | ||
95 | */ | ||
96 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
97 | if (err < 0) { | ||
98 | pr_debug("Couldn't run the workload!\n"); | ||
99 | goto out_delete_evlist; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Config the evsels, setting attr->comm on the first one, etc. | ||
104 | */ | ||
105 | evsel = perf_evlist__first(evlist); | ||
106 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | ||
107 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | ||
108 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | ||
109 | perf_evlist__config_attrs(evlist, &opts); | ||
110 | |||
111 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); | ||
112 | if (err < 0) { | ||
113 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
114 | goto out_delete_evlist; | ||
115 | } | ||
116 | |||
117 | cpu = err; | ||
118 | |||
119 | /* | ||
120 | * So that we can check perf_sample.cpu on all the samples. | ||
121 | */ | ||
122 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { | ||
123 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
124 | goto out_delete_evlist; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
129 | * grouping them if asked to. | ||
130 | */ | ||
131 | err = perf_evlist__open(evlist); | ||
132 | if (err < 0) { | ||
133 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
134 | goto out_delete_evlist; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * mmap the first fd on a given CPU and ask for events for the other | ||
139 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
140 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
141 | */ | ||
142 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
143 | if (err < 0) { | ||
144 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
145 | goto out_delete_evlist; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Now that all is properly set up, enable the events, they will | ||
150 | * count just on workload.pid, which will start... | ||
151 | */ | ||
152 | perf_evlist__enable(evlist); | ||
153 | |||
154 | /* | ||
155 | * Now! | ||
156 | */ | ||
157 | perf_evlist__start_workload(evlist); | ||
158 | |||
159 | while (1) { | ||
160 | int before = total_events; | ||
161 | |||
162 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
163 | union perf_event *event; | ||
164 | |||
165 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
166 | const u32 type = event->header.type; | ||
167 | const char *name = perf_event__name(type); | ||
168 | |||
169 | ++total_events; | ||
170 | if (type < PERF_RECORD_MAX) | ||
171 | nr_events[type]++; | ||
172 | |||
173 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
174 | if (err < 0) { | ||
175 | if (verbose) | ||
176 | perf_event__fprintf(event, stderr); | ||
177 | pr_debug("Couldn't parse sample\n"); | ||
178 | goto out_err; | ||
179 | } | ||
180 | |||
181 | if (verbose) { | ||
182 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
183 | perf_event__fprintf(event, stderr); | ||
184 | } | ||
185 | |||
186 | if (prev_time > sample.time) { | ||
187 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
188 | name, prev_time, sample.time); | ||
189 | ++errs; | ||
190 | } | ||
191 | |||
192 | prev_time = sample.time; | ||
193 | |||
194 | if (sample.cpu != cpu) { | ||
195 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
196 | name, cpu, sample.cpu); | ||
197 | ++errs; | ||
198 | } | ||
199 | |||
200 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
201 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
202 | name, evlist->workload.pid, sample.pid); | ||
203 | ++errs; | ||
204 | } | ||
205 | |||
206 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
207 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
208 | name, evlist->workload.pid, sample.tid); | ||
209 | ++errs; | ||
210 | } | ||
211 | |||
212 | if ((type == PERF_RECORD_COMM || | ||
213 | type == PERF_RECORD_MMAP || | ||
214 | type == PERF_RECORD_FORK || | ||
215 | type == PERF_RECORD_EXIT) && | ||
216 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
217 | pr_debug("%s with unexpected pid/tid\n", name); | ||
218 | ++errs; | ||
219 | } | ||
220 | |||
221 | if ((type == PERF_RECORD_COMM || | ||
222 | type == PERF_RECORD_MMAP) && | ||
223 | event->comm.pid != event->comm.tid) { | ||
224 | pr_debug("%s with different pid/tid!\n", name); | ||
225 | ++errs; | ||
226 | } | ||
227 | |||
228 | switch (type) { | ||
229 | case PERF_RECORD_COMM: | ||
230 | if (strcmp(event->comm.comm, cmd)) { | ||
231 | pr_debug("%s with unexpected comm!\n", name); | ||
232 | ++errs; | ||
233 | } | ||
234 | break; | ||
235 | case PERF_RECORD_EXIT: | ||
236 | goto found_exit; | ||
237 | case PERF_RECORD_MMAP: | ||
238 | bname = strrchr(event->mmap.filename, '/'); | ||
239 | if (bname != NULL) { | ||
240 | if (!found_cmd_mmap) | ||
241 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
242 | if (!found_libc_mmap) | ||
243 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
244 | if (!found_ld_mmap) | ||
245 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
246 | } else if (!found_vdso_mmap) | ||
247 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
248 | break; | ||
249 | |||
250 | case PERF_RECORD_SAMPLE: | ||
251 | /* Just ignore samples for now */ | ||
252 | break; | ||
253 | default: | ||
254 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
255 | type); | ||
256 | ++errs; | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * We don't use poll here because at least at 3.1 times the | ||
263 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
264 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
265 | */ | ||
266 | if (total_events == before && false) | ||
267 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
268 | |||
269 | sleep(1); | ||
270 | if (++wakeups > 5) { | ||
271 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | found_exit: | ||
277 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
278 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
279 | ++errs; | ||
280 | } | ||
281 | |||
282 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
283 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
284 | ++errs; | ||
285 | } | ||
286 | |||
287 | if (!found_cmd_mmap) { | ||
288 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
289 | ++errs; | ||
290 | } | ||
291 | |||
292 | if (!found_libc_mmap) { | ||
293 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
294 | ++errs; | ||
295 | } | ||
296 | |||
297 | if (!found_ld_mmap) { | ||
298 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
299 | ++errs; | ||
300 | } | ||
301 | |||
302 | if (!found_vdso_mmap) { | ||
303 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
304 | ++errs; | ||
305 | } | ||
306 | out_err: | ||
307 | perf_evlist__munmap(evlist); | ||
308 | out_delete_evlist: | ||
309 | perf_evlist__delete(evlist); | ||
310 | out: | ||
311 | return (err < 0 || errs > 0) ? -1 : 0; | ||
312 | } | ||
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c new file mode 100644 index 000000000000..a5f379863b8f --- /dev/null +++ b/tools/perf/tests/pmu.c | |||
@@ -0,0 +1,178 @@ | |||
1 | #include "parse-events.h" | ||
2 | #include "pmu.h" | ||
3 | #include "util.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | /* Simulated format definitions. */ | ||
7 | static struct test_format { | ||
8 | const char *name; | ||
9 | const char *value; | ||
10 | } test_formats[] = { | ||
11 | { "krava01", "config:0-1,62-63\n", }, | ||
12 | { "krava02", "config:10-17\n", }, | ||
13 | { "krava03", "config:5\n", }, | ||
14 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
15 | { "krava12", "config1:63\n", }, | ||
16 | { "krava13", "config1:45-47\n", }, | ||
17 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
18 | { "krava22", "config2:8,18,48,58\n", }, | ||
19 | { "krava23", "config2:28-29,38\n", }, | ||
20 | }; | ||
21 | |||
22 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
23 | |||
24 | /* Simulated users input. */ | ||
25 | static struct parse_events__term test_terms[] = { | ||
26 | { | ||
27 | .config = (char *) "krava01", | ||
28 | .val.num = 15, | ||
29 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
30 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
31 | }, | ||
32 | { | ||
33 | .config = (char *) "krava02", | ||
34 | .val.num = 170, | ||
35 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
36 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
37 | }, | ||
38 | { | ||
39 | .config = (char *) "krava03", | ||
40 | .val.num = 1, | ||
41 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
42 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
43 | }, | ||
44 | { | ||
45 | .config = (char *) "krava11", | ||
46 | .val.num = 27, | ||
47 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
48 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
49 | }, | ||
50 | { | ||
51 | .config = (char *) "krava12", | ||
52 | .val.num = 1, | ||
53 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
54 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
55 | }, | ||
56 | { | ||
57 | .config = (char *) "krava13", | ||
58 | .val.num = 2, | ||
59 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
60 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
61 | }, | ||
62 | { | ||
63 | .config = (char *) "krava21", | ||
64 | .val.num = 119, | ||
65 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
66 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
67 | }, | ||
68 | { | ||
69 | .config = (char *) "krava22", | ||
70 | .val.num = 11, | ||
71 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
72 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
73 | }, | ||
74 | { | ||
75 | .config = (char *) "krava23", | ||
76 | .val.num = 2, | ||
77 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
78 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
79 | }, | ||
80 | }; | ||
81 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
82 | |||
83 | /* | ||
84 | * Prepare format directory data, exported by kernel | ||
85 | * at /sys/bus/event_source/devices/<dev>/format. | ||
86 | */ | ||
87 | static char *test_format_dir_get(void) | ||
88 | { | ||
89 | static char dir[PATH_MAX]; | ||
90 | unsigned int i; | ||
91 | |||
92 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
93 | if (!mkdtemp(dir)) | ||
94 | return NULL; | ||
95 | |||
96 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
97 | static char name[PATH_MAX]; | ||
98 | struct test_format *format = &test_formats[i]; | ||
99 | FILE *file; | ||
100 | |||
101 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
102 | |||
103 | file = fopen(name, "w"); | ||
104 | if (!file) | ||
105 | return NULL; | ||
106 | |||
107 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
108 | break; | ||
109 | |||
110 | fclose(file); | ||
111 | } | ||
112 | |||
113 | return dir; | ||
114 | } | ||
115 | |||
116 | /* Cleanup format directory. */ | ||
117 | static int test_format_dir_put(char *dir) | ||
118 | { | ||
119 | char buf[PATH_MAX]; | ||
120 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
121 | if (system(buf)) | ||
122 | return -1; | ||
123 | |||
124 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
125 | return system(buf); | ||
126 | } | ||
127 | |||
128 | static struct list_head *test_terms_list(void) | ||
129 | { | ||
130 | static LIST_HEAD(terms); | ||
131 | unsigned int i; | ||
132 | |||
133 | for (i = 0; i < TERMS_CNT; i++) | ||
134 | list_add_tail(&test_terms[i].list, &terms); | ||
135 | |||
136 | return &terms; | ||
137 | } | ||
138 | |||
139 | #undef TERMS_CNT | ||
140 | |||
141 | int test__pmu(void) | ||
142 | { | ||
143 | char *format = test_format_dir_get(); | ||
144 | LIST_HEAD(formats); | ||
145 | struct list_head *terms = test_terms_list(); | ||
146 | int ret; | ||
147 | |||
148 | if (!format) | ||
149 | return -EINVAL; | ||
150 | |||
151 | do { | ||
152 | struct perf_event_attr attr; | ||
153 | |||
154 | memset(&attr, 0, sizeof(attr)); | ||
155 | |||
156 | ret = perf_pmu__format_parse(format, &formats); | ||
157 | if (ret) | ||
158 | break; | ||
159 | |||
160 | ret = perf_pmu__config_terms(&formats, &attr, terms); | ||
161 | if (ret) | ||
162 | break; | ||
163 | |||
164 | ret = -EINVAL; | ||
165 | |||
166 | if (attr.config != 0xc00000000002a823) | ||
167 | break; | ||
168 | if (attr.config1 != 0x8000400000000145) | ||
169 | break; | ||
170 | if (attr.config2 != 0x0400000020041d07) | ||
171 | break; | ||
172 | |||
173 | ret = 0; | ||
174 | } while (0); | ||
175 | |||
176 | test_format_dir_put(format); | ||
177 | return ret; | ||
178 | } | ||
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c new file mode 100644 index 000000000000..ff94886aad99 --- /dev/null +++ b/tools/perf/tests/rdpmc.c | |||
@@ -0,0 +1,175 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <signal.h> | ||
4 | #include <sys/mman.h> | ||
5 | #include "types.h" | ||
6 | #include "perf.h" | ||
7 | #include "debug.h" | ||
8 | #include "tests.h" | ||
9 | |||
10 | #if defined(__x86_64__) || defined(__i386__) | ||
11 | |||
12 | #define barrier() asm volatile("" ::: "memory") | ||
13 | |||
14 | static u64 rdpmc(unsigned int counter) | ||
15 | { | ||
16 | unsigned int low, high; | ||
17 | |||
18 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
19 | |||
20 | return low | ((u64)high) << 32; | ||
21 | } | ||
22 | |||
23 | static u64 rdtsc(void) | ||
24 | { | ||
25 | unsigned int low, high; | ||
26 | |||
27 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
28 | |||
29 | return low | ((u64)high) << 32; | ||
30 | } | ||
31 | |||
32 | static u64 mmap_read_self(void *addr) | ||
33 | { | ||
34 | struct perf_event_mmap_page *pc = addr; | ||
35 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
36 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
37 | |||
38 | do { | ||
39 | seq = pc->lock; | ||
40 | barrier(); | ||
41 | |||
42 | enabled = pc->time_enabled; | ||
43 | running = pc->time_running; | ||
44 | |||
45 | if (enabled != running) { | ||
46 | cyc = rdtsc(); | ||
47 | time_mult = pc->time_mult; | ||
48 | time_shift = pc->time_shift; | ||
49 | time_offset = pc->time_offset; | ||
50 | } | ||
51 | |||
52 | idx = pc->index; | ||
53 | count = pc->offset; | ||
54 | if (idx) | ||
55 | count += rdpmc(idx - 1); | ||
56 | |||
57 | barrier(); | ||
58 | } while (pc->lock != seq); | ||
59 | |||
60 | if (enabled != running) { | ||
61 | u64 quot, rem; | ||
62 | |||
63 | quot = (cyc >> time_shift); | ||
64 | rem = cyc & ((1 << time_shift) - 1); | ||
65 | delta = time_offset + quot * time_mult + | ||
66 | ((rem * time_mult) >> time_shift); | ||
67 | |||
68 | enabled += delta; | ||
69 | if (idx) | ||
70 | running += delta; | ||
71 | |||
72 | quot = count / running; | ||
73 | rem = count % running; | ||
74 | count = quot * enabled + (rem * enabled) / running; | ||
75 | } | ||
76 | |||
77 | return count; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
82 | */ | ||
83 | static void segfault_handler(int sig __maybe_unused, | ||
84 | siginfo_t *info __maybe_unused, | ||
85 | void *uc __maybe_unused) | ||
86 | { | ||
87 | exit(-1); | ||
88 | } | ||
89 | |||
90 | static int __test__rdpmc(void) | ||
91 | { | ||
92 | volatile int tmp = 0; | ||
93 | u64 i, loops = 1000; | ||
94 | int n; | ||
95 | int fd; | ||
96 | void *addr; | ||
97 | struct perf_event_attr attr = { | ||
98 | .type = PERF_TYPE_HARDWARE, | ||
99 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
100 | .exclude_kernel = 1, | ||
101 | }; | ||
102 | u64 delta_sum = 0; | ||
103 | struct sigaction sa; | ||
104 | |||
105 | sigfillset(&sa.sa_mask); | ||
106 | sa.sa_sigaction = segfault_handler; | ||
107 | sigaction(SIGSEGV, &sa, NULL); | ||
108 | |||
109 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
110 | if (fd < 0) { | ||
111 | pr_err("Error: sys_perf_event_open() syscall returned " | ||
112 | "with %d (%s)\n", fd, strerror(errno)); | ||
113 | return -1; | ||
114 | } | ||
115 | |||
116 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
117 | if (addr == (void *)(-1)) { | ||
118 | pr_err("Error: mmap() syscall returned with (%s)\n", | ||
119 | strerror(errno)); | ||
120 | goto out_close; | ||
121 | } | ||
122 | |||
123 | for (n = 0; n < 6; n++) { | ||
124 | u64 stamp, now, delta; | ||
125 | |||
126 | stamp = mmap_read_self(addr); | ||
127 | |||
128 | for (i = 0; i < loops; i++) | ||
129 | tmp++; | ||
130 | |||
131 | now = mmap_read_self(addr); | ||
132 | loops *= 10; | ||
133 | |||
134 | delta = now - stamp; | ||
135 | pr_debug("%14d: %14Lu\n", n, (long long)delta); | ||
136 | |||
137 | delta_sum += delta; | ||
138 | } | ||
139 | |||
140 | munmap(addr, page_size); | ||
141 | pr_debug(" "); | ||
142 | out_close: | ||
143 | close(fd); | ||
144 | |||
145 | if (!delta_sum) | ||
146 | return -1; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int test__rdpmc(void) | ||
152 | { | ||
153 | int status = 0; | ||
154 | int wret = 0; | ||
155 | int ret; | ||
156 | int pid; | ||
157 | |||
158 | pid = fork(); | ||
159 | if (pid < 0) | ||
160 | return -1; | ||
161 | |||
162 | if (!pid) { | ||
163 | ret = __test__rdpmc(); | ||
164 | |||
165 | exit(ret); | ||
166 | } | ||
167 | |||
168 | wret = waitpid(pid, &status, 0); | ||
169 | if (wret < 0 || status) | ||
170 | return -1; | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | #endif | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h new file mode 100644 index 000000000000..fc121edab016 --- /dev/null +++ b/tools/perf/tests/tests.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef TESTS_H | ||
2 | #define TESTS_H | ||
3 | |||
4 | /* Tests */ | ||
5 | int test__vmlinux_matches_kallsyms(void); | ||
6 | int test__open_syscall_event(void); | ||
7 | int test__open_syscall_event_on_all_cpus(void); | ||
8 | int test__basic_mmap(void); | ||
9 | int test__PERF_RECORD(void); | ||
10 | int test__rdpmc(void); | ||
11 | int test__perf_evsel__roundtrip_name_test(void); | ||
12 | int test__perf_evsel__tp_sched_test(void); | ||
13 | int test__syscall_open_tp_fields(void); | ||
14 | int test__pmu(void); | ||
15 | int test__attr(void); | ||
16 | int test__dso_data(void); | ||
17 | int test__parse_events(void); | ||
18 | |||
19 | /* Util */ | ||
20 | int trace_event__id(const char *evname); | ||
21 | |||
22 | #endif /* TESTS_H */ | ||
diff --git a/tools/perf/tests/util.c b/tools/perf/tests/util.c new file mode 100644 index 000000000000..748f2e8f6961 --- /dev/null +++ b/tools/perf/tests/util.c | |||
@@ -0,0 +1,30 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <sys/types.h> | ||
5 | #include <sys/stat.h> | ||
6 | #include <fcntl.h> | ||
7 | #include "tests.h" | ||
8 | #include "debugfs.h" | ||
9 | |||
10 | int trace_event__id(const char *evname) | ||
11 | { | ||
12 | char *filename; | ||
13 | int err = -1, fd; | ||
14 | |||
15 | if (asprintf(&filename, | ||
16 | "%s/syscalls/%s/id", | ||
17 | tracing_events_path, evname) < 0) | ||
18 | return -1; | ||
19 | |||
20 | fd = open(filename, O_RDONLY); | ||
21 | if (fd >= 0) { | ||
22 | char id[16]; | ||
23 | if (read(fd, id, sizeof(id)) > 0) | ||
24 | err = atoi(id); | ||
25 | close(fd); | ||
26 | } | ||
27 | |||
28 | free(filename); | ||
29 | return err; | ||
30 | } | ||
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c new file mode 100644 index 000000000000..0d1cdbee2f59 --- /dev/null +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -0,0 +1,230 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/rbtree.h> | ||
3 | #include <string.h> | ||
4 | #include "map.h" | ||
5 | #include "symbol.h" | ||
6 | #include "util.h" | ||
7 | #include "tests.h" | ||
8 | #include "debug.h" | ||
9 | #include "machine.h" | ||
10 | |||
11 | static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | ||
12 | struct symbol *sym) | ||
13 | { | ||
14 | bool *visited = symbol__priv(sym); | ||
15 | *visited = true; | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | int test__vmlinux_matches_kallsyms(void) | ||
20 | { | ||
21 | int err = -1; | ||
22 | struct rb_node *nd; | ||
23 | struct symbol *sym; | ||
24 | struct map *kallsyms_map, *vmlinux_map; | ||
25 | struct machine kallsyms, vmlinux; | ||
26 | enum map_type type = MAP__FUNCTION; | ||
27 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | ||
28 | |||
29 | /* | ||
30 | * Step 1: | ||
31 | * | ||
32 | * Init the machines that will hold kernel, modules obtained from | ||
33 | * both vmlinux + .ko files and from /proc/kallsyms split by modules. | ||
34 | */ | ||
35 | machine__init(&kallsyms, "", HOST_KERNEL_ID); | ||
36 | machine__init(&vmlinux, "", HOST_KERNEL_ID); | ||
37 | |||
38 | /* | ||
39 | * Step 2: | ||
40 | * | ||
41 | * Create the kernel maps for kallsyms and the DSO where we will then | ||
42 | * load /proc/kallsyms. Also create the modules maps from /proc/modules | ||
43 | * and find the .ko files that match them in /lib/modules/`uname -r`/. | ||
44 | */ | ||
45 | if (machine__create_kernel_maps(&kallsyms) < 0) { | ||
46 | pr_debug("machine__create_kernel_maps "); | ||
47 | return -1; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Step 3: | ||
52 | * | ||
53 | * Load and split /proc/kallsyms into multiple maps, one per module. | ||
54 | */ | ||
55 | if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { | ||
56 | pr_debug("dso__load_kallsyms "); | ||
57 | goto out; | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Step 4: | ||
62 | * | ||
63 | * kallsyms will be internally on demand sorted by name so that we can | ||
64 | * find the reference relocation * symbol, i.e. the symbol we will use | ||
65 | * to see if the running kernel was relocated by checking if it has the | ||
66 | * same value in the vmlinux file we load. | ||
67 | */ | ||
68 | kallsyms_map = machine__kernel_map(&kallsyms, type); | ||
69 | |||
70 | sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); | ||
71 | if (sym == NULL) { | ||
72 | pr_debug("dso__find_symbol_by_name "); | ||
73 | goto out; | ||
74 | } | ||
75 | |||
76 | ref_reloc_sym.addr = sym->start; | ||
77 | |||
78 | /* | ||
79 | * Step 5: | ||
80 | * | ||
81 | * Now repeat step 2, this time for the vmlinux file we'll auto-locate. | ||
82 | */ | ||
83 | if (machine__create_kernel_maps(&vmlinux) < 0) { | ||
84 | pr_debug("machine__create_kernel_maps "); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | vmlinux_map = machine__kernel_map(&vmlinux, type); | ||
89 | map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; | ||
90 | |||
91 | /* | ||
92 | * Step 6: | ||
93 | * | ||
94 | * Locate a vmlinux file in the vmlinux path that has a buildid that | ||
95 | * matches the one of the running kernel. | ||
96 | * | ||
97 | * While doing that look if we find the ref reloc symbol, if we find it | ||
98 | * we'll have its ref_reloc_symbol.unrelocated_addr and then | ||
99 | * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines | ||
100 | * to fixup the symbols. | ||
101 | */ | ||
102 | if (machine__load_vmlinux_path(&vmlinux, type, | ||
103 | vmlinux_matches_kallsyms_filter) <= 0) { | ||
104 | pr_debug("machine__load_vmlinux_path "); | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | err = 0; | ||
109 | /* | ||
110 | * Step 7: | ||
111 | * | ||
112 | * Now look at the symbols in the vmlinux DSO and check if we find all of them | ||
113 | * in the kallsyms dso. For the ones that are in both, check its names and | ||
114 | * end addresses too. | ||
115 | */ | ||
116 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
117 | struct symbol *pair, *first_pair; | ||
118 | bool backwards = true; | ||
119 | |||
120 | sym = rb_entry(nd, struct symbol, rb_node); | ||
121 | |||
122 | if (sym->start == sym->end) | ||
123 | continue; | ||
124 | |||
125 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
126 | pair = first_pair; | ||
127 | |||
128 | if (pair && pair->start == sym->start) { | ||
129 | next_pair: | ||
130 | if (strcmp(sym->name, pair->name) == 0) { | ||
131 | /* | ||
132 | * kallsyms don't have the symbol end, so we | ||
133 | * set that by using the next symbol start - 1, | ||
134 | * in some cases we get this up to a page | ||
135 | * wrong, trace_kmalloc when I was developing | ||
136 | * this code was one such example, 2106 bytes | ||
137 | * off the real size. More than that and we | ||
138 | * _really_ have a problem. | ||
139 | */ | ||
140 | s64 skew = sym->end - pair->end; | ||
141 | if (llabs(skew) < page_size) | ||
142 | continue; | ||
143 | |||
144 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", | ||
145 | sym->start, sym->name, sym->end, pair->end); | ||
146 | } else { | ||
147 | struct rb_node *nnd; | ||
148 | detour: | ||
149 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
150 | rb_next(&pair->rb_node); | ||
151 | if (nnd) { | ||
152 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
153 | |||
154 | if (next->start == sym->start) { | ||
155 | pair = next; | ||
156 | goto next_pair; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | if (backwards) { | ||
161 | backwards = false; | ||
162 | pair = first_pair; | ||
163 | goto detour; | ||
164 | } | ||
165 | |||
166 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
167 | sym->start, sym->name, pair->name); | ||
168 | } | ||
169 | } else | ||
170 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | ||
171 | |||
172 | err = -1; | ||
173 | } | ||
174 | |||
175 | if (!verbose) | ||
176 | goto out; | ||
177 | |||
178 | pr_info("Maps only in vmlinux:\n"); | ||
179 | |||
180 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
181 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
182 | /* | ||
183 | * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while | ||
184 | * the kernel will have the path for the vmlinux file being used, | ||
185 | * so use the short name, less descriptive but the same ("[kernel]" in | ||
186 | * both cases. | ||
187 | */ | ||
188 | pair = map_groups__find_by_name(&kallsyms.kmaps, type, | ||
189 | (pos->dso->kernel ? | ||
190 | pos->dso->short_name : | ||
191 | pos->dso->name)); | ||
192 | if (pair) | ||
193 | pair->priv = 1; | ||
194 | else | ||
195 | map__fprintf(pos, stderr); | ||
196 | } | ||
197 | |||
198 | pr_info("Maps in vmlinux with a different name in kallsyms:\n"); | ||
199 | |||
200 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
201 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
202 | |||
203 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | ||
204 | if (pair == NULL || pair->priv) | ||
205 | continue; | ||
206 | |||
207 | if (pair->start == pos->start) { | ||
208 | pair->priv = 1; | ||
209 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | ||
210 | pos->start, pos->end, pos->pgoff, pos->dso->name); | ||
211 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | ||
212 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | ||
213 | pair->start, pair->end, pair->pgoff); | ||
214 | pr_info(" %s\n", pair->dso->name); | ||
215 | pair->priv = 1; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | pr_info("Maps only in kallsyms:\n"); | ||
220 | |||
221 | for (nd = rb_first(&kallsyms.kmaps.maps[type]); | ||
222 | nd; nd = rb_next(nd)) { | ||
223 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
224 | |||
225 | if (!pos->priv) | ||
226 | map__fprintf(pos, stderr); | ||
227 | } | ||
228 | out: | ||
229 | return err; | ||
230 | } | ||
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8f8cd2d73b3b..5dab3ca96980 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -188,6 +188,12 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
188 | struct disasm_line *cursor = ab->selection, *target; | 188 | struct disasm_line *cursor = ab->selection, *target; |
189 | struct browser_disasm_line *btarget, *bcursor; | 189 | struct browser_disasm_line *btarget, *bcursor; |
190 | unsigned int from, to; | 190 | unsigned int from, to; |
191 | struct map_symbol *ms = ab->b.priv; | ||
192 | struct symbol *sym = ms->sym; | ||
193 | |||
194 | /* PLT symbols contain external offsets */ | ||
195 | if (strstr(sym->name, "@plt")) | ||
196 | return; | ||
191 | 197 | ||
192 | if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || | 198 | if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || |
193 | !disasm_line__has_offset(cursor)) | 199 | !disasm_line__has_offset(cursor)) |
@@ -386,9 +392,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) | |||
386 | browser->b.nr_entries = browser->nr_asm_entries; | 392 | browser->b.nr_entries = browser->nr_asm_entries; |
387 | } | 393 | } |
388 | 394 | ||
389 | static bool annotate_browser__callq(struct annotate_browser *browser, | 395 | static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, |
390 | int evidx, void (*timer)(void *arg), | 396 | struct hist_browser_timer *hbt) |
391 | void *arg, int delay_secs) | ||
392 | { | 397 | { |
393 | struct map_symbol *ms = browser->b.priv; | 398 | struct map_symbol *ms = browser->b.priv; |
394 | struct disasm_line *dl = browser->selection; | 399 | struct disasm_line *dl = browser->selection; |
@@ -418,7 +423,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, | |||
418 | } | 423 | } |
419 | 424 | ||
420 | pthread_mutex_unlock(¬es->lock); | 425 | pthread_mutex_unlock(¬es->lock); |
421 | symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs); | 426 | symbol__tui_annotate(target, ms->map, evidx, hbt); |
422 | ui_browser__show_title(&browser->b, sym->name); | 427 | ui_browser__show_title(&browser->b, sym->name); |
423 | return true; | 428 | return true; |
424 | } | 429 | } |
@@ -602,13 +607,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser | |||
602 | } | 607 | } |
603 | 608 | ||
604 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, | 609 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, |
605 | void(*timer)(void *arg), | 610 | struct hist_browser_timer *hbt) |
606 | void *arg, int delay_secs) | ||
607 | { | 611 | { |
608 | struct rb_node *nd = NULL; | 612 | struct rb_node *nd = NULL; |
609 | struct map_symbol *ms = browser->b.priv; | 613 | struct map_symbol *ms = browser->b.priv; |
610 | struct symbol *sym = ms->sym; | 614 | struct symbol *sym = ms->sym; |
611 | const char *help = "Press 'h' for help on key bindings"; | 615 | const char *help = "Press 'h' for help on key bindings"; |
616 | int delay_secs = hbt ? hbt->refresh : 0; | ||
612 | int key; | 617 | int key; |
613 | 618 | ||
614 | if (ui_browser__show(&browser->b, sym->name, help) < 0) | 619 | if (ui_browser__show(&browser->b, sym->name, help) < 0) |
@@ -639,8 +644,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
639 | 644 | ||
640 | switch (key) { | 645 | switch (key) { |
641 | case K_TIMER: | 646 | case K_TIMER: |
642 | if (timer != NULL) | 647 | if (hbt) |
643 | timer(arg); | 648 | hbt->timer(hbt->arg); |
644 | 649 | ||
645 | if (delay_secs != 0) | 650 | if (delay_secs != 0) |
646 | symbol__annotate_decay_histogram(sym, evidx); | 651 | symbol__annotate_decay_histogram(sym, evidx); |
@@ -676,8 +681,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
676 | "o Toggle disassembler output/simplified view\n" | 681 | "o Toggle disassembler output/simplified view\n" |
677 | "s Toggle source code view\n" | 682 | "s Toggle source code view\n" |
678 | "/ Search string\n" | 683 | "/ Search string\n" |
684 | "r Run available scripts\n" | ||
679 | "? Search previous string\n"); | 685 | "? Search previous string\n"); |
680 | continue; | 686 | continue; |
687 | case 'r': | ||
688 | { | ||
689 | script_browse(NULL); | ||
690 | continue; | ||
691 | } | ||
681 | case 'H': | 692 | case 'H': |
682 | nd = browser->curr_hot; | 693 | nd = browser->curr_hot; |
683 | break; | 694 | break; |
@@ -734,7 +745,7 @@ show_help: | |||
734 | goto show_sup_ins; | 745 | goto show_sup_ins; |
735 | goto out; | 746 | goto out; |
736 | } else if (!(annotate_browser__jump(browser) || | 747 | } else if (!(annotate_browser__jump(browser) || |
737 | annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) { | 748 | annotate_browser__callq(browser, evidx, hbt))) { |
738 | show_sup_ins: | 749 | show_sup_ins: |
739 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); | 750 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); |
740 | } | 751 | } |
@@ -757,16 +768,21 @@ out: | |||
757 | } | 768 | } |
758 | 769 | ||
759 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 770 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, |
760 | void(*timer)(void *arg), void *arg, int delay_secs) | 771 | struct hist_browser_timer *hbt) |
761 | { | 772 | { |
762 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, | 773 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); |
763 | timer, arg, delay_secs); | ||
764 | } | 774 | } |
765 | 775 | ||
766 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, | 776 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, |
767 | size_t size) | 777 | size_t size) |
768 | { | 778 | { |
769 | u64 offset; | 779 | u64 offset; |
780 | struct map_symbol *ms = browser->b.priv; | ||
781 | struct symbol *sym = ms->sym; | ||
782 | |||
783 | /* PLT symbols contain external offsets */ | ||
784 | if (strstr(sym->name, "@plt")) | ||
785 | return; | ||
770 | 786 | ||
771 | for (offset = 0; offset < size; ++offset) { | 787 | for (offset = 0; offset < size; ++offset) { |
772 | struct disasm_line *dl = browser->offsets[offset], *dlt; | 788 | struct disasm_line *dl = browser->offsets[offset], *dlt; |
@@ -810,8 +826,7 @@ static inline int width_jumps(int n) | |||
810 | } | 826 | } |
811 | 827 | ||
812 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 828 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
813 | void(*timer)(void *arg), void *arg, | 829 | struct hist_browser_timer *hbt) |
814 | int delay_secs) | ||
815 | { | 830 | { |
816 | struct disasm_line *pos, *n; | 831 | struct disasm_line *pos, *n; |
817 | struct annotation *notes; | 832 | struct annotation *notes; |
@@ -893,7 +908,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
893 | 908 | ||
894 | annotate_browser__update_addr_width(&browser); | 909 | annotate_browser__update_addr_width(&browser); |
895 | 910 | ||
896 | ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); | 911 | ret = annotate_browser__run(&browser, evidx, hbt); |
897 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 912 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
898 | list_del(&pos->node); | 913 | list_del(&pos->node); |
899 | disasm_line__free(pos); | 914 | disasm_line__free(pos); |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ef2f93ca7496..ccc4bd161420 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include "../../util/pstack.h" | 11 | #include "../../util/pstack.h" |
12 | #include "../../util/sort.h" | 12 | #include "../../util/sort.h" |
13 | #include "../../util/util.h" | 13 | #include "../../util/util.h" |
14 | #include "../../arch/common.h" | ||
14 | 15 | ||
15 | #include "../browser.h" | 16 | #include "../browser.h" |
16 | #include "../helpline.h" | 17 | #include "../helpline.h" |
@@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) | |||
310 | } | 311 | } |
311 | 312 | ||
312 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | 313 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, |
313 | void(*timer)(void *arg), void *arg, int delay_secs) | 314 | struct hist_browser_timer *hbt) |
314 | { | 315 | { |
315 | int key; | 316 | int key; |
316 | char title[160]; | 317 | char title[160]; |
318 | int delay_secs = hbt ? hbt->refresh : 0; | ||
317 | 319 | ||
318 | browser->b.entries = &browser->hists->entries; | 320 | browser->b.entries = &browser->hists->entries; |
319 | browser->b.nr_entries = browser->hists->nr_entries; | 321 | browser->b.nr_entries = browser->hists->nr_entries; |
@@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
330 | 332 | ||
331 | switch (key) { | 333 | switch (key) { |
332 | case K_TIMER: | 334 | case K_TIMER: |
333 | timer(arg); | 335 | hbt->timer(hbt->arg); |
334 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | 336 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); |
335 | 337 | ||
336 | if (browser->hists->stats.nr_lost_warned != | 338 | if (browser->hists->stats.nr_lost_warned != |
@@ -1127,11 +1129,17 @@ static inline void free_popup_options(char **options, int n) | |||
1127 | } | 1129 | } |
1128 | } | 1130 | } |
1129 | 1131 | ||
1132 | /* Check whether the browser is for 'top' or 'report' */ | ||
1133 | static inline bool is_report_browser(void *timer) | ||
1134 | { | ||
1135 | return timer == NULL; | ||
1136 | } | ||
1137 | |||
1130 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 1138 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
1131 | const char *helpline, const char *ev_name, | 1139 | const char *helpline, const char *ev_name, |
1132 | bool left_exits, | 1140 | bool left_exits, |
1133 | void(*timer)(void *arg), void *arg, | 1141 | struct hist_browser_timer *hbt, |
1134 | int delay_secs) | 1142 | struct perf_session_env *env) |
1135 | { | 1143 | { |
1136 | struct hists *hists = &evsel->hists; | 1144 | struct hists *hists = &evsel->hists; |
1137 | struct hist_browser *browser = hist_browser__new(hists); | 1145 | struct hist_browser *browser = hist_browser__new(hists); |
@@ -1141,6 +1149,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1141 | int nr_options = 0; | 1149 | int nr_options = 0; |
1142 | int key = -1; | 1150 | int key = -1; |
1143 | char buf[64]; | 1151 | char buf[64]; |
1152 | char script_opt[64]; | ||
1153 | int delay_secs = hbt ? hbt->refresh : 0; | ||
1144 | 1154 | ||
1145 | if (browser == NULL) | 1155 | if (browser == NULL) |
1146 | return -1; | 1156 | return -1; |
@@ -1159,10 +1169,11 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1159 | int choice = 0, | 1169 | int choice = 0, |
1160 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 1170 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
1161 | annotate_f = -2, annotate_t = -2, browse_map = -2; | 1171 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
1172 | int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2; | ||
1162 | 1173 | ||
1163 | nr_options = 0; | 1174 | nr_options = 0; |
1164 | 1175 | ||
1165 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); | 1176 | key = hist_browser__run(browser, ev_name, hbt); |
1166 | 1177 | ||
1167 | if (browser->he_selection != NULL) { | 1178 | if (browser->he_selection != NULL) { |
1168 | thread = hist_browser__selected_thread(browser); | 1179 | thread = hist_browser__selected_thread(browser); |
@@ -1211,6 +1222,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1211 | hist_browser__reset(browser); | 1222 | hist_browser__reset(browser); |
1212 | } | 1223 | } |
1213 | continue; | 1224 | continue; |
1225 | case 'r': | ||
1226 | if (is_report_browser(hbt)) | ||
1227 | goto do_scripts; | ||
1228 | continue; | ||
1214 | case K_F1: | 1229 | case K_F1: |
1215 | case 'h': | 1230 | case 'h': |
1216 | case '?': | 1231 | case '?': |
@@ -1229,6 +1244,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1229 | "E Expand all callchains\n" | 1244 | "E Expand all callchains\n" |
1230 | "d Zoom into current DSO\n" | 1245 | "d Zoom into current DSO\n" |
1231 | "t Zoom into current Thread\n" | 1246 | "t Zoom into current Thread\n" |
1247 | "r Run available scripts('perf report' only)\n" | ||
1232 | "P Print histograms to perf.hist.N\n" | 1248 | "P Print histograms to perf.hist.N\n" |
1233 | "V Verbose (DSO names in callchains, etc)\n" | 1249 | "V Verbose (DSO names in callchains, etc)\n" |
1234 | "/ Filter symbol by name"); | 1250 | "/ Filter symbol by name"); |
@@ -1317,6 +1333,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1317 | browser->selection->map != NULL && | 1333 | browser->selection->map != NULL && |
1318 | asprintf(&options[nr_options], "Browse map details") > 0) | 1334 | asprintf(&options[nr_options], "Browse map details") > 0) |
1319 | browse_map = nr_options++; | 1335 | browse_map = nr_options++; |
1336 | |||
1337 | /* perf script support */ | ||
1338 | if (browser->he_selection) { | ||
1339 | struct symbol *sym; | ||
1340 | |||
1341 | if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", | ||
1342 | browser->he_selection->thread->comm) > 0) | ||
1343 | scripts_comm = nr_options++; | ||
1344 | |||
1345 | sym = browser->he_selection->ms.sym; | ||
1346 | if (sym && sym->namelen && | ||
1347 | asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", | ||
1348 | sym->name) > 0) | ||
1349 | scripts_symbol = nr_options++; | ||
1350 | } | ||
1351 | |||
1352 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) | ||
1353 | scripts_all = nr_options++; | ||
1354 | |||
1320 | add_exit_option: | 1355 | add_exit_option: |
1321 | options[nr_options++] = (char *)"Exit"; | 1356 | options[nr_options++] = (char *)"Exit"; |
1322 | retry_popup_menu: | 1357 | retry_popup_menu: |
@@ -1334,6 +1369,9 @@ retry_popup_menu: | |||
1334 | struct hist_entry *he; | 1369 | struct hist_entry *he; |
1335 | int err; | 1370 | int err; |
1336 | do_annotate: | 1371 | do_annotate: |
1372 | if (!objdump_path && perf_session_env__lookup_objdump(env)) | ||
1373 | continue; | ||
1374 | |||
1337 | he = hist_browser__selected_entry(browser); | 1375 | he = hist_browser__selected_entry(browser); |
1338 | if (he == NULL) | 1376 | if (he == NULL) |
1339 | continue; | 1377 | continue; |
@@ -1356,8 +1394,7 @@ do_annotate: | |||
1356 | * Don't let this be freed, say, by hists__decay_entry. | 1394 | * Don't let this be freed, say, by hists__decay_entry. |
1357 | */ | 1395 | */ |
1358 | he->used = true; | 1396 | he->used = true; |
1359 | err = hist_entry__tui_annotate(he, evsel->idx, | 1397 | err = hist_entry__tui_annotate(he, evsel->idx, hbt); |
1360 | timer, arg, delay_secs); | ||
1361 | he->used = false; | 1398 | he->used = false; |
1362 | /* | 1399 | /* |
1363 | * offer option to annotate the other branch source or target | 1400 | * offer option to annotate the other branch source or target |
@@ -1411,6 +1448,20 @@ zoom_out_thread: | |||
1411 | hists__filter_by_thread(hists); | 1448 | hists__filter_by_thread(hists); |
1412 | hist_browser__reset(browser); | 1449 | hist_browser__reset(browser); |
1413 | } | 1450 | } |
1451 | /* perf scripts support */ | ||
1452 | else if (choice == scripts_all || choice == scripts_comm || | ||
1453 | choice == scripts_symbol) { | ||
1454 | do_scripts: | ||
1455 | memset(script_opt, 0, 64); | ||
1456 | |||
1457 | if (choice == scripts_comm) | ||
1458 | sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); | ||
1459 | |||
1460 | if (choice == scripts_symbol) | ||
1461 | sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); | ||
1462 | |||
1463 | script_browse(script_opt); | ||
1464 | } | ||
1414 | } | 1465 | } |
1415 | out_free_stack: | 1466 | out_free_stack: |
1416 | pstack__delete(fstack); | 1467 | pstack__delete(fstack); |
@@ -1424,6 +1475,7 @@ struct perf_evsel_menu { | |||
1424 | struct ui_browser b; | 1475 | struct ui_browser b; |
1425 | struct perf_evsel *selection; | 1476 | struct perf_evsel *selection; |
1426 | bool lost_events, lost_events_warned; | 1477 | bool lost_events, lost_events_warned; |
1478 | struct perf_session_env *env; | ||
1427 | }; | 1479 | }; |
1428 | 1480 | ||
1429 | static void perf_evsel_menu__write(struct ui_browser *browser, | 1481 | static void perf_evsel_menu__write(struct ui_browser *browser, |
@@ -1466,11 +1518,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1466 | 1518 | ||
1467 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, | 1519 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1468 | int nr_events, const char *help, | 1520 | int nr_events, const char *help, |
1469 | void(*timer)(void *arg), void *arg, int delay_secs) | 1521 | struct hist_browser_timer *hbt) |
1470 | { | 1522 | { |
1471 | struct perf_evlist *evlist = menu->b.priv; | 1523 | struct perf_evlist *evlist = menu->b.priv; |
1472 | struct perf_evsel *pos; | 1524 | struct perf_evsel *pos; |
1473 | const char *ev_name, *title = "Available samples"; | 1525 | const char *ev_name, *title = "Available samples"; |
1526 | int delay_secs = hbt ? hbt->refresh : 0; | ||
1474 | int key; | 1527 | int key; |
1475 | 1528 | ||
1476 | if (ui_browser__show(&menu->b, title, | 1529 | if (ui_browser__show(&menu->b, title, |
@@ -1482,7 +1535,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, | |||
1482 | 1535 | ||
1483 | switch (key) { | 1536 | switch (key) { |
1484 | case K_TIMER: | 1537 | case K_TIMER: |
1485 | timer(arg); | 1538 | hbt->timer(hbt->arg); |
1486 | 1539 | ||
1487 | if (!menu->lost_events_warned && menu->lost_events) { | 1540 | if (!menu->lost_events_warned && menu->lost_events) { |
1488 | ui_browser__warn_lost_events(&menu->b); | 1541 | ui_browser__warn_lost_events(&menu->b); |
@@ -1500,12 +1553,12 @@ browse_hists: | |||
1500 | * Give the calling tool a chance to populate the non | 1553 | * Give the calling tool a chance to populate the non |
1501 | * default evsel resorted hists tree. | 1554 | * default evsel resorted hists tree. |
1502 | */ | 1555 | */ |
1503 | if (timer) | 1556 | if (hbt) |
1504 | timer(arg); | 1557 | hbt->timer(hbt->arg); |
1505 | ev_name = perf_evsel__name(pos); | 1558 | ev_name = perf_evsel__name(pos); |
1506 | key = perf_evsel__hists_browse(pos, nr_events, help, | 1559 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1507 | ev_name, true, timer, | 1560 | ev_name, true, hbt, |
1508 | arg, delay_secs); | 1561 | menu->env); |
1509 | ui_browser__show_title(&menu->b, title); | 1562 | ui_browser__show_title(&menu->b, title); |
1510 | switch (key) { | 1563 | switch (key) { |
1511 | case K_TAB: | 1564 | case K_TAB: |
@@ -1553,8 +1606,8 @@ out: | |||
1553 | 1606 | ||
1554 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1607 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1555 | const char *help, | 1608 | const char *help, |
1556 | void(*timer)(void *arg), void *arg, | 1609 | struct hist_browser_timer *hbt, |
1557 | int delay_secs) | 1610 | struct perf_session_env *env) |
1558 | { | 1611 | { |
1559 | struct perf_evsel *pos; | 1612 | struct perf_evsel *pos; |
1560 | struct perf_evsel_menu menu = { | 1613 | struct perf_evsel_menu menu = { |
@@ -1566,6 +1619,7 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1566 | .nr_entries = evlist->nr_entries, | 1619 | .nr_entries = evlist->nr_entries, |
1567 | .priv = evlist, | 1620 | .priv = evlist, |
1568 | }, | 1621 | }, |
1622 | .env = env, | ||
1569 | }; | 1623 | }; |
1570 | 1624 | ||
1571 | ui_helpline__push("Press ESC to exit"); | 1625 | ui_helpline__push("Press ESC to exit"); |
@@ -1578,23 +1632,20 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1578 | menu.b.width = line_len; | 1632 | menu.b.width = line_len; |
1579 | } | 1633 | } |
1580 | 1634 | ||
1581 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, | 1635 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); |
1582 | arg, delay_secs); | ||
1583 | } | 1636 | } |
1584 | 1637 | ||
1585 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 1638 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1586 | void(*timer)(void *arg), void *arg, | 1639 | struct hist_browser_timer *hbt, |
1587 | int delay_secs) | 1640 | struct perf_session_env *env) |
1588 | { | 1641 | { |
1589 | if (evlist->nr_entries == 1) { | 1642 | if (evlist->nr_entries == 1) { |
1590 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1643 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1591 | struct perf_evsel, node); | 1644 | struct perf_evsel, node); |
1592 | const char *ev_name = perf_evsel__name(first); | 1645 | const char *ev_name = perf_evsel__name(first); |
1593 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, | 1646 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
1594 | ev_name, false, timer, arg, | 1647 | ev_name, false, hbt, env); |
1595 | delay_secs); | ||
1596 | } | 1648 | } |
1597 | 1649 | ||
1598 | return __perf_evlist__tui_browse_hists(evlist, help, | 1650 | return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); |
1599 | timer, arg, delay_secs); | ||
1600 | } | 1651 | } |
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c new file mode 100644 index 000000000000..cbbd44b0d93e --- /dev/null +++ b/tools/perf/ui/browsers/scripts.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include <elf.h> | ||
2 | #include <newt.h> | ||
3 | #include <inttypes.h> | ||
4 | #include <sys/ttydefaults.h> | ||
5 | #include <string.h> | ||
6 | #include "../../util/sort.h" | ||
7 | #include "../../util/util.h" | ||
8 | #include "../../util/hist.h" | ||
9 | #include "../../util/debug.h" | ||
10 | #include "../../util/symbol.h" | ||
11 | #include "../browser.h" | ||
12 | #include "../helpline.h" | ||
13 | #include "../libslang.h" | ||
14 | |||
15 | /* 2048 lines should be enough for a script output */ | ||
16 | #define MAX_LINES 2048 | ||
17 | |||
18 | /* 160 bytes for one output line */ | ||
19 | #define AVERAGE_LINE_LEN 160 | ||
20 | |||
21 | struct script_line { | ||
22 | struct list_head node; | ||
23 | char line[AVERAGE_LINE_LEN]; | ||
24 | }; | ||
25 | |||
26 | struct perf_script_browser { | ||
27 | struct ui_browser b; | ||
28 | struct list_head entries; | ||
29 | const char *script_name; | ||
30 | int nr_lines; | ||
31 | }; | ||
32 | |||
33 | #define SCRIPT_NAMELEN 128 | ||
34 | #define SCRIPT_MAX_NO 64 | ||
35 | /* | ||
36 | * Usually the full path for a script is: | ||
37 | * /home/username/libexec/perf-core/scripts/python/xxx.py | ||
38 | * /home/username/libexec/perf-core/scripts/perl/xxx.pl | ||
39 | * So 256 should be long enough to contain the full path. | ||
40 | */ | ||
41 | #define SCRIPT_FULLPATH_LEN 256 | ||
42 | |||
43 | /* | ||
44 | * When success, will copy the full path of the selected script | ||
45 | * into the buffer pointed by script_name, and return 0. | ||
46 | * Return -1 on failure. | ||
47 | */ | ||
48 | static int list_scripts(char *script_name) | ||
49 | { | ||
50 | char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; | ||
51 | int i, num, choice, ret = -1; | ||
52 | |||
53 | /* Preset the script name to SCRIPT_NAMELEN */ | ||
54 | buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); | ||
55 | if (!buf) | ||
56 | return ret; | ||
57 | |||
58 | for (i = 0; i < SCRIPT_MAX_NO; i++) { | ||
59 | names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); | ||
60 | paths[i] = names[i] + SCRIPT_NAMELEN; | ||
61 | } | ||
62 | |||
63 | num = find_scripts(names, paths); | ||
64 | if (num > 0) { | ||
65 | choice = ui__popup_menu(num, names); | ||
66 | if (choice < num && choice >= 0) { | ||
67 | strcpy(script_name, paths[choice]); | ||
68 | ret = 0; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | free(buf); | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static void script_browser__write(struct ui_browser *browser, | ||
77 | void *entry, int row) | ||
78 | { | ||
79 | struct script_line *sline = list_entry(entry, struct script_line, node); | ||
80 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
81 | |||
82 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | ||
83 | HE_COLORSET_NORMAL); | ||
84 | |||
85 | slsmg_write_nstring(sline->line, browser->width); | ||
86 | } | ||
87 | |||
88 | static int script_browser__run(struct perf_script_browser *self) | ||
89 | { | ||
90 | int key; | ||
91 | |||
92 | if (ui_browser__show(&self->b, self->script_name, | ||
93 | "Press <- or ESC to exit") < 0) | ||
94 | return -1; | ||
95 | |||
96 | while (1) { | ||
97 | key = ui_browser__run(&self->b, 0); | ||
98 | |||
99 | /* We can add some special key handling here if needed */ | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | ui_browser__hide(&self->b); | ||
104 | return key; | ||
105 | } | ||
106 | |||
107 | |||
108 | int script_browse(const char *script_opt) | ||
109 | { | ||
110 | char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; | ||
111 | char *line = NULL; | ||
112 | size_t len = 0; | ||
113 | ssize_t retlen; | ||
114 | int ret = -1, nr_entries = 0; | ||
115 | FILE *fp; | ||
116 | void *buf; | ||
117 | struct script_line *sline; | ||
118 | |||
119 | struct perf_script_browser script = { | ||
120 | .b = { | ||
121 | .refresh = ui_browser__list_head_refresh, | ||
122 | .seek = ui_browser__list_head_seek, | ||
123 | .write = script_browser__write, | ||
124 | }, | ||
125 | .script_name = script_name, | ||
126 | }; | ||
127 | |||
128 | INIT_LIST_HEAD(&script.entries); | ||
129 | |||
130 | /* Save each line of the output in one struct script_line object. */ | ||
131 | buf = zalloc((sizeof(*sline)) * MAX_LINES); | ||
132 | if (!buf) | ||
133 | return -1; | ||
134 | sline = buf; | ||
135 | |||
136 | memset(script_name, 0, SCRIPT_FULLPATH_LEN); | ||
137 | if (list_scripts(script_name)) | ||
138 | goto exit; | ||
139 | |||
140 | sprintf(cmd, "perf script -s %s ", script_name); | ||
141 | |||
142 | if (script_opt) | ||
143 | strcat(cmd, script_opt); | ||
144 | |||
145 | if (input_name) { | ||
146 | strcat(cmd, " -i "); | ||
147 | strcat(cmd, input_name); | ||
148 | } | ||
149 | |||
150 | strcat(cmd, " 2>&1"); | ||
151 | |||
152 | fp = popen(cmd, "r"); | ||
153 | if (!fp) | ||
154 | goto exit; | ||
155 | |||
156 | while ((retlen = getline(&line, &len, fp)) != -1) { | ||
157 | strncpy(sline->line, line, AVERAGE_LINE_LEN); | ||
158 | |||
159 | /* If one output line is very large, just cut it short */ | ||
160 | if (retlen >= AVERAGE_LINE_LEN) { | ||
161 | sline->line[AVERAGE_LINE_LEN - 1] = '\0'; | ||
162 | sline->line[AVERAGE_LINE_LEN - 2] = '\n'; | ||
163 | } | ||
164 | list_add_tail(&sline->node, &script.entries); | ||
165 | |||
166 | if (script.b.width < retlen) | ||
167 | script.b.width = retlen; | ||
168 | |||
169 | if (nr_entries++ >= MAX_LINES - 1) | ||
170 | break; | ||
171 | sline++; | ||
172 | } | ||
173 | |||
174 | if (script.b.width > AVERAGE_LINE_LEN) | ||
175 | script.b.width = AVERAGE_LINE_LEN; | ||
176 | |||
177 | if (line) | ||
178 | free(line); | ||
179 | pclose(fp); | ||
180 | |||
181 | script.nr_lines = nr_entries; | ||
182 | script.b.nr_entries = nr_entries; | ||
183 | script.b.entries = &script.entries; | ||
184 | |||
185 | ret = script_browser__run(&script); | ||
186 | exit: | ||
187 | free(buf); | ||
188 | return ret; | ||
189 | } | ||
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 4125c6284114..253b6219a39e 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c | |||
@@ -237,9 +237,7 @@ static GtkWidget *perf_gtk__setup_statusbar(void) | |||
237 | 237 | ||
238 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | 238 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, |
239 | const char *help, | 239 | const char *help, |
240 | void (*timer) (void *arg)__maybe_unused, | 240 | struct hist_browser_timer *hbt __maybe_unused) |
241 | void *arg __maybe_unused, | ||
242 | int delay_secs __maybe_unused) | ||
243 | { | 241 | { |
244 | struct perf_evsel *pos; | 242 | struct perf_evsel *pos; |
245 | GtkWidget *vbox; | 243 | GtkWidget *vbox; |
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 687af0bba187..856320e2cc05 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h | |||
@@ -30,6 +30,7 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window); | |||
30 | int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); | 30 | int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); |
31 | 31 | ||
32 | void perf_gtk__init_helpline(void); | 32 | void perf_gtk__init_helpline(void); |
33 | void perf_gtk__init_progress(void); | ||
33 | void perf_gtk__init_hpp(void); | 34 | void perf_gtk__init_hpp(void); |
34 | 35 | ||
35 | #ifndef HAVE_GTK_INFO_BAR | 36 | #ifndef HAVE_GTK_INFO_BAR |
diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c new file mode 100644 index 000000000000..482bcf3df9b7 --- /dev/null +++ b/tools/perf/ui/gtk/progress.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <inttypes.h> | ||
2 | |||
3 | #include "gtk.h" | ||
4 | #include "../progress.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | static GtkWidget *dialog; | ||
8 | static GtkWidget *progress; | ||
9 | |||
10 | static void gtk_progress_update(u64 curr, u64 total, const char *title) | ||
11 | { | ||
12 | double fraction = total ? 1.0 * curr / total : 0.0; | ||
13 | char buf[1024]; | ||
14 | |||
15 | if (dialog == NULL) { | ||
16 | GtkWidget *vbox = gtk_vbox_new(TRUE, 5); | ||
17 | GtkWidget *label = gtk_label_new(title); | ||
18 | |||
19 | dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
20 | progress = gtk_progress_bar_new(); | ||
21 | |||
22 | gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3); | ||
23 | gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3); | ||
24 | |||
25 | gtk_container_add(GTK_CONTAINER(dialog), vbox); | ||
26 | |||
27 | gtk_window_set_title(GTK_WINDOW(dialog), "perf"); | ||
28 | gtk_window_resize(GTK_WINDOW(dialog), 300, 80); | ||
29 | gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); | ||
30 | |||
31 | gtk_widget_show_all(dialog); | ||
32 | } | ||
33 | |||
34 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); | ||
35 | snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total); | ||
36 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf); | ||
37 | |||
38 | /* we didn't call gtk_main yet, so do it manually */ | ||
39 | while (gtk_events_pending()) | ||
40 | gtk_main_iteration(); | ||
41 | } | ||
42 | |||
43 | static void gtk_progress_finish(void) | ||
44 | { | ||
45 | /* this will also destroy all of its children */ | ||
46 | gtk_widget_destroy(dialog); | ||
47 | |||
48 | dialog = NULL; | ||
49 | } | ||
50 | |||
51 | static struct ui_progress gtk_progress_fns = { | ||
52 | .update = gtk_progress_update, | ||
53 | .finish = gtk_progress_finish, | ||
54 | }; | ||
55 | |||
56 | void perf_gtk__init_progress(void) | ||
57 | { | ||
58 | progress_fns = >k_progress_fns; | ||
59 | } | ||
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 3c4c6ef78283..6c2dd2e423f3 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c | |||
@@ -8,7 +8,9 @@ int perf_gtk__init(void) | |||
8 | { | 8 | { |
9 | perf_error__register(&perf_gtk_eops); | 9 | perf_error__register(&perf_gtk_eops); |
10 | perf_gtk__init_helpline(); | 10 | perf_gtk__init_helpline(); |
11 | perf_gtk__init_progress(); | ||
11 | perf_gtk__init_hpp(); | 12 | perf_gtk__init_hpp(); |
13 | |||
12 | return gtk_init_check(NULL, NULL) ? 0 : -1; | 14 | return gtk_init_check(NULL, NULL) ? 0 : -1; |
13 | } | 15 | } |
14 | 16 | ||
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index ccb046aac98b..c06942a41c78 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c | |||
@@ -111,14 +111,3 @@ struct perf_error_ops perf_gtk_eops = { | |||
111 | .warning = perf_gtk__warning_statusbar, | 111 | .warning = perf_gtk__warning_statusbar, |
112 | #endif | 112 | #endif |
113 | }; | 113 | }; |
114 | |||
115 | /* | ||
116 | * FIXME: Functions below should be implemented properly. | ||
117 | * For now, just add stubs for NO_NEWT=1 build. | ||
118 | */ | ||
119 | #ifndef NEWT_SUPPORT | ||
120 | void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, | ||
121 | const char *title __maybe_unused) | ||
122 | { | ||
123 | } | ||
124 | #endif | ||
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index f5a1e4f65263..aa84130024d5 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -161,7 +161,7 @@ static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) | |||
161 | 161 | ||
162 | static double baseline_percent(struct hist_entry *he) | 162 | static double baseline_percent(struct hist_entry *he) |
163 | { | 163 | { |
164 | struct hist_entry *pair = he->pair; | 164 | struct hist_entry *pair = hist_entry__next_pair(he); |
165 | struct hists *pair_hists = pair ? pair->hists : NULL; | 165 | struct hists *pair_hists = pair ? pair->hists : NULL; |
166 | double percent = 0.0; | 166 | double percent = 0.0; |
167 | 167 | ||
@@ -179,7 +179,10 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
179 | { | 179 | { |
180 | double percent = baseline_percent(he); | 180 | double percent = baseline_percent(he); |
181 | 181 | ||
182 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | 182 | if (hist_entry__has_pairs(he)) |
183 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | ||
184 | else | ||
185 | return scnprintf(hpp->buf, hpp->size, " "); | ||
183 | } | 186 | } |
184 | 187 | ||
185 | static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | 188 | static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) |
@@ -187,7 +190,10 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
187 | double percent = baseline_percent(he); | 190 | double percent = baseline_percent(he); |
188 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; | 191 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; |
189 | 192 | ||
190 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 193 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) |
194 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
195 | else | ||
196 | return scnprintf(hpp->buf, hpp->size, " "); | ||
191 | } | 197 | } |
192 | 198 | ||
193 | static int hpp__header_samples(struct perf_hpp *hpp) | 199 | static int hpp__header_samples(struct perf_hpp *hpp) |
@@ -228,6 +234,26 @@ static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he) | |||
228 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); | 234 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); |
229 | } | 235 | } |
230 | 236 | ||
237 | static int hpp__header_period_baseline(struct perf_hpp *hpp) | ||
238 | { | ||
239 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; | ||
240 | |||
241 | return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); | ||
242 | } | ||
243 | |||
244 | static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused) | ||
245 | { | ||
246 | return 12; | ||
247 | } | ||
248 | |||
249 | static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he) | ||
250 | { | ||
251 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
252 | u64 period = pair ? pair->stat.period : 0; | ||
253 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; | ||
254 | |||
255 | return scnprintf(hpp->buf, hpp->size, fmt, period); | ||
256 | } | ||
231 | static int hpp__header_delta(struct perf_hpp *hpp) | 257 | static int hpp__header_delta(struct perf_hpp *hpp) |
232 | { | 258 | { |
233 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | 259 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; |
@@ -242,30 +268,79 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) | |||
242 | 268 | ||
243 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) | 269 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) |
244 | { | 270 | { |
245 | struct hist_entry *pair = he->pair; | ||
246 | struct hists *pair_hists = pair ? pair->hists : NULL; | ||
247 | struct hists *hists = he->hists; | ||
248 | u64 old_total, new_total; | ||
249 | double old_percent = 0, new_percent = 0; | ||
250 | double diff; | ||
251 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; | 271 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; |
252 | char buf[32] = " "; | 272 | char buf[32] = " "; |
273 | double diff; | ||
253 | 274 | ||
254 | old_total = pair_hists ? pair_hists->stats.total_period : 0; | 275 | if (he->diff.computed) |
255 | if (old_total > 0 && pair) | 276 | diff = he->diff.period_ratio_delta; |
256 | old_percent = 100.0 * pair->stat.period / old_total; | 277 | else |
257 | 278 | diff = perf_diff__compute_delta(he); | |
258 | new_total = hists->stats.total_period; | ||
259 | if (new_total > 0) | ||
260 | new_percent = 100.0 * he->stat.period / new_total; | ||
261 | 279 | ||
262 | diff = new_percent - old_percent; | ||
263 | if (fabs(diff) >= 0.01) | 280 | if (fabs(diff) >= 0.01) |
264 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); | 281 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); |
265 | 282 | ||
266 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 283 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
267 | } | 284 | } |
268 | 285 | ||
286 | static int hpp__header_ratio(struct perf_hpp *hpp) | ||
287 | { | ||
288 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
289 | |||
290 | return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); | ||
291 | } | ||
292 | |||
293 | static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) | ||
294 | { | ||
295 | return 14; | ||
296 | } | ||
297 | |||
298 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) | ||
299 | { | ||
300 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
301 | char buf[32] = " "; | ||
302 | double ratio; | ||
303 | |||
304 | if (he->diff.computed) | ||
305 | ratio = he->diff.period_ratio; | ||
306 | else | ||
307 | ratio = perf_diff__compute_ratio(he); | ||
308 | |||
309 | if (ratio > 0.0) | ||
310 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); | ||
311 | |||
312 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
313 | } | ||
314 | |||
315 | static int hpp__header_wdiff(struct perf_hpp *hpp) | ||
316 | { | ||
317 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
318 | |||
319 | return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); | ||
320 | } | ||
321 | |||
322 | static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) | ||
323 | { | ||
324 | return 14; | ||
325 | } | ||
326 | |||
327 | static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) | ||
328 | { | ||
329 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
330 | char buf[32] = " "; | ||
331 | s64 wdiff; | ||
332 | |||
333 | if (he->diff.computed) | ||
334 | wdiff = he->diff.wdiff; | ||
335 | else | ||
336 | wdiff = perf_diff__compute_wdiff(he); | ||
337 | |||
338 | if (wdiff != 0) | ||
339 | scnprintf(buf, sizeof(buf), "%14ld", wdiff); | ||
340 | |||
341 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
342 | } | ||
343 | |||
269 | static int hpp__header_displ(struct perf_hpp *hpp) | 344 | static int hpp__header_displ(struct perf_hpp *hpp) |
270 | { | 345 | { |
271 | return scnprintf(hpp->buf, hpp->size, "Displ."); | 346 | return scnprintf(hpp->buf, hpp->size, "Displ."); |
@@ -279,7 +354,7 @@ static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused) | |||
279 | static int hpp__entry_displ(struct perf_hpp *hpp, | 354 | static int hpp__entry_displ(struct perf_hpp *hpp, |
280 | struct hist_entry *he) | 355 | struct hist_entry *he) |
281 | { | 356 | { |
282 | struct hist_entry *pair = he->pair; | 357 | struct hist_entry *pair = hist_entry__next_pair(he); |
283 | long displacement = pair ? pair->position - he->position : 0; | 358 | long displacement = pair ? pair->position - he->position : 0; |
284 | const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; | 359 | const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; |
285 | char buf[32] = " "; | 360 | char buf[32] = " "; |
@@ -290,6 +365,27 @@ static int hpp__entry_displ(struct perf_hpp *hpp, | |||
290 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 365 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
291 | } | 366 | } |
292 | 367 | ||
368 | static int hpp__header_formula(struct perf_hpp *hpp) | ||
369 | { | ||
370 | const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; | ||
371 | |||
372 | return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); | ||
373 | } | ||
374 | |||
375 | static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) | ||
376 | { | ||
377 | return 70; | ||
378 | } | ||
379 | |||
380 | static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) | ||
381 | { | ||
382 | const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; | ||
383 | char buf[96] = " "; | ||
384 | |||
385 | perf_diff__formula(buf, sizeof(buf), he); | ||
386 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
387 | } | ||
388 | |||
293 | #define HPP__COLOR_PRINT_FNS(_name) \ | 389 | #define HPP__COLOR_PRINT_FNS(_name) \ |
294 | .header = hpp__header_ ## _name, \ | 390 | .header = hpp__header_ ## _name, \ |
295 | .width = hpp__width_ ## _name, \ | 391 | .width = hpp__width_ ## _name, \ |
@@ -310,8 +406,12 @@ struct perf_hpp_fmt perf_hpp__format[] = { | |||
310 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) }, | 406 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) }, |
311 | { .cond = false, HPP__PRINT_FNS(samples) }, | 407 | { .cond = false, HPP__PRINT_FNS(samples) }, |
312 | { .cond = false, HPP__PRINT_FNS(period) }, | 408 | { .cond = false, HPP__PRINT_FNS(period) }, |
409 | { .cond = false, HPP__PRINT_FNS(period_baseline) }, | ||
313 | { .cond = false, HPP__PRINT_FNS(delta) }, | 410 | { .cond = false, HPP__PRINT_FNS(delta) }, |
314 | { .cond = false, HPP__PRINT_FNS(displ) } | 411 | { .cond = false, HPP__PRINT_FNS(ratio) }, |
412 | { .cond = false, HPP__PRINT_FNS(wdiff) }, | ||
413 | { .cond = false, HPP__PRINT_FNS(displ) }, | ||
414 | { .cond = false, HPP__PRINT_FNS(formula) } | ||
315 | }; | 415 | }; |
316 | 416 | ||
317 | #undef HPP__COLOR_PRINT_FNS | 417 | #undef HPP__COLOR_PRINT_FNS |
diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index 13aa64e50e11..3ec695607a4d 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c | |||
@@ -1,32 +1,26 @@ | |||
1 | #include "../cache.h" | 1 | #include "../cache.h" |
2 | #include "progress.h" | 2 | #include "progress.h" |
3 | #include "libslang.h" | ||
4 | #include "ui.h" | ||
5 | #include "browser.h" | ||
6 | 3 | ||
7 | void ui_progress__update(u64 curr, u64 total, const char *title) | 4 | static void nop_progress_update(u64 curr __maybe_unused, |
5 | u64 total __maybe_unused, | ||
6 | const char *title __maybe_unused) | ||
8 | { | 7 | { |
9 | int bar, y; | 8 | } |
10 | /* | ||
11 | * FIXME: We should have a per UI backend way of showing progress, | ||
12 | * stdio will just show a percentage as NN%, etc. | ||
13 | */ | ||
14 | if (use_browser <= 0) | ||
15 | return; | ||
16 | 9 | ||
17 | if (total == 0) | 10 | static struct ui_progress default_progress_fns = |
18 | return; | 11 | { |
12 | .update = nop_progress_update, | ||
13 | }; | ||
19 | 14 | ||
20 | ui__refresh_dimensions(true); | 15 | struct ui_progress *progress_fns = &default_progress_fns; |
21 | pthread_mutex_lock(&ui__lock); | 16 | |
22 | y = SLtt_Screen_Rows / 2 - 2; | 17 | void ui_progress__update(u64 curr, u64 total, const char *title) |
23 | SLsmg_set_color(0); | 18 | { |
24 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); | 19 | return progress_fns->update(curr, total, title); |
25 | SLsmg_gotorc(y++, 1); | 20 | } |
26 | SLsmg_write_string((char *)title); | 21 | |
27 | SLsmg_set_color(HE_COLORSET_SELECTED); | 22 | void ui_progress__finish(void) |
28 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | 23 | { |
29 | SLsmg_fill_region(y, 1, 1, bar, ' '); | 24 | if (progress_fns->finish) |
30 | SLsmg_refresh(); | 25 | progress_fns->finish(); |
31 | pthread_mutex_unlock(&ui__lock); | ||
32 | } | 26 | } |
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index d9c205b59aa1..257cc224f9cf 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h | |||
@@ -3,6 +3,16 @@ | |||
3 | 3 | ||
4 | #include <../types.h> | 4 | #include <../types.h> |
5 | 5 | ||
6 | struct ui_progress { | ||
7 | void (*update)(u64, u64, const char *); | ||
8 | void (*finish)(void); | ||
9 | }; | ||
10 | |||
11 | extern struct ui_progress *progress_fns; | ||
12 | |||
13 | void ui_progress__init(void); | ||
14 | |||
6 | void ui_progress__update(u64 curr, u64 total, const char *title); | 15 | void ui_progress__update(u64 curr, u64 total, const char *title); |
16 | void ui_progress__finish(void); | ||
7 | 17 | ||
8 | #endif | 18 | #endif |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index fbd4e32d0743..f0ee204f99bb 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -342,7 +342,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
342 | const char *sep = symbol_conf.field_sep; | 342 | const char *sep = symbol_conf.field_sep; |
343 | const char *col_width = symbol_conf.col_width_list_str; | 343 | const char *col_width = symbol_conf.col_width_list_str; |
344 | int idx, nr_rows = 0; | 344 | int idx, nr_rows = 0; |
345 | char bf[64]; | 345 | char bf[96]; |
346 | struct perf_hpp dummy_hpp = { | 346 | struct perf_hpp dummy_hpp = { |
347 | .buf = bf, | 347 | .buf = bf, |
348 | .size = sizeof(bf), | 348 | .size = sizeof(bf), |
diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c new file mode 100644 index 000000000000..6c2184d53cbf --- /dev/null +++ b/tools/perf/ui/tui/progress.c | |||
@@ -0,0 +1,42 @@ | |||
1 | #include "../cache.h" | ||
2 | #include "../progress.h" | ||
3 | #include "../libslang.h" | ||
4 | #include "../ui.h" | ||
5 | #include "../browser.h" | ||
6 | |||
7 | static void tui_progress__update(u64 curr, u64 total, const char *title) | ||
8 | { | ||
9 | int bar, y; | ||
10 | /* | ||
11 | * FIXME: We should have a per UI backend way of showing progress, | ||
12 | * stdio will just show a percentage as NN%, etc. | ||
13 | */ | ||
14 | if (use_browser <= 0) | ||
15 | return; | ||
16 | |||
17 | if (total == 0) | ||
18 | return; | ||
19 | |||
20 | ui__refresh_dimensions(true); | ||
21 | pthread_mutex_lock(&ui__lock); | ||
22 | y = SLtt_Screen_Rows / 2 - 2; | ||
23 | SLsmg_set_color(0); | ||
24 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); | ||
25 | SLsmg_gotorc(y++, 1); | ||
26 | SLsmg_write_string((char *)title); | ||
27 | SLsmg_set_color(HE_COLORSET_SELECTED); | ||
28 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | ||
29 | SLsmg_fill_region(y, 1, 1, bar, ' '); | ||
30 | SLsmg_refresh(); | ||
31 | pthread_mutex_unlock(&ui__lock); | ||
32 | } | ||
33 | |||
34 | static struct ui_progress tui_progress_fns = | ||
35 | { | ||
36 | .update = tui_progress__update, | ||
37 | }; | ||
38 | |||
39 | void ui_progress__init(void) | ||
40 | { | ||
41 | progress_fns = &tui_progress_fns; | ||
42 | } | ||
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 60debb81537a..81efa192e86c 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -118,6 +118,7 @@ int ui__init(void) | |||
118 | newtSetSuspendCallback(newt_suspend, NULL); | 118 | newtSetSuspendCallback(newt_suspend, NULL); |
119 | ui_helpline__init(); | 119 | ui_helpline__init(); |
120 | ui_browser__init(); | 120 | ui_browser__init(); |
121 | ui_progress__init(); | ||
121 | 122 | ||
122 | signal(SIGSEGV, ui__signal); | 123 | signal(SIGSEGV, ui__signal); |
123 | signal(SIGFPE, ui__signal); | 124 | signal(SIGFPE, ui__signal); |
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 7b67045479f6..d86359c99907 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h | |||
@@ -3,9 +3,37 @@ | |||
3 | 3 | ||
4 | #include <pthread.h> | 4 | #include <pthread.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <linux/compiler.h> | ||
6 | 7 | ||
7 | extern pthread_mutex_t ui__lock; | 8 | extern pthread_mutex_t ui__lock; |
8 | 9 | ||
10 | extern int use_browser; | ||
11 | |||
12 | void setup_browser(bool fallback_to_pager); | ||
13 | void exit_browser(bool wait_for_ok); | ||
14 | |||
15 | #ifdef NEWT_SUPPORT | ||
16 | int ui__init(void); | ||
17 | void ui__exit(bool wait_for_ok); | ||
18 | #else | ||
19 | static inline int ui__init(void) | ||
20 | { | ||
21 | return -1; | ||
22 | } | ||
23 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} | ||
24 | #endif | ||
25 | |||
26 | #ifdef GTK2_SUPPORT | ||
27 | int perf_gtk__init(void); | ||
28 | void perf_gtk__exit(bool wait_for_ok); | ||
29 | #else | ||
30 | static inline int perf_gtk__init(void) | ||
31 | { | ||
32 | return -1; | ||
33 | } | ||
34 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} | ||
35 | #endif | ||
36 | |||
9 | void ui__refresh_dimensions(bool force); | 37 | void ui__refresh_dimensions(bool force); |
10 | 38 | ||
11 | #endif /* _PERF_UI_H_ */ | 39 | #endif /* _PERF_UI_H_ */ |
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 95264f304179..6aa34e5afdcf 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -9,18 +9,14 @@ GVF=${OUTPUT}PERF-VERSION-FILE | |||
9 | LF=' | 9 | LF=' |
10 | ' | 10 | ' |
11 | 11 | ||
12 | # | ||
12 | # First check if there is a .git to get the version from git describe | 13 | # First check if there is a .git to get the version from git describe |
13 | # otherwise try to get the version from the kernel makefile | 14 | # otherwise try to get the version from the kernel Makefile |
15 | # | ||
14 | if test -d ../../.git -o -f ../../.git && | 16 | if test -d ../../.git -o -f ../../.git && |
15 | VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) && | 17 | VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*") |
16 | case "$VN" in | ||
17 | *$LF*) (exit 1) ;; | ||
18 | v[0-9]*) | ||
19 | git update-index -q --refresh | ||
20 | test -z "$(git diff-index --name-only HEAD --)" || | ||
21 | VN="$VN-dirty" ;; | ||
22 | esac | ||
23 | then | 18 | then |
19 | VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD)) | ||
24 | VN=$(echo "$VN" | sed -e 's/-/./g'); | 20 | VN=$(echo "$VN" | sed -e 's/-/./g'); |
25 | else | 21 | else |
26 | VN=$(MAKEFLAGS= make -sC ../.. kernelversion) | 22 | VN=$(MAKEFLAGS= make -sC ../.. kernelversion) |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f0a910371377..07aaeea60000 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | #include <linux/bitops.h> | ||
18 | 19 | ||
19 | const char *disassembler_style; | 20 | const char *disassembler_style; |
20 | const char *objdump_path; | 21 | const char *objdump_path; |
@@ -170,15 +171,15 @@ static int lock__parse(struct ins_operands *ops) | |||
170 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | 171 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) |
171 | goto out_free_ops; | 172 | goto out_free_ops; |
172 | 173 | ||
173 | ops->locked.ins = ins__find(name); | 174 | ops->locked.ins = ins__find(name); |
174 | if (ops->locked.ins == NULL) | 175 | if (ops->locked.ins == NULL) |
175 | goto out_free_ops; | 176 | goto out_free_ops; |
176 | 177 | ||
177 | if (!ops->locked.ins->ops) | 178 | if (!ops->locked.ins->ops) |
178 | return 0; | 179 | return 0; |
179 | 180 | ||
180 | if (ops->locked.ins->ops->parse) | 181 | if (ops->locked.ins->ops->parse) |
181 | ops->locked.ins->ops->parse(ops->locked.ops); | 182 | ops->locked.ins->ops->parse(ops->locked.ops); |
182 | 183 | ||
183 | return 0; | 184 | return 0; |
184 | 185 | ||
@@ -400,6 +401,8 @@ static struct ins instructions[] = { | |||
400 | { .name = "testb", .ops = &mov_ops, }, | 401 | { .name = "testb", .ops = &mov_ops, }, |
401 | { .name = "testl", .ops = &mov_ops, }, | 402 | { .name = "testl", .ops = &mov_ops, }, |
402 | { .name = "xadd", .ops = &mov_ops, }, | 403 | { .name = "xadd", .ops = &mov_ops, }, |
404 | { .name = "xbeginl", .ops = &jump_ops, }, | ||
405 | { .name = "xbeginq", .ops = &jump_ops, }, | ||
403 | }; | 406 | }; |
404 | 407 | ||
405 | static int ins__cmp(const void *name, const void *insp) | 408 | static int ins__cmp(const void *name, const void *insp) |
@@ -855,21 +858,68 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
855 | struct source_line *iter; | 858 | struct source_line *iter; |
856 | struct rb_node **p = &root->rb_node; | 859 | struct rb_node **p = &root->rb_node; |
857 | struct rb_node *parent = NULL; | 860 | struct rb_node *parent = NULL; |
861 | int ret; | ||
858 | 862 | ||
859 | while (*p != NULL) { | 863 | while (*p != NULL) { |
860 | parent = *p; | 864 | parent = *p; |
861 | iter = rb_entry(parent, struct source_line, node); | 865 | iter = rb_entry(parent, struct source_line, node); |
862 | 866 | ||
863 | if (src_line->percent > iter->percent) | 867 | ret = strcmp(iter->path, src_line->path); |
868 | if (ret == 0) { | ||
869 | iter->percent_sum += src_line->percent; | ||
870 | return; | ||
871 | } | ||
872 | |||
873 | if (ret < 0) | ||
864 | p = &(*p)->rb_left; | 874 | p = &(*p)->rb_left; |
865 | else | 875 | else |
866 | p = &(*p)->rb_right; | 876 | p = &(*p)->rb_right; |
867 | } | 877 | } |
868 | 878 | ||
879 | src_line->percent_sum = src_line->percent; | ||
880 | |||
869 | rb_link_node(&src_line->node, parent, p); | 881 | rb_link_node(&src_line->node, parent, p); |
870 | rb_insert_color(&src_line->node, root); | 882 | rb_insert_color(&src_line->node, root); |
871 | } | 883 | } |
872 | 884 | ||
885 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) | ||
886 | { | ||
887 | struct source_line *iter; | ||
888 | struct rb_node **p = &root->rb_node; | ||
889 | struct rb_node *parent = NULL; | ||
890 | |||
891 | while (*p != NULL) { | ||
892 | parent = *p; | ||
893 | iter = rb_entry(parent, struct source_line, node); | ||
894 | |||
895 | if (src_line->percent_sum > iter->percent_sum) | ||
896 | p = &(*p)->rb_left; | ||
897 | else | ||
898 | p = &(*p)->rb_right; | ||
899 | } | ||
900 | |||
901 | rb_link_node(&src_line->node, parent, p); | ||
902 | rb_insert_color(&src_line->node, root); | ||
903 | } | ||
904 | |||
905 | static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) | ||
906 | { | ||
907 | struct source_line *src_line; | ||
908 | struct rb_node *node; | ||
909 | |||
910 | node = rb_first(src_root); | ||
911 | while (node) { | ||
912 | struct rb_node *next; | ||
913 | |||
914 | src_line = rb_entry(node, struct source_line, node); | ||
915 | next = rb_next(node); | ||
916 | rb_erase(node, src_root); | ||
917 | |||
918 | __resort_source_line(dest_root, src_line); | ||
919 | node = next; | ||
920 | } | ||
921 | } | ||
922 | |||
873 | static void symbol__free_source_line(struct symbol *sym, int len) | 923 | static void symbol__free_source_line(struct symbol *sym, int len) |
874 | { | 924 | { |
875 | struct annotation *notes = symbol__annotation(sym); | 925 | struct annotation *notes = symbol__annotation(sym); |
@@ -894,6 +944,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
894 | struct source_line *src_line; | 944 | struct source_line *src_line; |
895 | struct annotation *notes = symbol__annotation(sym); | 945 | struct annotation *notes = symbol__annotation(sym); |
896 | struct sym_hist *h = annotation__histogram(notes, evidx); | 946 | struct sym_hist *h = annotation__histogram(notes, evidx); |
947 | struct rb_root tmp_root = RB_ROOT; | ||
897 | 948 | ||
898 | if (!h->sum) | 949 | if (!h->sum) |
899 | return 0; | 950 | return 0; |
@@ -928,12 +979,13 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
928 | goto next; | 979 | goto next; |
929 | 980 | ||
930 | strcpy(src_line[i].path, path); | 981 | strcpy(src_line[i].path, path); |
931 | insert_source_line(root, &src_line[i]); | 982 | insert_source_line(&tmp_root, &src_line[i]); |
932 | 983 | ||
933 | next: | 984 | next: |
934 | pclose(fp); | 985 | pclose(fp); |
935 | } | 986 | } |
936 | 987 | ||
988 | resort_source_line(root, &tmp_root); | ||
937 | return 0; | 989 | return 0; |
938 | } | 990 | } |
939 | 991 | ||
@@ -957,7 +1009,7 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
957 | char *path; | 1009 | char *path; |
958 | 1010 | ||
959 | src_line = rb_entry(node, struct source_line, node); | 1011 | src_line = rb_entry(node, struct source_line, node); |
960 | percent = src_line->percent; | 1012 | percent = src_line->percent_sum; |
961 | color = get_percent_color(percent); | 1013 | color = get_percent_color(percent); |
962 | path = src_line->path; | 1014 | path = src_line->path; |
963 | 1015 | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 39242dcee8f2..8eec94358a4a 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <stdint.h> | 5 | #include <stdint.h> |
6 | #include "types.h" | 6 | #include "types.h" |
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include "hist.h" | ||
8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
9 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
10 | #include <pthread.h> | 11 | #include <pthread.h> |
@@ -75,6 +76,7 @@ struct sym_hist { | |||
75 | struct source_line { | 76 | struct source_line { |
76 | struct rb_node node; | 77 | struct rb_node node; |
77 | double percent; | 78 | double percent; |
79 | double percent_sum; | ||
78 | char *path; | 80 | char *path; |
79 | }; | 81 | }; |
80 | 82 | ||
@@ -140,20 +142,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
140 | 142 | ||
141 | #ifdef NEWT_SUPPORT | 143 | #ifdef NEWT_SUPPORT |
142 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 144 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
143 | void(*timer)(void *arg), void *arg, int delay_secs); | 145 | struct hist_browser_timer *hbt); |
144 | #else | 146 | #else |
145 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, | 147 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
146 | struct map *map __maybe_unused, | 148 | struct map *map __maybe_unused, |
147 | int evidx __maybe_unused, | 149 | int evidx __maybe_unused, |
148 | void(*timer)(void *arg) __maybe_unused, | 150 | struct hist_browser_timer *hbt |
149 | void *arg __maybe_unused, | 151 | __maybe_unused) |
150 | int delay_secs __maybe_unused) | ||
151 | { | 152 | { |
152 | return 0; | 153 | return 0; |
153 | } | 154 | } |
154 | #endif | 155 | #endif |
155 | 156 | ||
156 | extern const char *disassembler_style; | 157 | extern const char *disassembler_style; |
157 | extern const char *objdump_path; | ||
158 | 158 | ||
159 | #endif /* __PERF_ANNOTATE_H */ | 159 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 8e3a740ddbd4..5295625c0c00 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -16,11 +16,11 @@ | |||
16 | #include "session.h" | 16 | #include "session.h" |
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | 18 | ||
19 | static int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 20 | union perf_event *event, |
21 | struct perf_sample *sample __maybe_unused, | 21 | struct perf_sample *sample __maybe_unused, |
22 | struct perf_evsel *evsel __maybe_unused, | 22 | struct perf_evsel *evsel __maybe_unused, |
23 | struct machine *machine) | 23 | struct machine *machine) |
24 | { | 24 | { |
25 | struct addr_location al; | 25 | struct addr_location al; |
26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
@@ -64,12 +64,27 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
64 | struct perf_tool build_id__mark_dso_hit_ops = { | 64 | struct perf_tool build_id__mark_dso_hit_ops = { |
65 | .sample = build_id__mark_dso_hit, | 65 | .sample = build_id__mark_dso_hit, |
66 | .mmap = perf_event__process_mmap, | 66 | .mmap = perf_event__process_mmap, |
67 | .fork = perf_event__process_task, | 67 | .fork = perf_event__process_fork, |
68 | .exit = perf_event__exit_del_thread, | 68 | .exit = perf_event__exit_del_thread, |
69 | .attr = perf_event__process_attr, | 69 | .attr = perf_event__process_attr, |
70 | .build_id = perf_event__process_build_id, | 70 | .build_id = perf_event__process_build_id, |
71 | }; | 71 | }; |
72 | 72 | ||
73 | int build_id__sprintf(const u8 *build_id, int len, char *bf) | ||
74 | { | ||
75 | char *bid = bf; | ||
76 | const u8 *raw = build_id; | ||
77 | int i; | ||
78 | |||
79 | for (i = 0; i < len; ++i) { | ||
80 | sprintf(bid, "%02x", *raw); | ||
81 | ++raw; | ||
82 | bid += 2; | ||
83 | } | ||
84 | |||
85 | return raw - build_id; | ||
86 | } | ||
87 | |||
73 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 88 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
74 | { | 89 | { |
75 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 90 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a993ba87d996..a811f5c62e18 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -1,10 +1,19 @@ | |||
1 | #ifndef PERF_BUILD_ID_H_ | 1 | #ifndef PERF_BUILD_ID_H_ |
2 | #define PERF_BUILD_ID_H_ 1 | 2 | #define PERF_BUILD_ID_H_ 1 |
3 | 3 | ||
4 | #include "session.h" | 4 | #define BUILD_ID_SIZE 20 |
5 | |||
6 | #include "tool.h" | ||
7 | #include "types.h" | ||
5 | 8 | ||
6 | extern struct perf_tool build_id__mark_dso_hit_ops; | 9 | extern struct perf_tool build_id__mark_dso_hit_ops; |
10 | struct dso; | ||
7 | 11 | ||
12 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | ||
8 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size); | 13 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size); |
9 | 14 | ||
15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | ||
16 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
17 | struct machine *machine); | ||
18 | |||
10 | #endif | 19 | #endif |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 2bd51370ad28..26e367239873 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "util.h" | 5 | #include "util.h" |
6 | #include "strbuf.h" | 6 | #include "strbuf.h" |
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "../ui/ui.h" | ||
8 | 9 | ||
9 | #define CMD_EXEC_PATH "--exec-path" | 10 | #define CMD_EXEC_PATH "--exec-path" |
10 | #define CMD_PERF_DIR "--perf-dir=" | 11 | #define CMD_PERF_DIR "--perf-dir=" |
@@ -31,44 +32,6 @@ extern const char *pager_program; | |||
31 | extern int pager_in_use(void); | 32 | extern int pager_in_use(void); |
32 | extern int pager_use_color; | 33 | extern int pager_use_color; |
33 | 34 | ||
34 | extern int use_browser; | ||
35 | |||
36 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) | ||
37 | void setup_browser(bool fallback_to_pager); | ||
38 | void exit_browser(bool wait_for_ok); | ||
39 | |||
40 | #ifdef NEWT_SUPPORT | ||
41 | int ui__init(void); | ||
42 | void ui__exit(bool wait_for_ok); | ||
43 | #else | ||
44 | static inline int ui__init(void) | ||
45 | { | ||
46 | return -1; | ||
47 | } | ||
48 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} | ||
49 | #endif | ||
50 | |||
51 | #ifdef GTK2_SUPPORT | ||
52 | int perf_gtk__init(void); | ||
53 | void perf_gtk__exit(bool wait_for_ok); | ||
54 | #else | ||
55 | static inline int perf_gtk__init(void) | ||
56 | { | ||
57 | return -1; | ||
58 | } | ||
59 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} | ||
60 | #endif | ||
61 | |||
62 | #else /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
63 | |||
64 | static inline void setup_browser(bool fallback_to_pager) | ||
65 | { | ||
66 | if (fallback_to_pager) | ||
67 | setup_pager(); | ||
68 | } | ||
69 | static inline void exit_browser(bool wait_for_ok __maybe_unused) {} | ||
70 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
71 | |||
72 | char *alias_lookup(const char *alias); | 35 | char *alias_lookup(const char *alias); |
73 | int split_cmdline(char *cmdline, const char ***argv); | 36 | int split_cmdline(char *cmdline, const char ***argv); |
74 | 37 | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index dec98750b484..83e8d234af6b 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -26,6 +26,7 @@ int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | |||
26 | static inline void ui_progress__update(u64 curr __maybe_unused, | 26 | static inline void ui_progress__update(u64 curr __maybe_unused, |
27 | u64 total __maybe_unused, | 27 | u64 total __maybe_unused, |
28 | const char *title __maybe_unused) {} | 28 | const char *title __maybe_unused) {} |
29 | static inline void ui_progress__finish(void) {} | ||
29 | 30 | ||
30 | #define ui__error(format, arg...) ui__warning(format, ##arg) | 31 | #define ui__error(format, arg...) ui__warning(format, ##arg) |
31 | 32 | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c new file mode 100644 index 000000000000..d6d9a465acdb --- /dev/null +++ b/tools/perf/util/dso.c | |||
@@ -0,0 +1,595 @@ | |||
1 | #include "symbol.h" | ||
2 | #include "dso.h" | ||
3 | #include "machine.h" | ||
4 | #include "util.h" | ||
5 | #include "debug.h" | ||
6 | |||
7 | char dso__symtab_origin(const struct dso *dso) | ||
8 | { | ||
9 | static const char origin[] = { | ||
10 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | ||
11 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
12 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | ||
13 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | ||
14 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | ||
15 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | ||
16 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | ||
17 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | ||
18 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | ||
19 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | ||
20 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | ||
21 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | ||
22 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
23 | }; | ||
24 | |||
25 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | ||
26 | return '!'; | ||
27 | return origin[dso->symtab_type]; | ||
28 | } | ||
29 | |||
30 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
31 | char *root_dir, char *file, size_t size) | ||
32 | { | ||
33 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
34 | int ret = 0; | ||
35 | |||
36 | switch (type) { | ||
37 | case DSO_BINARY_TYPE__DEBUGLINK: { | ||
38 | char *debuglink; | ||
39 | |||
40 | strncpy(file, dso->long_name, size); | ||
41 | debuglink = file + dso->long_name_len; | ||
42 | while (debuglink != file && *debuglink != '/') | ||
43 | debuglink--; | ||
44 | if (*debuglink == '/') | ||
45 | debuglink++; | ||
46 | filename__read_debuglink(dso->long_name, debuglink, | ||
47 | size - (debuglink - file)); | ||
48 | } | ||
49 | break; | ||
50 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | ||
51 | /* skip the locally configured cache if a symfs is given */ | ||
52 | if (symbol_conf.symfs[0] || | ||
53 | (dso__build_id_filename(dso, file, size) == NULL)) | ||
54 | ret = -1; | ||
55 | break; | ||
56 | |||
57 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | ||
58 | snprintf(file, size, "%s/usr/lib/debug%s.debug", | ||
59 | symbol_conf.symfs, dso->long_name); | ||
60 | break; | ||
61 | |||
62 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | ||
63 | snprintf(file, size, "%s/usr/lib/debug%s", | ||
64 | symbol_conf.symfs, dso->long_name); | ||
65 | break; | ||
66 | |||
67 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | ||
68 | if (!dso->has_build_id) { | ||
69 | ret = -1; | ||
70 | break; | ||
71 | } | ||
72 | |||
73 | build_id__sprintf(dso->build_id, | ||
74 | sizeof(dso->build_id), | ||
75 | build_id_hex); | ||
76 | snprintf(file, size, | ||
77 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
78 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | ||
79 | break; | ||
80 | |||
81 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | ||
82 | snprintf(file, size, "%s%s", | ||
83 | symbol_conf.symfs, dso->long_name); | ||
84 | break; | ||
85 | |||
86 | case DSO_BINARY_TYPE__GUEST_KMODULE: | ||
87 | snprintf(file, size, "%s%s%s", symbol_conf.symfs, | ||
88 | root_dir, dso->long_name); | ||
89 | break; | ||
90 | |||
91 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | ||
92 | snprintf(file, size, "%s%s", symbol_conf.symfs, | ||
93 | dso->long_name); | ||
94 | break; | ||
95 | |||
96 | default: | ||
97 | case DSO_BINARY_TYPE__KALLSYMS: | ||
98 | case DSO_BINARY_TYPE__VMLINUX: | ||
99 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | ||
100 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
101 | case DSO_BINARY_TYPE__JAVA_JIT: | ||
102 | case DSO_BINARY_TYPE__NOT_FOUND: | ||
103 | ret = -1; | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static int open_dso(struct dso *dso, struct machine *machine) | ||
111 | { | ||
112 | char *root_dir = (char *) ""; | ||
113 | char *name; | ||
114 | int fd; | ||
115 | |||
116 | name = malloc(PATH_MAX); | ||
117 | if (!name) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | if (machine) | ||
121 | root_dir = machine->root_dir; | ||
122 | |||
123 | if (dso__binary_type_file(dso, dso->data_type, | ||
124 | root_dir, name, PATH_MAX)) { | ||
125 | free(name); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | fd = open(name, O_RDONLY); | ||
130 | free(name); | ||
131 | return fd; | ||
132 | } | ||
133 | |||
134 | int dso__data_fd(struct dso *dso, struct machine *machine) | ||
135 | { | ||
136 | static enum dso_binary_type binary_type_data[] = { | ||
137 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
138 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
139 | DSO_BINARY_TYPE__NOT_FOUND, | ||
140 | }; | ||
141 | int i = 0; | ||
142 | |||
143 | if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) | ||
144 | return open_dso(dso, machine); | ||
145 | |||
146 | do { | ||
147 | int fd; | ||
148 | |||
149 | dso->data_type = binary_type_data[i++]; | ||
150 | |||
151 | fd = open_dso(dso, machine); | ||
152 | if (fd >= 0) | ||
153 | return fd; | ||
154 | |||
155 | } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); | ||
156 | |||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | static void | ||
161 | dso_cache__free(struct rb_root *root) | ||
162 | { | ||
163 | struct rb_node *next = rb_first(root); | ||
164 | |||
165 | while (next) { | ||
166 | struct dso_cache *cache; | ||
167 | |||
168 | cache = rb_entry(next, struct dso_cache, rb_node); | ||
169 | next = rb_next(&cache->rb_node); | ||
170 | rb_erase(&cache->rb_node, root); | ||
171 | free(cache); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static struct dso_cache* | ||
176 | dso_cache__find(struct rb_root *root, u64 offset) | ||
177 | { | ||
178 | struct rb_node **p = &root->rb_node; | ||
179 | struct rb_node *parent = NULL; | ||
180 | struct dso_cache *cache; | ||
181 | |||
182 | while (*p != NULL) { | ||
183 | u64 end; | ||
184 | |||
185 | parent = *p; | ||
186 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
187 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
188 | |||
189 | if (offset < cache->offset) | ||
190 | p = &(*p)->rb_left; | ||
191 | else if (offset >= end) | ||
192 | p = &(*p)->rb_right; | ||
193 | else | ||
194 | return cache; | ||
195 | } | ||
196 | return NULL; | ||
197 | } | ||
198 | |||
199 | static void | ||
200 | dso_cache__insert(struct rb_root *root, struct dso_cache *new) | ||
201 | { | ||
202 | struct rb_node **p = &root->rb_node; | ||
203 | struct rb_node *parent = NULL; | ||
204 | struct dso_cache *cache; | ||
205 | u64 offset = new->offset; | ||
206 | |||
207 | while (*p != NULL) { | ||
208 | u64 end; | ||
209 | |||
210 | parent = *p; | ||
211 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
212 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
213 | |||
214 | if (offset < cache->offset) | ||
215 | p = &(*p)->rb_left; | ||
216 | else if (offset >= end) | ||
217 | p = &(*p)->rb_right; | ||
218 | } | ||
219 | |||
220 | rb_link_node(&new->rb_node, parent, p); | ||
221 | rb_insert_color(&new->rb_node, root); | ||
222 | } | ||
223 | |||
224 | static ssize_t | ||
225 | dso_cache__memcpy(struct dso_cache *cache, u64 offset, | ||
226 | u8 *data, u64 size) | ||
227 | { | ||
228 | u64 cache_offset = offset - cache->offset; | ||
229 | u64 cache_size = min(cache->size - cache_offset, size); | ||
230 | |||
231 | memcpy(data, cache->data + cache_offset, cache_size); | ||
232 | return cache_size; | ||
233 | } | ||
234 | |||
235 | static ssize_t | ||
236 | dso_cache__read(struct dso *dso, struct machine *machine, | ||
237 | u64 offset, u8 *data, ssize_t size) | ||
238 | { | ||
239 | struct dso_cache *cache; | ||
240 | ssize_t ret; | ||
241 | int fd; | ||
242 | |||
243 | fd = dso__data_fd(dso, machine); | ||
244 | if (fd < 0) | ||
245 | return -1; | ||
246 | |||
247 | do { | ||
248 | u64 cache_offset; | ||
249 | |||
250 | ret = -ENOMEM; | ||
251 | |||
252 | cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); | ||
253 | if (!cache) | ||
254 | break; | ||
255 | |||
256 | cache_offset = offset & DSO__DATA_CACHE_MASK; | ||
257 | ret = -EINVAL; | ||
258 | |||
259 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | ||
260 | break; | ||
261 | |||
262 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
263 | if (ret <= 0) | ||
264 | break; | ||
265 | |||
266 | cache->offset = cache_offset; | ||
267 | cache->size = ret; | ||
268 | dso_cache__insert(&dso->cache, cache); | ||
269 | |||
270 | ret = dso_cache__memcpy(cache, offset, data, size); | ||
271 | |||
272 | } while (0); | ||
273 | |||
274 | if (ret <= 0) | ||
275 | free(cache); | ||
276 | |||
277 | close(fd); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | ||
282 | u64 offset, u8 *data, ssize_t size) | ||
283 | { | ||
284 | struct dso_cache *cache; | ||
285 | |||
286 | cache = dso_cache__find(&dso->cache, offset); | ||
287 | if (cache) | ||
288 | return dso_cache__memcpy(cache, offset, data, size); | ||
289 | else | ||
290 | return dso_cache__read(dso, machine, offset, data, size); | ||
291 | } | ||
292 | |||
293 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
294 | u64 offset, u8 *data, ssize_t size) | ||
295 | { | ||
296 | ssize_t r = 0; | ||
297 | u8 *p = data; | ||
298 | |||
299 | do { | ||
300 | ssize_t ret; | ||
301 | |||
302 | ret = dso_cache_read(dso, machine, offset, p, size); | ||
303 | if (ret < 0) | ||
304 | return ret; | ||
305 | |||
306 | /* Reached EOF, return what we have. */ | ||
307 | if (!ret) | ||
308 | break; | ||
309 | |||
310 | BUG_ON(ret > size); | ||
311 | |||
312 | r += ret; | ||
313 | p += ret; | ||
314 | offset += ret; | ||
315 | size -= ret; | ||
316 | |||
317 | } while (size); | ||
318 | |||
319 | return r; | ||
320 | } | ||
321 | |||
322 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
323 | struct machine *machine, u64 addr, | ||
324 | u8 *data, ssize_t size) | ||
325 | { | ||
326 | u64 offset = map->map_ip(map, addr); | ||
327 | return dso__data_read_offset(dso, machine, offset, data, size); | ||
328 | } | ||
329 | |||
330 | struct map *dso__new_map(const char *name) | ||
331 | { | ||
332 | struct map *map = NULL; | ||
333 | struct dso *dso = dso__new(name); | ||
334 | |||
335 | if (dso) | ||
336 | map = map__new2(0, dso, MAP__FUNCTION); | ||
337 | |||
338 | return map; | ||
339 | } | ||
340 | |||
341 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | ||
342 | const char *short_name, int dso_type) | ||
343 | { | ||
344 | /* | ||
345 | * The kernel dso could be created by build_id processing. | ||
346 | */ | ||
347 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); | ||
348 | |||
349 | /* | ||
350 | * We need to run this in all cases, since during the build_id | ||
351 | * processing we had no idea this was the kernel dso. | ||
352 | */ | ||
353 | if (dso != NULL) { | ||
354 | dso__set_short_name(dso, short_name); | ||
355 | dso->kernel = dso_type; | ||
356 | } | ||
357 | |||
358 | return dso; | ||
359 | } | ||
360 | |||
361 | void dso__set_long_name(struct dso *dso, char *name) | ||
362 | { | ||
363 | if (name == NULL) | ||
364 | return; | ||
365 | dso->long_name = name; | ||
366 | dso->long_name_len = strlen(name); | ||
367 | } | ||
368 | |||
369 | void dso__set_short_name(struct dso *dso, const char *name) | ||
370 | { | ||
371 | if (name == NULL) | ||
372 | return; | ||
373 | dso->short_name = name; | ||
374 | dso->short_name_len = strlen(name); | ||
375 | } | ||
376 | |||
377 | static void dso__set_basename(struct dso *dso) | ||
378 | { | ||
379 | dso__set_short_name(dso, basename(dso->long_name)); | ||
380 | } | ||
381 | |||
382 | int dso__name_len(const struct dso *dso) | ||
383 | { | ||
384 | if (!dso) | ||
385 | return strlen("[unknown]"); | ||
386 | if (verbose) | ||
387 | return dso->long_name_len; | ||
388 | |||
389 | return dso->short_name_len; | ||
390 | } | ||
391 | |||
392 | bool dso__loaded(const struct dso *dso, enum map_type type) | ||
393 | { | ||
394 | return dso->loaded & (1 << type); | ||
395 | } | ||
396 | |||
397 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | ||
398 | { | ||
399 | return dso->sorted_by_name & (1 << type); | ||
400 | } | ||
401 | |||
402 | void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | ||
403 | { | ||
404 | dso->sorted_by_name |= (1 << type); | ||
405 | } | ||
406 | |||
407 | struct dso *dso__new(const char *name) | ||
408 | { | ||
409 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | ||
410 | |||
411 | if (dso != NULL) { | ||
412 | int i; | ||
413 | strcpy(dso->name, name); | ||
414 | dso__set_long_name(dso, dso->name); | ||
415 | dso__set_short_name(dso, dso->name); | ||
416 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
417 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | ||
418 | dso->cache = RB_ROOT; | ||
419 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
420 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
421 | dso->loaded = 0; | ||
422 | dso->sorted_by_name = 0; | ||
423 | dso->has_build_id = 0; | ||
424 | dso->kernel = DSO_TYPE_USER; | ||
425 | dso->needs_swap = DSO_SWAP__UNSET; | ||
426 | INIT_LIST_HEAD(&dso->node); | ||
427 | } | ||
428 | |||
429 | return dso; | ||
430 | } | ||
431 | |||
432 | void dso__delete(struct dso *dso) | ||
433 | { | ||
434 | int i; | ||
435 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
436 | symbols__delete(&dso->symbols[i]); | ||
437 | if (dso->sname_alloc) | ||
438 | free((char *)dso->short_name); | ||
439 | if (dso->lname_alloc) | ||
440 | free(dso->long_name); | ||
441 | dso_cache__free(&dso->cache); | ||
442 | free(dso); | ||
443 | } | ||
444 | |||
445 | void dso__set_build_id(struct dso *dso, void *build_id) | ||
446 | { | ||
447 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | ||
448 | dso->has_build_id = 1; | ||
449 | } | ||
450 | |||
451 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
452 | { | ||
453 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | ||
454 | } | ||
455 | |||
456 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | ||
457 | { | ||
458 | char path[PATH_MAX]; | ||
459 | |||
460 | if (machine__is_default_guest(machine)) | ||
461 | return; | ||
462 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | ||
463 | if (sysfs__read_build_id(path, dso->build_id, | ||
464 | sizeof(dso->build_id)) == 0) | ||
465 | dso->has_build_id = true; | ||
466 | } | ||
467 | |||
468 | int dso__kernel_module_get_build_id(struct dso *dso, | ||
469 | const char *root_dir) | ||
470 | { | ||
471 | char filename[PATH_MAX]; | ||
472 | /* | ||
473 | * kernel module short names are of the form "[module]" and | ||
474 | * we need just "module" here. | ||
475 | */ | ||
476 | const char *name = dso->short_name + 1; | ||
477 | |||
478 | snprintf(filename, sizeof(filename), | ||
479 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | ||
480 | root_dir, (int)strlen(name) - 1, name); | ||
481 | |||
482 | if (sysfs__read_build_id(filename, dso->build_id, | ||
483 | sizeof(dso->build_id)) == 0) | ||
484 | dso->has_build_id = true; | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | ||
490 | { | ||
491 | bool have_build_id = false; | ||
492 | struct dso *pos; | ||
493 | |||
494 | list_for_each_entry(pos, head, node) { | ||
495 | if (with_hits && !pos->hit) | ||
496 | continue; | ||
497 | if (pos->has_build_id) { | ||
498 | have_build_id = true; | ||
499 | continue; | ||
500 | } | ||
501 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
502 | sizeof(pos->build_id)) > 0) { | ||
503 | have_build_id = true; | ||
504 | pos->has_build_id = true; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | return have_build_id; | ||
509 | } | ||
510 | |||
511 | void dsos__add(struct list_head *head, struct dso *dso) | ||
512 | { | ||
513 | list_add_tail(&dso->node, head); | ||
514 | } | ||
515 | |||
516 | struct dso *dsos__find(struct list_head *head, const char *name) | ||
517 | { | ||
518 | struct dso *pos; | ||
519 | |||
520 | list_for_each_entry(pos, head, node) | ||
521 | if (strcmp(pos->long_name, name) == 0) | ||
522 | return pos; | ||
523 | return NULL; | ||
524 | } | ||
525 | |||
526 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | ||
527 | { | ||
528 | struct dso *dso = dsos__find(head, name); | ||
529 | |||
530 | if (!dso) { | ||
531 | dso = dso__new(name); | ||
532 | if (dso != NULL) { | ||
533 | dsos__add(head, dso); | ||
534 | dso__set_basename(dso); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | return dso; | ||
539 | } | ||
540 | |||
541 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
542 | bool with_hits) | ||
543 | { | ||
544 | struct dso *pos; | ||
545 | size_t ret = 0; | ||
546 | |||
547 | list_for_each_entry(pos, head, node) { | ||
548 | if (with_hits && !pos->hit) | ||
549 | continue; | ||
550 | ret += dso__fprintf_buildid(pos, fp); | ||
551 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
552 | } | ||
553 | return ret; | ||
554 | } | ||
555 | |||
556 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | ||
557 | { | ||
558 | struct dso *pos; | ||
559 | size_t ret = 0; | ||
560 | |||
561 | list_for_each_entry(pos, head, node) { | ||
562 | int i; | ||
563 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
564 | ret += dso__fprintf(pos, i, fp); | ||
565 | } | ||
566 | |||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | ||
571 | { | ||
572 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
573 | |||
574 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
575 | return fprintf(fp, "%s", sbuild_id); | ||
576 | } | ||
577 | |||
578 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | ||
579 | { | ||
580 | struct rb_node *nd; | ||
581 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | ||
582 | |||
583 | if (dso->short_name != dso->long_name) | ||
584 | ret += fprintf(fp, "%s, ", dso->long_name); | ||
585 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
586 | dso->loaded ? "" : "NOT "); | ||
587 | ret += dso__fprintf_buildid(dso, fp); | ||
588 | ret += fprintf(fp, ")\n"); | ||
589 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
590 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
591 | ret += symbol__fprintf(pos, fp); | ||
592 | } | ||
593 | |||
594 | return ret; | ||
595 | } | ||
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h new file mode 100644 index 000000000000..e03276940b99 --- /dev/null +++ b/tools/perf/util/dso.h | |||
@@ -0,0 +1,148 @@ | |||
1 | #ifndef __PERF_DSO | ||
2 | #define __PERF_DSO | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <linux/rbtree.h> | ||
6 | #include "types.h" | ||
7 | #include "map.h" | ||
8 | |||
9 | enum dso_binary_type { | ||
10 | DSO_BINARY_TYPE__KALLSYMS = 0, | ||
11 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | ||
12 | DSO_BINARY_TYPE__VMLINUX, | ||
13 | DSO_BINARY_TYPE__GUEST_VMLINUX, | ||
14 | DSO_BINARY_TYPE__JAVA_JIT, | ||
15 | DSO_BINARY_TYPE__DEBUGLINK, | ||
16 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
17 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | ||
18 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | ||
19 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | ||
20 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
21 | DSO_BINARY_TYPE__GUEST_KMODULE, | ||
22 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | ||
23 | DSO_BINARY_TYPE__NOT_FOUND, | ||
24 | }; | ||
25 | |||
26 | enum dso_kernel_type { | ||
27 | DSO_TYPE_USER = 0, | ||
28 | DSO_TYPE_KERNEL, | ||
29 | DSO_TYPE_GUEST_KERNEL | ||
30 | }; | ||
31 | |||
32 | enum dso_swap_type { | ||
33 | DSO_SWAP__UNSET, | ||
34 | DSO_SWAP__NO, | ||
35 | DSO_SWAP__YES, | ||
36 | }; | ||
37 | |||
38 | #define DSO__SWAP(dso, type, val) \ | ||
39 | ({ \ | ||
40 | type ____r = val; \ | ||
41 | BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \ | ||
42 | if (dso->needs_swap == DSO_SWAP__YES) { \ | ||
43 | switch (sizeof(____r)) { \ | ||
44 | case 2: \ | ||
45 | ____r = bswap_16(val); \ | ||
46 | break; \ | ||
47 | case 4: \ | ||
48 | ____r = bswap_32(val); \ | ||
49 | break; \ | ||
50 | case 8: \ | ||
51 | ____r = bswap_64(val); \ | ||
52 | break; \ | ||
53 | default: \ | ||
54 | BUG_ON(1); \ | ||
55 | } \ | ||
56 | } \ | ||
57 | ____r; \ | ||
58 | }) | ||
59 | |||
60 | #define DSO__DATA_CACHE_SIZE 4096 | ||
61 | #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) | ||
62 | |||
63 | struct dso_cache { | ||
64 | struct rb_node rb_node; | ||
65 | u64 offset; | ||
66 | u64 size; | ||
67 | char data[0]; | ||
68 | }; | ||
69 | |||
70 | struct dso { | ||
71 | struct list_head node; | ||
72 | struct rb_root symbols[MAP__NR_TYPES]; | ||
73 | struct rb_root symbol_names[MAP__NR_TYPES]; | ||
74 | struct rb_root cache; | ||
75 | enum dso_kernel_type kernel; | ||
76 | enum dso_swap_type needs_swap; | ||
77 | enum dso_binary_type symtab_type; | ||
78 | enum dso_binary_type data_type; | ||
79 | u8 adjust_symbols:1; | ||
80 | u8 has_build_id:1; | ||
81 | u8 hit:1; | ||
82 | u8 annotate_warned:1; | ||
83 | u8 sname_alloc:1; | ||
84 | u8 lname_alloc:1; | ||
85 | u8 sorted_by_name; | ||
86 | u8 loaded; | ||
87 | u8 build_id[BUILD_ID_SIZE]; | ||
88 | const char *short_name; | ||
89 | char *long_name; | ||
90 | u16 long_name_len; | ||
91 | u16 short_name_len; | ||
92 | char name[0]; | ||
93 | }; | ||
94 | |||
95 | static inline void dso__set_loaded(struct dso *dso, enum map_type type) | ||
96 | { | ||
97 | dso->loaded |= (1 << type); | ||
98 | } | ||
99 | |||
100 | struct dso *dso__new(const char *name); | ||
101 | void dso__delete(struct dso *dso); | ||
102 | |||
103 | void dso__set_short_name(struct dso *dso, const char *name); | ||
104 | void dso__set_long_name(struct dso *dso, char *name); | ||
105 | |||
106 | int dso__name_len(const struct dso *dso); | ||
107 | |||
108 | bool dso__loaded(const struct dso *dso, enum map_type type); | ||
109 | |||
110 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type); | ||
111 | void dso__set_sorted_by_name(struct dso *dso, enum map_type type); | ||
112 | void dso__sort_by_name(struct dso *dso, enum map_type type); | ||
113 | |||
114 | void dso__set_build_id(struct dso *dso, void *build_id); | ||
115 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
116 | void dso__read_running_kernel_build_id(struct dso *dso, | ||
117 | struct machine *machine); | ||
118 | int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); | ||
119 | |||
120 | char dso__symtab_origin(const struct dso *dso); | ||
121 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
122 | char *root_dir, char *file, size_t size); | ||
123 | |||
124 | int dso__data_fd(struct dso *dso, struct machine *machine); | ||
125 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
126 | u64 offset, u8 *data, ssize_t size); | ||
127 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
128 | struct machine *machine, u64 addr, | ||
129 | u8 *data, ssize_t size); | ||
130 | |||
131 | struct map *dso__new_map(const char *name); | ||
132 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | ||
133 | const char *short_name, int dso_type); | ||
134 | |||
135 | void dsos__add(struct list_head *head, struct dso *dso); | ||
136 | struct dso *dsos__find(struct list_head *head, const char *name); | ||
137 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
138 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | ||
139 | |||
140 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
141 | bool with_hits); | ||
142 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); | ||
143 | |||
144 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | ||
145 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
146 | enum map_type type, FILE *fp); | ||
147 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | ||
148 | #endif /* __PERF_DSO */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6715b1938725..3cf2c3e0605f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
2 | #include "event.h" | 2 | #include "event.h" |
3 | #include "debug.h" | 3 | #include "debug.h" |
4 | #include "machine.h" | ||
4 | #include "sort.h" | 5 | #include "sort.h" |
5 | #include "string.h" | 6 | #include "string.h" |
6 | #include "strlist.h" | 7 | #include "strlist.h" |
@@ -192,55 +193,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
192 | event->header.misc = PERF_RECORD_MISC_USER; | 193 | event->header.misc = PERF_RECORD_MISC_USER; |
193 | 194 | ||
194 | while (1) { | 195 | while (1) { |
195 | char bf[BUFSIZ], *pbf = bf; | 196 | char bf[BUFSIZ]; |
196 | int n; | 197 | char prot[5]; |
198 | char execname[PATH_MAX]; | ||
199 | char anonstr[] = "//anon"; | ||
197 | size_t size; | 200 | size_t size; |
201 | |||
198 | if (fgets(bf, sizeof(bf), fp) == NULL) | 202 | if (fgets(bf, sizeof(bf), fp) == NULL) |
199 | break; | 203 | break; |
200 | 204 | ||
205 | /* ensure null termination since stack will be reused. */ | ||
206 | strcpy(execname, ""); | ||
207 | |||
201 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 208 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
202 | n = hex2u64(pbf, &event->mmap.start); | 209 | sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", |
203 | if (n < 0) | 210 | &event->mmap.start, &event->mmap.len, prot, |
204 | continue; | 211 | &event->mmap.pgoff, execname); |
205 | pbf += n + 1; | 212 | |
206 | n = hex2u64(pbf, &event->mmap.len); | 213 | if (prot[2] != 'x') |
207 | if (n < 0) | ||
208 | continue; | 214 | continue; |
209 | pbf += n + 3; | 215 | |
210 | if (*pbf == 'x') { /* vm_exec */ | 216 | if (!strcmp(execname, "")) |
211 | char anonstr[] = "//anon\n"; | 217 | strcpy(execname, anonstr); |
212 | char *execname = strchr(bf, '/'); | 218 | |
213 | 219 | size = strlen(execname) + 1; | |
214 | /* Catch VDSO */ | 220 | memcpy(event->mmap.filename, execname, size); |
215 | if (execname == NULL) | 221 | size = PERF_ALIGN(size, sizeof(u64)); |
216 | execname = strstr(bf, "[vdso]"); | 222 | event->mmap.len -= event->mmap.start; |
217 | 223 | event->mmap.header.size = (sizeof(event->mmap) - | |
218 | /* Catch anonymous mmaps */ | 224 | (sizeof(event->mmap.filename) - size)); |
219 | if ((execname == NULL) && !strstr(bf, "[")) | 225 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); |
220 | execname = anonstr; | 226 | event->mmap.header.size += machine->id_hdr_size; |
221 | 227 | event->mmap.pid = tgid; | |
222 | if (execname == NULL) | 228 | event->mmap.tid = pid; |
223 | continue; | 229 | |
224 | 230 | if (process(tool, event, &synth_sample, machine) != 0) { | |
225 | pbf += 3; | 231 | rc = -1; |
226 | n = hex2u64(pbf, &event->mmap.pgoff); | 232 | break; |
227 | |||
228 | size = strlen(execname); | ||
229 | execname[size - 1] = '\0'; /* Remove \n */ | ||
230 | memcpy(event->mmap.filename, execname, size); | ||
231 | size = PERF_ALIGN(size, sizeof(u64)); | ||
232 | event->mmap.len -= event->mmap.start; | ||
233 | event->mmap.header.size = (sizeof(event->mmap) - | ||
234 | (sizeof(event->mmap.filename) - size)); | ||
235 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); | ||
236 | event->mmap.header.size += machine->id_hdr_size; | ||
237 | event->mmap.pid = tgid; | ||
238 | event->mmap.tid = pid; | ||
239 | |||
240 | if (process(tool, event, &synth_sample, machine) != 0) { | ||
241 | rc = -1; | ||
242 | break; | ||
243 | } | ||
244 | } | 233 | } |
245 | } | 234 | } |
246 | 235 | ||
@@ -404,16 +393,15 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
404 | 393 | ||
405 | if (*end) /* only interested in proper numerical dirents */ | 394 | if (*end) /* only interested in proper numerical dirents */ |
406 | continue; | 395 | continue; |
407 | 396 | /* | |
408 | if (__event__synthesize_thread(comm_event, mmap_event, pid, 1, | 397 | * We may race with exiting thread, so don't stop just because |
409 | process, tool, machine) != 0) { | 398 | * one thread couldn't be synthesized. |
410 | err = -1; | 399 | */ |
411 | goto out_closedir; | 400 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, |
412 | } | 401 | process, tool, machine); |
413 | } | 402 | } |
414 | 403 | ||
415 | err = 0; | 404 | err = 0; |
416 | out_closedir: | ||
417 | closedir(proc); | 405 | closedir(proc); |
418 | out_free_mmap: | 406 | out_free_mmap: |
419 | free(mmap_event); | 407 | free(mmap_event); |
@@ -519,134 +507,15 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | |||
519 | struct perf_sample *sample __maybe_unused, | 507 | struct perf_sample *sample __maybe_unused, |
520 | struct machine *machine) | 508 | struct machine *machine) |
521 | { | 509 | { |
522 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 510 | return machine__process_comm_event(machine, event); |
523 | |||
524 | if (dump_trace) | ||
525 | perf_event__fprintf_comm(event, stdout); | ||
526 | |||
527 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | ||
528 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
529 | return -1; | ||
530 | } | ||
531 | |||
532 | return 0; | ||
533 | } | 511 | } |
534 | 512 | ||
535 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | 513 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
536 | union perf_event *event, | 514 | union perf_event *event, |
537 | struct perf_sample *sample __maybe_unused, | 515 | struct perf_sample *sample __maybe_unused, |
538 | struct machine *machine __maybe_unused) | 516 | struct machine *machine) |
539 | { | ||
540 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | ||
541 | event->lost.id, event->lost.lost); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static void perf_event__set_kernel_mmap_len(union perf_event *event, | ||
546 | struct map **maps) | ||
547 | { | ||
548 | maps[MAP__FUNCTION]->start = event->mmap.start; | ||
549 | maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; | ||
550 | /* | ||
551 | * Be a bit paranoid here, some perf.data file came with | ||
552 | * a zero sized synthesized MMAP event for the kernel. | ||
553 | */ | ||
554 | if (maps[MAP__FUNCTION]->end == 0) | ||
555 | maps[MAP__FUNCTION]->end = ~0ULL; | ||
556 | } | ||
557 | |||
558 | static int perf_event__process_kernel_mmap(struct perf_tool *tool | ||
559 | __maybe_unused, | ||
560 | union perf_event *event, | ||
561 | struct machine *machine) | ||
562 | { | 517 | { |
563 | struct map *map; | 518 | return machine__process_lost_event(machine, event); |
564 | char kmmap_prefix[PATH_MAX]; | ||
565 | enum dso_kernel_type kernel_type; | ||
566 | bool is_kernel_mmap; | ||
567 | |||
568 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | ||
569 | if (machine__is_host(machine)) | ||
570 | kernel_type = DSO_TYPE_KERNEL; | ||
571 | else | ||
572 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
573 | |||
574 | is_kernel_mmap = memcmp(event->mmap.filename, | ||
575 | kmmap_prefix, | ||
576 | strlen(kmmap_prefix) - 1) == 0; | ||
577 | if (event->mmap.filename[0] == '/' || | ||
578 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | ||
579 | |||
580 | char short_module_name[1024]; | ||
581 | char *name, *dot; | ||
582 | |||
583 | if (event->mmap.filename[0] == '/') { | ||
584 | name = strrchr(event->mmap.filename, '/'); | ||
585 | if (name == NULL) | ||
586 | goto out_problem; | ||
587 | |||
588 | ++name; /* skip / */ | ||
589 | dot = strrchr(name, '.'); | ||
590 | if (dot == NULL) | ||
591 | goto out_problem; | ||
592 | snprintf(short_module_name, sizeof(short_module_name), | ||
593 | "[%.*s]", (int)(dot - name), name); | ||
594 | strxfrchar(short_module_name, '-', '_'); | ||
595 | } else | ||
596 | strcpy(short_module_name, event->mmap.filename); | ||
597 | |||
598 | map = machine__new_module(machine, event->mmap.start, | ||
599 | event->mmap.filename); | ||
600 | if (map == NULL) | ||
601 | goto out_problem; | ||
602 | |||
603 | name = strdup(short_module_name); | ||
604 | if (name == NULL) | ||
605 | goto out_problem; | ||
606 | |||
607 | map->dso->short_name = name; | ||
608 | map->dso->sname_alloc = 1; | ||
609 | map->end = map->start + event->mmap.len; | ||
610 | } else if (is_kernel_mmap) { | ||
611 | const char *symbol_name = (event->mmap.filename + | ||
612 | strlen(kmmap_prefix)); | ||
613 | /* | ||
614 | * Should be there already, from the build-id table in | ||
615 | * the header. | ||
616 | */ | ||
617 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | ||
618 | kmmap_prefix); | ||
619 | if (kernel == NULL) | ||
620 | goto out_problem; | ||
621 | |||
622 | kernel->kernel = kernel_type; | ||
623 | if (__machine__create_kernel_maps(machine, kernel) < 0) | ||
624 | goto out_problem; | ||
625 | |||
626 | perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); | ||
627 | |||
628 | /* | ||
629 | * Avoid using a zero address (kptr_restrict) for the ref reloc | ||
630 | * symbol. Effectively having zero here means that at record | ||
631 | * time /proc/sys/kernel/kptr_restrict was non zero. | ||
632 | */ | ||
633 | if (event->mmap.pgoff != 0) { | ||
634 | maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | ||
635 | symbol_name, | ||
636 | event->mmap.pgoff); | ||
637 | } | ||
638 | |||
639 | if (machine__is_default_guest(machine)) { | ||
640 | /* | ||
641 | * preload dso of guest kernel and modules | ||
642 | */ | ||
643 | dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], | ||
644 | NULL); | ||
645 | } | ||
646 | } | ||
647 | return 0; | ||
648 | out_problem: | ||
649 | return -1; | ||
650 | } | 519 | } |
651 | 520 | ||
652 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 521 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
@@ -656,43 +525,12 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
656 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 525 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
657 | } | 526 | } |
658 | 527 | ||
659 | int perf_event__process_mmap(struct perf_tool *tool, | 528 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
660 | union perf_event *event, | 529 | union perf_event *event, |
661 | struct perf_sample *sample __maybe_unused, | 530 | struct perf_sample *sample __maybe_unused, |
662 | struct machine *machine) | 531 | struct machine *machine) |
663 | { | 532 | { |
664 | struct thread *thread; | 533 | return machine__process_mmap_event(machine, event); |
665 | struct map *map; | ||
666 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
667 | int ret = 0; | ||
668 | |||
669 | if (dump_trace) | ||
670 | perf_event__fprintf_mmap(event, stdout); | ||
671 | |||
672 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
673 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
674 | ret = perf_event__process_kernel_mmap(tool, event, machine); | ||
675 | if (ret < 0) | ||
676 | goto out_problem; | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | thread = machine__findnew_thread(machine, event->mmap.pid); | ||
681 | if (thread == NULL) | ||
682 | goto out_problem; | ||
683 | map = map__new(&machine->user_dsos, event->mmap.start, | ||
684 | event->mmap.len, event->mmap.pgoff, | ||
685 | event->mmap.pid, event->mmap.filename, | ||
686 | MAP__FUNCTION); | ||
687 | if (map == NULL) | ||
688 | goto out_problem; | ||
689 | |||
690 | thread__insert_map(thread, map); | ||
691 | return 0; | ||
692 | |||
693 | out_problem: | ||
694 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
695 | return 0; | ||
696 | } | 534 | } |
697 | 535 | ||
698 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | 536 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
@@ -702,29 +540,20 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | |||
702 | event->fork.ppid, event->fork.ptid); | 540 | event->fork.ppid, event->fork.ptid); |
703 | } | 541 | } |
704 | 542 | ||
705 | int perf_event__process_task(struct perf_tool *tool __maybe_unused, | 543 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, |
706 | union perf_event *event, | 544 | union perf_event *event, |
707 | struct perf_sample *sample __maybe_unused, | 545 | struct perf_sample *sample __maybe_unused, |
708 | struct machine *machine) | 546 | struct machine *machine) |
709 | { | 547 | { |
710 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 548 | return machine__process_fork_event(machine, event); |
711 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 549 | } |
712 | |||
713 | if (dump_trace) | ||
714 | perf_event__fprintf_task(event, stdout); | ||
715 | |||
716 | if (event->header.type == PERF_RECORD_EXIT) { | ||
717 | machine__remove_thread(machine, thread); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | if (thread == NULL || parent == NULL || | ||
722 | thread__fork(thread, parent) < 0) { | ||
723 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
724 | return -1; | ||
725 | } | ||
726 | 550 | ||
727 | return 0; | 551 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, |
552 | union perf_event *event, | ||
553 | struct perf_sample *sample __maybe_unused, | ||
554 | struct machine *machine) | ||
555 | { | ||
556 | return machine__process_exit_event(machine, event); | ||
728 | } | 557 | } |
729 | 558 | ||
730 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) | 559 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) |
@@ -750,27 +579,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
750 | return ret; | 579 | return ret; |
751 | } | 580 | } |
752 | 581 | ||
753 | int perf_event__process(struct perf_tool *tool, union perf_event *event, | 582 | int perf_event__process(struct perf_tool *tool __maybe_unused, |
754 | struct perf_sample *sample, struct machine *machine) | 583 | union perf_event *event, |
584 | struct perf_sample *sample __maybe_unused, | ||
585 | struct machine *machine) | ||
755 | { | 586 | { |
756 | switch (event->header.type) { | 587 | return machine__process_event(machine, event); |
757 | case PERF_RECORD_COMM: | ||
758 | perf_event__process_comm(tool, event, sample, machine); | ||
759 | break; | ||
760 | case PERF_RECORD_MMAP: | ||
761 | perf_event__process_mmap(tool, event, sample, machine); | ||
762 | break; | ||
763 | case PERF_RECORD_FORK: | ||
764 | case PERF_RECORD_EXIT: | ||
765 | perf_event__process_task(tool, event, sample, machine); | ||
766 | break; | ||
767 | case PERF_RECORD_LOST: | ||
768 | perf_event__process_lost(tool, event, sample, machine); | ||
769 | default: | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | } | 588 | } |
775 | 589 | ||
776 | void thread__find_addr_map(struct thread *self, | 590 | void thread__find_addr_map(struct thread *self, |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 21b99e741a87..0d573ff4771a 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | ||
9 | 10 | ||
10 | /* | 11 | /* |
11 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 12 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -96,8 +97,6 @@ struct perf_sample { | |||
96 | struct stack_dump user_stack; | 97 | struct stack_dump user_stack; |
97 | }; | 98 | }; |
98 | 99 | ||
99 | #define BUILD_ID_SIZE 20 | ||
100 | |||
101 | struct build_id_event { | 100 | struct build_id_event { |
102 | struct perf_event_header header; | 101 | struct perf_event_header header; |
103 | pid_t pid; | 102 | pid_t pid; |
@@ -191,7 +190,11 @@ int perf_event__process_mmap(struct perf_tool *tool, | |||
191 | union perf_event *event, | 190 | union perf_event *event, |
192 | struct perf_sample *sample, | 191 | struct perf_sample *sample, |
193 | struct machine *machine); | 192 | struct machine *machine); |
194 | int perf_event__process_task(struct perf_tool *tool, | 193 | int perf_event__process_fork(struct perf_tool *tool, |
194 | union perf_event *event, | ||
195 | struct perf_sample *sample, | ||
196 | struct machine *machine); | ||
197 | int perf_event__process_exit(struct perf_tool *tool, | ||
195 | union perf_event *event, | 198 | union perf_event *event, |
196 | struct perf_sample *sample, | 199 | struct perf_sample *sample, |
197 | struct machine *machine); | 200 | struct machine *machine); |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 186b87730396..705293489e3c 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -52,15 +52,13 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
52 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 52 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
53 | struct perf_record_opts *opts) | 53 | struct perf_record_opts *opts) |
54 | { | 54 | { |
55 | struct perf_evsel *evsel, *first; | 55 | struct perf_evsel *evsel; |
56 | 56 | ||
57 | if (evlist->cpus->map[0] < 0) | 57 | if (evlist->cpus->map[0] < 0) |
58 | opts->no_inherit = true; | 58 | opts->no_inherit = true; |
59 | 59 | ||
60 | first = perf_evlist__first(evlist); | ||
61 | |||
62 | list_for_each_entry(evsel, &evlist->entries, node) { | 60 | list_for_each_entry(evsel, &evlist->entries, node) { |
63 | perf_evsel__config(evsel, opts, first); | 61 | perf_evsel__config(evsel, opts); |
64 | 62 | ||
65 | if (evlist->nr_entries > 1) | 63 | if (evlist->nr_entries > 1) |
66 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 64 | evsel->attr.sample_type |= PERF_SAMPLE_ID; |
@@ -224,6 +222,8 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
224 | 222 | ||
225 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 223 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { |
226 | list_for_each_entry(pos, &evlist->entries, node) { | 224 | list_for_each_entry(pos, &evlist->entries, node) { |
225 | if (perf_evsel__is_group_member(pos)) | ||
226 | continue; | ||
227 | for (thread = 0; thread < evlist->threads->nr; thread++) | 227 | for (thread = 0; thread < evlist->threads->nr; thread++) |
228 | ioctl(FD(pos, cpu, thread), | 228 | ioctl(FD(pos, cpu, thread), |
229 | PERF_EVENT_IOC_DISABLE, 0); | 229 | PERF_EVENT_IOC_DISABLE, 0); |
@@ -238,6 +238,8 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
238 | 238 | ||
239 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { | 239 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { |
240 | list_for_each_entry(pos, &evlist->entries, node) { | 240 | list_for_each_entry(pos, &evlist->entries, node) { |
241 | if (perf_evsel__is_group_member(pos)) | ||
242 | continue; | ||
241 | for (thread = 0; thread < evlist->threads->nr; thread++) | 243 | for (thread = 0; thread < evlist->threads->nr; thread++) |
242 | ioctl(FD(pos, cpu, thread), | 244 | ioctl(FD(pos, cpu, thread), |
243 | PERF_EVENT_IOC_ENABLE, 0); | 245 | PERF_EVENT_IOC_ENABLE, 0); |
@@ -325,8 +327,6 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
325 | 327 | ||
326 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 328 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
327 | { | 329 | { |
328 | /* XXX Move this to perf.c, making it generally available */ | ||
329 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
330 | struct perf_mmap *md = &evlist->mmap[idx]; | 330 | struct perf_mmap *md = &evlist->mmap[idx]; |
331 | unsigned int head = perf_mmap__read_head(md); | 331 | unsigned int head = perf_mmap__read_head(md); |
332 | unsigned int old = md->prev; | 332 | unsigned int old = md->prev; |
@@ -528,7 +528,6 @@ out_unmap: | |||
528 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 528 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
529 | bool overwrite) | 529 | bool overwrite) |
530 | { | 530 | { |
531 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
532 | struct perf_evsel *evsel; | 531 | struct perf_evsel *evsel; |
533 | const struct cpu_map *cpus = evlist->cpus; | 532 | const struct cpu_map *cpus = evlist->cpus; |
534 | const struct thread_map *threads = evlist->threads; | 533 | const struct thread_map *threads = evlist->threads; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d144d464ce39..1b16dd1edc8e 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -404,13 +404,40 @@ const char *perf_evsel__name(struct perf_evsel *evsel) | |||
404 | return evsel->name ?: "unknown"; | 404 | return evsel->name ?: "unknown"; |
405 | } | 405 | } |
406 | 406 | ||
407 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | 407 | /* |
408 | struct perf_evsel *first) | 408 | * The enable_on_exec/disabled value strategy: |
409 | * | ||
410 | * 1) For any type of traced program: | ||
411 | * - all independent events and group leaders are disabled | ||
412 | * - all group members are enabled | ||
413 | * | ||
414 | * Group members are ruled by group leaders. They need to | ||
415 | * be enabled, because the group scheduling relies on that. | ||
416 | * | ||
417 | * 2) For traced programs executed by perf: | ||
418 | * - all independent events and group leaders have | ||
419 | * enable_on_exec set | ||
420 | * - we don't specifically enable or disable any event during | ||
421 | * the record command | ||
422 | * | ||
423 | * Independent events and group leaders are initially disabled | ||
424 | * and get enabled by exec. Group members are ruled by group | ||
425 | * leaders as stated in 1). | ||
426 | * | ||
427 | * 3) For traced programs attached by perf (pid/tid): | ||
428 | * - we specifically enable or disable all events during | ||
429 | * the record command | ||
430 | * | ||
431 | * When attaching events to already running traced we | ||
432 | * enable/disable events specifically, as there's no | ||
433 | * initial traced exec call. | ||
434 | */ | ||
435 | void perf_evsel__config(struct perf_evsel *evsel, | ||
436 | struct perf_record_opts *opts) | ||
409 | { | 437 | { |
410 | struct perf_event_attr *attr = &evsel->attr; | 438 | struct perf_event_attr *attr = &evsel->attr; |
411 | int track = !evsel->idx; /* only the first counter needs these */ | 439 | int track = !evsel->idx; /* only the first counter needs these */ |
412 | 440 | ||
413 | attr->disabled = 1; | ||
414 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | 441 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
415 | attr->inherit = !opts->no_inherit; | 442 | attr->inherit = !opts->no_inherit; |
416 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 443 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
@@ -486,10 +513,21 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
486 | attr->mmap = track; | 513 | attr->mmap = track; |
487 | attr->comm = track; | 514 | attr->comm = track; |
488 | 515 | ||
489 | if (perf_target__none(&opts->target) && | 516 | /* |
490 | (!opts->group || evsel == first)) { | 517 | * XXX see the function comment above |
518 | * | ||
519 | * Disabling only independent events or group leaders, | ||
520 | * keeping group members enabled. | ||
521 | */ | ||
522 | if (!perf_evsel__is_group_member(evsel)) | ||
523 | attr->disabled = 1; | ||
524 | |||
525 | /* | ||
526 | * Setting enable_on_exec for independent events and | ||
527 | * group leaders for traced executed by perf. | ||
528 | */ | ||
529 | if (perf_target__none(&opts->target) && !perf_evsel__is_group_member(evsel)) | ||
491 | attr->enable_on_exec = 1; | 530 | attr->enable_on_exec = 1; |
492 | } | ||
493 | } | 531 | } |
494 | 532 | ||
495 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 533 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
@@ -669,7 +707,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | |||
669 | struct perf_evsel *leader = evsel->leader; | 707 | struct perf_evsel *leader = evsel->leader; |
670 | int fd; | 708 | int fd; |
671 | 709 | ||
672 | if (!leader) | 710 | if (!perf_evsel__is_group_member(evsel)) |
673 | return -1; | 711 | return -1; |
674 | 712 | ||
675 | /* | 713 | /* |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index d99b476ef37c..3d2b8017438c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -93,8 +93,7 @@ void perf_evsel__exit(struct perf_evsel *evsel); | |||
93 | void perf_evsel__delete(struct perf_evsel *evsel); | 93 | void perf_evsel__delete(struct perf_evsel *evsel); |
94 | 94 | ||
95 | void perf_evsel__config(struct perf_evsel *evsel, | 95 | void perf_evsel__config(struct perf_evsel *evsel, |
96 | struct perf_record_opts *opts, | 96 | struct perf_record_opts *opts); |
97 | struct perf_evsel *first); | ||
98 | 97 | ||
99 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); | 98 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); |
100 | 99 | ||
@@ -226,4 +225,9 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
226 | { | 225 | { |
227 | return list_entry(evsel->node.next, struct perf_evsel, node); | 226 | return list_entry(evsel->node.next, struct perf_evsel, node); |
228 | } | 227 | } |
228 | |||
229 | static inline bool perf_evsel__is_group_member(const struct perf_evsel *evsel) | ||
230 | { | ||
231 | return evsel->leader != NULL; | ||
232 | } | ||
229 | #endif /* __PERF_EVSEL_H */ | 233 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 566b84c695c8..b7da4634a047 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include "pmu.h" | 23 | #include "pmu.h" |
24 | #include "vdso.h" | 24 | #include "vdso.h" |
25 | #include "strbuf.h" | 25 | #include "strbuf.h" |
26 | #include "build-id.h" | ||
26 | 27 | ||
27 | static bool no_buildid_cache = false; | 28 | static bool no_buildid_cache = false; |
28 | 29 | ||
@@ -2342,6 +2343,16 @@ static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph) | |||
2342 | return -1; | 2343 | return -1; |
2343 | } | 2344 | } |
2344 | 2345 | ||
2346 | bool is_perf_magic(u64 magic) | ||
2347 | { | ||
2348 | if (!memcmp(&magic, __perf_magic1, sizeof(magic)) | ||
2349 | || magic == __perf_magic2 | ||
2350 | || magic == __perf_magic2_sw) | ||
2351 | return true; | ||
2352 | |||
2353 | return false; | ||
2354 | } | ||
2355 | |||
2345 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, | 2356 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, |
2346 | bool is_pipe, struct perf_header *ph) | 2357 | bool is_pipe, struct perf_header *ph) |
2347 | { | 2358 | { |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 9bc00783f24f..20f0344accb1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -154,6 +154,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, | |||
154 | int perf_event__process_build_id(struct perf_tool *tool, | 154 | int perf_event__process_build_id(struct perf_tool *tool, |
155 | union perf_event *event, | 155 | union perf_event *event, |
156 | struct perf_session *session); | 156 | struct perf_session *session); |
157 | bool is_perf_magic(u64 magic); | ||
157 | 158 | ||
158 | /* | 159 | /* |
159 | * arch specific callback | 160 | * arch specific callback |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 277947a669b2..cb17e2a8c6ed 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -244,6 +244,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
244 | he->ms.map->referenced = true; | 244 | he->ms.map->referenced = true; |
245 | if (symbol_conf.use_callchain) | 245 | if (symbol_conf.use_callchain) |
246 | callchain_init(he->callchain); | 246 | callchain_init(he->callchain); |
247 | |||
248 | INIT_LIST_HEAD(&he->pairs.node); | ||
247 | } | 249 | } |
248 | 250 | ||
249 | return he; | 251 | return he; |
@@ -410,6 +412,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
410 | 412 | ||
411 | void hist_entry__free(struct hist_entry *he) | 413 | void hist_entry__free(struct hist_entry *he) |
412 | { | 414 | { |
415 | free(he->branch_info); | ||
413 | free(he); | 416 | free(he); |
414 | } | 417 | } |
415 | 418 | ||
@@ -713,3 +716,99 @@ void hists__inc_nr_events(struct hists *hists, u32 type) | |||
713 | ++hists->stats.nr_events[0]; | 716 | ++hists->stats.nr_events[0]; |
714 | ++hists->stats.nr_events[type]; | 717 | ++hists->stats.nr_events[type]; |
715 | } | 718 | } |
719 | |||
720 | static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | ||
721 | struct hist_entry *pair) | ||
722 | { | ||
723 | struct rb_node **p = &hists->entries.rb_node; | ||
724 | struct rb_node *parent = NULL; | ||
725 | struct hist_entry *he; | ||
726 | int cmp; | ||
727 | |||
728 | while (*p != NULL) { | ||
729 | parent = *p; | ||
730 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
731 | |||
732 | cmp = hist_entry__cmp(pair, he); | ||
733 | |||
734 | if (!cmp) | ||
735 | goto out; | ||
736 | |||
737 | if (cmp < 0) | ||
738 | p = &(*p)->rb_left; | ||
739 | else | ||
740 | p = &(*p)->rb_right; | ||
741 | } | ||
742 | |||
743 | he = hist_entry__new(pair); | ||
744 | if (he) { | ||
745 | memset(&he->stat, 0, sizeof(he->stat)); | ||
746 | he->hists = hists; | ||
747 | rb_link_node(&he->rb_node, parent, p); | ||
748 | rb_insert_color(&he->rb_node, &hists->entries); | ||
749 | hists__inc_nr_entries(hists, he); | ||
750 | } | ||
751 | out: | ||
752 | return he; | ||
753 | } | ||
754 | |||
755 | static struct hist_entry *hists__find_entry(struct hists *hists, | ||
756 | struct hist_entry *he) | ||
757 | { | ||
758 | struct rb_node *n = hists->entries.rb_node; | ||
759 | |||
760 | while (n) { | ||
761 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | ||
762 | int64_t cmp = hist_entry__cmp(he, iter); | ||
763 | |||
764 | if (cmp < 0) | ||
765 | n = n->rb_left; | ||
766 | else if (cmp > 0) | ||
767 | n = n->rb_right; | ||
768 | else | ||
769 | return iter; | ||
770 | } | ||
771 | |||
772 | return NULL; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * Look for pairs to link to the leader buckets (hist_entries): | ||
777 | */ | ||
778 | void hists__match(struct hists *leader, struct hists *other) | ||
779 | { | ||
780 | struct rb_node *nd; | ||
781 | struct hist_entry *pos, *pair; | ||
782 | |||
783 | for (nd = rb_first(&leader->entries); nd; nd = rb_next(nd)) { | ||
784 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
785 | pair = hists__find_entry(other, pos); | ||
786 | |||
787 | if (pair) | ||
788 | hist__entry_add_pair(pos, pair); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | /* | ||
793 | * Look for entries in the other hists that are not present in the leader, if | ||
794 | * we find them, just add a dummy entry on the leader hists, with period=0, | ||
795 | * nr_events=0, to serve as the list header. | ||
796 | */ | ||
797 | int hists__link(struct hists *leader, struct hists *other) | ||
798 | { | ||
799 | struct rb_node *nd; | ||
800 | struct hist_entry *pos, *pair; | ||
801 | |||
802 | for (nd = rb_first(&other->entries); nd; nd = rb_next(nd)) { | ||
803 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
804 | |||
805 | if (!hist_entry__has_pairs(pos)) { | ||
806 | pair = hists__add_dummy_entry(leader, pos); | ||
807 | if (pair == NULL) | ||
808 | return -1; | ||
809 | hist__entry_add_pair(pair, pos); | ||
810 | } | ||
811 | } | ||
812 | |||
813 | return 0; | ||
814 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 66cb31fe81d2..8b091a51e4a2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | 5 | #include <pthread.h> |
6 | #include "callchain.h" | 6 | #include "callchain.h" |
7 | #include "header.h" | ||
7 | 8 | ||
8 | extern struct callchain_param callchain_param; | 9 | extern struct callchain_param callchain_param; |
9 | 10 | ||
@@ -114,6 +115,9 @@ bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | |||
114 | void hists__reset_col_len(struct hists *hists); | 115 | void hists__reset_col_len(struct hists *hists); |
115 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | 116 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); |
116 | 117 | ||
118 | void hists__match(struct hists *leader, struct hists *other); | ||
119 | int hists__link(struct hists *leader, struct hists *other); | ||
120 | |||
117 | struct perf_hpp { | 121 | struct perf_hpp { |
118 | char *buf; | 122 | char *buf; |
119 | size_t size; | 123 | size_t size; |
@@ -140,8 +144,12 @@ enum { | |||
140 | PERF_HPP__OVERHEAD_GUEST_US, | 144 | PERF_HPP__OVERHEAD_GUEST_US, |
141 | PERF_HPP__SAMPLES, | 145 | PERF_HPP__SAMPLES, |
142 | PERF_HPP__PERIOD, | 146 | PERF_HPP__PERIOD, |
147 | PERF_HPP__PERIOD_BASELINE, | ||
143 | PERF_HPP__DELTA, | 148 | PERF_HPP__DELTA, |
149 | PERF_HPP__RATIO, | ||
150 | PERF_HPP__WEIGHTED_DIFF, | ||
144 | PERF_HPP__DISPL, | 151 | PERF_HPP__DISPL, |
152 | PERF_HPP__FORMULA, | ||
145 | 153 | ||
146 | PERF_HPP__MAX_INDEX | 154 | PERF_HPP__MAX_INDEX |
147 | }; | 155 | }; |
@@ -153,21 +161,27 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | |||
153 | 161 | ||
154 | struct perf_evlist; | 162 | struct perf_evlist; |
155 | 163 | ||
164 | struct hist_browser_timer { | ||
165 | void (*timer)(void *arg); | ||
166 | void *arg; | ||
167 | int refresh; | ||
168 | }; | ||
169 | |||
156 | #ifdef NEWT_SUPPORT | 170 | #ifdef NEWT_SUPPORT |
157 | #include "../ui/keysyms.h" | 171 | #include "../ui/keysyms.h" |
158 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 172 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, |
159 | void(*timer)(void *arg), void *arg, int delay_secs); | 173 | struct hist_browser_timer *hbt); |
160 | 174 | ||
161 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 175 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
162 | void(*timer)(void *arg), void *arg, | 176 | struct hist_browser_timer *hbt, |
163 | int refresh); | 177 | struct perf_session_env *env); |
178 | int script_browse(const char *script_opt); | ||
164 | #else | 179 | #else |
165 | static inline | 180 | static inline |
166 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | 181 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
167 | const char *help __maybe_unused, | 182 | const char *help __maybe_unused, |
168 | void(*timer)(void *arg) __maybe_unused, | 183 | struct hist_browser_timer *hbt __maybe_unused, |
169 | void *arg __maybe_unused, | 184 | struct perf_session_env *env __maybe_unused) |
170 | int refresh __maybe_unused) | ||
171 | { | 185 | { |
172 | return 0; | 186 | return 0; |
173 | } | 187 | } |
@@ -175,28 +189,29 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
175 | static inline int hist_entry__tui_annotate(struct hist_entry *self | 189 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
176 | __maybe_unused, | 190 | __maybe_unused, |
177 | int evidx __maybe_unused, | 191 | int evidx __maybe_unused, |
178 | void(*timer)(void *arg) | 192 | struct hist_browser_timer *hbt |
179 | __maybe_unused, | 193 | __maybe_unused) |
180 | void *arg __maybe_unused, | ||
181 | int delay_secs __maybe_unused) | ||
182 | { | 194 | { |
183 | return 0; | 195 | return 0; |
184 | } | 196 | } |
197 | |||
198 | static inline int script_browse(const char *script_opt __maybe_unused) | ||
199 | { | ||
200 | return 0; | ||
201 | } | ||
202 | |||
185 | #define K_LEFT -1 | 203 | #define K_LEFT -1 |
186 | #define K_RIGHT -2 | 204 | #define K_RIGHT -2 |
187 | #endif | 205 | #endif |
188 | 206 | ||
189 | #ifdef GTK2_SUPPORT | 207 | #ifdef GTK2_SUPPORT |
190 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | 208 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, |
191 | void(*timer)(void *arg), void *arg, | 209 | struct hist_browser_timer *hbt __maybe_unused); |
192 | int refresh); | ||
193 | #else | 210 | #else |
194 | static inline | 211 | static inline |
195 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | 212 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, |
196 | const char *help __maybe_unused, | 213 | const char *help __maybe_unused, |
197 | void(*timer)(void *arg) __maybe_unused, | 214 | struct hist_browser_timer *hbt __maybe_unused) |
198 | void *arg __maybe_unused, | ||
199 | int refresh __maybe_unused) | ||
200 | { | 215 | { |
201 | return 0; | 216 | return 0; |
202 | } | 217 | } |
@@ -204,4 +219,8 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
204 | 219 | ||
205 | unsigned int hists__sort_list_width(struct hists *self); | 220 | unsigned int hists__sort_list_width(struct hists *self); |
206 | 221 | ||
222 | double perf_diff__compute_delta(struct hist_entry *he); | ||
223 | double perf_diff__compute_ratio(struct hist_entry *he); | ||
224 | s64 perf_diff__compute_wdiff(struct hist_entry *he); | ||
225 | int perf_diff__formula(char *buf, size_t size, struct hist_entry *he); | ||
207 | #endif /* __PERF_HIST_H */ | 226 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c new file mode 100644 index 000000000000..1f09d0581e6b --- /dev/null +++ b/tools/perf/util/machine.c | |||
@@ -0,0 +1,464 @@ | |||
1 | #include "debug.h" | ||
2 | #include "event.h" | ||
3 | #include "machine.h" | ||
4 | #include "map.h" | ||
5 | #include "strlist.h" | ||
6 | #include "thread.h" | ||
7 | #include <stdbool.h> | ||
8 | |||
9 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | ||
10 | { | ||
11 | map_groups__init(&machine->kmaps); | ||
12 | RB_CLEAR_NODE(&machine->rb_node); | ||
13 | INIT_LIST_HEAD(&machine->user_dsos); | ||
14 | INIT_LIST_HEAD(&machine->kernel_dsos); | ||
15 | |||
16 | machine->threads = RB_ROOT; | ||
17 | INIT_LIST_HEAD(&machine->dead_threads); | ||
18 | machine->last_match = NULL; | ||
19 | |||
20 | machine->kmaps.machine = machine; | ||
21 | machine->pid = pid; | ||
22 | |||
23 | machine->root_dir = strdup(root_dir); | ||
24 | if (machine->root_dir == NULL) | ||
25 | return -ENOMEM; | ||
26 | |||
27 | if (pid != HOST_KERNEL_ID) { | ||
28 | struct thread *thread = machine__findnew_thread(machine, pid); | ||
29 | char comm[64]; | ||
30 | |||
31 | if (thread == NULL) | ||
32 | return -ENOMEM; | ||
33 | |||
34 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); | ||
35 | thread__set_comm(thread, comm); | ||
36 | } | ||
37 | |||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | static void dsos__delete(struct list_head *dsos) | ||
42 | { | ||
43 | struct dso *pos, *n; | ||
44 | |||
45 | list_for_each_entry_safe(pos, n, dsos, node) { | ||
46 | list_del(&pos->node); | ||
47 | dso__delete(pos); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | void machine__exit(struct machine *machine) | ||
52 | { | ||
53 | map_groups__exit(&machine->kmaps); | ||
54 | dsos__delete(&machine->user_dsos); | ||
55 | dsos__delete(&machine->kernel_dsos); | ||
56 | free(machine->root_dir); | ||
57 | machine->root_dir = NULL; | ||
58 | } | ||
59 | |||
60 | void machine__delete(struct machine *machine) | ||
61 | { | ||
62 | machine__exit(machine); | ||
63 | free(machine); | ||
64 | } | ||
65 | |||
66 | struct machine *machines__add(struct rb_root *machines, pid_t pid, | ||
67 | const char *root_dir) | ||
68 | { | ||
69 | struct rb_node **p = &machines->rb_node; | ||
70 | struct rb_node *parent = NULL; | ||
71 | struct machine *pos, *machine = malloc(sizeof(*machine)); | ||
72 | |||
73 | if (machine == NULL) | ||
74 | return NULL; | ||
75 | |||
76 | if (machine__init(machine, root_dir, pid) != 0) { | ||
77 | free(machine); | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | while (*p != NULL) { | ||
82 | parent = *p; | ||
83 | pos = rb_entry(parent, struct machine, rb_node); | ||
84 | if (pid < pos->pid) | ||
85 | p = &(*p)->rb_left; | ||
86 | else | ||
87 | p = &(*p)->rb_right; | ||
88 | } | ||
89 | |||
90 | rb_link_node(&machine->rb_node, parent, p); | ||
91 | rb_insert_color(&machine->rb_node, machines); | ||
92 | |||
93 | return machine; | ||
94 | } | ||
95 | |||
96 | struct machine *machines__find(struct rb_root *machines, pid_t pid) | ||
97 | { | ||
98 | struct rb_node **p = &machines->rb_node; | ||
99 | struct rb_node *parent = NULL; | ||
100 | struct machine *machine; | ||
101 | struct machine *default_machine = NULL; | ||
102 | |||
103 | while (*p != NULL) { | ||
104 | parent = *p; | ||
105 | machine = rb_entry(parent, struct machine, rb_node); | ||
106 | if (pid < machine->pid) | ||
107 | p = &(*p)->rb_left; | ||
108 | else if (pid > machine->pid) | ||
109 | p = &(*p)->rb_right; | ||
110 | else | ||
111 | return machine; | ||
112 | if (!machine->pid) | ||
113 | default_machine = machine; | ||
114 | } | ||
115 | |||
116 | return default_machine; | ||
117 | } | ||
118 | |||
119 | struct machine *machines__findnew(struct rb_root *machines, pid_t pid) | ||
120 | { | ||
121 | char path[PATH_MAX]; | ||
122 | const char *root_dir = ""; | ||
123 | struct machine *machine = machines__find(machines, pid); | ||
124 | |||
125 | if (machine && (machine->pid == pid)) | ||
126 | goto out; | ||
127 | |||
128 | if ((pid != HOST_KERNEL_ID) && | ||
129 | (pid != DEFAULT_GUEST_KERNEL_ID) && | ||
130 | (symbol_conf.guestmount)) { | ||
131 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); | ||
132 | if (access(path, R_OK)) { | ||
133 | static struct strlist *seen; | ||
134 | |||
135 | if (!seen) | ||
136 | seen = strlist__new(true, NULL); | ||
137 | |||
138 | if (!strlist__has_entry(seen, path)) { | ||
139 | pr_err("Can't access file %s\n", path); | ||
140 | strlist__add(seen, path); | ||
141 | } | ||
142 | machine = NULL; | ||
143 | goto out; | ||
144 | } | ||
145 | root_dir = path; | ||
146 | } | ||
147 | |||
148 | machine = machines__add(machines, pid, root_dir); | ||
149 | out: | ||
150 | return machine; | ||
151 | } | ||
152 | |||
153 | void machines__process(struct rb_root *machines, | ||
154 | machine__process_t process, void *data) | ||
155 | { | ||
156 | struct rb_node *nd; | ||
157 | |||
158 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
159 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
160 | process(pos, data); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size) | ||
165 | { | ||
166 | if (machine__is_host(machine)) | ||
167 | snprintf(bf, size, "[%s]", "kernel.kallsyms"); | ||
168 | else if (machine__is_default_guest(machine)) | ||
169 | snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); | ||
170 | else { | ||
171 | snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", | ||
172 | machine->pid); | ||
173 | } | ||
174 | |||
175 | return bf; | ||
176 | } | ||
177 | |||
178 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) | ||
179 | { | ||
180 | struct rb_node *node; | ||
181 | struct machine *machine; | ||
182 | |||
183 | for (node = rb_first(machines); node; node = rb_next(node)) { | ||
184 | machine = rb_entry(node, struct machine, rb_node); | ||
185 | machine->id_hdr_size = id_hdr_size; | ||
186 | } | ||
187 | |||
188 | return; | ||
189 | } | ||
190 | |||
191 | static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, | ||
192 | bool create) | ||
193 | { | ||
194 | struct rb_node **p = &machine->threads.rb_node; | ||
195 | struct rb_node *parent = NULL; | ||
196 | struct thread *th; | ||
197 | |||
198 | /* | ||
199 | * Font-end cache - PID lookups come in blocks, | ||
200 | * so most of the time we dont have to look up | ||
201 | * the full rbtree: | ||
202 | */ | ||
203 | if (machine->last_match && machine->last_match->pid == pid) | ||
204 | return machine->last_match; | ||
205 | |||
206 | while (*p != NULL) { | ||
207 | parent = *p; | ||
208 | th = rb_entry(parent, struct thread, rb_node); | ||
209 | |||
210 | if (th->pid == pid) { | ||
211 | machine->last_match = th; | ||
212 | return th; | ||
213 | } | ||
214 | |||
215 | if (pid < th->pid) | ||
216 | p = &(*p)->rb_left; | ||
217 | else | ||
218 | p = &(*p)->rb_right; | ||
219 | } | ||
220 | |||
221 | if (!create) | ||
222 | return NULL; | ||
223 | |||
224 | th = thread__new(pid); | ||
225 | if (th != NULL) { | ||
226 | rb_link_node(&th->rb_node, parent, p); | ||
227 | rb_insert_color(&th->rb_node, &machine->threads); | ||
228 | machine->last_match = th; | ||
229 | } | ||
230 | |||
231 | return th; | ||
232 | } | ||
233 | |||
234 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid) | ||
235 | { | ||
236 | return __machine__findnew_thread(machine, pid, true); | ||
237 | } | ||
238 | |||
239 | struct thread *machine__find_thread(struct machine *machine, pid_t pid) | ||
240 | { | ||
241 | return __machine__findnew_thread(machine, pid, false); | ||
242 | } | ||
243 | |||
244 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | ||
245 | { | ||
246 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | ||
247 | |||
248 | if (dump_trace) | ||
249 | perf_event__fprintf_comm(event, stdout); | ||
250 | |||
251 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | ||
252 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | int machine__process_lost_event(struct machine *machine __maybe_unused, | ||
260 | union perf_event *event) | ||
261 | { | ||
262 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | ||
263 | event->lost.id, event->lost.lost); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static void machine__set_kernel_mmap_len(struct machine *machine, | ||
268 | union perf_event *event) | ||
269 | { | ||
270 | int i; | ||
271 | |||
272 | for (i = 0; i < MAP__NR_TYPES; i++) { | ||
273 | machine->vmlinux_maps[i]->start = event->mmap.start; | ||
274 | machine->vmlinux_maps[i]->end = (event->mmap.start + | ||
275 | event->mmap.len); | ||
276 | /* | ||
277 | * Be a bit paranoid here, some perf.data file came with | ||
278 | * a zero sized synthesized MMAP event for the kernel. | ||
279 | */ | ||
280 | if (machine->vmlinux_maps[i]->end == 0) | ||
281 | machine->vmlinux_maps[i]->end = ~0ULL; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | static int machine__process_kernel_mmap_event(struct machine *machine, | ||
286 | union perf_event *event) | ||
287 | { | ||
288 | struct map *map; | ||
289 | char kmmap_prefix[PATH_MAX]; | ||
290 | enum dso_kernel_type kernel_type; | ||
291 | bool is_kernel_mmap; | ||
292 | |||
293 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | ||
294 | if (machine__is_host(machine)) | ||
295 | kernel_type = DSO_TYPE_KERNEL; | ||
296 | else | ||
297 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
298 | |||
299 | is_kernel_mmap = memcmp(event->mmap.filename, | ||
300 | kmmap_prefix, | ||
301 | strlen(kmmap_prefix) - 1) == 0; | ||
302 | if (event->mmap.filename[0] == '/' || | ||
303 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | ||
304 | |||
305 | char short_module_name[1024]; | ||
306 | char *name, *dot; | ||
307 | |||
308 | if (event->mmap.filename[0] == '/') { | ||
309 | name = strrchr(event->mmap.filename, '/'); | ||
310 | if (name == NULL) | ||
311 | goto out_problem; | ||
312 | |||
313 | ++name; /* skip / */ | ||
314 | dot = strrchr(name, '.'); | ||
315 | if (dot == NULL) | ||
316 | goto out_problem; | ||
317 | snprintf(short_module_name, sizeof(short_module_name), | ||
318 | "[%.*s]", (int)(dot - name), name); | ||
319 | strxfrchar(short_module_name, '-', '_'); | ||
320 | } else | ||
321 | strcpy(short_module_name, event->mmap.filename); | ||
322 | |||
323 | map = machine__new_module(machine, event->mmap.start, | ||
324 | event->mmap.filename); | ||
325 | if (map == NULL) | ||
326 | goto out_problem; | ||
327 | |||
328 | name = strdup(short_module_name); | ||
329 | if (name == NULL) | ||
330 | goto out_problem; | ||
331 | |||
332 | map->dso->short_name = name; | ||
333 | map->dso->sname_alloc = 1; | ||
334 | map->end = map->start + event->mmap.len; | ||
335 | } else if (is_kernel_mmap) { | ||
336 | const char *symbol_name = (event->mmap.filename + | ||
337 | strlen(kmmap_prefix)); | ||
338 | /* | ||
339 | * Should be there already, from the build-id table in | ||
340 | * the header. | ||
341 | */ | ||
342 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | ||
343 | kmmap_prefix); | ||
344 | if (kernel == NULL) | ||
345 | goto out_problem; | ||
346 | |||
347 | kernel->kernel = kernel_type; | ||
348 | if (__machine__create_kernel_maps(machine, kernel) < 0) | ||
349 | goto out_problem; | ||
350 | |||
351 | machine__set_kernel_mmap_len(machine, event); | ||
352 | |||
353 | /* | ||
354 | * Avoid using a zero address (kptr_restrict) for the ref reloc | ||
355 | * symbol. Effectively having zero here means that at record | ||
356 | * time /proc/sys/kernel/kptr_restrict was non zero. | ||
357 | */ | ||
358 | if (event->mmap.pgoff != 0) { | ||
359 | maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | ||
360 | symbol_name, | ||
361 | event->mmap.pgoff); | ||
362 | } | ||
363 | |||
364 | if (machine__is_default_guest(machine)) { | ||
365 | /* | ||
366 | * preload dso of guest kernel and modules | ||
367 | */ | ||
368 | dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], | ||
369 | NULL); | ||
370 | } | ||
371 | } | ||
372 | return 0; | ||
373 | out_problem: | ||
374 | return -1; | ||
375 | } | ||
376 | |||
377 | int machine__process_mmap_event(struct machine *machine, union perf_event *event) | ||
378 | { | ||
379 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
380 | struct thread *thread; | ||
381 | struct map *map; | ||
382 | int ret = 0; | ||
383 | |||
384 | if (dump_trace) | ||
385 | perf_event__fprintf_mmap(event, stdout); | ||
386 | |||
387 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
388 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
389 | ret = machine__process_kernel_mmap_event(machine, event); | ||
390 | if (ret < 0) | ||
391 | goto out_problem; | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | thread = machine__findnew_thread(machine, event->mmap.pid); | ||
396 | if (thread == NULL) | ||
397 | goto out_problem; | ||
398 | map = map__new(&machine->user_dsos, event->mmap.start, | ||
399 | event->mmap.len, event->mmap.pgoff, | ||
400 | event->mmap.pid, event->mmap.filename, | ||
401 | MAP__FUNCTION); | ||
402 | if (map == NULL) | ||
403 | goto out_problem; | ||
404 | |||
405 | thread__insert_map(thread, map); | ||
406 | return 0; | ||
407 | |||
408 | out_problem: | ||
409 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | ||
414 | { | ||
415 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | ||
416 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | ||
417 | |||
418 | if (dump_trace) | ||
419 | perf_event__fprintf_task(event, stdout); | ||
420 | |||
421 | if (thread == NULL || parent == NULL || | ||
422 | thread__fork(thread, parent) < 0) { | ||
423 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
424 | return -1; | ||
425 | } | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | ||
431 | { | ||
432 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | ||
433 | |||
434 | if (dump_trace) | ||
435 | perf_event__fprintf_task(event, stdout); | ||
436 | |||
437 | if (thread != NULL) | ||
438 | machine__remove_thread(machine, thread); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | int machine__process_event(struct machine *machine, union perf_event *event) | ||
444 | { | ||
445 | int ret; | ||
446 | |||
447 | switch (event->header.type) { | ||
448 | case PERF_RECORD_COMM: | ||
449 | ret = machine__process_comm_event(machine, event); break; | ||
450 | case PERF_RECORD_MMAP: | ||
451 | ret = machine__process_mmap_event(machine, event); break; | ||
452 | case PERF_RECORD_FORK: | ||
453 | ret = machine__process_fork_event(machine, event); break; | ||
454 | case PERF_RECORD_EXIT: | ||
455 | ret = machine__process_exit_event(machine, event); break; | ||
456 | case PERF_RECORD_LOST: | ||
457 | ret = machine__process_lost_event(machine, event); break; | ||
458 | default: | ||
459 | ret = -1; | ||
460 | break; | ||
461 | } | ||
462 | |||
463 | return ret; | ||
464 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h new file mode 100644 index 000000000000..b7cde7467d55 --- /dev/null +++ b/tools/perf/util/machine.h | |||
@@ -0,0 +1,148 @@ | |||
1 | #ifndef __PERF_MACHINE_H | ||
2 | #define __PERF_MACHINE_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include <linux/rbtree.h> | ||
6 | #include "map.h" | ||
7 | |||
8 | struct branch_stack; | ||
9 | struct perf_evsel; | ||
10 | struct perf_sample; | ||
11 | struct symbol; | ||
12 | struct thread; | ||
13 | union perf_event; | ||
14 | |||
15 | /* Native host kernel uses -1 as pid index in machine */ | ||
16 | #define HOST_KERNEL_ID (-1) | ||
17 | #define DEFAULT_GUEST_KERNEL_ID (0) | ||
18 | |||
19 | struct machine { | ||
20 | struct rb_node rb_node; | ||
21 | pid_t pid; | ||
22 | u16 id_hdr_size; | ||
23 | char *root_dir; | ||
24 | struct rb_root threads; | ||
25 | struct list_head dead_threads; | ||
26 | struct thread *last_match; | ||
27 | struct list_head user_dsos; | ||
28 | struct list_head kernel_dsos; | ||
29 | struct map_groups kmaps; | ||
30 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
31 | }; | ||
32 | |||
33 | static inline | ||
34 | struct map *machine__kernel_map(struct machine *machine, enum map_type type) | ||
35 | { | ||
36 | return machine->vmlinux_maps[type]; | ||
37 | } | ||
38 | |||
39 | struct thread *machine__find_thread(struct machine *machine, pid_t pid); | ||
40 | |||
41 | int machine__process_comm_event(struct machine *machine, union perf_event *event); | ||
42 | int machine__process_exit_event(struct machine *machine, union perf_event *event); | ||
43 | int machine__process_fork_event(struct machine *machine, union perf_event *event); | ||
44 | int machine__process_lost_event(struct machine *machine, union perf_event *event); | ||
45 | int machine__process_mmap_event(struct machine *machine, union perf_event *event); | ||
46 | int machine__process_event(struct machine *machine, union perf_event *event); | ||
47 | |||
48 | typedef void (*machine__process_t)(struct machine *machine, void *data); | ||
49 | |||
50 | void machines__process(struct rb_root *machines, | ||
51 | machine__process_t process, void *data); | ||
52 | |||
53 | struct machine *machines__add(struct rb_root *machines, pid_t pid, | ||
54 | const char *root_dir); | ||
55 | struct machine *machines__find_host(struct rb_root *machines); | ||
56 | struct machine *machines__find(struct rb_root *machines, pid_t pid); | ||
57 | struct machine *machines__findnew(struct rb_root *machines, pid_t pid); | ||
58 | |||
59 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size); | ||
60 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | ||
61 | |||
62 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | ||
63 | void machine__exit(struct machine *machine); | ||
64 | void machine__delete(struct machine *machine); | ||
65 | |||
66 | |||
67 | struct branch_info *machine__resolve_bstack(struct machine *machine, | ||
68 | struct thread *thread, | ||
69 | struct branch_stack *bs); | ||
70 | int machine__resolve_callchain(struct machine *machine, | ||
71 | struct perf_evsel *evsel, | ||
72 | struct thread *thread, | ||
73 | struct perf_sample *sample, | ||
74 | struct symbol **parent); | ||
75 | |||
76 | /* | ||
77 | * Default guest kernel is defined by parameter --guestkallsyms | ||
78 | * and --guestmodules | ||
79 | */ | ||
80 | static inline bool machine__is_default_guest(struct machine *machine) | ||
81 | { | ||
82 | return machine ? machine->pid == DEFAULT_GUEST_KERNEL_ID : false; | ||
83 | } | ||
84 | |||
85 | static inline bool machine__is_host(struct machine *machine) | ||
86 | { | ||
87 | return machine ? machine->pid == HOST_KERNEL_ID : false; | ||
88 | } | ||
89 | |||
90 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | ||
91 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
92 | |||
93 | size_t machine__fprintf(struct machine *machine, FILE *fp); | ||
94 | |||
95 | static inline | ||
96 | struct symbol *machine__find_kernel_symbol(struct machine *machine, | ||
97 | enum map_type type, u64 addr, | ||
98 | struct map **mapp, | ||
99 | symbol_filter_t filter) | ||
100 | { | ||
101 | return map_groups__find_symbol(&machine->kmaps, type, addr, | ||
102 | mapp, filter); | ||
103 | } | ||
104 | |||
105 | static inline | ||
106 | struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr, | ||
107 | struct map **mapp, | ||
108 | symbol_filter_t filter) | ||
109 | { | ||
110 | return machine__find_kernel_symbol(machine, MAP__FUNCTION, addr, | ||
111 | mapp, filter); | ||
112 | } | ||
113 | |||
114 | static inline | ||
115 | struct symbol *machine__find_kernel_function_by_name(struct machine *machine, | ||
116 | const char *name, | ||
117 | struct map **mapp, | ||
118 | symbol_filter_t filter) | ||
119 | { | ||
120 | return map_groups__find_function_by_name(&machine->kmaps, name, mapp, | ||
121 | filter); | ||
122 | } | ||
123 | |||
124 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
125 | const char *filename); | ||
126 | |||
127 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
128 | enum map_type type, symbol_filter_t filter); | ||
129 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
130 | symbol_filter_t filter); | ||
131 | |||
132 | size_t machine__fprintf_dsos_buildid(struct machine *machine, | ||
133 | FILE *fp, bool with_hits); | ||
134 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp); | ||
135 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | ||
136 | FILE *fp, bool with_hits); | ||
137 | |||
138 | void machine__destroy_kernel_maps(struct machine *machine); | ||
139 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | ||
140 | int machine__create_kernel_maps(struct machine *machine); | ||
141 | |||
142 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid); | ||
143 | int machines__create_guest_kernel_maps(struct rb_root *machines); | ||
144 | void machines__destroy_guest_kernel_maps(struct rb_root *machines); | ||
145 | |||
146 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | ||
147 | |||
148 | #endif /* __PERF_MACHINE_H */ | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 6109fa4d14cd..0328d45c4f2a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "thread.h" | 10 | #include "thread.h" |
11 | #include "strlist.h" | 11 | #include "strlist.h" |
12 | #include "vdso.h" | 12 | #include "vdso.h" |
13 | #include "build-id.h" | ||
13 | 14 | ||
14 | const char *map_type__name[MAP__NR_TYPES] = { | 15 | const char *map_type__name[MAP__NR_TYPES] = { |
15 | [MAP__FUNCTION] = "Functions", | 16 | [MAP__FUNCTION] = "Functions", |
@@ -23,7 +24,7 @@ static inline int is_anon_memory(const char *filename) | |||
23 | 24 | ||
24 | static inline int is_no_dso_memory(const char *filename) | 25 | static inline int is_no_dso_memory(const char *filename) |
25 | { | 26 | { |
26 | return !strcmp(filename, "[stack]") || | 27 | return !strncmp(filename, "[stack", 6) || |
27 | !strcmp(filename, "[heap]"); | 28 | !strcmp(filename, "[heap]"); |
28 | } | 29 | } |
29 | 30 | ||
@@ -589,182 +590,3 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
589 | 590 | ||
590 | return NULL; | 591 | return NULL; |
591 | } | 592 | } |
592 | |||
593 | int machine__init(struct machine *self, const char *root_dir, pid_t pid) | ||
594 | { | ||
595 | map_groups__init(&self->kmaps); | ||
596 | RB_CLEAR_NODE(&self->rb_node); | ||
597 | INIT_LIST_HEAD(&self->user_dsos); | ||
598 | INIT_LIST_HEAD(&self->kernel_dsos); | ||
599 | |||
600 | self->threads = RB_ROOT; | ||
601 | INIT_LIST_HEAD(&self->dead_threads); | ||
602 | self->last_match = NULL; | ||
603 | |||
604 | self->kmaps.machine = self; | ||
605 | self->pid = pid; | ||
606 | self->root_dir = strdup(root_dir); | ||
607 | if (self->root_dir == NULL) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | if (pid != HOST_KERNEL_ID) { | ||
611 | struct thread *thread = machine__findnew_thread(self, pid); | ||
612 | char comm[64]; | ||
613 | |||
614 | if (thread == NULL) | ||
615 | return -ENOMEM; | ||
616 | |||
617 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); | ||
618 | thread__set_comm(thread, comm); | ||
619 | } | ||
620 | |||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static void dsos__delete(struct list_head *self) | ||
625 | { | ||
626 | struct dso *pos, *n; | ||
627 | |||
628 | list_for_each_entry_safe(pos, n, self, node) { | ||
629 | list_del(&pos->node); | ||
630 | dso__delete(pos); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | void machine__exit(struct machine *self) | ||
635 | { | ||
636 | map_groups__exit(&self->kmaps); | ||
637 | dsos__delete(&self->user_dsos); | ||
638 | dsos__delete(&self->kernel_dsos); | ||
639 | free(self->root_dir); | ||
640 | self->root_dir = NULL; | ||
641 | } | ||
642 | |||
643 | void machine__delete(struct machine *self) | ||
644 | { | ||
645 | machine__exit(self); | ||
646 | free(self); | ||
647 | } | ||
648 | |||
649 | struct machine *machines__add(struct rb_root *self, pid_t pid, | ||
650 | const char *root_dir) | ||
651 | { | ||
652 | struct rb_node **p = &self->rb_node; | ||
653 | struct rb_node *parent = NULL; | ||
654 | struct machine *pos, *machine = malloc(sizeof(*machine)); | ||
655 | |||
656 | if (!machine) | ||
657 | return NULL; | ||
658 | |||
659 | if (machine__init(machine, root_dir, pid) != 0) { | ||
660 | free(machine); | ||
661 | return NULL; | ||
662 | } | ||
663 | |||
664 | while (*p != NULL) { | ||
665 | parent = *p; | ||
666 | pos = rb_entry(parent, struct machine, rb_node); | ||
667 | if (pid < pos->pid) | ||
668 | p = &(*p)->rb_left; | ||
669 | else | ||
670 | p = &(*p)->rb_right; | ||
671 | } | ||
672 | |||
673 | rb_link_node(&machine->rb_node, parent, p); | ||
674 | rb_insert_color(&machine->rb_node, self); | ||
675 | |||
676 | return machine; | ||
677 | } | ||
678 | |||
679 | struct machine *machines__find(struct rb_root *self, pid_t pid) | ||
680 | { | ||
681 | struct rb_node **p = &self->rb_node; | ||
682 | struct rb_node *parent = NULL; | ||
683 | struct machine *machine; | ||
684 | struct machine *default_machine = NULL; | ||
685 | |||
686 | while (*p != NULL) { | ||
687 | parent = *p; | ||
688 | machine = rb_entry(parent, struct machine, rb_node); | ||
689 | if (pid < machine->pid) | ||
690 | p = &(*p)->rb_left; | ||
691 | else if (pid > machine->pid) | ||
692 | p = &(*p)->rb_right; | ||
693 | else | ||
694 | return machine; | ||
695 | if (!machine->pid) | ||
696 | default_machine = machine; | ||
697 | } | ||
698 | |||
699 | return default_machine; | ||
700 | } | ||
701 | |||
702 | struct machine *machines__findnew(struct rb_root *self, pid_t pid) | ||
703 | { | ||
704 | char path[PATH_MAX]; | ||
705 | const char *root_dir = ""; | ||
706 | struct machine *machine = machines__find(self, pid); | ||
707 | |||
708 | if (machine && (machine->pid == pid)) | ||
709 | goto out; | ||
710 | |||
711 | if ((pid != HOST_KERNEL_ID) && | ||
712 | (pid != DEFAULT_GUEST_KERNEL_ID) && | ||
713 | (symbol_conf.guestmount)) { | ||
714 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); | ||
715 | if (access(path, R_OK)) { | ||
716 | static struct strlist *seen; | ||
717 | |||
718 | if (!seen) | ||
719 | seen = strlist__new(true, NULL); | ||
720 | |||
721 | if (!strlist__has_entry(seen, path)) { | ||
722 | pr_err("Can't access file %s\n", path); | ||
723 | strlist__add(seen, path); | ||
724 | } | ||
725 | machine = NULL; | ||
726 | goto out; | ||
727 | } | ||
728 | root_dir = path; | ||
729 | } | ||
730 | |||
731 | machine = machines__add(self, pid, root_dir); | ||
732 | |||
733 | out: | ||
734 | return machine; | ||
735 | } | ||
736 | |||
737 | void machines__process(struct rb_root *self, machine__process_t process, void *data) | ||
738 | { | ||
739 | struct rb_node *nd; | ||
740 | |||
741 | for (nd = rb_first(self); nd; nd = rb_next(nd)) { | ||
742 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
743 | process(pos, data); | ||
744 | } | ||
745 | } | ||
746 | |||
747 | char *machine__mmap_name(struct machine *self, char *bf, size_t size) | ||
748 | { | ||
749 | if (machine__is_host(self)) | ||
750 | snprintf(bf, size, "[%s]", "kernel.kallsyms"); | ||
751 | else if (machine__is_default_guest(self)) | ||
752 | snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); | ||
753 | else | ||
754 | snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); | ||
755 | |||
756 | return bf; | ||
757 | } | ||
758 | |||
759 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) | ||
760 | { | ||
761 | struct rb_node *node; | ||
762 | struct machine *machine; | ||
763 | |||
764 | for (node = rb_first(machines); node; node = rb_next(node)) { | ||
765 | machine = rb_entry(node, struct machine, rb_node); | ||
766 | machine->id_hdr_size = id_hdr_size; | ||
767 | } | ||
768 | |||
769 | return; | ||
770 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index d2250fc97e25..bcb39e2a6965 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -57,30 +57,6 @@ struct map_groups { | |||
57 | struct machine *machine; | 57 | struct machine *machine; |
58 | }; | 58 | }; |
59 | 59 | ||
60 | /* Native host kernel uses -1 as pid index in machine */ | ||
61 | #define HOST_KERNEL_ID (-1) | ||
62 | #define DEFAULT_GUEST_KERNEL_ID (0) | ||
63 | |||
64 | struct machine { | ||
65 | struct rb_node rb_node; | ||
66 | pid_t pid; | ||
67 | u16 id_hdr_size; | ||
68 | char *root_dir; | ||
69 | struct rb_root threads; | ||
70 | struct list_head dead_threads; | ||
71 | struct thread *last_match; | ||
72 | struct list_head user_dsos; | ||
73 | struct list_head kernel_dsos; | ||
74 | struct map_groups kmaps; | ||
75 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
76 | }; | ||
77 | |||
78 | static inline | ||
79 | struct map *machine__kernel_map(struct machine *self, enum map_type type) | ||
80 | { | ||
81 | return self->vmlinux_maps[type]; | ||
82 | } | ||
83 | |||
84 | static inline struct kmap *map__kmap(struct map *self) | 60 | static inline struct kmap *map__kmap(struct map *self) |
85 | { | 61 | { |
86 | return (struct kmap *)(self + 1); | 62 | return (struct kmap *)(self + 1); |
@@ -143,44 +119,9 @@ int map_groups__clone(struct map_groups *mg, | |||
143 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); | 119 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); |
144 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); | 120 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); |
145 | 121 | ||
146 | typedef void (*machine__process_t)(struct machine *self, void *data); | ||
147 | |||
148 | void machines__process(struct rb_root *self, machine__process_t process, void *data); | ||
149 | struct machine *machines__add(struct rb_root *self, pid_t pid, | ||
150 | const char *root_dir); | ||
151 | struct machine *machines__find_host(struct rb_root *self); | ||
152 | struct machine *machines__find(struct rb_root *self, pid_t pid); | ||
153 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); | ||
154 | void machines__set_id_hdr_size(struct rb_root *self, u16 id_hdr_size); | ||
155 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); | ||
156 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); | ||
157 | void machine__exit(struct machine *self); | ||
158 | void machine__delete(struct machine *self); | ||
159 | |||
160 | struct perf_evsel; | ||
161 | struct perf_sample; | ||
162 | int machine__resolve_callchain(struct machine *machine, | ||
163 | struct perf_evsel *evsel, | ||
164 | struct thread *thread, | ||
165 | struct perf_sample *sample, | ||
166 | struct symbol **parent); | ||
167 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, | 122 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, |
168 | u64 addr); | 123 | u64 addr); |
169 | 124 | ||
170 | /* | ||
171 | * Default guest kernel is defined by parameter --guestkallsyms | ||
172 | * and --guestmodules | ||
173 | */ | ||
174 | static inline bool machine__is_default_guest(struct machine *self) | ||
175 | { | ||
176 | return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false; | ||
177 | } | ||
178 | |||
179 | static inline bool machine__is_host(struct machine *self) | ||
180 | { | ||
181 | return self ? self->pid == HOST_KERNEL_ID : false; | ||
182 | } | ||
183 | |||
184 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) | 125 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
185 | { | 126 | { |
186 | maps__insert(&mg->maps[map->type], map); | 127 | maps__insert(&mg->maps[map->type], map); |
@@ -209,29 +150,6 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
209 | struct map **mapp, | 150 | struct map **mapp, |
210 | symbol_filter_t filter); | 151 | symbol_filter_t filter); |
211 | 152 | ||
212 | |||
213 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | ||
214 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
215 | |||
216 | size_t machine__fprintf(struct machine *machine, FILE *fp); | ||
217 | |||
218 | static inline | ||
219 | struct symbol *machine__find_kernel_symbol(struct machine *self, | ||
220 | enum map_type type, u64 addr, | ||
221 | struct map **mapp, | ||
222 | symbol_filter_t filter) | ||
223 | { | ||
224 | return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter); | ||
225 | } | ||
226 | |||
227 | static inline | ||
228 | struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, | ||
229 | struct map **mapp, | ||
230 | symbol_filter_t filter) | ||
231 | { | ||
232 | return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter); | ||
233 | } | ||
234 | |||
235 | static inline | 153 | static inline |
236 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | 154 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
237 | const char *name, struct map **mapp, | 155 | const char *name, struct map **mapp, |
@@ -240,22 +158,11 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | |||
240 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); | 158 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
241 | } | 159 | } |
242 | 160 | ||
243 | static inline | ||
244 | struct symbol *machine__find_kernel_function_by_name(struct machine *self, | ||
245 | const char *name, | ||
246 | struct map **mapp, | ||
247 | symbol_filter_t filter) | ||
248 | { | ||
249 | return map_groups__find_function_by_name(&self->kmaps, name, mapp, | ||
250 | filter); | ||
251 | } | ||
252 | |||
253 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | 161 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
254 | int verbose, FILE *fp); | 162 | int verbose, FILE *fp); |
255 | 163 | ||
256 | struct map *map_groups__find_by_name(struct map_groups *mg, | 164 | struct map *map_groups__find_by_name(struct map_groups *mg, |
257 | enum map_type type, const char *name); | 165 | enum map_type type, const char *name); |
258 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); | ||
259 | 166 | ||
260 | void map_groups__flush(struct map_groups *mg); | 167 | void map_groups__flush(struct map_groups *mg); |
261 | 168 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6b6d03e93c3d..2d8d53bec17e 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -722,6 +722,27 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
722 | return 0; | 722 | return 0; |
723 | } | 723 | } |
724 | 724 | ||
725 | /* | ||
726 | * Basic modifier sanity check to validate it contains only one | ||
727 | * instance of any modifier (apart from 'p') present. | ||
728 | */ | ||
729 | static int check_modifier(char *str) | ||
730 | { | ||
731 | char *p = str; | ||
732 | |||
733 | /* The sizeof includes 0 byte as well. */ | ||
734 | if (strlen(str) > (sizeof("ukhGHppp") - 1)) | ||
735 | return -1; | ||
736 | |||
737 | while (*p) { | ||
738 | if (*p != 'p' && strchr(p + 1, *p)) | ||
739 | return -1; | ||
740 | p++; | ||
741 | } | ||
742 | |||
743 | return 0; | ||
744 | } | ||
745 | |||
725 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) | 746 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) |
726 | { | 747 | { |
727 | struct perf_evsel *evsel; | 748 | struct perf_evsel *evsel; |
@@ -730,6 +751,9 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) | |||
730 | if (str == NULL) | 751 | if (str == NULL) |
731 | return 0; | 752 | return 0; |
732 | 753 | ||
754 | if (check_modifier(str)) | ||
755 | return -EINVAL; | ||
756 | |||
733 | if (!add && get_event_modifier(&mod, str, NULL)) | 757 | if (!add && get_event_modifier(&mod, str, NULL)) |
734 | return -EINVAL; | 758 | return -EINVAL; |
735 | 759 | ||
@@ -827,8 +851,6 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
827 | * Both call perf_evlist__delete in case of error, so we dont | 851 | * Both call perf_evlist__delete in case of error, so we dont |
828 | * need to bother. | 852 | * need to bother. |
829 | */ | 853 | */ |
830 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
831 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
832 | return ret; | 854 | return ret; |
833 | } | 855 | } |
834 | 856 | ||
@@ -836,7 +858,13 @@ int parse_events_option(const struct option *opt, const char *str, | |||
836 | int unset __maybe_unused) | 858 | int unset __maybe_unused) |
837 | { | 859 | { |
838 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 860 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
839 | return parse_events(evlist, str, unset); | 861 | int ret = parse_events(evlist, str, unset); |
862 | |||
863 | if (ret) { | ||
864 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
865 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
866 | } | ||
867 | return ret; | ||
840 | } | 868 | } |
841 | 869 | ||
842 | int parse_filter(const struct option *opt, const char *str, | 870 | int parse_filter(const struct option *opt, const char *str, |
@@ -1081,7 +1109,7 @@ void print_events(const char *event_glob, bool name_only) | |||
1081 | printf(" %-50s [%s]\n", | 1109 | printf(" %-50s [%s]\n", |
1082 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | 1110 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", |
1083 | event_type_descriptors[PERF_TYPE_RAW]); | 1111 | event_type_descriptors[PERF_TYPE_RAW]); |
1084 | printf(" (see 'perf list --help' on how to encode it)\n"); | 1112 | printf(" (see 'man perf-list' on how to encode it)\n"); |
1085 | printf("\n"); | 1113 | printf("\n"); |
1086 | 1114 | ||
1087 | printf(" %-50s [%s]\n", | 1115 | printf(" %-50s [%s]\n", |
@@ -1142,6 +1170,24 @@ int parse_events__term_str(struct parse_events__term **term, | |||
1142 | config, str, 0); | 1170 | config, str, 0); |
1143 | } | 1171 | } |
1144 | 1172 | ||
1173 | int parse_events__term_sym_hw(struct parse_events__term **term, | ||
1174 | char *config, unsigned idx) | ||
1175 | { | ||
1176 | struct event_symbol *sym; | ||
1177 | |||
1178 | BUG_ON(idx >= PERF_COUNT_HW_MAX); | ||
1179 | sym = &event_symbols_hw[idx]; | ||
1180 | |||
1181 | if (config) | ||
1182 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | ||
1183 | PARSE_EVENTS__TERM_TYPE_USER, config, | ||
1184 | (char *) sym->symbol, 0); | ||
1185 | else | ||
1186 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | ||
1187 | PARSE_EVENTS__TERM_TYPE_USER, | ||
1188 | (char *) "event", (char *) sym->symbol, 0); | ||
1189 | } | ||
1190 | |||
1145 | int parse_events__term_clone(struct parse_events__term **new, | 1191 | int parse_events__term_clone(struct parse_events__term **new, |
1146 | struct parse_events__term *term) | 1192 | struct parse_events__term *term) |
1147 | { | 1193 | { |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 2820c407adb2..b7af80b8bdda 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -76,6 +76,8 @@ int parse_events__term_num(struct parse_events__term **_term, | |||
76 | int type_term, char *config, u64 num); | 76 | int type_term, char *config, u64 num); |
77 | int parse_events__term_str(struct parse_events__term **_term, | 77 | int parse_events__term_str(struct parse_events__term **_term, |
78 | int type_term, char *config, char *str); | 78 | int type_term, char *config, char *str); |
79 | int parse_events__term_sym_hw(struct parse_events__term **term, | ||
80 | char *config, unsigned idx); | ||
79 | int parse_events__term_clone(struct parse_events__term **new, | 81 | int parse_events__term_clone(struct parse_events__term **new, |
80 | struct parse_events__term *term); | 82 | struct parse_events__term *term); |
81 | void parse_events__free_terms(struct list_head *terms); | 83 | void parse_events__free_terms(struct list_head *terms); |
@@ -97,7 +99,6 @@ void parse_events__set_leader(char *name, struct list_head *list); | |||
97 | void parse_events_update_lists(struct list_head *list_event, | 99 | void parse_events_update_lists(struct list_head *list_event, |
98 | struct list_head *list_all); | 100 | struct list_head *list_all); |
99 | void parse_events_error(void *data, void *scanner, char const *msg); | 101 | void parse_events_error(void *data, void *scanner, char const *msg); |
100 | int parse_events__test(void); | ||
101 | 102 | ||
102 | void print_events(const char *event_glob, bool name_only); | 103 | void print_events(const char *event_glob, bool name_only); |
103 | void print_events_type(u8 type); | 104 | void print_events_type(u8 type); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index c87efc12579d..e9d1134c2c68 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -81,7 +81,8 @@ num_dec [0-9]+ | |||
81 | num_hex 0x[a-fA-F0-9]+ | 81 | num_hex 0x[a-fA-F0-9]+ |
82 | num_raw_hex [a-fA-F0-9]+ | 82 | num_raw_hex [a-fA-F0-9]+ |
83 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* | 83 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* |
84 | modifier_event [ukhpGH]{1,8} | 84 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]* |
85 | modifier_event [ukhpGH]+ | ||
85 | modifier_bp [rwx]{1,3} | 86 | modifier_bp [rwx]{1,3} |
86 | 87 | ||
87 | %% | 88 | %% |
@@ -168,6 +169,7 @@ period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | |||
168 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | 169 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } |
169 | , { return ','; } | 170 | , { return ','; } |
170 | "/" { BEGIN(INITIAL); return '/'; } | 171 | "/" { BEGIN(INITIAL); return '/'; } |
172 | {name_minus} { return str(yyscanner, PE_NAME); } | ||
171 | } | 173 | } |
172 | 174 | ||
173 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } | 175 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index cd88209e3c58..0f9914ae6bac 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -352,6 +352,15 @@ PE_NAME '=' PE_VALUE | |||
352 | $$ = term; | 352 | $$ = term; |
353 | } | 353 | } |
354 | | | 354 | | |
355 | PE_NAME '=' PE_VALUE_SYM_HW | ||
356 | { | ||
357 | struct parse_events__term *term; | ||
358 | int config = $3 & 255; | ||
359 | |||
360 | ABORT_ON(parse_events__term_sym_hw(&term, $1, config)); | ||
361 | $$ = term; | ||
362 | } | ||
363 | | | ||
355 | PE_NAME | 364 | PE_NAME |
356 | { | 365 | { |
357 | struct parse_events__term *term; | 366 | struct parse_events__term *term; |
@@ -361,6 +370,15 @@ PE_NAME | |||
361 | $$ = term; | 370 | $$ = term; |
362 | } | 371 | } |
363 | | | 372 | | |
373 | PE_VALUE_SYM_HW | ||
374 | { | ||
375 | struct parse_events__term *term; | ||
376 | int config = $1 & 255; | ||
377 | |||
378 | ABORT_ON(parse_events__term_sym_hw(&term, NULL, config)); | ||
379 | $$ = term; | ||
380 | } | ||
381 | | | ||
364 | PE_TERM '=' PE_NAME | 382 | PE_TERM '=' PE_NAME |
365 | { | 383 | { |
366 | struct parse_events__term *term; | 384 | struct parse_events__term *term; |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 8a2229da594f..9bdc60c6f138 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -22,7 +22,7 @@ static LIST_HEAD(pmus); | |||
22 | * Parse & process all the sysfs attributes located under | 22 | * Parse & process all the sysfs attributes located under |
23 | * the directory specified in 'dir' parameter. | 23 | * the directory specified in 'dir' parameter. |
24 | */ | 24 | */ |
25 | static int pmu_format_parse(char *dir, struct list_head *head) | 25 | int perf_pmu__format_parse(char *dir, struct list_head *head) |
26 | { | 26 | { |
27 | struct dirent *evt_ent; | 27 | struct dirent *evt_ent; |
28 | DIR *format_dir; | 28 | DIR *format_dir; |
@@ -77,7 +77,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
77 | if (stat(path, &st) < 0) | 77 | if (stat(path, &st) < 0) |
78 | return 0; /* no error if format does not exist */ | 78 | return 0; /* no error if format does not exist */ |
79 | 79 | ||
80 | if (pmu_format_parse(path, format)) | 80 | if (perf_pmu__format_parse(path, format)) |
81 | return -1; | 81 | return -1; |
82 | 82 | ||
83 | return 0; | 83 | return 0; |
@@ -164,7 +164,7 @@ static int pmu_aliases(char *name, struct list_head *head) | |||
164 | "%s/bus/event_source/devices/%s/events", sysfs, name); | 164 | "%s/bus/event_source/devices/%s/events", sysfs, name); |
165 | 165 | ||
166 | if (stat(path, &st) < 0) | 166 | if (stat(path, &st) < 0) |
167 | return -1; | 167 | return 0; /* no error if 'events' does not exist */ |
168 | 168 | ||
169 | if (pmu_aliases_parse(path, head)) | 169 | if (pmu_aliases_parse(path, head)) |
170 | return -1; | 170 | return -1; |
@@ -296,6 +296,9 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
296 | if (pmu_format(name, &format)) | 296 | if (pmu_format(name, &format)) |
297 | return NULL; | 297 | return NULL; |
298 | 298 | ||
299 | if (pmu_aliases(name, &aliases)) | ||
300 | return NULL; | ||
301 | |||
299 | if (pmu_type(name, &type)) | 302 | if (pmu_type(name, &type)) |
300 | return NULL; | 303 | return NULL; |
301 | 304 | ||
@@ -305,8 +308,6 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
305 | 308 | ||
306 | pmu->cpus = pmu_cpumask(name); | 309 | pmu->cpus = pmu_cpumask(name); |
307 | 310 | ||
308 | pmu_aliases(name, &aliases); | ||
309 | |||
310 | INIT_LIST_HEAD(&pmu->format); | 311 | INIT_LIST_HEAD(&pmu->format); |
311 | INIT_LIST_HEAD(&pmu->aliases); | 312 | INIT_LIST_HEAD(&pmu->aliases); |
312 | list_splice(&format, &pmu->format); | 313 | list_splice(&format, &pmu->format); |
@@ -445,8 +446,9 @@ static int pmu_config_term(struct list_head *formats, | |||
445 | return 0; | 446 | return 0; |
446 | } | 447 | } |
447 | 448 | ||
448 | static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, | 449 | int perf_pmu__config_terms(struct list_head *formats, |
449 | struct list_head *head_terms) | 450 | struct perf_event_attr *attr, |
451 | struct list_head *head_terms) | ||
450 | { | 452 | { |
451 | struct parse_events__term *term; | 453 | struct parse_events__term *term; |
452 | 454 | ||
@@ -466,7 +468,7 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
466 | struct list_head *head_terms) | 468 | struct list_head *head_terms) |
467 | { | 469 | { |
468 | attr->type = pmu->type; | 470 | attr->type = pmu->type; |
469 | return pmu_config(&pmu->format, attr, head_terms); | 471 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); |
470 | } | 472 | } |
471 | 473 | ||
472 | static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | 474 | static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, |
@@ -550,177 +552,3 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) | |||
550 | for (b = from; b <= to; b++) | 552 | for (b = from; b <= to; b++) |
551 | set_bit(b, bits); | 553 | set_bit(b, bits); |
552 | } | 554 | } |
553 | |||
554 | /* Simulated format definitions. */ | ||
555 | static struct test_format { | ||
556 | const char *name; | ||
557 | const char *value; | ||
558 | } test_formats[] = { | ||
559 | { "krava01", "config:0-1,62-63\n", }, | ||
560 | { "krava02", "config:10-17\n", }, | ||
561 | { "krava03", "config:5\n", }, | ||
562 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
563 | { "krava12", "config1:63\n", }, | ||
564 | { "krava13", "config1:45-47\n", }, | ||
565 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
566 | { "krava22", "config2:8,18,48,58\n", }, | ||
567 | { "krava23", "config2:28-29,38\n", }, | ||
568 | }; | ||
569 | |||
570 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
571 | |||
572 | /* Simulated users input. */ | ||
573 | static struct parse_events__term test_terms[] = { | ||
574 | { | ||
575 | .config = (char *) "krava01", | ||
576 | .val.num = 15, | ||
577 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
578 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
579 | }, | ||
580 | { | ||
581 | .config = (char *) "krava02", | ||
582 | .val.num = 170, | ||
583 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
584 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
585 | }, | ||
586 | { | ||
587 | .config = (char *) "krava03", | ||
588 | .val.num = 1, | ||
589 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
590 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
591 | }, | ||
592 | { | ||
593 | .config = (char *) "krava11", | ||
594 | .val.num = 27, | ||
595 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
596 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
597 | }, | ||
598 | { | ||
599 | .config = (char *) "krava12", | ||
600 | .val.num = 1, | ||
601 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
602 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
603 | }, | ||
604 | { | ||
605 | .config = (char *) "krava13", | ||
606 | .val.num = 2, | ||
607 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
608 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
609 | }, | ||
610 | { | ||
611 | .config = (char *) "krava21", | ||
612 | .val.num = 119, | ||
613 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
614 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
615 | }, | ||
616 | { | ||
617 | .config = (char *) "krava22", | ||
618 | .val.num = 11, | ||
619 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
620 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
621 | }, | ||
622 | { | ||
623 | .config = (char *) "krava23", | ||
624 | .val.num = 2, | ||
625 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
626 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
627 | }, | ||
628 | }; | ||
629 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
630 | |||
631 | /* | ||
632 | * Prepare format directory data, exported by kernel | ||
633 | * at /sys/bus/event_source/devices/<dev>/format. | ||
634 | */ | ||
635 | static char *test_format_dir_get(void) | ||
636 | { | ||
637 | static char dir[PATH_MAX]; | ||
638 | unsigned int i; | ||
639 | |||
640 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
641 | if (!mkdtemp(dir)) | ||
642 | return NULL; | ||
643 | |||
644 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
645 | static char name[PATH_MAX]; | ||
646 | struct test_format *format = &test_formats[i]; | ||
647 | FILE *file; | ||
648 | |||
649 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
650 | |||
651 | file = fopen(name, "w"); | ||
652 | if (!file) | ||
653 | return NULL; | ||
654 | |||
655 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
656 | break; | ||
657 | |||
658 | fclose(file); | ||
659 | } | ||
660 | |||
661 | return dir; | ||
662 | } | ||
663 | |||
664 | /* Cleanup format directory. */ | ||
665 | static int test_format_dir_put(char *dir) | ||
666 | { | ||
667 | char buf[PATH_MAX]; | ||
668 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
669 | if (system(buf)) | ||
670 | return -1; | ||
671 | |||
672 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
673 | return system(buf); | ||
674 | } | ||
675 | |||
676 | static struct list_head *test_terms_list(void) | ||
677 | { | ||
678 | static LIST_HEAD(terms); | ||
679 | unsigned int i; | ||
680 | |||
681 | for (i = 0; i < TERMS_CNT; i++) | ||
682 | list_add_tail(&test_terms[i].list, &terms); | ||
683 | |||
684 | return &terms; | ||
685 | } | ||
686 | |||
687 | #undef TERMS_CNT | ||
688 | |||
689 | int perf_pmu__test(void) | ||
690 | { | ||
691 | char *format = test_format_dir_get(); | ||
692 | LIST_HEAD(formats); | ||
693 | struct list_head *terms = test_terms_list(); | ||
694 | int ret; | ||
695 | |||
696 | if (!format) | ||
697 | return -EINVAL; | ||
698 | |||
699 | do { | ||
700 | struct perf_event_attr attr; | ||
701 | |||
702 | memset(&attr, 0, sizeof(attr)); | ||
703 | |||
704 | ret = pmu_format_parse(format, &formats); | ||
705 | if (ret) | ||
706 | break; | ||
707 | |||
708 | ret = pmu_config(&formats, &attr, terms); | ||
709 | if (ret) | ||
710 | break; | ||
711 | |||
712 | ret = -EINVAL; | ||
713 | |||
714 | if (attr.config != 0xc00000000002a823) | ||
715 | break; | ||
716 | if (attr.config1 != 0x8000400000000145) | ||
717 | break; | ||
718 | if (attr.config2 != 0x0400000020041d07) | ||
719 | break; | ||
720 | |||
721 | ret = 0; | ||
722 | } while (0); | ||
723 | |||
724 | test_format_dir_put(format); | ||
725 | return ret; | ||
726 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index fdeb8ac7c5d2..a313ed76a49a 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -37,6 +37,9 @@ struct perf_pmu { | |||
37 | struct perf_pmu *perf_pmu__find(char *name); | 37 | struct perf_pmu *perf_pmu__find(char *name); |
38 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 38 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
39 | struct list_head *head_terms); | 39 | struct list_head *head_terms); |
40 | int perf_pmu__config_terms(struct list_head *formats, | ||
41 | struct perf_event_attr *attr, | ||
42 | struct list_head *head_terms); | ||
40 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); | 43 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); |
41 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 44 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
42 | struct list_head *head_terms); | 45 | struct list_head *head_terms); |
@@ -46,6 +49,7 @@ void perf_pmu_error(struct list_head *list, char *name, char const *msg); | |||
46 | int perf_pmu__new_format(struct list_head *list, char *name, | 49 | int perf_pmu__new_format(struct list_head *list, char *name, |
47 | int config, unsigned long *bits); | 50 | int config, unsigned long *bits); |
48 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | 51 | void perf_pmu__set_format(unsigned long *bits, long from, long to); |
52 | int perf_pmu__format_parse(char *dir, struct list_head *head); | ||
49 | 53 | ||
50 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 54 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
51 | 55 | ||
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c index 13d36faf64eb..daa17aeb6c63 100644 --- a/tools/perf/util/pstack.c +++ b/tools/perf/util/pstack.c | |||
@@ -17,59 +17,59 @@ struct pstack { | |||
17 | 17 | ||
18 | struct pstack *pstack__new(unsigned short max_nr_entries) | 18 | struct pstack *pstack__new(unsigned short max_nr_entries) |
19 | { | 19 | { |
20 | struct pstack *self = zalloc((sizeof(*self) + | 20 | struct pstack *pstack = zalloc((sizeof(*pstack) + |
21 | max_nr_entries * sizeof(void *))); | 21 | max_nr_entries * sizeof(void *))); |
22 | if (self != NULL) | 22 | if (pstack != NULL) |
23 | self->max_nr_entries = max_nr_entries; | 23 | pstack->max_nr_entries = max_nr_entries; |
24 | return self; | 24 | return pstack; |
25 | } | 25 | } |
26 | 26 | ||
27 | void pstack__delete(struct pstack *self) | 27 | void pstack__delete(struct pstack *pstack) |
28 | { | 28 | { |
29 | free(self); | 29 | free(pstack); |
30 | } | 30 | } |
31 | 31 | ||
32 | bool pstack__empty(const struct pstack *self) | 32 | bool pstack__empty(const struct pstack *pstack) |
33 | { | 33 | { |
34 | return self->top == 0; | 34 | return pstack->top == 0; |
35 | } | 35 | } |
36 | 36 | ||
37 | void pstack__remove(struct pstack *self, void *key) | 37 | void pstack__remove(struct pstack *pstack, void *key) |
38 | { | 38 | { |
39 | unsigned short i = self->top, last_index = self->top - 1; | 39 | unsigned short i = pstack->top, last_index = pstack->top - 1; |
40 | 40 | ||
41 | while (i-- != 0) { | 41 | while (i-- != 0) { |
42 | if (self->entries[i] == key) { | 42 | if (pstack->entries[i] == key) { |
43 | if (i < last_index) | 43 | if (i < last_index) |
44 | memmove(self->entries + i, | 44 | memmove(pstack->entries + i, |
45 | self->entries + i + 1, | 45 | pstack->entries + i + 1, |
46 | (last_index - i) * sizeof(void *)); | 46 | (last_index - i) * sizeof(void *)); |
47 | --self->top; | 47 | --pstack->top; |
48 | return; | 48 | return; |
49 | } | 49 | } |
50 | } | 50 | } |
51 | pr_err("%s: %p not on the pstack!\n", __func__, key); | 51 | pr_err("%s: %p not on the pstack!\n", __func__, key); |
52 | } | 52 | } |
53 | 53 | ||
54 | void pstack__push(struct pstack *self, void *key) | 54 | void pstack__push(struct pstack *pstack, void *key) |
55 | { | 55 | { |
56 | if (self->top == self->max_nr_entries) { | 56 | if (pstack->top == pstack->max_nr_entries) { |
57 | pr_err("%s: top=%d, overflow!\n", __func__, self->top); | 57 | pr_err("%s: top=%d, overflow!\n", __func__, pstack->top); |
58 | return; | 58 | return; |
59 | } | 59 | } |
60 | self->entries[self->top++] = key; | 60 | pstack->entries[pstack->top++] = key; |
61 | } | 61 | } |
62 | 62 | ||
63 | void *pstack__pop(struct pstack *self) | 63 | void *pstack__pop(struct pstack *pstack) |
64 | { | 64 | { |
65 | void *ret; | 65 | void *ret; |
66 | 66 | ||
67 | if (self->top == 0) { | 67 | if (pstack->top == 0) { |
68 | pr_err("%s: underflow!\n", __func__); | 68 | pr_err("%s: underflow!\n", __func__); |
69 | return NULL; | 69 | return NULL; |
70 | } | 70 | } |
71 | 71 | ||
72 | ret = self->entries[--self->top]; | 72 | ret = pstack->entries[--pstack->top]; |
73 | self->entries[self->top] = NULL; | 73 | pstack->entries[pstack->top] = NULL; |
74 | return ret; | 74 | return ret; |
75 | } | 75 | } |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 9181bf212fb9..a2657fd96837 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -1015,6 +1015,8 @@ PyMODINIT_FUNC initperf(void) | |||
1015 | pyrf_cpu_map__setup_types() < 0) | 1015 | pyrf_cpu_map__setup_types() < 0) |
1016 | return; | 1016 | return; |
1017 | 1017 | ||
1018 | page_size = sysconf(_SC_PAGE_SIZE); | ||
1019 | |||
1018 | Py_INCREF(&pyrf_evlist__type); | 1020 | Py_INCREF(&pyrf_evlist__type); |
1019 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); | 1021 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); |
1020 | 1022 | ||
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index 0171fb611004..a16cdd2625ad 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c | |||
@@ -44,6 +44,7 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry) | |||
44 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) | 44 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) |
45 | { | 45 | { |
46 | rb_erase(rb_node, &rblist->entries); | 46 | rb_erase(rb_node, &rblist->entries); |
47 | --rblist->nr_entries; | ||
47 | rblist->node_delete(rblist, rb_node); | 48 | rblist->node_delete(rblist, rb_node); |
48 | } | 49 | } |
49 | 50 | ||
@@ -87,8 +88,7 @@ void rblist__delete(struct rblist *rblist) | |||
87 | while (next) { | 88 | while (next) { |
88 | pos = next; | 89 | pos = next; |
89 | next = rb_next(pos); | 90 | next = rb_next(pos); |
90 | rb_erase(pos, &rblist->entries); | 91 | rblist__remove_node(rblist, pos); |
91 | rblist->node_delete(rblist, pos); | ||
92 | } | 92 | } |
93 | free(rblist); | 93 | free(rblist); |
94 | } | 94 | } |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 730c6630cba5..14683dfca2ee 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -32,7 +32,6 @@ | |||
32 | #include "../event.h" | 32 | #include "../event.h" |
33 | #include "../thread.h" | 33 | #include "../thread.h" |
34 | #include "../trace-event.h" | 34 | #include "../trace-event.h" |
35 | #include "../evsel.h" | ||
36 | 35 | ||
37 | PyMODINIT_FUNC initperf_trace_context(void); | 36 | PyMODINIT_FUNC initperf_trace_context(void); |
38 | 37 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8cdd23239c90..ce6f51162386 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1375,15 +1375,13 @@ int __perf_session__process_events(struct perf_session *session, | |||
1375 | { | 1375 | { |
1376 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1376 | u64 head, page_offset, file_offset, file_pos, progress_next; |
1377 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1377 | int err, mmap_prot, mmap_flags, map_idx = 0; |
1378 | size_t page_size, mmap_size; | 1378 | size_t mmap_size; |
1379 | char *buf, *mmaps[8]; | 1379 | char *buf, *mmaps[8]; |
1380 | union perf_event *event; | 1380 | union perf_event *event; |
1381 | uint32_t size; | 1381 | uint32_t size; |
1382 | 1382 | ||
1383 | perf_tool__fill_defaults(tool); | 1383 | perf_tool__fill_defaults(tool); |
1384 | 1384 | ||
1385 | page_size = sysconf(_SC_PAGESIZE); | ||
1386 | |||
1387 | page_offset = page_size * (data_offset / page_size); | 1385 | page_offset = page_size * (data_offset / page_size); |
1388 | file_offset = page_offset; | 1386 | file_offset = page_offset; |
1389 | head = data_offset - page_offset; | 1387 | head = data_offset - page_offset; |
@@ -1460,6 +1458,7 @@ more: | |||
1460 | session->ordered_samples.next_flush = ULLONG_MAX; | 1458 | session->ordered_samples.next_flush = ULLONG_MAX; |
1461 | err = flush_sample_queue(session, tool); | 1459 | err = flush_sample_queue(session, tool); |
1462 | out_err: | 1460 | out_err: |
1461 | ui_progress__finish(); | ||
1463 | perf_session__warn_about_errors(session, tool); | 1462 | perf_session__warn_about_errors(session, tool); |
1464 | perf_session_free_sample_buffers(session); | 1463 | perf_session_free_sample_buffers(session); |
1465 | return err; | 1464 | return err; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0eae00ad5fe7..cea133a6bdf1 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "hist.h" | 4 | #include "hist.h" |
5 | #include "event.h" | 5 | #include "event.h" |
6 | #include "header.h" | 6 | #include "header.h" |
7 | #include "machine.h" | ||
7 | #include "symbol.h" | 8 | #include "symbol.h" |
8 | #include "thread.h" | 9 | #include "thread.h" |
9 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
@@ -68,10 +69,6 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel | |||
68 | struct ip_callchain *chain, | 69 | struct ip_callchain *chain, |
69 | struct symbol **parent); | 70 | struct symbol **parent); |
70 | 71 | ||
71 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
72 | struct thread *thread, | ||
73 | struct branch_stack *bs); | ||
74 | |||
75 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 72 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
76 | 73 | ||
77 | void mem_bswap_64(void *src, int byte_size); | 74 | void mem_bswap_64(void *src, int byte_size); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5786f323b597..b4e8c3ba559d 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -52,6 +52,22 @@ struct he_stat { | |||
52 | u32 nr_events; | 52 | u32 nr_events; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct hist_entry_diff { | ||
56 | bool computed; | ||
57 | |||
58 | /* PERF_HPP__DISPL */ | ||
59 | int displacement; | ||
60 | |||
61 | /* PERF_HPP__DELTA */ | ||
62 | double period_ratio_delta; | ||
63 | |||
64 | /* PERF_HPP__RATIO */ | ||
65 | double period_ratio; | ||
66 | |||
67 | /* HISTC_WEIGHTED_DIFF */ | ||
68 | s64 wdiff; | ||
69 | }; | ||
70 | |||
55 | /** | 71 | /** |
56 | * struct hist_entry - histogram entry | 72 | * struct hist_entry - histogram entry |
57 | * | 73 | * |
@@ -61,12 +77,18 @@ struct he_stat { | |||
61 | struct hist_entry { | 77 | struct hist_entry { |
62 | struct rb_node rb_node_in; | 78 | struct rb_node rb_node_in; |
63 | struct rb_node rb_node; | 79 | struct rb_node rb_node; |
80 | union { | ||
81 | struct list_head node; | ||
82 | struct list_head head; | ||
83 | } pairs; | ||
64 | struct he_stat stat; | 84 | struct he_stat stat; |
65 | struct map_symbol ms; | 85 | struct map_symbol ms; |
66 | struct thread *thread; | 86 | struct thread *thread; |
67 | u64 ip; | 87 | u64 ip; |
68 | s32 cpu; | 88 | s32 cpu; |
69 | 89 | ||
90 | struct hist_entry_diff diff; | ||
91 | |||
70 | /* XXX These two should move to some tree widget lib */ | 92 | /* XXX These two should move to some tree widget lib */ |
71 | u16 row_offset; | 93 | u16 row_offset; |
72 | u16 nr_rows; | 94 | u16 nr_rows; |
@@ -78,15 +100,30 @@ struct hist_entry { | |||
78 | char *srcline; | 100 | char *srcline; |
79 | struct symbol *parent; | 101 | struct symbol *parent; |
80 | unsigned long position; | 102 | unsigned long position; |
81 | union { | 103 | struct rb_root sorted_chain; |
82 | struct hist_entry *pair; | ||
83 | struct rb_root sorted_chain; | ||
84 | }; | ||
85 | struct branch_info *branch_info; | 104 | struct branch_info *branch_info; |
86 | struct hists *hists; | 105 | struct hists *hists; |
87 | struct callchain_root callchain[0]; | 106 | struct callchain_root callchain[0]; |
88 | }; | 107 | }; |
89 | 108 | ||
109 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | ||
110 | { | ||
111 | return !list_empty(&he->pairs.node); | ||
112 | } | ||
113 | |||
114 | static inline struct hist_entry *hist_entry__next_pair(struct hist_entry *he) | ||
115 | { | ||
116 | if (hist_entry__has_pairs(he)) | ||
117 | return list_entry(he->pairs.node.next, struct hist_entry, pairs.node); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | static inline void hist__entry_add_pair(struct hist_entry *he, | ||
122 | struct hist_entry *pair) | ||
123 | { | ||
124 | list_add_tail(&he->pairs.head, &pair->pairs.node); | ||
125 | } | ||
126 | |||
90 | enum sort_type { | 127 | enum sort_type { |
91 | SORT_PID, | 128 | SORT_PID, |
92 | SORT_COMM, | 129 | SORT_COMM, |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 32170590892d..346707df04b9 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -314,6 +314,24 @@ int strtailcmp(const char *s1, const char *s2) | |||
314 | } | 314 | } |
315 | 315 | ||
316 | /** | 316 | /** |
317 | * strxfrchar - Locate and replace character in @s | ||
318 | * @s: The string to be searched/changed. | ||
319 | * @from: Source character to be replaced. | ||
320 | * @to: Destination character. | ||
321 | * | ||
322 | * Return pointer to the changed string. | ||
323 | */ | ||
324 | char *strxfrchar(char *s, char from, char to) | ||
325 | { | ||
326 | char *p = s; | ||
327 | |||
328 | while ((p = strchr(p, from)) != NULL) | ||
329 | *p++ = to; | ||
330 | |||
331 | return s; | ||
332 | } | ||
333 | |||
334 | /** | ||
317 | * rtrim - Removes trailing whitespace from @s. | 335 | * rtrim - Removes trailing whitespace from @s. |
318 | * @s: The string to be stripped. | 336 | * @s: The string to be stripped. |
319 | * | 337 | * |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e2e8c697cffe..295f8d4feedf 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "build-id.h" | 12 | #include "build-id.h" |
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "debug.h" | 14 | #include "debug.h" |
15 | #include "machine.h" | ||
15 | #include "symbol.h" | 16 | #include "symbol.h" |
16 | #include "strlist.h" | 17 | #include "strlist.h" |
17 | 18 | ||
@@ -23,7 +24,6 @@ | |||
23 | #define KSYM_NAME_LEN 256 | 24 | #define KSYM_NAME_LEN 256 |
24 | #endif | 25 | #endif |
25 | 26 | ||
26 | static void dso_cache__free(struct rb_root *root); | ||
27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
28 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
@@ -56,39 +56,6 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
56 | 56 | ||
57 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) | 57 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) |
58 | 58 | ||
59 | static enum dso_binary_type binary_type_data[] = { | ||
60 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
61 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
62 | DSO_BINARY_TYPE__NOT_FOUND, | ||
63 | }; | ||
64 | |||
65 | #define DSO_BINARY_TYPE__DATA_CNT ARRAY_SIZE(binary_type_data) | ||
66 | |||
67 | int dso__name_len(const struct dso *dso) | ||
68 | { | ||
69 | if (!dso) | ||
70 | return strlen("[unknown]"); | ||
71 | if (verbose) | ||
72 | return dso->long_name_len; | ||
73 | |||
74 | return dso->short_name_len; | ||
75 | } | ||
76 | |||
77 | bool dso__loaded(const struct dso *dso, enum map_type type) | ||
78 | { | ||
79 | return dso->loaded & (1 << type); | ||
80 | } | ||
81 | |||
82 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | ||
83 | { | ||
84 | return dso->sorted_by_name & (1 << type); | ||
85 | } | ||
86 | |||
87 | static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | ||
88 | { | ||
89 | dso->sorted_by_name |= (1 << type); | ||
90 | } | ||
91 | |||
92 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 59 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
93 | { | 60 | { |
94 | symbol_type = toupper(symbol_type); | 61 | symbol_type = toupper(symbol_type); |
@@ -270,7 +237,7 @@ void symbol__delete(struct symbol *sym) | |||
270 | free(((void *)sym) - symbol_conf.priv_size); | 237 | free(((void *)sym) - symbol_conf.priv_size); |
271 | } | 238 | } |
272 | 239 | ||
273 | static size_t symbol__fprintf(struct symbol *sym, FILE *fp) | 240 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) |
274 | { | 241 | { |
275 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | 242 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", |
276 | sym->start, sym->end, | 243 | sym->start, sym->end, |
@@ -301,53 +268,7 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | |||
301 | return symbol__fprintf_symname_offs(sym, NULL, fp); | 268 | return symbol__fprintf_symname_offs(sym, NULL, fp); |
302 | } | 269 | } |
303 | 270 | ||
304 | void dso__set_long_name(struct dso *dso, char *name) | 271 | void symbols__delete(struct rb_root *symbols) |
305 | { | ||
306 | if (name == NULL) | ||
307 | return; | ||
308 | dso->long_name = name; | ||
309 | dso->long_name_len = strlen(name); | ||
310 | } | ||
311 | |||
312 | static void dso__set_short_name(struct dso *dso, const char *name) | ||
313 | { | ||
314 | if (name == NULL) | ||
315 | return; | ||
316 | dso->short_name = name; | ||
317 | dso->short_name_len = strlen(name); | ||
318 | } | ||
319 | |||
320 | static void dso__set_basename(struct dso *dso) | ||
321 | { | ||
322 | dso__set_short_name(dso, basename(dso->long_name)); | ||
323 | } | ||
324 | |||
325 | struct dso *dso__new(const char *name) | ||
326 | { | ||
327 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | ||
328 | |||
329 | if (dso != NULL) { | ||
330 | int i; | ||
331 | strcpy(dso->name, name); | ||
332 | dso__set_long_name(dso, dso->name); | ||
333 | dso__set_short_name(dso, dso->name); | ||
334 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
335 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | ||
336 | dso->cache = RB_ROOT; | ||
337 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
338 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
339 | dso->loaded = 0; | ||
340 | dso->sorted_by_name = 0; | ||
341 | dso->has_build_id = 0; | ||
342 | dso->kernel = DSO_TYPE_USER; | ||
343 | dso->needs_swap = DSO_SWAP__UNSET; | ||
344 | INIT_LIST_HEAD(&dso->node); | ||
345 | } | ||
346 | |||
347 | return dso; | ||
348 | } | ||
349 | |||
350 | static void symbols__delete(struct rb_root *symbols) | ||
351 | { | 272 | { |
352 | struct symbol *pos; | 273 | struct symbol *pos; |
353 | struct rb_node *next = rb_first(symbols); | 274 | struct rb_node *next = rb_first(symbols); |
@@ -360,25 +281,6 @@ static void symbols__delete(struct rb_root *symbols) | |||
360 | } | 281 | } |
361 | } | 282 | } |
362 | 283 | ||
363 | void dso__delete(struct dso *dso) | ||
364 | { | ||
365 | int i; | ||
366 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
367 | symbols__delete(&dso->symbols[i]); | ||
368 | if (dso->sname_alloc) | ||
369 | free((char *)dso->short_name); | ||
370 | if (dso->lname_alloc) | ||
371 | free(dso->long_name); | ||
372 | dso_cache__free(&dso->cache); | ||
373 | free(dso); | ||
374 | } | ||
375 | |||
376 | void dso__set_build_id(struct dso *dso, void *build_id) | ||
377 | { | ||
378 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | ||
379 | dso->has_build_id = 1; | ||
380 | } | ||
381 | |||
382 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 284 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
383 | { | 285 | { |
384 | struct rb_node **p = &symbols->rb_node; | 286 | struct rb_node **p = &symbols->rb_node; |
@@ -504,29 +406,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type) | |||
504 | &dso->symbols[type]); | 406 | &dso->symbols[type]); |
505 | } | 407 | } |
506 | 408 | ||
507 | int build_id__sprintf(const u8 *build_id, int len, char *bf) | ||
508 | { | ||
509 | char *bid = bf; | ||
510 | const u8 *raw = build_id; | ||
511 | int i; | ||
512 | |||
513 | for (i = 0; i < len; ++i) { | ||
514 | sprintf(bid, "%02x", *raw); | ||
515 | ++raw; | ||
516 | bid += 2; | ||
517 | } | ||
518 | |||
519 | return raw - build_id; | ||
520 | } | ||
521 | |||
522 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | ||
523 | { | ||
524 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
525 | |||
526 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
527 | return fprintf(fp, "%s", sbuild_id); | ||
528 | } | ||
529 | |||
530 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 409 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
531 | enum map_type type, FILE *fp) | 410 | enum map_type type, FILE *fp) |
532 | { | 411 | { |
@@ -542,25 +421,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, | |||
542 | return ret; | 421 | return ret; |
543 | } | 422 | } |
544 | 423 | ||
545 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | ||
546 | { | ||
547 | struct rb_node *nd; | ||
548 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | ||
549 | |||
550 | if (dso->short_name != dso->long_name) | ||
551 | ret += fprintf(fp, "%s, ", dso->long_name); | ||
552 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
553 | dso->loaded ? "" : "NOT "); | ||
554 | ret += dso__fprintf_buildid(dso, fp); | ||
555 | ret += fprintf(fp, ")\n"); | ||
556 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
557 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
558 | ret += symbol__fprintf(pos, fp); | ||
559 | } | ||
560 | |||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | int kallsyms__parse(const char *filename, void *arg, | 424 | int kallsyms__parse(const char *filename, void *arg, |
565 | int (*process_symbol)(void *arg, const char *name, | 425 | int (*process_symbol)(void *arg, const char *name, |
566 | char type, u64 start)) | 426 | char type, u64 start)) |
@@ -892,136 +752,6 @@ out_failure: | |||
892 | return -1; | 752 | return -1; |
893 | } | 753 | } |
894 | 754 | ||
895 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
896 | { | ||
897 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | ||
898 | } | ||
899 | |||
900 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | ||
901 | { | ||
902 | bool have_build_id = false; | ||
903 | struct dso *pos; | ||
904 | |||
905 | list_for_each_entry(pos, head, node) { | ||
906 | if (with_hits && !pos->hit) | ||
907 | continue; | ||
908 | if (pos->has_build_id) { | ||
909 | have_build_id = true; | ||
910 | continue; | ||
911 | } | ||
912 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
913 | sizeof(pos->build_id)) > 0) { | ||
914 | have_build_id = true; | ||
915 | pos->has_build_id = true; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | return have_build_id; | ||
920 | } | ||
921 | |||
922 | char dso__symtab_origin(const struct dso *dso) | ||
923 | { | ||
924 | static const char origin[] = { | ||
925 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | ||
926 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
927 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | ||
928 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | ||
929 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | ||
930 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | ||
931 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | ||
932 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | ||
933 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | ||
934 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | ||
935 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | ||
936 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | ||
937 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
938 | }; | ||
939 | |||
940 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | ||
941 | return '!'; | ||
942 | return origin[dso->symtab_type]; | ||
943 | } | ||
944 | |||
945 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
946 | char *root_dir, char *file, size_t size) | ||
947 | { | ||
948 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
949 | int ret = 0; | ||
950 | |||
951 | switch (type) { | ||
952 | case DSO_BINARY_TYPE__DEBUGLINK: { | ||
953 | char *debuglink; | ||
954 | |||
955 | strncpy(file, dso->long_name, size); | ||
956 | debuglink = file + dso->long_name_len; | ||
957 | while (debuglink != file && *debuglink != '/') | ||
958 | debuglink--; | ||
959 | if (*debuglink == '/') | ||
960 | debuglink++; | ||
961 | filename__read_debuglink(dso->long_name, debuglink, | ||
962 | size - (debuglink - file)); | ||
963 | } | ||
964 | break; | ||
965 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | ||
966 | /* skip the locally configured cache if a symfs is given */ | ||
967 | if (symbol_conf.symfs[0] || | ||
968 | (dso__build_id_filename(dso, file, size) == NULL)) | ||
969 | ret = -1; | ||
970 | break; | ||
971 | |||
972 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | ||
973 | snprintf(file, size, "%s/usr/lib/debug%s.debug", | ||
974 | symbol_conf.symfs, dso->long_name); | ||
975 | break; | ||
976 | |||
977 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | ||
978 | snprintf(file, size, "%s/usr/lib/debug%s", | ||
979 | symbol_conf.symfs, dso->long_name); | ||
980 | break; | ||
981 | |||
982 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | ||
983 | if (!dso->has_build_id) { | ||
984 | ret = -1; | ||
985 | break; | ||
986 | } | ||
987 | |||
988 | build_id__sprintf(dso->build_id, | ||
989 | sizeof(dso->build_id), | ||
990 | build_id_hex); | ||
991 | snprintf(file, size, | ||
992 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
993 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | ||
994 | break; | ||
995 | |||
996 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | ||
997 | snprintf(file, size, "%s%s", | ||
998 | symbol_conf.symfs, dso->long_name); | ||
999 | break; | ||
1000 | |||
1001 | case DSO_BINARY_TYPE__GUEST_KMODULE: | ||
1002 | snprintf(file, size, "%s%s%s", symbol_conf.symfs, | ||
1003 | root_dir, dso->long_name); | ||
1004 | break; | ||
1005 | |||
1006 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | ||
1007 | snprintf(file, size, "%s%s", symbol_conf.symfs, | ||
1008 | dso->long_name); | ||
1009 | break; | ||
1010 | |||
1011 | default: | ||
1012 | case DSO_BINARY_TYPE__KALLSYMS: | ||
1013 | case DSO_BINARY_TYPE__VMLINUX: | ||
1014 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | ||
1015 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
1016 | case DSO_BINARY_TYPE__JAVA_JIT: | ||
1017 | case DSO_BINARY_TYPE__NOT_FOUND: | ||
1018 | ret = -1; | ||
1019 | break; | ||
1020 | } | ||
1021 | |||
1022 | return ret; | ||
1023 | } | ||
1024 | |||
1025 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | 755 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) |
1026 | { | 756 | { |
1027 | char *name; | 757 | char *name; |
@@ -1157,27 +887,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, | |||
1157 | return NULL; | 887 | return NULL; |
1158 | } | 888 | } |
1159 | 889 | ||
1160 | static int dso__kernel_module_get_build_id(struct dso *dso, | ||
1161 | const char *root_dir) | ||
1162 | { | ||
1163 | char filename[PATH_MAX]; | ||
1164 | /* | ||
1165 | * kernel module short names are of the form "[module]" and | ||
1166 | * we need just "module" here. | ||
1167 | */ | ||
1168 | const char *name = dso->short_name + 1; | ||
1169 | |||
1170 | snprintf(filename, sizeof(filename), | ||
1171 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1172 | root_dir, (int)strlen(name) - 1, name); | ||
1173 | |||
1174 | if (sysfs__read_build_id(filename, dso->build_id, | ||
1175 | sizeof(dso->build_id)) == 0) | ||
1176 | dso->has_build_id = true; | ||
1177 | |||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | 890 | static int map_groups__set_modules_path_dir(struct map_groups *mg, |
1182 | const char *dir_name) | 891 | const char *dir_name) |
1183 | { | 892 | { |
@@ -1591,50 +1300,6 @@ out_try_fixup: | |||
1591 | return err; | 1300 | return err; |
1592 | } | 1301 | } |
1593 | 1302 | ||
1594 | void dsos__add(struct list_head *head, struct dso *dso) | ||
1595 | { | ||
1596 | list_add_tail(&dso->node, head); | ||
1597 | } | ||
1598 | |||
1599 | struct dso *dsos__find(struct list_head *head, const char *name) | ||
1600 | { | ||
1601 | struct dso *pos; | ||
1602 | |||
1603 | list_for_each_entry(pos, head, node) | ||
1604 | if (strcmp(pos->long_name, name) == 0) | ||
1605 | return pos; | ||
1606 | return NULL; | ||
1607 | } | ||
1608 | |||
1609 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | ||
1610 | { | ||
1611 | struct dso *dso = dsos__find(head, name); | ||
1612 | |||
1613 | if (!dso) { | ||
1614 | dso = dso__new(name); | ||
1615 | if (dso != NULL) { | ||
1616 | dsos__add(head, dso); | ||
1617 | dso__set_basename(dso); | ||
1618 | } | ||
1619 | } | ||
1620 | |||
1621 | return dso; | ||
1622 | } | ||
1623 | |||
1624 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | ||
1625 | { | ||
1626 | struct dso *pos; | ||
1627 | size_t ret = 0; | ||
1628 | |||
1629 | list_for_each_entry(pos, head, node) { | ||
1630 | int i; | ||
1631 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
1632 | ret += dso__fprintf(pos, i, fp); | ||
1633 | } | ||
1634 | |||
1635 | return ret; | ||
1636 | } | ||
1637 | |||
1638 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | 1303 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) |
1639 | { | 1304 | { |
1640 | struct rb_node *nd; | 1305 | struct rb_node *nd; |
@@ -1649,21 +1314,6 @@ size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | |||
1649 | return ret; | 1314 | return ret; |
1650 | } | 1315 | } |
1651 | 1316 | ||
1652 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
1653 | bool with_hits) | ||
1654 | { | ||
1655 | struct dso *pos; | ||
1656 | size_t ret = 0; | ||
1657 | |||
1658 | list_for_each_entry(pos, head, node) { | ||
1659 | if (with_hits && !pos->hit) | ||
1660 | continue; | ||
1661 | ret += dso__fprintf_buildid(pos, fp); | ||
1662 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
1663 | } | ||
1664 | return ret; | ||
1665 | } | ||
1666 | |||
1667 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 1317 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, |
1668 | bool with_hits) | 1318 | bool with_hits) |
1669 | { | 1319 | { |
@@ -1684,39 +1334,6 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | |||
1684 | return ret; | 1334 | return ret; |
1685 | } | 1335 | } |
1686 | 1336 | ||
1687 | static struct dso* | ||
1688 | dso__kernel_findnew(struct machine *machine, const char *name, | ||
1689 | const char *short_name, int dso_type) | ||
1690 | { | ||
1691 | /* | ||
1692 | * The kernel dso could be created by build_id processing. | ||
1693 | */ | ||
1694 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); | ||
1695 | |||
1696 | /* | ||
1697 | * We need to run this in all cases, since during the build_id | ||
1698 | * processing we had no idea this was the kernel dso. | ||
1699 | */ | ||
1700 | if (dso != NULL) { | ||
1701 | dso__set_short_name(dso, short_name); | ||
1702 | dso->kernel = dso_type; | ||
1703 | } | ||
1704 | |||
1705 | return dso; | ||
1706 | } | ||
1707 | |||
1708 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | ||
1709 | { | ||
1710 | char path[PATH_MAX]; | ||
1711 | |||
1712 | if (machine__is_default_guest(machine)) | ||
1713 | return; | ||
1714 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | ||
1715 | if (sysfs__read_build_id(path, dso->build_id, | ||
1716 | sizeof(dso->build_id)) == 0) | ||
1717 | dso->has_build_id = true; | ||
1718 | } | ||
1719 | |||
1720 | static struct dso *machine__get_kernel(struct machine *machine) | 1337 | static struct dso *machine__get_kernel(struct machine *machine) |
1721 | { | 1338 | { |
1722 | const char *vmlinux_name = NULL; | 1339 | const char *vmlinux_name = NULL; |
@@ -2065,49 +1682,6 @@ int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | |||
2065 | return machine__create_kernel_maps(machine); | 1682 | return machine__create_kernel_maps(machine); |
2066 | } | 1683 | } |
2067 | 1684 | ||
2068 | static int hex(char ch) | ||
2069 | { | ||
2070 | if ((ch >= '0') && (ch <= '9')) | ||
2071 | return ch - '0'; | ||
2072 | if ((ch >= 'a') && (ch <= 'f')) | ||
2073 | return ch - 'a' + 10; | ||
2074 | if ((ch >= 'A') && (ch <= 'F')) | ||
2075 | return ch - 'A' + 10; | ||
2076 | return -1; | ||
2077 | } | ||
2078 | |||
2079 | /* | ||
2080 | * While we find nice hex chars, build a long_val. | ||
2081 | * Return number of chars processed. | ||
2082 | */ | ||
2083 | int hex2u64(const char *ptr, u64 *long_val) | ||
2084 | { | ||
2085 | const char *p = ptr; | ||
2086 | *long_val = 0; | ||
2087 | |||
2088 | while (*p) { | ||
2089 | const int hex_val = hex(*p); | ||
2090 | |||
2091 | if (hex_val < 0) | ||
2092 | break; | ||
2093 | |||
2094 | *long_val = (*long_val << 4) | hex_val; | ||
2095 | p++; | ||
2096 | } | ||
2097 | |||
2098 | return p - ptr; | ||
2099 | } | ||
2100 | |||
2101 | char *strxfrchar(char *s, char from, char to) | ||
2102 | { | ||
2103 | char *p = s; | ||
2104 | |||
2105 | while ((p = strchr(p, from)) != NULL) | ||
2106 | *p++ = to; | ||
2107 | |||
2108 | return s; | ||
2109 | } | ||
2110 | |||
2111 | int machines__create_guest_kernel_maps(struct rb_root *machines) | 1685 | int machines__create_guest_kernel_maps(struct rb_root *machines) |
2112 | { | 1686 | { |
2113 | int ret = 0; | 1687 | int ret = 0; |
@@ -2202,229 +1776,3 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | |||
2202 | 1776 | ||
2203 | return ret; | 1777 | return ret; |
2204 | } | 1778 | } |
2205 | |||
2206 | struct map *dso__new_map(const char *name) | ||
2207 | { | ||
2208 | struct map *map = NULL; | ||
2209 | struct dso *dso = dso__new(name); | ||
2210 | |||
2211 | if (dso) | ||
2212 | map = map__new2(0, dso, MAP__FUNCTION); | ||
2213 | |||
2214 | return map; | ||
2215 | } | ||
2216 | |||
2217 | static int open_dso(struct dso *dso, struct machine *machine) | ||
2218 | { | ||
2219 | char *root_dir = (char *) ""; | ||
2220 | char *name; | ||
2221 | int fd; | ||
2222 | |||
2223 | name = malloc(PATH_MAX); | ||
2224 | if (!name) | ||
2225 | return -ENOMEM; | ||
2226 | |||
2227 | if (machine) | ||
2228 | root_dir = machine->root_dir; | ||
2229 | |||
2230 | if (dso__binary_type_file(dso, dso->data_type, | ||
2231 | root_dir, name, PATH_MAX)) { | ||
2232 | free(name); | ||
2233 | return -EINVAL; | ||
2234 | } | ||
2235 | |||
2236 | fd = open(name, O_RDONLY); | ||
2237 | free(name); | ||
2238 | return fd; | ||
2239 | } | ||
2240 | |||
2241 | int dso__data_fd(struct dso *dso, struct machine *machine) | ||
2242 | { | ||
2243 | int i = 0; | ||
2244 | |||
2245 | if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) | ||
2246 | return open_dso(dso, machine); | ||
2247 | |||
2248 | do { | ||
2249 | int fd; | ||
2250 | |||
2251 | dso->data_type = binary_type_data[i++]; | ||
2252 | |||
2253 | fd = open_dso(dso, machine); | ||
2254 | if (fd >= 0) | ||
2255 | return fd; | ||
2256 | |||
2257 | } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); | ||
2258 | |||
2259 | return -EINVAL; | ||
2260 | } | ||
2261 | |||
2262 | static void | ||
2263 | dso_cache__free(struct rb_root *root) | ||
2264 | { | ||
2265 | struct rb_node *next = rb_first(root); | ||
2266 | |||
2267 | while (next) { | ||
2268 | struct dso_cache *cache; | ||
2269 | |||
2270 | cache = rb_entry(next, struct dso_cache, rb_node); | ||
2271 | next = rb_next(&cache->rb_node); | ||
2272 | rb_erase(&cache->rb_node, root); | ||
2273 | free(cache); | ||
2274 | } | ||
2275 | } | ||
2276 | |||
2277 | static struct dso_cache* | ||
2278 | dso_cache__find(struct rb_root *root, u64 offset) | ||
2279 | { | ||
2280 | struct rb_node **p = &root->rb_node; | ||
2281 | struct rb_node *parent = NULL; | ||
2282 | struct dso_cache *cache; | ||
2283 | |||
2284 | while (*p != NULL) { | ||
2285 | u64 end; | ||
2286 | |||
2287 | parent = *p; | ||
2288 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
2289 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
2290 | |||
2291 | if (offset < cache->offset) | ||
2292 | p = &(*p)->rb_left; | ||
2293 | else if (offset >= end) | ||
2294 | p = &(*p)->rb_right; | ||
2295 | else | ||
2296 | return cache; | ||
2297 | } | ||
2298 | return NULL; | ||
2299 | } | ||
2300 | |||
2301 | static void | ||
2302 | dso_cache__insert(struct rb_root *root, struct dso_cache *new) | ||
2303 | { | ||
2304 | struct rb_node **p = &root->rb_node; | ||
2305 | struct rb_node *parent = NULL; | ||
2306 | struct dso_cache *cache; | ||
2307 | u64 offset = new->offset; | ||
2308 | |||
2309 | while (*p != NULL) { | ||
2310 | u64 end; | ||
2311 | |||
2312 | parent = *p; | ||
2313 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
2314 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
2315 | |||
2316 | if (offset < cache->offset) | ||
2317 | p = &(*p)->rb_left; | ||
2318 | else if (offset >= end) | ||
2319 | p = &(*p)->rb_right; | ||
2320 | } | ||
2321 | |||
2322 | rb_link_node(&new->rb_node, parent, p); | ||
2323 | rb_insert_color(&new->rb_node, root); | ||
2324 | } | ||
2325 | |||
2326 | static ssize_t | ||
2327 | dso_cache__memcpy(struct dso_cache *cache, u64 offset, | ||
2328 | u8 *data, u64 size) | ||
2329 | { | ||
2330 | u64 cache_offset = offset - cache->offset; | ||
2331 | u64 cache_size = min(cache->size - cache_offset, size); | ||
2332 | |||
2333 | memcpy(data, cache->data + cache_offset, cache_size); | ||
2334 | return cache_size; | ||
2335 | } | ||
2336 | |||
2337 | static ssize_t | ||
2338 | dso_cache__read(struct dso *dso, struct machine *machine, | ||
2339 | u64 offset, u8 *data, ssize_t size) | ||
2340 | { | ||
2341 | struct dso_cache *cache; | ||
2342 | ssize_t ret; | ||
2343 | int fd; | ||
2344 | |||
2345 | fd = dso__data_fd(dso, machine); | ||
2346 | if (fd < 0) | ||
2347 | return -1; | ||
2348 | |||
2349 | do { | ||
2350 | u64 cache_offset; | ||
2351 | |||
2352 | ret = -ENOMEM; | ||
2353 | |||
2354 | cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); | ||
2355 | if (!cache) | ||
2356 | break; | ||
2357 | |||
2358 | cache_offset = offset & DSO__DATA_CACHE_MASK; | ||
2359 | ret = -EINVAL; | ||
2360 | |||
2361 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | ||
2362 | break; | ||
2363 | |||
2364 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
2365 | if (ret <= 0) | ||
2366 | break; | ||
2367 | |||
2368 | cache->offset = cache_offset; | ||
2369 | cache->size = ret; | ||
2370 | dso_cache__insert(&dso->cache, cache); | ||
2371 | |||
2372 | ret = dso_cache__memcpy(cache, offset, data, size); | ||
2373 | |||
2374 | } while (0); | ||
2375 | |||
2376 | if (ret <= 0) | ||
2377 | free(cache); | ||
2378 | |||
2379 | close(fd); | ||
2380 | return ret; | ||
2381 | } | ||
2382 | |||
2383 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | ||
2384 | u64 offset, u8 *data, ssize_t size) | ||
2385 | { | ||
2386 | struct dso_cache *cache; | ||
2387 | |||
2388 | cache = dso_cache__find(&dso->cache, offset); | ||
2389 | if (cache) | ||
2390 | return dso_cache__memcpy(cache, offset, data, size); | ||
2391 | else | ||
2392 | return dso_cache__read(dso, machine, offset, data, size); | ||
2393 | } | ||
2394 | |||
2395 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
2396 | u64 offset, u8 *data, ssize_t size) | ||
2397 | { | ||
2398 | ssize_t r = 0; | ||
2399 | u8 *p = data; | ||
2400 | |||
2401 | do { | ||
2402 | ssize_t ret; | ||
2403 | |||
2404 | ret = dso_cache_read(dso, machine, offset, p, size); | ||
2405 | if (ret < 0) | ||
2406 | return ret; | ||
2407 | |||
2408 | /* Reached EOF, return what we have. */ | ||
2409 | if (!ret) | ||
2410 | break; | ||
2411 | |||
2412 | BUG_ON(ret > size); | ||
2413 | |||
2414 | r += ret; | ||
2415 | p += ret; | ||
2416 | offset += ret; | ||
2417 | size -= ret; | ||
2418 | |||
2419 | } while (size); | ||
2420 | |||
2421 | return r; | ||
2422 | } | ||
2423 | |||
2424 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
2425 | struct machine *machine, u64 addr, | ||
2426 | u8 *data, ssize_t size) | ||
2427 | { | ||
2428 | u64 offset = map->map_ip(map, addr); | ||
2429 | return dso__data_read_offset(dso, machine, offset, data, size); | ||
2430 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 8b6ef7fac745..de68f98b236d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <stdio.h> | 11 | #include <stdio.h> |
12 | #include <byteswap.h> | 12 | #include <byteswap.h> |
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | ||
14 | 15 | ||
15 | #ifdef LIBELF_SUPPORT | 16 | #ifdef LIBELF_SUPPORT |
16 | #include <libelf.h> | 17 | #include <libelf.h> |
@@ -18,6 +19,8 @@ | |||
18 | #include <elf.h> | 19 | #include <elf.h> |
19 | #endif | 20 | #endif |
20 | 21 | ||
22 | #include "dso.h" | ||
23 | |||
21 | #ifdef HAVE_CPLUS_DEMANGLE | 24 | #ifdef HAVE_CPLUS_DEMANGLE |
22 | extern char *cplus_demangle(const char *, int); | 25 | extern char *cplus_demangle(const char *, int); |
23 | 26 | ||
@@ -39,9 +42,6 @@ static inline char *bfd_demangle(void __maybe_unused *v, | |||
39 | #endif | 42 | #endif |
40 | #endif | 43 | #endif |
41 | 44 | ||
42 | int hex2u64(const char *ptr, u64 *val); | ||
43 | char *strxfrchar(char *s, char from, char to); | ||
44 | |||
45 | /* | 45 | /* |
46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
47 | * for newer versions we can use mmap to reduce memory usage: | 47 | * for newer versions we can use mmap to reduce memory usage: |
@@ -57,8 +57,6 @@ char *strxfrchar(char *s, char from, char to); | |||
57 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 57 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | #define BUILD_ID_SIZE 20 | ||
61 | |||
62 | /** struct symbol - symtab entry | 60 | /** struct symbol - symtab entry |
63 | * | 61 | * |
64 | * @ignore - resolvable but tools ignore it (e.g. idle routines) | 62 | * @ignore - resolvable but tools ignore it (e.g. idle routines) |
@@ -74,6 +72,7 @@ struct symbol { | |||
74 | }; | 72 | }; |
75 | 73 | ||
76 | void symbol__delete(struct symbol *sym); | 74 | void symbol__delete(struct symbol *sym); |
75 | void symbols__delete(struct rb_root *symbols); | ||
77 | 76 | ||
78 | static inline size_t symbol__size(const struct symbol *sym) | 77 | static inline size_t symbol__size(const struct symbol *sym) |
79 | { | 78 | { |
@@ -164,70 +163,6 @@ struct addr_location { | |||
164 | s32 cpu; | 163 | s32 cpu; |
165 | }; | 164 | }; |
166 | 165 | ||
167 | enum dso_binary_type { | ||
168 | DSO_BINARY_TYPE__KALLSYMS = 0, | ||
169 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | ||
170 | DSO_BINARY_TYPE__VMLINUX, | ||
171 | DSO_BINARY_TYPE__GUEST_VMLINUX, | ||
172 | DSO_BINARY_TYPE__JAVA_JIT, | ||
173 | DSO_BINARY_TYPE__DEBUGLINK, | ||
174 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
175 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | ||
176 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | ||
177 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | ||
178 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
179 | DSO_BINARY_TYPE__GUEST_KMODULE, | ||
180 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | ||
181 | DSO_BINARY_TYPE__NOT_FOUND, | ||
182 | }; | ||
183 | |||
184 | enum dso_kernel_type { | ||
185 | DSO_TYPE_USER = 0, | ||
186 | DSO_TYPE_KERNEL, | ||
187 | DSO_TYPE_GUEST_KERNEL | ||
188 | }; | ||
189 | |||
190 | enum dso_swap_type { | ||
191 | DSO_SWAP__UNSET, | ||
192 | DSO_SWAP__NO, | ||
193 | DSO_SWAP__YES, | ||
194 | }; | ||
195 | |||
196 | #define DSO__DATA_CACHE_SIZE 4096 | ||
197 | #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) | ||
198 | |||
199 | struct dso_cache { | ||
200 | struct rb_node rb_node; | ||
201 | u64 offset; | ||
202 | u64 size; | ||
203 | char data[0]; | ||
204 | }; | ||
205 | |||
206 | struct dso { | ||
207 | struct list_head node; | ||
208 | struct rb_root symbols[MAP__NR_TYPES]; | ||
209 | struct rb_root symbol_names[MAP__NR_TYPES]; | ||
210 | struct rb_root cache; | ||
211 | enum dso_kernel_type kernel; | ||
212 | enum dso_swap_type needs_swap; | ||
213 | enum dso_binary_type symtab_type; | ||
214 | enum dso_binary_type data_type; | ||
215 | u8 adjust_symbols:1; | ||
216 | u8 has_build_id:1; | ||
217 | u8 hit:1; | ||
218 | u8 annotate_warned:1; | ||
219 | u8 sname_alloc:1; | ||
220 | u8 lname_alloc:1; | ||
221 | u8 sorted_by_name; | ||
222 | u8 loaded; | ||
223 | u8 build_id[BUILD_ID_SIZE]; | ||
224 | const char *short_name; | ||
225 | char *long_name; | ||
226 | u16 long_name_len; | ||
227 | u16 short_name_len; | ||
228 | char name[0]; | ||
229 | }; | ||
230 | |||
231 | struct symsrc { | 166 | struct symsrc { |
232 | char *name; | 167 | char *name; |
233 | int fd; | 168 | int fd; |
@@ -258,47 +193,6 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
258 | bool symsrc__has_symtab(struct symsrc *ss); | 193 | bool symsrc__has_symtab(struct symsrc *ss); |
259 | bool symsrc__possibly_runtime(struct symsrc *ss); | 194 | bool symsrc__possibly_runtime(struct symsrc *ss); |
260 | 195 | ||
261 | #define DSO__SWAP(dso, type, val) \ | ||
262 | ({ \ | ||
263 | type ____r = val; \ | ||
264 | BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \ | ||
265 | if (dso->needs_swap == DSO_SWAP__YES) { \ | ||
266 | switch (sizeof(____r)) { \ | ||
267 | case 2: \ | ||
268 | ____r = bswap_16(val); \ | ||
269 | break; \ | ||
270 | case 4: \ | ||
271 | ____r = bswap_32(val); \ | ||
272 | break; \ | ||
273 | case 8: \ | ||
274 | ____r = bswap_64(val); \ | ||
275 | break; \ | ||
276 | default: \ | ||
277 | BUG_ON(1); \ | ||
278 | } \ | ||
279 | } \ | ||
280 | ____r; \ | ||
281 | }) | ||
282 | |||
283 | struct dso *dso__new(const char *name); | ||
284 | void dso__delete(struct dso *dso); | ||
285 | |||
286 | int dso__name_len(const struct dso *dso); | ||
287 | |||
288 | bool dso__loaded(const struct dso *dso, enum map_type type); | ||
289 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type); | ||
290 | |||
291 | static inline void dso__set_loaded(struct dso *dso, enum map_type type) | ||
292 | { | ||
293 | dso->loaded |= (1 << type); | ||
294 | } | ||
295 | |||
296 | void dso__sort_by_name(struct dso *dso, enum map_type type); | ||
297 | |||
298 | void dsos__add(struct list_head *head, struct dso *dso); | ||
299 | struct dso *dsos__find(struct list_head *head, const char *name); | ||
300 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
301 | |||
302 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); | 196 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); |
303 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 197 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
304 | const char *vmlinux, symbol_filter_t filter); | 198 | const char *vmlinux, symbol_filter_t filter); |
@@ -306,30 +200,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
306 | symbol_filter_t filter); | 200 | symbol_filter_t filter); |
307 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, | 201 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, |
308 | symbol_filter_t filter); | 202 | symbol_filter_t filter); |
309 | int machine__load_kallsyms(struct machine *machine, const char *filename, | 203 | |
310 | enum map_type type, symbol_filter_t filter); | ||
311 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
312 | symbol_filter_t filter); | ||
313 | |||
314 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); | ||
315 | |||
316 | size_t machine__fprintf_dsos_buildid(struct machine *machine, | ||
317 | FILE *fp, bool with_hits); | ||
318 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp); | ||
319 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | ||
320 | FILE *fp, bool with_hits); | ||
321 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | ||
322 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
323 | enum map_type type, FILE *fp); | ||
324 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | ||
325 | |||
326 | char dso__symtab_origin(const struct dso *dso); | ||
327 | void dso__set_long_name(struct dso *dso, char *name); | ||
328 | void dso__set_build_id(struct dso *dso, void *build_id); | ||
329 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
330 | void dso__read_running_kernel_build_id(struct dso *dso, | ||
331 | struct machine *machine); | ||
332 | struct map *dso__new_map(const char *name); | ||
333 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, | 204 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, |
334 | u64 addr); | 205 | u64 addr); |
335 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 206 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
@@ -337,22 +208,12 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | |||
337 | 208 | ||
338 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 209 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
339 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 210 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
340 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | ||
341 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | ||
342 | int kallsyms__parse(const char *filename, void *arg, | 211 | int kallsyms__parse(const char *filename, void *arg, |
343 | int (*process_symbol)(void *arg, const char *name, | 212 | int (*process_symbol)(void *arg, const char *name, |
344 | char type, u64 start)); | 213 | char type, u64 start)); |
345 | int filename__read_debuglink(const char *filename, char *debuglink, | 214 | int filename__read_debuglink(const char *filename, char *debuglink, |
346 | size_t size); | 215 | size_t size); |
347 | 216 | ||
348 | void machine__destroy_kernel_maps(struct machine *machine); | ||
349 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | ||
350 | int machine__create_kernel_maps(struct machine *machine); | ||
351 | |||
352 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid); | ||
353 | int machines__create_guest_kernel_maps(struct rb_root *machines); | ||
354 | void machines__destroy_guest_kernel_maps(struct rb_root *machines); | ||
355 | |||
356 | int symbol__init(void); | 217 | int symbol__init(void); |
357 | void symbol__exit(void); | 218 | void symbol__exit(void); |
358 | void symbol__elf_init(void); | 219 | void symbol__elf_init(void); |
@@ -360,20 +221,9 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | |||
360 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 221 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
361 | const struct addr_location *al, FILE *fp); | 222 | const struct addr_location *al, FILE *fp); |
362 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 223 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
224 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); | ||
363 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 225 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
364 | 226 | ||
365 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | ||
366 | |||
367 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
368 | char *root_dir, char *file, size_t size); | ||
369 | |||
370 | int dso__data_fd(struct dso *dso, struct machine *machine); | ||
371 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
372 | u64 offset, u8 *data, ssize_t size); | ||
373 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
374 | struct machine *machine, u64 addr, | ||
375 | u8 *data, ssize_t size); | ||
376 | int dso__test_data(void); | ||
377 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | 227 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, |
378 | struct symsrc *runtime_ss, symbol_filter_t filter, | 228 | struct symsrc *runtime_ss, symbol_filter_t filter, |
379 | int kmodule); | 229 | int kmodule); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 8b3e5939afb6..df59623ac763 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | static struct thread *thread__new(pid_t pid) | 10 | struct thread *thread__new(pid_t pid) |
11 | { | 11 | { |
12 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
13 | 13 | ||
@@ -60,45 +60,6 @@ static size_t thread__fprintf(struct thread *self, FILE *fp) | |||
60 | map_groups__fprintf(&self->mg, verbose, fp); | 60 | map_groups__fprintf(&self->mg, verbose, fp); |
61 | } | 61 | } |
62 | 62 | ||
63 | struct thread *machine__findnew_thread(struct machine *self, pid_t pid) | ||
64 | { | ||
65 | struct rb_node **p = &self->threads.rb_node; | ||
66 | struct rb_node *parent = NULL; | ||
67 | struct thread *th; | ||
68 | |||
69 | /* | ||
70 | * Font-end cache - PID lookups come in blocks, | ||
71 | * so most of the time we dont have to look up | ||
72 | * the full rbtree: | ||
73 | */ | ||
74 | if (self->last_match && self->last_match->pid == pid) | ||
75 | return self->last_match; | ||
76 | |||
77 | while (*p != NULL) { | ||
78 | parent = *p; | ||
79 | th = rb_entry(parent, struct thread, rb_node); | ||
80 | |||
81 | if (th->pid == pid) { | ||
82 | self->last_match = th; | ||
83 | return th; | ||
84 | } | ||
85 | |||
86 | if (pid < th->pid) | ||
87 | p = &(*p)->rb_left; | ||
88 | else | ||
89 | p = &(*p)->rb_right; | ||
90 | } | ||
91 | |||
92 | th = thread__new(pid); | ||
93 | if (th != NULL) { | ||
94 | rb_link_node(&th->rb_node, parent, p); | ||
95 | rb_insert_color(&th->rb_node, &self->threads); | ||
96 | self->last_match = th; | ||
97 | } | ||
98 | |||
99 | return th; | ||
100 | } | ||
101 | |||
102 | void thread__insert_map(struct thread *self, struct map *map) | 63 | void thread__insert_map(struct thread *self, struct map *map) |
103 | { | 64 | { |
104 | map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); | 65 | map_groups__fixup_overlappings(&self->mg, map, verbose, stderr); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index f66610b7bacf..f2fa17caa7d5 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include <sys/types.h> | ||
6 | #include "symbol.h" | 7 | #include "symbol.h" |
7 | 8 | ||
8 | struct thread { | 9 | struct thread { |
@@ -22,6 +23,7 @@ struct thread { | |||
22 | 23 | ||
23 | struct machine; | 24 | struct machine; |
24 | 25 | ||
26 | struct thread *thread__new(pid_t pid); | ||
25 | void thread__delete(struct thread *self); | 27 | void thread__delete(struct thread *self); |
26 | 28 | ||
27 | int thread__set_comm(struct thread *self, const char *comm); | 29 | int thread__set_comm(struct thread *self, const char *comm); |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 719ed74a8565..3741572696af 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -47,8 +47,6 @@ int file_bigendian; | |||
47 | int host_bigendian; | 47 | int host_bigendian; |
48 | static int long_size; | 48 | static int long_size; |
49 | 49 | ||
50 | static unsigned long page_size; | ||
51 | |||
52 | static ssize_t calc_data_size; | 50 | static ssize_t calc_data_size; |
53 | static bool repipe; | 51 | static bool repipe; |
54 | 52 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 99664598bc1a..5906e8426cc7 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -10,6 +10,8 @@ | |||
10 | /* | 10 | /* |
11 | * XXX We need to find a better place for these things... | 11 | * XXX We need to find a better place for these things... |
12 | */ | 12 | */ |
13 | unsigned int page_size; | ||
14 | |||
13 | bool perf_host = true; | 15 | bool perf_host = true; |
14 | bool perf_guest = false; | 16 | bool perf_guest = false; |
15 | 17 | ||
@@ -164,6 +166,39 @@ size_t hex_width(u64 v) | |||
164 | return n; | 166 | return n; |
165 | } | 167 | } |
166 | 168 | ||
169 | static int hex(char ch) | ||
170 | { | ||
171 | if ((ch >= '0') && (ch <= '9')) | ||
172 | return ch - '0'; | ||
173 | if ((ch >= 'a') && (ch <= 'f')) | ||
174 | return ch - 'a' + 10; | ||
175 | if ((ch >= 'A') && (ch <= 'F')) | ||
176 | return ch - 'A' + 10; | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * While we find nice hex chars, build a long_val. | ||
182 | * Return number of chars processed. | ||
183 | */ | ||
184 | int hex2u64(const char *ptr, u64 *long_val) | ||
185 | { | ||
186 | const char *p = ptr; | ||
187 | *long_val = 0; | ||
188 | |||
189 | while (*p) { | ||
190 | const int hex_val = hex(*p); | ||
191 | |||
192 | if (hex_val < 0) | ||
193 | break; | ||
194 | |||
195 | *long_val = (*long_val << 4) | hex_val; | ||
196 | p++; | ||
197 | } | ||
198 | |||
199 | return p - ptr; | ||
200 | } | ||
201 | |||
167 | /* Obtain a backtrace and print it to stdout. */ | 202 | /* Obtain a backtrace and print it to stdout. */ |
168 | #ifdef BACKTRACE_SUPPORT | 203 | #ifdef BACKTRACE_SUPPORT |
169 | void dump_stack(void) | 204 | void dump_stack(void) |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 70fa70b535b2..c2330918110c 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -198,6 +198,10 @@ static inline int has_extension(const char *filename, const char *ext) | |||
198 | #undef tolower | 198 | #undef tolower |
199 | #undef toupper | 199 | #undef toupper |
200 | 200 | ||
201 | #ifndef NSEC_PER_MSEC | ||
202 | #define NSEC_PER_MSEC 1000000L | ||
203 | #endif | ||
204 | |||
201 | extern unsigned char sane_ctype[256]; | 205 | extern unsigned char sane_ctype[256]; |
202 | #define GIT_SPACE 0x01 | 206 | #define GIT_SPACE 0x01 |
203 | #define GIT_DIGIT 0x02 | 207 | #define GIT_DIGIT 0x02 |
@@ -236,6 +240,7 @@ void argv_free(char **argv); | |||
236 | bool strglobmatch(const char *str, const char *pat); | 240 | bool strglobmatch(const char *str, const char *pat); |
237 | bool strlazymatch(const char *str, const char *pat); | 241 | bool strlazymatch(const char *str, const char *pat); |
238 | int strtailcmp(const char *s1, const char *s2); | 242 | int strtailcmp(const char *s1, const char *s2); |
243 | char *strxfrchar(char *s, char from, char to); | ||
239 | unsigned long convert_unit(unsigned long value, char *unit); | 244 | unsigned long convert_unit(unsigned long value, char *unit); |
240 | int readn(int fd, void *buf, size_t size); | 245 | int readn(int fd, void *buf, size_t size); |
241 | 246 | ||
@@ -258,9 +263,12 @@ bool is_power_of_2(unsigned long n) | |||
258 | } | 263 | } |
259 | 264 | ||
260 | size_t hex_width(u64 v); | 265 | size_t hex_width(u64 v); |
266 | int hex2u64(const char *ptr, u64 *val); | ||
261 | 267 | ||
262 | char *rtrim(char *s); | 268 | char *rtrim(char *s); |
263 | 269 | ||
264 | void dump_stack(void); | 270 | void dump_stack(void); |
265 | 271 | ||
272 | extern unsigned int page_size; | ||
273 | |||
266 | #endif | 274 | #endif |