diff options
Diffstat (limited to 'tools/perf')
32 files changed, 1966 insertions, 429 deletions
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 3caf7dab50e8..aecf61dcd754 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -66,6 +66,9 @@ include config/utilities.mak | |||
66 | # | 66 | # |
67 | # Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 | 67 | # Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 |
68 | # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode | 68 | # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode |
69 | # | ||
70 | # Define NO_ZLIB if you do not want to support compressed kernel modules | ||
71 | |||
69 | 72 | ||
70 | ifeq ($(srctree),) | 73 | ifeq ($(srctree),) |
71 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | 74 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) |
@@ -317,6 +320,7 @@ LIB_H += ui/util.h | |||
317 | LIB_H += ui/ui.h | 320 | LIB_H += ui/ui.h |
318 | LIB_H += util/data.h | 321 | LIB_H += util/data.h |
319 | LIB_H += util/kvm-stat.h | 322 | LIB_H += util/kvm-stat.h |
323 | LIB_H += util/thread-stack.h | ||
320 | 324 | ||
321 | LIB_OBJS += $(OUTPUT)util/abspath.o | 325 | LIB_OBJS += $(OUTPUT)util/abspath.o |
322 | LIB_OBJS += $(OUTPUT)util/alias.o | 326 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -394,6 +398,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o | |||
394 | LIB_OBJS += $(OUTPUT)util/data.o | 398 | LIB_OBJS += $(OUTPUT)util/data.o |
395 | LIB_OBJS += $(OUTPUT)util/tsc.o | 399 | LIB_OBJS += $(OUTPUT)util/tsc.o |
396 | LIB_OBJS += $(OUTPUT)util/cloexec.o | 400 | LIB_OBJS += $(OUTPUT)util/cloexec.o |
401 | LIB_OBJS += $(OUTPUT)util/thread-stack.o | ||
397 | 402 | ||
398 | LIB_OBJS += $(OUTPUT)ui/setup.o | 403 | LIB_OBJS += $(OUTPUT)ui/setup.o |
399 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 404 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
@@ -582,6 +587,10 @@ ifndef NO_LIBNUMA | |||
582 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o | 587 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o |
583 | endif | 588 | endif |
584 | 589 | ||
590 | ifndef NO_ZLIB | ||
591 | LIB_OBJS += $(OUTPUT)util/zlib.o | ||
592 | endif | ||
593 | |||
585 | ifdef ASCIIDOC8 | 594 | ifdef ASCIIDOC8 |
586 | export ASCIIDOC8 | 595 | export ASCIIDOC8 |
587 | endif | 596 | endif |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5091a27e6d28..582c4da155ea 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -200,6 +200,17 @@ static int process_buildids(struct record *rec) | |||
200 | if (size == 0) | 200 | if (size == 0) |
201 | return 0; | 201 | return 0; |
202 | 202 | ||
203 | /* | ||
204 | * During this process, it'll load kernel map and replace the | ||
205 | * dso->long_name to a real pathname it found. In this case | ||
206 | * we prefer the vmlinux path like | ||
207 | * /lib/modules/3.16.4/build/vmlinux | ||
208 | * | ||
209 | * rather than build-id path (in debug directory). | ||
210 | * $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551 | ||
211 | */ | ||
212 | symbol_conf.ignore_vmlinux_buildid = true; | ||
213 | |||
203 | return __perf_session__process_events(session, start, | 214 | return __perf_session__process_events(session, start, |
204 | size - start, | 215 | size - start, |
205 | size, &build_id__mark_dso_hit_ops); | 216 | size, &build_id__mark_dso_hit_ops); |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 71264e41fa85..79f906c7124e 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -200,7 +200,8 @@ CORE_FEATURE_TESTS = \ | |||
200 | libunwind \ | 200 | libunwind \ |
201 | stackprotector-all \ | 201 | stackprotector-all \ |
202 | timerfd \ | 202 | timerfd \ |
203 | libdw-dwarf-unwind | 203 | libdw-dwarf-unwind \ |
204 | zlib | ||
204 | 205 | ||
205 | LIB_FEATURE_TESTS = \ | 206 | LIB_FEATURE_TESTS = \ |
206 | dwarf \ | 207 | dwarf \ |
@@ -214,7 +215,8 @@ LIB_FEATURE_TESTS = \ | |||
214 | libpython \ | 215 | libpython \ |
215 | libslang \ | 216 | libslang \ |
216 | libunwind \ | 217 | libunwind \ |
217 | libdw-dwarf-unwind | 218 | libdw-dwarf-unwind \ |
219 | zlib | ||
218 | 220 | ||
219 | VF_FEATURE_TESTS = \ | 221 | VF_FEATURE_TESTS = \ |
220 | backtrace \ | 222 | backtrace \ |
@@ -604,6 +606,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),) | |||
604 | CFLAGS += -DHAVE_LIBBFD_SUPPORT | 606 | CFLAGS += -DHAVE_LIBBFD_SUPPORT |
605 | endif | 607 | endif |
606 | 608 | ||
609 | ifndef NO_ZLIB | ||
610 | ifeq ($(feature-zlib), 1) | ||
611 | CFLAGS += -DHAVE_ZLIB_SUPPORT | ||
612 | EXTLIBS += -lz | ||
613 | else | ||
614 | NO_ZLIB := 1 | ||
615 | endif | ||
616 | endif | ||
617 | |||
607 | ifndef NO_BACKTRACE | 618 | ifndef NO_BACKTRACE |
608 | ifeq ($(feature-backtrace), 1) | 619 | ifeq ($(feature-backtrace), 1) |
609 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT | 620 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT |
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 7c68ec74a808..53f19b5dbc37 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
@@ -29,7 +29,8 @@ FILES= \ | |||
29 | test-timerfd.bin \ | 29 | test-timerfd.bin \ |
30 | test-libdw-dwarf-unwind.bin \ | 30 | test-libdw-dwarf-unwind.bin \ |
31 | test-compile-32.bin \ | 31 | test-compile-32.bin \ |
32 | test-compile-x32.bin | 32 | test-compile-x32.bin \ |
33 | test-zlib.bin | ||
33 | 34 | ||
34 | CC := $(CROSS_COMPILE)gcc -MD | 35 | CC := $(CROSS_COMPILE)gcc -MD |
35 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 36 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
@@ -41,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) | |||
41 | ############################### | 42 | ############################### |
42 | 43 | ||
43 | test-all.bin: | 44 | test-all.bin: |
44 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl | 45 | $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz |
45 | 46 | ||
46 | test-hello.bin: | 47 | test-hello.bin: |
47 | $(BUILD) | 48 | $(BUILD) |
@@ -139,6 +140,9 @@ test-compile-32.bin: | |||
139 | test-compile-x32.bin: | 140 | test-compile-x32.bin: |
140 | $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c | 141 | $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c |
141 | 142 | ||
143 | test-zlib.bin: | ||
144 | $(BUILD) -lz | ||
145 | |||
142 | -include *.d | 146 | -include *.d |
143 | 147 | ||
144 | ############################### | 148 | ############################### |
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index a7d022e161c0..652e0098eba6 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
@@ -93,6 +93,10 @@ | |||
93 | # include "test-sync-compare-and-swap.c" | 93 | # include "test-sync-compare-and-swap.c" |
94 | #undef main | 94 | #undef main |
95 | 95 | ||
96 | #define main main_test_zlib | ||
97 | # include "test-zlib.c" | ||
98 | #undef main | ||
99 | |||
96 | int main(int argc, char *argv[]) | 100 | int main(int argc, char *argv[]) |
97 | { | 101 | { |
98 | main_test_libpython(); | 102 | main_test_libpython(); |
@@ -116,6 +120,7 @@ int main(int argc, char *argv[]) | |||
116 | main_test_stackprotector_all(); | 120 | main_test_stackprotector_all(); |
117 | main_test_libdw_dwarf_unwind(); | 121 | main_test_libdw_dwarf_unwind(); |
118 | main_test_sync_compare_and_swap(argc, argv); | 122 | main_test_sync_compare_and_swap(argc, argv); |
123 | main_test_zlib(); | ||
119 | 124 | ||
120 | return 0; | 125 | return 0; |
121 | } | 126 | } |
diff --git a/tools/perf/config/feature-checks/test-zlib.c b/tools/perf/config/feature-checks/test-zlib.c new file mode 100644 index 000000000000..e111fff6240e --- /dev/null +++ b/tools/perf/config/feature-checks/test-zlib.c | |||
@@ -0,0 +1,9 @@ | |||
1 | #include <zlib.h> | ||
2 | |||
3 | int main(void) | ||
4 | { | ||
5 | z_stream zs; | ||
6 | |||
7 | inflateInit(&zs); | ||
8 | return 0; | ||
9 | } | ||
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report index a8fdd15f85bf..cd335b6e2a01 100644 --- a/tools/perf/scripts/python/bin/export-to-postgresql-report +++ b/tools/perf/scripts/python/bin/export-to-postgresql-report | |||
@@ -1,6 +1,6 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: export perf data to a postgresql database | 2 | # description: export perf data to a postgresql database |
3 | # args: [database name] [columns] | 3 | # args: [database name] [columns] [calls] |
4 | n_args=0 | 4 | n_args=0 |
5 | for i in "$@" | 5 | for i in "$@" |
6 | do | 6 | do |
@@ -9,11 +9,16 @@ do | |||
9 | fi | 9 | fi |
10 | n_args=$(( $n_args + 1 )) | 10 | n_args=$(( $n_args + 1 )) |
11 | done | 11 | done |
12 | if [ "$n_args" -gt 2 ] ; then | 12 | if [ "$n_args" -gt 3 ] ; then |
13 | echo "usage: export-to-postgresql-report [database name] [columns]" | 13 | echo "usage: export-to-postgresql-report [database name] [columns] [calls]" |
14 | exit | 14 | exit |
15 | fi | 15 | fi |
16 | if [ "$n_args" -gt 1 ] ; then | 16 | if [ "$n_args" -gt 2 ] ; then |
17 | dbname=$1 | ||
18 | columns=$2 | ||
19 | calls=$3 | ||
20 | shift 3 | ||
21 | elif [ "$n_args" -gt 1 ] ; then | ||
17 | dbname=$1 | 22 | dbname=$1 |
18 | columns=$2 | 23 | columns=$2 |
19 | shift 2 | 24 | shift 2 |
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then | |||
21 | dbname=$1 | 26 | dbname=$1 |
22 | shift | 27 | shift |
23 | fi | 28 | fi |
24 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns | 29 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls |
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py index d8f6df0093d6..4cdafd880074 100644 --- a/tools/perf/scripts/python/export-to-postgresql.py +++ b/tools/perf/scripts/python/export-to-postgresql.py | |||
@@ -40,10 +40,12 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | |||
40 | #from Core import * | 40 | #from Core import * |
41 | 41 | ||
42 | perf_db_export_mode = True | 42 | perf_db_export_mode = True |
43 | perf_db_export_calls = False | ||
43 | 44 | ||
44 | def usage(): | 45 | def usage(): |
45 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]" | 46 | print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]" |
46 | print >> sys.stderr, "where: columns 'all' or 'branches'" | 47 | print >> sys.stderr, "where: columns 'all' or 'branches'" |
48 | print >> sys.stderr, " calls 'calls' => create calls table" | ||
47 | raise Exception("Too few arguments") | 49 | raise Exception("Too few arguments") |
48 | 50 | ||
49 | if (len(sys.argv) < 2): | 51 | if (len(sys.argv) < 2): |
@@ -61,6 +63,12 @@ if columns not in ("all", "branches"): | |||
61 | 63 | ||
62 | branches = (columns == "branches") | 64 | branches = (columns == "branches") |
63 | 65 | ||
66 | if (len(sys.argv) >= 4): | ||
67 | if (sys.argv[3] == "calls"): | ||
68 | perf_db_export_calls = True | ||
69 | else: | ||
70 | usage() | ||
71 | |||
64 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" | 72 | output_dir_name = os.getcwd() + "/" + dbname + "-perf-data" |
65 | os.mkdir(output_dir_name) | 73 | os.mkdir(output_dir_name) |
66 | 74 | ||
@@ -123,6 +131,10 @@ do_query(query, 'CREATE TABLE symbols (' | |||
123 | 'sym_end bigint,' | 131 | 'sym_end bigint,' |
124 | 'binding integer,' | 132 | 'binding integer,' |
125 | 'name varchar(2048))') | 133 | 'name varchar(2048))') |
134 | do_query(query, 'CREATE TABLE branch_types (' | ||
135 | 'id integer NOT NULL,' | ||
136 | 'name varchar(80))') | ||
137 | |||
126 | if branches: | 138 | if branches: |
127 | do_query(query, 'CREATE TABLE samples (' | 139 | do_query(query, 'CREATE TABLE samples (' |
128 | 'id bigint NOT NULL,' | 140 | 'id bigint NOT NULL,' |
@@ -139,7 +151,9 @@ if branches: | |||
139 | 'to_dso_id bigint,' | 151 | 'to_dso_id bigint,' |
140 | 'to_symbol_id bigint,' | 152 | 'to_symbol_id bigint,' |
141 | 'to_sym_offset bigint,' | 153 | 'to_sym_offset bigint,' |
142 | 'to_ip bigint)') | 154 | 'to_ip bigint,' |
155 | 'branch_type integer,' | ||
156 | 'in_tx boolean)') | ||
143 | else: | 157 | else: |
144 | do_query(query, 'CREATE TABLE samples (' | 158 | do_query(query, 'CREATE TABLE samples (' |
145 | 'id bigint NOT NULL,' | 159 | 'id bigint NOT NULL,' |
@@ -160,7 +174,28 @@ else: | |||
160 | 'period bigint,' | 174 | 'period bigint,' |
161 | 'weight bigint,' | 175 | 'weight bigint,' |
162 | 'transaction bigint,' | 176 | 'transaction bigint,' |
163 | 'data_src bigint)') | 177 | 'data_src bigint,' |
178 | 'branch_type integer,' | ||
179 | 'in_tx boolean)') | ||
180 | |||
181 | if perf_db_export_calls: | ||
182 | do_query(query, 'CREATE TABLE call_paths (' | ||
183 | 'id bigint NOT NULL,' | ||
184 | 'parent_id bigint,' | ||
185 | 'symbol_id bigint,' | ||
186 | 'ip bigint)') | ||
187 | do_query(query, 'CREATE TABLE calls (' | ||
188 | 'id bigint NOT NULL,' | ||
189 | 'thread_id bigint,' | ||
190 | 'comm_id bigint,' | ||
191 | 'call_path_id bigint,' | ||
192 | 'call_time bigint,' | ||
193 | 'return_time bigint,' | ||
194 | 'branch_count bigint,' | ||
195 | 'call_id bigint,' | ||
196 | 'return_id bigint,' | ||
197 | 'parent_call_path_id bigint,' | ||
198 | 'flags integer)') | ||
164 | 199 | ||
165 | do_query(query, 'CREATE VIEW samples_view AS ' | 200 | do_query(query, 'CREATE VIEW samples_view AS ' |
166 | 'SELECT ' | 201 | 'SELECT ' |
@@ -178,7 +213,9 @@ do_query(query, 'CREATE VIEW samples_view AS ' | |||
178 | 'to_hex(to_ip) AS to_ip_hex,' | 213 | 'to_hex(to_ip) AS to_ip_hex,' |
179 | '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' | 214 | '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' |
180 | 'to_sym_offset,' | 215 | 'to_sym_offset,' |
181 | '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name' | 216 | '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' |
217 | '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' | ||
218 | 'in_tx' | ||
182 | ' FROM samples') | 219 | ' FROM samples') |
183 | 220 | ||
184 | 221 | ||
@@ -234,7 +271,11 @@ comm_file = open_output_file("comm_table.bin") | |||
234 | comm_thread_file = open_output_file("comm_thread_table.bin") | 271 | comm_thread_file = open_output_file("comm_thread_table.bin") |
235 | dso_file = open_output_file("dso_table.bin") | 272 | dso_file = open_output_file("dso_table.bin") |
236 | symbol_file = open_output_file("symbol_table.bin") | 273 | symbol_file = open_output_file("symbol_table.bin") |
274 | branch_type_file = open_output_file("branch_type_table.bin") | ||
237 | sample_file = open_output_file("sample_table.bin") | 275 | sample_file = open_output_file("sample_table.bin") |
276 | if perf_db_export_calls: | ||
277 | call_path_file = open_output_file("call_path_table.bin") | ||
278 | call_file = open_output_file("call_table.bin") | ||
238 | 279 | ||
239 | def trace_begin(): | 280 | def trace_begin(): |
240 | print datetime.datetime.today(), "Writing to intermediate files..." | 281 | print datetime.datetime.today(), "Writing to intermediate files..." |
@@ -245,6 +286,9 @@ def trace_begin(): | |||
245 | comm_table(0, "unknown") | 286 | comm_table(0, "unknown") |
246 | dso_table(0, 0, "unknown", "unknown", "") | 287 | dso_table(0, 0, "unknown", "unknown", "") |
247 | symbol_table(0, 0, 0, 0, 0, "unknown") | 288 | symbol_table(0, 0, 0, 0, 0, "unknown") |
289 | sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | ||
290 | if perf_db_export_calls: | ||
291 | call_path_table(0, 0, 0, 0) | ||
248 | 292 | ||
249 | unhandled_count = 0 | 293 | unhandled_count = 0 |
250 | 294 | ||
@@ -257,7 +301,11 @@ def trace_end(): | |||
257 | copy_output_file(comm_thread_file, "comm_threads") | 301 | copy_output_file(comm_thread_file, "comm_threads") |
258 | copy_output_file(dso_file, "dsos") | 302 | copy_output_file(dso_file, "dsos") |
259 | copy_output_file(symbol_file, "symbols") | 303 | copy_output_file(symbol_file, "symbols") |
304 | copy_output_file(branch_type_file, "branch_types") | ||
260 | copy_output_file(sample_file, "samples") | 305 | copy_output_file(sample_file, "samples") |
306 | if perf_db_export_calls: | ||
307 | copy_output_file(call_path_file, "call_paths") | ||
308 | copy_output_file(call_file, "calls") | ||
261 | 309 | ||
262 | print datetime.datetime.today(), "Removing intermediate files..." | 310 | print datetime.datetime.today(), "Removing intermediate files..." |
263 | remove_output_file(evsel_file) | 311 | remove_output_file(evsel_file) |
@@ -267,7 +315,11 @@ def trace_end(): | |||
267 | remove_output_file(comm_thread_file) | 315 | remove_output_file(comm_thread_file) |
268 | remove_output_file(dso_file) | 316 | remove_output_file(dso_file) |
269 | remove_output_file(symbol_file) | 317 | remove_output_file(symbol_file) |
318 | remove_output_file(branch_type_file) | ||
270 | remove_output_file(sample_file) | 319 | remove_output_file(sample_file) |
320 | if perf_db_export_calls: | ||
321 | remove_output_file(call_path_file) | ||
322 | remove_output_file(call_file) | ||
271 | os.rmdir(output_dir_name) | 323 | os.rmdir(output_dir_name) |
272 | print datetime.datetime.today(), "Adding primary keys" | 324 | print datetime.datetime.today(), "Adding primary keys" |
273 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') | 325 | do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') |
@@ -277,7 +329,11 @@ def trace_end(): | |||
277 | do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') | 329 | do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') |
278 | do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') | 330 | do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') |
279 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') | 331 | do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') |
332 | do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') | ||
280 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') | 333 | do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') |
334 | if perf_db_export_calls: | ||
335 | do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)') | ||
336 | do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') | ||
281 | 337 | ||
282 | print datetime.datetime.today(), "Adding foreign keys" | 338 | print datetime.datetime.today(), "Adding foreign keys" |
283 | do_query(query, 'ALTER TABLE threads ' | 339 | do_query(query, 'ALTER TABLE threads ' |
@@ -299,6 +355,18 @@ def trace_end(): | |||
299 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' | 355 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),' |
300 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' | 356 | 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),' |
301 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') | 357 | 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)') |
358 | if perf_db_export_calls: | ||
359 | do_query(query, 'ALTER TABLE call_paths ' | ||
360 | 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),' | ||
361 | 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)') | ||
362 | do_query(query, 'ALTER TABLE calls ' | ||
363 | 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),' | ||
364 | 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),' | ||
365 | 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),' | ||
366 | 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),' | ||
367 | 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),' | ||
368 | 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)') | ||
369 | do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') | ||
302 | 370 | ||
303 | if (unhandled_count): | 371 | if (unhandled_count): |
304 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" | 372 | print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" |
@@ -352,9 +420,25 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x | |||
352 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) | 420 | value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) |
353 | symbol_file.write(value) | 421 | symbol_file.write(value) |
354 | 422 | ||
355 | def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x): | 423 | def branch_type_table(branch_type, name, *x): |
424 | n = len(name) | ||
425 | fmt = "!hiii" + str(n) + "s" | ||
426 | value = struct.pack(fmt, 2, 4, branch_type, n, name) | ||
427 | branch_type_file.write(value) | ||
428 | |||
429 | def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x): | ||
356 | if branches: | 430 | if branches: |
357 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip) | 431 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx) |
358 | else: | 432 | else: |
359 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src) | 433 | value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx) |
360 | sample_file.write(value) | 434 | sample_file.write(value) |
435 | |||
436 | def call_path_table(cp_id, parent_id, symbol_id, ip, *x): | ||
437 | fmt = "!hiqiqiqiq" | ||
438 | value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip) | ||
439 | call_path_file.write(value) | ||
440 | |||
441 | def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x): | ||
442 | fmt = "!hiqiqiqiqiqiqiqiqiqiqii" | ||
443 | value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags) | ||
444 | call_file.write(value) | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 2e7c68e39330..dd2a3e52ada1 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "session.h" | 16 | #include "session.h" |
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | #include "header.h" | ||
19 | #include "vdso.h" | ||
18 | 20 | ||
19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 21 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 22 | union perf_event *event, |
@@ -105,3 +107,335 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) | |||
105 | build_id_hex, build_id_hex + 2); | 107 | build_id_hex, build_id_hex + 2); |
106 | return bf; | 108 | return bf; |
107 | } | 109 | } |
110 | |||
111 | #define dsos__for_each_with_build_id(pos, head) \ | ||
112 | list_for_each_entry(pos, head, node) \ | ||
113 | if (!pos->has_build_id) \ | ||
114 | continue; \ | ||
115 | else | ||
116 | |||
117 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
118 | pid_t pid, u16 misc, int fd) | ||
119 | { | ||
120 | int err; | ||
121 | struct build_id_event b; | ||
122 | size_t len; | ||
123 | |||
124 | len = name_len + 1; | ||
125 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
126 | |||
127 | memset(&b, 0, sizeof(b)); | ||
128 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
129 | b.pid = pid; | ||
130 | b.header.misc = misc; | ||
131 | b.header.size = sizeof(b) + len; | ||
132 | |||
133 | err = writen(fd, &b, sizeof(b)); | ||
134 | if (err < 0) | ||
135 | return err; | ||
136 | |||
137 | return write_padded(fd, name, name_len + 1, len); | ||
138 | } | ||
139 | |||
140 | static int __dsos__write_buildid_table(struct list_head *head, | ||
141 | struct machine *machine, | ||
142 | pid_t pid, u16 misc, int fd) | ||
143 | { | ||
144 | char nm[PATH_MAX]; | ||
145 | struct dso *pos; | ||
146 | |||
147 | dsos__for_each_with_build_id(pos, head) { | ||
148 | int err; | ||
149 | const char *name; | ||
150 | size_t name_len; | ||
151 | |||
152 | if (!pos->hit) | ||
153 | continue; | ||
154 | |||
155 | if (dso__is_vdso(pos)) { | ||
156 | name = pos->short_name; | ||
157 | name_len = pos->short_name_len + 1; | ||
158 | } else if (dso__is_kcore(pos)) { | ||
159 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
160 | name = nm; | ||
161 | name_len = strlen(nm) + 1; | ||
162 | } else { | ||
163 | name = pos->long_name; | ||
164 | name_len = pos->long_name_len + 1; | ||
165 | } | ||
166 | |||
167 | err = write_buildid(name, name_len, pos->build_id, | ||
168 | pid, misc, fd); | ||
169 | if (err) | ||
170 | return err; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
177 | { | ||
178 | int err; | ||
179 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
180 | umisc = PERF_RECORD_MISC_USER; | ||
181 | |||
182 | if (!machine__is_host(machine)) { | ||
183 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
184 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
185 | } | ||
186 | |||
187 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, | ||
188 | machine->pid, kmisc, fd); | ||
189 | if (err == 0) | ||
190 | err = __dsos__write_buildid_table(&machine->user_dsos.head, | ||
191 | machine, machine->pid, umisc, | ||
192 | fd); | ||
193 | return err; | ||
194 | } | ||
195 | |||
196 | int perf_session__write_buildid_table(struct perf_session *session, int fd) | ||
197 | { | ||
198 | struct rb_node *nd; | ||
199 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
200 | |||
201 | if (err) | ||
202 | return err; | ||
203 | |||
204 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
205 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
206 | err = machine__write_buildid_table(pos, fd); | ||
207 | if (err) | ||
208 | break; | ||
209 | } | ||
210 | return err; | ||
211 | } | ||
212 | |||
213 | static int __dsos__hit_all(struct list_head *head) | ||
214 | { | ||
215 | struct dso *pos; | ||
216 | |||
217 | list_for_each_entry(pos, head, node) | ||
218 | pos->hit = true; | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static int machine__hit_all_dsos(struct machine *machine) | ||
224 | { | ||
225 | int err; | ||
226 | |||
227 | err = __dsos__hit_all(&machine->kernel_dsos.head); | ||
228 | if (err) | ||
229 | return err; | ||
230 | |||
231 | return __dsos__hit_all(&machine->user_dsos.head); | ||
232 | } | ||
233 | |||
234 | int dsos__hit_all(struct perf_session *session) | ||
235 | { | ||
236 | struct rb_node *nd; | ||
237 | int err; | ||
238 | |||
239 | err = machine__hit_all_dsos(&session->machines.host); | ||
240 | if (err) | ||
241 | return err; | ||
242 | |||
243 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
244 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
245 | |||
246 | err = machine__hit_all_dsos(pos); | ||
247 | if (err) | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
255 | const char *name, bool is_kallsyms, bool is_vdso) | ||
256 | { | ||
257 | const size_t size = PATH_MAX; | ||
258 | char *realname, *filename = zalloc(size), | ||
259 | *linkname = zalloc(size), *targetname; | ||
260 | int len, err = -1; | ||
261 | bool slash = is_kallsyms || is_vdso; | ||
262 | |||
263 | if (is_kallsyms) { | ||
264 | if (symbol_conf.kptr_restrict) { | ||
265 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
266 | err = 0; | ||
267 | goto out_free; | ||
268 | } | ||
269 | realname = (char *) name; | ||
270 | } else | ||
271 | realname = realpath(name, NULL); | ||
272 | |||
273 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
274 | goto out_free; | ||
275 | |||
276 | len = scnprintf(filename, size, "%s%s%s", | ||
277 | debugdir, slash ? "/" : "", | ||
278 | is_vdso ? DSO__NAME_VDSO : realname); | ||
279 | if (mkdir_p(filename, 0755)) | ||
280 | goto out_free; | ||
281 | |||
282 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
283 | |||
284 | if (access(filename, F_OK)) { | ||
285 | if (is_kallsyms) { | ||
286 | if (copyfile("/proc/kallsyms", filename)) | ||
287 | goto out_free; | ||
288 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
289 | goto out_free; | ||
290 | } | ||
291 | |||
292 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
293 | debugdir, sbuild_id); | ||
294 | |||
295 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
296 | goto out_free; | ||
297 | |||
298 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
299 | targetname = filename + strlen(debugdir) - 5; | ||
300 | memcpy(targetname, "../..", 5); | ||
301 | |||
302 | if (symlink(targetname, linkname) == 0) | ||
303 | err = 0; | ||
304 | out_free: | ||
305 | if (!is_kallsyms) | ||
306 | free(realname); | ||
307 | free(filename); | ||
308 | free(linkname); | ||
309 | return err; | ||
310 | } | ||
311 | |||
312 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
313 | const char *name, const char *debugdir, | ||
314 | bool is_kallsyms, bool is_vdso) | ||
315 | { | ||
316 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
317 | |||
318 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
319 | |||
320 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
321 | is_kallsyms, is_vdso); | ||
322 | } | ||
323 | |||
324 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
325 | { | ||
326 | const size_t size = PATH_MAX; | ||
327 | char *filename = zalloc(size), | ||
328 | *linkname = zalloc(size); | ||
329 | int err = -1; | ||
330 | |||
331 | if (filename == NULL || linkname == NULL) | ||
332 | goto out_free; | ||
333 | |||
334 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
335 | debugdir, sbuild_id, sbuild_id + 2); | ||
336 | |||
337 | if (access(linkname, F_OK)) | ||
338 | goto out_free; | ||
339 | |||
340 | if (readlink(linkname, filename, size - 1) < 0) | ||
341 | goto out_free; | ||
342 | |||
343 | if (unlink(linkname)) | ||
344 | goto out_free; | ||
345 | |||
346 | /* | ||
347 | * Since the link is relative, we must make it absolute: | ||
348 | */ | ||
349 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
350 | debugdir, sbuild_id, filename); | ||
351 | |||
352 | if (unlink(linkname)) | ||
353 | goto out_free; | ||
354 | |||
355 | err = 0; | ||
356 | out_free: | ||
357 | free(filename); | ||
358 | free(linkname); | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
363 | const char *debugdir) | ||
364 | { | ||
365 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
366 | bool is_vdso = dso__is_vdso(dso); | ||
367 | const char *name = dso->long_name; | ||
368 | char nm[PATH_MAX]; | ||
369 | |||
370 | if (dso__is_kcore(dso)) { | ||
371 | is_kallsyms = true; | ||
372 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
373 | name = nm; | ||
374 | } | ||
375 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
376 | debugdir, is_kallsyms, is_vdso); | ||
377 | } | ||
378 | |||
379 | static int __dsos__cache_build_ids(struct list_head *head, | ||
380 | struct machine *machine, const char *debugdir) | ||
381 | { | ||
382 | struct dso *pos; | ||
383 | int err = 0; | ||
384 | |||
385 | dsos__for_each_with_build_id(pos, head) | ||
386 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
387 | err = -1; | ||
388 | |||
389 | return err; | ||
390 | } | ||
391 | |||
392 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
393 | { | ||
394 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, | ||
395 | debugdir); | ||
396 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, | ||
397 | debugdir); | ||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | int perf_session__cache_build_ids(struct perf_session *session) | ||
402 | { | ||
403 | struct rb_node *nd; | ||
404 | int ret; | ||
405 | char debugdir[PATH_MAX]; | ||
406 | |||
407 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | ||
408 | |||
409 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
410 | return -1; | ||
411 | |||
412 | ret = machine__cache_build_ids(&session->machines.host, debugdir); | ||
413 | |||
414 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
415 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
416 | ret |= machine__cache_build_ids(pos, debugdir); | ||
417 | } | ||
418 | return ret ? -1 : 0; | ||
419 | } | ||
420 | |||
421 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
422 | { | ||
423 | bool ret; | ||
424 | |||
425 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
426 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
431 | { | ||
432 | struct rb_node *nd; | ||
433 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
434 | |||
435 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
436 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
437 | ret |= machine__read_build_ids(pos, with_hits); | ||
438 | } | ||
439 | |||
440 | return ret; | ||
441 | } | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index ae392561470b..666a3bd4f64e 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -15,4 +15,15 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); | |||
15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | 15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, |
16 | struct perf_sample *sample, struct perf_evsel *evsel, | 16 | struct perf_sample *sample, struct perf_evsel *evsel, |
17 | struct machine *machine); | 17 | struct machine *machine); |
18 | |||
19 | int dsos__hit_all(struct perf_session *session); | ||
20 | |||
21 | bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); | ||
22 | int perf_session__write_buildid_table(struct perf_session *session, int fd); | ||
23 | int perf_session__cache_build_ids(struct perf_session *session); | ||
24 | |||
25 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
26 | const char *name, bool is_kallsyms, bool is_vdso); | ||
27 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
28 | |||
18 | #endif | 29 | #endif |
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c index be128b075a32..c81dae399763 100644 --- a/tools/perf/util/db-export.c +++ b/tools/perf/util/db-export.c | |||
@@ -21,16 +21,76 @@ | |||
21 | #include "comm.h" | 21 | #include "comm.h" |
22 | #include "symbol.h" | 22 | #include "symbol.h" |
23 | #include "event.h" | 23 | #include "event.h" |
24 | #include "util.h" | ||
25 | #include "thread-stack.h" | ||
24 | #include "db-export.h" | 26 | #include "db-export.h" |
25 | 27 | ||
28 | struct deferred_export { | ||
29 | struct list_head node; | ||
30 | struct comm *comm; | ||
31 | }; | ||
32 | |||
33 | static int db_export__deferred(struct db_export *dbe) | ||
34 | { | ||
35 | struct deferred_export *de; | ||
36 | int err; | ||
37 | |||
38 | while (!list_empty(&dbe->deferred)) { | ||
39 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
40 | node); | ||
41 | err = dbe->export_comm(dbe, de->comm); | ||
42 | list_del(&de->node); | ||
43 | free(de); | ||
44 | if (err) | ||
45 | return err; | ||
46 | } | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static void db_export__free_deferred(struct db_export *dbe) | ||
52 | { | ||
53 | struct deferred_export *de; | ||
54 | |||
55 | while (!list_empty(&dbe->deferred)) { | ||
56 | de = list_entry(dbe->deferred.next, struct deferred_export, | ||
57 | node); | ||
58 | list_del(&de->node); | ||
59 | free(de); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | static int db_export__defer_comm(struct db_export *dbe, struct comm *comm) | ||
64 | { | ||
65 | struct deferred_export *de; | ||
66 | |||
67 | de = zalloc(sizeof(struct deferred_export)); | ||
68 | if (!de) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | de->comm = comm; | ||
72 | list_add_tail(&de->node, &dbe->deferred); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
26 | int db_export__init(struct db_export *dbe) | 77 | int db_export__init(struct db_export *dbe) |
27 | { | 78 | { |
28 | memset(dbe, 0, sizeof(struct db_export)); | 79 | memset(dbe, 0, sizeof(struct db_export)); |
80 | INIT_LIST_HEAD(&dbe->deferred); | ||
29 | return 0; | 81 | return 0; |
30 | } | 82 | } |
31 | 83 | ||
32 | void db_export__exit(struct db_export *dbe __maybe_unused) | 84 | int db_export__flush(struct db_export *dbe) |
85 | { | ||
86 | return db_export__deferred(dbe); | ||
87 | } | ||
88 | |||
89 | void db_export__exit(struct db_export *dbe) | ||
33 | { | 90 | { |
91 | db_export__free_deferred(dbe); | ||
92 | call_return_processor__free(dbe->crp); | ||
93 | dbe->crp = NULL; | ||
34 | } | 94 | } |
35 | 95 | ||
36 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel) | 96 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel) |
@@ -112,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm, | |||
112 | comm->db_id = ++dbe->comm_last_db_id; | 172 | comm->db_id = ++dbe->comm_last_db_id; |
113 | 173 | ||
114 | if (dbe->export_comm) { | 174 | if (dbe->export_comm) { |
115 | err = dbe->export_comm(dbe, comm); | 175 | if (main_thread->comm_set) |
176 | err = dbe->export_comm(dbe, comm); | ||
177 | else | ||
178 | err = db_export__defer_comm(dbe, comm); | ||
116 | if (err) | 179 | if (err) |
117 | return err; | 180 | return err; |
118 | } | 181 | } |
@@ -208,6 +271,15 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, | |||
208 | return 0; | 271 | return 0; |
209 | } | 272 | } |
210 | 273 | ||
274 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
275 | const char *name) | ||
276 | { | ||
277 | if (dbe->export_branch_type) | ||
278 | return dbe->export_branch_type(dbe, branch_type, name); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
211 | int db_export__sample(struct db_export *dbe, union perf_event *event, | 283 | int db_export__sample(struct db_export *dbe, union perf_event *event, |
212 | struct perf_sample *sample, struct perf_evsel *evsel, | 284 | struct perf_sample *sample, struct perf_evsel *evsel, |
213 | struct thread *thread, struct addr_location *al) | 285 | struct thread *thread, struct addr_location *al) |
@@ -261,6 +333,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, | |||
261 | &es.addr_sym_db_id, &es.addr_offset); | 333 | &es.addr_sym_db_id, &es.addr_offset); |
262 | if (err) | 334 | if (err) |
263 | return err; | 335 | return err; |
336 | if (dbe->crp) { | ||
337 | err = thread_stack__process(thread, comm, sample, al, | ||
338 | &addr_al, es.db_id, | ||
339 | dbe->crp); | ||
340 | if (err) | ||
341 | return err; | ||
342 | } | ||
264 | } | 343 | } |
265 | 344 | ||
266 | if (dbe->export_sample) | 345 | if (dbe->export_sample) |
@@ -268,3 +347,82 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, | |||
268 | 347 | ||
269 | return 0; | 348 | return 0; |
270 | } | 349 | } |
350 | |||
351 | static struct { | ||
352 | u32 branch_type; | ||
353 | const char *name; | ||
354 | } branch_types[] = { | ||
355 | {0, "no branch"}, | ||
356 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"}, | ||
357 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"}, | ||
358 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"}, | ||
359 | {PERF_IP_FLAG_BRANCH, "unconditional jump"}, | ||
360 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, | ||
361 | "software interrupt"}, | ||
362 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, | ||
363 | "return from interrupt"}, | ||
364 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, | ||
365 | "system call"}, | ||
366 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, | ||
367 | "return from system call"}, | ||
368 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"}, | ||
369 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | | ||
370 | PERF_IP_FLAG_INTERRUPT, "hardware interrupt"}, | ||
371 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"}, | ||
372 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"}, | ||
373 | {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"}, | ||
374 | {0, NULL} | ||
375 | }; | ||
376 | |||
377 | int db_export__branch_types(struct db_export *dbe) | ||
378 | { | ||
379 | int i, err = 0; | ||
380 | |||
381 | for (i = 0; branch_types[i].name ; i++) { | ||
382 | err = db_export__branch_type(dbe, branch_types[i].branch_type, | ||
383 | branch_types[i].name); | ||
384 | if (err) | ||
385 | break; | ||
386 | } | ||
387 | return err; | ||
388 | } | ||
389 | |||
390 | int db_export__call_path(struct db_export *dbe, struct call_path *cp) | ||
391 | { | ||
392 | int err; | ||
393 | |||
394 | if (cp->db_id) | ||
395 | return 0; | ||
396 | |||
397 | if (cp->parent) { | ||
398 | err = db_export__call_path(dbe, cp->parent); | ||
399 | if (err) | ||
400 | return err; | ||
401 | } | ||
402 | |||
403 | cp->db_id = ++dbe->call_path_last_db_id; | ||
404 | |||
405 | if (dbe->export_call_path) | ||
406 | return dbe->export_call_path(dbe, cp); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | int db_export__call_return(struct db_export *dbe, struct call_return *cr) | ||
412 | { | ||
413 | int err; | ||
414 | |||
415 | if (cr->db_id) | ||
416 | return 0; | ||
417 | |||
418 | err = db_export__call_path(dbe, cr->cp); | ||
419 | if (err) | ||
420 | return err; | ||
421 | |||
422 | cr->db_id = ++dbe->call_return_last_db_id; | ||
423 | |||
424 | if (dbe->export_call_return) | ||
425 | return dbe->export_call_return(dbe, cr); | ||
426 | |||
427 | return 0; | ||
428 | } | ||
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h index b3643e8e5750..adbd22d66798 100644 --- a/tools/perf/util/db-export.h +++ b/tools/perf/util/db-export.h | |||
@@ -17,6 +17,7 @@ | |||
17 | #define __PERF_DB_EXPORT_H | 17 | #define __PERF_DB_EXPORT_H |
18 | 18 | ||
19 | #include <linux/types.h> | 19 | #include <linux/types.h> |
20 | #include <linux/list.h> | ||
20 | 21 | ||
21 | struct perf_evsel; | 22 | struct perf_evsel; |
22 | struct machine; | 23 | struct machine; |
@@ -25,6 +26,9 @@ struct comm; | |||
25 | struct dso; | 26 | struct dso; |
26 | struct perf_sample; | 27 | struct perf_sample; |
27 | struct addr_location; | 28 | struct addr_location; |
29 | struct call_return_processor; | ||
30 | struct call_path; | ||
31 | struct call_return; | ||
28 | 32 | ||
29 | struct export_sample { | 33 | struct export_sample { |
30 | union perf_event *event; | 34 | union perf_event *event; |
@@ -54,7 +58,13 @@ struct db_export { | |||
54 | struct machine *machine); | 58 | struct machine *machine); |
55 | int (*export_symbol)(struct db_export *dbe, struct symbol *sym, | 59 | int (*export_symbol)(struct db_export *dbe, struct symbol *sym, |
56 | struct dso *dso); | 60 | struct dso *dso); |
61 | int (*export_branch_type)(struct db_export *dbe, u32 branch_type, | ||
62 | const char *name); | ||
57 | int (*export_sample)(struct db_export *dbe, struct export_sample *es); | 63 | int (*export_sample)(struct db_export *dbe, struct export_sample *es); |
64 | int (*export_call_path)(struct db_export *dbe, struct call_path *cp); | ||
65 | int (*export_call_return)(struct db_export *dbe, | ||
66 | struct call_return *cr); | ||
67 | struct call_return_processor *crp; | ||
58 | u64 evsel_last_db_id; | 68 | u64 evsel_last_db_id; |
59 | u64 machine_last_db_id; | 69 | u64 machine_last_db_id; |
60 | u64 thread_last_db_id; | 70 | u64 thread_last_db_id; |
@@ -63,9 +73,13 @@ struct db_export { | |||
63 | u64 dso_last_db_id; | 73 | u64 dso_last_db_id; |
64 | u64 symbol_last_db_id; | 74 | u64 symbol_last_db_id; |
65 | u64 sample_last_db_id; | 75 | u64 sample_last_db_id; |
76 | u64 call_path_last_db_id; | ||
77 | u64 call_return_last_db_id; | ||
78 | struct list_head deferred; | ||
66 | }; | 79 | }; |
67 | 80 | ||
68 | int db_export__init(struct db_export *dbe); | 81 | int db_export__init(struct db_export *dbe); |
82 | int db_export__flush(struct db_export *dbe); | ||
69 | void db_export__exit(struct db_export *dbe); | 83 | void db_export__exit(struct db_export *dbe); |
70 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel); | 84 | int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel); |
71 | int db_export__machine(struct db_export *dbe, struct machine *machine); | 85 | int db_export__machine(struct db_export *dbe, struct machine *machine); |
@@ -79,8 +93,15 @@ int db_export__dso(struct db_export *dbe, struct dso *dso, | |||
79 | struct machine *machine); | 93 | struct machine *machine); |
80 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, | 94 | int db_export__symbol(struct db_export *dbe, struct symbol *sym, |
81 | struct dso *dso); | 95 | struct dso *dso); |
96 | int db_export__branch_type(struct db_export *dbe, u32 branch_type, | ||
97 | const char *name); | ||
82 | int db_export__sample(struct db_export *dbe, union perf_event *event, | 98 | int db_export__sample(struct db_export *dbe, union perf_event *event, |
83 | struct perf_sample *sample, struct perf_evsel *evsel, | 99 | struct perf_sample *sample, struct perf_evsel *evsel, |
84 | struct thread *thread, struct addr_location *al); | 100 | struct thread *thread, struct addr_location *al); |
85 | 101 | ||
102 | int db_export__branch_types(struct db_export *dbe); | ||
103 | |||
104 | int db_export__call_path(struct db_export *dbe, struct call_path *cp); | ||
105 | int db_export__call_return(struct db_export *dbe, struct call_return *cr); | ||
106 | |||
86 | #endif | 107 | #endif |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 0247acfdfaca..45be944d450a 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso) | |||
21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | 21 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', |
22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | 22 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', |
23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | 23 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', |
24 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm', | ||
24 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | 25 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', |
25 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | 26 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', |
27 | [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M', | ||
26 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | 28 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', |
27 | }; | 29 | }; |
28 | 30 | ||
@@ -112,11 +114,13 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
112 | break; | 114 | break; |
113 | 115 | ||
114 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 116 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
117 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | ||
115 | path__join3(filename, size, symbol_conf.symfs, | 118 | path__join3(filename, size, symbol_conf.symfs, |
116 | root_dir, dso->long_name); | 119 | root_dir, dso->long_name); |
117 | break; | 120 | break; |
118 | 121 | ||
119 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 122 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
123 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | ||
120 | __symbol__join_symfs(filename, size, dso->long_name); | 124 | __symbol__join_symfs(filename, size, dso->long_name); |
121 | break; | 125 | break; |
122 | 126 | ||
@@ -137,6 +141,73 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
137 | return ret; | 141 | return ret; |
138 | } | 142 | } |
139 | 143 | ||
144 | static const struct { | ||
145 | const char *fmt; | ||
146 | int (*decompress)(const char *input, int output); | ||
147 | } compressions[] = { | ||
148 | #ifdef HAVE_ZLIB_SUPPORT | ||
149 | { "gz", gzip_decompress_to_file }, | ||
150 | #endif | ||
151 | { NULL, NULL }, | ||
152 | }; | ||
153 | |||
154 | bool is_supported_compression(const char *ext) | ||
155 | { | ||
156 | unsigned i; | ||
157 | |||
158 | for (i = 0; compressions[i].fmt; i++) { | ||
159 | if (!strcmp(ext, compressions[i].fmt)) | ||
160 | return true; | ||
161 | } | ||
162 | return false; | ||
163 | } | ||
164 | |||
165 | bool is_kmodule_extension(const char *ext) | ||
166 | { | ||
167 | if (strncmp(ext, "ko", 2)) | ||
168 | return false; | ||
169 | |||
170 | if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3))) | ||
171 | return true; | ||
172 | |||
173 | return false; | ||
174 | } | ||
175 | |||
176 | bool is_kernel_module(const char *pathname, bool *compressed) | ||
177 | { | ||
178 | const char *ext = strrchr(pathname, '.'); | ||
179 | |||
180 | if (ext == NULL) | ||
181 | return false; | ||
182 | |||
183 | if (is_supported_compression(ext + 1)) { | ||
184 | if (compressed) | ||
185 | *compressed = true; | ||
186 | ext -= 3; | ||
187 | } else if (compressed) | ||
188 | *compressed = false; | ||
189 | |||
190 | return is_kmodule_extension(ext + 1); | ||
191 | } | ||
192 | |||
193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd) | ||
194 | { | ||
195 | unsigned i; | ||
196 | |||
197 | for (i = 0; compressions[i].fmt; i++) { | ||
198 | if (!strcmp(ext, compressions[i].fmt)) | ||
199 | return !compressions[i].decompress(filename, | ||
200 | output_fd); | ||
201 | } | ||
202 | return false; | ||
203 | } | ||
204 | |||
205 | bool dso__needs_decompress(struct dso *dso) | ||
206 | { | ||
207 | return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || | ||
208 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
209 | } | ||
210 | |||
140 | /* | 211 | /* |
141 | * Global list of open DSOs and the counter. | 212 | * Global list of open DSOs and the counter. |
142 | */ | 213 | */ |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index a316e4af321f..3782c82c6e44 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -22,7 +22,9 @@ enum dso_binary_type { | |||
22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 22 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 23 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
24 | DSO_BINARY_TYPE__GUEST_KMODULE, | 24 | DSO_BINARY_TYPE__GUEST_KMODULE, |
25 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
25 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 26 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
27 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
26 | DSO_BINARY_TYPE__KCORE, | 28 | DSO_BINARY_TYPE__KCORE, |
27 | DSO_BINARY_TYPE__GUEST_KCORE, | 29 | DSO_BINARY_TYPE__GUEST_KCORE, |
28 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 30 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
@@ -185,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); | |||
185 | char dso__symtab_origin(const struct dso *dso); | 187 | char dso__symtab_origin(const struct dso *dso); |
186 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 188 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
187 | char *root_dir, char *filename, size_t size); | 189 | char *root_dir, char *filename, size_t size); |
190 | bool is_supported_compression(const char *ext); | ||
191 | bool is_kmodule_extension(const char *ext); | ||
192 | bool is_kernel_module(const char *pathname, bool *compressed); | ||
193 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); | ||
194 | bool dso__needs_decompress(struct dso *dso); | ||
188 | 195 | ||
189 | /* | 196 | /* |
190 | * The dso__data_* external interface provides following functions: | 197 | * The dso__data_* external interface provides following functions: |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8c7fe9d64e79..7be389735402 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -143,6 +143,32 @@ struct branch_stack { | |||
143 | struct branch_entry entries[0]; | 143 | struct branch_entry entries[0]; |
144 | }; | 144 | }; |
145 | 145 | ||
146 | enum { | ||
147 | PERF_IP_FLAG_BRANCH = 1ULL << 0, | ||
148 | PERF_IP_FLAG_CALL = 1ULL << 1, | ||
149 | PERF_IP_FLAG_RETURN = 1ULL << 2, | ||
150 | PERF_IP_FLAG_CONDITIONAL = 1ULL << 3, | ||
151 | PERF_IP_FLAG_SYSCALLRET = 1ULL << 4, | ||
152 | PERF_IP_FLAG_ASYNC = 1ULL << 5, | ||
153 | PERF_IP_FLAG_INTERRUPT = 1ULL << 6, | ||
154 | PERF_IP_FLAG_TX_ABORT = 1ULL << 7, | ||
155 | PERF_IP_FLAG_TRACE_BEGIN = 1ULL << 8, | ||
156 | PERF_IP_FLAG_TRACE_END = 1ULL << 9, | ||
157 | PERF_IP_FLAG_IN_TX = 1ULL << 10, | ||
158 | }; | ||
159 | |||
160 | #define PERF_BRANCH_MASK (\ | ||
161 | PERF_IP_FLAG_BRANCH |\ | ||
162 | PERF_IP_FLAG_CALL |\ | ||
163 | PERF_IP_FLAG_RETURN |\ | ||
164 | PERF_IP_FLAG_CONDITIONAL |\ | ||
165 | PERF_IP_FLAG_SYSCALLRET |\ | ||
166 | PERF_IP_FLAG_ASYNC |\ | ||
167 | PERF_IP_FLAG_INTERRUPT |\ | ||
168 | PERF_IP_FLAG_TX_ABORT |\ | ||
169 | PERF_IP_FLAG_TRACE_BEGIN |\ | ||
170 | PERF_IP_FLAG_TRACE_END) | ||
171 | |||
146 | struct perf_sample { | 172 | struct perf_sample { |
147 | u64 ip; | 173 | u64 ip; |
148 | u32 pid, tid; | 174 | u32 pid, tid; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 2f9e68025ede..12b4396c7175 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -853,8 +853,6 @@ void perf_evsel__exit(struct perf_evsel *evsel) | |||
853 | perf_evsel__free_id(evsel); | 853 | perf_evsel__free_id(evsel); |
854 | close_cgroup(evsel->cgrp); | 854 | close_cgroup(evsel->cgrp); |
855 | zfree(&evsel->group_name); | 855 | zfree(&evsel->group_name); |
856 | if (evsel->tp_format) | ||
857 | pevent_free_format(evsel->tp_format); | ||
858 | zfree(&evsel->name); | 856 | zfree(&evsel->name); |
859 | perf_evsel__object.fini(evsel); | 857 | perf_evsel__object.fini(evsel); |
860 | } | 858 | } |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 0ecf4a304cbc..76442caca37e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -79,10 +79,7 @@ static int do_write(int fd, const void *buf, size_t size) | |||
79 | return 0; | 79 | return 0; |
80 | } | 80 | } |
81 | 81 | ||
82 | #define NAME_ALIGN 64 | 82 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned) |
83 | |||
84 | static int write_padded(int fd, const void *bf, size_t count, | ||
85 | size_t count_aligned) | ||
86 | { | 83 | { |
87 | static const char zero_buf[NAME_ALIGN]; | 84 | static const char zero_buf[NAME_ALIGN]; |
88 | int err = do_write(fd, bf, count); | 85 | int err = do_write(fd, bf, count); |
@@ -171,340 +168,6 @@ perf_header__set_cmdline(int argc, const char **argv) | |||
171 | return 0; | 168 | return 0; |
172 | } | 169 | } |
173 | 170 | ||
174 | #define dsos__for_each_with_build_id(pos, head) \ | ||
175 | list_for_each_entry(pos, head, node) \ | ||
176 | if (!pos->has_build_id) \ | ||
177 | continue; \ | ||
178 | else | ||
179 | |||
180 | static int write_buildid(const char *name, size_t name_len, u8 *build_id, | ||
181 | pid_t pid, u16 misc, int fd) | ||
182 | { | ||
183 | int err; | ||
184 | struct build_id_event b; | ||
185 | size_t len; | ||
186 | |||
187 | len = name_len + 1; | ||
188 | len = PERF_ALIGN(len, NAME_ALIGN); | ||
189 | |||
190 | memset(&b, 0, sizeof(b)); | ||
191 | memcpy(&b.build_id, build_id, BUILD_ID_SIZE); | ||
192 | b.pid = pid; | ||
193 | b.header.misc = misc; | ||
194 | b.header.size = sizeof(b) + len; | ||
195 | |||
196 | err = do_write(fd, &b, sizeof(b)); | ||
197 | if (err < 0) | ||
198 | return err; | ||
199 | |||
200 | return write_padded(fd, name, name_len + 1, len); | ||
201 | } | ||
202 | |||
203 | static int __dsos__hit_all(struct list_head *head) | ||
204 | { | ||
205 | struct dso *pos; | ||
206 | |||
207 | list_for_each_entry(pos, head, node) | ||
208 | pos->hit = true; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int machine__hit_all_dsos(struct machine *machine) | ||
214 | { | ||
215 | int err; | ||
216 | |||
217 | err = __dsos__hit_all(&machine->kernel_dsos.head); | ||
218 | if (err) | ||
219 | return err; | ||
220 | |||
221 | return __dsos__hit_all(&machine->user_dsos.head); | ||
222 | } | ||
223 | |||
224 | int dsos__hit_all(struct perf_session *session) | ||
225 | { | ||
226 | struct rb_node *nd; | ||
227 | int err; | ||
228 | |||
229 | err = machine__hit_all_dsos(&session->machines.host); | ||
230 | if (err) | ||
231 | return err; | ||
232 | |||
233 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
234 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
235 | |||
236 | err = machine__hit_all_dsos(pos); | ||
237 | if (err) | ||
238 | return err; | ||
239 | } | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static int __dsos__write_buildid_table(struct list_head *head, | ||
245 | struct machine *machine, | ||
246 | pid_t pid, u16 misc, int fd) | ||
247 | { | ||
248 | char nm[PATH_MAX]; | ||
249 | struct dso *pos; | ||
250 | |||
251 | dsos__for_each_with_build_id(pos, head) { | ||
252 | int err; | ||
253 | const char *name; | ||
254 | size_t name_len; | ||
255 | |||
256 | if (!pos->hit) | ||
257 | continue; | ||
258 | |||
259 | if (dso__is_vdso(pos)) { | ||
260 | name = pos->short_name; | ||
261 | name_len = pos->short_name_len + 1; | ||
262 | } else if (dso__is_kcore(pos)) { | ||
263 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
264 | name = nm; | ||
265 | name_len = strlen(nm) + 1; | ||
266 | } else { | ||
267 | name = pos->long_name; | ||
268 | name_len = pos->long_name_len + 1; | ||
269 | } | ||
270 | |||
271 | err = write_buildid(name, name_len, pos->build_id, | ||
272 | pid, misc, fd); | ||
273 | if (err) | ||
274 | return err; | ||
275 | } | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int machine__write_buildid_table(struct machine *machine, int fd) | ||
281 | { | ||
282 | int err; | ||
283 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | ||
284 | umisc = PERF_RECORD_MISC_USER; | ||
285 | |||
286 | if (!machine__is_host(machine)) { | ||
287 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | ||
288 | umisc = PERF_RECORD_MISC_GUEST_USER; | ||
289 | } | ||
290 | |||
291 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, | ||
292 | machine->pid, kmisc, fd); | ||
293 | if (err == 0) | ||
294 | err = __dsos__write_buildid_table(&machine->user_dsos.head, | ||
295 | machine, machine->pid, umisc, | ||
296 | fd); | ||
297 | return err; | ||
298 | } | ||
299 | |||
300 | static int dsos__write_buildid_table(struct perf_header *header, int fd) | ||
301 | { | ||
302 | struct perf_session *session = container_of(header, | ||
303 | struct perf_session, header); | ||
304 | struct rb_node *nd; | ||
305 | int err = machine__write_buildid_table(&session->machines.host, fd); | ||
306 | |||
307 | if (err) | ||
308 | return err; | ||
309 | |||
310 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
311 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
312 | err = machine__write_buildid_table(pos, fd); | ||
313 | if (err) | ||
314 | break; | ||
315 | } | ||
316 | return err; | ||
317 | } | ||
318 | |||
319 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
320 | const char *name, bool is_kallsyms, bool is_vdso) | ||
321 | { | ||
322 | const size_t size = PATH_MAX; | ||
323 | char *realname, *filename = zalloc(size), | ||
324 | *linkname = zalloc(size), *targetname; | ||
325 | int len, err = -1; | ||
326 | bool slash = is_kallsyms || is_vdso; | ||
327 | |||
328 | if (is_kallsyms) { | ||
329 | if (symbol_conf.kptr_restrict) { | ||
330 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | ||
331 | err = 0; | ||
332 | goto out_free; | ||
333 | } | ||
334 | realname = (char *) name; | ||
335 | } else | ||
336 | realname = realpath(name, NULL); | ||
337 | |||
338 | if (realname == NULL || filename == NULL || linkname == NULL) | ||
339 | goto out_free; | ||
340 | |||
341 | len = scnprintf(filename, size, "%s%s%s", | ||
342 | debugdir, slash ? "/" : "", | ||
343 | is_vdso ? DSO__NAME_VDSO : realname); | ||
344 | if (mkdir_p(filename, 0755)) | ||
345 | goto out_free; | ||
346 | |||
347 | snprintf(filename + len, size - len, "/%s", sbuild_id); | ||
348 | |||
349 | if (access(filename, F_OK)) { | ||
350 | if (is_kallsyms) { | ||
351 | if (copyfile("/proc/kallsyms", filename)) | ||
352 | goto out_free; | ||
353 | } else if (link(realname, filename) && copyfile(name, filename)) | ||
354 | goto out_free; | ||
355 | } | ||
356 | |||
357 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", | ||
358 | debugdir, sbuild_id); | ||
359 | |||
360 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
361 | goto out_free; | ||
362 | |||
363 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
364 | targetname = filename + strlen(debugdir) - 5; | ||
365 | memcpy(targetname, "../..", 5); | ||
366 | |||
367 | if (symlink(targetname, linkname) == 0) | ||
368 | err = 0; | ||
369 | out_free: | ||
370 | if (!is_kallsyms) | ||
371 | free(realname); | ||
372 | free(filename); | ||
373 | free(linkname); | ||
374 | return err; | ||
375 | } | ||
376 | |||
377 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
378 | const char *name, const char *debugdir, | ||
379 | bool is_kallsyms, bool is_vdso) | ||
380 | { | ||
381 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
382 | |||
383 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
384 | |||
385 | return build_id_cache__add_s(sbuild_id, debugdir, name, | ||
386 | is_kallsyms, is_vdso); | ||
387 | } | ||
388 | |||
389 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
390 | { | ||
391 | const size_t size = PATH_MAX; | ||
392 | char *filename = zalloc(size), | ||
393 | *linkname = zalloc(size); | ||
394 | int err = -1; | ||
395 | |||
396 | if (filename == NULL || linkname == NULL) | ||
397 | goto out_free; | ||
398 | |||
399 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
400 | debugdir, sbuild_id, sbuild_id + 2); | ||
401 | |||
402 | if (access(linkname, F_OK)) | ||
403 | goto out_free; | ||
404 | |||
405 | if (readlink(linkname, filename, size - 1) < 0) | ||
406 | goto out_free; | ||
407 | |||
408 | if (unlink(linkname)) | ||
409 | goto out_free; | ||
410 | |||
411 | /* | ||
412 | * Since the link is relative, we must make it absolute: | ||
413 | */ | ||
414 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
415 | debugdir, sbuild_id, filename); | ||
416 | |||
417 | if (unlink(linkname)) | ||
418 | goto out_free; | ||
419 | |||
420 | err = 0; | ||
421 | out_free: | ||
422 | free(filename); | ||
423 | free(linkname); | ||
424 | return err; | ||
425 | } | ||
426 | |||
427 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, | ||
428 | const char *debugdir) | ||
429 | { | ||
430 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | ||
431 | bool is_vdso = dso__is_vdso(dso); | ||
432 | const char *name = dso->long_name; | ||
433 | char nm[PATH_MAX]; | ||
434 | |||
435 | if (dso__is_kcore(dso)) { | ||
436 | is_kallsyms = true; | ||
437 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
438 | name = nm; | ||
439 | } | ||
440 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
441 | debugdir, is_kallsyms, is_vdso); | ||
442 | } | ||
443 | |||
444 | static int __dsos__cache_build_ids(struct list_head *head, | ||
445 | struct machine *machine, const char *debugdir) | ||
446 | { | ||
447 | struct dso *pos; | ||
448 | int err = 0; | ||
449 | |||
450 | dsos__for_each_with_build_id(pos, head) | ||
451 | if (dso__cache_build_id(pos, machine, debugdir)) | ||
452 | err = -1; | ||
453 | |||
454 | return err; | ||
455 | } | ||
456 | |||
457 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | ||
458 | { | ||
459 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, | ||
460 | debugdir); | ||
461 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, | ||
462 | debugdir); | ||
463 | return ret; | ||
464 | } | ||
465 | |||
466 | static int perf_session__cache_build_ids(struct perf_session *session) | ||
467 | { | ||
468 | struct rb_node *nd; | ||
469 | int ret; | ||
470 | char debugdir[PATH_MAX]; | ||
471 | |||
472 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | ||
473 | |||
474 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
475 | return -1; | ||
476 | |||
477 | ret = machine__cache_build_ids(&session->machines.host, debugdir); | ||
478 | |||
479 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
480 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
481 | ret |= machine__cache_build_ids(pos, debugdir); | ||
482 | } | ||
483 | return ret ? -1 : 0; | ||
484 | } | ||
485 | |||
486 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | ||
487 | { | ||
488 | bool ret; | ||
489 | |||
490 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
491 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | ||
496 | { | ||
497 | struct rb_node *nd; | ||
498 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); | ||
499 | |||
500 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { | ||
501 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
502 | ret |= machine__read_build_ids(pos, with_hits); | ||
503 | } | ||
504 | |||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, | 171 | static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, |
509 | struct perf_evlist *evlist) | 172 | struct perf_evlist *evlist) |
510 | { | 173 | { |
@@ -523,7 +186,7 @@ static int write_build_id(int fd, struct perf_header *h, | |||
523 | if (!perf_session__read_build_ids(session, true)) | 186 | if (!perf_session__read_build_ids(session, true)) |
524 | return -1; | 187 | return -1; |
525 | 188 | ||
526 | err = dsos__write_buildid_table(h, fd); | 189 | err = perf_session__write_buildid_table(session, fd); |
527 | if (err < 0) { | 190 | if (err < 0) { |
528 | pr_debug("failed to write buildid table\n"); | 191 | pr_debug("failed to write buildid table\n"); |
529 | return err; | 192 | return err; |
@@ -1606,7 +1269,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1606 | 1269 | ||
1607 | dso__set_build_id(dso, &bev->build_id); | 1270 | dso__set_build_id(dso, &bev->build_id); |
1608 | 1271 | ||
1609 | if (filename[0] == '[') | 1272 | if (!is_kernel_module(filename, NULL)) |
1610 | dso->kernel = dso_type; | 1273 | dso->kernel = dso_type; |
1611 | 1274 | ||
1612 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1275 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 8f5cbaea64a5..3bb90ac172a1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
122 | 122 | ||
123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | 123 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); |
124 | 124 | ||
125 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
126 | const char *name, bool is_kallsyms, bool is_vdso); | ||
127 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
128 | |||
129 | int perf_event__synthesize_attr(struct perf_tool *tool, | 125 | int perf_event__synthesize_attr(struct perf_tool *tool, |
130 | struct perf_event_attr *attr, u32 ids, u64 *id, | 126 | struct perf_event_attr *attr, u32 ids, u64 *id, |
131 | perf_event__handler_t process); | 127 | perf_event__handler_t process); |
@@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool, | |||
151 | struct perf_session *session); | 147 | struct perf_session *session); |
152 | bool is_perf_magic(u64 magic); | 148 | bool is_perf_magic(u64 magic); |
153 | 149 | ||
154 | int dsos__hit_all(struct perf_session *session); | 150 | #define NAME_ALIGN 64 |
151 | |||
152 | int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); | ||
155 | 153 | ||
156 | /* | 154 | /* |
157 | * arch specific callback | 155 | * arch specific callback |
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 01ffd12dc791..40bd21488032 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, | |||
46 | __bitmap_or(dst, src1, src2, nbits); | 46 | __bitmap_or(dst, src1, src2, nbits); |
47 | } | 47 | } |
48 | 48 | ||
49 | /** | ||
50 | * test_and_set_bit - Set a bit and return its old value | ||
51 | * @nr: Bit to set | ||
52 | * @addr: Address to count from | ||
53 | */ | ||
54 | static inline int test_and_set_bit(int nr, unsigned long *addr) | ||
55 | { | ||
56 | unsigned long mask = BIT_MASK(nr); | ||
57 | unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); | ||
58 | unsigned long old; | ||
59 | |||
60 | old = *p; | ||
61 | *p = old | mask; | ||
62 | |||
63 | return (old & mask) != 0; | ||
64 | } | ||
65 | |||
49 | #endif /* _PERF_BITOPS_H */ | 66 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index dadfa7e54287..c3294163de17 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -15,6 +15,8 @@ | |||
15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) | 15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) |
16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) | 16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) |
17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) | 17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) |
18 | #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) | ||
19 | #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) | ||
18 | 20 | ||
19 | #define for_each_set_bit(bit, addr, size) \ | 21 | #define for_each_set_bit(bit, addr, size) \ |
20 | for ((bit) = find_first_bit((addr), (size)); \ | 22 | for ((bit) = find_first_bit((addr), (size)); \ |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 51a630301afa..52e94902afb1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -464,6 +464,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
464 | { | 464 | { |
465 | struct map *map; | 465 | struct map *map; |
466 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | 466 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); |
467 | bool compressed; | ||
467 | 468 | ||
468 | if (dso == NULL) | 469 | if (dso == NULL) |
469 | return NULL; | 470 | return NULL; |
@@ -476,6 +477,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
476 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | 477 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; |
477 | else | 478 | else |
478 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | 479 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; |
480 | |||
481 | /* _KMODULE_COMP should be next to _KMODULE */ | ||
482 | if (is_kernel_module(filename, &compressed) && compressed) | ||
483 | dso->symtab_type++; | ||
484 | |||
479 | map_groups__insert(&machine->kmaps, map); | 485 | map_groups__insert(&machine->kmaps, map); |
480 | return map; | 486 | return map; |
481 | } | 487 | } |
@@ -861,8 +867,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, | |||
861 | struct map *map; | 867 | struct map *map; |
862 | char *long_name; | 868 | char *long_name; |
863 | 869 | ||
864 | if (dot == NULL || strcmp(dot, ".ko")) | 870 | if (dot == NULL) |
865 | continue; | 871 | continue; |
872 | |||
873 | /* On some system, modules are compressed like .ko.gz */ | ||
874 | if (is_supported_compression(dot + 1) && | ||
875 | is_kmodule_extension(dot - 2)) | ||
876 | dot -= 3; | ||
877 | |||
866 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 878 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", |
867 | (int)(dot - dent->d_name), dent->d_name); | 879 | (int)(dot - dent->d_name), dent->d_name); |
868 | 880 | ||
@@ -1044,6 +1056,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1044 | dot = strrchr(name, '.'); | 1056 | dot = strrchr(name, '.'); |
1045 | if (dot == NULL) | 1057 | if (dot == NULL) |
1046 | goto out_problem; | 1058 | goto out_problem; |
1059 | /* On some system, modules are compressed like .ko.gz */ | ||
1060 | if (is_supported_compression(dot + 1)) | ||
1061 | dot -= 3; | ||
1062 | if (!is_kmodule_extension(dot + 1)) | ||
1063 | goto out_problem; | ||
1047 | snprintf(short_module_name, sizeof(short_module_name), | 1064 | snprintf(short_module_name, sizeof(short_module_name), |
1048 | "[%.*s]", (int)(dot - name), name); | 1065 | "[%.*s]", (int)(dot - name), name); |
1049 | strxfrchar(short_module_name, '-', '_'); | 1066 | strxfrchar(short_module_name, '-', '_'); |
@@ -1068,8 +1085,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1068 | * Should be there already, from the build-id table in | 1085 | * Should be there already, from the build-id table in |
1069 | * the header. | 1086 | * the header. |
1070 | */ | 1087 | */ |
1071 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | 1088 | struct dso *kernel = NULL; |
1072 | kmmap_prefix); | 1089 | struct dso *dso; |
1090 | |||
1091 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { | ||
1092 | if (is_kernel_module(dso->long_name, NULL)) | ||
1093 | continue; | ||
1094 | |||
1095 | kernel = dso; | ||
1096 | break; | ||
1097 | } | ||
1098 | |||
1099 | if (kernel == NULL) | ||
1100 | kernel = __dsos__findnew(&machine->kernel_dsos, | ||
1101 | kmmap_prefix); | ||
1073 | if (kernel == NULL) | 1102 | if (kernel == NULL) |
1074 | goto out_problem; | 1103 | goto out_problem; |
1075 | 1104 | ||
@@ -1077,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1077 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 1106 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
1078 | goto out_problem; | 1107 | goto out_problem; |
1079 | 1108 | ||
1109 | if (strstr(dso->long_name, "vmlinux")) | ||
1110 | dso__set_short_name(dso, "[kernel.vmlinux]", false); | ||
1111 | |||
1080 | machine__set_kernel_mmap_len(machine, event); | 1112 | machine__set_kernel_mmap_len(machine, event); |
1081 | 1113 | ||
1082 | /* | 1114 | /* |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 0a01bac4ce02..22ebc46226e7 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <ctype.h> | 25 | #include <ctype.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | #include <linux/bitmap.h> | ||
27 | 28 | ||
28 | #include "../util.h" | 29 | #include "../util.h" |
29 | #include <EXTERN.h> | 30 | #include <EXTERN.h> |
@@ -57,7 +58,7 @@ INTERP my_perl; | |||
57 | #define FTRACE_MAX_EVENT \ | 58 | #define FTRACE_MAX_EVENT \ |
58 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 59 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
59 | 60 | ||
60 | struct event_format *events[FTRACE_MAX_EVENT]; | 61 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
61 | 62 | ||
62 | extern struct scripting_context *scripting_context; | 63 | extern struct scripting_context *scripting_context; |
63 | 64 | ||
@@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event, | |||
238 | define_event_symbols(event, ev_name, args->next); | 239 | define_event_symbols(event, ev_name, args->next); |
239 | } | 240 | } |
240 | 241 | ||
241 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
242 | { | ||
243 | static char ev_name[256]; | ||
244 | struct event_format *event; | ||
245 | int type = evsel->attr.config; | ||
246 | |||
247 | if (events[type]) | ||
248 | return events[type]; | ||
249 | |||
250 | events[type] = event = evsel->tp_format; | ||
251 | if (!event) | ||
252 | return NULL; | ||
253 | |||
254 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
255 | |||
256 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
257 | |||
258 | return event; | ||
259 | } | ||
260 | |||
261 | static void perl_process_tracepoint(struct perf_sample *sample, | 242 | static void perl_process_tracepoint(struct perf_sample *sample, |
262 | struct perf_evsel *evsel, | 243 | struct perf_evsel *evsel, |
263 | struct thread *thread) | 244 | struct thread *thread) |
264 | { | 245 | { |
246 | struct event_format *event = evsel->tp_format; | ||
265 | struct format_field *field; | 247 | struct format_field *field; |
266 | static char handler[256]; | 248 | static char handler[256]; |
267 | unsigned long long val; | 249 | unsigned long long val; |
268 | unsigned long s, ns; | 250 | unsigned long s, ns; |
269 | struct event_format *event; | ||
270 | int pid; | 251 | int pid; |
271 | int cpu = sample->cpu; | 252 | int cpu = sample->cpu; |
272 | void *data = sample->raw_data; | 253 | void *data = sample->raw_data; |
@@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
278 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) | 259 | if (evsel->attr.type != PERF_TYPE_TRACEPOINT) |
279 | return; | 260 | return; |
280 | 261 | ||
281 | event = find_cache_event(evsel); | ||
282 | if (!event) | 262 | if (!event) |
283 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); | 263 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
284 | 264 | ||
@@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample, | |||
286 | 266 | ||
287 | sprintf(handler, "%s::%s", event->system, event->name); | 267 | sprintf(handler, "%s::%s", event->system, event->name); |
288 | 268 | ||
269 | if (!test_and_set_bit(event->id, events_defined)) | ||
270 | define_event_symbols(event, handler, event->print_fmt.args); | ||
271 | |||
289 | s = nsecs / NSECS_PER_SEC; | 272 | s = nsecs / NSECS_PER_SEC; |
290 | ns = nsecs - s * NSECS_PER_SEC; | 273 | ns = nsecs - s * NSECS_PER_SEC; |
291 | 274 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 2fd7ee8f18c7..d808a328f4dc 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include <stdbool.h> | 27 | #include <stdbool.h> |
28 | #include <errno.h> | 28 | #include <errno.h> |
29 | #include <linux/bitmap.h> | ||
29 | 30 | ||
30 | #include "../../perf.h" | 31 | #include "../../perf.h" |
31 | #include "../debug.h" | 32 | #include "../debug.h" |
@@ -37,6 +38,7 @@ | |||
37 | #include "../comm.h" | 38 | #include "../comm.h" |
38 | #include "../machine.h" | 39 | #include "../machine.h" |
39 | #include "../db-export.h" | 40 | #include "../db-export.h" |
41 | #include "../thread-stack.h" | ||
40 | #include "../trace-event.h" | 42 | #include "../trace-event.h" |
41 | #include "../machine.h" | 43 | #include "../machine.h" |
42 | 44 | ||
@@ -45,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void); | |||
45 | #define FTRACE_MAX_EVENT \ | 47 | #define FTRACE_MAX_EVENT \ |
46 | ((1 << (sizeof(unsigned short) * 8)) - 1) | 48 | ((1 << (sizeof(unsigned short) * 8)) - 1) |
47 | 49 | ||
48 | struct event_format *events[FTRACE_MAX_EVENT]; | 50 | static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT); |
49 | 51 | ||
50 | #define MAX_FIELDS 64 | 52 | #define MAX_FIELDS 64 |
51 | #define N_COMMON_FIELDS 7 | 53 | #define N_COMMON_FIELDS 7 |
@@ -66,7 +68,10 @@ struct tables { | |||
66 | PyObject *comm_thread_handler; | 68 | PyObject *comm_thread_handler; |
67 | PyObject *dso_handler; | 69 | PyObject *dso_handler; |
68 | PyObject *symbol_handler; | 70 | PyObject *symbol_handler; |
71 | PyObject *branch_type_handler; | ||
69 | PyObject *sample_handler; | 72 | PyObject *sample_handler; |
73 | PyObject *call_path_handler; | ||
74 | PyObject *call_return_handler; | ||
70 | bool db_export_mode; | 75 | bool db_export_mode; |
71 | }; | 76 | }; |
72 | 77 | ||
@@ -251,31 +256,6 @@ static void define_event_symbols(struct event_format *event, | |||
251 | define_event_symbols(event, ev_name, args->next); | 256 | define_event_symbols(event, ev_name, args->next); |
252 | } | 257 | } |
253 | 258 | ||
254 | static inline struct event_format *find_cache_event(struct perf_evsel *evsel) | ||
255 | { | ||
256 | static char ev_name[256]; | ||
257 | struct event_format *event; | ||
258 | int type = evsel->attr.config; | ||
259 | |||
260 | /* | ||
261 | * XXX: Do we really need to cache this since now we have evsel->tp_format | ||
262 | * cached already? Need to re-read this "cache" routine that as well calls | ||
263 | * define_event_symbols() :-\ | ||
264 | */ | ||
265 | if (events[type]) | ||
266 | return events[type]; | ||
267 | |||
268 | events[type] = event = evsel->tp_format; | ||
269 | if (!event) | ||
270 | return NULL; | ||
271 | |||
272 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
273 | |||
274 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
275 | |||
276 | return event; | ||
277 | } | ||
278 | |||
279 | static PyObject *get_field_numeric_entry(struct event_format *event, | 259 | static PyObject *get_field_numeric_entry(struct event_format *event, |
280 | struct format_field *field, void *data) | 260 | struct format_field *field, void *data) |
281 | { | 261 | { |
@@ -399,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
399 | struct thread *thread, | 379 | struct thread *thread, |
400 | struct addr_location *al) | 380 | struct addr_location *al) |
401 | { | 381 | { |
382 | struct event_format *event = evsel->tp_format; | ||
402 | PyObject *handler, *context, *t, *obj, *callchain; | 383 | PyObject *handler, *context, *t, *obj, *callchain; |
403 | PyObject *dict = NULL; | 384 | PyObject *dict = NULL; |
404 | static char handler_name[256]; | 385 | static char handler_name[256]; |
405 | struct format_field *field; | 386 | struct format_field *field; |
406 | unsigned long s, ns; | 387 | unsigned long s, ns; |
407 | struct event_format *event; | ||
408 | unsigned n = 0; | 388 | unsigned n = 0; |
409 | int pid; | 389 | int pid; |
410 | int cpu = sample->cpu; | 390 | int cpu = sample->cpu; |
@@ -416,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
416 | if (!t) | 396 | if (!t) |
417 | Py_FatalError("couldn't create Python tuple"); | 397 | Py_FatalError("couldn't create Python tuple"); |
418 | 398 | ||
419 | event = find_cache_event(evsel); | ||
420 | if (!event) | 399 | if (!event) |
421 | die("ug! no event found for type %d", (int)evsel->attr.config); | 400 | die("ug! no event found for type %d", (int)evsel->attr.config); |
422 | 401 | ||
@@ -424,6 +403,9 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
424 | 403 | ||
425 | sprintf(handler_name, "%s__%s", event->system, event->name); | 404 | sprintf(handler_name, "%s__%s", event->system, event->name); |
426 | 405 | ||
406 | if (!test_and_set_bit(event->id, events_defined)) | ||
407 | define_event_symbols(event, handler_name, event->print_fmt.args); | ||
408 | |||
427 | handler = get_handler(handler_name); | 409 | handler = get_handler(handler_name); |
428 | if (!handler) { | 410 | if (!handler) { |
429 | dict = PyDict_New(); | 411 | dict = PyDict_New(); |
@@ -664,13 +646,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym, | |||
664 | return 0; | 646 | return 0; |
665 | } | 647 | } |
666 | 648 | ||
649 | static int python_export_branch_type(struct db_export *dbe, u32 branch_type, | ||
650 | const char *name) | ||
651 | { | ||
652 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
653 | PyObject *t; | ||
654 | |||
655 | t = tuple_new(2); | ||
656 | |||
657 | tuple_set_s32(t, 0, branch_type); | ||
658 | tuple_set_string(t, 1, name); | ||
659 | |||
660 | call_object(tables->branch_type_handler, t, "branch_type_table"); | ||
661 | |||
662 | Py_DECREF(t); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static int python_export_sample(struct db_export *dbe, | 667 | static int python_export_sample(struct db_export *dbe, |
668 | struct export_sample *es) | 668 | struct export_sample *es) |
669 | { | 669 | { |
670 | struct tables *tables = container_of(dbe, struct tables, dbe); | 670 | struct tables *tables = container_of(dbe, struct tables, dbe); |
671 | PyObject *t; | 671 | PyObject *t; |
672 | 672 | ||
673 | t = tuple_new(19); | 673 | t = tuple_new(21); |
674 | 674 | ||
675 | tuple_set_u64(t, 0, es->db_id); | 675 | tuple_set_u64(t, 0, es->db_id); |
676 | tuple_set_u64(t, 1, es->evsel->db_id); | 676 | tuple_set_u64(t, 1, es->evsel->db_id); |
@@ -691,6 +691,8 @@ static int python_export_sample(struct db_export *dbe, | |||
691 | tuple_set_u64(t, 16, es->sample->weight); | 691 | tuple_set_u64(t, 16, es->sample->weight); |
692 | tuple_set_u64(t, 17, es->sample->transaction); | 692 | tuple_set_u64(t, 17, es->sample->transaction); |
693 | tuple_set_u64(t, 18, es->sample->data_src); | 693 | tuple_set_u64(t, 18, es->sample->data_src); |
694 | tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK); | ||
695 | tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX)); | ||
694 | 696 | ||
695 | call_object(tables->sample_handler, t, "sample_table"); | 697 | call_object(tables->sample_handler, t, "sample_table"); |
696 | 698 | ||
@@ -699,6 +701,64 @@ static int python_export_sample(struct db_export *dbe, | |||
699 | return 0; | 701 | return 0; |
700 | } | 702 | } |
701 | 703 | ||
704 | static int python_export_call_path(struct db_export *dbe, struct call_path *cp) | ||
705 | { | ||
706 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
707 | PyObject *t; | ||
708 | u64 parent_db_id, sym_db_id; | ||
709 | |||
710 | parent_db_id = cp->parent ? cp->parent->db_id : 0; | ||
711 | sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0; | ||
712 | |||
713 | t = tuple_new(4); | ||
714 | |||
715 | tuple_set_u64(t, 0, cp->db_id); | ||
716 | tuple_set_u64(t, 1, parent_db_id); | ||
717 | tuple_set_u64(t, 2, sym_db_id); | ||
718 | tuple_set_u64(t, 3, cp->ip); | ||
719 | |||
720 | call_object(tables->call_path_handler, t, "call_path_table"); | ||
721 | |||
722 | Py_DECREF(t); | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static int python_export_call_return(struct db_export *dbe, | ||
728 | struct call_return *cr) | ||
729 | { | ||
730 | struct tables *tables = container_of(dbe, struct tables, dbe); | ||
731 | u64 comm_db_id = cr->comm ? cr->comm->db_id : 0; | ||
732 | PyObject *t; | ||
733 | |||
734 | t = tuple_new(11); | ||
735 | |||
736 | tuple_set_u64(t, 0, cr->db_id); | ||
737 | tuple_set_u64(t, 1, cr->thread->db_id); | ||
738 | tuple_set_u64(t, 2, comm_db_id); | ||
739 | tuple_set_u64(t, 3, cr->cp->db_id); | ||
740 | tuple_set_u64(t, 4, cr->call_time); | ||
741 | tuple_set_u64(t, 5, cr->return_time); | ||
742 | tuple_set_u64(t, 6, cr->branch_count); | ||
743 | tuple_set_u64(t, 7, cr->call_ref); | ||
744 | tuple_set_u64(t, 8, cr->return_ref); | ||
745 | tuple_set_u64(t, 9, cr->cp->parent->db_id); | ||
746 | tuple_set_s32(t, 10, cr->flags); | ||
747 | |||
748 | call_object(tables->call_return_handler, t, "call_return_table"); | ||
749 | |||
750 | Py_DECREF(t); | ||
751 | |||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int python_process_call_return(struct call_return *cr, void *data) | ||
756 | { | ||
757 | struct db_export *dbe = data; | ||
758 | |||
759 | return db_export__call_return(dbe, cr); | ||
760 | } | ||
761 | |||
702 | static void python_process_general_event(struct perf_sample *sample, | 762 | static void python_process_general_event(struct perf_sample *sample, |
703 | struct perf_evsel *evsel, | 763 | struct perf_evsel *evsel, |
704 | struct thread *thread, | 764 | struct thread *thread, |
@@ -831,7 +891,9 @@ error: | |||
831 | static void set_table_handlers(struct tables *tables) | 891 | static void set_table_handlers(struct tables *tables) |
832 | { | 892 | { |
833 | const char *perf_db_export_mode = "perf_db_export_mode"; | 893 | const char *perf_db_export_mode = "perf_db_export_mode"; |
834 | PyObject *db_export_mode; | 894 | const char *perf_db_export_calls = "perf_db_export_calls"; |
895 | PyObject *db_export_mode, *db_export_calls; | ||
896 | bool export_calls = false; | ||
835 | int ret; | 897 | int ret; |
836 | 898 | ||
837 | memset(tables, 0, sizeof(struct tables)); | 899 | memset(tables, 0, sizeof(struct tables)); |
@@ -848,6 +910,23 @@ static void set_table_handlers(struct tables *tables) | |||
848 | if (!ret) | 910 | if (!ret) |
849 | return; | 911 | return; |
850 | 912 | ||
913 | tables->dbe.crp = NULL; | ||
914 | db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls); | ||
915 | if (db_export_calls) { | ||
916 | ret = PyObject_IsTrue(db_export_calls); | ||
917 | if (ret == -1) | ||
918 | handler_call_die(perf_db_export_calls); | ||
919 | export_calls = !!ret; | ||
920 | } | ||
921 | |||
922 | if (export_calls) { | ||
923 | tables->dbe.crp = | ||
924 | call_return_processor__new(python_process_call_return, | ||
925 | &tables->dbe); | ||
926 | if (!tables->dbe.crp) | ||
927 | Py_FatalError("failed to create calls processor"); | ||
928 | } | ||
929 | |||
851 | tables->db_export_mode = true; | 930 | tables->db_export_mode = true; |
852 | /* | 931 | /* |
853 | * Reserve per symbol space for symbol->db_id via symbol__priv() | 932 | * Reserve per symbol space for symbol->db_id via symbol__priv() |
@@ -861,7 +940,10 @@ static void set_table_handlers(struct tables *tables) | |||
861 | SET_TABLE_HANDLER(comm_thread); | 940 | SET_TABLE_HANDLER(comm_thread); |
862 | SET_TABLE_HANDLER(dso); | 941 | SET_TABLE_HANDLER(dso); |
863 | SET_TABLE_HANDLER(symbol); | 942 | SET_TABLE_HANDLER(symbol); |
943 | SET_TABLE_HANDLER(branch_type); | ||
864 | SET_TABLE_HANDLER(sample); | 944 | SET_TABLE_HANDLER(sample); |
945 | SET_TABLE_HANDLER(call_path); | ||
946 | SET_TABLE_HANDLER(call_return); | ||
865 | } | 947 | } |
866 | 948 | ||
867 | /* | 949 | /* |
@@ -910,6 +992,12 @@ static int python_start_script(const char *script, int argc, const char **argv) | |||
910 | 992 | ||
911 | set_table_handlers(tables); | 993 | set_table_handlers(tables); |
912 | 994 | ||
995 | if (tables->db_export_mode) { | ||
996 | err = db_export__branch_types(&tables->dbe); | ||
997 | if (err) | ||
998 | goto error; | ||
999 | } | ||
1000 | |||
913 | return err; | 1001 | return err; |
914 | error: | 1002 | error: |
915 | Py_Finalize(); | 1003 | Py_Finalize(); |
@@ -920,7 +1008,9 @@ error: | |||
920 | 1008 | ||
921 | static int python_flush_script(void) | 1009 | static int python_flush_script(void) |
922 | { | 1010 | { |
923 | return 0; | 1011 | struct tables *tables = &tables_global; |
1012 | |||
1013 | return db_export__flush(&tables->dbe); | ||
924 | } | 1014 | } |
925 | 1015 | ||
926 | /* | 1016 | /* |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 1e23a5bfb044..efc7eb6b8f0f 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -546,6 +546,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata) | |||
546 | return 0; | 546 | return 0; |
547 | } | 547 | } |
548 | 548 | ||
549 | static int decompress_kmodule(struct dso *dso, const char *name, | ||
550 | enum dso_binary_type type) | ||
551 | { | ||
552 | int fd; | ||
553 | const char *ext = strrchr(name, '.'); | ||
554 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; | ||
555 | |||
556 | if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && | ||
557 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) || | ||
558 | type != dso->symtab_type) | ||
559 | return -1; | ||
560 | |||
561 | if (!ext || !is_supported_compression(ext + 1)) | ||
562 | return -1; | ||
563 | |||
564 | fd = mkstemp(tmpbuf); | ||
565 | if (fd < 0) | ||
566 | return -1; | ||
567 | |||
568 | if (!decompress_to_file(ext + 1, name, fd)) { | ||
569 | close(fd); | ||
570 | fd = -1; | ||
571 | } | ||
572 | |||
573 | unlink(tmpbuf); | ||
574 | |||
575 | return fd; | ||
576 | } | ||
577 | |||
549 | bool symsrc__possibly_runtime(struct symsrc *ss) | 578 | bool symsrc__possibly_runtime(struct symsrc *ss) |
550 | { | 579 | { |
551 | return ss->dynsym || ss->opdsec; | 580 | return ss->dynsym || ss->opdsec; |
@@ -571,7 +600,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
571 | Elf *elf; | 600 | Elf *elf; |
572 | int fd; | 601 | int fd; |
573 | 602 | ||
574 | fd = open(name, O_RDONLY); | 603 | if (dso__needs_decompress(dso)) |
604 | fd = decompress_kmodule(dso, name, type); | ||
605 | else | ||
606 | fd = open(name, O_RDONLY); | ||
607 | |||
575 | if (fd < 0) | 608 | if (fd < 0) |
576 | return -1; | 609 | return -1; |
577 | 610 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 078331140d8c..c24c5b83156c 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -51,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
53 | DSO_BINARY_TYPE__GUEST_KMODULE, | 53 | DSO_BINARY_TYPE__GUEST_KMODULE, |
54 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | ||
54 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 55 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
56 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | ||
55 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 57 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
56 | DSO_BINARY_TYPE__NOT_FOUND, | 58 | DSO_BINARY_TYPE__NOT_FOUND, |
57 | }; | 59 | }; |
@@ -1300,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | |||
1300 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; | 1302 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; |
1301 | 1303 | ||
1302 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 1304 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
1305 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | ||
1303 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 1306 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
1307 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | ||
1304 | /* | 1308 | /* |
1305 | * kernel modules know their symtab type - it's set when | 1309 | * kernel modules know their symtab type - it's set when |
1306 | * creating a module dso in machine__new_module(). | 1310 | * creating a module dso in machine__new_module(). |
@@ -1368,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1368 | return -1; | 1372 | return -1; |
1369 | 1373 | ||
1370 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | 1374 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || |
1371 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | 1375 | dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || |
1376 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE || | ||
1377 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | ||
1372 | 1378 | ||
1373 | /* | 1379 | /* |
1374 | * Iterate over candidate debug images. | 1380 | * Iterate over candidate debug images. |
@@ -1505,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1505 | symbol_filter_t filter) | 1511 | symbol_filter_t filter) |
1506 | { | 1512 | { |
1507 | int i, err = 0; | 1513 | int i, err = 0; |
1508 | char *filename; | 1514 | char *filename = NULL; |
1509 | 1515 | ||
1510 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1516 | if (!symbol_conf.ignore_vmlinux_buildid) |
1511 | vmlinux_path__nr_entries + 1); | 1517 | filename = dso__build_id_filename(dso, NULL, 0); |
1512 | |||
1513 | filename = dso__build_id_filename(dso, NULL, 0); | ||
1514 | if (filename != NULL) { | 1518 | if (filename != NULL) { |
1515 | err = dso__load_vmlinux(dso, map, filename, true, filter); | 1519 | err = dso__load_vmlinux(dso, map, filename, true, filter); |
1516 | if (err > 0) | 1520 | if (err > 0) |
@@ -1518,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1518 | free(filename); | 1522 | free(filename); |
1519 | } | 1523 | } |
1520 | 1524 | ||
1525 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1526 | vmlinux_path__nr_entries + 1); | ||
1527 | |||
1521 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1528 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
1522 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); | 1529 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); |
1523 | if (err > 0) | 1530 | if (err > 0) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index eb2c19bf8d90..ded3ca7266de 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -105,6 +105,7 @@ struct symbol_conf { | |||
105 | unsigned short nr_events; | 105 | unsigned short nr_events; |
106 | bool try_vmlinux_path, | 106 | bool try_vmlinux_path, |
107 | ignore_vmlinux, | 107 | ignore_vmlinux, |
108 | ignore_vmlinux_buildid, | ||
108 | show_kernel_path, | 109 | show_kernel_path, |
109 | use_modules, | 110 | use_modules, |
110 | sort_by_name, | 111 | sort_by_name, |
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c new file mode 100644 index 000000000000..9ed59a452d1f --- /dev/null +++ b/tools/perf/util/thread-stack.c | |||
@@ -0,0 +1,747 @@ | |||
1 | /* | ||
2 | * thread-stack.c: Synthesize a thread's stack using call / return events | ||
3 | * Copyright (c) 2014, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/rbtree.h> | ||
17 | #include <linux/list.h> | ||
18 | #include "thread.h" | ||
19 | #include "event.h" | ||
20 | #include "machine.h" | ||
21 | #include "util.h" | ||
22 | #include "debug.h" | ||
23 | #include "symbol.h" | ||
24 | #include "comm.h" | ||
25 | #include "thread-stack.h" | ||
26 | |||
27 | #define CALL_PATH_BLOCK_SHIFT 8 | ||
28 | #define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) | ||
29 | #define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) | ||
30 | |||
31 | struct call_path_block { | ||
32 | struct call_path cp[CALL_PATH_BLOCK_SIZE]; | ||
33 | struct list_head node; | ||
34 | }; | ||
35 | |||
36 | /** | ||
37 | * struct call_path_root - root of all call paths. | ||
38 | * @call_path: root call path | ||
39 | * @blocks: list of blocks to store call paths | ||
40 | * @next: next free space | ||
41 | * @sz: number of spaces | ||
42 | */ | ||
43 | struct call_path_root { | ||
44 | struct call_path call_path; | ||
45 | struct list_head blocks; | ||
46 | size_t next; | ||
47 | size_t sz; | ||
48 | }; | ||
49 | |||
50 | /** | ||
51 | * struct call_return_processor - provides a call-back to consume call-return | ||
52 | * information. | ||
53 | * @cpr: call path root | ||
54 | * @process: call-back that accepts call/return information | ||
55 | * @data: anonymous data for call-back | ||
56 | */ | ||
57 | struct call_return_processor { | ||
58 | struct call_path_root *cpr; | ||
59 | int (*process)(struct call_return *cr, void *data); | ||
60 | void *data; | ||
61 | }; | ||
62 | |||
63 | #define STACK_GROWTH 2048 | ||
64 | |||
65 | /** | ||
66 | * struct thread_stack_entry - thread stack entry. | ||
67 | * @ret_addr: return address | ||
68 | * @timestamp: timestamp (if known) | ||
69 | * @ref: external reference (e.g. db_id of sample) | ||
70 | * @branch_count: the branch count when the entry was created | ||
71 | * @cp: call path | ||
72 | * @no_call: a 'call' was not seen | ||
73 | */ | ||
74 | struct thread_stack_entry { | ||
75 | u64 ret_addr; | ||
76 | u64 timestamp; | ||
77 | u64 ref; | ||
78 | u64 branch_count; | ||
79 | struct call_path *cp; | ||
80 | bool no_call; | ||
81 | }; | ||
82 | |||
83 | /** | ||
84 | * struct thread_stack - thread stack constructed from 'call' and 'return' | ||
85 | * branch samples. | ||
86 | * @stack: array that holds the stack | ||
87 | * @cnt: number of entries in the stack | ||
88 | * @sz: current maximum stack size | ||
89 | * @trace_nr: current trace number | ||
90 | * @branch_count: running branch count | ||
91 | * @kernel_start: kernel start address | ||
92 | * @last_time: last timestamp | ||
93 | * @crp: call/return processor | ||
94 | * @comm: current comm | ||
95 | */ | ||
96 | struct thread_stack { | ||
97 | struct thread_stack_entry *stack; | ||
98 | size_t cnt; | ||
99 | size_t sz; | ||
100 | u64 trace_nr; | ||
101 | u64 branch_count; | ||
102 | u64 kernel_start; | ||
103 | u64 last_time; | ||
104 | struct call_return_processor *crp; | ||
105 | struct comm *comm; | ||
106 | }; | ||
107 | |||
108 | static int thread_stack__grow(struct thread_stack *ts) | ||
109 | { | ||
110 | struct thread_stack_entry *new_stack; | ||
111 | size_t sz, new_sz; | ||
112 | |||
113 | new_sz = ts->sz + STACK_GROWTH; | ||
114 | sz = new_sz * sizeof(struct thread_stack_entry); | ||
115 | |||
116 | new_stack = realloc(ts->stack, sz); | ||
117 | if (!new_stack) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | ts->stack = new_stack; | ||
121 | ts->sz = new_sz; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static struct thread_stack *thread_stack__new(struct thread *thread, | ||
127 | struct call_return_processor *crp) | ||
128 | { | ||
129 | struct thread_stack *ts; | ||
130 | |||
131 | ts = zalloc(sizeof(struct thread_stack)); | ||
132 | if (!ts) | ||
133 | return NULL; | ||
134 | |||
135 | if (thread_stack__grow(ts)) { | ||
136 | free(ts); | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | if (thread->mg && thread->mg->machine) | ||
141 | ts->kernel_start = machine__kernel_start(thread->mg->machine); | ||
142 | else | ||
143 | ts->kernel_start = 1ULL << 63; | ||
144 | ts->crp = crp; | ||
145 | |||
146 | return ts; | ||
147 | } | ||
148 | |||
149 | static int thread_stack__push(struct thread_stack *ts, u64 ret_addr) | ||
150 | { | ||
151 | int err = 0; | ||
152 | |||
153 | if (ts->cnt == ts->sz) { | ||
154 | err = thread_stack__grow(ts); | ||
155 | if (err) { | ||
156 | pr_warning("Out of memory: discarding thread stack\n"); | ||
157 | ts->cnt = 0; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | ts->stack[ts->cnt++].ret_addr = ret_addr; | ||
162 | |||
163 | return err; | ||
164 | } | ||
165 | |||
166 | static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) | ||
167 | { | ||
168 | size_t i; | ||
169 | |||
170 | /* | ||
171 | * In some cases there may be functions which are not seen to return. | ||
172 | * For example when setjmp / longjmp has been used. Or the perf context | ||
173 | * switch in the kernel which doesn't stop and start tracing in exactly | ||
174 | * the same code path. When that happens the return address will be | ||
175 | * further down the stack. If the return address is not found at all, | ||
176 | * we assume the opposite (i.e. this is a return for a call that wasn't | ||
177 | * seen for some reason) and leave the stack alone. | ||
178 | */ | ||
179 | for (i = ts->cnt; i; ) { | ||
180 | if (ts->stack[--i].ret_addr == ret_addr) { | ||
181 | ts->cnt = i; | ||
182 | return; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static bool thread_stack__in_kernel(struct thread_stack *ts) | ||
188 | { | ||
189 | if (!ts->cnt) | ||
190 | return false; | ||
191 | |||
192 | return ts->stack[ts->cnt - 1].cp->in_kernel; | ||
193 | } | ||
194 | |||
195 | static int thread_stack__call_return(struct thread *thread, | ||
196 | struct thread_stack *ts, size_t idx, | ||
197 | u64 timestamp, u64 ref, bool no_return) | ||
198 | { | ||
199 | struct call_return_processor *crp = ts->crp; | ||
200 | struct thread_stack_entry *tse; | ||
201 | struct call_return cr = { | ||
202 | .thread = thread, | ||
203 | .comm = ts->comm, | ||
204 | .db_id = 0, | ||
205 | }; | ||
206 | |||
207 | tse = &ts->stack[idx]; | ||
208 | cr.cp = tse->cp; | ||
209 | cr.call_time = tse->timestamp; | ||
210 | cr.return_time = timestamp; | ||
211 | cr.branch_count = ts->branch_count - tse->branch_count; | ||
212 | cr.call_ref = tse->ref; | ||
213 | cr.return_ref = ref; | ||
214 | if (tse->no_call) | ||
215 | cr.flags |= CALL_RETURN_NO_CALL; | ||
216 | if (no_return) | ||
217 | cr.flags |= CALL_RETURN_NO_RETURN; | ||
218 | |||
219 | return crp->process(&cr, crp->data); | ||
220 | } | ||
221 | |||
222 | static int thread_stack__flush(struct thread *thread, struct thread_stack *ts) | ||
223 | { | ||
224 | struct call_return_processor *crp = ts->crp; | ||
225 | int err; | ||
226 | |||
227 | if (!crp) { | ||
228 | ts->cnt = 0; | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | while (ts->cnt) { | ||
233 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
234 | ts->last_time, 0, true); | ||
235 | if (err) { | ||
236 | pr_err("Error flushing thread stack!\n"); | ||
237 | ts->cnt = 0; | ||
238 | return err; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
246 | u64 to_ip, u16 insn_len, u64 trace_nr) | ||
247 | { | ||
248 | if (!thread) | ||
249 | return -EINVAL; | ||
250 | |||
251 | if (!thread->ts) { | ||
252 | thread->ts = thread_stack__new(thread, NULL); | ||
253 | if (!thread->ts) { | ||
254 | pr_warning("Out of memory: no thread stack\n"); | ||
255 | return -ENOMEM; | ||
256 | } | ||
257 | thread->ts->trace_nr = trace_nr; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * When the trace is discontinuous, the trace_nr changes. In that case | ||
262 | * the stack might be completely invalid. Better to report nothing than | ||
263 | * to report something misleading, so flush the stack. | ||
264 | */ | ||
265 | if (trace_nr != thread->ts->trace_nr) { | ||
266 | if (thread->ts->trace_nr) | ||
267 | thread_stack__flush(thread, thread->ts); | ||
268 | thread->ts->trace_nr = trace_nr; | ||
269 | } | ||
270 | |||
271 | /* Stop here if thread_stack__process() is in use */ | ||
272 | if (thread->ts->crp) | ||
273 | return 0; | ||
274 | |||
275 | if (flags & PERF_IP_FLAG_CALL) { | ||
276 | u64 ret_addr; | ||
277 | |||
278 | if (!to_ip) | ||
279 | return 0; | ||
280 | ret_addr = from_ip + insn_len; | ||
281 | if (ret_addr == to_ip) | ||
282 | return 0; /* Zero-length calls are excluded */ | ||
283 | return thread_stack__push(thread->ts, ret_addr); | ||
284 | } else if (flags & PERF_IP_FLAG_RETURN) { | ||
285 | if (!from_ip) | ||
286 | return 0; | ||
287 | thread_stack__pop(thread->ts, to_ip); | ||
288 | } | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr) | ||
294 | { | ||
295 | if (!thread || !thread->ts) | ||
296 | return; | ||
297 | |||
298 | if (trace_nr != thread->ts->trace_nr) { | ||
299 | if (thread->ts->trace_nr) | ||
300 | thread_stack__flush(thread, thread->ts); | ||
301 | thread->ts->trace_nr = trace_nr; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | void thread_stack__free(struct thread *thread) | ||
306 | { | ||
307 | if (thread->ts) { | ||
308 | thread_stack__flush(thread, thread->ts); | ||
309 | zfree(&thread->ts->stack); | ||
310 | zfree(&thread->ts); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
315 | size_t sz, u64 ip) | ||
316 | { | ||
317 | size_t i; | ||
318 | |||
319 | if (!thread || !thread->ts) | ||
320 | chain->nr = 1; | ||
321 | else | ||
322 | chain->nr = min(sz, thread->ts->cnt + 1); | ||
323 | |||
324 | chain->ips[0] = ip; | ||
325 | |||
326 | for (i = 1; i < chain->nr; i++) | ||
327 | chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; | ||
328 | } | ||
329 | |||
330 | static void call_path__init(struct call_path *cp, struct call_path *parent, | ||
331 | struct symbol *sym, u64 ip, bool in_kernel) | ||
332 | { | ||
333 | cp->parent = parent; | ||
334 | cp->sym = sym; | ||
335 | cp->ip = sym ? 0 : ip; | ||
336 | cp->db_id = 0; | ||
337 | cp->in_kernel = in_kernel; | ||
338 | RB_CLEAR_NODE(&cp->rb_node); | ||
339 | cp->children = RB_ROOT; | ||
340 | } | ||
341 | |||
342 | static struct call_path_root *call_path_root__new(void) | ||
343 | { | ||
344 | struct call_path_root *cpr; | ||
345 | |||
346 | cpr = zalloc(sizeof(struct call_path_root)); | ||
347 | if (!cpr) | ||
348 | return NULL; | ||
349 | call_path__init(&cpr->call_path, NULL, NULL, 0, false); | ||
350 | INIT_LIST_HEAD(&cpr->blocks); | ||
351 | return cpr; | ||
352 | } | ||
353 | |||
354 | static void call_path_root__free(struct call_path_root *cpr) | ||
355 | { | ||
356 | struct call_path_block *pos, *n; | ||
357 | |||
358 | list_for_each_entry_safe(pos, n, &cpr->blocks, node) { | ||
359 | list_del(&pos->node); | ||
360 | free(pos); | ||
361 | } | ||
362 | free(cpr); | ||
363 | } | ||
364 | |||
365 | static struct call_path *call_path__new(struct call_path_root *cpr, | ||
366 | struct call_path *parent, | ||
367 | struct symbol *sym, u64 ip, | ||
368 | bool in_kernel) | ||
369 | { | ||
370 | struct call_path_block *cpb; | ||
371 | struct call_path *cp; | ||
372 | size_t n; | ||
373 | |||
374 | if (cpr->next < cpr->sz) { | ||
375 | cpb = list_last_entry(&cpr->blocks, struct call_path_block, | ||
376 | node); | ||
377 | } else { | ||
378 | cpb = zalloc(sizeof(struct call_path_block)); | ||
379 | if (!cpb) | ||
380 | return NULL; | ||
381 | list_add_tail(&cpb->node, &cpr->blocks); | ||
382 | cpr->sz += CALL_PATH_BLOCK_SIZE; | ||
383 | } | ||
384 | |||
385 | n = cpr->next++ & CALL_PATH_BLOCK_MASK; | ||
386 | cp = &cpb->cp[n]; | ||
387 | |||
388 | call_path__init(cp, parent, sym, ip, in_kernel); | ||
389 | |||
390 | return cp; | ||
391 | } | ||
392 | |||
393 | static struct call_path *call_path__findnew(struct call_path_root *cpr, | ||
394 | struct call_path *parent, | ||
395 | struct symbol *sym, u64 ip, u64 ks) | ||
396 | { | ||
397 | struct rb_node **p; | ||
398 | struct rb_node *node_parent = NULL; | ||
399 | struct call_path *cp; | ||
400 | bool in_kernel = ip >= ks; | ||
401 | |||
402 | if (sym) | ||
403 | ip = 0; | ||
404 | |||
405 | if (!parent) | ||
406 | return call_path__new(cpr, parent, sym, ip, in_kernel); | ||
407 | |||
408 | p = &parent->children.rb_node; | ||
409 | while (*p != NULL) { | ||
410 | node_parent = *p; | ||
411 | cp = rb_entry(node_parent, struct call_path, rb_node); | ||
412 | |||
413 | if (cp->sym == sym && cp->ip == ip) | ||
414 | return cp; | ||
415 | |||
416 | if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) | ||
417 | p = &(*p)->rb_left; | ||
418 | else | ||
419 | p = &(*p)->rb_right; | ||
420 | } | ||
421 | |||
422 | cp = call_path__new(cpr, parent, sym, ip, in_kernel); | ||
423 | if (!cp) | ||
424 | return NULL; | ||
425 | |||
426 | rb_link_node(&cp->rb_node, node_parent, p); | ||
427 | rb_insert_color(&cp->rb_node, &parent->children); | ||
428 | |||
429 | return cp; | ||
430 | } | ||
431 | |||
432 | struct call_return_processor * | ||
433 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
434 | void *data) | ||
435 | { | ||
436 | struct call_return_processor *crp; | ||
437 | |||
438 | crp = zalloc(sizeof(struct call_return_processor)); | ||
439 | if (!crp) | ||
440 | return NULL; | ||
441 | crp->cpr = call_path_root__new(); | ||
442 | if (!crp->cpr) | ||
443 | goto out_free; | ||
444 | crp->process = process; | ||
445 | crp->data = data; | ||
446 | return crp; | ||
447 | |||
448 | out_free: | ||
449 | free(crp); | ||
450 | return NULL; | ||
451 | } | ||
452 | |||
453 | void call_return_processor__free(struct call_return_processor *crp) | ||
454 | { | ||
455 | if (crp) { | ||
456 | call_path_root__free(crp->cpr); | ||
457 | free(crp); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr, | ||
462 | u64 timestamp, u64 ref, struct call_path *cp, | ||
463 | bool no_call) | ||
464 | { | ||
465 | struct thread_stack_entry *tse; | ||
466 | int err; | ||
467 | |||
468 | if (ts->cnt == ts->sz) { | ||
469 | err = thread_stack__grow(ts); | ||
470 | if (err) | ||
471 | return err; | ||
472 | } | ||
473 | |||
474 | tse = &ts->stack[ts->cnt++]; | ||
475 | tse->ret_addr = ret_addr; | ||
476 | tse->timestamp = timestamp; | ||
477 | tse->ref = ref; | ||
478 | tse->branch_count = ts->branch_count; | ||
479 | tse->cp = cp; | ||
480 | tse->no_call = no_call; | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts, | ||
486 | u64 ret_addr, u64 timestamp, u64 ref, | ||
487 | struct symbol *sym) | ||
488 | { | ||
489 | int err; | ||
490 | |||
491 | if (!ts->cnt) | ||
492 | return 1; | ||
493 | |||
494 | if (ts->cnt == 1) { | ||
495 | struct thread_stack_entry *tse = &ts->stack[0]; | ||
496 | |||
497 | if (tse->cp->sym == sym) | ||
498 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
499 | timestamp, ref, false); | ||
500 | } | ||
501 | |||
502 | if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) { | ||
503 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
504 | timestamp, ref, false); | ||
505 | } else { | ||
506 | size_t i = ts->cnt - 1; | ||
507 | |||
508 | while (i--) { | ||
509 | if (ts->stack[i].ret_addr != ret_addr) | ||
510 | continue; | ||
511 | i += 1; | ||
512 | while (ts->cnt > i) { | ||
513 | err = thread_stack__call_return(thread, ts, | ||
514 | --ts->cnt, | ||
515 | timestamp, ref, | ||
516 | true); | ||
517 | if (err) | ||
518 | return err; | ||
519 | } | ||
520 | return thread_stack__call_return(thread, ts, --ts->cnt, | ||
521 | timestamp, ref, false); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | return 1; | ||
526 | } | ||
527 | |||
528 | static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts, | ||
529 | struct perf_sample *sample, | ||
530 | struct addr_location *from_al, | ||
531 | struct addr_location *to_al, u64 ref) | ||
532 | { | ||
533 | struct call_path_root *cpr = ts->crp->cpr; | ||
534 | struct call_path *cp; | ||
535 | struct symbol *sym; | ||
536 | u64 ip; | ||
537 | |||
538 | if (sample->ip) { | ||
539 | ip = sample->ip; | ||
540 | sym = from_al->sym; | ||
541 | } else if (sample->addr) { | ||
542 | ip = sample->addr; | ||
543 | sym = to_al->sym; | ||
544 | } else { | ||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | cp = call_path__findnew(cpr, &cpr->call_path, sym, ip, | ||
549 | ts->kernel_start); | ||
550 | if (!cp) | ||
551 | return -ENOMEM; | ||
552 | |||
553 | return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp, | ||
554 | true); | ||
555 | } | ||
556 | |||
557 | static int thread_stack__no_call_return(struct thread *thread, | ||
558 | struct thread_stack *ts, | ||
559 | struct perf_sample *sample, | ||
560 | struct addr_location *from_al, | ||
561 | struct addr_location *to_al, u64 ref) | ||
562 | { | ||
563 | struct call_path_root *cpr = ts->crp->cpr; | ||
564 | struct call_path *cp, *parent; | ||
565 | u64 ks = ts->kernel_start; | ||
566 | int err; | ||
567 | |||
568 | if (sample->ip >= ks && sample->addr < ks) { | ||
569 | /* Return to userspace, so pop all kernel addresses */ | ||
570 | while (thread_stack__in_kernel(ts)) { | ||
571 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
572 | sample->time, ref, | ||
573 | true); | ||
574 | if (err) | ||
575 | return err; | ||
576 | } | ||
577 | |||
578 | /* If the stack is empty, push the userspace address */ | ||
579 | if (!ts->cnt) { | ||
580 | cp = call_path__findnew(cpr, &cpr->call_path, | ||
581 | to_al->sym, sample->addr, | ||
582 | ts->kernel_start); | ||
583 | if (!cp) | ||
584 | return -ENOMEM; | ||
585 | return thread_stack__push_cp(ts, 0, sample->time, ref, | ||
586 | cp, true); | ||
587 | } | ||
588 | } else if (thread_stack__in_kernel(ts) && sample->ip < ks) { | ||
589 | /* Return to userspace, so pop all kernel addresses */ | ||
590 | while (thread_stack__in_kernel(ts)) { | ||
591 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
592 | sample->time, ref, | ||
593 | true); | ||
594 | if (err) | ||
595 | return err; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | if (ts->cnt) | ||
600 | parent = ts->stack[ts->cnt - 1].cp; | ||
601 | else | ||
602 | parent = &cpr->call_path; | ||
603 | |||
604 | /* This 'return' had no 'call', so push and pop top of stack */ | ||
605 | cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip, | ||
606 | ts->kernel_start); | ||
607 | if (!cp) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp, | ||
611 | true); | ||
612 | if (err) | ||
613 | return err; | ||
614 | |||
615 | return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref, | ||
616 | to_al->sym); | ||
617 | } | ||
618 | |||
619 | static int thread_stack__trace_begin(struct thread *thread, | ||
620 | struct thread_stack *ts, u64 timestamp, | ||
621 | u64 ref) | ||
622 | { | ||
623 | struct thread_stack_entry *tse; | ||
624 | int err; | ||
625 | |||
626 | if (!ts->cnt) | ||
627 | return 0; | ||
628 | |||
629 | /* Pop trace end */ | ||
630 | tse = &ts->stack[ts->cnt - 1]; | ||
631 | if (tse->cp->sym == NULL && tse->cp->ip == 0) { | ||
632 | err = thread_stack__call_return(thread, ts, --ts->cnt, | ||
633 | timestamp, ref, false); | ||
634 | if (err) | ||
635 | return err; | ||
636 | } | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static int thread_stack__trace_end(struct thread_stack *ts, | ||
642 | struct perf_sample *sample, u64 ref) | ||
643 | { | ||
644 | struct call_path_root *cpr = ts->crp->cpr; | ||
645 | struct call_path *cp; | ||
646 | u64 ret_addr; | ||
647 | |||
648 | /* No point having 'trace end' on the bottom of the stack */ | ||
649 | if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref)) | ||
650 | return 0; | ||
651 | |||
652 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0, | ||
653 | ts->kernel_start); | ||
654 | if (!cp) | ||
655 | return -ENOMEM; | ||
656 | |||
657 | ret_addr = sample->ip + sample->insn_len; | ||
658 | |||
659 | return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp, | ||
660 | false); | ||
661 | } | ||
662 | |||
663 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
664 | struct perf_sample *sample, | ||
665 | struct addr_location *from_al, | ||
666 | struct addr_location *to_al, u64 ref, | ||
667 | struct call_return_processor *crp) | ||
668 | { | ||
669 | struct thread_stack *ts = thread->ts; | ||
670 | int err = 0; | ||
671 | |||
672 | if (ts) { | ||
673 | if (!ts->crp) { | ||
674 | /* Supersede thread_stack__event() */ | ||
675 | thread_stack__free(thread); | ||
676 | thread->ts = thread_stack__new(thread, crp); | ||
677 | if (!thread->ts) | ||
678 | return -ENOMEM; | ||
679 | ts = thread->ts; | ||
680 | ts->comm = comm; | ||
681 | } | ||
682 | } else { | ||
683 | thread->ts = thread_stack__new(thread, crp); | ||
684 | if (!thread->ts) | ||
685 | return -ENOMEM; | ||
686 | ts = thread->ts; | ||
687 | ts->comm = comm; | ||
688 | } | ||
689 | |||
690 | /* Flush stack on exec */ | ||
691 | if (ts->comm != comm && thread->pid_ == thread->tid) { | ||
692 | err = thread_stack__flush(thread, ts); | ||
693 | if (err) | ||
694 | return err; | ||
695 | ts->comm = comm; | ||
696 | } | ||
697 | |||
698 | /* If the stack is empty, put the current symbol on the stack */ | ||
699 | if (!ts->cnt) { | ||
700 | err = thread_stack__bottom(thread, ts, sample, from_al, to_al, | ||
701 | ref); | ||
702 | if (err) | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | ts->branch_count += 1; | ||
707 | ts->last_time = sample->time; | ||
708 | |||
709 | if (sample->flags & PERF_IP_FLAG_CALL) { | ||
710 | struct call_path_root *cpr = ts->crp->cpr; | ||
711 | struct call_path *cp; | ||
712 | u64 ret_addr; | ||
713 | |||
714 | if (!sample->ip || !sample->addr) | ||
715 | return 0; | ||
716 | |||
717 | ret_addr = sample->ip + sample->insn_len; | ||
718 | if (ret_addr == sample->addr) | ||
719 | return 0; /* Zero-length calls are excluded */ | ||
720 | |||
721 | cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, | ||
722 | to_al->sym, sample->addr, | ||
723 | ts->kernel_start); | ||
724 | if (!cp) | ||
725 | return -ENOMEM; | ||
726 | err = thread_stack__push_cp(ts, ret_addr, sample->time, ref, | ||
727 | cp, false); | ||
728 | } else if (sample->flags & PERF_IP_FLAG_RETURN) { | ||
729 | if (!sample->ip || !sample->addr) | ||
730 | return 0; | ||
731 | |||
732 | err = thread_stack__pop_cp(thread, ts, sample->addr, | ||
733 | sample->time, ref, from_al->sym); | ||
734 | if (err) { | ||
735 | if (err < 0) | ||
736 | return err; | ||
737 | err = thread_stack__no_call_return(thread, ts, sample, | ||
738 | from_al, to_al, ref); | ||
739 | } | ||
740 | } else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) { | ||
741 | err = thread_stack__trace_begin(thread, ts, sample->time, ref); | ||
742 | } else if (sample->flags & PERF_IP_FLAG_TRACE_END) { | ||
743 | err = thread_stack__trace_end(ts, sample, ref); | ||
744 | } | ||
745 | |||
746 | return err; | ||
747 | } | ||
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h new file mode 100644 index 000000000000..b843bbef8ba2 --- /dev/null +++ b/tools/perf/util/thread-stack.h | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * thread-stack.h: Synthesize a thread's stack using call / return events | ||
3 | * Copyright (c) 2014, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #ifndef __PERF_THREAD_STACK_H | ||
17 | #define __PERF_THREAD_STACK_H | ||
18 | |||
19 | #include <sys/types.h> | ||
20 | |||
21 | #include <linux/types.h> | ||
22 | #include <linux/rbtree.h> | ||
23 | |||
24 | struct thread; | ||
25 | struct comm; | ||
26 | struct ip_callchain; | ||
27 | struct symbol; | ||
28 | struct dso; | ||
29 | struct call_return_processor; | ||
30 | struct comm; | ||
31 | struct perf_sample; | ||
32 | struct addr_location; | ||
33 | |||
34 | /* | ||
35 | * Call/Return flags. | ||
36 | * | ||
37 | * CALL_RETURN_NO_CALL: 'return' but no matching 'call' | ||
38 | * CALL_RETURN_NO_RETURN: 'call' but no matching 'return' | ||
39 | */ | ||
40 | enum { | ||
41 | CALL_RETURN_NO_CALL = 1 << 0, | ||
42 | CALL_RETURN_NO_RETURN = 1 << 1, | ||
43 | }; | ||
44 | |||
45 | /** | ||
46 | * struct call_return - paired call/return information. | ||
47 | * @thread: thread in which call/return occurred | ||
48 | * @comm: comm in which call/return occurred | ||
49 | * @cp: call path | ||
50 | * @call_time: timestamp of call (if known) | ||
51 | * @return_time: timestamp of return (if known) | ||
52 | * @branch_count: number of branches seen between call and return | ||
53 | * @call_ref: external reference to 'call' sample (e.g. db_id) | ||
54 | * @return_ref: external reference to 'return' sample (e.g. db_id) | ||
55 | * @db_id: id used for db-export | ||
56 | * @flags: Call/Return flags | ||
57 | */ | ||
58 | struct call_return { | ||
59 | struct thread *thread; | ||
60 | struct comm *comm; | ||
61 | struct call_path *cp; | ||
62 | u64 call_time; | ||
63 | u64 return_time; | ||
64 | u64 branch_count; | ||
65 | u64 call_ref; | ||
66 | u64 return_ref; | ||
67 | u64 db_id; | ||
68 | u32 flags; | ||
69 | }; | ||
70 | |||
71 | /** | ||
72 | * struct call_path - node in list of calls leading to a function call. | ||
73 | * @parent: call path to the parent function call | ||
74 | * @sym: symbol of function called | ||
75 | * @ip: only if sym is null, the ip of the function | ||
76 | * @db_id: id used for db-export | ||
77 | * @in_kernel: whether function is a in the kernel | ||
78 | * @rb_node: node in parent's tree of called functions | ||
79 | * @children: tree of call paths of functions called | ||
80 | * | ||
81 | * In combination with the call_return structure, the call_path structure | ||
82 | * defines a context-sensitve call-graph. | ||
83 | */ | ||
84 | struct call_path { | ||
85 | struct call_path *parent; | ||
86 | struct symbol *sym; | ||
87 | u64 ip; | ||
88 | u64 db_id; | ||
89 | bool in_kernel; | ||
90 | struct rb_node rb_node; | ||
91 | struct rb_root children; | ||
92 | }; | ||
93 | |||
94 | int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip, | ||
95 | u64 to_ip, u16 insn_len, u64 trace_nr); | ||
96 | void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr); | ||
97 | void thread_stack__sample(struct thread *thread, struct ip_callchain *chain, | ||
98 | size_t sz, u64 ip); | ||
99 | void thread_stack__free(struct thread *thread); | ||
100 | |||
101 | struct call_return_processor * | ||
102 | call_return_processor__new(int (*process)(struct call_return *cr, void *data), | ||
103 | void *data); | ||
104 | void call_return_processor__free(struct call_return_processor *crp); | ||
105 | int thread_stack__process(struct thread *thread, struct comm *comm, | ||
106 | struct perf_sample *sample, | ||
107 | struct addr_location *from_al, | ||
108 | struct addr_location *to_al, u64 ref, | ||
109 | struct call_return_processor *crp); | ||
110 | |||
111 | #endif | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index bf5bf858b7f6..a2157f0ef1df 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include "session.h" | 5 | #include "session.h" |
6 | #include "thread.h" | 6 | #include "thread.h" |
7 | #include "thread-stack.h" | ||
7 | #include "util.h" | 8 | #include "util.h" |
8 | #include "debug.h" | 9 | #include "debug.h" |
9 | #include "comm.h" | 10 | #include "comm.h" |
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) | |||
66 | { | 67 | { |
67 | struct comm *comm, *tmp; | 68 | struct comm *comm, *tmp; |
68 | 69 | ||
70 | thread_stack__free(thread); | ||
71 | |||
69 | if (thread->mg) { | 72 | if (thread->mg) { |
70 | map_groups__put(thread->mg); | 73 | map_groups__put(thread->mg); |
71 | thread->mg = NULL; | 74 | thread->mg = NULL; |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d34cf5c0d0d9..160fd066a7d1 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -8,6 +8,8 @@ | |||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include <strlist.h> | 9 | #include <strlist.h> |
10 | 10 | ||
11 | struct thread_stack; | ||
12 | |||
11 | struct thread { | 13 | struct thread { |
12 | union { | 14 | union { |
13 | struct rb_node rb_node; | 15 | struct rb_node rb_node; |
@@ -26,6 +28,7 @@ struct thread { | |||
26 | u64 db_id; | 28 | u64 db_id; |
27 | 29 | ||
28 | void *priv; | 30 | void *priv; |
31 | struct thread_stack *ts; | ||
29 | }; | 32 | }; |
30 | 33 | ||
31 | struct machine; | 34 | struct machine; |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 80bfdaa0e2a4..7dc44cfe25b3 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -351,4 +351,9 @@ void mem_bswap_32(void *src, int byte_size); | |||
351 | 351 | ||
352 | const char *get_filename_for_perf_kvm(void); | 352 | const char *get_filename_for_perf_kvm(void); |
353 | bool find_process(const char *name); | 353 | bool find_process(const char *name); |
354 | |||
355 | #ifdef HAVE_ZLIB_SUPPORT | ||
356 | int gzip_decompress_to_file(const char *input, int output_fd); | ||
357 | #endif | ||
358 | |||
354 | #endif /* GIT_COMPAT_UTIL_H */ | 359 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/perf/util/zlib.c b/tools/perf/util/zlib.c new file mode 100644 index 000000000000..495a449fc25c --- /dev/null +++ b/tools/perf/util/zlib.c | |||
@@ -0,0 +1,78 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <sys/mman.h> | ||
5 | #include <zlib.h> | ||
6 | |||
7 | #include "util/util.h" | ||
8 | #include "util/debug.h" | ||
9 | |||
10 | |||
11 | #define CHUNK_SIZE 16384 | ||
12 | |||
13 | int gzip_decompress_to_file(const char *input, int output_fd) | ||
14 | { | ||
15 | int ret = Z_STREAM_ERROR; | ||
16 | int input_fd; | ||
17 | void *ptr; | ||
18 | int len; | ||
19 | struct stat stbuf; | ||
20 | unsigned char buf[CHUNK_SIZE]; | ||
21 | z_stream zs = { | ||
22 | .zalloc = Z_NULL, | ||
23 | .zfree = Z_NULL, | ||
24 | .opaque = Z_NULL, | ||
25 | .avail_in = 0, | ||
26 | .next_in = Z_NULL, | ||
27 | }; | ||
28 | |||
29 | input_fd = open(input, O_RDONLY); | ||
30 | if (input_fd < 0) | ||
31 | return -1; | ||
32 | |||
33 | if (fstat(input_fd, &stbuf) < 0) | ||
34 | goto out_close; | ||
35 | |||
36 | ptr = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0); | ||
37 | if (ptr == MAP_FAILED) | ||
38 | goto out_close; | ||
39 | |||
40 | if (inflateInit2(&zs, 16 + MAX_WBITS) != Z_OK) | ||
41 | goto out_unmap; | ||
42 | |||
43 | zs.next_in = ptr; | ||
44 | zs.avail_in = stbuf.st_size; | ||
45 | |||
46 | do { | ||
47 | zs.next_out = buf; | ||
48 | zs.avail_out = CHUNK_SIZE; | ||
49 | |||
50 | ret = inflate(&zs, Z_NO_FLUSH); | ||
51 | switch (ret) { | ||
52 | case Z_NEED_DICT: | ||
53 | ret = Z_DATA_ERROR; | ||
54 | /* fall through */ | ||
55 | case Z_DATA_ERROR: | ||
56 | case Z_MEM_ERROR: | ||
57 | goto out; | ||
58 | default: | ||
59 | break; | ||
60 | } | ||
61 | |||
62 | len = CHUNK_SIZE - zs.avail_out; | ||
63 | if (writen(output_fd, buf, len) != len) { | ||
64 | ret = Z_DATA_ERROR; | ||
65 | goto out; | ||
66 | } | ||
67 | |||
68 | } while (ret != Z_STREAM_END); | ||
69 | |||
70 | out: | ||
71 | inflateEnd(&zs); | ||
72 | out_unmap: | ||
73 | munmap(ptr, stbuf.st_size); | ||
74 | out_close: | ||
75 | close(input_fd); | ||
76 | |||
77 | return ret == Z_STREAM_END ? 0 : -1; | ||
78 | } | ||