diff options
Diffstat (limited to 'tools/perf')
50 files changed, 2918 insertions, 1277 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 416684be0ad..8f8fbc227a4 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
@@ -19,3 +19,7 @@ TAGS | |||
19 | cscope* | 19 | cscope* |
20 | config.mak | 20 | config.mak |
21 | config.mak.autogen | 21 | config.mak.autogen |
22 | *-bison.* | ||
23 | *-flex.* | ||
24 | *.pyc | ||
25 | *.pyo | ||
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 6f5a498608b..85c5f026930 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -66,6 +66,12 @@ OPTIONS | |||
66 | used. This interfaces starts by centering on the line with more | 66 | used. This interfaces starts by centering on the line with more |
67 | samples, TAB/UNTAB cycles through the lines with more samples. | 67 | samples, TAB/UNTAB cycles through the lines with more samples. |
68 | 68 | ||
69 | -c:: | ||
70 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
71 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
72 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
73 | CPUs. | ||
74 | |||
69 | SEE ALSO | 75 | SEE ALSO |
70 | -------- | 76 | -------- |
71 | linkperf:perf-record[1], linkperf:perf-report[1] | 77 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 02bafce4b34..2780d9ce48b 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -34,9 +34,11 @@ OPTIONS | |||
34 | Specify vmlinux path which has debuginfo (Dwarf binary). | 34 | Specify vmlinux path which has debuginfo (Dwarf binary). |
35 | 35 | ||
36 | -m:: | 36 | -m:: |
37 | --module=MODNAME:: | 37 | --module=MODNAME|PATH:: |
38 | Specify module name in which perf-probe searches probe points | 38 | Specify module name in which perf-probe searches probe points |
39 | or lines. | 39 | or lines. If a path of module file is passed, perf-probe |
40 | treat it as an offline module (this means you can add a probe on | ||
41 | a module which has not been loaded yet). | ||
40 | 42 | ||
41 | -s:: | 43 | -s:: |
42 | --source=PATH:: | 44 | --source=PATH:: |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8ba03d6e539..04253c07d19 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -80,15 +80,24 @@ OPTIONS | |||
80 | --dump-raw-trace:: | 80 | --dump-raw-trace:: |
81 | Dump raw trace in ASCII. | 81 | Dump raw trace in ASCII. |
82 | 82 | ||
83 | -g [type,min]:: | 83 | -g [type,min,order]:: |
84 | --call-graph:: | 84 | --call-graph:: |
85 | Display call chains using type and min percent threshold. | 85 | Display call chains using type, min percent threshold and order. |
86 | type can be either: | 86 | type can be either: |
87 | - flat: single column, linear exposure of call chains. | 87 | - flat: single column, linear exposure of call chains. |
88 | - graph: use a graph tree, displaying absolute overhead rates. | 88 | - graph: use a graph tree, displaying absolute overhead rates. |
89 | - fractal: like graph, but displays relative rates. Each branch of | 89 | - fractal: like graph, but displays relative rates. Each branch of |
90 | the tree is considered as a new profiled object. + | 90 | the tree is considered as a new profiled object. + |
91 | Default: fractal,0.5. | 91 | |
92 | order can be either: | ||
93 | - callee: callee based call graph. | ||
94 | - caller: inverted caller based call graph. | ||
95 | |||
96 | Default: fractal,0.5,callee. | ||
97 | |||
98 | -G:: | ||
99 | --inverted:: | ||
100 | alias for inverted caller based call graph. | ||
92 | 101 | ||
93 | --pretty=<key>:: | 102 | --pretty=<key>:: |
94 | Pretty printing style. key: normal, raw | 103 | Pretty printing style. key: normal, raw |
@@ -119,6 +128,12 @@ OPTIONS | |||
119 | --symfs=<directory>:: | 128 | --symfs=<directory>:: |
120 | Look for files with symbols relative to this directory. | 129 | Look for files with symbols relative to this directory. |
121 | 130 | ||
131 | -c:: | ||
132 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
133 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
134 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
135 | CPUs. | ||
136 | |||
122 | SEE ALSO | 137 | SEE ALSO |
123 | -------- | 138 | -------- |
124 | linkperf:perf-stat[1] | 139 | linkperf:perf-stat[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 86c87e214b1..db017867d9e 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -115,10 +115,10 @@ OPTIONS | |||
115 | -f:: | 115 | -f:: |
116 | --fields:: | 116 | --fields:: |
117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
118 | comm, tid, pid, time, cpu, event, trace, sym. Field | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr. |
119 | list can be prepended with the type, trace, sw or hw, | 119 | Field list can be prepended with the type, trace, sw or hw, |
120 | to indicate to which event type the field list applies. | 120 | to indicate to which event type the field list applies. |
121 | e.g., -f sw:comm,tid,time,sym and -f trace:time,cpu,trace | 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
122 | 122 | ||
123 | perf script -f <fields> | 123 | perf script -f <fields> |
124 | 124 | ||
@@ -132,17 +132,17 @@ OPTIONS | |||
132 | The arguments are processed in the order received. A later usage can | 132 | The arguments are processed in the order received. A later usage can |
133 | reset a prior request. e.g.: | 133 | reset a prior request. e.g.: |
134 | 134 | ||
135 | -f trace: -f comm,tid,time,sym | 135 | -f trace: -f comm,tid,time,ip,sym |
136 | 136 | ||
137 | The first -f suppresses trace events (field list is ""), but then the | 137 | The first -f suppresses trace events (field list is ""), but then the |
138 | second invocation sets the fields to comm,tid,time,sym. In this case a | 138 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a |
139 | warning is given to the user: | 139 | warning is given to the user: |
140 | 140 | ||
141 | "Overriding previous field request for all events." | 141 | "Overriding previous field request for all events." |
142 | 142 | ||
143 | Alternativey, consider the order: | 143 | Alternativey, consider the order: |
144 | 144 | ||
145 | -f comm,tid,time,sym -f trace: | 145 | -f comm,tid,time,ip,sym -f trace: |
146 | 146 | ||
147 | The first -f sets the fields for all events and the second -f | 147 | The first -f sets the fields for all events and the second -f |
148 | suppresses trace events. The user is given a warning message about | 148 | suppresses trace events. The user is given a warning message about |
@@ -182,6 +182,12 @@ OPTIONS | |||
182 | --hide-call-graph:: | 182 | --hide-call-graph:: |
183 | When printing symbols do not display call chain. | 183 | When printing symbols do not display call chain. |
184 | 184 | ||
185 | -c:: | ||
186 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | ||
187 | be provided as a comma-separated list with no space: 0,1. Ranges of | ||
188 | CPUs are specified with -: 0-2. Default is to report samples on all | ||
189 | CPUs. | ||
190 | |||
185 | SEE ALSO | 191 | SEE ALSO |
186 | -------- | 192 | -------- |
187 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 193 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 940257b5774..e9d5c271db6 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -30,6 +30,8 @@ endif | |||
30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. | 30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. |
31 | # | 31 | # |
32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. | 32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. |
33 | # | ||
34 | # Define WERROR=0 to disable treating any warnings as errors. | ||
33 | 35 | ||
34 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 36 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
35 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 37 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
@@ -52,7 +54,10 @@ ifeq ($(ARCH),i386) | |||
52 | endif | 54 | endif |
53 | ifeq ($(ARCH),x86_64) | 55 | ifeq ($(ARCH),x86_64) |
54 | ARCH := x86 | 56 | ARCH := x86 |
55 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) | 57 | IS_X86_64 := 0 |
58 | ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) | ||
59 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) | ||
60 | endif | ||
56 | ifeq (${IS_X86_64}, 1) | 61 | ifeq (${IS_X86_64}, 1) |
57 | RAW_ARCH := x86_64 | 62 | RAW_ARCH := x86_64 |
58 | ARCH_CFLAGS := -DARCH_X86_64 | 63 | ARCH_CFLAGS := -DARCH_X86_64 |
@@ -60,6 +65,11 @@ ifeq ($(ARCH),x86_64) | |||
60 | endif | 65 | endif |
61 | endif | 66 | endif |
62 | 67 | ||
68 | # Treat warnings as errors unless directed not to | ||
69 | ifneq ($(WERROR),0) | ||
70 | CFLAGS_WERROR := -Werror | ||
71 | endif | ||
72 | |||
63 | # | 73 | # |
64 | # Include saner warnings here, which can catch bugs: | 74 | # Include saner warnings here, which can catch bugs: |
65 | # | 75 | # |
@@ -92,7 +102,7 @@ ifndef PERF_DEBUG | |||
92 | CFLAGS_OPTIMIZE = -O6 | 102 | CFLAGS_OPTIMIZE = -O6 |
93 | endif | 103 | endif |
94 | 104 | ||
95 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | 105 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) |
96 | EXTLIBS = -lpthread -lrt -lelf -lm | 106 | EXTLIBS = -lpthread -lrt -lelf -lm |
97 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | 107 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 |
98 | ALL_LDFLAGS = $(LDFLAGS) | 108 | ALL_LDFLAGS = $(LDFLAGS) |
@@ -178,9 +188,9 @@ strip-libs = $(filter-out -l%,$(1)) | |||
178 | 188 | ||
179 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) | 189 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) |
180 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 190 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
181 | --quiet build_ext \ | 191 | --quiet build_ext; \ |
182 | --build-lib='$(OUTPUT)python' \ | 192 | mkdir -p $(OUTPUT)python && \ |
183 | --build-temp='$(OUTPUT)python/temp' | 193 | cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ |
184 | # | 194 | # |
185 | # No Perl scripts right now: | 195 | # No Perl scripts right now: |
186 | # | 196 | # |
@@ -279,6 +289,7 @@ LIB_H += util/thread.h | |||
279 | LIB_H += util/thread_map.h | 289 | LIB_H += util/thread_map.h |
280 | LIB_H += util/trace-event.h | 290 | LIB_H += util/trace-event.h |
281 | LIB_H += util/probe-finder.h | 291 | LIB_H += util/probe-finder.h |
292 | LIB_H += util/dwarf-aux.h | ||
282 | LIB_H += util/probe-event.h | 293 | LIB_H += util/probe-event.h |
283 | LIB_H += util/pstack.h | 294 | LIB_H += util/pstack.h |
284 | LIB_H += util/cpumap.h | 295 | LIB_H += util/cpumap.h |
@@ -435,6 +446,7 @@ else | |||
435 | BASIC_CFLAGS += -DDWARF_SUPPORT | 446 | BASIC_CFLAGS += -DDWARF_SUPPORT |
436 | EXTLIBS += -lelf -ldw | 447 | EXTLIBS += -lelf -ldw |
437 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 448 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
449 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | ||
438 | endif # PERF_HAVE_DWARF_REGS | 450 | endif # PERF_HAVE_DWARF_REGS |
439 | endif # NO_DWARF | 451 | endif # NO_DWARF |
440 | 452 | ||
@@ -504,9 +516,13 @@ else | |||
504 | 516 | ||
505 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | 517 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) |
506 | 518 | ||
507 | python-clean := $(PYTHON_WORD) util/setup.py clean \ | 519 | # python extension build directories |
508 | --build-lib='$(OUTPUT)python' \ | 520 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ |
509 | --build-temp='$(OUTPUT)python/temp' | 521 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ |
522 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
523 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
524 | |||
525 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
510 | 526 | ||
511 | ifdef NO_LIBPYTHON | 527 | ifdef NO_LIBPYTHON |
512 | $(call disable-python) | 528 | $(call disable-python) |
@@ -863,6 +879,9 @@ install: all | |||
863 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | 879 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' |
864 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 880 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
865 | 881 | ||
882 | install-python_ext: | ||
883 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' | ||
884 | |||
866 | install-doc: | 885 | install-doc: |
867 | $(MAKE) -C Documentation install | 886 | $(MAKE) -C Documentation install |
868 | 887 | ||
@@ -890,7 +909,7 @@ quick-install-html: | |||
890 | ### Cleaning rules | 909 | ### Cleaning rules |
891 | 910 | ||
892 | clean: | 911 | clean: |
893 | $(RM) $(OUTPUT){*.o,*/*.o,*/*/*.o,*/*/*/*.o,$(LIB_FILE),perf-archive} | 912 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
894 | $(RM) $(ALL_PROGRAMS) perf | 913 | $(RM) $(ALL_PROGRAMS) perf |
895 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 914 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
896 | $(MAKE) -C Documentation/ clean | 915 | $(MAKE) -C Documentation/ clean |
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index fff6450c8c9..e8d5c551c69 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c | |||
@@ -8,7 +8,10 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <stdlib.h> | ||
12 | #ifndef __UCLIBC__ | ||
11 | #include <libio.h> | 13 | #include <libio.h> |
14 | #endif | ||
12 | #include <dwarf-regs.h> | 15 | #include <dwarf-regs.h> |
13 | 16 | ||
14 | struct pt_regs_dwarfnum { | 17 | struct pt_regs_dwarfnum { |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 7b139e1e7e8..555aefd7fe0 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include "util/hist.h" | 28 | #include "util/hist.h" |
29 | #include "util/session.h" | 29 | #include "util/session.h" |
30 | 30 | ||
31 | #include <linux/bitmap.h> | ||
32 | |||
31 | static char const *input_name = "perf.data"; | 33 | static char const *input_name = "perf.data"; |
32 | 34 | ||
33 | static bool force, use_tui, use_stdio; | 35 | static bool force, use_tui, use_stdio; |
@@ -38,6 +40,9 @@ static bool print_line; | |||
38 | 40 | ||
39 | static const char *sym_hist_filter; | 41 | static const char *sym_hist_filter; |
40 | 42 | ||
43 | static const char *cpu_list; | ||
44 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
45 | |||
41 | static int perf_evlist__add_sample(struct perf_evlist *evlist, | 46 | static int perf_evlist__add_sample(struct perf_evlist *evlist, |
42 | struct perf_sample *sample, | 47 | struct perf_sample *sample, |
43 | struct perf_evsel *evsel, | 48 | struct perf_evsel *evsel, |
@@ -90,6 +95,9 @@ static int process_sample_event(union perf_event *event, | |||
90 | return -1; | 95 | return -1; |
91 | } | 96 | } |
92 | 97 | ||
98 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
99 | return 0; | ||
100 | |||
93 | if (!al.filtered && | 101 | if (!al.filtered && |
94 | perf_evlist__add_sample(session->evlist, sample, evsel, &al)) { | 102 | perf_evlist__add_sample(session->evlist, sample, evsel, &al)) { |
95 | pr_warning("problem incrementing symbol count, " | 103 | pr_warning("problem incrementing symbol count, " |
@@ -177,6 +185,12 @@ static int __cmd_annotate(void) | |||
177 | if (session == NULL) | 185 | if (session == NULL) |
178 | return -ENOMEM; | 186 | return -ENOMEM; |
179 | 187 | ||
188 | if (cpu_list) { | ||
189 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | ||
190 | if (ret) | ||
191 | goto out_delete; | ||
192 | } | ||
193 | |||
180 | ret = perf_session__process_events(session, &event_ops); | 194 | ret = perf_session__process_events(session, &event_ops); |
181 | if (ret) | 195 | if (ret) |
182 | goto out_delete; | 196 | goto out_delete; |
@@ -252,6 +266,7 @@ static const struct option options[] = { | |||
252 | "print matching source lines (may be slow)"), | 266 | "print matching source lines (may be slow)"), |
253 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 267 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
254 | "Don't shorten the displayed pathnames"), | 268 | "Don't shorten the displayed pathnames"), |
269 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
255 | OPT_END() | 270 | OPT_END() |
256 | }; | 271 | }; |
257 | 272 | ||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 9ac05aafd9b..899080ace26 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -942,10 +942,10 @@ static const char *record_args[] = { | |||
942 | "-f", | 942 | "-f", |
943 | "-m", "1024", | 943 | "-m", "1024", |
944 | "-c", "1", | 944 | "-c", "1", |
945 | "-e", "lock:lock_acquire:r", | 945 | "-e", "lock:lock_acquire", |
946 | "-e", "lock:lock_acquired:r", | 946 | "-e", "lock:lock_acquired", |
947 | "-e", "lock:lock_contended:r", | 947 | "-e", "lock:lock_contended", |
948 | "-e", "lock:lock_release:r", | 948 | "-e", "lock:lock_release", |
949 | }; | 949 | }; |
950 | 950 | ||
951 | static int __cmd_record(int argc, const char **argv) | 951 | static int __cmd_record(int argc, const char **argv) |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 2c0e64d0b4a..710ae3d0a48 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -134,10 +134,18 @@ static int opt_show_lines(const struct option *opt __used, | |||
134 | { | 134 | { |
135 | int ret = 0; | 135 | int ret = 0; |
136 | 136 | ||
137 | if (str) | 137 | if (!str) |
138 | ret = parse_line_range_desc(str, ¶ms.line_range); | 138 | return 0; |
139 | INIT_LIST_HEAD(¶ms.line_range.line_list); | 139 | |
140 | if (params.show_lines) { | ||
141 | pr_warning("Warning: more than one --line options are" | ||
142 | " detected. Only the first one is valid.\n"); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
140 | params.show_lines = true; | 146 | params.show_lines = true; |
147 | ret = parse_line_range_desc(str, ¶ms.line_range); | ||
148 | INIT_LIST_HEAD(¶ms.line_range.line_list); | ||
141 | 149 | ||
142 | return ret; | 150 | return ret; |
143 | } | 151 | } |
@@ -242,7 +250,8 @@ static const struct option options[] = { | |||
242 | OPT_STRING('s', "source", &symbol_conf.source_prefix, | 250 | OPT_STRING('s', "source", &symbol_conf.source_prefix, |
243 | "directory", "path to kernel source"), | 251 | "directory", "path to kernel source"), |
244 | OPT_STRING('m', "module", ¶ms.target_module, | 252 | OPT_STRING('m', "module", ¶ms.target_module, |
245 | "modname", "target module name"), | 253 | "modname|path", |
254 | "target module name (for online) or path (for offline)"), | ||
246 | #endif | 255 | #endif |
247 | OPT__DRY_RUN(&probe_event_dry_run), | 256 | OPT__DRY_RUN(&probe_event_dry_run), |
248 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 257 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8e2c8579818..f4c3fbee4ba 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -30,8 +30,6 @@ | |||
30 | #include <sched.h> | 30 | #include <sched.h> |
31 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
32 | 32 | ||
33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
34 | |||
35 | enum write_mode_t { | 33 | enum write_mode_t { |
36 | WRITE_FORCE, | 34 | WRITE_FORCE, |
37 | WRITE_APPEND | 35 | WRITE_APPEND |
@@ -47,7 +45,7 @@ static int freq = 1000; | |||
47 | static int output; | 45 | static int output; |
48 | static int pipe_output = 0; | 46 | static int pipe_output = 0; |
49 | static const char *output_name = NULL; | 47 | static const char *output_name = NULL; |
50 | static int group = 0; | 48 | static bool group = false; |
51 | static int realtime_prio = 0; | 49 | static int realtime_prio = 0; |
52 | static bool nodelay = false; | 50 | static bool nodelay = false; |
53 | static bool raw_samples = false; | 51 | static bool raw_samples = false; |
@@ -163,6 +161,7 @@ static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | |||
163 | struct perf_event_attr *attr = &evsel->attr; | 161 | struct perf_event_attr *attr = &evsel->attr; |
164 | int track = !evsel->idx; /* only the first counter needs these */ | 162 | int track = !evsel->idx; /* only the first counter needs these */ |
165 | 163 | ||
164 | attr->disabled = 1; | ||
166 | attr->inherit = !no_inherit; | 165 | attr->inherit = !no_inherit; |
167 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 166 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
168 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 167 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -438,7 +437,6 @@ static void mmap_read_all(void) | |||
438 | 437 | ||
439 | static int __cmd_record(int argc, const char **argv) | 438 | static int __cmd_record(int argc, const char **argv) |
440 | { | 439 | { |
441 | int i; | ||
442 | struct stat st; | 440 | struct stat st; |
443 | int flags; | 441 | int flags; |
444 | int err; | 442 | int err; |
@@ -674,6 +672,8 @@ static int __cmd_record(int argc, const char **argv) | |||
674 | } | 672 | } |
675 | } | 673 | } |
676 | 674 | ||
675 | perf_evlist__enable(evsel_list); | ||
676 | |||
677 | /* | 677 | /* |
678 | * Let the child rip | 678 | * Let the child rip |
679 | */ | 679 | */ |
@@ -682,7 +682,6 @@ static int __cmd_record(int argc, const char **argv) | |||
682 | 682 | ||
683 | for (;;) { | 683 | for (;;) { |
684 | int hits = samples; | 684 | int hits = samples; |
685 | int thread; | ||
686 | 685 | ||
687 | mmap_read_all(); | 686 | mmap_read_all(); |
688 | 687 | ||
@@ -693,19 +692,8 @@ static int __cmd_record(int argc, const char **argv) | |||
693 | waking++; | 692 | waking++; |
694 | } | 693 | } |
695 | 694 | ||
696 | if (done) { | 695 | if (done) |
697 | for (i = 0; i < evsel_list->cpus->nr; i++) { | 696 | perf_evlist__disable(evsel_list); |
698 | struct perf_evsel *pos; | ||
699 | |||
700 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
701 | for (thread = 0; | ||
702 | thread < evsel_list->threads->nr; | ||
703 | thread++) | ||
704 | ioctl(FD(pos, i, thread), | ||
705 | PERF_EVENT_IOC_DISABLE); | ||
706 | } | ||
707 | } | ||
708 | } | ||
709 | } | 697 | } |
710 | 698 | ||
711 | if (quiet || signr == SIGUSR1) | 699 | if (quiet || signr == SIGUSR1) |
@@ -740,7 +728,7 @@ static bool force, append_file; | |||
740 | const struct option record_options[] = { | 728 | const struct option record_options[] = { |
741 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 729 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
742 | "event selector. use 'perf list' to list available events", | 730 | "event selector. use 'perf list' to list available events", |
743 | parse_events), | 731 | parse_events_option), |
744 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 732 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
745 | "event filter", parse_filter), | 733 | "event filter", parse_filter), |
746 | OPT_INTEGER('p', "pid", &target_pid, | 734 | OPT_INTEGER('p', "pid", &target_pid, |
@@ -768,6 +756,8 @@ const struct option record_options[] = { | |||
768 | "child tasks do not inherit counters"), | 756 | "child tasks do not inherit counters"), |
769 | OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), | 757 | OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), |
770 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), | 758 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), |
759 | OPT_BOOLEAN(0, "group", &group, | ||
760 | "put the counters into a counter group"), | ||
771 | OPT_BOOLEAN('g', "call-graph", &call_graph, | 761 | OPT_BOOLEAN('g', "call-graph", &call_graph, |
772 | "do call-graph (stack chain/backtrace) recording"), | 762 | "do call-graph (stack chain/backtrace) recording"), |
773 | OPT_INCR('v', "verbose", &verbose, | 763 | OPT_INCR('v', "verbose", &verbose, |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 287a173523a..d7ff277bdb7 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include "util/sort.h" | 33 | #include "util/sort.h" |
34 | #include "util/hist.h" | 34 | #include "util/hist.h" |
35 | 35 | ||
36 | #include <linux/bitmap.h> | ||
37 | |||
36 | static char const *input_name = "perf.data"; | 38 | static char const *input_name = "perf.data"; |
37 | 39 | ||
38 | static bool force, use_tui, use_stdio; | 40 | static bool force, use_tui, use_stdio; |
@@ -45,9 +47,13 @@ static struct perf_read_values show_threads_values; | |||
45 | static const char default_pretty_printing_style[] = "normal"; | 47 | static const char default_pretty_printing_style[] = "normal"; |
46 | static const char *pretty_printing_style = default_pretty_printing_style; | 48 | static const char *pretty_printing_style = default_pretty_printing_style; |
47 | 49 | ||
48 | static char callchain_default_opt[] = "fractal,0.5"; | 50 | static char callchain_default_opt[] = "fractal,0.5,callee"; |
51 | static bool inverted_callchain; | ||
49 | static symbol_filter_t annotate_init; | 52 | static symbol_filter_t annotate_init; |
50 | 53 | ||
54 | static const char *cpu_list; | ||
55 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
56 | |||
51 | static int perf_session__add_hist_entry(struct perf_session *session, | 57 | static int perf_session__add_hist_entry(struct perf_session *session, |
52 | struct addr_location *al, | 58 | struct addr_location *al, |
53 | struct perf_sample *sample, | 59 | struct perf_sample *sample, |
@@ -116,6 +122,9 @@ static int process_sample_event(union perf_event *event, | |||
116 | if (al.filtered || (hide_unresolved && al.sym == NULL)) | 122 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
117 | return 0; | 123 | return 0; |
118 | 124 | ||
125 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
126 | return 0; | ||
127 | |||
119 | if (al.map != NULL) | 128 | if (al.map != NULL) |
120 | al.map->dso->hit = 1; | 129 | al.map->dso->hit = 1; |
121 | 130 | ||
@@ -153,23 +162,22 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
153 | { | 162 | { |
154 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { | 163 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { |
155 | if (sort__has_parent) { | 164 | if (sort__has_parent) { |
156 | fprintf(stderr, "selected --sort parent, but no" | 165 | ui__warning("Selected --sort parent, but no " |
157 | " callchain data. Did you call" | 166 | "callchain data. Did you call " |
158 | " perf record without -g?\n"); | 167 | "'perf record' without -g?\n"); |
159 | return -EINVAL; | 168 | return -EINVAL; |
160 | } | 169 | } |
161 | if (symbol_conf.use_callchain) { | 170 | if (symbol_conf.use_callchain) { |
162 | fprintf(stderr, "selected -g but no callchain data." | 171 | ui__warning("Selected -g but no callchain data. Did " |
163 | " Did you call perf record without" | 172 | "you call 'perf record' without -g?\n"); |
164 | " -g?\n"); | ||
165 | return -1; | 173 | return -1; |
166 | } | 174 | } |
167 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 175 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
168 | !symbol_conf.use_callchain) { | 176 | !symbol_conf.use_callchain) { |
169 | symbol_conf.use_callchain = true; | 177 | symbol_conf.use_callchain = true; |
170 | if (callchain_register_param(&callchain_param) < 0) { | 178 | if (callchain_register_param(&callchain_param) < 0) { |
171 | fprintf(stderr, "Can't register callchain" | 179 | ui__warning("Can't register callchain " |
172 | " params\n"); | 180 | "params.\n"); |
173 | return -EINVAL; | 181 | return -EINVAL; |
174 | } | 182 | } |
175 | } | 183 | } |
@@ -262,6 +270,12 @@ static int __cmd_report(void) | |||
262 | if (session == NULL) | 270 | if (session == NULL) |
263 | return -ENOMEM; | 271 | return -ENOMEM; |
264 | 272 | ||
273 | if (cpu_list) { | ||
274 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | ||
275 | if (ret) | ||
276 | goto out_delete; | ||
277 | } | ||
278 | |||
265 | if (show_threads) | 279 | if (show_threads) |
266 | perf_read_values_init(&show_threads_values); | 280 | perf_read_values_init(&show_threads_values); |
267 | 281 | ||
@@ -386,13 +400,29 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
386 | if (!tok) | 400 | if (!tok) |
387 | goto setup; | 401 | goto setup; |
388 | 402 | ||
389 | tok2 = strtok(NULL, ","); | ||
390 | callchain_param.min_percent = strtod(tok, &endptr); | 403 | callchain_param.min_percent = strtod(tok, &endptr); |
391 | if (tok == endptr) | 404 | if (tok == endptr) |
392 | return -1; | 405 | return -1; |
393 | 406 | ||
394 | if (tok2) | 407 | /* get the print limit */ |
408 | tok2 = strtok(NULL, ","); | ||
409 | if (!tok2) | ||
410 | goto setup; | ||
411 | |||
412 | if (tok2[0] != 'c') { | ||
395 | callchain_param.print_limit = strtod(tok2, &endptr); | 413 | callchain_param.print_limit = strtod(tok2, &endptr); |
414 | tok2 = strtok(NULL, ","); | ||
415 | if (!tok2) | ||
416 | goto setup; | ||
417 | } | ||
418 | |||
419 | /* get the call chain order */ | ||
420 | if (!strcmp(tok2, "caller")) | ||
421 | callchain_param.order = ORDER_CALLER; | ||
422 | else if (!strcmp(tok2, "callee")) | ||
423 | callchain_param.order = ORDER_CALLEE; | ||
424 | else | ||
425 | return -1; | ||
396 | setup: | 426 | setup: |
397 | if (callchain_register_param(&callchain_param) < 0) { | 427 | if (callchain_register_param(&callchain_param) < 0) { |
398 | fprintf(stderr, "Can't register callchain params\n"); | 428 | fprintf(stderr, "Can't register callchain params\n"); |
@@ -436,9 +466,10 @@ static const struct option options[] = { | |||
436 | "regex filter to identify parent, see: '--sort parent'"), | 466 | "regex filter to identify parent, see: '--sort parent'"), |
437 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 467 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
438 | "Only display entries with parent-match"), | 468 | "Only display entries with parent-match"), |
439 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", | 469 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order", |
440 | "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. " | 470 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. " |
441 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | 471 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), |
472 | OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"), | ||
442 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 473 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
443 | "only consider symbols in these dsos"), | 474 | "only consider symbols in these dsos"), |
444 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 475 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
@@ -455,6 +486,7 @@ static const struct option options[] = { | |||
455 | "Only display entries resolved to a symbol"), | 486 | "Only display entries resolved to a symbol"), |
456 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 487 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
457 | "Look for files with symbols relative to this directory"), | 488 | "Look for files with symbols relative to this directory"), |
489 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
458 | OPT_END() | 490 | OPT_END() |
459 | }; | 491 | }; |
460 | 492 | ||
@@ -467,6 +499,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
467 | else if (use_tui) | 499 | else if (use_tui) |
468 | use_browser = 1; | 500 | use_browser = 1; |
469 | 501 | ||
502 | if (inverted_callchain) | ||
503 | callchain_param.order = ORDER_CALLER; | ||
504 | |||
470 | if (strcmp(input_name, "-") != 0) | 505 | if (strcmp(input_name, "-") != 0) |
471 | setup_browser(true); | 506 | setup_browser(true); |
472 | else | 507 | else |
@@ -504,7 +539,14 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
504 | if (parent_pattern != default_parent_pattern) { | 539 | if (parent_pattern != default_parent_pattern) { |
505 | if (sort_dimension__add("parent") < 0) | 540 | if (sort_dimension__add("parent") < 0) |
506 | return -1; | 541 | return -1; |
507 | sort_parent.elide = 1; | 542 | |
543 | /* | ||
544 | * Only show the parent fields if we explicitly | ||
545 | * sort that way. If we only use parent machinery | ||
546 | * for filtering, we don't want it. | ||
547 | */ | ||
548 | if (!strstr(sort_order, "parent")) | ||
549 | sort_parent.elide = 1; | ||
508 | } else | 550 | } else |
509 | symbol_conf.exclude_other = false; | 551 | symbol_conf.exclude_other = false; |
510 | 552 | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index dcfe8873c9a..5177964943e 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1637,23 +1637,29 @@ static struct perf_event_ops event_ops = { | |||
1637 | .ordered_samples = true, | 1637 | .ordered_samples = true, |
1638 | }; | 1638 | }; |
1639 | 1639 | ||
1640 | static int read_events(void) | 1640 | static void read_events(bool destroy, struct perf_session **psession) |
1641 | { | 1641 | { |
1642 | int err = -EINVAL; | 1642 | int err = -EINVAL; |
1643 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | 1643 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
1644 | 0, false, &event_ops); | 1644 | 0, false, &event_ops); |
1645 | if (session == NULL) | 1645 | if (session == NULL) |
1646 | return -ENOMEM; | 1646 | die("No Memory"); |
1647 | 1647 | ||
1648 | if (perf_session__has_traces(session, "record -R")) { | 1648 | if (perf_session__has_traces(session, "record -R")) { |
1649 | err = perf_session__process_events(session, &event_ops); | 1649 | err = perf_session__process_events(session, &event_ops); |
1650 | if (err) | ||
1651 | die("Failed to process events, error %d", err); | ||
1652 | |||
1650 | nr_events = session->hists.stats.nr_events[0]; | 1653 | nr_events = session->hists.stats.nr_events[0]; |
1651 | nr_lost_events = session->hists.stats.total_lost; | 1654 | nr_lost_events = session->hists.stats.total_lost; |
1652 | nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; | 1655 | nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; |
1653 | } | 1656 | } |
1654 | 1657 | ||
1655 | perf_session__delete(session); | 1658 | if (destroy) |
1656 | return err; | 1659 | perf_session__delete(session); |
1660 | |||
1661 | if (psession) | ||
1662 | *psession = session; | ||
1657 | } | 1663 | } |
1658 | 1664 | ||
1659 | static void print_bad_events(void) | 1665 | static void print_bad_events(void) |
@@ -1689,9 +1695,10 @@ static void print_bad_events(void) | |||
1689 | static void __cmd_lat(void) | 1695 | static void __cmd_lat(void) |
1690 | { | 1696 | { |
1691 | struct rb_node *next; | 1697 | struct rb_node *next; |
1698 | struct perf_session *session; | ||
1692 | 1699 | ||
1693 | setup_pager(); | 1700 | setup_pager(); |
1694 | read_events(); | 1701 | read_events(false, &session); |
1695 | sort_lat(); | 1702 | sort_lat(); |
1696 | 1703 | ||
1697 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); | 1704 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); |
@@ -1717,6 +1724,7 @@ static void __cmd_lat(void) | |||
1717 | print_bad_events(); | 1724 | print_bad_events(); |
1718 | printf("\n"); | 1725 | printf("\n"); |
1719 | 1726 | ||
1727 | perf_session__delete(session); | ||
1720 | } | 1728 | } |
1721 | 1729 | ||
1722 | static struct trace_sched_handler map_ops = { | 1730 | static struct trace_sched_handler map_ops = { |
@@ -1731,7 +1739,7 @@ static void __cmd_map(void) | |||
1731 | max_cpu = sysconf(_SC_NPROCESSORS_CONF); | 1739 | max_cpu = sysconf(_SC_NPROCESSORS_CONF); |
1732 | 1740 | ||
1733 | setup_pager(); | 1741 | setup_pager(); |
1734 | read_events(); | 1742 | read_events(true, NULL); |
1735 | print_bad_events(); | 1743 | print_bad_events(); |
1736 | } | 1744 | } |
1737 | 1745 | ||
@@ -1744,7 +1752,7 @@ static void __cmd_replay(void) | |||
1744 | 1752 | ||
1745 | test_calibrations(); | 1753 | test_calibrations(); |
1746 | 1754 | ||
1747 | read_events(); | 1755 | read_events(true, NULL); |
1748 | 1756 | ||
1749 | printf("nr_run_events: %ld\n", nr_run_events); | 1757 | printf("nr_run_events: %ld\n", nr_run_events); |
1750 | printf("nr_sleep_events: %ld\n", nr_sleep_events); | 1758 | printf("nr_sleep_events: %ld\n", nr_sleep_events); |
@@ -1769,7 +1777,7 @@ static void __cmd_replay(void) | |||
1769 | 1777 | ||
1770 | 1778 | ||
1771 | static const char * const sched_usage[] = { | 1779 | static const char * const sched_usage[] = { |
1772 | "perf sched [<options>] {record|latency|map|replay|trace}", | 1780 | "perf sched [<options>] {record|latency|map|replay|script}", |
1773 | NULL | 1781 | NULL |
1774 | }; | 1782 | }; |
1775 | 1783 | ||
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 22747de7234..09024ec2ab2 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include "util/util.h" | 13 | #include "util/util.h" |
14 | #include "util/evlist.h" | 14 | #include "util/evlist.h" |
15 | #include "util/evsel.h" | 15 | #include "util/evsel.h" |
16 | #include <linux/bitmap.h> | ||
16 | 17 | ||
17 | static char const *script_name; | 18 | static char const *script_name; |
18 | static char const *generate_script_lang; | 19 | static char const *generate_script_lang; |
@@ -21,6 +22,8 @@ static u64 last_timestamp; | |||
21 | static u64 nr_unordered; | 22 | static u64 nr_unordered; |
22 | extern const struct option record_options[]; | 23 | extern const struct option record_options[]; |
23 | static bool no_callchain; | 24 | static bool no_callchain; |
25 | static const char *cpu_list; | ||
26 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
24 | 27 | ||
25 | enum perf_output_field { | 28 | enum perf_output_field { |
26 | PERF_OUTPUT_COMM = 1U << 0, | 29 | PERF_OUTPUT_COMM = 1U << 0, |
@@ -30,7 +33,10 @@ enum perf_output_field { | |||
30 | PERF_OUTPUT_CPU = 1U << 4, | 33 | PERF_OUTPUT_CPU = 1U << 4, |
31 | PERF_OUTPUT_EVNAME = 1U << 5, | 34 | PERF_OUTPUT_EVNAME = 1U << 5, |
32 | PERF_OUTPUT_TRACE = 1U << 6, | 35 | PERF_OUTPUT_TRACE = 1U << 6, |
33 | PERF_OUTPUT_SYM = 1U << 7, | 36 | PERF_OUTPUT_IP = 1U << 7, |
37 | PERF_OUTPUT_SYM = 1U << 8, | ||
38 | PERF_OUTPUT_DSO = 1U << 9, | ||
39 | PERF_OUTPUT_ADDR = 1U << 10, | ||
34 | }; | 40 | }; |
35 | 41 | ||
36 | struct output_option { | 42 | struct output_option { |
@@ -44,7 +50,10 @@ struct output_option { | |||
44 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, | 50 | {.str = "cpu", .field = PERF_OUTPUT_CPU}, |
45 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, | 51 | {.str = "event", .field = PERF_OUTPUT_EVNAME}, |
46 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, | 52 | {.str = "trace", .field = PERF_OUTPUT_TRACE}, |
53 | {.str = "ip", .field = PERF_OUTPUT_IP}, | ||
47 | {.str = "sym", .field = PERF_OUTPUT_SYM}, | 54 | {.str = "sym", .field = PERF_OUTPUT_SYM}, |
55 | {.str = "dso", .field = PERF_OUTPUT_DSO}, | ||
56 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | ||
48 | }; | 57 | }; |
49 | 58 | ||
50 | /* default set to maintain compatibility with current format */ | 59 | /* default set to maintain compatibility with current format */ |
@@ -60,7 +69,8 @@ static struct { | |||
60 | 69 | ||
61 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 70 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
62 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 71 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
63 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 72 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
73 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
64 | 74 | ||
65 | .invalid_fields = PERF_OUTPUT_TRACE, | 75 | .invalid_fields = PERF_OUTPUT_TRACE, |
66 | }, | 76 | }, |
@@ -70,7 +80,8 @@ static struct { | |||
70 | 80 | ||
71 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 81 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
72 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 82 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
73 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 83 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
84 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
74 | 85 | ||
75 | .invalid_fields = PERF_OUTPUT_TRACE, | 86 | .invalid_fields = PERF_OUTPUT_TRACE, |
76 | }, | 87 | }, |
@@ -88,7 +99,8 @@ static struct { | |||
88 | 99 | ||
89 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 100 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
90 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 101 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
91 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_SYM, | 102 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
103 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | ||
92 | 104 | ||
93 | .invalid_fields = PERF_OUTPUT_TRACE, | 105 | .invalid_fields = PERF_OUTPUT_TRACE, |
94 | }, | 106 | }, |
@@ -157,9 +169,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
157 | !perf_session__has_traces(session, "record -R")) | 169 | !perf_session__has_traces(session, "record -R")) |
158 | return -EINVAL; | 170 | return -EINVAL; |
159 | 171 | ||
160 | if (PRINT_FIELD(SYM)) { | 172 | if (PRINT_FIELD(IP)) { |
161 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", | 173 | if (perf_event_attr__check_stype(attr, PERF_SAMPLE_IP, "IP", |
162 | PERF_OUTPUT_SYM)) | 174 | PERF_OUTPUT_IP)) |
163 | return -EINVAL; | 175 | return -EINVAL; |
164 | 176 | ||
165 | if (!no_callchain && | 177 | if (!no_callchain && |
@@ -167,6 +179,24 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
167 | symbol_conf.use_callchain = false; | 179 | symbol_conf.use_callchain = false; |
168 | } | 180 | } |
169 | 181 | ||
182 | if (PRINT_FIELD(ADDR) && | ||
183 | perf_event_attr__check_stype(attr, PERF_SAMPLE_ADDR, "ADDR", | ||
184 | PERF_OUTPUT_ADDR)) | ||
185 | return -EINVAL; | ||
186 | |||
187 | if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
188 | pr_err("Display of symbols requested but neither sample IP nor " | ||
189 | "sample address\nis selected. Hence, no addresses to convert " | ||
190 | "to symbols.\n"); | ||
191 | return -EINVAL; | ||
192 | } | ||
193 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | ||
194 | pr_err("Display of DSO requested but neither sample IP nor " | ||
195 | "sample address\nis selected. Hence, no addresses to convert " | ||
196 | "to DSO.\n"); | ||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
170 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && | 200 | if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && |
171 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", | 201 | perf_event_attr__check_stype(attr, PERF_SAMPLE_TID, "TID", |
172 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) | 202 | PERF_OUTPUT_TID|PERF_OUTPUT_PID)) |
@@ -230,7 +260,7 @@ static void print_sample_start(struct perf_sample *sample, | |||
230 | if (PRINT_FIELD(COMM)) { | 260 | if (PRINT_FIELD(COMM)) { |
231 | if (latency_format) | 261 | if (latency_format) |
232 | printf("%8.8s ", thread->comm); | 262 | printf("%8.8s ", thread->comm); |
233 | else if (PRINT_FIELD(SYM) && symbol_conf.use_callchain) | 263 | else if (PRINT_FIELD(IP) && symbol_conf.use_callchain) |
234 | printf("%s ", thread->comm); | 264 | printf("%s ", thread->comm); |
235 | else | 265 | else |
236 | printf("%16s ", thread->comm); | 266 | printf("%16s ", thread->comm); |
@@ -271,6 +301,63 @@ static void print_sample_start(struct perf_sample *sample, | |||
271 | } | 301 | } |
272 | } | 302 | } |
273 | 303 | ||
304 | static bool sample_addr_correlates_sym(struct perf_event_attr *attr) | ||
305 | { | ||
306 | if ((attr->type == PERF_TYPE_SOFTWARE) && | ||
307 | ((attr->config == PERF_COUNT_SW_PAGE_FAULTS) || | ||
308 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MIN) || | ||
309 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) | ||
310 | return true; | ||
311 | |||
312 | return false; | ||
313 | } | ||
314 | |||
315 | static void print_sample_addr(union perf_event *event, | ||
316 | struct perf_sample *sample, | ||
317 | struct perf_session *session, | ||
318 | struct thread *thread, | ||
319 | struct perf_event_attr *attr) | ||
320 | { | ||
321 | struct addr_location al; | ||
322 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
323 | const char *symname, *dsoname; | ||
324 | |||
325 | printf("%16" PRIx64, sample->addr); | ||
326 | |||
327 | if (!sample_addr_correlates_sym(attr)) | ||
328 | return; | ||
329 | |||
330 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
331 | event->ip.pid, sample->addr, &al); | ||
332 | if (!al.map) | ||
333 | thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE, | ||
334 | event->ip.pid, sample->addr, &al); | ||
335 | |||
336 | al.cpu = sample->cpu; | ||
337 | al.sym = NULL; | ||
338 | |||
339 | if (al.map) | ||
340 | al.sym = map__find_symbol(al.map, al.addr, NULL); | ||
341 | |||
342 | if (PRINT_FIELD(SYM)) { | ||
343 | if (al.sym && al.sym->name) | ||
344 | symname = al.sym->name; | ||
345 | else | ||
346 | symname = ""; | ||
347 | |||
348 | printf(" %16s", symname); | ||
349 | } | ||
350 | |||
351 | if (PRINT_FIELD(DSO)) { | ||
352 | if (al.map && al.map->dso && al.map->dso->name) | ||
353 | dsoname = al.map->dso->name; | ||
354 | else | ||
355 | dsoname = ""; | ||
356 | |||
357 | printf(" (%s)", dsoname); | ||
358 | } | ||
359 | } | ||
360 | |||
274 | static void process_event(union perf_event *event __unused, | 361 | static void process_event(union perf_event *event __unused, |
275 | struct perf_sample *sample, | 362 | struct perf_sample *sample, |
276 | struct perf_evsel *evsel, | 363 | struct perf_evsel *evsel, |
@@ -288,12 +375,16 @@ static void process_event(union perf_event *event __unused, | |||
288 | print_trace_event(sample->cpu, sample->raw_data, | 375 | print_trace_event(sample->cpu, sample->raw_data, |
289 | sample->raw_size); | 376 | sample->raw_size); |
290 | 377 | ||
291 | if (PRINT_FIELD(SYM)) { | 378 | if (PRINT_FIELD(ADDR)) |
379 | print_sample_addr(event, sample, session, thread, attr); | ||
380 | |||
381 | if (PRINT_FIELD(IP)) { | ||
292 | if (!symbol_conf.use_callchain) | 382 | if (!symbol_conf.use_callchain) |
293 | printf(" "); | 383 | printf(" "); |
294 | else | 384 | else |
295 | printf("\n"); | 385 | printf("\n"); |
296 | perf_session__print_symbols(event, sample, session); | 386 | perf_session__print_ip(event, sample, session, |
387 | PRINT_FIELD(SYM), PRINT_FIELD(DSO)); | ||
297 | } | 388 | } |
298 | 389 | ||
299 | printf("\n"); | 390 | printf("\n"); |
@@ -365,6 +456,10 @@ static int process_sample_event(union perf_event *event, | |||
365 | last_timestamp = sample->time; | 456 | last_timestamp = sample->time; |
366 | return 0; | 457 | return 0; |
367 | } | 458 | } |
459 | |||
460 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | ||
461 | return 0; | ||
462 | |||
368 | scripting_ops->process_event(event, sample, evsel, session, thread); | 463 | scripting_ops->process_event(event, sample, evsel, session, thread); |
369 | 464 | ||
370 | session->hists.stats.total_period += sample->period; | 465 | session->hists.stats.total_period += sample->period; |
@@ -985,8 +1080,9 @@ static const struct option options[] = { | |||
985 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 1080 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
986 | "Look for files with symbols relative to this directory"), | 1081 | "Look for files with symbols relative to this directory"), |
987 | OPT_CALLBACK('f', "fields", NULL, "str", | 1082 | OPT_CALLBACK('f', "fields", NULL, "str", |
988 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,sym", | 1083 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", |
989 | parse_output_fields), | 1084 | parse_output_fields), |
1085 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
990 | 1086 | ||
991 | OPT_END() | 1087 | OPT_END() |
992 | }; | 1088 | }; |
@@ -1167,6 +1263,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __used) | |||
1167 | if (session == NULL) | 1263 | if (session == NULL) |
1168 | return -ENOMEM; | 1264 | return -ENOMEM; |
1169 | 1265 | ||
1266 | if (cpu_list) { | ||
1267 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | ||
1268 | return -1; | ||
1269 | } | ||
1270 | |||
1170 | if (!no_callchain) | 1271 | if (!no_callchain) |
1171 | symbol_conf.use_callchain = true; | 1272 | symbol_conf.use_callchain = true; |
1172 | else | 1273 | else |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a9f06715e44..5deb17d9e79 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -61,6 +61,8 @@ | |||
61 | #include <locale.h> | 61 | #include <locale.h> |
62 | 62 | ||
63 | #define DEFAULT_SEPARATOR " " | 63 | #define DEFAULT_SEPARATOR " " |
64 | #define CNTR_NOT_SUPPORTED "<not supported>" | ||
65 | #define CNTR_NOT_COUNTED "<not counted>" | ||
64 | 66 | ||
65 | static struct perf_event_attr default_attrs[] = { | 67 | static struct perf_event_attr default_attrs[] = { |
66 | 68 | ||
@@ -191,6 +193,7 @@ static int big_num_opt = -1; | |||
191 | static const char *cpu_list; | 193 | static const char *cpu_list; |
192 | static const char *csv_sep = NULL; | 194 | static const char *csv_sep = NULL; |
193 | static bool csv_output = false; | 195 | static bool csv_output = false; |
196 | static bool group = false; | ||
194 | 197 | ||
195 | static volatile int done = 0; | 198 | static volatile int done = 0; |
196 | 199 | ||
@@ -278,14 +281,14 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
278 | attr->inherit = !no_inherit; | 281 | attr->inherit = !no_inherit; |
279 | 282 | ||
280 | if (system_wide) | 283 | if (system_wide) |
281 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, false); | 284 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, group); |
282 | 285 | ||
283 | if (target_pid == -1 && target_tid == -1) { | 286 | if (target_pid == -1 && target_tid == -1) { |
284 | attr->disabled = 1; | 287 | attr->disabled = 1; |
285 | attr->enable_on_exec = 1; | 288 | attr->enable_on_exec = 1; |
286 | } | 289 | } |
287 | 290 | ||
288 | return perf_evsel__open_per_thread(evsel, evsel_list->threads, false); | 291 | return perf_evsel__open_per_thread(evsel, evsel_list->threads, group); |
289 | } | 292 | } |
290 | 293 | ||
291 | /* | 294 | /* |
@@ -448,6 +451,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
448 | if (verbose) | 451 | if (verbose) |
449 | ui__warning("%s event is not supported by the kernel.\n", | 452 | ui__warning("%s event is not supported by the kernel.\n", |
450 | event_name(counter)); | 453 | event_name(counter)); |
454 | counter->supported = false; | ||
451 | continue; | 455 | continue; |
452 | } | 456 | } |
453 | 457 | ||
@@ -466,6 +470,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
466 | die("Not all events could be opened.\n"); | 470 | die("Not all events could be opened.\n"); |
467 | return -1; | 471 | return -1; |
468 | } | 472 | } |
473 | counter->supported = true; | ||
469 | } | 474 | } |
470 | 475 | ||
471 | if (perf_evlist__set_filters(evsel_list)) { | 476 | if (perf_evlist__set_filters(evsel_list)) { |
@@ -513,7 +518,10 @@ static void print_noise_pct(double total, double avg) | |||
513 | if (avg) | 518 | if (avg) |
514 | pct = 100.0*total/avg; | 519 | pct = 100.0*total/avg; |
515 | 520 | ||
516 | fprintf(stderr, " ( +-%6.2f%% )", pct); | 521 | if (csv_output) |
522 | fprintf(stderr, "%s%.2f%%", csv_sep, pct); | ||
523 | else | ||
524 | fprintf(stderr, " ( +-%6.2f%% )", pct); | ||
517 | } | 525 | } |
518 | 526 | ||
519 | static void print_noise(struct perf_evsel *evsel, double avg) | 527 | static void print_noise(struct perf_evsel *evsel, double avg) |
@@ -861,7 +869,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
861 | if (scaled == -1) { | 869 | if (scaled == -1) { |
862 | fprintf(stderr, "%*s%s%*s", | 870 | fprintf(stderr, "%*s%s%*s", |
863 | csv_output ? 0 : 18, | 871 | csv_output ? 0 : 18, |
864 | "<not counted>", | 872 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
865 | csv_sep, | 873 | csv_sep, |
866 | csv_output ? 0 : -24, | 874 | csv_output ? 0 : -24, |
867 | event_name(counter)); | 875 | event_name(counter)); |
@@ -878,13 +886,13 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
878 | else | 886 | else |
879 | abs_printout(-1, counter, avg); | 887 | abs_printout(-1, counter, avg); |
880 | 888 | ||
889 | print_noise(counter, avg); | ||
890 | |||
881 | if (csv_output) { | 891 | if (csv_output) { |
882 | fputc('\n', stderr); | 892 | fputc('\n', stderr); |
883 | return; | 893 | return; |
884 | } | 894 | } |
885 | 895 | ||
886 | print_noise(counter, avg); | ||
887 | |||
888 | if (scaled) { | 896 | if (scaled) { |
889 | double avg_enabled, avg_running; | 897 | double avg_enabled, avg_running; |
890 | 898 | ||
@@ -914,7 +922,8 @@ static void print_counter(struct perf_evsel *counter) | |||
914 | csv_output ? 0 : -4, | 922 | csv_output ? 0 : -4, |
915 | evsel_list->cpus->map[cpu], csv_sep, | 923 | evsel_list->cpus->map[cpu], csv_sep, |
916 | csv_output ? 0 : 18, | 924 | csv_output ? 0 : 18, |
917 | "<not counted>", csv_sep, | 925 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
926 | csv_sep, | ||
918 | csv_output ? 0 : -24, | 927 | csv_output ? 0 : -24, |
919 | event_name(counter)); | 928 | event_name(counter)); |
920 | 929 | ||
@@ -1024,7 +1033,7 @@ static int stat__set_big_num(const struct option *opt __used, | |||
1024 | static const struct option options[] = { | 1033 | static const struct option options[] = { |
1025 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 1034 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
1026 | "event selector. use 'perf list' to list available events", | 1035 | "event selector. use 'perf list' to list available events", |
1027 | parse_events), | 1036 | parse_events_option), |
1028 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 1037 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
1029 | "event filter", parse_filter), | 1038 | "event filter", parse_filter), |
1030 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 1039 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
@@ -1035,6 +1044,8 @@ static const struct option options[] = { | |||
1035 | "stat events on existing thread id"), | 1044 | "stat events on existing thread id"), |
1036 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1045 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1037 | "system-wide collection from all CPUs"), | 1046 | "system-wide collection from all CPUs"), |
1047 | OPT_BOOLEAN('g', "group", &group, | ||
1048 | "put the counters into a counter group"), | ||
1038 | OPT_BOOLEAN('c', "scale", &scale, | 1049 | OPT_BOOLEAN('c', "scale", &scale, |
1039 | "scale/normalize counters"), | 1050 | "scale/normalize counters"), |
1040 | OPT_INCR('v', "verbose", &verbose, | 1051 | OPT_INCR('v', "verbose", &verbose, |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 2da9162262b..efe696f936e 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "util/parse-events.h" | 12 | #include "util/parse-events.h" |
13 | #include "util/symbol.h" | 13 | #include "util/symbol.h" |
14 | #include "util/thread_map.h" | 14 | #include "util/thread_map.h" |
15 | #include "../../include/linux/hw_breakpoint.h" | ||
15 | 16 | ||
16 | static long page_size; | 17 | static long page_size; |
17 | 18 | ||
@@ -245,8 +246,8 @@ static int trace_event__id(const char *evname) | |||
245 | int err = -1, fd; | 246 | int err = -1, fd; |
246 | 247 | ||
247 | if (asprintf(&filename, | 248 | if (asprintf(&filename, |
248 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | 249 | "%s/syscalls/%s/id", |
249 | evname) < 0) | 250 | debugfs_path, evname) < 0) |
250 | return -1; | 251 | return -1; |
251 | 252 | ||
252 | fd = open(filename, O_RDONLY); | 253 | fd = open(filename, O_RDONLY); |
@@ -560,7 +561,7 @@ static int test__basic_mmap(void) | |||
560 | } | 561 | } |
561 | 562 | ||
562 | err = perf_event__parse_sample(event, attr.sample_type, sample_size, | 563 | err = perf_event__parse_sample(event, attr.sample_type, sample_size, |
563 | false, &sample); | 564 | false, &sample, false); |
564 | if (err) { | 565 | if (err) { |
565 | pr_err("Can't parse sample, err = %d\n", err); | 566 | pr_err("Can't parse sample, err = %d\n", err); |
566 | goto out_munmap; | 567 | goto out_munmap; |
@@ -600,6 +601,246 @@ out_free_threads: | |||
600 | #undef nsyscalls | 601 | #undef nsyscalls |
601 | } | 602 | } |
602 | 603 | ||
604 | #define TEST_ASSERT_VAL(text, cond) \ | ||
605 | do { \ | ||
606 | if (!cond) { \ | ||
607 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | ||
608 | return -1; \ | ||
609 | } \ | ||
610 | } while (0) | ||
611 | |||
612 | static int test__checkevent_tracepoint(struct perf_evlist *evlist) | ||
613 | { | ||
614 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
615 | struct perf_evsel, node); | ||
616 | |||
617 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
618 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
619 | TEST_ASSERT_VAL("wrong sample_type", | ||
620 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == | ||
621 | evsel->attr.sample_type); | ||
622 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) | ||
627 | { | ||
628 | struct perf_evsel *evsel; | ||
629 | |||
630 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); | ||
631 | |||
632 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
633 | TEST_ASSERT_VAL("wrong type", | ||
634 | PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
635 | TEST_ASSERT_VAL("wrong sample_type", | ||
636 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) | ||
637 | == evsel->attr.sample_type); | ||
638 | TEST_ASSERT_VAL("wrong sample_period", | ||
639 | 1 == evsel->attr.sample_period); | ||
640 | } | ||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | static int test__checkevent_raw(struct perf_evlist *evlist) | ||
645 | { | ||
646 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
647 | struct perf_evsel, node); | ||
648 | |||
649 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
650 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
651 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int test__checkevent_numeric(struct perf_evlist *evlist) | ||
656 | { | ||
657 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
658 | struct perf_evsel, node); | ||
659 | |||
660 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
661 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
662 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | ||
667 | { | ||
668 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
669 | struct perf_evsel, node); | ||
670 | |||
671 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
672 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
673 | TEST_ASSERT_VAL("wrong config", | ||
674 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | ||
679 | { | ||
680 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
681 | struct perf_evsel, node); | ||
682 | |||
683 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
684 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); | ||
685 | TEST_ASSERT_VAL("wrong config", | ||
686 | PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); | ||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | static int test__checkevent_genhw(struct perf_evlist *evlist) | ||
691 | { | ||
692 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
693 | struct perf_evsel, node); | ||
694 | |||
695 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
696 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); | ||
697 | TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static int test__checkevent_breakpoint(struct perf_evlist *evlist) | ||
702 | { | ||
703 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
704 | struct perf_evsel, node); | ||
705 | |||
706 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
707 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
708 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
709 | TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == | ||
710 | evsel->attr.bp_type); | ||
711 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 == | ||
712 | evsel->attr.bp_len); | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) | ||
717 | { | ||
718 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
719 | struct perf_evsel, node); | ||
720 | |||
721 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
722 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
723 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
724 | TEST_ASSERT_VAL("wrong bp_type", | ||
725 | HW_BREAKPOINT_X == evsel->attr.bp_type); | ||
726 | TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len); | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) | ||
731 | { | ||
732 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
733 | struct perf_evsel, node); | ||
734 | |||
735 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
736 | TEST_ASSERT_VAL("wrong type", | ||
737 | PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
738 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
739 | TEST_ASSERT_VAL("wrong bp_type", | ||
740 | HW_BREAKPOINT_R == evsel->attr.bp_type); | ||
741 | TEST_ASSERT_VAL("wrong bp_len", | ||
742 | HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); | ||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) | ||
747 | { | ||
748 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
749 | struct perf_evsel, node); | ||
750 | |||
751 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
752 | TEST_ASSERT_VAL("wrong type", | ||
753 | PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
754 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
755 | TEST_ASSERT_VAL("wrong bp_type", | ||
756 | HW_BREAKPOINT_W == evsel->attr.bp_type); | ||
757 | TEST_ASSERT_VAL("wrong bp_len", | ||
758 | HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static struct test__event_st { | ||
763 | const char *name; | ||
764 | __u32 type; | ||
765 | int (*check)(struct perf_evlist *evlist); | ||
766 | } test__events[] = { | ||
767 | { | ||
768 | .name = "syscalls:sys_enter_open", | ||
769 | .check = test__checkevent_tracepoint, | ||
770 | }, | ||
771 | { | ||
772 | .name = "syscalls:*", | ||
773 | .check = test__checkevent_tracepoint_multi, | ||
774 | }, | ||
775 | { | ||
776 | .name = "r1", | ||
777 | .check = test__checkevent_raw, | ||
778 | }, | ||
779 | { | ||
780 | .name = "1:1", | ||
781 | .check = test__checkevent_numeric, | ||
782 | }, | ||
783 | { | ||
784 | .name = "instructions", | ||
785 | .check = test__checkevent_symbolic_name, | ||
786 | }, | ||
787 | { | ||
788 | .name = "faults", | ||
789 | .check = test__checkevent_symbolic_alias, | ||
790 | }, | ||
791 | { | ||
792 | .name = "L1-dcache-load-miss", | ||
793 | .check = test__checkevent_genhw, | ||
794 | }, | ||
795 | { | ||
796 | .name = "mem:0", | ||
797 | .check = test__checkevent_breakpoint, | ||
798 | }, | ||
799 | { | ||
800 | .name = "mem:0:x", | ||
801 | .check = test__checkevent_breakpoint_x, | ||
802 | }, | ||
803 | { | ||
804 | .name = "mem:0:r", | ||
805 | .check = test__checkevent_breakpoint_r, | ||
806 | }, | ||
807 | { | ||
808 | .name = "mem:0:w", | ||
809 | .check = test__checkevent_breakpoint_w, | ||
810 | }, | ||
811 | }; | ||
812 | |||
813 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) | ||
814 | |||
815 | static int test__parse_events(void) | ||
816 | { | ||
817 | struct perf_evlist *evlist; | ||
818 | u_int i; | ||
819 | int ret = 0; | ||
820 | |||
821 | for (i = 0; i < TEST__EVENTS_CNT; i++) { | ||
822 | struct test__event_st *e = &test__events[i]; | ||
823 | |||
824 | evlist = perf_evlist__new(NULL, NULL); | ||
825 | if (evlist == NULL) | ||
826 | break; | ||
827 | |||
828 | ret = parse_events(evlist, e->name, 0); | ||
829 | if (ret) { | ||
830 | pr_debug("failed to parse event '%s', err %d\n", | ||
831 | e->name, ret); | ||
832 | break; | ||
833 | } | ||
834 | |||
835 | ret = e->check(evlist); | ||
836 | if (ret) | ||
837 | break; | ||
838 | |||
839 | perf_evlist__delete(evlist); | ||
840 | } | ||
841 | |||
842 | return ret; | ||
843 | } | ||
603 | static struct test { | 844 | static struct test { |
604 | const char *desc; | 845 | const char *desc; |
605 | int (*func)(void); | 846 | int (*func)(void); |
@@ -621,6 +862,10 @@ static struct test { | |||
621 | .func = test__basic_mmap, | 862 | .func = test__basic_mmap, |
622 | }, | 863 | }, |
623 | { | 864 | { |
865 | .desc = "parse events tests", | ||
866 | .func = test__parse_events, | ||
867 | }, | ||
868 | { | ||
624 | .func = NULL, | 869 | .func = NULL, |
625 | }, | 870 | }, |
626 | }; | 871 | }; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f2f3f4937aa..d28013b7d61 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -191,7 +191,8 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
191 | symbol__annotate_zero_histograms(sym); | 191 | symbol__annotate_zero_histograms(sym); |
192 | } | 192 | } |
193 | 193 | ||
194 | static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | 194 | static void record_precise_ip(struct sym_entry *syme, struct map *map, |
195 | int counter, u64 ip) | ||
195 | { | 196 | { |
196 | struct annotation *notes; | 197 | struct annotation *notes; |
197 | struct symbol *sym; | 198 | struct symbol *sym; |
@@ -205,8 +206,8 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
205 | if (pthread_mutex_trylock(¬es->lock)) | 206 | if (pthread_mutex_trylock(¬es->lock)) |
206 | return; | 207 | return; |
207 | 208 | ||
208 | ip = syme->map->map_ip(syme->map, ip); | 209 | ip = map->map_ip(map, ip); |
209 | symbol__inc_addr_samples(sym, syme->map, counter, ip); | 210 | symbol__inc_addr_samples(sym, map, counter, ip); |
210 | 211 | ||
211 | pthread_mutex_unlock(¬es->lock); | 212 | pthread_mutex_unlock(¬es->lock); |
212 | } | 213 | } |
@@ -810,7 +811,7 @@ static void perf_event__process_sample(const union perf_event *event, | |||
810 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); | 811 | evsel = perf_evlist__id2evsel(top.evlist, sample->id); |
811 | assert(evsel != NULL); | 812 | assert(evsel != NULL); |
812 | syme->count[evsel->idx]++; | 813 | syme->count[evsel->idx]++; |
813 | record_precise_ip(syme, evsel->idx, ip); | 814 | record_precise_ip(syme, al.map, evsel->idx, ip); |
814 | pthread_mutex_lock(&top.active_symbols_lock); | 815 | pthread_mutex_lock(&top.active_symbols_lock); |
815 | if (list_empty(&syme->node) || !syme->node.next) { | 816 | if (list_empty(&syme->node) || !syme->node.next) { |
816 | static bool first = true; | 817 | static bool first = true; |
@@ -990,7 +991,7 @@ static const char * const top_usage[] = { | |||
990 | static const struct option options[] = { | 991 | static const struct option options[] = { |
991 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 992 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
992 | "event selector. use 'perf list' to list available events", | 993 | "event selector. use 'perf list' to list available events", |
993 | parse_events), | 994 | parse_events_option), |
994 | OPT_INTEGER('c', "count", &default_interval, | 995 | OPT_INTEGER('c', "count", &default_interval, |
995 | "event period to sample"), | 996 | "event period to sample"), |
996 | OPT_INTEGER('p', "pid", &top.target_pid, | 997 | OPT_INTEGER('p', "pid", &top.target_pid, |
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c new file mode 100644 index 00000000000..0d1cdbee2f5 --- /dev/null +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -0,0 +1,230 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/rbtree.h> | ||
3 | #include <string.h> | ||
4 | #include "map.h" | ||
5 | #include "symbol.h" | ||
6 | #include "util.h" | ||
7 | #include "tests.h" | ||
8 | #include "debug.h" | ||
9 | #include "machine.h" | ||
10 | |||
11 | static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | ||
12 | struct symbol *sym) | ||
13 | { | ||
14 | bool *visited = symbol__priv(sym); | ||
15 | *visited = true; | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | int test__vmlinux_matches_kallsyms(void) | ||
20 | { | ||
21 | int err = -1; | ||
22 | struct rb_node *nd; | ||
23 | struct symbol *sym; | ||
24 | struct map *kallsyms_map, *vmlinux_map; | ||
25 | struct machine kallsyms, vmlinux; | ||
26 | enum map_type type = MAP__FUNCTION; | ||
27 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | ||
28 | |||
29 | /* | ||
30 | * Step 1: | ||
31 | * | ||
32 | * Init the machines that will hold kernel, modules obtained from | ||
33 | * both vmlinux + .ko files and from /proc/kallsyms split by modules. | ||
34 | */ | ||
35 | machine__init(&kallsyms, "", HOST_KERNEL_ID); | ||
36 | machine__init(&vmlinux, "", HOST_KERNEL_ID); | ||
37 | |||
38 | /* | ||
39 | * Step 2: | ||
40 | * | ||
41 | * Create the kernel maps for kallsyms and the DSO where we will then | ||
42 | * load /proc/kallsyms. Also create the modules maps from /proc/modules | ||
43 | * and find the .ko files that match them in /lib/modules/`uname -r`/. | ||
44 | */ | ||
45 | if (machine__create_kernel_maps(&kallsyms) < 0) { | ||
46 | pr_debug("machine__create_kernel_maps "); | ||
47 | return -1; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Step 3: | ||
52 | * | ||
53 | * Load and split /proc/kallsyms into multiple maps, one per module. | ||
54 | */ | ||
55 | if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { | ||
56 | pr_debug("dso__load_kallsyms "); | ||
57 | goto out; | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Step 4: | ||
62 | * | ||
63 | * kallsyms will be internally on demand sorted by name so that we can | ||
64 | * find the reference relocation * symbol, i.e. the symbol we will use | ||
65 | * to see if the running kernel was relocated by checking if it has the | ||
66 | * same value in the vmlinux file we load. | ||
67 | */ | ||
68 | kallsyms_map = machine__kernel_map(&kallsyms, type); | ||
69 | |||
70 | sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); | ||
71 | if (sym == NULL) { | ||
72 | pr_debug("dso__find_symbol_by_name "); | ||
73 | goto out; | ||
74 | } | ||
75 | |||
76 | ref_reloc_sym.addr = sym->start; | ||
77 | |||
78 | /* | ||
79 | * Step 5: | ||
80 | * | ||
81 | * Now repeat step 2, this time for the vmlinux file we'll auto-locate. | ||
82 | */ | ||
83 | if (machine__create_kernel_maps(&vmlinux) < 0) { | ||
84 | pr_debug("machine__create_kernel_maps "); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | vmlinux_map = machine__kernel_map(&vmlinux, type); | ||
89 | map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; | ||
90 | |||
91 | /* | ||
92 | * Step 6: | ||
93 | * | ||
94 | * Locate a vmlinux file in the vmlinux path that has a buildid that | ||
95 | * matches the one of the running kernel. | ||
96 | * | ||
97 | * While doing that look if we find the ref reloc symbol, if we find it | ||
98 | * we'll have its ref_reloc_symbol.unrelocated_addr and then | ||
99 | * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines | ||
100 | * to fixup the symbols. | ||
101 | */ | ||
102 | if (machine__load_vmlinux_path(&vmlinux, type, | ||
103 | vmlinux_matches_kallsyms_filter) <= 0) { | ||
104 | pr_debug("machine__load_vmlinux_path "); | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | err = 0; | ||
109 | /* | ||
110 | * Step 7: | ||
111 | * | ||
112 | * Now look at the symbols in the vmlinux DSO and check if we find all of them | ||
113 | * in the kallsyms dso. For the ones that are in both, check its names and | ||
114 | * end addresses too. | ||
115 | */ | ||
116 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
117 | struct symbol *pair, *first_pair; | ||
118 | bool backwards = true; | ||
119 | |||
120 | sym = rb_entry(nd, struct symbol, rb_node); | ||
121 | |||
122 | if (sym->start == sym->end) | ||
123 | continue; | ||
124 | |||
125 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
126 | pair = first_pair; | ||
127 | |||
128 | if (pair && pair->start == sym->start) { | ||
129 | next_pair: | ||
130 | if (strcmp(sym->name, pair->name) == 0) { | ||
131 | /* | ||
132 | * kallsyms don't have the symbol end, so we | ||
133 | * set that by using the next symbol start - 1, | ||
134 | * in some cases we get this up to a page | ||
135 | * wrong, trace_kmalloc when I was developing | ||
136 | * this code was one such example, 2106 bytes | ||
137 | * off the real size. More than that and we | ||
138 | * _really_ have a problem. | ||
139 | */ | ||
140 | s64 skew = sym->end - pair->end; | ||
141 | if (llabs(skew) < page_size) | ||
142 | continue; | ||
143 | |||
144 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", | ||
145 | sym->start, sym->name, sym->end, pair->end); | ||
146 | } else { | ||
147 | struct rb_node *nnd; | ||
148 | detour: | ||
149 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
150 | rb_next(&pair->rb_node); | ||
151 | if (nnd) { | ||
152 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
153 | |||
154 | if (next->start == sym->start) { | ||
155 | pair = next; | ||
156 | goto next_pair; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | if (backwards) { | ||
161 | backwards = false; | ||
162 | pair = first_pair; | ||
163 | goto detour; | ||
164 | } | ||
165 | |||
166 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
167 | sym->start, sym->name, pair->name); | ||
168 | } | ||
169 | } else | ||
170 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | ||
171 | |||
172 | err = -1; | ||
173 | } | ||
174 | |||
175 | if (!verbose) | ||
176 | goto out; | ||
177 | |||
178 | pr_info("Maps only in vmlinux:\n"); | ||
179 | |||
180 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
181 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
182 | /* | ||
183 | * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while | ||
184 | * the kernel will have the path for the vmlinux file being used, | ||
185 | * so use the short name, less descriptive but the same ("[kernel]" in | ||
186 | * both cases. | ||
187 | */ | ||
188 | pair = map_groups__find_by_name(&kallsyms.kmaps, type, | ||
189 | (pos->dso->kernel ? | ||
190 | pos->dso->short_name : | ||
191 | pos->dso->name)); | ||
192 | if (pair) | ||
193 | pair->priv = 1; | ||
194 | else | ||
195 | map__fprintf(pos, stderr); | ||
196 | } | ||
197 | |||
198 | pr_info("Maps in vmlinux with a different name in kallsyms:\n"); | ||
199 | |||
200 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
201 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
202 | |||
203 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | ||
204 | if (pair == NULL || pair->priv) | ||
205 | continue; | ||
206 | |||
207 | if (pair->start == pos->start) { | ||
208 | pair->priv = 1; | ||
209 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | ||
210 | pos->start, pos->end, pos->pgoff, pos->dso->name); | ||
211 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | ||
212 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | ||
213 | pair->start, pair->end, pair->pgoff); | ||
214 | pr_info(" %s\n", pair->dso->name); | ||
215 | pair->priv = 1; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | pr_info("Maps only in kallsyms:\n"); | ||
220 | |||
221 | for (nd = rb_first(&kallsyms.kmaps.maps[type]); | ||
222 | nd; nd = rb_next(nd)) { | ||
223 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
224 | |||
225 | if (!pos->priv) | ||
226 | map__fprintf(pos, stderr); | ||
227 | } | ||
228 | out: | ||
229 | return err; | ||
230 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 1a79df9f739..9b4ff16cac9 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -14,6 +14,11 @@ enum chain_mode { | |||
14 | CHAIN_GRAPH_REL | 14 | CHAIN_GRAPH_REL |
15 | }; | 15 | }; |
16 | 16 | ||
17 | enum chain_order { | ||
18 | ORDER_CALLER, | ||
19 | ORDER_CALLEE | ||
20 | }; | ||
21 | |||
17 | struct callchain_node { | 22 | struct callchain_node { |
18 | struct callchain_node *parent; | 23 | struct callchain_node *parent; |
19 | struct list_head siblings; | 24 | struct list_head siblings; |
@@ -41,6 +46,7 @@ struct callchain_param { | |||
41 | u32 print_limit; | 46 | u32 print_limit; |
42 | double min_percent; | 47 | double min_percent; |
43 | sort_chain_func_t sort; | 48 | sort_chain_func_t sort; |
49 | enum chain_order order; | ||
44 | }; | 50 | }; |
45 | 51 | ||
46 | struct callchain_list { | 52 | struct callchain_list { |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index e02d78cae70..fe02903f7d0 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -399,7 +399,6 @@ static int perf_config_global(void) | |||
399 | int perf_config(config_fn_t fn, void *data) | 399 | int perf_config(config_fn_t fn, void *data) |
400 | { | 400 | { |
401 | int ret = 0, found = 0; | 401 | int ret = 0, found = 0; |
402 | char *repo_config = NULL; | ||
403 | const char *home = NULL; | 402 | const char *home = NULL; |
404 | 403 | ||
405 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ | 404 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ |
@@ -414,19 +413,32 @@ int perf_config(config_fn_t fn, void *data) | |||
414 | home = getenv("HOME"); | 413 | home = getenv("HOME"); |
415 | if (perf_config_global() && home) { | 414 | if (perf_config_global() && home) { |
416 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); | 415 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); |
417 | if (!access(user_config, R_OK)) { | 416 | struct stat st; |
418 | ret += perf_config_from_file(fn, user_config, data); | 417 | |
419 | found += 1; | 418 | if (user_config == NULL) { |
419 | warning("Not enough memory to process %s/.perfconfig, " | ||
420 | "ignoring it.", home); | ||
421 | goto out; | ||
420 | } | 422 | } |
421 | free(user_config); | ||
422 | } | ||
423 | 423 | ||
424 | repo_config = perf_pathdup("config"); | 424 | if (stat(user_config, &st) < 0) |
425 | if (!access(repo_config, R_OK)) { | 425 | goto out_free; |
426 | ret += perf_config_from_file(fn, repo_config, data); | 426 | |
427 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
428 | warning("File %s not owned by current user or root, " | ||
429 | "ignoring it.", user_config); | ||
430 | goto out_free; | ||
431 | } | ||
432 | |||
433 | if (!st.st_size) | ||
434 | goto out_free; | ||
435 | |||
436 | ret += perf_config_from_file(fn, user_config, data); | ||
427 | found += 1; | 437 | found += 1; |
438 | out_free: | ||
439 | free(user_config); | ||
428 | } | 440 | } |
429 | free(repo_config); | 441 | out: |
430 | if (found == 0) | 442 | if (found == 0) |
431 | return -1; | 443 | return -1; |
432 | return ret; | 444 | return ret; |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c new file mode 100644 index 00000000000..ee51e9b4dc0 --- /dev/null +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -0,0 +1,843 @@ | |||
1 | /* | ||
2 | * dwarf-aux.c : libdw auxiliary interfaces | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <stdbool.h> | ||
21 | #include "util.h" | ||
22 | #include "debug.h" | ||
23 | #include "dwarf-aux.h" | ||
24 | |||
25 | /** | ||
26 | * cu_find_realpath - Find the realpath of the target file | ||
27 | * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) | ||
28 | * @fname: The tail filename of the target file | ||
29 | * | ||
30 | * Find the real(long) path of @fname in @cu_die. | ||
31 | */ | ||
32 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
33 | { | ||
34 | Dwarf_Files *files; | ||
35 | size_t nfiles, i; | ||
36 | const char *src = NULL; | ||
37 | int ret; | ||
38 | |||
39 | if (!fname) | ||
40 | return NULL; | ||
41 | |||
42 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
43 | if (ret != 0) | ||
44 | return NULL; | ||
45 | |||
46 | for (i = 0; i < nfiles; i++) { | ||
47 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
48 | if (strtailcmp(src, fname) == 0) | ||
49 | break; | ||
50 | } | ||
51 | if (i == nfiles) | ||
52 | return NULL; | ||
53 | return src; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * cu_get_comp_dir - Get the path of compilation directory | ||
58 | * @cu_die: a CU DIE | ||
59 | * | ||
60 | * Get the path of compilation directory of given @cu_die. | ||
61 | * Since this depends on DW_AT_comp_dir, older gcc will not | ||
62 | * embedded it. In that case, this returns NULL. | ||
63 | */ | ||
64 | const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
65 | { | ||
66 | Dwarf_Attribute attr; | ||
67 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
68 | return NULL; | ||
69 | return dwarf_formstring(&attr); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * cu_find_lineinfo - Get a line number and file name for given address | ||
74 | * @cu_die: a CU DIE | ||
75 | * @addr: An address | ||
76 | * @fname: a pointer which returns the file name string | ||
77 | * @lineno: a pointer which returns the line number | ||
78 | * | ||
79 | * Find a line number and file name for @addr in @cu_die. | ||
80 | */ | ||
81 | int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | ||
82 | const char **fname, int *lineno) | ||
83 | { | ||
84 | Dwarf_Line *line; | ||
85 | Dwarf_Addr laddr; | ||
86 | |||
87 | line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); | ||
88 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
89 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
90 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
91 | if (!*fname) | ||
92 | /* line number is useless without filename */ | ||
93 | *lineno = 0; | ||
94 | } | ||
95 | |||
96 | return *lineno ?: -ENOENT; | ||
97 | } | ||
98 | |||
99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); | ||
100 | |||
101 | /** | ||
102 | * cu_walk_functions_at - Walk on function DIEs at given address | ||
103 | * @cu_die: A CU DIE | ||
104 | * @addr: An address | ||
105 | * @callback: A callback which called with found DIEs | ||
106 | * @data: A user data | ||
107 | * | ||
108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs | ||
109 | * should be subprogram or inlined-subroutines. | ||
110 | */ | ||
111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
112 | int (*callback)(Dwarf_Die *, void *), void *data) | ||
113 | { | ||
114 | Dwarf_Die die_mem; | ||
115 | Dwarf_Die *sc_die; | ||
116 | int ret = -ENOENT; | ||
117 | |||
118 | /* Inlined function could be recursive. Trace it until fail */ | ||
119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); | ||
120 | sc_die != NULL; | ||
121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, | ||
122 | &die_mem)) { | ||
123 | ret = callback(sc_die, data); | ||
124 | if (ret) | ||
125 | break; | ||
126 | } | ||
127 | |||
128 | return ret; | ||
129 | |||
130 | } | ||
131 | |||
132 | /** | ||
133 | * die_compare_name - Compare diename and tname | ||
134 | * @dw_die: a DIE | ||
135 | * @tname: a string of target name | ||
136 | * | ||
137 | * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. | ||
138 | */ | ||
139 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
140 | { | ||
141 | const char *name; | ||
142 | name = dwarf_diename(dw_die); | ||
143 | return name ? (strcmp(tname, name) == 0) : false; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * die_get_call_lineno - Get callsite line number of inline-function instance | ||
148 | * @in_die: a DIE of an inlined function instance | ||
149 | * | ||
150 | * Get call-site line number of @in_die. This means from where the inline | ||
151 | * function is called. | ||
152 | */ | ||
153 | int die_get_call_lineno(Dwarf_Die *in_die) | ||
154 | { | ||
155 | Dwarf_Attribute attr; | ||
156 | Dwarf_Word ret; | ||
157 | |||
158 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
159 | return -ENOENT; | ||
160 | |||
161 | dwarf_formudata(&attr, &ret); | ||
162 | return (int)ret; | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * die_get_type - Get type DIE | ||
167 | * @vr_die: a DIE of a variable | ||
168 | * @die_mem: where to store a type DIE | ||
169 | * | ||
170 | * Get a DIE of the type of given variable (@vr_die), and store | ||
171 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
172 | */ | ||
173 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
174 | { | ||
175 | Dwarf_Attribute attr; | ||
176 | |||
177 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
178 | dwarf_formref_die(&attr, die_mem)) | ||
179 | return die_mem; | ||
180 | else | ||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* Get a type die, but skip qualifiers */ | ||
185 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
186 | { | ||
187 | int tag; | ||
188 | |||
189 | do { | ||
190 | vr_die = die_get_type(vr_die, die_mem); | ||
191 | if (!vr_die) | ||
192 | break; | ||
193 | tag = dwarf_tag(vr_die); | ||
194 | } while (tag == DW_TAG_const_type || | ||
195 | tag == DW_TAG_restrict_type || | ||
196 | tag == DW_TAG_volatile_type || | ||
197 | tag == DW_TAG_shared_type); | ||
198 | |||
199 | return vr_die; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * die_get_real_type - Get a type die, but skip qualifiers and typedef | ||
204 | * @vr_die: a DIE of a variable | ||
205 | * @die_mem: where to store a type DIE | ||
206 | * | ||
207 | * Get a DIE of the type of given variable (@vr_die), and store | ||
208 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
209 | * If the type is qualifiers (e.g. const) or typedef, this skips it | ||
210 | * and tries to find real type (structure or basic types, e.g. int). | ||
211 | */ | ||
212 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
213 | { | ||
214 | do { | ||
215 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
216 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
217 | |||
218 | return vr_die; | ||
219 | } | ||
220 | |||
221 | /* Get attribute and translate it as a udata */ | ||
222 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
223 | Dwarf_Word *result) | ||
224 | { | ||
225 | Dwarf_Attribute attr; | ||
226 | |||
227 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
228 | dwarf_formudata(&attr, result) != 0) | ||
229 | return -ENOENT; | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | /* Get attribute and translate it as a sdata */ | ||
235 | static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
236 | Dwarf_Sword *result) | ||
237 | { | ||
238 | Dwarf_Attribute attr; | ||
239 | |||
240 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
241 | dwarf_formsdata(&attr, result) != 0) | ||
242 | return -ENOENT; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * die_is_signed_type - Check whether a type DIE is signed or not | ||
249 | * @tp_die: a DIE of a type | ||
250 | * | ||
251 | * Get the encoding of @tp_die and return true if the encoding | ||
252 | * is signed. | ||
253 | */ | ||
254 | bool die_is_signed_type(Dwarf_Die *tp_die) | ||
255 | { | ||
256 | Dwarf_Word ret; | ||
257 | |||
258 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
259 | return false; | ||
260 | |||
261 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
262 | ret == DW_ATE_signed_fixed); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * die_get_data_member_location - Get the data-member offset | ||
267 | * @mb_die: a DIE of a member of a data structure | ||
268 | * @offs: The offset of the member in the data structure | ||
269 | * | ||
270 | * Get the offset of @mb_die in the data structure including @mb_die, and | ||
271 | * stores result offset to @offs. If any error occurs this returns errno. | ||
272 | */ | ||
273 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
274 | { | ||
275 | Dwarf_Attribute attr; | ||
276 | Dwarf_Op *expr; | ||
277 | size_t nexpr; | ||
278 | int ret; | ||
279 | |||
280 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
281 | return -ENOENT; | ||
282 | |||
283 | if (dwarf_formudata(&attr, offs) != 0) { | ||
284 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
285 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
286 | if (ret < 0 || nexpr == 0) | ||
287 | return -ENOENT; | ||
288 | |||
289 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
290 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
291 | expr[0].atom, nexpr); | ||
292 | return -ENOTSUP; | ||
293 | } | ||
294 | *offs = (Dwarf_Word)expr[0].number; | ||
295 | } | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Get the call file index number in CU DIE */ | ||
300 | static int die_get_call_fileno(Dwarf_Die *in_die) | ||
301 | { | ||
302 | Dwarf_Sword idx; | ||
303 | |||
304 | if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0) | ||
305 | return (int)idx; | ||
306 | else | ||
307 | return -ENOENT; | ||
308 | } | ||
309 | |||
310 | /* Get the declared file index number in CU DIE */ | ||
311 | static int die_get_decl_fileno(Dwarf_Die *pdie) | ||
312 | { | ||
313 | Dwarf_Sword idx; | ||
314 | |||
315 | if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0) | ||
316 | return (int)idx; | ||
317 | else | ||
318 | return -ENOENT; | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * die_get_call_file - Get callsite file name of inlined function instance | ||
323 | * @in_die: a DIE of an inlined function instance | ||
324 | * | ||
325 | * Get call-site file name of @in_die. This means from which file the inline | ||
326 | * function is called. | ||
327 | */ | ||
328 | const char *die_get_call_file(Dwarf_Die *in_die) | ||
329 | { | ||
330 | Dwarf_Die cu_die; | ||
331 | Dwarf_Files *files; | ||
332 | int idx; | ||
333 | |||
334 | idx = die_get_call_fileno(in_die); | ||
335 | if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) || | ||
336 | dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) | ||
337 | return NULL; | ||
338 | |||
339 | return dwarf_filesrc(files, idx, NULL, NULL); | ||
340 | } | ||
341 | |||
342 | |||
343 | /** | ||
344 | * die_find_child - Generic DIE search function in DIE tree | ||
345 | * @rt_die: a root DIE | ||
346 | * @callback: a callback function | ||
347 | * @data: a user data passed to the callback function | ||
348 | * @die_mem: a buffer for result DIE | ||
349 | * | ||
350 | * Trace DIE tree from @rt_die and call @callback for each child DIE. | ||
351 | * If @callback returns DIE_FIND_CB_END, this stores the DIE into | ||
352 | * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, | ||
353 | * this continues to trace the tree. Optionally, @callback can return | ||
354 | * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only | ||
355 | * the children and trace only the siblings respectively. | ||
356 | * Returns NULL if @callback can't find any appropriate DIE. | ||
357 | */ | ||
358 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
359 | int (*callback)(Dwarf_Die *, void *), | ||
360 | void *data, Dwarf_Die *die_mem) | ||
361 | { | ||
362 | Dwarf_Die child_die; | ||
363 | int ret; | ||
364 | |||
365 | ret = dwarf_child(rt_die, die_mem); | ||
366 | if (ret != 0) | ||
367 | return NULL; | ||
368 | |||
369 | do { | ||
370 | ret = callback(die_mem, data); | ||
371 | if (ret == DIE_FIND_CB_END) | ||
372 | return die_mem; | ||
373 | |||
374 | if ((ret & DIE_FIND_CB_CHILD) && | ||
375 | die_find_child(die_mem, callback, data, &child_die)) { | ||
376 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
377 | return die_mem; | ||
378 | } | ||
379 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
380 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
381 | |||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | struct __addr_die_search_param { | ||
386 | Dwarf_Addr addr; | ||
387 | Dwarf_Die *die_mem; | ||
388 | }; | ||
389 | |||
390 | /* die_find callback for non-inlined function search */ | ||
391 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
392 | { | ||
393 | struct __addr_die_search_param *ad = data; | ||
394 | |||
395 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
396 | dwarf_haspc(fn_die, ad->addr)) { | ||
397 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
398 | return DWARF_CB_ABORT; | ||
399 | } | ||
400 | return DWARF_CB_OK; | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * die_find_realfunc - Search a non-inlined function at given address | ||
405 | * @cu_die: a CU DIE which including @addr | ||
406 | * @addr: target address | ||
407 | * @die_mem: a buffer for result DIE | ||
408 | * | ||
409 | * Search a non-inlined function DIE which includes @addr. Stores the | ||
410 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
411 | */ | ||
412 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
413 | Dwarf_Die *die_mem) | ||
414 | { | ||
415 | struct __addr_die_search_param ad; | ||
416 | ad.addr = addr; | ||
417 | ad.die_mem = die_mem; | ||
418 | /* dwarf_getscopes can't find subprogram. */ | ||
419 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
420 | return NULL; | ||
421 | else | ||
422 | return die_mem; | ||
423 | } | ||
424 | |||
425 | /* die_find callback for inline function search */ | ||
426 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
427 | { | ||
428 | Dwarf_Addr *addr = data; | ||
429 | |||
430 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
431 | dwarf_haspc(die_mem, *addr)) | ||
432 | return DIE_FIND_CB_END; | ||
433 | |||
434 | return DIE_FIND_CB_CONTINUE; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * die_find_inlinefunc - Search an inlined function at given address | ||
439 | * @cu_die: a CU DIE which including @addr | ||
440 | * @addr: target address | ||
441 | * @die_mem: a buffer for result DIE | ||
442 | * | ||
443 | * Search an inlined function DIE which includes @addr. Stores the | ||
444 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
445 | * If several inlined functions are expanded recursively, this trace | ||
446 | * it and returns deepest one. | ||
447 | */ | ||
448 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
449 | Dwarf_Die *die_mem) | ||
450 | { | ||
451 | Dwarf_Die tmp_die; | ||
452 | |||
453 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
454 | if (!sp_die) | ||
455 | return NULL; | ||
456 | |||
457 | /* Inlined function could be recursive. Trace it until fail */ | ||
458 | while (sp_die) { | ||
459 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
460 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
461 | &tmp_die); | ||
462 | } | ||
463 | |||
464 | return die_mem; | ||
465 | } | ||
466 | |||
467 | struct __instance_walk_param { | ||
468 | void *addr; | ||
469 | int (*callback)(Dwarf_Die *, void *); | ||
470 | void *data; | ||
471 | int retval; | ||
472 | }; | ||
473 | |||
474 | static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) | ||
475 | { | ||
476 | struct __instance_walk_param *iwp = data; | ||
477 | Dwarf_Attribute attr_mem; | ||
478 | Dwarf_Die origin_mem; | ||
479 | Dwarf_Attribute *attr; | ||
480 | Dwarf_Die *origin; | ||
481 | int tmp; | ||
482 | |||
483 | attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); | ||
484 | if (attr == NULL) | ||
485 | return DIE_FIND_CB_CONTINUE; | ||
486 | |||
487 | origin = dwarf_formref_die(attr, &origin_mem); | ||
488 | if (origin == NULL || origin->addr != iwp->addr) | ||
489 | return DIE_FIND_CB_CONTINUE; | ||
490 | |||
491 | /* Ignore redundant instances */ | ||
492 | if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { | ||
493 | dwarf_decl_line(origin, &tmp); | ||
494 | if (die_get_call_lineno(inst) == tmp) { | ||
495 | tmp = die_get_decl_fileno(origin); | ||
496 | if (die_get_call_fileno(inst) == tmp) | ||
497 | return DIE_FIND_CB_CONTINUE; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | iwp->retval = iwp->callback(inst, iwp->data); | ||
502 | |||
503 | return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * die_walk_instances - Walk on instances of given DIE | ||
508 | * @or_die: an abstract original DIE | ||
509 | * @callback: a callback function which is called with instance DIE | ||
510 | * @data: user data | ||
511 | * | ||
512 | * Walk on the instances of give @in_die. @in_die must be an inlined function | ||
513 | * declartion. This returns the return value of @callback if it returns | ||
514 | * non-zero value, or -ENOENT if there is no instance. | ||
515 | */ | ||
516 | int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), | ||
517 | void *data) | ||
518 | { | ||
519 | Dwarf_Die cu_die; | ||
520 | Dwarf_Die die_mem; | ||
521 | struct __instance_walk_param iwp = { | ||
522 | .addr = or_die->addr, | ||
523 | .callback = callback, | ||
524 | .data = data, | ||
525 | .retval = -ENOENT, | ||
526 | }; | ||
527 | |||
528 | if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) | ||
529 | return -ENOENT; | ||
530 | |||
531 | die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); | ||
532 | |||
533 | return iwp.retval; | ||
534 | } | ||
535 | |||
536 | /* Line walker internal parameters */ | ||
537 | struct __line_walk_param { | ||
538 | bool recursive; | ||
539 | line_walk_callback_t callback; | ||
540 | void *data; | ||
541 | int retval; | ||
542 | }; | ||
543 | |||
544 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
545 | { | ||
546 | struct __line_walk_param *lw = data; | ||
547 | Dwarf_Addr addr = 0; | ||
548 | const char *fname; | ||
549 | int lineno; | ||
550 | |||
551 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
552 | fname = die_get_call_file(in_die); | ||
553 | lineno = die_get_call_lineno(in_die); | ||
554 | if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
555 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | ||
556 | if (lw->retval != 0) | ||
557 | return DIE_FIND_CB_END; | ||
558 | } | ||
559 | } | ||
560 | if (!lw->recursive) | ||
561 | /* Don't need to search recursively */ | ||
562 | return DIE_FIND_CB_SIBLING; | ||
563 | |||
564 | if (addr) { | ||
565 | fname = dwarf_decl_file(in_die); | ||
566 | if (fname && dwarf_decl_line(in_die, &lineno) == 0) { | ||
567 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | ||
568 | if (lw->retval != 0) | ||
569 | return DIE_FIND_CB_END; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /* Continue to search nested inlined function call-sites */ | ||
574 | return DIE_FIND_CB_CONTINUE; | ||
575 | } | ||
576 | |||
577 | /* Walk on lines of blocks included in given DIE */ | ||
578 | static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, | ||
579 | line_walk_callback_t callback, void *data) | ||
580 | { | ||
581 | struct __line_walk_param lw = { | ||
582 | .recursive = recursive, | ||
583 | .callback = callback, | ||
584 | .data = data, | ||
585 | .retval = 0, | ||
586 | }; | ||
587 | Dwarf_Die die_mem; | ||
588 | Dwarf_Addr addr; | ||
589 | const char *fname; | ||
590 | int lineno; | ||
591 | |||
592 | /* Handle function declaration line */ | ||
593 | fname = dwarf_decl_file(sp_die); | ||
594 | if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
595 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
596 | lw.retval = callback(fname, lineno, addr, data); | ||
597 | if (lw.retval != 0) | ||
598 | goto done; | ||
599 | } | ||
600 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
601 | done: | ||
602 | return lw.retval; | ||
603 | } | ||
604 | |||
605 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
606 | { | ||
607 | struct __line_walk_param *lw = data; | ||
608 | |||
609 | lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); | ||
610 | if (lw->retval != 0) | ||
611 | return DWARF_CB_ABORT; | ||
612 | |||
613 | return DWARF_CB_OK; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * die_walk_lines - Walk on lines inside given DIE | ||
618 | * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) | ||
619 | * @callback: callback routine | ||
620 | * @data: user data | ||
621 | * | ||
622 | * Walk on all lines inside given @rt_die and call @callback on each line. | ||
623 | * If the @rt_die is a function, walk only on the lines inside the function, | ||
624 | * otherwise @rt_die must be a CU DIE. | ||
625 | * Note that this walks not only dwarf line list, but also function entries | ||
626 | * and inline call-site. | ||
627 | */ | ||
628 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) | ||
629 | { | ||
630 | Dwarf_Lines *lines; | ||
631 | Dwarf_Line *line; | ||
632 | Dwarf_Addr addr; | ||
633 | const char *fname; | ||
634 | int lineno, ret = 0; | ||
635 | Dwarf_Die die_mem, *cu_die; | ||
636 | size_t nlines, i; | ||
637 | |||
638 | /* Get the CU die */ | ||
639 | if (dwarf_tag(rt_die) != DW_TAG_compile_unit) | ||
640 | cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); | ||
641 | else | ||
642 | cu_die = rt_die; | ||
643 | if (!cu_die) { | ||
644 | pr_debug2("Failed to get CU from given DIE.\n"); | ||
645 | return -EINVAL; | ||
646 | } | ||
647 | |||
648 | /* Get lines list in the CU */ | ||
649 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
650 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
651 | return -ENOENT; | ||
652 | } | ||
653 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
654 | |||
655 | /* Walk on the lines on lines list */ | ||
656 | for (i = 0; i < nlines; i++) { | ||
657 | line = dwarf_onesrcline(lines, i); | ||
658 | if (line == NULL || | ||
659 | dwarf_lineno(line, &lineno) != 0 || | ||
660 | dwarf_lineaddr(line, &addr) != 0) { | ||
661 | pr_debug2("Failed to get line info. " | ||
662 | "Possible error in debuginfo.\n"); | ||
663 | continue; | ||
664 | } | ||
665 | /* Filter lines based on address */ | ||
666 | if (rt_die != cu_die) | ||
667 | /* | ||
668 | * Address filtering | ||
669 | * The line is included in given function, and | ||
670 | * no inline block includes it. | ||
671 | */ | ||
672 | if (!dwarf_haspc(rt_die, addr) || | ||
673 | die_find_inlinefunc(rt_die, addr, &die_mem)) | ||
674 | continue; | ||
675 | /* Get source line */ | ||
676 | fname = dwarf_linesrc(line, NULL, NULL); | ||
677 | |||
678 | ret = callback(fname, lineno, addr, data); | ||
679 | if (ret != 0) | ||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | /* | ||
684 | * Dwarf lines doesn't include function declarations and inlined | ||
685 | * subroutines. We have to check functions list or given function. | ||
686 | */ | ||
687 | if (rt_die != cu_die) | ||
688 | /* | ||
689 | * Don't need walk functions recursively, because nested | ||
690 | * inlined functions don't have lines of the specified DIE. | ||
691 | */ | ||
692 | ret = __die_walk_funclines(rt_die, false, callback, data); | ||
693 | else { | ||
694 | struct __line_walk_param param = { | ||
695 | .callback = callback, | ||
696 | .data = data, | ||
697 | .retval = 0, | ||
698 | }; | ||
699 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
700 | ret = param.retval; | ||
701 | } | ||
702 | |||
703 | return ret; | ||
704 | } | ||
705 | |||
706 | struct __find_variable_param { | ||
707 | const char *name; | ||
708 | Dwarf_Addr addr; | ||
709 | }; | ||
710 | |||
711 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
712 | { | ||
713 | struct __find_variable_param *fvp = data; | ||
714 | int tag; | ||
715 | |||
716 | tag = dwarf_tag(die_mem); | ||
717 | if ((tag == DW_TAG_formal_parameter || | ||
718 | tag == DW_TAG_variable) && | ||
719 | die_compare_name(die_mem, fvp->name)) | ||
720 | return DIE_FIND_CB_END; | ||
721 | |||
722 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
723 | return DIE_FIND_CB_CONTINUE; | ||
724 | else | ||
725 | return DIE_FIND_CB_SIBLING; | ||
726 | } | ||
727 | |||
728 | /** | ||
729 | * die_find_variable_at - Find a given name variable at given address | ||
730 | * @sp_die: a function DIE | ||
731 | * @name: variable name | ||
732 | * @addr: address | ||
733 | * @die_mem: a buffer for result DIE | ||
734 | * | ||
735 | * Find a variable DIE called @name at @addr in @sp_die. | ||
736 | */ | ||
737 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
738 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
739 | { | ||
740 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
741 | |||
742 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
743 | die_mem); | ||
744 | } | ||
745 | |||
746 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
747 | { | ||
748 | const char *name = data; | ||
749 | |||
750 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
751 | die_compare_name(die_mem, name)) | ||
752 | return DIE_FIND_CB_END; | ||
753 | |||
754 | return DIE_FIND_CB_SIBLING; | ||
755 | } | ||
756 | |||
757 | /** | ||
758 | * die_find_member - Find a given name member in a data structure | ||
759 | * @st_die: a data structure type DIE | ||
760 | * @name: member name | ||
761 | * @die_mem: a buffer for result DIE | ||
762 | * | ||
763 | * Find a member DIE called @name in @st_die. | ||
764 | */ | ||
765 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
766 | Dwarf_Die *die_mem) | ||
767 | { | ||
768 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
769 | die_mem); | ||
770 | } | ||
771 | |||
772 | /** | ||
773 | * die_get_typename - Get the name of given variable DIE | ||
774 | * @vr_die: a variable DIE | ||
775 | * @buf: a buffer for result type name | ||
776 | * @len: a max-length of @buf | ||
777 | * | ||
778 | * Get the name of @vr_die and stores it to @buf. Return the actual length | ||
779 | * of type name if succeeded. Return -E2BIG if @len is not enough long, and | ||
780 | * Return -ENOENT if failed to find type name. | ||
781 | * Note that the result will stores typedef name if possible, and stores | ||
782 | * "*(function_type)" if the type is a function pointer. | ||
783 | */ | ||
784 | int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
785 | { | ||
786 | Dwarf_Die type; | ||
787 | int tag, ret, ret2; | ||
788 | const char *tmp = ""; | ||
789 | |||
790 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
791 | return -ENOENT; | ||
792 | |||
793 | tag = dwarf_tag(&type); | ||
794 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
795 | tmp = "*"; | ||
796 | else if (tag == DW_TAG_subroutine_type) { | ||
797 | /* Function pointer */ | ||
798 | ret = snprintf(buf, len, "(function_type)"); | ||
799 | return (ret >= len) ? -E2BIG : ret; | ||
800 | } else { | ||
801 | if (!dwarf_diename(&type)) | ||
802 | return -ENOENT; | ||
803 | if (tag == DW_TAG_union_type) | ||
804 | tmp = "union "; | ||
805 | else if (tag == DW_TAG_structure_type) | ||
806 | tmp = "struct "; | ||
807 | /* Write a base name */ | ||
808 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
809 | return (ret >= len) ? -E2BIG : ret; | ||
810 | } | ||
811 | ret = die_get_typename(&type, buf, len); | ||
812 | if (ret > 0) { | ||
813 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
814 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
815 | } | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * die_get_varname - Get the name and type of given variable DIE | ||
821 | * @vr_die: a variable DIE | ||
822 | * @buf: a buffer for type and variable name | ||
823 | * @len: the max-length of @buf | ||
824 | * | ||
825 | * Get the name and type of @vr_die and stores it in @buf as "type\tname". | ||
826 | */ | ||
827 | int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
828 | { | ||
829 | int ret, ret2; | ||
830 | |||
831 | ret = die_get_typename(vr_die, buf, len); | ||
832 | if (ret < 0) { | ||
833 | pr_debug("Failed to get type, make it unknown.\n"); | ||
834 | ret = snprintf(buf, len, "(unknown_type)"); | ||
835 | } | ||
836 | if (ret > 0) { | ||
837 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
838 | dwarf_diename(vr_die)); | ||
839 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
840 | } | ||
841 | return ret; | ||
842 | } | ||
843 | |||
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h new file mode 100644 index 00000000000..6ce1717784b --- /dev/null +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -0,0 +1,111 @@ | |||
1 | #ifndef _DWARF_AUX_H | ||
2 | #define _DWARF_AUX_H | ||
3 | /* | ||
4 | * dwarf-aux.h : libdw auxiliary interfaces | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <dwarf.h> | ||
23 | #include <elfutils/libdw.h> | ||
24 | #include <elfutils/libdwfl.h> | ||
25 | #include <elfutils/version.h> | ||
26 | |||
27 | /* Find the realpath of the target file */ | ||
28 | extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname); | ||
29 | |||
30 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
31 | extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | ||
32 | |||
33 | /* Get a line number and file name for given address */ | ||
34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
35 | const char **fname, int *lineno); | ||
36 | |||
37 | /* Walk on funcitons at given address */ | ||
38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
39 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
40 | |||
41 | /* Compare diename and tname */ | ||
42 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | ||
43 | |||
44 | /* Get callsite line number of inline-function instance */ | ||
45 | extern int die_get_call_lineno(Dwarf_Die *in_die); | ||
46 | |||
47 | /* Get callsite file name of inlined function instance */ | ||
48 | extern const char *die_get_call_file(Dwarf_Die *in_die); | ||
49 | |||
50 | /* Get type die */ | ||
51 | extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
52 | |||
53 | /* Get a type die, but skip qualifiers and typedef */ | ||
54 | extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
55 | |||
56 | /* Check whether the DIE is signed or not */ | ||
57 | extern bool die_is_signed_type(Dwarf_Die *tp_die); | ||
58 | |||
59 | /* Get data_member_location offset */ | ||
60 | extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs); | ||
61 | |||
62 | /* Return values for die_find_child() callbacks */ | ||
63 | enum { | ||
64 | DIE_FIND_CB_END = 0, /* End of Search */ | ||
65 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
66 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
67 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
68 | }; | ||
69 | |||
70 | /* Search child DIEs */ | ||
71 | extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
72 | int (*callback)(Dwarf_Die *, void *), | ||
73 | void *data, Dwarf_Die *die_mem); | ||
74 | |||
75 | /* Search a non-inlined function including given address */ | ||
76 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
77 | Dwarf_Die *die_mem); | ||
78 | |||
79 | /* Search an inlined function including given address */ | ||
80 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
81 | Dwarf_Die *die_mem); | ||
82 | |||
83 | /* Walk on the instances of given DIE */ | ||
84 | extern int die_walk_instances(Dwarf_Die *in_die, | ||
85 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
86 | |||
87 | /* Walker on lines (Note: line number will not be sorted) */ | ||
88 | typedef int (* line_walk_callback_t) (const char *fname, int lineno, | ||
89 | Dwarf_Addr addr, void *data); | ||
90 | |||
91 | /* | ||
92 | * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on | ||
93 | * the lines inside the subprogram, otherwise the DIE must be a CU DIE. | ||
94 | */ | ||
95 | extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, | ||
96 | void *data); | ||
97 | |||
98 | /* Find a variable called 'name' at given address */ | ||
99 | extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
100 | Dwarf_Addr addr, Dwarf_Die *die_mem); | ||
101 | |||
102 | /* Find a member called 'name' */ | ||
103 | extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
104 | Dwarf_Die *die_mem); | ||
105 | |||
106 | /* Get the name of given variable DIE */ | ||
107 | extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len); | ||
108 | |||
109 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | ||
110 | extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len); | ||
111 | #endif | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3c1b8a63210..437f8ca679a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -169,12 +169,17 @@ static int perf_event__synthesize_mmap_events(union perf_event *event, | |||
169 | continue; | 169 | continue; |
170 | pbf += n + 3; | 170 | pbf += n + 3; |
171 | if (*pbf == 'x') { /* vm_exec */ | 171 | if (*pbf == 'x') { /* vm_exec */ |
172 | char anonstr[] = "//anon\n"; | ||
172 | char *execname = strchr(bf, '/'); | 173 | char *execname = strchr(bf, '/'); |
173 | 174 | ||
174 | /* Catch VDSO */ | 175 | /* Catch VDSO */ |
175 | if (execname == NULL) | 176 | if (execname == NULL) |
176 | execname = strstr(bf, "[vdso]"); | 177 | execname = strstr(bf, "[vdso]"); |
177 | 178 | ||
179 | /* Catch anonymous mmaps */ | ||
180 | if ((execname == NULL) && !strstr(bf, "[")) | ||
181 | execname = anonstr; | ||
182 | |||
178 | if (execname == NULL) | 183 | if (execname == NULL) |
179 | continue; | 184 | continue; |
180 | 185 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1d7f66488a8..357a85b8524 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -186,6 +186,6 @@ const char *perf_event__name(unsigned int id); | |||
186 | 186 | ||
187 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 187 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
188 | int sample_size, bool sample_id_all, | 188 | int sample_size, bool sample_id_all, |
189 | struct perf_sample *sample); | 189 | struct perf_sample *sample, bool swapped); |
190 | 190 | ||
191 | #endif /* __PERF_RECORD_H */ | 191 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b021ea9265c..72e9f4886b6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -85,10 +85,45 @@ int perf_evlist__add_default(struct perf_evlist *evlist) | |||
85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | 85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); |
86 | 86 | ||
87 | if (evsel == NULL) | 87 | if (evsel == NULL) |
88 | return -ENOMEM; | 88 | goto error; |
89 | |||
90 | /* use strdup() because free(evsel) assumes name is allocated */ | ||
91 | evsel->name = strdup("cycles"); | ||
92 | if (!evsel->name) | ||
93 | goto error_free; | ||
89 | 94 | ||
90 | perf_evlist__add(evlist, evsel); | 95 | perf_evlist__add(evlist, evsel); |
91 | return 0; | 96 | return 0; |
97 | error_free: | ||
98 | perf_evsel__delete(evsel); | ||
99 | error: | ||
100 | return -ENOMEM; | ||
101 | } | ||
102 | |||
103 | void perf_evlist__disable(struct perf_evlist *evlist) | ||
104 | { | ||
105 | int cpu, thread; | ||
106 | struct perf_evsel *pos; | ||
107 | |||
108 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
109 | list_for_each_entry(pos, &evlist->entries, node) { | ||
110 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
111 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE); | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | void perf_evlist__enable(struct perf_evlist *evlist) | ||
117 | { | ||
118 | int cpu, thread; | ||
119 | struct perf_evsel *pos; | ||
120 | |||
121 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
122 | list_for_each_entry(pos, &evlist->entries, node) { | ||
123 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
124 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE); | ||
125 | } | ||
126 | } | ||
92 | } | 127 | } |
93 | 128 | ||
94 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 129 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index b2b862374f3..f3491500274 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -53,6 +53,9 @@ int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | |||
53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | 53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); |
54 | void perf_evlist__munmap(struct perf_evlist *evlist); | 54 | void perf_evlist__munmap(struct perf_evlist *evlist); |
55 | 55 | ||
56 | void perf_evlist__disable(struct perf_evlist *evlist); | ||
57 | void perf_evlist__enable(struct perf_evlist *evlist); | ||
58 | |||
56 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 59 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
57 | struct cpu_map *cpus, | 60 | struct cpu_map *cpus, |
58 | struct thread_map *threads) | 61 | struct thread_map *threads) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0239eb87b23..e389815078d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <byteswap.h> | ||
11 | #include "asm/bug.h" | ||
10 | #include "evsel.h" | 12 | #include "evsel.h" |
11 | #include "evlist.h" | 13 | #include "evlist.h" |
12 | #include "util.h" | 14 | #include "util.h" |
@@ -342,10 +344,20 @@ static bool sample_overlap(const union perf_event *event, | |||
342 | 344 | ||
343 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 345 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
344 | int sample_size, bool sample_id_all, | 346 | int sample_size, bool sample_id_all, |
345 | struct perf_sample *data) | 347 | struct perf_sample *data, bool swapped) |
346 | { | 348 | { |
347 | const u64 *array; | 349 | const u64 *array; |
348 | 350 | ||
351 | /* | ||
352 | * used for cross-endian analysis. See git commit 65014ab3 | ||
353 | * for why this goofiness is needed. | ||
354 | */ | ||
355 | union { | ||
356 | u64 val64; | ||
357 | u32 val32[2]; | ||
358 | } u; | ||
359 | |||
360 | |||
349 | data->cpu = data->pid = data->tid = -1; | 361 | data->cpu = data->pid = data->tid = -1; |
350 | data->stream_id = data->id = data->time = -1ULL; | 362 | data->stream_id = data->id = data->time = -1ULL; |
351 | 363 | ||
@@ -366,9 +378,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
366 | } | 378 | } |
367 | 379 | ||
368 | if (type & PERF_SAMPLE_TID) { | 380 | if (type & PERF_SAMPLE_TID) { |
369 | u32 *p = (u32 *)array; | 381 | u.val64 = *array; |
370 | data->pid = p[0]; | 382 | if (swapped) { |
371 | data->tid = p[1]; | 383 | /* undo swap of u64, then swap on individual u32s */ |
384 | u.val64 = bswap_64(u.val64); | ||
385 | u.val32[0] = bswap_32(u.val32[0]); | ||
386 | u.val32[1] = bswap_32(u.val32[1]); | ||
387 | } | ||
388 | |||
389 | data->pid = u.val32[0]; | ||
390 | data->tid = u.val32[1]; | ||
372 | array++; | 391 | array++; |
373 | } | 392 | } |
374 | 393 | ||
@@ -377,6 +396,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
377 | array++; | 396 | array++; |
378 | } | 397 | } |
379 | 398 | ||
399 | data->addr = 0; | ||
380 | if (type & PERF_SAMPLE_ADDR) { | 400 | if (type & PERF_SAMPLE_ADDR) { |
381 | data->addr = *array; | 401 | data->addr = *array; |
382 | array++; | 402 | array++; |
@@ -394,8 +414,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
394 | } | 414 | } |
395 | 415 | ||
396 | if (type & PERF_SAMPLE_CPU) { | 416 | if (type & PERF_SAMPLE_CPU) { |
397 | u32 *p = (u32 *)array; | 417 | |
398 | data->cpu = *p; | 418 | u.val64 = *array; |
419 | if (swapped) { | ||
420 | /* undo swap of u64, then swap on individual u32s */ | ||
421 | u.val64 = bswap_64(u.val64); | ||
422 | u.val32[0] = bswap_32(u.val32[0]); | ||
423 | } | ||
424 | |||
425 | data->cpu = u.val32[0]; | ||
399 | array++; | 426 | array++; |
400 | } | 427 | } |
401 | 428 | ||
@@ -422,18 +449,27 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
422 | } | 449 | } |
423 | 450 | ||
424 | if (type & PERF_SAMPLE_RAW) { | 451 | if (type & PERF_SAMPLE_RAW) { |
425 | u32 *p = (u32 *)array; | 452 | const u64 *pdata; |
453 | |||
454 | u.val64 = *array; | ||
455 | if (WARN_ONCE(swapped, | ||
456 | "Endianness of raw data not corrected!\n")) { | ||
457 | /* undo swap of u64, then swap on individual u32s */ | ||
458 | u.val64 = bswap_64(u.val64); | ||
459 | u.val32[0] = bswap_32(u.val32[0]); | ||
460 | u.val32[1] = bswap_32(u.val32[1]); | ||
461 | } | ||
426 | 462 | ||
427 | if (sample_overlap(event, array, sizeof(u32))) | 463 | if (sample_overlap(event, array, sizeof(u32))) |
428 | return -EFAULT; | 464 | return -EFAULT; |
429 | 465 | ||
430 | data->raw_size = *p; | 466 | data->raw_size = u.val32[0]; |
431 | p++; | 467 | pdata = (void *) array + sizeof(u32); |
432 | 468 | ||
433 | if (sample_overlap(event, p, data->raw_size)) | 469 | if (sample_overlap(event, pdata, data->raw_size)) |
434 | return -EFAULT; | 470 | return -EFAULT; |
435 | 471 | ||
436 | data->raw_data = p; | 472 | data->raw_data = (void *) pdata; |
437 | } | 473 | } |
438 | 474 | ||
439 | return 0; | 475 | return 0; |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 7e9366e4490..e9a31554e26 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -61,6 +61,7 @@ struct perf_evsel { | |||
61 | off_t id_offset; | 61 | off_t id_offset; |
62 | }; | 62 | }; |
63 | struct cgroup_sel *cgrp; | 63 | struct cgroup_sel *cgrp; |
64 | bool supported; | ||
64 | }; | 65 | }; |
65 | 66 | ||
66 | struct cpu_map; | 67 | struct cpu_map; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index afb0849fe53..b6c1ad123ca 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -189,8 +189,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
189 | const char *name, bool is_kallsyms) | 189 | const char *name, bool is_kallsyms) |
190 | { | 190 | { |
191 | const size_t size = PATH_MAX; | 191 | const size_t size = PATH_MAX; |
192 | char *realname, *filename = malloc(size), | 192 | char *realname, *filename = zalloc(size), |
193 | *linkname = malloc(size), *targetname; | 193 | *linkname = zalloc(size), *targetname; |
194 | int len, err = -1; | 194 | int len, err = -1; |
195 | 195 | ||
196 | if (is_kallsyms) { | 196 | if (is_kallsyms) { |
@@ -254,8 +254,8 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | |||
254 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | 254 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) |
255 | { | 255 | { |
256 | const size_t size = PATH_MAX; | 256 | const size_t size = PATH_MAX; |
257 | char *filename = malloc(size), | 257 | char *filename = zalloc(size), |
258 | *linkname = malloc(size); | 258 | *linkname = zalloc(size); |
259 | int err = -1; | 259 | int err = -1; |
260 | 260 | ||
261 | if (filename == NULL || linkname == NULL) | 261 | if (filename == NULL || linkname == NULL) |
@@ -726,7 +726,16 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | |||
726 | return -1; | 726 | return -1; |
727 | 727 | ||
728 | bev.header = old_bev.header; | 728 | bev.header = old_bev.header; |
729 | bev.pid = 0; | 729 | |
730 | /* | ||
731 | * As the pid is the missing value, we need to fill | ||
732 | * it properly. The header.misc value give us nice hint. | ||
733 | */ | ||
734 | bev.pid = HOST_KERNEL_ID; | ||
735 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
736 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
737 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
738 | |||
730 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | 739 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); |
731 | __event_process_build_id(&bev, filename, session); | 740 | __event_process_build_id(&bev, filename, session); |
732 | 741 | ||
@@ -877,9 +886,12 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
877 | struct perf_evsel *evsel; | 886 | struct perf_evsel *evsel; |
878 | off_t tmp; | 887 | off_t tmp; |
879 | 888 | ||
880 | if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) | 889 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) |
881 | goto out_errno; | 890 | goto out_errno; |
882 | 891 | ||
892 | if (header->needs_swap) | ||
893 | perf_event__attr_swap(&f_attr.attr); | ||
894 | |||
883 | tmp = lseek(fd, 0, SEEK_CUR); | 895 | tmp = lseek(fd, 0, SEEK_CUR); |
884 | evsel = perf_evsel__new(&f_attr.attr, i); | 896 | evsel = perf_evsel__new(&f_attr.attr, i); |
885 | 897 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 627a02e03c5..677e1da6bb3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -14,7 +14,8 @@ enum hist_filter { | |||
14 | 14 | ||
15 | struct callchain_param callchain_param = { | 15 | struct callchain_param callchain_param = { |
16 | .mode = CHAIN_GRAPH_REL, | 16 | .mode = CHAIN_GRAPH_REL, |
17 | .min_percent = 0.5 | 17 | .min_percent = 0.5, |
18 | .order = ORDER_CALLEE | ||
18 | }; | 19 | }; |
19 | 20 | ||
20 | u16 hists__col_len(struct hists *self, enum hist_column col) | 21 | u16 hists__col_len(struct hists *self, enum hist_column col) |
@@ -846,6 +847,9 @@ print_entries: | |||
846 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
847 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
848 | 849 | ||
850 | if (h->filtered) | ||
851 | continue; | ||
852 | |||
849 | if (show_displacement) { | 853 | if (show_displacement) { |
850 | if (h->pair != NULL) | 854 | if (h->pair != NULL) |
851 | displacement = ((long)h->pair->position - | 855 | displacement = ((long)h->pair->position - |
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 791f9dd27eb..547628e97f3 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -5,7 +5,9 @@ | |||
5 | #define __always_inline inline | 5 | #define __always_inline inline |
6 | #endif | 6 | #endif |
7 | #define __user | 7 | #define __user |
8 | #ifndef __attribute_const__ | ||
8 | #define __attribute_const__ | 9 | #define __attribute_const__ |
10 | #endif | ||
9 | 11 | ||
10 | #define __used __attribute__((__unused__)) | 12 | #define __used __attribute__((__unused__)) |
11 | 13 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 41982c373fa..928918b796b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -86,22 +86,24 @@ static const char *sw_event_names[PERF_COUNT_SW_MAX] = { | |||
86 | 86 | ||
87 | #define MAX_ALIASES 8 | 87 | #define MAX_ALIASES 8 |
88 | 88 | ||
89 | static const char *hw_cache[][MAX_ALIASES] = { | 89 | static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = { |
90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | 90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | 91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
92 | { "LLC", "L2" }, | 92 | { "LLC", "L2", }, |
93 | { "dTLB", "d-tlb", "Data-TLB", }, | 93 | { "dTLB", "d-tlb", "Data-TLB", }, |
94 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 94 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
95 | { "branch", "branches", "bpu", "btb", "bpc", }, | 95 | { "branch", "branches", "bpu", "btb", "bpc", }, |
96 | { "node", }, | ||
96 | }; | 97 | }; |
97 | 98 | ||
98 | static const char *hw_cache_op[][MAX_ALIASES] = { | 99 | static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = { |
99 | { "load", "loads", "read", }, | 100 | { "load", "loads", "read", }, |
100 | { "store", "stores", "write", }, | 101 | { "store", "stores", "write", }, |
101 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | 102 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, |
102 | }; | 103 | }; |
103 | 104 | ||
104 | static const char *hw_cache_result[][MAX_ALIASES] = { | 105 | static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] |
106 | [MAX_ALIASES] = { | ||
105 | { "refs", "Reference", "ops", "access", }, | 107 | { "refs", "Reference", "ops", "access", }, |
106 | { "misses", "miss", }, | 108 | { "misses", "miss", }, |
107 | }; | 109 | }; |
@@ -124,6 +126,7 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
124 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | 126 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), |
125 | [C(ITLB)] = (CACHE_READ), | 127 | [C(ITLB)] = (CACHE_READ), |
126 | [C(BPU)] = (CACHE_READ), | 128 | [C(BPU)] = (CACHE_READ), |
129 | [C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
127 | }; | 130 | }; |
128 | 131 | ||
129 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ | 132 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ |
@@ -393,7 +396,7 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
393 | PERF_COUNT_HW_CACHE_OP_MAX); | 396 | PERF_COUNT_HW_CACHE_OP_MAX); |
394 | if (cache_op >= 0) { | 397 | if (cache_op >= 0) { |
395 | if (!is_cache_op_valid(cache_type, cache_op)) | 398 | if (!is_cache_op_valid(cache_type, cache_op)) |
396 | return 0; | 399 | return EVT_FAILED; |
397 | continue; | 400 | continue; |
398 | } | 401 | } |
399 | } | 402 | } |
@@ -475,7 +478,7 @@ parse_single_tracepoint_event(char *sys_name, | |||
475 | /* sys + ':' + event + ':' + flags*/ | 478 | /* sys + ':' + event + ':' + flags*/ |
476 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
477 | static enum event_result | 480 | static enum event_result |
478 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | 481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, |
479 | const char *evt_exp, char *flags) | 482 | const char *evt_exp, char *flags) |
480 | { | 483 | { |
481 | char evt_path[MAXPATHLEN]; | 484 | char evt_path[MAXPATHLEN]; |
@@ -509,7 +512,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
509 | if (len < 0) | 512 | if (len < 0) |
510 | return EVT_FAILED; | 513 | return EVT_FAILED; |
511 | 514 | ||
512 | if (parse_events(opt, event_opt, 0)) | 515 | if (parse_events(evlist, event_opt, 0)) |
513 | return EVT_FAILED; | 516 | return EVT_FAILED; |
514 | } | 517 | } |
515 | 518 | ||
@@ -517,7 +520,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
517 | } | 520 | } |
518 | 521 | ||
519 | static enum event_result | 522 | static enum event_result |
520 | parse_tracepoint_event(const struct option *opt, const char **strp, | 523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, |
521 | struct perf_event_attr *attr) | 524 | struct perf_event_attr *attr) |
522 | { | 525 | { |
523 | const char *evt_name; | 526 | const char *evt_name; |
@@ -557,8 +560,8 @@ parse_tracepoint_event(const struct option *opt, const char **strp, | |||
557 | return EVT_FAILED; | 560 | return EVT_FAILED; |
558 | if (strpbrk(evt_name, "*?")) { | 561 | if (strpbrk(evt_name, "*?")) { |
559 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
560 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, | 563 | return parse_multiple_tracepoint_event(evlist, sys_name, |
561 | flags); | 564 | evt_name, flags); |
562 | } else { | 565 | } else { |
563 | return parse_single_tracepoint_event(sys_name, evt_name, | 566 | return parse_single_tracepoint_event(sys_name, evt_name, |
564 | evt_length, attr, strp); | 567 | evt_length, attr, strp); |
@@ -694,7 +697,11 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) | |||
694 | return EVT_FAILED; | 697 | return EVT_FAILED; |
695 | n = hex2u64(str + 1, &config); | 698 | n = hex2u64(str + 1, &config); |
696 | if (n > 0) { | 699 | if (n > 0) { |
697 | *strp = str + n + 1; | 700 | const char *end = str + n + 1; |
701 | if (*end != '\0' && *end != ',' && *end != ':') | ||
702 | return EVT_FAILED; | ||
703 | |||
704 | *strp = end; | ||
698 | attr->type = PERF_TYPE_RAW; | 705 | attr->type = PERF_TYPE_RAW; |
699 | attr->config = config; | 706 | attr->config = config; |
700 | return EVT_HANDLED; | 707 | return EVT_HANDLED; |
@@ -778,12 +785,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
778 | * Symbolic names are (almost) exactly matched. | 785 | * Symbolic names are (almost) exactly matched. |
779 | */ | 786 | */ |
780 | static enum event_result | 787 | static enum event_result |
781 | parse_event_symbols(const struct option *opt, const char **str, | 788 | parse_event_symbols(struct perf_evlist *evlist, const char **str, |
782 | struct perf_event_attr *attr) | 789 | struct perf_event_attr *attr) |
783 | { | 790 | { |
784 | enum event_result ret; | 791 | enum event_result ret; |
785 | 792 | ||
786 | ret = parse_tracepoint_event(opt, str, attr); | 793 | ret = parse_tracepoint_event(evlist, str, attr); |
787 | if (ret != EVT_FAILED) | 794 | if (ret != EVT_FAILED) |
788 | goto modifier; | 795 | goto modifier; |
789 | 796 | ||
@@ -822,9 +829,8 @@ modifier: | |||
822 | return ret; | 829 | return ret; |
823 | } | 830 | } |
824 | 831 | ||
825 | int parse_events(const struct option *opt, const char *str, int unset __used) | 832 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) |
826 | { | 833 | { |
827 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
828 | struct perf_event_attr attr; | 834 | struct perf_event_attr attr; |
829 | enum event_result ret; | 835 | enum event_result ret; |
830 | const char *ostr; | 836 | const char *ostr; |
@@ -832,7 +838,7 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
832 | for (;;) { | 838 | for (;;) { |
833 | ostr = str; | 839 | ostr = str; |
834 | memset(&attr, 0, sizeof(attr)); | 840 | memset(&attr, 0, sizeof(attr)); |
835 | ret = parse_event_symbols(opt, &str, &attr); | 841 | ret = parse_event_symbols(evlist, &str, &attr); |
836 | if (ret == EVT_FAILED) | 842 | if (ret == EVT_FAILED) |
837 | return -1; | 843 | return -1; |
838 | 844 | ||
@@ -863,6 +869,13 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
863 | return 0; | 869 | return 0; |
864 | } | 870 | } |
865 | 871 | ||
872 | int parse_events_option(const struct option *opt, const char *str, | ||
873 | int unset __used) | ||
874 | { | ||
875 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
876 | return parse_events(evlist, str, unset); | ||
877 | } | ||
878 | |||
866 | int parse_filter(const struct option *opt, const char *str, | 879 | int parse_filter(const struct option *opt, const char *str, |
867 | int unset __used) | 880 | int unset __used) |
868 | { | 881 | { |
@@ -1088,6 +1101,4 @@ void print_events(const char *event_glob) | |||
1088 | printf("\n"); | 1101 | printf("\n"); |
1089 | 1102 | ||
1090 | print_tracepoint_events(NULL, NULL); | 1103 | print_tracepoint_events(NULL, NULL); |
1091 | |||
1092 | exit(129); | ||
1093 | } | 1104 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 746d3fcbfc2..2f8e375e038 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | struct list_head; | 9 | struct list_head; |
10 | struct perf_evsel; | 10 | struct perf_evsel; |
11 | struct perf_evlist; | ||
11 | 12 | ||
12 | struct option; | 13 | struct option; |
13 | 14 | ||
@@ -24,7 +25,10 @@ const char *event_type(int type); | |||
24 | const char *event_name(struct perf_evsel *event); | 25 | const char *event_name(struct perf_evsel *event); |
25 | extern const char *__event_name(int type, u64 config); | 26 | extern const char *__event_name(int type, u64 config); |
26 | 27 | ||
27 | extern int parse_events(const struct option *opt, const char *str, int unset); | 28 | extern int parse_events_option(const struct option *opt, const char *str, |
29 | int unset); | ||
30 | extern int parse_events(struct perf_evlist *evlist, const char *str, | ||
31 | int unset); | ||
28 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 32 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
29 | 33 | ||
30 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f0223166e76..eb25900e221 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module) | |||
117 | struct rb_node *nd; | 117 | struct rb_node *nd; |
118 | struct map_groups *grp = &machine.kmaps; | 118 | struct map_groups *grp = &machine.kmaps; |
119 | 119 | ||
120 | /* A file path -- this is an offline module */ | ||
121 | if (module && strchr(module, '/')) | ||
122 | return machine__new_module(&machine, 0, module); | ||
123 | |||
120 | if (!module) | 124 | if (!module) |
121 | module = "kernel"; | 125 | module = "kernel"; |
122 | 126 | ||
@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module) | |||
170 | } | 174 | } |
171 | 175 | ||
172 | #ifdef DWARF_SUPPORT | 176 | #ifdef DWARF_SUPPORT |
173 | static int open_vmlinux(const char *module) | 177 | /* Open new debuginfo of given module */ |
178 | static struct debuginfo *open_debuginfo(const char *module) | ||
174 | { | 179 | { |
175 | const char *path = kernel_get_module_path(module); | 180 | const char *path; |
176 | if (!path) { | 181 | |
177 | pr_err("Failed to find path of %s module.\n", | 182 | /* A file path -- this is an offline module */ |
178 | module ?: "kernel"); | 183 | if (module && strchr(module, '/')) |
179 | return -ENOENT; | 184 | path = module; |
185 | else { | ||
186 | path = kernel_get_module_path(module); | ||
187 | |||
188 | if (!path) { | ||
189 | pr_err("Failed to find path of %s module.\n", | ||
190 | module ?: "kernel"); | ||
191 | return NULL; | ||
192 | } | ||
180 | } | 193 | } |
181 | pr_debug("Try to open %s\n", path); | 194 | return debuginfo__new(path); |
182 | return open(path, O_RDONLY); | ||
183 | } | 195 | } |
184 | 196 | ||
185 | /* | 197 | /* |
@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
193 | struct map *map; | 205 | struct map *map; |
194 | u64 addr; | 206 | u64 addr; |
195 | int ret = -ENOENT; | 207 | int ret = -ENOENT; |
208 | struct debuginfo *dinfo; | ||
196 | 209 | ||
197 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 210 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
198 | if (sym) { | 211 | if (sym) { |
199 | addr = map->unmap_ip(map, sym->start + tp->offset); | 212 | addr = map->unmap_ip(map, sym->start + tp->offset); |
200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, | 213 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
201 | tp->offset, addr); | 214 | tp->offset, addr); |
202 | ret = find_perf_probe_point((unsigned long)addr, pp); | 215 | |
216 | dinfo = debuginfo__new_online_kernel(addr); | ||
217 | if (dinfo) { | ||
218 | ret = debuginfo__find_probe_point(dinfo, | ||
219 | (unsigned long)addr, pp); | ||
220 | debuginfo__delete(dinfo); | ||
221 | } else { | ||
222 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", | ||
223 | addr); | ||
224 | ret = -ENOENT; | ||
225 | } | ||
203 | } | 226 | } |
204 | if (ret <= 0) { | 227 | if (ret <= 0) { |
205 | pr_debug("Failed to find corresponding probes from " | 228 | pr_debug("Failed to find corresponding probes from " |
@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
214 | return 0; | 237 | return 0; |
215 | } | 238 | } |
216 | 239 | ||
240 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | ||
241 | int ntevs, const char *module) | ||
242 | { | ||
243 | int i, ret = 0; | ||
244 | char *tmp; | ||
245 | |||
246 | if (!module) | ||
247 | return 0; | ||
248 | |||
249 | tmp = strrchr(module, '/'); | ||
250 | if (tmp) { | ||
251 | /* This is a module path -- get the module name */ | ||
252 | module = strdup(tmp + 1); | ||
253 | if (!module) | ||
254 | return -ENOMEM; | ||
255 | tmp = strchr(module, '.'); | ||
256 | if (tmp) | ||
257 | *tmp = '\0'; | ||
258 | tmp = (char *)module; /* For free() */ | ||
259 | } | ||
260 | |||
261 | for (i = 0; i < ntevs; i++) { | ||
262 | tevs[i].point.module = strdup(module); | ||
263 | if (!tevs[i].point.module) { | ||
264 | ret = -ENOMEM; | ||
265 | break; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | if (tmp) | ||
270 | free(tmp); | ||
271 | |||
272 | return ret; | ||
273 | } | ||
274 | |||
217 | /* Try to find perf_probe_event with debuginfo */ | 275 | /* Try to find perf_probe_event with debuginfo */ |
218 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
219 | struct probe_trace_event **tevs, | 277 | struct probe_trace_event **tevs, |
220 | int max_tevs, const char *module) | 278 | int max_tevs, const char *module) |
221 | { | 279 | { |
222 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
223 | int fd, ntevs; | 281 | struct debuginfo *dinfo = open_debuginfo(module); |
282 | int ntevs, ret = 0; | ||
224 | 283 | ||
225 | fd = open_vmlinux(module); | 284 | if (!dinfo) { |
226 | if (fd < 0) { | ||
227 | if (need_dwarf) { | 285 | if (need_dwarf) { |
228 | pr_warning("Failed to open debuginfo file.\n"); | 286 | pr_warning("Failed to open debuginfo file.\n"); |
229 | return fd; | 287 | return -ENOENT; |
230 | } | 288 | } |
231 | pr_debug("Could not open vmlinux. Try to use symbols.\n"); | 289 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
232 | return 0; | 290 | return 0; |
233 | } | 291 | } |
234 | 292 | ||
235 | /* Searching trace events corresponding to probe event */ | 293 | /* Searching trace events corresponding to a probe event */ |
236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 294 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
295 | |||
296 | debuginfo__delete(dinfo); | ||
237 | 297 | ||
238 | if (ntevs > 0) { /* Succeeded to find trace events */ | 298 | if (ntevs > 0) { /* Succeeded to find trace events */ |
239 | pr_debug("find %d probe_trace_events.\n", ntevs); | 299 | pr_debug("find %d probe_trace_events.\n", ntevs); |
240 | return ntevs; | 300 | if (module) |
301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | ||
302 | module); | ||
303 | return ret < 0 ? ret : ntevs; | ||
241 | } | 304 | } |
242 | 305 | ||
243 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 306 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module) | |||
371 | { | 434 | { |
372 | int l = 1; | 435 | int l = 1; |
373 | struct line_node *ln; | 436 | struct line_node *ln; |
437 | struct debuginfo *dinfo; | ||
374 | FILE *fp; | 438 | FILE *fp; |
375 | int fd, ret; | 439 | int ret; |
376 | char *tmp; | 440 | char *tmp; |
377 | 441 | ||
378 | /* Search a line range */ | 442 | /* Search a line range */ |
@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module) | |||
380 | if (ret < 0) | 444 | if (ret < 0) |
381 | return ret; | 445 | return ret; |
382 | 446 | ||
383 | fd = open_vmlinux(module); | 447 | dinfo = open_debuginfo(module); |
384 | if (fd < 0) { | 448 | if (!dinfo) { |
385 | pr_warning("Failed to open debuginfo file.\n"); | 449 | pr_warning("Failed to open debuginfo file.\n"); |
386 | return fd; | 450 | return -ENOENT; |
387 | } | 451 | } |
388 | 452 | ||
389 | ret = find_line_range(fd, lr); | 453 | ret = debuginfo__find_line_range(dinfo, lr); |
454 | debuginfo__delete(dinfo); | ||
390 | if (ret == 0) { | 455 | if (ret == 0) { |
391 | pr_warning("Specified source line is not found.\n"); | 456 | pr_warning("Specified source line is not found.\n"); |
392 | return -ENOENT; | 457 | return -ENOENT; |
@@ -448,7 +513,8 @@ end: | |||
448 | return ret; | 513 | return ret; |
449 | } | 514 | } |
450 | 515 | ||
451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 516 | static int show_available_vars_at(struct debuginfo *dinfo, |
517 | struct perf_probe_event *pev, | ||
452 | int max_vls, struct strfilter *_filter, | 518 | int max_vls, struct strfilter *_filter, |
453 | bool externs) | 519 | bool externs) |
454 | { | 520 | { |
@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
463 | return -EINVAL; | 529 | return -EINVAL; |
464 | pr_debug("Searching variables at %s\n", buf); | 530 | pr_debug("Searching variables at %s\n", buf); |
465 | 531 | ||
466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 532 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
533 | max_vls, externs); | ||
467 | if (ret <= 0) { | 534 | if (ret <= 0) { |
468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 535 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
469 | goto end; | 536 | goto end; |
@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
504 | int max_vls, const char *module, | 571 | int max_vls, const char *module, |
505 | struct strfilter *_filter, bool externs) | 572 | struct strfilter *_filter, bool externs) |
506 | { | 573 | { |
507 | int i, fd, ret = 0; | 574 | int i, ret = 0; |
575 | struct debuginfo *dinfo; | ||
508 | 576 | ||
509 | ret = init_vmlinux(); | 577 | ret = init_vmlinux(); |
510 | if (ret < 0) | 578 | if (ret < 0) |
511 | return ret; | 579 | return ret; |
512 | 580 | ||
581 | dinfo = open_debuginfo(module); | ||
582 | if (!dinfo) { | ||
583 | pr_warning("Failed to open debuginfo file.\n"); | ||
584 | return -ENOENT; | ||
585 | } | ||
586 | |||
513 | setup_pager(); | 587 | setup_pager(); |
514 | 588 | ||
515 | for (i = 0; i < npevs && ret >= 0; i++) { | 589 | for (i = 0; i < npevs && ret >= 0; i++) |
516 | fd = open_vmlinux(module); | 590 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
517 | if (fd < 0) { | ||
518 | pr_warning("Failed to open debug information file.\n"); | ||
519 | ret = fd; | ||
520 | break; | ||
521 | } | ||
522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
523 | externs); | 591 | externs); |
524 | } | 592 | |
593 | debuginfo__delete(dinfo); | ||
525 | return ret; | 594 | return ret; |
526 | } | 595 | } |
527 | 596 | ||
@@ -990,7 +1059,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | |||
990 | 1059 | ||
991 | /* Parse probe_events event into struct probe_point */ | 1060 | /* Parse probe_events event into struct probe_point */ |
992 | static int parse_probe_trace_command(const char *cmd, | 1061 | static int parse_probe_trace_command(const char *cmd, |
993 | struct probe_trace_event *tev) | 1062 | struct probe_trace_event *tev) |
994 | { | 1063 | { |
995 | struct probe_trace_point *tp = &tev->point; | 1064 | struct probe_trace_point *tp = &tev->point; |
996 | char pr; | 1065 | char pr; |
@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd, | |||
1023 | 1092 | ||
1024 | tp->retprobe = (pr == 'r'); | 1093 | tp->retprobe = (pr == 'r'); |
1025 | 1094 | ||
1026 | /* Scan function name and offset */ | 1095 | /* Scan module name(if there), function name and offset */ |
1027 | ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, | 1096 | p = strchr(argv[1], ':'); |
1097 | if (p) { | ||
1098 | tp->module = strndup(argv[1], p - argv[1]); | ||
1099 | p++; | ||
1100 | } else | ||
1101 | p = argv[1]; | ||
1102 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, | ||
1028 | &tp->offset); | 1103 | &tp->offset); |
1029 | if (ret == 1) | 1104 | if (ret == 1) |
1030 | tp->offset = 0; | 1105 | tp->offset = 0; |
@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) | |||
1269 | if (buf == NULL) | 1344 | if (buf == NULL) |
1270 | return NULL; | 1345 | return NULL; |
1271 | 1346 | ||
1272 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", | 1347 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", |
1273 | tp->retprobe ? 'r' : 'p', | 1348 | tp->retprobe ? 'r' : 'p', |
1274 | tev->group, tev->event, | 1349 | tev->group, tev->event, |
1350 | tp->module ?: "", tp->module ? ":" : "", | ||
1275 | tp->symbol, tp->offset); | 1351 | tp->symbol, tp->offset); |
1276 | if (len <= 0) | 1352 | if (len <= 0) |
1277 | goto error; | 1353 | goto error; |
@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
1378 | free(tev->group); | 1454 | free(tev->group); |
1379 | if (tev->point.symbol) | 1455 | if (tev->point.symbol) |
1380 | free(tev->point.symbol); | 1456 | free(tev->point.symbol); |
1457 | if (tev->point.module) | ||
1458 | free(tev->point.module); | ||
1381 | for (i = 0; i < tev->nargs; i++) { | 1459 | for (i = 0; i < tev->nargs; i++) { |
1382 | if (tev->args[i].name) | 1460 | if (tev->args[i].name) |
1383 | free(tev->args[i].name); | 1461 | free(tev->args[i].name); |
@@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1729 | /* Convert perf_probe_event with debuginfo */ | 1807 | /* Convert perf_probe_event with debuginfo */ |
1730 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
1731 | if (ret != 0) | 1809 | if (ret != 0) |
1732 | return ret; | 1810 | return ret; /* Found in debuginfo or got an error */ |
1733 | 1811 | ||
1734 | /* Allocate trace event buffer */ | 1812 | /* Allocate trace event buffer */ |
1735 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); | 1813 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
@@ -1742,6 +1820,15 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1742 | ret = -ENOMEM; | 1820 | ret = -ENOMEM; |
1743 | goto error; | 1821 | goto error; |
1744 | } | 1822 | } |
1823 | |||
1824 | if (module) { | ||
1825 | tev->point.module = strdup(module); | ||
1826 | if (tev->point.module == NULL) { | ||
1827 | ret = -ENOMEM; | ||
1828 | goto error; | ||
1829 | } | ||
1830 | } | ||
1831 | |||
1745 | tev->point.offset = pev->point.offset; | 1832 | tev->point.offset = pev->point.offset; |
1746 | tev->point.retprobe = pev->point.retprobe; | 1833 | tev->point.retprobe = pev->point.retprobe; |
1747 | tev->nargs = pev->nargs; | 1834 | tev->nargs = pev->nargs; |
@@ -1869,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
1869 | 1956 | ||
1870 | pr_debug("Writing event: %s\n", buf); | 1957 | pr_debug("Writing event: %s\n", buf); |
1871 | ret = write(fd, buf, strlen(buf)); | 1958 | ret = write(fd, buf, strlen(buf)); |
1872 | if (ret < 0) | 1959 | if (ret < 0) { |
1960 | ret = -errno; | ||
1873 | goto error; | 1961 | goto error; |
1962 | } | ||
1874 | 1963 | ||
1875 | printf("Remove event: %s\n", ent->s); | 1964 | printf("Remove event: %s\n", ent->s); |
1876 | return 0; | 1965 | return 0; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 3434fc9d79d..a7dee835f49 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -10,6 +10,7 @@ extern bool probe_event_dry_run; | |||
10 | /* kprobe-tracer tracing point */ | 10 | /* kprobe-tracer tracing point */ |
11 | struct probe_trace_point { | 11 | struct probe_trace_point { |
12 | char *symbol; /* Base symbol */ | 12 | char *symbol; /* Base symbol */ |
13 | char *module; /* Module name */ | ||
13 | unsigned long offset; /* Offset from symbol */ | 14 | unsigned long offset; /* Offset from symbol */ |
14 | bool retprobe; /* Return probe flag */ | 15 | bool retprobe; /* Return probe flag */ |
15 | }; | 16 | }; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3b9d0b800d5..5d732621a46 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -43,21 +43,6 @@ | |||
43 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
44 | #define MAX_BASIC_TYPE_BITS 64 | 44 | #define MAX_BASIC_TYPE_BITS 64 |
45 | 45 | ||
46 | /* | ||
47 | * Compare the tail of two strings. | ||
48 | * Return 0 if whole of either string is same as another's tail part. | ||
49 | */ | ||
50 | static int strtailcmp(const char *s1, const char *s2) | ||
51 | { | ||
52 | int i1 = strlen(s1); | ||
53 | int i2 = strlen(s2); | ||
54 | while (--i1 >= 0 && --i2 >= 0) { | ||
55 | if (s1[i1] != s2[i2]) | ||
56 | return s1[i1] - s2[i2]; | ||
57 | } | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* Line number list operations */ | 46 | /* Line number list operations */ |
62 | 47 | ||
63 | /* Add a line to line number list */ | 48 | /* Add a line to line number list */ |
@@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
131 | }; | 116 | }; |
132 | 117 | ||
133 | /* Get a Dwarf from offline image */ | 118 | /* Get a Dwarf from offline image */ |
134 | static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) | 119 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
120 | const char *path) | ||
135 | { | 121 | { |
136 | Dwfl_Module *mod; | 122 | Dwfl_Module *mod; |
137 | Dwarf *dbg = NULL; | 123 | int fd; |
138 | 124 | ||
139 | if (!dwflp) | 125 | fd = open(path, O_RDONLY); |
140 | return NULL; | 126 | if (fd < 0) |
127 | return fd; | ||
141 | 128 | ||
142 | *dwflp = dwfl_begin(&offline_callbacks); | 129 | self->dwfl = dwfl_begin(&offline_callbacks); |
143 | if (!*dwflp) | 130 | if (!self->dwfl) |
144 | return NULL; | 131 | goto error; |
145 | 132 | ||
146 | mod = dwfl_report_offline(*dwflp, "", "", fd); | 133 | mod = dwfl_report_offline(self->dwfl, "", "", fd); |
147 | if (!mod) | 134 | if (!mod) |
148 | goto error; | 135 | goto error; |
149 | 136 | ||
150 | dbg = dwfl_module_getdwarf(mod, bias); | 137 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); |
151 | if (!dbg) { | 138 | if (!self->dbg) |
139 | goto error; | ||
140 | |||
141 | return 0; | ||
152 | error: | 142 | error: |
153 | dwfl_end(*dwflp); | 143 | if (self->dwfl) |
154 | *dwflp = NULL; | 144 | dwfl_end(self->dwfl); |
155 | } | 145 | else |
156 | return dbg; | 146 | close(fd); |
147 | memset(self, 0, sizeof(*self)); | ||
148 | |||
149 | return -ENOENT; | ||
157 | } | 150 | } |
158 | 151 | ||
159 | #if _ELFUTILS_PREREQ(0, 148) | 152 | #if _ELFUTILS_PREREQ(0, 148) |
@@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = { | |||
189 | }; | 182 | }; |
190 | 183 | ||
191 | /* Get a Dwarf from live kernel image */ | 184 | /* Get a Dwarf from live kernel image */ |
192 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, | 185 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
193 | Dwarf_Addr *bias) | 186 | Dwarf_Addr addr) |
194 | { | 187 | { |
195 | Dwarf *dbg; | 188 | self->dwfl = dwfl_begin(&kernel_callbacks); |
196 | 189 | if (!self->dwfl) | |
197 | if (!dwflp) | 190 | return -EINVAL; |
198 | return NULL; | ||
199 | |||
200 | *dwflp = dwfl_begin(&kernel_callbacks); | ||
201 | if (!*dwflp) | ||
202 | return NULL; | ||
203 | 191 | ||
204 | /* Load the kernel dwarves: Don't care the result here */ | 192 | /* Load the kernel dwarves: Don't care the result here */ |
205 | dwfl_linux_kernel_report_kernel(*dwflp); | 193 | dwfl_linux_kernel_report_kernel(self->dwfl); |
206 | dwfl_linux_kernel_report_modules(*dwflp); | 194 | dwfl_linux_kernel_report_modules(self->dwfl); |
207 | 195 | ||
208 | dbg = dwfl_addrdwarf(*dwflp, addr, bias); | 196 | self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); |
209 | /* Here, check whether we could get a real dwarf */ | 197 | /* Here, check whether we could get a real dwarf */ |
210 | if (!dbg) { | 198 | if (!self->dbg) { |
211 | pr_debug("Failed to find kernel dwarf at %lx\n", | 199 | pr_debug("Failed to find kernel dwarf at %lx\n", |
212 | (unsigned long)addr); | 200 | (unsigned long)addr); |
213 | dwfl_end(*dwflp); | 201 | dwfl_end(self->dwfl); |
214 | *dwflp = NULL; | 202 | memset(self, 0, sizeof(*self)); |
203 | return -ENOENT; | ||
215 | } | 204 | } |
216 | return dbg; | 205 | |
206 | return 0; | ||
217 | } | 207 | } |
218 | #else | 208 | #else |
219 | /* With older elfutils, this just support kernel module... */ | 209 | /* With older elfutils, this just support kernel module... */ |
220 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp, | 210 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
221 | Dwarf_Addr *bias) | 211 | Dwarf_Addr addr __used) |
222 | { | 212 | { |
223 | int fd; | ||
224 | const char *path = kernel_get_module_path("kernel"); | 213 | const char *path = kernel_get_module_path("kernel"); |
225 | 214 | ||
226 | if (!path) { | 215 | if (!path) { |
227 | pr_err("Failed to find vmlinux path\n"); | 216 | pr_err("Failed to find vmlinux path\n"); |
228 | return NULL; | 217 | return -ENOENT; |
229 | } | 218 | } |
230 | 219 | ||
231 | pr_debug2("Use file %s for debuginfo\n", path); | 220 | pr_debug2("Use file %s for debuginfo\n", path); |
232 | fd = open(path, O_RDONLY); | 221 | return debuginfo__init_offline_dwarf(self, path); |
233 | if (fd < 0) | ||
234 | return NULL; | ||
235 | |||
236 | return dwfl_init_offline_dwarf(fd, dwflp, bias); | ||
237 | } | 222 | } |
238 | #endif | 223 | #endif |
239 | 224 | ||
240 | /* Dwarf wrappers */ | 225 | struct debuginfo *debuginfo__new(const char *path) |
241 | |||
242 | /* Find the realpath of the target file. */ | ||
243 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
244 | { | 226 | { |
245 | Dwarf_Files *files; | 227 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
246 | size_t nfiles, i; | 228 | if (!self) |
247 | const char *src = NULL; | ||
248 | int ret; | ||
249 | |||
250 | if (!fname) | ||
251 | return NULL; | 229 | return NULL; |
252 | 230 | ||
253 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | 231 | if (debuginfo__init_offline_dwarf(self, path) < 0) { |
254 | if (ret != 0) | 232 | free(self); |
255 | return NULL; | 233 | self = NULL; |
256 | |||
257 | for (i = 0; i < nfiles; i++) { | ||
258 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
259 | if (strtailcmp(src, fname) == 0) | ||
260 | break; | ||
261 | } | 234 | } |
262 | if (i == nfiles) | ||
263 | return NULL; | ||
264 | return src; | ||
265 | } | ||
266 | 235 | ||
267 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | 236 | return self; |
268 | static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
269 | { | ||
270 | Dwarf_Attribute attr; | ||
271 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
272 | return NULL; | ||
273 | return dwarf_formstring(&attr); | ||
274 | } | 237 | } |
275 | 238 | ||
276 | /* Get a line number and file name for given address */ | 239 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) |
277 | static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
278 | const char **fname, int *lineno) | ||
279 | { | 240 | { |
280 | Dwarf_Line *line; | 241 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
281 | Dwarf_Addr laddr; | 242 | if (!self) |
282 | |||
283 | line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr); | ||
284 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
285 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
286 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
287 | if (!*fname) | ||
288 | /* line number is useless without filename */ | ||
289 | *lineno = 0; | ||
290 | } | ||
291 | |||
292 | return *lineno ?: -ENOENT; | ||
293 | } | ||
294 | |||
295 | /* Compare diename and tname */ | ||
296 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
297 | { | ||
298 | const char *name; | ||
299 | name = dwarf_diename(dw_die); | ||
300 | return name ? (strcmp(tname, name) == 0) : false; | ||
301 | } | ||
302 | |||
303 | /* Get callsite line number of inline-function instance */ | ||
304 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
305 | { | ||
306 | Dwarf_Attribute attr; | ||
307 | Dwarf_Word ret; | ||
308 | |||
309 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
310 | return -ENOENT; | ||
311 | |||
312 | dwarf_formudata(&attr, &ret); | ||
313 | return (int)ret; | ||
314 | } | ||
315 | |||
316 | /* Get type die */ | ||
317 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
318 | { | ||
319 | Dwarf_Attribute attr; | ||
320 | |||
321 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
322 | dwarf_formref_die(&attr, die_mem)) | ||
323 | return die_mem; | ||
324 | else | ||
325 | return NULL; | 243 | return NULL; |
326 | } | ||
327 | |||
328 | /* Get a type die, but skip qualifiers */ | ||
329 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
330 | { | ||
331 | int tag; | ||
332 | |||
333 | do { | ||
334 | vr_die = die_get_type(vr_die, die_mem); | ||
335 | if (!vr_die) | ||
336 | break; | ||
337 | tag = dwarf_tag(vr_die); | ||
338 | } while (tag == DW_TAG_const_type || | ||
339 | tag == DW_TAG_restrict_type || | ||
340 | tag == DW_TAG_volatile_type || | ||
341 | tag == DW_TAG_shared_type); | ||
342 | |||
343 | return vr_die; | ||
344 | } | ||
345 | |||
346 | /* Get a type die, but skip qualifiers and typedef */ | ||
347 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
348 | { | ||
349 | do { | ||
350 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
351 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
352 | |||
353 | return vr_die; | ||
354 | } | ||
355 | |||
356 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
357 | Dwarf_Word *result) | ||
358 | { | ||
359 | Dwarf_Attribute attr; | ||
360 | |||
361 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
362 | dwarf_formudata(&attr, result) != 0) | ||
363 | return -ENOENT; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | 244 | ||
368 | static bool die_is_signed_type(Dwarf_Die *tp_die) | 245 | if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { |
369 | { | 246 | free(self); |
370 | Dwarf_Word ret; | 247 | self = NULL; |
371 | |||
372 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
373 | return false; | ||
374 | |||
375 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
376 | ret == DW_ATE_signed_fixed); | ||
377 | } | ||
378 | |||
379 | static int die_get_byte_size(Dwarf_Die *tp_die) | ||
380 | { | ||
381 | Dwarf_Word ret; | ||
382 | |||
383 | if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) | ||
384 | return 0; | ||
385 | |||
386 | return (int)ret; | ||
387 | } | ||
388 | |||
389 | static int die_get_bit_size(Dwarf_Die *tp_die) | ||
390 | { | ||
391 | Dwarf_Word ret; | ||
392 | |||
393 | if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) | ||
394 | return 0; | ||
395 | |||
396 | return (int)ret; | ||
397 | } | ||
398 | |||
399 | static int die_get_bit_offset(Dwarf_Die *tp_die) | ||
400 | { | ||
401 | Dwarf_Word ret; | ||
402 | |||
403 | if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) | ||
404 | return 0; | ||
405 | |||
406 | return (int)ret; | ||
407 | } | ||
408 | |||
409 | /* Get data_member_location offset */ | ||
410 | static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
411 | { | ||
412 | Dwarf_Attribute attr; | ||
413 | Dwarf_Op *expr; | ||
414 | size_t nexpr; | ||
415 | int ret; | ||
416 | |||
417 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
418 | return -ENOENT; | ||
419 | |||
420 | if (dwarf_formudata(&attr, offs) != 0) { | ||
421 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
422 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
423 | if (ret < 0 || nexpr == 0) | ||
424 | return -ENOENT; | ||
425 | |||
426 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
427 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
428 | expr[0].atom, nexpr); | ||
429 | return -ENOTSUP; | ||
430 | } | ||
431 | *offs = (Dwarf_Word)expr[0].number; | ||
432 | } | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Return values for die_find callbacks */ | ||
437 | enum { | ||
438 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | ||
439 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
440 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
441 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
442 | }; | ||
443 | |||
444 | /* Search a child die */ | ||
445 | static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
446 | int (*callback)(Dwarf_Die *, void *), | ||
447 | void *data, Dwarf_Die *die_mem) | ||
448 | { | ||
449 | Dwarf_Die child_die; | ||
450 | int ret; | ||
451 | |||
452 | ret = dwarf_child(rt_die, die_mem); | ||
453 | if (ret != 0) | ||
454 | return NULL; | ||
455 | |||
456 | do { | ||
457 | ret = callback(die_mem, data); | ||
458 | if (ret == DIE_FIND_CB_FOUND) | ||
459 | return die_mem; | ||
460 | |||
461 | if ((ret & DIE_FIND_CB_CHILD) && | ||
462 | die_find_child(die_mem, callback, data, &child_die)) { | ||
463 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
464 | return die_mem; | ||
465 | } | ||
466 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
467 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
468 | |||
469 | return NULL; | ||
470 | } | ||
471 | |||
472 | struct __addr_die_search_param { | ||
473 | Dwarf_Addr addr; | ||
474 | Dwarf_Die *die_mem; | ||
475 | }; | ||
476 | |||
477 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
478 | { | ||
479 | struct __addr_die_search_param *ad = data; | ||
480 | |||
481 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
482 | dwarf_haspc(fn_die, ad->addr)) { | ||
483 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
484 | return DWARF_CB_ABORT; | ||
485 | } | 248 | } |
486 | return DWARF_CB_OK; | ||
487 | } | ||
488 | 249 | ||
489 | /* Search a real subprogram including this line, */ | 250 | return self; |
490 | static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
491 | Dwarf_Die *die_mem) | ||
492 | { | ||
493 | struct __addr_die_search_param ad; | ||
494 | ad.addr = addr; | ||
495 | ad.die_mem = die_mem; | ||
496 | /* dwarf_getscopes can't find subprogram. */ | ||
497 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
498 | return NULL; | ||
499 | else | ||
500 | return die_mem; | ||
501 | } | 251 | } |
502 | 252 | ||
503 | /* die_find callback for inline function search */ | 253 | void debuginfo__delete(struct debuginfo *self) |
504 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
505 | { | 254 | { |
506 | Dwarf_Addr *addr = data; | 255 | if (self) { |
507 | 256 | if (self->dwfl) | |
508 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | 257 | dwfl_end(self->dwfl); |
509 | dwarf_haspc(die_mem, *addr)) | 258 | free(self); |
510 | return DIE_FIND_CB_FOUND; | ||
511 | |||
512 | return DIE_FIND_CB_CONTINUE; | ||
513 | } | ||
514 | |||
515 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | ||
516 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
517 | Dwarf_Die *die_mem) | ||
518 | { | ||
519 | Dwarf_Die tmp_die; | ||
520 | |||
521 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
522 | if (!sp_die) | ||
523 | return NULL; | ||
524 | |||
525 | /* Inlined function could be recursive. Trace it until fail */ | ||
526 | while (sp_die) { | ||
527 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
528 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
529 | &tmp_die); | ||
530 | } | ||
531 | |||
532 | return die_mem; | ||
533 | } | ||
534 | |||
535 | /* Walker on lines (Note: line number will not be sorted) */ | ||
536 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
537 | Dwarf_Addr addr, void *data); | ||
538 | |||
539 | struct __line_walk_param { | ||
540 | const char *fname; | ||
541 | line_walk_handler_t handler; | ||
542 | void *data; | ||
543 | int retval; | ||
544 | }; | ||
545 | |||
546 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
547 | { | ||
548 | struct __line_walk_param *lw = data; | ||
549 | Dwarf_Addr addr; | ||
550 | int lineno; | ||
551 | |||
552 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
553 | lineno = die_get_call_lineno(in_die); | ||
554 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
555 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
556 | lw->data); | ||
557 | if (lw->retval != 0) | ||
558 | return DIE_FIND_CB_FOUND; | ||
559 | } | ||
560 | } | 259 | } |
561 | return DIE_FIND_CB_SIBLING; | ||
562 | } | ||
563 | |||
564 | /* Walk on lines of blocks included in given DIE */ | ||
565 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
566 | line_walk_handler_t handler, void *data) | ||
567 | { | ||
568 | struct __line_walk_param lw = { | ||
569 | .handler = handler, | ||
570 | .data = data, | ||
571 | .retval = 0, | ||
572 | }; | ||
573 | Dwarf_Die die_mem; | ||
574 | Dwarf_Addr addr; | ||
575 | int lineno; | ||
576 | |||
577 | /* Handle function declaration line */ | ||
578 | lw.fname = dwarf_decl_file(sp_die); | ||
579 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
580 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
581 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
582 | if (lw.retval != 0) | ||
583 | goto done; | ||
584 | } | ||
585 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
586 | done: | ||
587 | return lw.retval; | ||
588 | } | ||
589 | |||
590 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
591 | { | ||
592 | struct __line_walk_param *lw = data; | ||
593 | |||
594 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
595 | if (lw->retval != 0) | ||
596 | return DWARF_CB_ABORT; | ||
597 | |||
598 | return DWARF_CB_OK; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
603 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
604 | */ | ||
605 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
606 | void *data) | ||
607 | { | ||
608 | Dwarf_Lines *lines; | ||
609 | Dwarf_Line *line; | ||
610 | Dwarf_Addr addr; | ||
611 | const char *fname; | ||
612 | int lineno, ret = 0; | ||
613 | Dwarf_Die die_mem, *cu_die; | ||
614 | size_t nlines, i; | ||
615 | |||
616 | /* Get the CU die */ | ||
617 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
618 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
619 | else | ||
620 | cu_die = pdie; | ||
621 | if (!cu_die) { | ||
622 | pr_debug2("Failed to get CU from subprogram\n"); | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | |||
626 | /* Get lines list in the CU */ | ||
627 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
628 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
629 | return -ENOENT; | ||
630 | } | ||
631 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
632 | |||
633 | /* Walk on the lines on lines list */ | ||
634 | for (i = 0; i < nlines; i++) { | ||
635 | line = dwarf_onesrcline(lines, i); | ||
636 | if (line == NULL || | ||
637 | dwarf_lineno(line, &lineno) != 0 || | ||
638 | dwarf_lineaddr(line, &addr) != 0) { | ||
639 | pr_debug2("Failed to get line info. " | ||
640 | "Possible error in debuginfo.\n"); | ||
641 | continue; | ||
642 | } | ||
643 | /* Filter lines based on address */ | ||
644 | if (pdie != cu_die) | ||
645 | /* | ||
646 | * Address filtering | ||
647 | * The line is included in given function, and | ||
648 | * no inline block includes it. | ||
649 | */ | ||
650 | if (!dwarf_haspc(pdie, addr) || | ||
651 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
652 | continue; | ||
653 | /* Get source line */ | ||
654 | fname = dwarf_linesrc(line, NULL, NULL); | ||
655 | |||
656 | ret = handler(fname, lineno, addr, data); | ||
657 | if (ret != 0) | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * Dwarf lines doesn't include function declarations and inlined | ||
663 | * subroutines. We have to check functions list or given function. | ||
664 | */ | ||
665 | if (pdie != cu_die) | ||
666 | ret = __die_walk_funclines(pdie, handler, data); | ||
667 | else { | ||
668 | struct __line_walk_param param = { | ||
669 | .handler = handler, | ||
670 | .data = data, | ||
671 | .retval = 0, | ||
672 | }; | ||
673 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
674 | ret = param.retval; | ||
675 | } | ||
676 | |||
677 | return ret; | ||
678 | } | ||
679 | |||
680 | struct __find_variable_param { | ||
681 | const char *name; | ||
682 | Dwarf_Addr addr; | ||
683 | }; | ||
684 | |||
685 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
686 | { | ||
687 | struct __find_variable_param *fvp = data; | ||
688 | int tag; | ||
689 | |||
690 | tag = dwarf_tag(die_mem); | ||
691 | if ((tag == DW_TAG_formal_parameter || | ||
692 | tag == DW_TAG_variable) && | ||
693 | die_compare_name(die_mem, fvp->name)) | ||
694 | return DIE_FIND_CB_FOUND; | ||
695 | |||
696 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
697 | return DIE_FIND_CB_CONTINUE; | ||
698 | else | ||
699 | return DIE_FIND_CB_SIBLING; | ||
700 | } | ||
701 | |||
702 | /* Find a variable called 'name' at given address */ | ||
703 | static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
704 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
705 | { | ||
706 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
707 | |||
708 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
709 | die_mem); | ||
710 | } | ||
711 | |||
712 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
713 | { | ||
714 | const char *name = data; | ||
715 | |||
716 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
717 | die_compare_name(die_mem, name)) | ||
718 | return DIE_FIND_CB_FOUND; | ||
719 | |||
720 | return DIE_FIND_CB_SIBLING; | ||
721 | } | ||
722 | |||
723 | /* Find a member called 'name' */ | ||
724 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
725 | Dwarf_Die *die_mem) | ||
726 | { | ||
727 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
728 | die_mem); | ||
729 | } | ||
730 | |||
731 | /* Get the name of given variable DIE */ | ||
732 | static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
733 | { | ||
734 | Dwarf_Die type; | ||
735 | int tag, ret, ret2; | ||
736 | const char *tmp = ""; | ||
737 | |||
738 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
739 | return -ENOENT; | ||
740 | |||
741 | tag = dwarf_tag(&type); | ||
742 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
743 | tmp = "*"; | ||
744 | else if (tag == DW_TAG_subroutine_type) { | ||
745 | /* Function pointer */ | ||
746 | ret = snprintf(buf, len, "(function_type)"); | ||
747 | return (ret >= len) ? -E2BIG : ret; | ||
748 | } else { | ||
749 | if (!dwarf_diename(&type)) | ||
750 | return -ENOENT; | ||
751 | if (tag == DW_TAG_union_type) | ||
752 | tmp = "union "; | ||
753 | else if (tag == DW_TAG_structure_type) | ||
754 | tmp = "struct "; | ||
755 | /* Write a base name */ | ||
756 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
757 | return (ret >= len) ? -E2BIG : ret; | ||
758 | } | ||
759 | ret = die_get_typename(&type, buf, len); | ||
760 | if (ret > 0) { | ||
761 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
762 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
763 | } | ||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | ||
768 | static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
769 | { | ||
770 | int ret, ret2; | ||
771 | |||
772 | ret = die_get_typename(vr_die, buf, len); | ||
773 | if (ret < 0) { | ||
774 | pr_debug("Failed to get type, make it unknown.\n"); | ||
775 | ret = snprintf(buf, len, "(unknown_type)"); | ||
776 | } | ||
777 | if (ret > 0) { | ||
778 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
779 | dwarf_diename(vr_die)); | ||
780 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
781 | } | ||
782 | return ret; | ||
783 | } | 260 | } |
784 | 261 | ||
785 | /* | 262 | /* |
@@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
897 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 374 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
898 | Dwarf_Die type; | 375 | Dwarf_Die type; |
899 | char buf[16]; | 376 | char buf[16]; |
377 | int bsize, boffs, total; | ||
900 | int ret; | 378 | int ret; |
901 | 379 | ||
902 | /* TODO: check all types */ | 380 | /* TODO: check all types */ |
@@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
906 | return (tvar->type == NULL) ? -ENOMEM : 0; | 384 | return (tvar->type == NULL) ? -ENOMEM : 0; |
907 | } | 385 | } |
908 | 386 | ||
909 | if (die_get_bit_size(vr_die) != 0) { | 387 | bsize = dwarf_bitsize(vr_die); |
388 | if (bsize > 0) { | ||
910 | /* This is a bitfield */ | 389 | /* This is a bitfield */ |
911 | ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), | 390 | boffs = dwarf_bitoffset(vr_die); |
912 | die_get_bit_offset(vr_die), | 391 | total = dwarf_bytesize(vr_die); |
913 | BYTES_TO_BITS(die_get_byte_size(vr_die))); | 392 | if (boffs < 0 || total < 0) |
393 | return -ENOENT; | ||
394 | ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, | ||
395 | BYTES_TO_BITS(total)); | ||
914 | goto formatted; | 396 | goto formatted; |
915 | } | 397 | } |
916 | 398 | ||
@@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
958 | return (tvar->type == NULL) ? -ENOMEM : 0; | 440 | return (tvar->type == NULL) ? -ENOMEM : 0; |
959 | } | 441 | } |
960 | 442 | ||
961 | ret = BYTES_TO_BITS(die_get_byte_size(&type)); | 443 | ret = dwarf_bytesize(&type); |
962 | if (!ret) | 444 | if (ret <= 0) |
963 | /* No size ... try to use default type */ | 445 | /* No size ... try to use default type */ |
964 | return 0; | 446 | return 0; |
447 | ret = BYTES_TO_BITS(ret); | ||
965 | 448 | ||
966 | /* Check the bitwidth */ | 449 | /* Check the bitwidth */ |
967 | if (ret > MAX_BASIC_TYPE_BITS) { | 450 | if (ret > MAX_BASIC_TYPE_BITS) { |
@@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
1025 | else | 508 | else |
1026 | *ref_ptr = ref; | 509 | *ref_ptr = ref; |
1027 | } | 510 | } |
1028 | ref->offset += die_get_byte_size(&type) * field->index; | 511 | ref->offset += dwarf_bytesize(&type) * field->index; |
1029 | if (!field->next) | 512 | if (!field->next) |
1030 | /* Save vr_die for converting types */ | 513 | /* Save vr_die for converting types */ |
1031 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | 514 | memcpy(die_mem, vr_die, sizeof(*die_mem)); |
@@ -1129,12 +612,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
1129 | return ret; | 612 | return ret; |
1130 | } | 613 | } |
1131 | 614 | ||
1132 | /* Find a variable in a subprogram die */ | 615 | /* Find a variable in a scope DIE */ |
1133 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 616 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) |
1134 | { | 617 | { |
1135 | Dwarf_Die vr_die, *scopes; | 618 | Dwarf_Die vr_die; |
1136 | char buf[32], *ptr; | 619 | char buf[32], *ptr; |
1137 | int ret, nscopes; | 620 | int ret = 0; |
1138 | 621 | ||
1139 | if (!is_c_varname(pf->pvar->var)) { | 622 | if (!is_c_varname(pf->pvar->var)) { |
1140 | /* Copy raw parameters */ | 623 | /* Copy raw parameters */ |
@@ -1169,30 +652,16 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1169 | if (pf->tvar->name == NULL) | 652 | if (pf->tvar->name == NULL) |
1170 | return -ENOMEM; | 653 | return -ENOMEM; |
1171 | 654 | ||
1172 | pr_debug("Searching '%s' variable in context.\n", | 655 | pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); |
1173 | pf->pvar->var); | ||
1174 | /* Search child die for local variables and parameters. */ | 656 | /* Search child die for local variables and parameters. */ |
1175 | if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die)) | 657 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { |
1176 | ret = convert_variable(&vr_die, pf); | 658 | /* Search again in global variables */ |
1177 | else { | 659 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) |
1178 | /* Search upper class */ | 660 | ret = -ENOENT; |
1179 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | ||
1180 | while (nscopes-- > 1) { | ||
1181 | pr_debug("Searching variables in %s\n", | ||
1182 | dwarf_diename(&scopes[nscopes])); | ||
1183 | /* We should check this scope, so give dummy address */ | ||
1184 | if (die_find_variable_at(&scopes[nscopes], | ||
1185 | pf->pvar->var, 0, | ||
1186 | &vr_die)) { | ||
1187 | ret = convert_variable(&vr_die, pf); | ||
1188 | goto found; | ||
1189 | } | ||
1190 | } | ||
1191 | if (scopes) | ||
1192 | free(scopes); | ||
1193 | ret = -ENOENT; | ||
1194 | } | 661 | } |
1195 | found: | 662 | if (ret >= 0) |
663 | ret = convert_variable(&vr_die, pf); | ||
664 | |||
1196 | if (ret < 0) | 665 | if (ret < 0) |
1197 | pr_warning("Failed to find '%s' in this function.\n", | 666 | pr_warning("Failed to find '%s' in this function.\n", |
1198 | pf->pvar->var); | 667 | pf->pvar->var); |
@@ -1235,27 +704,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
1235 | return 0; | 704 | return 0; |
1236 | } | 705 | } |
1237 | 706 | ||
1238 | /* Call probe_finder callback with real subprogram DIE */ | 707 | /* Call probe_finder callback with scope DIE */ |
1239 | static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | 708 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) |
1240 | { | 709 | { |
1241 | Dwarf_Die die_mem; | ||
1242 | Dwarf_Attribute fb_attr; | 710 | Dwarf_Attribute fb_attr; |
1243 | size_t nops; | 711 | size_t nops; |
1244 | int ret; | 712 | int ret; |
1245 | 713 | ||
1246 | /* If no real subprogram, find a real one */ | 714 | if (!sc_die) { |
1247 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 715 | pr_err("Caller must pass a scope DIE. Program error.\n"); |
1248 | sp_die = die_find_real_subprogram(&pf->cu_die, | 716 | return -EINVAL; |
1249 | pf->addr, &die_mem); | 717 | } |
1250 | if (!sp_die) { | 718 | |
719 | /* If not a real subprogram, find a real one */ | ||
720 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | ||
721 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | ||
1251 | pr_warning("Failed to find probe point in any " | 722 | pr_warning("Failed to find probe point in any " |
1252 | "functions.\n"); | 723 | "functions.\n"); |
1253 | return -ENOENT; | 724 | return -ENOENT; |
1254 | } | 725 | } |
1255 | } | 726 | } else |
727 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); | ||
1256 | 728 | ||
1257 | /* Get the frame base attribute/ops */ | 729 | /* Get the frame base attribute/ops from subprogram */ |
1258 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 730 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); |
1259 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 731 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
1260 | if (ret <= 0 || nops == 0) { | 732 | if (ret <= 0 || nops == 0) { |
1261 | pf->fb_ops = NULL; | 733 | pf->fb_ops = NULL; |
@@ -1273,7 +745,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1273 | } | 745 | } |
1274 | 746 | ||
1275 | /* Call finder's callback handler */ | 747 | /* Call finder's callback handler */ |
1276 | ret = pf->callback(sp_die, pf); | 748 | ret = pf->callback(sc_die, pf); |
1277 | 749 | ||
1278 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 750 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
1279 | pf->fb_ops = NULL; | 751 | pf->fb_ops = NULL; |
@@ -1281,17 +753,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1281 | return ret; | 753 | return ret; |
1282 | } | 754 | } |
1283 | 755 | ||
756 | struct find_scope_param { | ||
757 | const char *function; | ||
758 | const char *file; | ||
759 | int line; | ||
760 | int diff; | ||
761 | Dwarf_Die *die_mem; | ||
762 | bool found; | ||
763 | }; | ||
764 | |||
765 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) | ||
766 | { | ||
767 | struct find_scope_param *fsp = data; | ||
768 | const char *file; | ||
769 | int lno; | ||
770 | |||
771 | /* Skip if declared file name does not match */ | ||
772 | if (fsp->file) { | ||
773 | file = dwarf_decl_file(fn_die); | ||
774 | if (!file || strcmp(fsp->file, file) != 0) | ||
775 | return 0; | ||
776 | } | ||
777 | /* If the function name is given, that's what user expects */ | ||
778 | if (fsp->function) { | ||
779 | if (die_compare_name(fn_die, fsp->function)) { | ||
780 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
781 | fsp->found = true; | ||
782 | return 1; | ||
783 | } | ||
784 | } else { | ||
785 | /* With the line number, find the nearest declared DIE */ | ||
786 | dwarf_decl_line(fn_die, &lno); | ||
787 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { | ||
788 | /* Keep a candidate and continue */ | ||
789 | fsp->diff = fsp->line - lno; | ||
790 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
791 | fsp->found = true; | ||
792 | } | ||
793 | } | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | /* Find an appropriate scope fits to given conditions */ | ||
798 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) | ||
799 | { | ||
800 | struct find_scope_param fsp = { | ||
801 | .function = pf->pev->point.function, | ||
802 | .file = pf->fname, | ||
803 | .line = pf->lno, | ||
804 | .diff = INT_MAX, | ||
805 | .die_mem = die_mem, | ||
806 | .found = false, | ||
807 | }; | ||
808 | |||
809 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); | ||
810 | |||
811 | return fsp.found ? die_mem : NULL; | ||
812 | } | ||
813 | |||
1284 | static int probe_point_line_walker(const char *fname, int lineno, | 814 | static int probe_point_line_walker(const char *fname, int lineno, |
1285 | Dwarf_Addr addr, void *data) | 815 | Dwarf_Addr addr, void *data) |
1286 | { | 816 | { |
1287 | struct probe_finder *pf = data; | 817 | struct probe_finder *pf = data; |
818 | Dwarf_Die *sc_die, die_mem; | ||
1288 | int ret; | 819 | int ret; |
1289 | 820 | ||
1290 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) | 821 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1291 | return 0; | 822 | return 0; |
1292 | 823 | ||
1293 | pf->addr = addr; | 824 | pf->addr = addr; |
1294 | ret = call_probe_finder(NULL, pf); | 825 | sc_die = find_best_scope(pf, &die_mem); |
826 | if (!sc_die) { | ||
827 | pr_warning("Failed to find scope of probe point.\n"); | ||
828 | return -ENOENT; | ||
829 | } | ||
830 | |||
831 | ret = call_probe_finder(sc_die, pf); | ||
1295 | 832 | ||
1296 | /* Continue if no error, because the line will be in inline function */ | 833 | /* Continue if no error, because the line will be in inline function */ |
1297 | return ret < 0 ? ret : 0; | 834 | return ret < 0 ? ret : 0; |
@@ -1345,6 +882,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
1345 | Dwarf_Addr addr, void *data) | 882 | Dwarf_Addr addr, void *data) |
1346 | { | 883 | { |
1347 | struct probe_finder *pf = data; | 884 | struct probe_finder *pf = data; |
885 | Dwarf_Die *sc_die, die_mem; | ||
1348 | int ret; | 886 | int ret; |
1349 | 887 | ||
1350 | if (!line_list__has_line(&pf->lcache, lineno) || | 888 | if (!line_list__has_line(&pf->lcache, lineno) || |
@@ -1354,7 +892,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
1354 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | 892 | pr_debug("Probe line found: line:%d addr:0x%llx\n", |
1355 | lineno, (unsigned long long)addr); | 893 | lineno, (unsigned long long)addr); |
1356 | pf->addr = addr; | 894 | pf->addr = addr; |
1357 | ret = call_probe_finder(NULL, pf); | 895 | pf->lno = lineno; |
896 | sc_die = find_best_scope(pf, &die_mem); | ||
897 | if (!sc_die) { | ||
898 | pr_warning("Failed to find scope of probe point.\n"); | ||
899 | return -ENOENT; | ||
900 | } | ||
901 | |||
902 | ret = call_probe_finder(sc_die, pf); | ||
1358 | 903 | ||
1359 | /* | 904 | /* |
1360 | * Continue if no error, because the lazy pattern will match | 905 | * Continue if no error, because the lazy pattern will match |
@@ -1379,42 +924,39 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1379 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); | 924 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
1380 | } | 925 | } |
1381 | 926 | ||
1382 | /* Callback parameter with return value */ | ||
1383 | struct dwarf_callback_param { | ||
1384 | void *data; | ||
1385 | int retval; | ||
1386 | }; | ||
1387 | |||
1388 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | 927 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) |
1389 | { | 928 | { |
1390 | struct dwarf_callback_param *param = data; | 929 | struct probe_finder *pf = data; |
1391 | struct probe_finder *pf = param->data; | ||
1392 | struct perf_probe_point *pp = &pf->pev->point; | 930 | struct perf_probe_point *pp = &pf->pev->point; |
1393 | Dwarf_Addr addr; | 931 | Dwarf_Addr addr; |
932 | int ret; | ||
1394 | 933 | ||
1395 | if (pp->lazy_line) | 934 | if (pp->lazy_line) |
1396 | param->retval = find_probe_point_lazy(in_die, pf); | 935 | ret = find_probe_point_lazy(in_die, pf); |
1397 | else { | 936 | else { |
1398 | /* Get probe address */ | 937 | /* Get probe address */ |
1399 | if (dwarf_entrypc(in_die, &addr) != 0) { | 938 | if (dwarf_entrypc(in_die, &addr) != 0) { |
1400 | pr_warning("Failed to get entry address of %s.\n", | 939 | pr_warning("Failed to get entry address of %s.\n", |
1401 | dwarf_diename(in_die)); | 940 | dwarf_diename(in_die)); |
1402 | param->retval = -ENOENT; | 941 | return -ENOENT; |
1403 | return DWARF_CB_ABORT; | ||
1404 | } | 942 | } |
1405 | pf->addr = addr; | 943 | pf->addr = addr; |
1406 | pf->addr += pp->offset; | 944 | pf->addr += pp->offset; |
1407 | pr_debug("found inline addr: 0x%jx\n", | 945 | pr_debug("found inline addr: 0x%jx\n", |
1408 | (uintmax_t)pf->addr); | 946 | (uintmax_t)pf->addr); |
1409 | 947 | ||
1410 | param->retval = call_probe_finder(in_die, pf); | 948 | ret = call_probe_finder(in_die, pf); |
1411 | if (param->retval < 0) | ||
1412 | return DWARF_CB_ABORT; | ||
1413 | } | 949 | } |
1414 | 950 | ||
1415 | return DWARF_CB_OK; | 951 | return ret; |
1416 | } | 952 | } |
1417 | 953 | ||
954 | /* Callback parameter with return value for libdw */ | ||
955 | struct dwarf_callback_param { | ||
956 | void *data; | ||
957 | int retval; | ||
958 | }; | ||
959 | |||
1418 | /* Search function from function name */ | 960 | /* Search function from function name */ |
1419 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | 961 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
1420 | { | 962 | { |
@@ -1451,14 +993,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
1451 | /* TODO: Check the address in this function */ | 993 | /* TODO: Check the address in this function */ |
1452 | param->retval = call_probe_finder(sp_die, pf); | 994 | param->retval = call_probe_finder(sp_die, pf); |
1453 | } | 995 | } |
1454 | } else { | 996 | } else |
1455 | struct dwarf_callback_param _param = {.data = (void *)pf, | ||
1456 | .retval = 0}; | ||
1457 | /* Inlined function: search instances */ | 997 | /* Inlined function: search instances */ |
1458 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, | 998 | param->retval = die_walk_instances(sp_die, |
1459 | &_param); | 999 | probe_point_inline_cb, (void *)pf); |
1460 | param->retval = _param.retval; | ||
1461 | } | ||
1462 | 1000 | ||
1463 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ | 1001 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ |
1464 | } | 1002 | } |
@@ -1504,28 +1042,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | |||
1504 | } | 1042 | } |
1505 | 1043 | ||
1506 | /* Find probe points from debuginfo */ | 1044 | /* Find probe points from debuginfo */ |
1507 | static int find_probes(int fd, struct probe_finder *pf) | 1045 | static int debuginfo__find_probes(struct debuginfo *self, |
1046 | struct probe_finder *pf) | ||
1508 | { | 1047 | { |
1509 | struct perf_probe_point *pp = &pf->pev->point; | 1048 | struct perf_probe_point *pp = &pf->pev->point; |
1510 | Dwarf_Off off, noff; | 1049 | Dwarf_Off off, noff; |
1511 | size_t cuhl; | 1050 | size_t cuhl; |
1512 | Dwarf_Die *diep; | 1051 | Dwarf_Die *diep; |
1513 | Dwarf *dbg = NULL; | ||
1514 | Dwfl *dwfl; | ||
1515 | Dwarf_Addr bias; /* Currently ignored */ | ||
1516 | int ret = 0; | 1052 | int ret = 0; |
1517 | 1053 | ||
1518 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
1519 | if (!dbg) { | ||
1520 | pr_warning("No debug information found in the vmlinux - " | ||
1521 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1522 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
1523 | return -EBADF; | ||
1524 | } | ||
1525 | |||
1526 | #if _ELFUTILS_PREREQ(0, 142) | 1054 | #if _ELFUTILS_PREREQ(0, 142) |
1527 | /* Get the call frame information from this dwarf */ | 1055 | /* Get the call frame information from this dwarf */ |
1528 | pf->cfi = dwarf_getcfi(dbg); | 1056 | pf->cfi = dwarf_getcfi(self->dbg); |
1529 | #endif | 1057 | #endif |
1530 | 1058 | ||
1531 | off = 0; | 1059 | off = 0; |
@@ -1544,7 +1072,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1544 | .data = pf, | 1072 | .data = pf, |
1545 | }; | 1073 | }; |
1546 | 1074 | ||
1547 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1075 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1076 | &pubname_param, 0); | ||
1548 | if (pubname_param.found) { | 1077 | if (pubname_param.found) { |
1549 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1078 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
1550 | if (ret) | 1079 | if (ret) |
@@ -1553,9 +1082,9 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1553 | } | 1082 | } |
1554 | 1083 | ||
1555 | /* Loop on CUs (Compilation Unit) */ | 1084 | /* Loop on CUs (Compilation Unit) */ |
1556 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1085 | while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1557 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1086 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1558 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1087 | diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); |
1559 | if (!diep) | 1088 | if (!diep) |
1560 | continue; | 1089 | continue; |
1561 | 1090 | ||
@@ -1582,14 +1111,12 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1582 | 1111 | ||
1583 | found: | 1112 | found: |
1584 | line_list__free(&pf->lcache); | 1113 | line_list__free(&pf->lcache); |
1585 | if (dwfl) | ||
1586 | dwfl_end(dwfl); | ||
1587 | 1114 | ||
1588 | return ret; | 1115 | return ret; |
1589 | } | 1116 | } |
1590 | 1117 | ||
1591 | /* Add a found probe point into trace event list */ | 1118 | /* Add a found probe point into trace event list */ |
1592 | static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | 1119 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
1593 | { | 1120 | { |
1594 | struct trace_event_finder *tf = | 1121 | struct trace_event_finder *tf = |
1595 | container_of(pf, struct trace_event_finder, pf); | 1122 | container_of(pf, struct trace_event_finder, pf); |
@@ -1604,8 +1131,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1604 | } | 1131 | } |
1605 | tev = &tf->tevs[tf->ntevs++]; | 1132 | tev = &tf->tevs[tf->ntevs++]; |
1606 | 1133 | ||
1607 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1134 | /* Trace point should be converted from subprogram DIE */ |
1608 | &tev->point); | 1135 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1136 | pf->pev->point.retprobe, &tev->point); | ||
1609 | if (ret < 0) | 1137 | if (ret < 0) |
1610 | return ret; | 1138 | return ret; |
1611 | 1139 | ||
@@ -1620,7 +1148,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1620 | for (i = 0; i < pf->pev->nargs; i++) { | 1148 | for (i = 0; i < pf->pev->nargs; i++) { |
1621 | pf->pvar = &pf->pev->args[i]; | 1149 | pf->pvar = &pf->pev->args[i]; |
1622 | pf->tvar = &tev->args[i]; | 1150 | pf->tvar = &tev->args[i]; |
1623 | ret = find_variable(sp_die, pf); | 1151 | /* Variable should be found from scope DIE */ |
1152 | ret = find_variable(sc_die, pf); | ||
1624 | if (ret != 0) | 1153 | if (ret != 0) |
1625 | return ret; | 1154 | return ret; |
1626 | } | 1155 | } |
@@ -1629,8 +1158,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1629 | } | 1158 | } |
1630 | 1159 | ||
1631 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1160 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1632 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 1161 | int debuginfo__find_trace_events(struct debuginfo *self, |
1633 | struct probe_trace_event **tevs, int max_tevs) | 1162 | struct perf_probe_event *pev, |
1163 | struct probe_trace_event **tevs, int max_tevs) | ||
1634 | { | 1164 | { |
1635 | struct trace_event_finder tf = { | 1165 | struct trace_event_finder tf = { |
1636 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1166 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
@@ -1645,7 +1175,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev, | |||
1645 | tf.tevs = *tevs; | 1175 | tf.tevs = *tevs; |
1646 | tf.ntevs = 0; | 1176 | tf.ntevs = 0; |
1647 | 1177 | ||
1648 | ret = find_probes(fd, &tf.pf); | 1178 | ret = debuginfo__find_probes(self, &tf.pf); |
1649 | if (ret < 0) { | 1179 | if (ret < 0) { |
1650 | free(*tevs); | 1180 | free(*tevs); |
1651 | *tevs = NULL; | 1181 | *tevs = NULL; |
@@ -1687,13 +1217,13 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
1687 | } | 1217 | } |
1688 | 1218 | ||
1689 | /* Add a found vars into available variables list */ | 1219 | /* Add a found vars into available variables list */ |
1690 | static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | 1220 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) |
1691 | { | 1221 | { |
1692 | struct available_var_finder *af = | 1222 | struct available_var_finder *af = |
1693 | container_of(pf, struct available_var_finder, pf); | 1223 | container_of(pf, struct available_var_finder, pf); |
1694 | struct variable_list *vl; | 1224 | struct variable_list *vl; |
1695 | Dwarf_Die die_mem, *scopes = NULL; | 1225 | Dwarf_Die die_mem; |
1696 | int ret, nscopes; | 1226 | int ret; |
1697 | 1227 | ||
1698 | /* Check number of tevs */ | 1228 | /* Check number of tevs */ |
1699 | if (af->nvls == af->max_vls) { | 1229 | if (af->nvls == af->max_vls) { |
@@ -1702,8 +1232,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1702 | } | 1232 | } |
1703 | vl = &af->vls[af->nvls++]; | 1233 | vl = &af->vls[af->nvls++]; |
1704 | 1234 | ||
1705 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1235 | /* Trace point should be converted from subprogram DIE */ |
1706 | &vl->point); | 1236 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1237 | pf->pev->point.retprobe, &vl->point); | ||
1707 | if (ret < 0) | 1238 | if (ret < 0) |
1708 | return ret; | 1239 | return ret; |
1709 | 1240 | ||
@@ -1715,19 +1246,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1715 | if (vl->vars == NULL) | 1246 | if (vl->vars == NULL) |
1716 | return -ENOMEM; | 1247 | return -ENOMEM; |
1717 | af->child = true; | 1248 | af->child = true; |
1718 | die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem); | 1249 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); |
1719 | 1250 | ||
1720 | /* Find external variables */ | 1251 | /* Find external variables */ |
1721 | if (!af->externs) | 1252 | if (!af->externs) |
1722 | goto out; | 1253 | goto out; |
1723 | /* Don't need to search child DIE for externs. */ | 1254 | /* Don't need to search child DIE for externs. */ |
1724 | af->child = false; | 1255 | af->child = false; |
1725 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 1256 | die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); |
1726 | while (nscopes-- > 1) | ||
1727 | die_find_child(&scopes[nscopes], collect_variables_cb, | ||
1728 | (void *)af, &die_mem); | ||
1729 | if (scopes) | ||
1730 | free(scopes); | ||
1731 | 1257 | ||
1732 | out: | 1258 | out: |
1733 | if (strlist__empty(vl->vars)) { | 1259 | if (strlist__empty(vl->vars)) { |
@@ -1739,9 +1265,10 @@ out: | |||
1739 | } | 1265 | } |
1740 | 1266 | ||
1741 | /* Find available variables at given probe point */ | 1267 | /* Find available variables at given probe point */ |
1742 | int find_available_vars_at(int fd, struct perf_probe_event *pev, | 1268 | int debuginfo__find_available_vars_at(struct debuginfo *self, |
1743 | struct variable_list **vls, int max_vls, | 1269 | struct perf_probe_event *pev, |
1744 | bool externs) | 1270 | struct variable_list **vls, |
1271 | int max_vls, bool externs) | ||
1745 | { | 1272 | { |
1746 | struct available_var_finder af = { | 1273 | struct available_var_finder af = { |
1747 | .pf = {.pev = pev, .callback = add_available_vars}, | 1274 | .pf = {.pev = pev, .callback = add_available_vars}, |
@@ -1756,7 +1283,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1756 | af.vls = *vls; | 1283 | af.vls = *vls; |
1757 | af.nvls = 0; | 1284 | af.nvls = 0; |
1758 | 1285 | ||
1759 | ret = find_probes(fd, &af.pf); | 1286 | ret = debuginfo__find_probes(self, &af.pf); |
1760 | if (ret < 0) { | 1287 | if (ret < 0) { |
1761 | /* Free vlist for error */ | 1288 | /* Free vlist for error */ |
1762 | while (af.nvls--) { | 1289 | while (af.nvls--) { |
@@ -1774,28 +1301,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1774 | } | 1301 | } |
1775 | 1302 | ||
1776 | /* Reverse search */ | 1303 | /* Reverse search */ |
1777 | int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | 1304 | int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, |
1305 | struct perf_probe_point *ppt) | ||
1778 | { | 1306 | { |
1779 | Dwarf_Die cudie, spdie, indie; | 1307 | Dwarf_Die cudie, spdie, indie; |
1780 | Dwarf *dbg = NULL; | 1308 | Dwarf_Addr _addr, baseaddr; |
1781 | Dwfl *dwfl = NULL; | ||
1782 | Dwarf_Addr _addr, baseaddr, bias = 0; | ||
1783 | const char *fname = NULL, *func = NULL, *tmp; | 1309 | const char *fname = NULL, *func = NULL, *tmp; |
1784 | int baseline = 0, lineno = 0, ret = 0; | 1310 | int baseline = 0, lineno = 0, ret = 0; |
1785 | 1311 | ||
1786 | /* Open the live linux kernel */ | ||
1787 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | ||
1788 | if (!dbg) { | ||
1789 | pr_warning("No debug information found in the vmlinux - " | ||
1790 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1791 | ret = -EINVAL; | ||
1792 | goto end; | ||
1793 | } | ||
1794 | |||
1795 | /* Adjust address with bias */ | 1312 | /* Adjust address with bias */ |
1796 | addr += bias; | 1313 | addr += self->bias; |
1314 | |||
1797 | /* Find cu die */ | 1315 | /* Find cu die */ |
1798 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { | 1316 | if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { |
1799 | pr_warning("Failed to find debug information for address %lx\n", | 1317 | pr_warning("Failed to find debug information for address %lx\n", |
1800 | addr); | 1318 | addr); |
1801 | ret = -EINVAL; | 1319 | ret = -EINVAL; |
@@ -1807,7 +1325,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1807 | /* Don't care whether it failed or not */ | 1325 | /* Don't care whether it failed or not */ |
1808 | 1326 | ||
1809 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1327 | /* Find a corresponding function (name, baseline and baseaddr) */ |
1810 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1328 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
1811 | /* Get function entry information */ | 1329 | /* Get function entry information */ |
1812 | tmp = dwarf_diename(&spdie); | 1330 | tmp = dwarf_diename(&spdie); |
1813 | if (!tmp || | 1331 | if (!tmp || |
@@ -1871,8 +1389,6 @@ post: | |||
1871 | } | 1389 | } |
1872 | } | 1390 | } |
1873 | end: | 1391 | end: |
1874 | if (dwfl) | ||
1875 | dwfl_end(dwfl); | ||
1876 | if (ret == 0 && (fname || func)) | 1392 | if (ret == 0 && (fname || func)) |
1877 | ret = 1; /* Found a point */ | 1393 | ret = 1; /* Found a point */ |
1878 | return ret; | 1394 | return ret; |
@@ -1929,10 +1445,14 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
1929 | 1445 | ||
1930 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | 1446 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) |
1931 | { | 1447 | { |
1932 | struct dwarf_callback_param *param = data; | 1448 | find_line_range_by_line(in_die, data); |
1933 | 1449 | ||
1934 | param->retval = find_line_range_by_line(in_die, param->data); | 1450 | /* |
1935 | return DWARF_CB_ABORT; /* No need to find other instances */ | 1451 | * We have to check all instances of inlined function, because |
1452 | * some execution paths can be optimized out depends on the | ||
1453 | * function argument of instances | ||
1454 | */ | ||
1455 | return 0; | ||
1936 | } | 1456 | } |
1937 | 1457 | ||
1938 | /* Search function from function name */ | 1458 | /* Search function from function name */ |
@@ -1960,15 +1480,10 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1960 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); | 1480 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); |
1961 | lr->start = lf->lno_s; | 1481 | lr->start = lf->lno_s; |
1962 | lr->end = lf->lno_e; | 1482 | lr->end = lf->lno_e; |
1963 | if (dwarf_func_inline(sp_die)) { | 1483 | if (dwarf_func_inline(sp_die)) |
1964 | struct dwarf_callback_param _param; | 1484 | param->retval = die_walk_instances(sp_die, |
1965 | _param.data = (void *)lf; | 1485 | line_range_inline_cb, lf); |
1966 | _param.retval = 0; | 1486 | else |
1967 | dwarf_func_inline_instances(sp_die, | ||
1968 | line_range_inline_cb, | ||
1969 | &_param); | ||
1970 | param->retval = _param.retval; | ||
1971 | } else | ||
1972 | param->retval = find_line_range_by_line(sp_die, lf); | 1487 | param->retval = find_line_range_by_line(sp_die, lf); |
1973 | return DWARF_CB_ABORT; | 1488 | return DWARF_CB_ABORT; |
1974 | } | 1489 | } |
@@ -1982,26 +1497,15 @@ static int find_line_range_by_func(struct line_finder *lf) | |||
1982 | return param.retval; | 1497 | return param.retval; |
1983 | } | 1498 | } |
1984 | 1499 | ||
1985 | int find_line_range(int fd, struct line_range *lr) | 1500 | int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) |
1986 | { | 1501 | { |
1987 | struct line_finder lf = {.lr = lr, .found = 0}; | 1502 | struct line_finder lf = {.lr = lr, .found = 0}; |
1988 | int ret = 0; | 1503 | int ret = 0; |
1989 | Dwarf_Off off = 0, noff; | 1504 | Dwarf_Off off = 0, noff; |
1990 | size_t cuhl; | 1505 | size_t cuhl; |
1991 | Dwarf_Die *diep; | 1506 | Dwarf_Die *diep; |
1992 | Dwarf *dbg = NULL; | ||
1993 | Dwfl *dwfl; | ||
1994 | Dwarf_Addr bias; /* Currently ignored */ | ||
1995 | const char *comp_dir; | 1507 | const char *comp_dir; |
1996 | 1508 | ||
1997 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
1998 | if (!dbg) { | ||
1999 | pr_warning("No debug information found in the vmlinux - " | ||
2000 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
2001 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
2002 | return -EBADF; | ||
2003 | } | ||
2004 | |||
2005 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 1509 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
2006 | if (lr->function) { | 1510 | if (lr->function) { |
2007 | struct pubname_callback_param pubname_param = { | 1511 | struct pubname_callback_param pubname_param = { |
@@ -2010,7 +1514,8 @@ int find_line_range(int fd, struct line_range *lr) | |||
2010 | struct dwarf_callback_param line_range_param = { | 1514 | struct dwarf_callback_param line_range_param = { |
2011 | .data = (void *)&lf, .retval = 0}; | 1515 | .data = (void *)&lf, .retval = 0}; |
2012 | 1516 | ||
2013 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1517 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1518 | &pubname_param, 0); | ||
2014 | if (pubname_param.found) { | 1519 | if (pubname_param.found) { |
2015 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1520 | line_range_search_cb(&lf.sp_die, &line_range_param); |
2016 | if (lf.found) | 1521 | if (lf.found) |
@@ -2020,11 +1525,12 @@ int find_line_range(int fd, struct line_range *lr) | |||
2020 | 1525 | ||
2021 | /* Loop on CUs (Compilation Unit) */ | 1526 | /* Loop on CUs (Compilation Unit) */ |
2022 | while (!lf.found && ret >= 0) { | 1527 | while (!lf.found && ret >= 0) { |
2023 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) | 1528 | if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, |
1529 | NULL, NULL, NULL) != 0) | ||
2024 | break; | 1530 | break; |
2025 | 1531 | ||
2026 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1532 | /* Get the DIE(Debugging Information Entry) of this CU */ |
2027 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | 1533 | diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); |
2028 | if (!diep) | 1534 | if (!diep) |
2029 | continue; | 1535 | continue; |
2030 | 1536 | ||
@@ -2058,7 +1564,6 @@ found: | |||
2058 | } | 1564 | } |
2059 | 1565 | ||
2060 | pr_debug("path: %s\n", lr->path); | 1566 | pr_debug("path: %s\n", lr->path); |
2061 | dwfl_end(dwfl); | ||
2062 | return (ret < 0) ? ret : lf.found; | 1567 | return (ret < 0) ? ret : lf.found; |
2063 | } | 1568 | } |
2064 | 1569 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 605730a366d..1132c8f0ce8 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -16,33 +16,48 @@ static inline int is_c_varname(const char *name) | |||
16 | } | 16 | } |
17 | 17 | ||
18 | #ifdef DWARF_SUPPORT | 18 | #ifdef DWARF_SUPPORT |
19 | |||
20 | #include "dwarf-aux.h" | ||
21 | |||
22 | /* TODO: export debuginfo data structure even if no dwarf support */ | ||
23 | |||
24 | /* debug information structure */ | ||
25 | struct debuginfo { | ||
26 | Dwarf *dbg; | ||
27 | Dwfl *dwfl; | ||
28 | Dwarf_Addr bias; | ||
29 | }; | ||
30 | |||
31 | extern struct debuginfo *debuginfo__new(const char *path); | ||
32 | extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); | ||
33 | extern void debuginfo__delete(struct debuginfo *self); | ||
34 | |||
19 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 35 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
20 | extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 36 | extern int debuginfo__find_trace_events(struct debuginfo *self, |
21 | struct probe_trace_event **tevs, | 37 | struct perf_probe_event *pev, |
22 | int max_tevs); | 38 | struct probe_trace_event **tevs, |
39 | int max_tevs); | ||
23 | 40 | ||
24 | /* Find a perf_probe_point from debuginfo */ | 41 | /* Find a perf_probe_point from debuginfo */ |
25 | extern int find_perf_probe_point(unsigned long addr, | 42 | extern int debuginfo__find_probe_point(struct debuginfo *self, |
26 | struct perf_probe_point *ppt); | 43 | unsigned long addr, |
44 | struct perf_probe_point *ppt); | ||
27 | 45 | ||
28 | /* Find a line range */ | 46 | /* Find a line range */ |
29 | extern int find_line_range(int fd, struct line_range *lr); | 47 | extern int debuginfo__find_line_range(struct debuginfo *self, |
48 | struct line_range *lr); | ||
30 | 49 | ||
31 | /* Find available variables */ | 50 | /* Find available variables */ |
32 | extern int find_available_vars_at(int fd, struct perf_probe_event *pev, | 51 | extern int debuginfo__find_available_vars_at(struct debuginfo *self, |
33 | struct variable_list **vls, int max_points, | 52 | struct perf_probe_event *pev, |
34 | bool externs); | 53 | struct variable_list **vls, |
35 | 54 | int max_points, bool externs); | |
36 | #include <dwarf.h> | ||
37 | #include <elfutils/libdw.h> | ||
38 | #include <elfutils/libdwfl.h> | ||
39 | #include <elfutils/version.h> | ||
40 | 55 | ||
41 | struct probe_finder { | 56 | struct probe_finder { |
42 | struct perf_probe_event *pev; /* Target probe event */ | 57 | struct perf_probe_event *pev; /* Target probe event */ |
43 | 58 | ||
44 | /* Callback when a probe point is found */ | 59 | /* Callback when a probe point is found */ |
45 | int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf); | 60 | int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf); |
46 | 61 | ||
47 | /* For function searching */ | 62 | /* For function searching */ |
48 | int lno; /* Line number */ | 63 | int lno; /* Line number */ |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a9ac0504aab..7624324efad 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -187,16 +187,119 @@ static PyTypeObject pyrf_throttle_event__type = { | |||
187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, | 187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, |
188 | }; | 188 | }; |
189 | 189 | ||
190 | static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object."); | ||
191 | |||
192 | static PyMemberDef pyrf_lost_event__members[] = { | ||
193 | sample_members | ||
194 | member_def(lost_event, id, T_ULONGLONG, "event id"), | ||
195 | member_def(lost_event, lost, T_ULONGLONG, "number of lost events"), | ||
196 | { .name = NULL, }, | ||
197 | }; | ||
198 | |||
199 | static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent) | ||
200 | { | ||
201 | PyObject *ret; | ||
202 | char *s; | ||
203 | |||
204 | if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", " | ||
205 | "lost: %#" PRIx64 " }", | ||
206 | pevent->event.lost.id, pevent->event.lost.lost) < 0) { | ||
207 | ret = PyErr_NoMemory(); | ||
208 | } else { | ||
209 | ret = PyString_FromString(s); | ||
210 | free(s); | ||
211 | } | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static PyTypeObject pyrf_lost_event__type = { | ||
216 | PyVarObject_HEAD_INIT(NULL, 0) | ||
217 | .tp_name = "perf.lost_event", | ||
218 | .tp_basicsize = sizeof(struct pyrf_event), | ||
219 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
220 | .tp_doc = pyrf_lost_event__doc, | ||
221 | .tp_members = pyrf_lost_event__members, | ||
222 | .tp_repr = (reprfunc)pyrf_lost_event__repr, | ||
223 | }; | ||
224 | |||
225 | static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object."); | ||
226 | |||
227 | static PyMemberDef pyrf_read_event__members[] = { | ||
228 | sample_members | ||
229 | member_def(read_event, pid, T_UINT, "event pid"), | ||
230 | member_def(read_event, tid, T_UINT, "event tid"), | ||
231 | { .name = NULL, }, | ||
232 | }; | ||
233 | |||
234 | static PyObject *pyrf_read_event__repr(struct pyrf_event *pevent) | ||
235 | { | ||
236 | return PyString_FromFormat("{ type: read, pid: %u, tid: %u }", | ||
237 | pevent->event.read.pid, | ||
238 | pevent->event.read.tid); | ||
239 | /* | ||
240 | * FIXME: return the array of read values, | ||
241 | * making this method useful ;-) | ||
242 | */ | ||
243 | } | ||
244 | |||
245 | static PyTypeObject pyrf_read_event__type = { | ||
246 | PyVarObject_HEAD_INIT(NULL, 0) | ||
247 | .tp_name = "perf.read_event", | ||
248 | .tp_basicsize = sizeof(struct pyrf_event), | ||
249 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
250 | .tp_doc = pyrf_read_event__doc, | ||
251 | .tp_members = pyrf_read_event__members, | ||
252 | .tp_repr = (reprfunc)pyrf_read_event__repr, | ||
253 | }; | ||
254 | |||
255 | static char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object."); | ||
256 | |||
257 | static PyMemberDef pyrf_sample_event__members[] = { | ||
258 | sample_members | ||
259 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
260 | { .name = NULL, }, | ||
261 | }; | ||
262 | |||
263 | static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent) | ||
264 | { | ||
265 | PyObject *ret; | ||
266 | char *s; | ||
267 | |||
268 | if (asprintf(&s, "{ type: sample }") < 0) { | ||
269 | ret = PyErr_NoMemory(); | ||
270 | } else { | ||
271 | ret = PyString_FromString(s); | ||
272 | free(s); | ||
273 | } | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static PyTypeObject pyrf_sample_event__type = { | ||
278 | PyVarObject_HEAD_INIT(NULL, 0) | ||
279 | .tp_name = "perf.sample_event", | ||
280 | .tp_basicsize = sizeof(struct pyrf_event), | ||
281 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
282 | .tp_doc = pyrf_sample_event__doc, | ||
283 | .tp_members = pyrf_sample_event__members, | ||
284 | .tp_repr = (reprfunc)pyrf_sample_event__repr, | ||
285 | }; | ||
286 | |||
190 | static int pyrf_event__setup_types(void) | 287 | static int pyrf_event__setup_types(void) |
191 | { | 288 | { |
192 | int err; | 289 | int err; |
193 | pyrf_mmap_event__type.tp_new = | 290 | pyrf_mmap_event__type.tp_new = |
194 | pyrf_task_event__type.tp_new = | 291 | pyrf_task_event__type.tp_new = |
195 | pyrf_comm_event__type.tp_new = | 292 | pyrf_comm_event__type.tp_new = |
293 | pyrf_lost_event__type.tp_new = | ||
294 | pyrf_read_event__type.tp_new = | ||
295 | pyrf_sample_event__type.tp_new = | ||
196 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; | 296 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; |
197 | err = PyType_Ready(&pyrf_mmap_event__type); | 297 | err = PyType_Ready(&pyrf_mmap_event__type); |
198 | if (err < 0) | 298 | if (err < 0) |
199 | goto out; | 299 | goto out; |
300 | err = PyType_Ready(&pyrf_lost_event__type); | ||
301 | if (err < 0) | ||
302 | goto out; | ||
200 | err = PyType_Ready(&pyrf_task_event__type); | 303 | err = PyType_Ready(&pyrf_task_event__type); |
201 | if (err < 0) | 304 | if (err < 0) |
202 | goto out; | 305 | goto out; |
@@ -206,20 +309,26 @@ static int pyrf_event__setup_types(void) | |||
206 | err = PyType_Ready(&pyrf_throttle_event__type); | 309 | err = PyType_Ready(&pyrf_throttle_event__type); |
207 | if (err < 0) | 310 | if (err < 0) |
208 | goto out; | 311 | goto out; |
312 | err = PyType_Ready(&pyrf_read_event__type); | ||
313 | if (err < 0) | ||
314 | goto out; | ||
315 | err = PyType_Ready(&pyrf_sample_event__type); | ||
316 | if (err < 0) | ||
317 | goto out; | ||
209 | out: | 318 | out: |
210 | return err; | 319 | return err; |
211 | } | 320 | } |
212 | 321 | ||
213 | static PyTypeObject *pyrf_event__type[] = { | 322 | static PyTypeObject *pyrf_event__type[] = { |
214 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, | 323 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, |
215 | [PERF_RECORD_LOST] = &pyrf_mmap_event__type, | 324 | [PERF_RECORD_LOST] = &pyrf_lost_event__type, |
216 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, | 325 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, |
217 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, | 326 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, |
218 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, | 327 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, |
219 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, | 328 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, |
220 | [PERF_RECORD_FORK] = &pyrf_task_event__type, | 329 | [PERF_RECORD_FORK] = &pyrf_task_event__type, |
221 | [PERF_RECORD_READ] = &pyrf_mmap_event__type, | 330 | [PERF_RECORD_READ] = &pyrf_read_event__type, |
222 | [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, | 331 | [PERF_RECORD_SAMPLE] = &pyrf_sample_event__type, |
223 | }; | 332 | }; |
224 | 333 | ||
225 | static PyObject *pyrf_event__new(union perf_event *event) | 334 | static PyObject *pyrf_event__new(union perf_event *event) |
@@ -247,7 +356,7 @@ struct pyrf_cpu_map { | |||
247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | 356 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, |
248 | PyObject *args, PyObject *kwargs) | 357 | PyObject *args, PyObject *kwargs) |
249 | { | 358 | { |
250 | static char *kwlist[] = { "cpustr", NULL, NULL, }; | 359 | static char *kwlist[] = { "cpustr", NULL }; |
251 | char *cpustr = NULL; | 360 | char *cpustr = NULL; |
252 | 361 | ||
253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | 362 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", |
@@ -316,7 +425,7 @@ struct pyrf_thread_map { | |||
316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
317 | PyObject *args, PyObject *kwargs) | 426 | PyObject *args, PyObject *kwargs) |
318 | { | 427 | { |
319 | static char *kwlist[] = { "pid", "tid", NULL, NULL, }; | 428 | static char *kwlist[] = { "pid", "tid", NULL }; |
320 | int pid = -1, tid = -1; | 429 | int pid = -1, tid = -1; |
321 | 430 | ||
322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", |
@@ -418,7 +527,9 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | |||
418 | "wakeup_events", | 527 | "wakeup_events", |
419 | "bp_type", | 528 | "bp_type", |
420 | "bp_addr", | 529 | "bp_addr", |
421 | "bp_len", NULL, NULL, }; | 530 | "bp_len", |
531 | NULL | ||
532 | }; | ||
422 | u64 sample_period = 0; | 533 | u64 sample_period = 0; |
423 | u32 disabled = 0, | 534 | u32 disabled = 0, |
424 | inherit = 0, | 535 | inherit = 0, |
@@ -499,7 +610,7 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
499 | struct thread_map *threads = NULL; | 610 | struct thread_map *threads = NULL; |
500 | PyObject *pcpus = NULL, *pthreads = NULL; | 611 | PyObject *pcpus = NULL, *pthreads = NULL; |
501 | int group = 0, inherit = 0; | 612 | int group = 0, inherit = 0; |
502 | static char *kwlist[] = {"cpus", "threads", "group", "inherit", NULL, NULL}; | 613 | static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; |
503 | 614 | ||
504 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | 615 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, |
505 | &pcpus, &pthreads, &group, &inherit)) | 616 | &pcpus, &pthreads, &group, &inherit)) |
@@ -582,8 +693,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | |||
582 | PyObject *args, PyObject *kwargs) | 693 | PyObject *args, PyObject *kwargs) |
583 | { | 694 | { |
584 | struct perf_evlist *evlist = &pevlist->evlist; | 695 | struct perf_evlist *evlist = &pevlist->evlist; |
585 | static char *kwlist[] = {"pages", "overwrite", | 696 | static char *kwlist[] = { "pages", "overwrite", NULL }; |
586 | NULL, NULL}; | ||
587 | int pages = 128, overwrite = false; | 697 | int pages = 128, overwrite = false; |
588 | 698 | ||
589 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | 699 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, |
@@ -603,7 +713,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
603 | PyObject *args, PyObject *kwargs) | 713 | PyObject *args, PyObject *kwargs) |
604 | { | 714 | { |
605 | struct perf_evlist *evlist = &pevlist->evlist; | 715 | struct perf_evlist *evlist = &pevlist->evlist; |
606 | static char *kwlist[] = {"timeout", NULL, NULL}; | 716 | static char *kwlist[] = { "timeout", NULL }; |
607 | int timeout = -1, n; | 717 | int timeout = -1, n; |
608 | 718 | ||
609 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 719 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
@@ -674,7 +784,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
674 | struct perf_evlist *evlist = &pevlist->evlist; | 784 | struct perf_evlist *evlist = &pevlist->evlist; |
675 | union perf_event *event; | 785 | union perf_event *event; |
676 | int sample_id_all = 1, cpu; | 786 | int sample_id_all = 1, cpu; |
677 | static char *kwlist[] = {"cpu", "sample_id_all", NULL, NULL}; | 787 | static char *kwlist[] = { "cpu", "sample_id_all", NULL }; |
678 | int err; | 788 | int err; |
679 | 789 | ||
680 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | 790 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, |
@@ -693,7 +803,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
693 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 803 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
694 | err = perf_event__parse_sample(event, first->attr.sample_type, | 804 | err = perf_event__parse_sample(event, first->attr.sample_type, |
695 | perf_evsel__sample_size(first), | 805 | perf_evsel__sample_size(first), |
696 | sample_id_all, &pevent->sample); | 806 | sample_id_all, &pevent->sample, false); |
697 | if (err) | 807 | if (err) |
698 | return PyErr_Format(PyExc_OSError, | 808 | return PyErr_Format(PyExc_OSError, |
699 | "perf: can't parse sample, err=%d", err); | 809 | "perf: can't parse sample, err=%d", err); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f5a8fbdd3f7..72458d9da5b 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "session.h" | 12 | #include "session.h" |
13 | #include "sort.h" | 13 | #include "sort.h" |
14 | #include "util.h" | 14 | #include "util.h" |
15 | #include "cpumap.h" | ||
15 | 16 | ||
16 | static int perf_session__open(struct perf_session *self, bool force) | 17 | static int perf_session__open(struct perf_session *self, bool force) |
17 | { | 18 | { |
@@ -247,9 +248,14 @@ int perf_session__resolve_callchain(struct perf_session *self, | |||
247 | callchain_cursor_reset(&self->callchain_cursor); | 248 | callchain_cursor_reset(&self->callchain_cursor); |
248 | 249 | ||
249 | for (i = 0; i < chain->nr; i++) { | 250 | for (i = 0; i < chain->nr; i++) { |
250 | u64 ip = chain->ips[i]; | 251 | u64 ip; |
251 | struct addr_location al; | 252 | struct addr_location al; |
252 | 253 | ||
254 | if (callchain_param.order == ORDER_CALLEE) | ||
255 | ip = chain->ips[i]; | ||
256 | else | ||
257 | ip = chain->ips[chain->nr - i - 1]; | ||
258 | |||
253 | if (ip >= PERF_CONTEXT_MAX) { | 259 | if (ip >= PERF_CONTEXT_MAX) { |
254 | switch (ip) { | 260 | switch (ip) { |
255 | case PERF_CONTEXT_HV: | 261 | case PERF_CONTEXT_HV: |
@@ -407,20 +413,26 @@ static void perf_event__read_swap(union perf_event *event) | |||
407 | event->read.id = bswap_64(event->read.id); | 413 | event->read.id = bswap_64(event->read.id); |
408 | } | 414 | } |
409 | 415 | ||
410 | static void perf_event__attr_swap(union perf_event *event) | 416 | /* exported for swapping attributes in file header */ |
417 | void perf_event__attr_swap(struct perf_event_attr *attr) | ||
418 | { | ||
419 | attr->type = bswap_32(attr->type); | ||
420 | attr->size = bswap_32(attr->size); | ||
421 | attr->config = bswap_64(attr->config); | ||
422 | attr->sample_period = bswap_64(attr->sample_period); | ||
423 | attr->sample_type = bswap_64(attr->sample_type); | ||
424 | attr->read_format = bswap_64(attr->read_format); | ||
425 | attr->wakeup_events = bswap_32(attr->wakeup_events); | ||
426 | attr->bp_type = bswap_32(attr->bp_type); | ||
427 | attr->bp_addr = bswap_64(attr->bp_addr); | ||
428 | attr->bp_len = bswap_64(attr->bp_len); | ||
429 | } | ||
430 | |||
431 | static void perf_event__hdr_attr_swap(union perf_event *event) | ||
411 | { | 432 | { |
412 | size_t size; | 433 | size_t size; |
413 | 434 | ||
414 | event->attr.attr.type = bswap_32(event->attr.attr.type); | 435 | perf_event__attr_swap(&event->attr.attr); |
415 | event->attr.attr.size = bswap_32(event->attr.attr.size); | ||
416 | event->attr.attr.config = bswap_64(event->attr.attr.config); | ||
417 | event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); | ||
418 | event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); | ||
419 | event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); | ||
420 | event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); | ||
421 | event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); | ||
422 | event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); | ||
423 | event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); | ||
424 | 436 | ||
425 | size = event->header.size; | 437 | size = event->header.size; |
426 | size -= (void *)&event->attr.id - (void *)event; | 438 | size -= (void *)&event->attr.id - (void *)event; |
@@ -448,7 +460,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
448 | [PERF_RECORD_LOST] = perf_event__all64_swap, | 460 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
449 | [PERF_RECORD_READ] = perf_event__read_swap, | 461 | [PERF_RECORD_READ] = perf_event__read_swap, |
450 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, | 462 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
451 | [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, | 463 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, |
452 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 464 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
453 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 465 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
454 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 466 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
@@ -708,9 +720,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
708 | if (!dump_trace) | 720 | if (!dump_trace) |
709 | return; | 721 | return; |
710 | 722 | ||
711 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", | 723 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", |
712 | event->header.misc, sample->pid, sample->tid, sample->ip, | 724 | event->header.misc, sample->pid, sample->tid, sample->ip, |
713 | sample->period); | 725 | sample->period, sample->addr); |
714 | 726 | ||
715 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 727 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
716 | callchain__printf(sample); | 728 | callchain__printf(sample); |
@@ -1202,9 +1214,10 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1202 | return NULL; | 1214 | return NULL; |
1203 | } | 1215 | } |
1204 | 1216 | ||
1205 | void perf_session__print_symbols(union perf_event *event, | 1217 | void perf_session__print_ip(union perf_event *event, |
1206 | struct perf_sample *sample, | 1218 | struct perf_sample *sample, |
1207 | struct perf_session *session) | 1219 | struct perf_session *session, |
1220 | int print_sym, int print_dso) | ||
1208 | { | 1221 | { |
1209 | struct addr_location al; | 1222 | struct addr_location al; |
1210 | const char *symname, *dsoname; | 1223 | const char *symname, *dsoname; |
@@ -1233,32 +1246,83 @@ void perf_session__print_symbols(union perf_event *event, | |||
1233 | if (!node) | 1246 | if (!node) |
1234 | break; | 1247 | break; |
1235 | 1248 | ||
1236 | if (node->sym && node->sym->name) | 1249 | printf("\t%16" PRIx64, node->ip); |
1237 | symname = node->sym->name; | 1250 | if (print_sym) { |
1251 | if (node->sym && node->sym->name) | ||
1252 | symname = node->sym->name; | ||
1253 | else | ||
1254 | symname = ""; | ||
1255 | |||
1256 | printf(" %s", symname); | ||
1257 | } | ||
1258 | if (print_dso) { | ||
1259 | if (node->map && node->map->dso && node->map->dso->name) | ||
1260 | dsoname = node->map->dso->name; | ||
1261 | else | ||
1262 | dsoname = ""; | ||
1263 | |||
1264 | printf(" (%s)", dsoname); | ||
1265 | } | ||
1266 | printf("\n"); | ||
1267 | |||
1268 | callchain_cursor_advance(cursor); | ||
1269 | } | ||
1270 | |||
1271 | } else { | ||
1272 | printf("%16" PRIx64, sample->ip); | ||
1273 | if (print_sym) { | ||
1274 | if (al.sym && al.sym->name) | ||
1275 | symname = al.sym->name; | ||
1238 | else | 1276 | else |
1239 | symname = ""; | 1277 | symname = ""; |
1240 | 1278 | ||
1241 | if (node->map && node->map->dso && node->map->dso->name) | 1279 | printf(" %s", symname); |
1242 | dsoname = node->map->dso->name; | 1280 | } |
1281 | |||
1282 | if (print_dso) { | ||
1283 | if (al.map && al.map->dso && al.map->dso->name) | ||
1284 | dsoname = al.map->dso->name; | ||
1243 | else | 1285 | else |
1244 | dsoname = ""; | 1286 | dsoname = ""; |
1245 | 1287 | ||
1246 | printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); | 1288 | printf(" (%s)", dsoname); |
1289 | } | ||
1290 | } | ||
1291 | } | ||
1247 | 1292 | ||
1248 | callchain_cursor_advance(cursor); | 1293 | int perf_session__cpu_bitmap(struct perf_session *session, |
1294 | const char *cpu_list, unsigned long *cpu_bitmap) | ||
1295 | { | ||
1296 | int i; | ||
1297 | struct cpu_map *map; | ||
1298 | |||
1299 | for (i = 0; i < PERF_TYPE_MAX; ++i) { | ||
1300 | struct perf_evsel *evsel; | ||
1301 | |||
1302 | evsel = perf_session__find_first_evtype(session, i); | ||
1303 | if (!evsel) | ||
1304 | continue; | ||
1305 | |||
1306 | if (!(evsel->attr.sample_type & PERF_SAMPLE_CPU)) { | ||
1307 | pr_err("File does not contain CPU events. " | ||
1308 | "Remove -c option to proceed.\n"); | ||
1309 | return -1; | ||
1249 | } | 1310 | } |
1311 | } | ||
1250 | 1312 | ||
1251 | } else { | 1313 | map = cpu_map__new(cpu_list); |
1252 | if (al.sym && al.sym->name) | ||
1253 | symname = al.sym->name; | ||
1254 | else | ||
1255 | symname = ""; | ||
1256 | 1314 | ||
1257 | if (al.map && al.map->dso && al.map->dso->name) | 1315 | for (i = 0; i < map->nr; i++) { |
1258 | dsoname = al.map->dso->name; | 1316 | int cpu = map->map[i]; |
1259 | else | 1317 | |
1260 | dsoname = ""; | 1318 | if (cpu >= MAX_NR_CPUS) { |
1319 | pr_err("Requested CPU %d too large. " | ||
1320 | "Consider raising MAX_NR_CPUS\n", cpu); | ||
1321 | return -1; | ||
1322 | } | ||
1261 | 1323 | ||
1262 | printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); | 1324 | set_bit(cpu, cpu_bitmap); |
1263 | } | 1325 | } |
1326 | |||
1327 | return 0; | ||
1264 | } | 1328 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 66d4e149087..974d0cbee5e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -112,6 +112,7 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
112 | u64 addr); | 112 | u64 addr); |
113 | 113 | ||
114 | void mem_bswap_64(void *src, int byte_size); | 114 | void mem_bswap_64(void *src, int byte_size); |
115 | void perf_event__attr_swap(struct perf_event_attr *attr); | ||
115 | 116 | ||
116 | int perf_session__create_kernel_maps(struct perf_session *self); | 117 | int perf_session__create_kernel_maps(struct perf_session *self); |
117 | 118 | ||
@@ -161,14 +162,19 @@ static inline int perf_session__parse_sample(struct perf_session *session, | |||
161 | { | 162 | { |
162 | return perf_event__parse_sample(event, session->sample_type, | 163 | return perf_event__parse_sample(event, session->sample_type, |
163 | session->sample_size, | 164 | session->sample_size, |
164 | session->sample_id_all, sample); | 165 | session->sample_id_all, sample, |
166 | session->header.needs_swap); | ||
165 | } | 167 | } |
166 | 168 | ||
167 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 169 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
168 | unsigned int type); | 170 | unsigned int type); |
169 | 171 | ||
170 | void perf_session__print_symbols(union perf_event *event, | 172 | void perf_session__print_ip(union perf_event *event, |
171 | struct perf_sample *sample, | 173 | struct perf_sample *sample, |
172 | struct perf_session *session); | 174 | struct perf_session *session, |
175 | int print_sym, int print_dso); | ||
176 | |||
177 | int perf_session__cpu_bitmap(struct perf_session *session, | ||
178 | const char *cpu_list, unsigned long *cpu_bitmap); | ||
173 | 179 | ||
174 | #endif /* __PERF_SESSION_H */ | 180 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index bbc982f5dd8..95d37007492 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -3,9 +3,27 @@ | |||
3 | from distutils.core import setup, Extension | 3 | from distutils.core import setup, Extension |
4 | from os import getenv | 4 | from os import getenv |
5 | 5 | ||
6 | from distutils.command.build_ext import build_ext as _build_ext | ||
7 | from distutils.command.install_lib import install_lib as _install_lib | ||
8 | |||
9 | class build_ext(_build_ext): | ||
10 | def finalize_options(self): | ||
11 | _build_ext.finalize_options(self) | ||
12 | self.build_lib = build_lib | ||
13 | self.build_temp = build_tmp | ||
14 | |||
15 | class install_lib(_install_lib): | ||
16 | def finalize_options(self): | ||
17 | _install_lib.finalize_options(self) | ||
18 | self.build_dir = build_lib | ||
19 | |||
20 | |||
6 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] | 21 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] |
7 | cflags += getenv('CFLAGS', '').split() | 22 | cflags += getenv('CFLAGS', '').split() |
8 | 23 | ||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | ||
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | ||
26 | |||
9 | perf = Extension('perf', | 27 | perf = Extension('perf', |
10 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | 28 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', |
11 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | 29 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', |
@@ -21,4 +39,5 @@ setup(name='perf', | |||
21 | author_email='acme@redhat.com', | 39 | author_email='acme@redhat.com', |
22 | license='GPLv2', | 40 | license='GPLv2', |
23 | url='http://perf.wiki.kernel.org', | 41 | url='http://perf.wiki.kernel.org', |
24 | ext_modules=[perf]) | 42 | ext_modules=[perf], |
43 | cmdclass={'build_ext': build_ext, 'install_lib': install_lib}) | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f44fa541d56..1ee8f1e40f1 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -15,95 +15,6 @@ char * field_sep; | |||
15 | 15 | ||
16 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
17 | 17 | ||
18 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | ||
19 | size_t size, unsigned int width); | ||
20 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | ||
21 | size_t size, unsigned int width); | ||
22 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | ||
23 | size_t size, unsigned int width); | ||
24 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | ||
25 | size_t size, unsigned int width); | ||
26 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | ||
27 | size_t size, unsigned int width); | ||
28 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
29 | size_t size, unsigned int width); | ||
30 | |||
31 | struct sort_entry sort_thread = { | ||
32 | .se_header = "Command: Pid", | ||
33 | .se_cmp = sort__thread_cmp, | ||
34 | .se_snprintf = hist_entry__thread_snprintf, | ||
35 | .se_width_idx = HISTC_THREAD, | ||
36 | }; | ||
37 | |||
38 | struct sort_entry sort_comm = { | ||
39 | .se_header = "Command", | ||
40 | .se_cmp = sort__comm_cmp, | ||
41 | .se_collapse = sort__comm_collapse, | ||
42 | .se_snprintf = hist_entry__comm_snprintf, | ||
43 | .se_width_idx = HISTC_COMM, | ||
44 | }; | ||
45 | |||
46 | struct sort_entry sort_dso = { | ||
47 | .se_header = "Shared Object", | ||
48 | .se_cmp = sort__dso_cmp, | ||
49 | .se_snprintf = hist_entry__dso_snprintf, | ||
50 | .se_width_idx = HISTC_DSO, | ||
51 | }; | ||
52 | |||
53 | struct sort_entry sort_sym = { | ||
54 | .se_header = "Symbol", | ||
55 | .se_cmp = sort__sym_cmp, | ||
56 | .se_snprintf = hist_entry__sym_snprintf, | ||
57 | .se_width_idx = HISTC_SYMBOL, | ||
58 | }; | ||
59 | |||
60 | struct sort_entry sort_parent = { | ||
61 | .se_header = "Parent symbol", | ||
62 | .se_cmp = sort__parent_cmp, | ||
63 | .se_snprintf = hist_entry__parent_snprintf, | ||
64 | .se_width_idx = HISTC_PARENT, | ||
65 | }; | ||
66 | |||
67 | struct sort_entry sort_cpu = { | ||
68 | .se_header = "CPU", | ||
69 | .se_cmp = sort__cpu_cmp, | ||
70 | .se_snprintf = hist_entry__cpu_snprintf, | ||
71 | .se_width_idx = HISTC_CPU, | ||
72 | }; | ||
73 | |||
74 | struct sort_dimension { | ||
75 | const char *name; | ||
76 | struct sort_entry *entry; | ||
77 | int taken; | ||
78 | }; | ||
79 | |||
80 | static struct sort_dimension sort_dimensions[] = { | ||
81 | { .name = "pid", .entry = &sort_thread, }, | ||
82 | { .name = "comm", .entry = &sort_comm, }, | ||
83 | { .name = "dso", .entry = &sort_dso, }, | ||
84 | { .name = "symbol", .entry = &sort_sym, }, | ||
85 | { .name = "parent", .entry = &sort_parent, }, | ||
86 | { .name = "cpu", .entry = &sort_cpu, }, | ||
87 | }; | ||
88 | |||
89 | int64_t cmp_null(void *l, void *r) | ||
90 | { | ||
91 | if (!l && !r) | ||
92 | return 0; | ||
93 | else if (!l) | ||
94 | return -1; | ||
95 | else | ||
96 | return 1; | ||
97 | } | ||
98 | |||
99 | /* --sort pid */ | ||
100 | |||
101 | int64_t | ||
102 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
103 | { | ||
104 | return right->thread->pid - left->thread->pid; | ||
105 | } | ||
106 | |||
107 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
108 | { | 19 | { |
109 | int n; | 20 | int n; |
@@ -125,6 +36,24 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
125 | return n; | 36 | return n; |
126 | } | 37 | } |
127 | 38 | ||
39 | static int64_t cmp_null(void *l, void *r) | ||
40 | { | ||
41 | if (!l && !r) | ||
42 | return 0; | ||
43 | else if (!l) | ||
44 | return -1; | ||
45 | else | ||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | /* --sort pid */ | ||
50 | |||
51 | static int64_t | ||
52 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
53 | { | ||
54 | return right->thread->pid - left->thread->pid; | ||
55 | } | ||
56 | |||
128 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 57 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
129 | size_t size, unsigned int width) | 58 | size_t size, unsigned int width) |
130 | { | 59 | { |
@@ -132,15 +61,50 @@ static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | |||
132 | self->thread->comm ?: "", self->thread->pid); | 61 | self->thread->comm ?: "", self->thread->pid); |
133 | } | 62 | } |
134 | 63 | ||
64 | struct sort_entry sort_thread = { | ||
65 | .se_header = "Command: Pid", | ||
66 | .se_cmp = sort__thread_cmp, | ||
67 | .se_snprintf = hist_entry__thread_snprintf, | ||
68 | .se_width_idx = HISTC_THREAD, | ||
69 | }; | ||
70 | |||
71 | /* --sort comm */ | ||
72 | |||
73 | static int64_t | ||
74 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
75 | { | ||
76 | return right->thread->pid - left->thread->pid; | ||
77 | } | ||
78 | |||
79 | static int64_t | ||
80 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
81 | { | ||
82 | char *comm_l = left->thread->comm; | ||
83 | char *comm_r = right->thread->comm; | ||
84 | |||
85 | if (!comm_l || !comm_r) | ||
86 | return cmp_null(comm_l, comm_r); | ||
87 | |||
88 | return strcmp(comm_l, comm_r); | ||
89 | } | ||
90 | |||
135 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | 91 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, |
136 | size_t size, unsigned int width) | 92 | size_t size, unsigned int width) |
137 | { | 93 | { |
138 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 94 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
139 | } | 95 | } |
140 | 96 | ||
97 | struct sort_entry sort_comm = { | ||
98 | .se_header = "Command", | ||
99 | .se_cmp = sort__comm_cmp, | ||
100 | .se_collapse = sort__comm_collapse, | ||
101 | .se_snprintf = hist_entry__comm_snprintf, | ||
102 | .se_width_idx = HISTC_COMM, | ||
103 | }; | ||
104 | |||
141 | /* --sort dso */ | 105 | /* --sort dso */ |
142 | 106 | ||
143 | int64_t | 107 | static int64_t |
144 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 108 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
145 | { | 109 | { |
146 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; | 110 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; |
@@ -173,18 +137,31 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
173 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 137 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
174 | } | 138 | } |
175 | 139 | ||
140 | struct sort_entry sort_dso = { | ||
141 | .se_header = "Shared Object", | ||
142 | .se_cmp = sort__dso_cmp, | ||
143 | .se_snprintf = hist_entry__dso_snprintf, | ||
144 | .se_width_idx = HISTC_DSO, | ||
145 | }; | ||
146 | |||
176 | /* --sort symbol */ | 147 | /* --sort symbol */ |
177 | 148 | ||
178 | int64_t | 149 | static int64_t |
179 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 150 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
180 | { | 151 | { |
181 | u64 ip_l, ip_r; | 152 | u64 ip_l, ip_r; |
182 | 153 | ||
154 | if (!left->ms.sym && !right->ms.sym) | ||
155 | return right->level - left->level; | ||
156 | |||
157 | if (!left->ms.sym || !right->ms.sym) | ||
158 | return cmp_null(left->ms.sym, right->ms.sym); | ||
159 | |||
183 | if (left->ms.sym == right->ms.sym) | 160 | if (left->ms.sym == right->ms.sym) |
184 | return 0; | 161 | return 0; |
185 | 162 | ||
186 | ip_l = left->ms.sym ? left->ms.sym->start : left->ip; | 163 | ip_l = left->ms.sym->start; |
187 | ip_r = right->ms.sym ? right->ms.sym->start : right->ip; | 164 | ip_r = right->ms.sym->start; |
188 | 165 | ||
189 | return (int64_t)(ip_r - ip_l); | 166 | return (int64_t)(ip_r - ip_l); |
190 | } | 167 | } |
@@ -211,29 +188,16 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
211 | return ret; | 188 | return ret; |
212 | } | 189 | } |
213 | 190 | ||
214 | /* --sort comm */ | 191 | struct sort_entry sort_sym = { |
215 | 192 | .se_header = "Symbol", | |
216 | int64_t | 193 | .se_cmp = sort__sym_cmp, |
217 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | 194 | .se_snprintf = hist_entry__sym_snprintf, |
218 | { | 195 | .se_width_idx = HISTC_SYMBOL, |
219 | return right->thread->pid - left->thread->pid; | 196 | }; |
220 | } | ||
221 | |||
222 | int64_t | ||
223 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
224 | { | ||
225 | char *comm_l = left->thread->comm; | ||
226 | char *comm_r = right->thread->comm; | ||
227 | |||
228 | if (!comm_l || !comm_r) | ||
229 | return cmp_null(comm_l, comm_r); | ||
230 | |||
231 | return strcmp(comm_l, comm_r); | ||
232 | } | ||
233 | 197 | ||
234 | /* --sort parent */ | 198 | /* --sort parent */ |
235 | 199 | ||
236 | int64_t | 200 | static int64_t |
237 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | 201 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) |
238 | { | 202 | { |
239 | struct symbol *sym_l = left->parent; | 203 | struct symbol *sym_l = left->parent; |
@@ -252,9 +216,16 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
252 | self->parent ? self->parent->name : "[other]"); | 216 | self->parent ? self->parent->name : "[other]"); |
253 | } | 217 | } |
254 | 218 | ||
219 | struct sort_entry sort_parent = { | ||
220 | .se_header = "Parent symbol", | ||
221 | .se_cmp = sort__parent_cmp, | ||
222 | .se_snprintf = hist_entry__parent_snprintf, | ||
223 | .se_width_idx = HISTC_PARENT, | ||
224 | }; | ||
225 | |||
255 | /* --sort cpu */ | 226 | /* --sort cpu */ |
256 | 227 | ||
257 | int64_t | 228 | static int64_t |
258 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | 229 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) |
259 | { | 230 | { |
260 | return right->cpu - left->cpu; | 231 | return right->cpu - left->cpu; |
@@ -266,6 +237,28 @@ static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | |||
266 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 237 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); |
267 | } | 238 | } |
268 | 239 | ||
240 | struct sort_entry sort_cpu = { | ||
241 | .se_header = "CPU", | ||
242 | .se_cmp = sort__cpu_cmp, | ||
243 | .se_snprintf = hist_entry__cpu_snprintf, | ||
244 | .se_width_idx = HISTC_CPU, | ||
245 | }; | ||
246 | |||
247 | struct sort_dimension { | ||
248 | const char *name; | ||
249 | struct sort_entry *entry; | ||
250 | int taken; | ||
251 | }; | ||
252 | |||
253 | static struct sort_dimension sort_dimensions[] = { | ||
254 | { .name = "pid", .entry = &sort_thread, }, | ||
255 | { .name = "comm", .entry = &sort_comm, }, | ||
256 | { .name = "dso", .entry = &sort_dso, }, | ||
257 | { .name = "symbol", .entry = &sort_sym, }, | ||
258 | { .name = "parent", .entry = &sort_parent, }, | ||
259 | { .name = "cpu", .entry = &sort_cpu, }, | ||
260 | }; | ||
261 | |||
269 | int sort_dimension__add(const char *tok) | 262 | int sort_dimension__add(const char *tok) |
270 | { | 263 | { |
271 | unsigned int i; | 264 | unsigned int i; |
@@ -273,15 +266,9 @@ int sort_dimension__add(const char *tok) | |||
273 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 266 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
274 | struct sort_dimension *sd = &sort_dimensions[i]; | 267 | struct sort_dimension *sd = &sort_dimensions[i]; |
275 | 268 | ||
276 | if (sd->taken) | ||
277 | continue; | ||
278 | |||
279 | if (strncasecmp(tok, sd->name, strlen(tok))) | 269 | if (strncasecmp(tok, sd->name, strlen(tok))) |
280 | continue; | 270 | continue; |
281 | 271 | ||
282 | if (sd->entry->se_collapse) | ||
283 | sort__need_collapse = 1; | ||
284 | |||
285 | if (sd->entry == &sort_parent) { | 272 | if (sd->entry == &sort_parent) { |
286 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 273 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
287 | if (ret) { | 274 | if (ret) { |
@@ -294,6 +281,12 @@ int sort_dimension__add(const char *tok) | |||
294 | sort__has_parent = 1; | 281 | sort__has_parent = 1; |
295 | } | 282 | } |
296 | 283 | ||
284 | if (sd->taken) | ||
285 | return 0; | ||
286 | |||
287 | if (sd->entry->se_collapse) | ||
288 | sort__need_collapse = 1; | ||
289 | |||
297 | if (list_empty(&hist_entry__sort_list)) { | 290 | if (list_empty(&hist_entry__sort_list)) { |
298 | if (!strcmp(sd->name, "pid")) | 291 | if (!strcmp(sd->name, "pid")) |
299 | sort__first_dimension = SORT_PID; | 292 | sort__first_dimension = SORT_PID; |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0b91053a7d1..77d0388ad41 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -103,20 +103,6 @@ extern struct sort_entry sort_thread; | |||
103 | extern struct list_head hist_entry__sort_list; | 103 | extern struct list_head hist_entry__sort_list; |
104 | 104 | ||
105 | void setup_sorting(const char * const usagestr[], const struct option *opts); | 105 | void setup_sorting(const char * const usagestr[], const struct option *opts); |
106 | |||
107 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
108 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
109 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
110 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
111 | extern int64_t cmp_null(void *, void *); | ||
112 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
113 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
114 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
115 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
116 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
117 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
118 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
119 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
120 | extern int sort_dimension__add(const char *); | 106 | extern int sort_dimension__add(const char *); |
121 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 107 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
122 | const char *list_name, FILE *fp); | 108 | const char *list_name, FILE *fp); |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index b9a985dadd0..d5836382ff2 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat) | |||
294 | { | 294 | { |
295 | return __match_glob(str, pat, true); | 295 | return __match_glob(str, pat, true); |
296 | } | 296 | } |
297 | |||
298 | /** | ||
299 | * strtailcmp - Compare the tail of two strings | ||
300 | * @s1: 1st string to be compared | ||
301 | * @s2: 2nd string to be compared | ||
302 | * | ||
303 | * Return 0 if whole of either string is same as another's tail part. | ||
304 | */ | ||
305 | int strtailcmp(const char *s1, const char *s2) | ||
306 | { | ||
307 | int i1 = strlen(s1); | ||
308 | int i2 = strlen(s2); | ||
309 | while (--i1 >= 0 && --i2 >= 0) { | ||
310 | if (s1[i1] != s2[i2]) | ||
311 | return s1[i1] - s2[i2]; | ||
312 | } | ||
313 | return 0; | ||
314 | } | ||
315 | |||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index eec196329fd..40eeaf07725 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -74,16 +74,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | |||
74 | 74 | ||
75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
76 | { | 76 | { |
77 | symbol_type = toupper(symbol_type); | ||
78 | |||
77 | switch (map_type) { | 79 | switch (map_type) { |
78 | case MAP__FUNCTION: | 80 | case MAP__FUNCTION: |
79 | return symbol_type == 'T' || symbol_type == 'W'; | 81 | return symbol_type == 'T' || symbol_type == 'W'; |
80 | case MAP__VARIABLE: | 82 | case MAP__VARIABLE: |
81 | return symbol_type == 'D' || symbol_type == 'd'; | 83 | return symbol_type == 'D'; |
82 | default: | 84 | default: |
83 | return false; | 85 | return false; |
84 | } | 86 | } |
85 | } | 87 | } |
86 | 88 | ||
89 | static int prefix_underscores_count(const char *str) | ||
90 | { | ||
91 | const char *tail = str; | ||
92 | |||
93 | while (*tail == '_') | ||
94 | tail++; | ||
95 | |||
96 | return tail - str; | ||
97 | } | ||
98 | |||
99 | #define SYMBOL_A 0 | ||
100 | #define SYMBOL_B 1 | ||
101 | |||
102 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | ||
103 | { | ||
104 | s64 a; | ||
105 | s64 b; | ||
106 | |||
107 | /* Prefer a symbol with non zero length */ | ||
108 | a = syma->end - syma->start; | ||
109 | b = symb->end - symb->start; | ||
110 | if ((b == 0) && (a > 0)) | ||
111 | return SYMBOL_A; | ||
112 | else if ((a == 0) && (b > 0)) | ||
113 | return SYMBOL_B; | ||
114 | |||
115 | /* Prefer a non weak symbol over a weak one */ | ||
116 | a = syma->binding == STB_WEAK; | ||
117 | b = symb->binding == STB_WEAK; | ||
118 | if (b && !a) | ||
119 | return SYMBOL_A; | ||
120 | if (a && !b) | ||
121 | return SYMBOL_B; | ||
122 | |||
123 | /* Prefer a global symbol over a non global one */ | ||
124 | a = syma->binding == STB_GLOBAL; | ||
125 | b = symb->binding == STB_GLOBAL; | ||
126 | if (a && !b) | ||
127 | return SYMBOL_A; | ||
128 | if (b && !a) | ||
129 | return SYMBOL_B; | ||
130 | |||
131 | /* Prefer a symbol with less underscores */ | ||
132 | a = prefix_underscores_count(syma->name); | ||
133 | b = prefix_underscores_count(symb->name); | ||
134 | if (b > a) | ||
135 | return SYMBOL_A; | ||
136 | else if (a > b) | ||
137 | return SYMBOL_B; | ||
138 | |||
139 | /* If all else fails, choose the symbol with the longest name */ | ||
140 | if (strlen(syma->name) >= strlen(symb->name)) | ||
141 | return SYMBOL_A; | ||
142 | else | ||
143 | return SYMBOL_B; | ||
144 | } | ||
145 | |||
146 | static void symbols__fixup_duplicate(struct rb_root *symbols) | ||
147 | { | ||
148 | struct rb_node *nd; | ||
149 | struct symbol *curr, *next; | ||
150 | |||
151 | nd = rb_first(symbols); | ||
152 | |||
153 | while (nd) { | ||
154 | curr = rb_entry(nd, struct symbol, rb_node); | ||
155 | again: | ||
156 | nd = rb_next(&curr->rb_node); | ||
157 | next = rb_entry(nd, struct symbol, rb_node); | ||
158 | |||
159 | if (!nd) | ||
160 | break; | ||
161 | |||
162 | if (curr->start != next->start) | ||
163 | continue; | ||
164 | |||
165 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | ||
166 | rb_erase(&next->rb_node, symbols); | ||
167 | goto again; | ||
168 | } else { | ||
169 | nd = rb_next(&curr->rb_node); | ||
170 | rb_erase(&curr->rb_node, symbols); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
87 | static void symbols__fixup_end(struct rb_root *symbols) | 175 | static void symbols__fixup_end(struct rb_root *symbols) |
88 | { | 176 | { |
89 | struct rb_node *nd, *prevnd = rb_first(symbols); | 177 | struct rb_node *nd, *prevnd = rb_first(symbols); |
@@ -438,18 +526,11 @@ int kallsyms__parse(const char *filename, void *arg, | |||
438 | char *line = NULL; | 526 | char *line = NULL; |
439 | size_t n; | 527 | size_t n; |
440 | int err = -1; | 528 | int err = -1; |
441 | u64 prev_start = 0; | ||
442 | char prev_symbol_type = 0; | ||
443 | char *prev_symbol_name; | ||
444 | FILE *file = fopen(filename, "r"); | 529 | FILE *file = fopen(filename, "r"); |
445 | 530 | ||
446 | if (file == NULL) | 531 | if (file == NULL) |
447 | goto out_failure; | 532 | goto out_failure; |
448 | 533 | ||
449 | prev_symbol_name = malloc(KSYM_NAME_LEN); | ||
450 | if (prev_symbol_name == NULL) | ||
451 | goto out_close; | ||
452 | |||
453 | err = 0; | 534 | err = 0; |
454 | 535 | ||
455 | while (!feof(file)) { | 536 | while (!feof(file)) { |
@@ -470,7 +551,7 @@ int kallsyms__parse(const char *filename, void *arg, | |||
470 | if (len + 2 >= line_len) | 551 | if (len + 2 >= line_len) |
471 | continue; | 552 | continue; |
472 | 553 | ||
473 | symbol_type = toupper(line[len]); | 554 | symbol_type = line[len]; |
474 | len += 2; | 555 | len += 2; |
475 | symbol_name = line + len; | 556 | symbol_name = line + len; |
476 | len = line_len - len; | 557 | len = line_len - len; |
@@ -480,24 +561,18 @@ int kallsyms__parse(const char *filename, void *arg, | |||
480 | break; | 561 | break; |
481 | } | 562 | } |
482 | 563 | ||
483 | if (prev_symbol_type) { | 564 | /* |
484 | u64 end = start; | 565 | * module symbols are not sorted so we add all |
485 | if (end != prev_start) | 566 | * symbols with zero length and rely on |
486 | --end; | 567 | * symbols__fixup_end() to fix it up. |
487 | err = process_symbol(arg, prev_symbol_name, | 568 | */ |
488 | prev_symbol_type, prev_start, end); | 569 | err = process_symbol(arg, symbol_name, |
489 | if (err) | 570 | symbol_type, start, start); |
490 | break; | 571 | if (err) |
491 | } | 572 | break; |
492 | |||
493 | memcpy(prev_symbol_name, symbol_name, len + 1); | ||
494 | prev_symbol_type = symbol_type; | ||
495 | prev_start = start; | ||
496 | } | 573 | } |
497 | 574 | ||
498 | free(prev_symbol_name); | ||
499 | free(line); | 575 | free(line); |
500 | out_close: | ||
501 | fclose(file); | 576 | fclose(file); |
502 | return err; | 577 | return err; |
503 | 578 | ||
@@ -703,6 +778,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
703 | if (dso__load_all_kallsyms(dso, filename, map) < 0) | 778 | if (dso__load_all_kallsyms(dso, filename, map) < 0) |
704 | return -1; | 779 | return -1; |
705 | 780 | ||
781 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
782 | symbols__fixup_end(&dso->symbols[map->type]); | ||
783 | |||
706 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 784 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
707 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; | 785 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; |
708 | else | 786 | else |
@@ -1092,8 +1170,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
1092 | if (dso->has_build_id) { | 1170 | if (dso->has_build_id) { |
1093 | u8 build_id[BUILD_ID_SIZE]; | 1171 | u8 build_id[BUILD_ID_SIZE]; |
1094 | 1172 | ||
1095 | if (elf_read_build_id(elf, build_id, | 1173 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) |
1096 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | ||
1097 | goto out_elf_end; | 1174 | goto out_elf_end; |
1098 | 1175 | ||
1099 | if (!dso__build_id_equal(dso, build_id)) | 1176 | if (!dso__build_id_equal(dso, build_id)) |
@@ -1111,6 +1188,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
1111 | } | 1188 | } |
1112 | 1189 | ||
1113 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | 1190 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); |
1191 | if (opdshdr.sh_type != SHT_PROGBITS) | ||
1192 | opdsec = NULL; | ||
1114 | if (opdsec) | 1193 | if (opdsec) |
1115 | opddata = elf_rawdata(opdsec, NULL); | 1194 | opddata = elf_rawdata(opdsec, NULL); |
1116 | 1195 | ||
@@ -1276,6 +1355,7 @@ new_symbol: | |||
1276 | * For misannotated, zeroed, ASM function sizes. | 1355 | * For misannotated, zeroed, ASM function sizes. |
1277 | */ | 1356 | */ |
1278 | if (nr > 0) { | 1357 | if (nr > 0) { |
1358 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
1279 | symbols__fixup_end(&dso->symbols[map->type]); | 1359 | symbols__fixup_end(&dso->symbols[map->type]); |
1280 | if (kmap) { | 1360 | if (kmap) { |
1281 | /* | 1361 | /* |
@@ -1362,8 +1442,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
1362 | ptr = data->d_buf; | 1442 | ptr = data->d_buf; |
1363 | while (ptr < (data->d_buf + data->d_size)) { | 1443 | while (ptr < (data->d_buf + data->d_size)) { |
1364 | GElf_Nhdr *nhdr = ptr; | 1444 | GElf_Nhdr *nhdr = ptr; |
1365 | int namesz = NOTE_ALIGN(nhdr->n_namesz), | 1445 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), |
1366 | descsz = NOTE_ALIGN(nhdr->n_descsz); | 1446 | descsz = NOTE_ALIGN(nhdr->n_descsz); |
1367 | const char *name; | 1447 | const char *name; |
1368 | 1448 | ||
1369 | ptr += sizeof(*nhdr); | 1449 | ptr += sizeof(*nhdr); |
@@ -1372,8 +1452,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
1372 | if (nhdr->n_type == NT_GNU_BUILD_ID && | 1452 | if (nhdr->n_type == NT_GNU_BUILD_ID && |
1373 | nhdr->n_namesz == sizeof("GNU")) { | 1453 | nhdr->n_namesz == sizeof("GNU")) { |
1374 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | 1454 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { |
1375 | memcpy(bf, ptr, BUILD_ID_SIZE); | 1455 | size_t sz = min(size, descsz); |
1376 | err = BUILD_ID_SIZE; | 1456 | memcpy(bf, ptr, sz); |
1457 | memset(bf + sz, 0, size - sz); | ||
1458 | err = descsz; | ||
1377 | break; | 1459 | break; |
1378 | } | 1460 | } |
1379 | } | 1461 | } |
@@ -1425,7 +1507,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
1425 | while (1) { | 1507 | while (1) { |
1426 | char bf[BUFSIZ]; | 1508 | char bf[BUFSIZ]; |
1427 | GElf_Nhdr nhdr; | 1509 | GElf_Nhdr nhdr; |
1428 | int namesz, descsz; | 1510 | size_t namesz, descsz; |
1429 | 1511 | ||
1430 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | 1512 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) |
1431 | break; | 1513 | break; |
@@ -1434,15 +1516,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
1434 | descsz = NOTE_ALIGN(nhdr.n_descsz); | 1516 | descsz = NOTE_ALIGN(nhdr.n_descsz); |
1435 | if (nhdr.n_type == NT_GNU_BUILD_ID && | 1517 | if (nhdr.n_type == NT_GNU_BUILD_ID && |
1436 | nhdr.n_namesz == sizeof("GNU")) { | 1518 | nhdr.n_namesz == sizeof("GNU")) { |
1437 | if (read(fd, bf, namesz) != namesz) | 1519 | if (read(fd, bf, namesz) != (ssize_t)namesz) |
1438 | break; | 1520 | break; |
1439 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | 1521 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { |
1440 | if (read(fd, build_id, | 1522 | size_t sz = min(descsz, size); |
1441 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | 1523 | if (read(fd, build_id, sz) == (ssize_t)sz) { |
1524 | memset(build_id + sz, 0, size - sz); | ||
1442 | err = 0; | 1525 | err = 0; |
1443 | break; | 1526 | break; |
1444 | } | 1527 | } |
1445 | } else if (read(fd, bf, descsz) != descsz) | 1528 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) |
1446 | break; | 1529 | break; |
1447 | } else { | 1530 | } else { |
1448 | int n = namesz + descsz; | 1531 | int n = namesz + descsz; |
@@ -1504,6 +1587,17 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1504 | dso->adjust_symbols = 0; | 1587 | dso->adjust_symbols = 0; |
1505 | 1588 | ||
1506 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 1589 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
1590 | struct stat st; | ||
1591 | |||
1592 | if (lstat(dso->name, &st) < 0) | ||
1593 | return -1; | ||
1594 | |||
1595 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
1596 | pr_warning("File %s not owned by current user or root, " | ||
1597 | "ignoring it.\n", dso->name); | ||
1598 | return -1; | ||
1599 | } | ||
1600 | |||
1507 | ret = dso__load_perf_map(dso, map, filter); | 1601 | ret = dso__load_perf_map(dso, map, filter); |
1508 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : | 1602 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : |
1509 | SYMTAB__NOT_FOUND; | 1603 | SYMTAB__NOT_FOUND; |
@@ -2170,27 +2264,22 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | |||
2170 | return ret; | 2264 | return ret; |
2171 | } | 2265 | } |
2172 | 2266 | ||
2173 | struct dso *dso__new_kernel(const char *name) | 2267 | static struct dso* |
2268 | dso__kernel_findnew(struct machine *machine, const char *name, | ||
2269 | const char *short_name, int dso_type) | ||
2174 | { | 2270 | { |
2175 | struct dso *dso = dso__new(name ?: "[kernel.kallsyms]"); | 2271 | /* |
2176 | 2272 | * The kernel dso could be created by build_id processing. | |
2177 | if (dso != NULL) { | 2273 | */ |
2178 | dso__set_short_name(dso, "[kernel]"); | 2274 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); |
2179 | dso->kernel = DSO_TYPE_KERNEL; | ||
2180 | } | ||
2181 | |||
2182 | return dso; | ||
2183 | } | ||
2184 | 2275 | ||
2185 | static struct dso *dso__new_guest_kernel(struct machine *machine, | 2276 | /* |
2186 | const char *name) | 2277 | * We need to run this in all cases, since during the build_id |
2187 | { | 2278 | * processing we had no idea this was the kernel dso. |
2188 | char bf[PATH_MAX]; | 2279 | */ |
2189 | struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf, | ||
2190 | sizeof(bf))); | ||
2191 | if (dso != NULL) { | 2280 | if (dso != NULL) { |
2192 | dso__set_short_name(dso, "[guest.kernel]"); | 2281 | dso__set_short_name(dso, short_name); |
2193 | dso->kernel = DSO_TYPE_GUEST_KERNEL; | 2282 | dso->kernel = dso_type; |
2194 | } | 2283 | } |
2195 | 2284 | ||
2196 | return dso; | 2285 | return dso; |
@@ -2208,24 +2297,36 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | |||
2208 | dso->has_build_id = true; | 2297 | dso->has_build_id = true; |
2209 | } | 2298 | } |
2210 | 2299 | ||
2211 | static struct dso *machine__create_kernel(struct machine *machine) | 2300 | static struct dso *machine__get_kernel(struct machine *machine) |
2212 | { | 2301 | { |
2213 | const char *vmlinux_name = NULL; | 2302 | const char *vmlinux_name = NULL; |
2214 | struct dso *kernel; | 2303 | struct dso *kernel; |
2215 | 2304 | ||
2216 | if (machine__is_host(machine)) { | 2305 | if (machine__is_host(machine)) { |
2217 | vmlinux_name = symbol_conf.vmlinux_name; | 2306 | vmlinux_name = symbol_conf.vmlinux_name; |
2218 | kernel = dso__new_kernel(vmlinux_name); | 2307 | if (!vmlinux_name) |
2308 | vmlinux_name = "[kernel.kallsyms]"; | ||
2309 | |||
2310 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
2311 | "[kernel]", | ||
2312 | DSO_TYPE_KERNEL); | ||
2219 | } else { | 2313 | } else { |
2314 | char bf[PATH_MAX]; | ||
2315 | |||
2220 | if (machine__is_default_guest(machine)) | 2316 | if (machine__is_default_guest(machine)) |
2221 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | 2317 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; |
2222 | kernel = dso__new_guest_kernel(machine, vmlinux_name); | 2318 | if (!vmlinux_name) |
2319 | vmlinux_name = machine__mmap_name(machine, bf, | ||
2320 | sizeof(bf)); | ||
2321 | |||
2322 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
2323 | "[guest.kernel]", | ||
2324 | DSO_TYPE_GUEST_KERNEL); | ||
2223 | } | 2325 | } |
2224 | 2326 | ||
2225 | if (kernel != NULL) { | 2327 | if (kernel != NULL && (!kernel->has_build_id)) |
2226 | dso__read_running_kernel_build_id(kernel, machine); | 2328 | dso__read_running_kernel_build_id(kernel, machine); |
2227 | dsos__add(&machine->kernel_dsos, kernel); | 2329 | |
2228 | } | ||
2229 | return kernel; | 2330 | return kernel; |
2230 | } | 2331 | } |
2231 | 2332 | ||
@@ -2329,7 +2430,7 @@ void machine__destroy_kernel_maps(struct machine *machine) | |||
2329 | 2430 | ||
2330 | int machine__create_kernel_maps(struct machine *machine) | 2431 | int machine__create_kernel_maps(struct machine *machine) |
2331 | { | 2432 | { |
2332 | struct dso *kernel = machine__create_kernel(machine); | 2433 | struct dso *kernel = machine__get_kernel(machine); |
2333 | 2434 | ||
2334 | if (kernel == NULL || | 2435 | if (kernel == NULL || |
2335 | __machine__create_kernel_maps(machine, kernel) < 0) | 2436 | __machine__create_kernel_maps(machine, kernel) < 0) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 325ee36a9d2..4f377d92e75 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -155,7 +155,6 @@ struct dso { | |||
155 | }; | 155 | }; |
156 | 156 | ||
157 | struct dso *dso__new(const char *name); | 157 | struct dso *dso__new(const char *name); |
158 | struct dso *dso__new_kernel(const char *name); | ||
159 | void dso__delete(struct dso *dso); | 158 | void dso__delete(struct dso *dso); |
160 | 159 | ||
161 | int dso__name_len(const struct dso *dso); | 160 | int dso__name_len(const struct dso *dso); |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 35729f4c40c..3403f814ad7 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -183,106 +183,59 @@ int bigendian(void) | |||
183 | return *ptr == 0x01020304; | 183 | return *ptr == 0x01020304; |
184 | } | 184 | } |
185 | 185 | ||
186 | static unsigned long long copy_file_fd(int fd) | 186 | /* unfortunately, you can not stat debugfs or proc files for size */ |
187 | static void record_file(const char *file, size_t hdr_sz) | ||
187 | { | 188 | { |
188 | unsigned long long size = 0; | 189 | unsigned long long size = 0; |
189 | char buf[BUFSIZ]; | 190 | char buf[BUFSIZ], *sizep; |
190 | int r; | 191 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
191 | 192 | int r, fd; | |
192 | do { | ||
193 | r = read(fd, buf, BUFSIZ); | ||
194 | if (r > 0) { | ||
195 | size += r; | ||
196 | write_or_die(buf, r); | ||
197 | } | ||
198 | } while (r > 0); | ||
199 | |||
200 | return size; | ||
201 | } | ||
202 | |||
203 | static unsigned long long copy_file(const char *file) | ||
204 | { | ||
205 | unsigned long long size = 0; | ||
206 | int fd; | ||
207 | 193 | ||
208 | fd = open(file, O_RDONLY); | 194 | fd = open(file, O_RDONLY); |
209 | if (fd < 0) | 195 | if (fd < 0) |
210 | die("Can't read '%s'", file); | 196 | die("Can't read '%s'", file); |
211 | size = copy_file_fd(fd); | ||
212 | close(fd); | ||
213 | 197 | ||
214 | return size; | 198 | /* put in zeros for file size, then fill true size later */ |
215 | } | 199 | write_or_die(&size, hdr_sz); |
216 | |||
217 | static unsigned long get_size_fd(int fd) | ||
218 | { | ||
219 | unsigned long long size = 0; | ||
220 | char buf[BUFSIZ]; | ||
221 | int r; | ||
222 | 200 | ||
223 | do { | 201 | do { |
224 | r = read(fd, buf, BUFSIZ); | 202 | r = read(fd, buf, BUFSIZ); |
225 | if (r > 0) | 203 | if (r > 0) { |
226 | size += r; | 204 | size += r; |
205 | write_or_die(buf, r); | ||
206 | } | ||
227 | } while (r > 0); | 207 | } while (r > 0); |
228 | |||
229 | lseek(fd, 0, SEEK_SET); | ||
230 | |||
231 | return size; | ||
232 | } | ||
233 | |||
234 | static unsigned long get_size(const char *file) | ||
235 | { | ||
236 | unsigned long long size = 0; | ||
237 | int fd; | ||
238 | |||
239 | fd = open(file, O_RDONLY); | ||
240 | if (fd < 0) | ||
241 | die("Can't read '%s'", file); | ||
242 | size = get_size_fd(fd); | ||
243 | close(fd); | 208 | close(fd); |
244 | 209 | ||
245 | return size; | 210 | /* ugh, handle big-endian hdr_size == 4 */ |
211 | sizep = (char*)&size; | ||
212 | if (bigendian()) | ||
213 | sizep += sizeof(u64) - hdr_sz; | ||
214 | |||
215 | if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | ||
216 | die("writing to %s", output_file); | ||
246 | } | 217 | } |
247 | 218 | ||
248 | static void read_header_files(void) | 219 | static void read_header_files(void) |
249 | { | 220 | { |
250 | unsigned long long size, check_size; | ||
251 | char *path; | 221 | char *path; |
252 | int fd; | 222 | struct stat st; |
253 | 223 | ||
254 | path = get_tracing_file("events/header_page"); | 224 | path = get_tracing_file("events/header_page"); |
255 | fd = open(path, O_RDONLY); | 225 | if (stat(path, &st) < 0) |
256 | if (fd < 0) | ||
257 | die("can't read '%s'", path); | 226 | die("can't read '%s'", path); |
258 | 227 | ||
259 | /* unfortunately, you can not stat debugfs files for size */ | ||
260 | size = get_size_fd(fd); | ||
261 | |||
262 | write_or_die("header_page", 12); | 228 | write_or_die("header_page", 12); |
263 | write_or_die(&size, 8); | 229 | record_file(path, 8); |
264 | check_size = copy_file_fd(fd); | ||
265 | close(fd); | ||
266 | |||
267 | if (size != check_size) | ||
268 | die("wrong size for '%s' size=%lld read=%lld", | ||
269 | path, size, check_size); | ||
270 | put_tracing_file(path); | 230 | put_tracing_file(path); |
271 | 231 | ||
272 | path = get_tracing_file("events/header_event"); | 232 | path = get_tracing_file("events/header_event"); |
273 | fd = open(path, O_RDONLY); | 233 | if (stat(path, &st) < 0) |
274 | if (fd < 0) | ||
275 | die("can't read '%s'", path); | 234 | die("can't read '%s'", path); |
276 | 235 | ||
277 | size = get_size_fd(fd); | ||
278 | |||
279 | write_or_die("header_event", 13); | 236 | write_or_die("header_event", 13); |
280 | write_or_die(&size, 8); | 237 | record_file(path, 8); |
281 | check_size = copy_file_fd(fd); | ||
282 | if (size != check_size) | ||
283 | die("wrong size for '%s'", path); | ||
284 | put_tracing_file(path); | 238 | put_tracing_file(path); |
285 | close(fd); | ||
286 | } | 239 | } |
287 | 240 | ||
288 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 241 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -298,7 +251,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
298 | 251 | ||
299 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) | 252 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) |
300 | { | 253 | { |
301 | unsigned long long size, check_size; | ||
302 | struct dirent *dent; | 254 | struct dirent *dent; |
303 | struct stat st; | 255 | struct stat st; |
304 | char *format; | 256 | char *format; |
@@ -338,14 +290,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
338 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 290 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
339 | ret = stat(format, &st); | 291 | ret = stat(format, &st); |
340 | 292 | ||
341 | if (ret >= 0) { | 293 | if (ret >= 0) |
342 | /* unfortunately, you can not stat debugfs files for size */ | 294 | record_file(format, 8); |
343 | size = get_size(format); | ||
344 | write_or_die(&size, 8); | ||
345 | check_size = copy_file(format); | ||
346 | if (size != check_size) | ||
347 | die("error in size of file '%s'", format); | ||
348 | } | ||
349 | 295 | ||
350 | free(format); | 296 | free(format); |
351 | } | 297 | } |
@@ -426,7 +372,7 @@ static void read_event_files(struct tracepoint_path *tps) | |||
426 | 372 | ||
427 | static void read_proc_kallsyms(void) | 373 | static void read_proc_kallsyms(void) |
428 | { | 374 | { |
429 | unsigned int size, check_size; | 375 | unsigned int size; |
430 | const char *path = "/proc/kallsyms"; | 376 | const char *path = "/proc/kallsyms"; |
431 | struct stat st; | 377 | struct stat st; |
432 | int ret; | 378 | int ret; |
@@ -438,17 +384,12 @@ static void read_proc_kallsyms(void) | |||
438 | write_or_die(&size, 4); | 384 | write_or_die(&size, 4); |
439 | return; | 385 | return; |
440 | } | 386 | } |
441 | size = get_size(path); | 387 | record_file(path, 4); |
442 | write_or_die(&size, 4); | ||
443 | check_size = copy_file(path); | ||
444 | if (size != check_size) | ||
445 | die("error in size of file '%s'", path); | ||
446 | |||
447 | } | 388 | } |
448 | 389 | ||
449 | static void read_ftrace_printk(void) | 390 | static void read_ftrace_printk(void) |
450 | { | 391 | { |
451 | unsigned int size, check_size; | 392 | unsigned int size; |
452 | char *path; | 393 | char *path; |
453 | struct stat st; | 394 | struct stat st; |
454 | int ret; | 395 | int ret; |
@@ -461,11 +402,8 @@ static void read_ftrace_printk(void) | |||
461 | write_or_die(&size, 4); | 402 | write_or_die(&size, 4); |
462 | goto out; | 403 | goto out; |
463 | } | 404 | } |
464 | size = get_size(path); | 405 | record_file(path, 4); |
465 | write_or_die(&size, 4); | 406 | |
466 | check_size = copy_file(path); | ||
467 | if (size != check_size) | ||
468 | die("error in size of file '%s'", path); | ||
469 | out: | 407 | out: |
470 | put_tracing_file(path); | 408 | put_tracing_file(path); |
471 | } | 409 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 0a7ed5b5e28..bf54c48871d 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -1537,6 +1537,8 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
1537 | field = malloc_or_die(sizeof(*field)); | 1537 | field = malloc_or_die(sizeof(*field)); |
1538 | 1538 | ||
1539 | type = process_arg(event, field, &token); | 1539 | type = process_arg(event, field, &token); |
1540 | while (type == EVENT_OP) | ||
1541 | type = process_op(event, field, &token); | ||
1540 | if (test_type_token(type, token, EVENT_DELIM, ",")) | 1542 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1541 | goto out_free; | 1543 | goto out_free; |
1542 | 1544 | ||
@@ -1580,6 +1582,8 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok) | |||
1580 | field = malloc_or_die(sizeof(*field)); | 1582 | field = malloc_or_die(sizeof(*field)); |
1581 | 1583 | ||
1582 | type = process_arg(event, field, &token); | 1584 | type = process_arg(event, field, &token); |
1585 | while (type == EVENT_OP) | ||
1586 | type = process_op(event, field, &token); | ||
1583 | if (test_type_token(type, token, EVENT_DELIM, ",")) | 1587 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1584 | goto out_free; | 1588 | goto out_free; |
1585 | 1589 | ||
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c index 5a06538532a..88403cf8396 100644 --- a/tools/perf/util/ui/browsers/top.c +++ b/tools/perf/util/ui/browsers/top.c | |||
@@ -208,6 +208,5 @@ int perf_top__tui_browser(struct perf_top *top) | |||
208 | }, | 208 | }, |
209 | }; | 209 | }; |
210 | 210 | ||
211 | ui_helpline__push("Press <- or ESC to exit"); | ||
212 | return perf_top_browser__run(&browser); | 211 | return perf_top_browser__run(&browser); |
213 | } | 212 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fc784284ac8..0128906bac8 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp); | |||
238 | void argv_free(char **argv); | 238 | void argv_free(char **argv); |
239 | bool strglobmatch(const char *str, const char *pat); | 239 | bool strglobmatch(const char *str, const char *pat); |
240 | bool strlazymatch(const char *str, const char *pat); | 240 | bool strlazymatch(const char *str, const char *pat); |
241 | int strtailcmp(const char *s1, const char *s2); | ||
241 | unsigned long convert_unit(unsigned long value, char *unit); | 242 | unsigned long convert_unit(unsigned long value, char *unit); |
242 | int readn(int fd, void *buf, size_t size); | 243 | int readn(int fd, void *buf, size_t size); |
243 | 244 | ||