diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-09-29 13:09:58 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-09-29 13:09:58 -0400 |
commit | 41aad2a6d4fcdda8d73c9739daf7a9f3f49499d6 (patch) | |
tree | d679538c1fa9111ba5d604ad5ef016176c71f278 | |
parent | 6b652de2b27c0a4020ce0e8f277e782b6af76096 (diff) | |
parent | d18019a53a07e009899ff6b8dc5ec30f249360d9 (diff) |
Merge tag 'perf-core-for-mingo-20160929' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
---------------------
New features:
- Add support for using symbols in address filters with Intel PT and ARM
CoreSight (hardware assisted tracing facilities) (Adrian Hunter, Mathieu Poirier)
Fixes:
- Fix MMAP event synthesis for pre-existing threads when no hugetlbfs
mount is in place (Adrian Hunter)
- Don't ignore kernel idle symbols in 'perf script' (Adrian Hunter)
- Assorted Intel PT fixes (Adrian Hunter)
Improvements:
- Fix handling of C++ symbols in 'perf probe' (Masami Hiramatsu)
- Beautify sched_[gs]et_attr return value in 'perf trace' (Arnaldo Carvalho de Melo)
Infrastructure changes:
-----------------------
New features:
- Add dwarf unwind 'perf test' for powerpc (Ravi Bangoria)
Fixes:
- Fix error paths in 'perf record' (Adrian Hunter)
Documentation:
- Update documentation info about quipper, a C++ parser for converting
to/from perf.data/chromium profiling format (Simon Que)
Build Fixes:
Fix building in 32 bit platform with libbabeltrace (Wang Nan)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
34 files changed, 1451 insertions, 62 deletions
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 1a24f4d64328..92335193dc33 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -35,15 +35,15 @@ OPTIONS | |||
35 | 35 | ||
36 | - a symbolically formed PMU event like 'pmu/param1=0x3,param2/' where | 36 | - a symbolically formed PMU event like 'pmu/param1=0x3,param2/' where |
37 | 'param1', 'param2', etc are defined as formats for the PMU in | 37 | 'param1', 'param2', etc are defined as formats for the PMU in |
38 | /sys/bus/event_sources/devices/<pmu>/format/*. | 38 | /sys/bus/event_source/devices/<pmu>/format/*. |
39 | 39 | ||
40 | - a symbolically formed event like 'pmu/config=M,config1=N,config3=K/' | 40 | - a symbolically formed event like 'pmu/config=M,config1=N,config3=K/' |
41 | 41 | ||
42 | where M, N, K are numbers (in decimal, hex, octal format). Acceptable | 42 | where M, N, K are numbers (in decimal, hex, octal format). Acceptable |
43 | values for each of 'config', 'config1' and 'config2' are defined by | 43 | values for each of 'config', 'config1' and 'config2' are defined by |
44 | corresponding entries in /sys/bus/event_sources/devices/<pmu>/format/* | 44 | corresponding entries in /sys/bus/event_source/devices/<pmu>/format/* |
45 | param1 and param2 are defined as formats for the PMU in: | 45 | param1 and param2 are defined as formats for the PMU in: |
46 | /sys/bus/event_sources/devices/<pmu>/format/* | 46 | /sys/bus/event_source/devices/<pmu>/format/* |
47 | 47 | ||
48 | There are also some params which are not defined in .../<pmu>/format/*. | 48 | There are also some params which are not defined in .../<pmu>/format/*. |
49 | These params can be used to overload default config values per event. | 49 | These params can be used to overload default config values per event. |
@@ -89,9 +89,62 @@ OPTIONS | |||
89 | 89 | ||
90 | --filter=<filter>:: | 90 | --filter=<filter>:: |
91 | Event filter. This option should follow a event selector (-e) which | 91 | Event filter. This option should follow a event selector (-e) which |
92 | selects tracepoint event(s). Multiple '--filter' options are combined | 92 | selects either tracepoint event(s) or a hardware trace PMU |
93 | (e.g. Intel PT or CoreSight). | ||
94 | |||
95 | - tracepoint filters | ||
96 | |||
97 | In the case of tracepoints, multiple '--filter' options are combined | ||
93 | using '&&'. | 98 | using '&&'. |
94 | 99 | ||
100 | - address filters | ||
101 | |||
102 | A hardware trace PMU advertises its ability to accept a number of | ||
103 | address filters by specifying a non-zero value in | ||
104 | /sys/bus/event_source/devices/<pmu>/nr_addr_filters. | ||
105 | |||
106 | Address filters have the format: | ||
107 | |||
108 | filter|start|stop|tracestop <start> [/ <size>] [@<file name>] | ||
109 | |||
110 | Where: | ||
111 | - 'filter': defines a region that will be traced. | ||
112 | - 'start': defines an address at which tracing will begin. | ||
113 | - 'stop': defines an address at which tracing will stop. | ||
114 | - 'tracestop': defines a region in which tracing will stop. | ||
115 | |||
116 | <file name> is the name of the object file, <start> is the offset to the | ||
117 | code to trace in that file, and <size> is the size of the region to | ||
118 | trace. 'start' and 'stop' filters need not specify a <size>. | ||
119 | |||
120 | If no object file is specified then the kernel is assumed, in which case | ||
121 | the start address must be a current kernel memory address. | ||
122 | |||
123 | <start> can also be specified by providing the name of a symbol. If the | ||
124 | symbol name is not unique, it can be disambiguated by inserting #n where | ||
125 | 'n' selects the n'th symbol in address order. Alternately #0, #g or #G | ||
126 | select only a global symbol. <size> can also be specified by providing | ||
127 | the name of a symbol, in which case the size is calculated to the end | ||
128 | of that symbol. For 'filter' and 'tracestop' filters, if <size> is | ||
129 | omitted and <start> is a symbol, then the size is calculated to the end | ||
130 | of that symbol. | ||
131 | |||
132 | If <size> is omitted and <start> is '*', then the start and size will | ||
133 | be calculated from the first and last symbols, i.e. to trace the whole | ||
134 | file. | ||
135 | |||
136 | If symbol names (or '*') are provided, they must be surrounded by white | ||
137 | space. | ||
138 | |||
139 | The filter passed to the kernel is not necessarily the same as entered. | ||
140 | To see the filter that is passed, use the -v option. | ||
141 | |||
142 | The kernel may not be able to configure a trace region if it is not | ||
143 | within a single mapping. MMAP events (or /proc/<pid>/maps) can be | ||
144 | examined to determine if that is a possibility. | ||
145 | |||
146 | Multiple filters can be separated with space or comma. | ||
147 | |||
95 | --exclude-perf:: | 148 | --exclude-perf:: |
96 | Don't record events issued by perf itself. This option should follow | 149 | Don't record events issued by perf itself. This option should follow |
97 | a event selector (-e) which selects tracepoint event(s). It adds a | 150 | a event selector (-e) which selects tracepoint event(s). It adds a |
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt index fdc99fe6bbc3..b664b18d3991 100644 --- a/tools/perf/Documentation/perf.data-file-format.txt +++ b/tools/perf/Documentation/perf.data-file-format.txt | |||
@@ -437,6 +437,10 @@ in pmu-tools parser. This allows to read perf.data from python and dump it. | |||
437 | quipper | 437 | quipper |
438 | 438 | ||
439 | The quipper C++ parser is available at | 439 | The quipper C++ parser is available at |
440 | https://chromium.googlesource.com/chromiumos/platform/chromiumos-wide-profiling/ | 440 | https://chromium.googlesource.com/chromiumos/platform2 |
441 | |||
442 | It is under the chromiumos-wide-profiling/ subdirectory. This library can | ||
443 | convert a perf data file to a protobuf and vice versa. | ||
444 | |||
441 | Unfortunately this parser tends to be many versions behind and may not be able | 445 | Unfortunately this parser tends to be many versions behind and may not be able |
442 | to parse data files generated by recent perf. | 446 | to parse data files generated by recent perf. |
diff --git a/tools/perf/arch/powerpc/Build b/tools/perf/arch/powerpc/Build index 54afe4a467e7..db52fa22d3a1 100644 --- a/tools/perf/arch/powerpc/Build +++ b/tools/perf/arch/powerpc/Build | |||
@@ -1 +1,2 @@ | |||
1 | libperf-y += util/ | 1 | libperf-y += util/ |
2 | libperf-y += tests/ | ||
diff --git a/tools/perf/arch/powerpc/include/arch-tests.h b/tools/perf/arch/powerpc/include/arch-tests.h new file mode 100644 index 000000000000..84d8dedef2ed --- /dev/null +++ b/tools/perf/arch/powerpc/include/arch-tests.h | |||
@@ -0,0 +1,13 @@ | |||
1 | #ifndef ARCH_TESTS_H | ||
2 | #define ARCH_TESTS_H | ||
3 | |||
4 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
5 | struct thread; | ||
6 | struct perf_sample; | ||
7 | int test__arch_unwind_sample(struct perf_sample *sample, | ||
8 | struct thread *thread); | ||
9 | #endif | ||
10 | |||
11 | extern struct test arch_tests[]; | ||
12 | |||
13 | #endif | ||
diff --git a/tools/perf/arch/powerpc/include/perf_regs.h b/tools/perf/arch/powerpc/include/perf_regs.h index 75de0e92e71e..c12f4e804f66 100644 --- a/tools/perf/arch/powerpc/include/perf_regs.h +++ b/tools/perf/arch/powerpc/include/perf_regs.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/types.h> | 5 | #include <linux/types.h> |
6 | #include <asm/perf_regs.h> | 6 | #include <asm/perf_regs.h> |
7 | 7 | ||
8 | void perf_regs_load(u64 *regs); | ||
9 | |||
8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1) | 10 | #define PERF_REGS_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1) |
9 | #define PERF_REGS_MAX PERF_REG_POWERPC_MAX | 11 | #define PERF_REGS_MAX PERF_REG_POWERPC_MAX |
10 | #ifdef __powerpc64__ | 12 | #ifdef __powerpc64__ |
diff --git a/tools/perf/arch/powerpc/tests/Build b/tools/perf/arch/powerpc/tests/Build new file mode 100644 index 000000000000..d827ef384b33 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/Build | |||
@@ -0,0 +1,4 @@ | |||
1 | libperf-$(CONFIG_DWARF_UNWIND) += regs_load.o | ||
2 | libperf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o | ||
3 | |||
4 | libperf-y += arch-tests.o | ||
diff --git a/tools/perf/arch/powerpc/tests/arch-tests.c b/tools/perf/arch/powerpc/tests/arch-tests.c new file mode 100644 index 000000000000..e24f46241f40 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/arch-tests.c | |||
@@ -0,0 +1,15 @@ | |||
1 | #include <string.h> | ||
2 | #include "tests/tests.h" | ||
3 | #include "arch-tests.h" | ||
4 | |||
5 | struct test arch_tests[] = { | ||
6 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
7 | { | ||
8 | .desc = "Test dwarf unwind", | ||
9 | .func = test__dwarf_unwind, | ||
10 | }, | ||
11 | #endif | ||
12 | { | ||
13 | .func = NULL, | ||
14 | }, | ||
15 | }; | ||
diff --git a/tools/perf/arch/powerpc/tests/dwarf-unwind.c b/tools/perf/arch/powerpc/tests/dwarf-unwind.c new file mode 100644 index 000000000000..0bac3137ccbd --- /dev/null +++ b/tools/perf/arch/powerpc/tests/dwarf-unwind.c | |||
@@ -0,0 +1,62 @@ | |||
1 | #include <string.h> | ||
2 | #include "perf_regs.h" | ||
3 | #include "thread.h" | ||
4 | #include "map.h" | ||
5 | #include "event.h" | ||
6 | #include "debug.h" | ||
7 | #include "tests/tests.h" | ||
8 | #include "arch-tests.h" | ||
9 | |||
10 | #define STACK_SIZE 8192 | ||
11 | |||
12 | static int sample_ustack(struct perf_sample *sample, | ||
13 | struct thread *thread, u64 *regs) | ||
14 | { | ||
15 | struct stack_dump *stack = &sample->user_stack; | ||
16 | struct map *map; | ||
17 | unsigned long sp; | ||
18 | u64 stack_size, *buf; | ||
19 | |||
20 | buf = malloc(STACK_SIZE); | ||
21 | if (!buf) { | ||
22 | pr_debug("failed to allocate sample uregs data\n"); | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | sp = (unsigned long) regs[PERF_REG_POWERPC_R1]; | ||
27 | |||
28 | map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp); | ||
29 | if (!map) { | ||
30 | pr_debug("failed to get stack map\n"); | ||
31 | free(buf); | ||
32 | return -1; | ||
33 | } | ||
34 | |||
35 | stack_size = map->end - sp; | ||
36 | stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size; | ||
37 | |||
38 | memcpy(buf, (void *) sp, stack_size); | ||
39 | stack->data = (char *) buf; | ||
40 | stack->size = stack_size; | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | int test__arch_unwind_sample(struct perf_sample *sample, | ||
45 | struct thread *thread) | ||
46 | { | ||
47 | struct regs_dump *regs = &sample->user_regs; | ||
48 | u64 *buf; | ||
49 | |||
50 | buf = calloc(1, sizeof(u64) * PERF_REGS_MAX); | ||
51 | if (!buf) { | ||
52 | pr_debug("failed to allocate sample uregs data\n"); | ||
53 | return -1; | ||
54 | } | ||
55 | |||
56 | perf_regs_load(buf); | ||
57 | regs->abi = PERF_SAMPLE_REGS_ABI; | ||
58 | regs->regs = buf; | ||
59 | regs->mask = PERF_REGS_MASK; | ||
60 | |||
61 | return sample_ustack(sample, thread, buf); | ||
62 | } | ||
diff --git a/tools/perf/arch/powerpc/tests/regs_load.S b/tools/perf/arch/powerpc/tests/regs_load.S new file mode 100644 index 000000000000..d76c9a32f327 --- /dev/null +++ b/tools/perf/arch/powerpc/tests/regs_load.S | |||
@@ -0,0 +1,94 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | |||
3 | /* Offset is based on macros from arch/powerpc/include/uapi/asm/ptrace.h. */ | ||
4 | #define R0 0 | ||
5 | #define R1 1 * 8 | ||
6 | #define R2 2 * 8 | ||
7 | #define R3 3 * 8 | ||
8 | #define R4 4 * 8 | ||
9 | #define R5 5 * 8 | ||
10 | #define R6 6 * 8 | ||
11 | #define R7 7 * 8 | ||
12 | #define R8 8 * 8 | ||
13 | #define R9 9 * 8 | ||
14 | #define R10 10 * 8 | ||
15 | #define R11 11 * 8 | ||
16 | #define R12 12 * 8 | ||
17 | #define R13 13 * 8 | ||
18 | #define R14 14 * 8 | ||
19 | #define R15 15 * 8 | ||
20 | #define R16 16 * 8 | ||
21 | #define R17 17 * 8 | ||
22 | #define R18 18 * 8 | ||
23 | #define R19 19 * 8 | ||
24 | #define R20 20 * 8 | ||
25 | #define R21 21 * 8 | ||
26 | #define R22 22 * 8 | ||
27 | #define R23 23 * 8 | ||
28 | #define R24 24 * 8 | ||
29 | #define R25 25 * 8 | ||
30 | #define R26 26 * 8 | ||
31 | #define R27 27 * 8 | ||
32 | #define R28 28 * 8 | ||
33 | #define R29 29 * 8 | ||
34 | #define R30 30 * 8 | ||
35 | #define R31 31 * 8 | ||
36 | #define NIP 32 * 8 | ||
37 | #define CTR 35 * 8 | ||
38 | #define LINK 36 * 8 | ||
39 | #define XER 37 * 8 | ||
40 | |||
41 | .globl perf_regs_load | ||
42 | perf_regs_load: | ||
43 | std 0, R0(3) | ||
44 | std 1, R1(3) | ||
45 | std 2, R2(3) | ||
46 | std 3, R3(3) | ||
47 | std 4, R4(3) | ||
48 | std 5, R5(3) | ||
49 | std 6, R6(3) | ||
50 | std 7, R7(3) | ||
51 | std 8, R8(3) | ||
52 | std 9, R9(3) | ||
53 | std 10, R10(3) | ||
54 | std 11, R11(3) | ||
55 | std 12, R12(3) | ||
56 | std 13, R13(3) | ||
57 | std 14, R14(3) | ||
58 | std 15, R15(3) | ||
59 | std 16, R16(3) | ||
60 | std 17, R17(3) | ||
61 | std 18, R18(3) | ||
62 | std 19, R19(3) | ||
63 | std 20, R20(3) | ||
64 | std 21, R21(3) | ||
65 | std 22, R22(3) | ||
66 | std 23, R23(3) | ||
67 | std 24, R24(3) | ||
68 | std 25, R25(3) | ||
69 | std 26, R26(3) | ||
70 | std 27, R27(3) | ||
71 | std 28, R28(3) | ||
72 | std 29, R29(3) | ||
73 | std 30, R30(3) | ||
74 | std 31, R31(3) | ||
75 | |||
76 | /* store NIP */ | ||
77 | mflr 4 | ||
78 | std 4, NIP(3) | ||
79 | |||
80 | /* Store LR */ | ||
81 | std 4, LINK(3) | ||
82 | |||
83 | /* Store XER */ | ||
84 | mfxer 4 | ||
85 | std 4, XER(3) | ||
86 | |||
87 | /* Store CTR */ | ||
88 | mfctr 4 | ||
89 | std 4, CTR(3) | ||
90 | |||
91 | /* Restore original value of r4 */ | ||
92 | ld 4, R4(3) | ||
93 | |||
94 | blr | ||
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index a2412e9d883b..90fa2286edcf 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c | |||
@@ -62,6 +62,7 @@ struct intel_pt_recording { | |||
62 | size_t snapshot_ref_buf_size; | 62 | size_t snapshot_ref_buf_size; |
63 | int snapshot_ref_cnt; | 63 | int snapshot_ref_cnt; |
64 | struct intel_pt_snapshot_ref *snapshot_refs; | 64 | struct intel_pt_snapshot_ref *snapshot_refs; |
65 | size_t priv_size; | ||
65 | }; | 66 | }; |
66 | 67 | ||
67 | static int intel_pt_parse_terms_with_default(struct list_head *formats, | 68 | static int intel_pt_parse_terms_with_default(struct list_head *formats, |
@@ -273,11 +274,37 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) | |||
273 | return attr; | 274 | return attr; |
274 | } | 275 | } |
275 | 276 | ||
277 | static const char *intel_pt_find_filter(struct perf_evlist *evlist, | ||
278 | struct perf_pmu *intel_pt_pmu) | ||
279 | { | ||
280 | struct perf_evsel *evsel; | ||
281 | |||
282 | evlist__for_each_entry(evlist, evsel) { | ||
283 | if (evsel->attr.type == intel_pt_pmu->type) | ||
284 | return evsel->filter; | ||
285 | } | ||
286 | |||
287 | return NULL; | ||
288 | } | ||
289 | |||
290 | static size_t intel_pt_filter_bytes(const char *filter) | ||
291 | { | ||
292 | size_t len = filter ? strlen(filter) : 0; | ||
293 | |||
294 | return len ? roundup(len + 1, 8) : 0; | ||
295 | } | ||
296 | |||
276 | static size_t | 297 | static size_t |
277 | intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused, | 298 | intel_pt_info_priv_size(struct auxtrace_record *itr, struct perf_evlist *evlist) |
278 | struct perf_evlist *evlist __maybe_unused) | ||
279 | { | 299 | { |
280 | return INTEL_PT_AUXTRACE_PRIV_SIZE; | 300 | struct intel_pt_recording *ptr = |
301 | container_of(itr, struct intel_pt_recording, itr); | ||
302 | const char *filter = intel_pt_find_filter(evlist, ptr->intel_pt_pmu); | ||
303 | |||
304 | ptr->priv_size = (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) + | ||
305 | intel_pt_filter_bytes(filter); | ||
306 | |||
307 | return ptr->priv_size; | ||
281 | } | 308 | } |
282 | 309 | ||
283 | static void intel_pt_tsc_ctc_ratio(u32 *n, u32 *d) | 310 | static void intel_pt_tsc_ctc_ratio(u32 *n, u32 *d) |
@@ -302,9 +329,13 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, | |||
302 | bool cap_user_time_zero = false, per_cpu_mmaps; | 329 | bool cap_user_time_zero = false, per_cpu_mmaps; |
303 | u64 tsc_bit, mtc_bit, mtc_freq_bits, cyc_bit, noretcomp_bit; | 330 | u64 tsc_bit, mtc_bit, mtc_freq_bits, cyc_bit, noretcomp_bit; |
304 | u32 tsc_ctc_ratio_n, tsc_ctc_ratio_d; | 331 | u32 tsc_ctc_ratio_n, tsc_ctc_ratio_d; |
332 | unsigned long max_non_turbo_ratio; | ||
333 | size_t filter_str_len; | ||
334 | const char *filter; | ||
335 | u64 *info; | ||
305 | int err; | 336 | int err; |
306 | 337 | ||
307 | if (priv_size != INTEL_PT_AUXTRACE_PRIV_SIZE) | 338 | if (priv_size != ptr->priv_size) |
308 | return -EINVAL; | 339 | return -EINVAL; |
309 | 340 | ||
310 | intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit); | 341 | intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit); |
@@ -317,6 +348,13 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, | |||
317 | 348 | ||
318 | intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); | 349 | intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); |
319 | 350 | ||
351 | if (perf_pmu__scan_file(intel_pt_pmu, "max_nonturbo_ratio", | ||
352 | "%lu", &max_non_turbo_ratio) != 1) | ||
353 | max_non_turbo_ratio = 0; | ||
354 | |||
355 | filter = intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); | ||
356 | filter_str_len = filter ? strlen(filter) : 0; | ||
357 | |||
320 | if (!session->evlist->nr_mmaps) | 358 | if (!session->evlist->nr_mmaps) |
321 | return -EINVAL; | 359 | return -EINVAL; |
322 | 360 | ||
@@ -351,6 +389,17 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, | |||
351 | auxtrace_info->priv[INTEL_PT_TSC_CTC_N] = tsc_ctc_ratio_n; | 389 | auxtrace_info->priv[INTEL_PT_TSC_CTC_N] = tsc_ctc_ratio_n; |
352 | auxtrace_info->priv[INTEL_PT_TSC_CTC_D] = tsc_ctc_ratio_d; | 390 | auxtrace_info->priv[INTEL_PT_TSC_CTC_D] = tsc_ctc_ratio_d; |
353 | auxtrace_info->priv[INTEL_PT_CYC_BIT] = cyc_bit; | 391 | auxtrace_info->priv[INTEL_PT_CYC_BIT] = cyc_bit; |
392 | auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO] = max_non_turbo_ratio; | ||
393 | auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] = filter_str_len; | ||
394 | |||
395 | info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; | ||
396 | |||
397 | if (filter_str_len) { | ||
398 | size_t len = intel_pt_filter_bytes(filter); | ||
399 | |||
400 | strncpy((char *)info, filter, len); | ||
401 | info += len >> 3; | ||
402 | } | ||
354 | 403 | ||
355 | return 0; | 404 | return 0; |
356 | } | 405 | } |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2d0d69be3bf8..67d2a9003294 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -1573,29 +1573,39 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1573 | if (!rec->itr) { | 1573 | if (!rec->itr) { |
1574 | rec->itr = auxtrace_record__init(rec->evlist, &err); | 1574 | rec->itr = auxtrace_record__init(rec->evlist, &err); |
1575 | if (err) | 1575 | if (err) |
1576 | return err; | 1576 | goto out; |
1577 | } | 1577 | } |
1578 | 1578 | ||
1579 | err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, | 1579 | err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts, |
1580 | rec->opts.auxtrace_snapshot_opts); | 1580 | rec->opts.auxtrace_snapshot_opts); |
1581 | if (err) | 1581 | if (err) |
1582 | return err; | 1582 | goto out; |
1583 | |||
1584 | /* | ||
1585 | * Allow aliases to facilitate the lookup of symbols for address | ||
1586 | * filters. Refer to auxtrace_parse_filters(). | ||
1587 | */ | ||
1588 | symbol_conf.allow_aliases = true; | ||
1589 | |||
1590 | symbol__init(NULL); | ||
1591 | |||
1592 | err = auxtrace_parse_filters(rec->evlist); | ||
1593 | if (err) | ||
1594 | goto out; | ||
1583 | 1595 | ||
1584 | if (dry_run) | 1596 | if (dry_run) |
1585 | return 0; | 1597 | goto out; |
1586 | 1598 | ||
1587 | err = bpf__setup_stdout(rec->evlist); | 1599 | err = bpf__setup_stdout(rec->evlist); |
1588 | if (err) { | 1600 | if (err) { |
1589 | bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); | 1601 | bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf)); |
1590 | pr_err("ERROR: Setup BPF stdout failed: %s\n", | 1602 | pr_err("ERROR: Setup BPF stdout failed: %s\n", |
1591 | errbuf); | 1603 | errbuf); |
1592 | return err; | 1604 | goto out; |
1593 | } | 1605 | } |
1594 | 1606 | ||
1595 | err = -ENOMEM; | 1607 | err = -ENOMEM; |
1596 | 1608 | ||
1597 | symbol__init(NULL); | ||
1598 | |||
1599 | if (symbol_conf.kptr_restrict) | 1609 | if (symbol_conf.kptr_restrict) |
1600 | pr_warning( | 1610 | pr_warning( |
1601 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" | 1611 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" |
@@ -1643,7 +1653,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1643 | if (rec->evlist->nr_entries == 0 && | 1653 | if (rec->evlist->nr_entries == 0 && |
1644 | perf_evlist__add_default(rec->evlist) < 0) { | 1654 | perf_evlist__add_default(rec->evlist) < 0) { |
1645 | pr_err("Not enough memory for event selector list\n"); | 1655 | pr_err("Not enough memory for event selector list\n"); |
1646 | goto out_symbol_exit; | 1656 | goto out; |
1647 | } | 1657 | } |
1648 | 1658 | ||
1649 | if (rec->opts.target.tid && !rec->opts.no_inherit_set) | 1659 | if (rec->opts.target.tid && !rec->opts.no_inherit_set) |
@@ -1663,7 +1673,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1663 | ui__error("%s", errbuf); | 1673 | ui__error("%s", errbuf); |
1664 | 1674 | ||
1665 | err = -saved_errno; | 1675 | err = -saved_errno; |
1666 | goto out_symbol_exit; | 1676 | goto out; |
1667 | } | 1677 | } |
1668 | 1678 | ||
1669 | err = -ENOMEM; | 1679 | err = -ENOMEM; |
@@ -1672,7 +1682,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1672 | 1682 | ||
1673 | err = auxtrace_record__options(rec->itr, rec->evlist, &rec->opts); | 1683 | err = auxtrace_record__options(rec->itr, rec->evlist, &rec->opts); |
1674 | if (err) | 1684 | if (err) |
1675 | goto out_symbol_exit; | 1685 | goto out; |
1676 | 1686 | ||
1677 | /* | 1687 | /* |
1678 | * We take all buildids when the file contains | 1688 | * We take all buildids when the file contains |
@@ -1684,11 +1694,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1684 | 1694 | ||
1685 | if (record_opts__config(&rec->opts)) { | 1695 | if (record_opts__config(&rec->opts)) { |
1686 | err = -EINVAL; | 1696 | err = -EINVAL; |
1687 | goto out_symbol_exit; | 1697 | goto out; |
1688 | } | 1698 | } |
1689 | 1699 | ||
1690 | err = __cmd_record(&record, argc, argv); | 1700 | err = __cmd_record(&record, argc, argv); |
1691 | out_symbol_exit: | 1701 | out: |
1692 | perf_evlist__delete(rec->evlist); | 1702 | perf_evlist__delete(rec->evlist); |
1693 | symbol__exit(); | 1703 | symbol__exit(); |
1694 | auxtrace_record__free(rec->itr); | 1704 | auxtrace_record__free(rec->itr); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index b4fc1ab3d2a7..c298bd3e1d90 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -742,6 +742,8 @@ static struct syscall_fmt { | |||
742 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, | 742 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, |
743 | { .name = "rt_tgsigqueueinfo", .errmsg = true, | 743 | { .name = "rt_tgsigqueueinfo", .errmsg = true, |
744 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, | 744 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, |
745 | { .name = "sched_getattr", .errmsg = true, }, | ||
746 | { .name = "sched_setattr", .errmsg = true, }, | ||
745 | { .name = "sched_setscheduler", .errmsg = true, | 747 | { .name = "sched_setscheduler", .errmsg = true, |
746 | .arg_scnprintf = { [1] = SCA_SCHED_POLICY, /* policy */ }, }, | 748 | .arg_scnprintf = { [1] = SCA_SCHED_POLICY, /* policy */ }, }, |
747 | { .name = "seccomp", .errmsg = true, | 749 | { .name = "seccomp", .errmsg = true, |
@@ -2141,6 +2143,7 @@ out_delete_sys_enter: | |||
2141 | static int trace__set_ev_qualifier_filter(struct trace *trace) | 2143 | static int trace__set_ev_qualifier_filter(struct trace *trace) |
2142 | { | 2144 | { |
2143 | int err = -1; | 2145 | int err = -1; |
2146 | struct perf_evsel *sys_exit; | ||
2144 | char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier, | 2147 | char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier, |
2145 | trace->ev_qualifier_ids.nr, | 2148 | trace->ev_qualifier_ids.nr, |
2146 | trace->ev_qualifier_ids.entries); | 2149 | trace->ev_qualifier_ids.entries); |
@@ -2148,8 +2151,11 @@ static int trace__set_ev_qualifier_filter(struct trace *trace) | |||
2148 | if (filter == NULL) | 2151 | if (filter == NULL) |
2149 | goto out_enomem; | 2152 | goto out_enomem; |
2150 | 2153 | ||
2151 | if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, "&&", filter)) | 2154 | if (!perf_evsel__append_tp_filter(trace->syscalls.events.sys_enter, |
2152 | err = perf_evsel__append_filter(trace->syscalls.events.sys_exit, "&&", filter); | 2155 | filter)) { |
2156 | sys_exit = trace->syscalls.events.sys_exit; | ||
2157 | err = perf_evsel__append_tp_filter(sys_exit, filter); | ||
2158 | } | ||
2153 | 2159 | ||
2154 | free(filter); | 2160 | free(filter); |
2155 | out: | 2161 | out: |
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index dc51bc570e51..8a4ce492f7b2 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -71,7 +71,7 @@ $(OUTPUT)tests/llvm-src-relocation.c: tests/bpf-script-test-relocation.c tests/B | |||
71 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ | 71 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ |
72 | $(Q)echo ';' >> $@ | 72 | $(Q)echo ';' >> $@ |
73 | 73 | ||
74 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) | 74 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64 powerpc)) |
75 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o | 75 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o |
76 | endif | 76 | endif |
77 | 77 | ||
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 8f6eb853aaf7..1046491de4b2 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -11,7 +11,7 @@ | |||
11 | #include "thread.h" | 11 | #include "thread.h" |
12 | #include "callchain.h" | 12 | #include "callchain.h" |
13 | 13 | ||
14 | #if defined (__x86_64__) || defined (__i386__) | 14 | #if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__) |
15 | #include "arch-tests.h" | 15 | #include "arch-tests.h" |
16 | #endif | 16 | #endif |
17 | 17 | ||
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index c0aba8e839aa..c5a6e0b12452 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c | |||
@@ -16,6 +16,10 @@ | |||
16 | #include <sys/types.h> | 16 | #include <sys/types.h> |
17 | #include <sys/mman.h> | 17 | #include <sys/mman.h> |
18 | #include <stdbool.h> | 18 | #include <stdbool.h> |
19 | #include <ctype.h> | ||
20 | #include <string.h> | ||
21 | #include <limits.h> | ||
22 | #include <errno.h> | ||
19 | 23 | ||
20 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
21 | #include <linux/perf_event.h> | 25 | #include <linux/perf_event.h> |
@@ -35,9 +39,14 @@ | |||
35 | #include "../perf.h" | 39 | #include "../perf.h" |
36 | #include "util.h" | 40 | #include "util.h" |
37 | #include "evlist.h" | 41 | #include "evlist.h" |
42 | #include "dso.h" | ||
43 | #include "map.h" | ||
44 | #include "pmu.h" | ||
45 | #include "evsel.h" | ||
38 | #include "cpumap.h" | 46 | #include "cpumap.h" |
39 | #include "thread_map.h" | 47 | #include "thread_map.h" |
40 | #include "asm/bug.h" | 48 | #include "asm/bug.h" |
49 | #include "symbol/kallsyms.h" | ||
41 | #include "auxtrace.h" | 50 | #include "auxtrace.h" |
42 | 51 | ||
43 | #include <linux/hash.h> | 52 | #include <linux/hash.h> |
@@ -1399,3 +1408,731 @@ void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key) | |||
1399 | 1408 | ||
1400 | return NULL; | 1409 | return NULL; |
1401 | } | 1410 | } |
1411 | |||
1412 | static void addr_filter__free_str(struct addr_filter *filt) | ||
1413 | { | ||
1414 | free(filt->str); | ||
1415 | filt->action = NULL; | ||
1416 | filt->sym_from = NULL; | ||
1417 | filt->sym_to = NULL; | ||
1418 | filt->filename = NULL; | ||
1419 | filt->str = NULL; | ||
1420 | } | ||
1421 | |||
1422 | static struct addr_filter *addr_filter__new(void) | ||
1423 | { | ||
1424 | struct addr_filter *filt = zalloc(sizeof(*filt)); | ||
1425 | |||
1426 | if (filt) | ||
1427 | INIT_LIST_HEAD(&filt->list); | ||
1428 | |||
1429 | return filt; | ||
1430 | } | ||
1431 | |||
1432 | static void addr_filter__free(struct addr_filter *filt) | ||
1433 | { | ||
1434 | if (filt) | ||
1435 | addr_filter__free_str(filt); | ||
1436 | free(filt); | ||
1437 | } | ||
1438 | |||
1439 | static void addr_filters__add(struct addr_filters *filts, | ||
1440 | struct addr_filter *filt) | ||
1441 | { | ||
1442 | list_add_tail(&filt->list, &filts->head); | ||
1443 | filts->cnt += 1; | ||
1444 | } | ||
1445 | |||
1446 | static void addr_filters__del(struct addr_filters *filts, | ||
1447 | struct addr_filter *filt) | ||
1448 | { | ||
1449 | list_del_init(&filt->list); | ||
1450 | filts->cnt -= 1; | ||
1451 | } | ||
1452 | |||
1453 | void addr_filters__init(struct addr_filters *filts) | ||
1454 | { | ||
1455 | INIT_LIST_HEAD(&filts->head); | ||
1456 | filts->cnt = 0; | ||
1457 | } | ||
1458 | |||
1459 | void addr_filters__exit(struct addr_filters *filts) | ||
1460 | { | ||
1461 | struct addr_filter *filt, *n; | ||
1462 | |||
1463 | list_for_each_entry_safe(filt, n, &filts->head, list) { | ||
1464 | addr_filters__del(filts, filt); | ||
1465 | addr_filter__free(filt); | ||
1466 | } | ||
1467 | } | ||
1468 | |||
1469 | static int parse_num_or_str(char **inp, u64 *num, const char **str, | ||
1470 | const char *str_delim) | ||
1471 | { | ||
1472 | *inp += strspn(*inp, " "); | ||
1473 | |||
1474 | if (isdigit(**inp)) { | ||
1475 | char *endptr; | ||
1476 | |||
1477 | if (!num) | ||
1478 | return -EINVAL; | ||
1479 | errno = 0; | ||
1480 | *num = strtoull(*inp, &endptr, 0); | ||
1481 | if (errno) | ||
1482 | return -errno; | ||
1483 | if (endptr == *inp) | ||
1484 | return -EINVAL; | ||
1485 | *inp = endptr; | ||
1486 | } else { | ||
1487 | size_t n; | ||
1488 | |||
1489 | if (!str) | ||
1490 | return -EINVAL; | ||
1491 | *inp += strspn(*inp, " "); | ||
1492 | *str = *inp; | ||
1493 | n = strcspn(*inp, str_delim); | ||
1494 | if (!n) | ||
1495 | return -EINVAL; | ||
1496 | *inp += n; | ||
1497 | if (**inp) { | ||
1498 | **inp = '\0'; | ||
1499 | *inp += 1; | ||
1500 | } | ||
1501 | } | ||
1502 | return 0; | ||
1503 | } | ||
1504 | |||
1505 | static int parse_action(struct addr_filter *filt) | ||
1506 | { | ||
1507 | if (!strcmp(filt->action, "filter")) { | ||
1508 | filt->start = true; | ||
1509 | filt->range = true; | ||
1510 | } else if (!strcmp(filt->action, "start")) { | ||
1511 | filt->start = true; | ||
1512 | } else if (!strcmp(filt->action, "stop")) { | ||
1513 | filt->start = false; | ||
1514 | } else if (!strcmp(filt->action, "tracestop")) { | ||
1515 | filt->start = false; | ||
1516 | filt->range = true; | ||
1517 | filt->action += 5; /* Change 'tracestop' to 'stop' */ | ||
1518 | } else { | ||
1519 | return -EINVAL; | ||
1520 | } | ||
1521 | return 0; | ||
1522 | } | ||
1523 | |||
1524 | static int parse_sym_idx(char **inp, int *idx) | ||
1525 | { | ||
1526 | *idx = -1; | ||
1527 | |||
1528 | *inp += strspn(*inp, " "); | ||
1529 | |||
1530 | if (**inp != '#') | ||
1531 | return 0; | ||
1532 | |||
1533 | *inp += 1; | ||
1534 | |||
1535 | if (**inp == 'g' || **inp == 'G') { | ||
1536 | *inp += 1; | ||
1537 | *idx = 0; | ||
1538 | } else { | ||
1539 | unsigned long num; | ||
1540 | char *endptr; | ||
1541 | |||
1542 | errno = 0; | ||
1543 | num = strtoul(*inp, &endptr, 0); | ||
1544 | if (errno) | ||
1545 | return -errno; | ||
1546 | if (endptr == *inp || num > INT_MAX) | ||
1547 | return -EINVAL; | ||
1548 | *inp = endptr; | ||
1549 | *idx = num; | ||
1550 | } | ||
1551 | |||
1552 | return 0; | ||
1553 | } | ||
1554 | |||
1555 | static int parse_addr_size(char **inp, u64 *num, const char **str, int *idx) | ||
1556 | { | ||
1557 | int err = parse_num_or_str(inp, num, str, " "); | ||
1558 | |||
1559 | if (!err && *str) | ||
1560 | err = parse_sym_idx(inp, idx); | ||
1561 | |||
1562 | return err; | ||
1563 | } | ||
1564 | |||
1565 | static int parse_one_filter(struct addr_filter *filt, const char **filter_inp) | ||
1566 | { | ||
1567 | char *fstr; | ||
1568 | int err; | ||
1569 | |||
1570 | filt->str = fstr = strdup(*filter_inp); | ||
1571 | if (!fstr) | ||
1572 | return -ENOMEM; | ||
1573 | |||
1574 | err = parse_num_or_str(&fstr, NULL, &filt->action, " "); | ||
1575 | if (err) | ||
1576 | goto out_err; | ||
1577 | |||
1578 | err = parse_action(filt); | ||
1579 | if (err) | ||
1580 | goto out_err; | ||
1581 | |||
1582 | err = parse_addr_size(&fstr, &filt->addr, &filt->sym_from, | ||
1583 | &filt->sym_from_idx); | ||
1584 | if (err) | ||
1585 | goto out_err; | ||
1586 | |||
1587 | fstr += strspn(fstr, " "); | ||
1588 | |||
1589 | if (*fstr == '/') { | ||
1590 | fstr += 1; | ||
1591 | err = parse_addr_size(&fstr, &filt->size, &filt->sym_to, | ||
1592 | &filt->sym_to_idx); | ||
1593 | if (err) | ||
1594 | goto out_err; | ||
1595 | filt->range = true; | ||
1596 | } | ||
1597 | |||
1598 | fstr += strspn(fstr, " "); | ||
1599 | |||
1600 | if (*fstr == '@') { | ||
1601 | fstr += 1; | ||
1602 | err = parse_num_or_str(&fstr, NULL, &filt->filename, " ,"); | ||
1603 | if (err) | ||
1604 | goto out_err; | ||
1605 | } | ||
1606 | |||
1607 | fstr += strspn(fstr, " ,"); | ||
1608 | |||
1609 | *filter_inp += fstr - filt->str; | ||
1610 | |||
1611 | return 0; | ||
1612 | |||
1613 | out_err: | ||
1614 | addr_filter__free_str(filt); | ||
1615 | |||
1616 | return err; | ||
1617 | } | ||
1618 | |||
1619 | int addr_filters__parse_bare_filter(struct addr_filters *filts, | ||
1620 | const char *filter) | ||
1621 | { | ||
1622 | struct addr_filter *filt; | ||
1623 | const char *fstr = filter; | ||
1624 | int err; | ||
1625 | |||
1626 | while (*fstr) { | ||
1627 | filt = addr_filter__new(); | ||
1628 | err = parse_one_filter(filt, &fstr); | ||
1629 | if (err) { | ||
1630 | addr_filter__free(filt); | ||
1631 | addr_filters__exit(filts); | ||
1632 | return err; | ||
1633 | } | ||
1634 | addr_filters__add(filts, filt); | ||
1635 | } | ||
1636 | |||
1637 | return 0; | ||
1638 | } | ||
1639 | |||
1640 | struct sym_args { | ||
1641 | const char *name; | ||
1642 | u64 start; | ||
1643 | u64 size; | ||
1644 | int idx; | ||
1645 | int cnt; | ||
1646 | bool started; | ||
1647 | bool global; | ||
1648 | bool selected; | ||
1649 | bool duplicate; | ||
1650 | bool near; | ||
1651 | }; | ||
1652 | |||
1653 | static bool kern_sym_match(struct sym_args *args, const char *name, char type) | ||
1654 | { | ||
1655 | /* A function with the same name, and global or the n'th found or any */ | ||
1656 | return symbol_type__is_a(type, MAP__FUNCTION) && | ||
1657 | !strcmp(name, args->name) && | ||
1658 | ((args->global && isupper(type)) || | ||
1659 | (args->selected && ++(args->cnt) == args->idx) || | ||
1660 | (!args->global && !args->selected)); | ||
1661 | } | ||
1662 | |||
1663 | static int find_kern_sym_cb(void *arg, const char *name, char type, u64 start) | ||
1664 | { | ||
1665 | struct sym_args *args = arg; | ||
1666 | |||
1667 | if (args->started) { | ||
1668 | if (!args->size) | ||
1669 | args->size = start - args->start; | ||
1670 | if (args->selected) { | ||
1671 | if (args->size) | ||
1672 | return 1; | ||
1673 | } else if (kern_sym_match(args, name, type)) { | ||
1674 | args->duplicate = true; | ||
1675 | return 1; | ||
1676 | } | ||
1677 | } else if (kern_sym_match(args, name, type)) { | ||
1678 | args->started = true; | ||
1679 | args->start = start; | ||
1680 | } | ||
1681 | |||
1682 | return 0; | ||
1683 | } | ||
1684 | |||
1685 | static int print_kern_sym_cb(void *arg, const char *name, char type, u64 start) | ||
1686 | { | ||
1687 | struct sym_args *args = arg; | ||
1688 | |||
1689 | if (kern_sym_match(args, name, type)) { | ||
1690 | pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", | ||
1691 | ++args->cnt, start, type, name); | ||
1692 | args->near = true; | ||
1693 | } else if (args->near) { | ||
1694 | args->near = false; | ||
1695 | pr_err("\t\twhich is near\t\t%s\n", name); | ||
1696 | } | ||
1697 | |||
1698 | return 0; | ||
1699 | } | ||
1700 | |||
1701 | static int sym_not_found_error(const char *sym_name, int idx) | ||
1702 | { | ||
1703 | if (idx > 0) { | ||
1704 | pr_err("N'th occurrence (N=%d) of symbol '%s' not found.\n", | ||
1705 | idx, sym_name); | ||
1706 | } else if (!idx) { | ||
1707 | pr_err("Global symbol '%s' not found.\n", sym_name); | ||
1708 | } else { | ||
1709 | pr_err("Symbol '%s' not found.\n", sym_name); | ||
1710 | } | ||
1711 | pr_err("Note that symbols must be functions.\n"); | ||
1712 | |||
1713 | return -EINVAL; | ||
1714 | } | ||
1715 | |||
1716 | static int find_kern_sym(const char *sym_name, u64 *start, u64 *size, int idx) | ||
1717 | { | ||
1718 | struct sym_args args = { | ||
1719 | .name = sym_name, | ||
1720 | .idx = idx, | ||
1721 | .global = !idx, | ||
1722 | .selected = idx > 0, | ||
1723 | }; | ||
1724 | int err; | ||
1725 | |||
1726 | *start = 0; | ||
1727 | *size = 0; | ||
1728 | |||
1729 | err = kallsyms__parse("/proc/kallsyms", &args, find_kern_sym_cb); | ||
1730 | if (err < 0) { | ||
1731 | pr_err("Failed to parse /proc/kallsyms\n"); | ||
1732 | return err; | ||
1733 | } | ||
1734 | |||
1735 | if (args.duplicate) { | ||
1736 | pr_err("Multiple kernel symbols with name '%s'\n", sym_name); | ||
1737 | args.cnt = 0; | ||
1738 | kallsyms__parse("/proc/kallsyms", &args, print_kern_sym_cb); | ||
1739 | pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", | ||
1740 | sym_name); | ||
1741 | pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); | ||
1742 | return -EINVAL; | ||
1743 | } | ||
1744 | |||
1745 | if (!args.started) { | ||
1746 | pr_err("Kernel symbol lookup: "); | ||
1747 | return sym_not_found_error(sym_name, idx); | ||
1748 | } | ||
1749 | |||
1750 | *start = args.start; | ||
1751 | *size = args.size; | ||
1752 | |||
1753 | return 0; | ||
1754 | } | ||
1755 | |||
1756 | static int find_entire_kern_cb(void *arg, const char *name __maybe_unused, | ||
1757 | char type, u64 start) | ||
1758 | { | ||
1759 | struct sym_args *args = arg; | ||
1760 | |||
1761 | if (!symbol_type__is_a(type, MAP__FUNCTION)) | ||
1762 | return 0; | ||
1763 | |||
1764 | if (!args->started) { | ||
1765 | args->started = true; | ||
1766 | args->start = start; | ||
1767 | } | ||
1768 | /* Don't know exactly where the kernel ends, so we add a page */ | ||
1769 | args->size = round_up(start, page_size) + page_size - args->start; | ||
1770 | |||
1771 | return 0; | ||
1772 | } | ||
1773 | |||
1774 | static int addr_filter__entire_kernel(struct addr_filter *filt) | ||
1775 | { | ||
1776 | struct sym_args args = { .started = false }; | ||
1777 | int err; | ||
1778 | |||
1779 | err = kallsyms__parse("/proc/kallsyms", &args, find_entire_kern_cb); | ||
1780 | if (err < 0 || !args.started) { | ||
1781 | pr_err("Failed to parse /proc/kallsyms\n"); | ||
1782 | return err; | ||
1783 | } | ||
1784 | |||
1785 | filt->addr = args.start; | ||
1786 | filt->size = args.size; | ||
1787 | |||
1788 | return 0; | ||
1789 | } | ||
1790 | |||
1791 | static int check_end_after_start(struct addr_filter *filt, u64 start, u64 size) | ||
1792 | { | ||
1793 | if (start + size >= filt->addr) | ||
1794 | return 0; | ||
1795 | |||
1796 | if (filt->sym_from) { | ||
1797 | pr_err("Symbol '%s' (0x%"PRIx64") comes before '%s' (0x%"PRIx64")\n", | ||
1798 | filt->sym_to, start, filt->sym_from, filt->addr); | ||
1799 | } else { | ||
1800 | pr_err("Symbol '%s' (0x%"PRIx64") comes before address 0x%"PRIx64")\n", | ||
1801 | filt->sym_to, start, filt->addr); | ||
1802 | } | ||
1803 | |||
1804 | return -EINVAL; | ||
1805 | } | ||
1806 | |||
1807 | static int addr_filter__resolve_kernel_syms(struct addr_filter *filt) | ||
1808 | { | ||
1809 | bool no_size = false; | ||
1810 | u64 start, size; | ||
1811 | int err; | ||
1812 | |||
1813 | if (symbol_conf.kptr_restrict) { | ||
1814 | pr_err("Kernel addresses are restricted. Unable to resolve kernel symbols.\n"); | ||
1815 | return -EINVAL; | ||
1816 | } | ||
1817 | |||
1818 | if (filt->sym_from && !strcmp(filt->sym_from, "*")) | ||
1819 | return addr_filter__entire_kernel(filt); | ||
1820 | |||
1821 | if (filt->sym_from) { | ||
1822 | err = find_kern_sym(filt->sym_from, &start, &size, | ||
1823 | filt->sym_from_idx); | ||
1824 | if (err) | ||
1825 | return err; | ||
1826 | filt->addr = start; | ||
1827 | if (filt->range && !filt->size && !filt->sym_to) { | ||
1828 | filt->size = size; | ||
1829 | no_size = !!size; | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | if (filt->sym_to) { | ||
1834 | err = find_kern_sym(filt->sym_to, &start, &size, | ||
1835 | filt->sym_to_idx); | ||
1836 | if (err) | ||
1837 | return err; | ||
1838 | |||
1839 | err = check_end_after_start(filt, start, size); | ||
1840 | if (err) | ||
1841 | return err; | ||
1842 | filt->size = start + size - filt->addr; | ||
1843 | no_size = !!size; | ||
1844 | } | ||
1845 | |||
1846 | /* The very last symbol in kallsyms does not imply a particular size */ | ||
1847 | if (no_size) { | ||
1848 | pr_err("Cannot determine size of symbol '%s'\n", | ||
1849 | filt->sym_to ? filt->sym_to : filt->sym_from); | ||
1850 | return -EINVAL; | ||
1851 | } | ||
1852 | |||
1853 | return 0; | ||
1854 | } | ||
1855 | |||
1856 | static struct dso *load_dso(const char *name) | ||
1857 | { | ||
1858 | struct map *map; | ||
1859 | struct dso *dso; | ||
1860 | |||
1861 | map = dso__new_map(name); | ||
1862 | if (!map) | ||
1863 | return NULL; | ||
1864 | |||
1865 | map__load(map); | ||
1866 | |||
1867 | dso = dso__get(map->dso); | ||
1868 | |||
1869 | map__put(map); | ||
1870 | |||
1871 | return dso; | ||
1872 | } | ||
1873 | |||
1874 | static bool dso_sym_match(struct symbol *sym, const char *name, int *cnt, | ||
1875 | int idx) | ||
1876 | { | ||
1877 | /* Same name, and global or the n'th found or any */ | ||
1878 | return !arch__compare_symbol_names(name, sym->name) && | ||
1879 | ((!idx && sym->binding == STB_GLOBAL) || | ||
1880 | (idx > 0 && ++*cnt == idx) || | ||
1881 | idx < 0); | ||
1882 | } | ||
1883 | |||
1884 | static void print_duplicate_syms(struct dso *dso, const char *sym_name) | ||
1885 | { | ||
1886 | struct symbol *sym; | ||
1887 | bool near = false; | ||
1888 | int cnt = 0; | ||
1889 | |||
1890 | pr_err("Multiple symbols with name '%s'\n", sym_name); | ||
1891 | |||
1892 | sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1893 | while (sym) { | ||
1894 | if (dso_sym_match(sym, sym_name, &cnt, -1)) { | ||
1895 | pr_err("#%d\t0x%"PRIx64"\t%c\t%s\n", | ||
1896 | ++cnt, sym->start, | ||
1897 | sym->binding == STB_GLOBAL ? 'g' : | ||
1898 | sym->binding == STB_LOCAL ? 'l' : 'w', | ||
1899 | sym->name); | ||
1900 | near = true; | ||
1901 | } else if (near) { | ||
1902 | near = false; | ||
1903 | pr_err("\t\twhich is near\t\t%s\n", sym->name); | ||
1904 | } | ||
1905 | sym = dso__next_symbol(sym); | ||
1906 | } | ||
1907 | |||
1908 | pr_err("Disambiguate symbol name by inserting #n after the name e.g. %s #2\n", | ||
1909 | sym_name); | ||
1910 | pr_err("Or select a global symbol by inserting #0 or #g or #G\n"); | ||
1911 | } | ||
1912 | |||
1913 | static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start, | ||
1914 | u64 *size, int idx) | ||
1915 | { | ||
1916 | struct symbol *sym; | ||
1917 | int cnt = 0; | ||
1918 | |||
1919 | *start = 0; | ||
1920 | *size = 0; | ||
1921 | |||
1922 | sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1923 | while (sym) { | ||
1924 | if (*start) { | ||
1925 | if (!*size) | ||
1926 | *size = sym->start - *start; | ||
1927 | if (idx > 0) { | ||
1928 | if (*size) | ||
1929 | return 1; | ||
1930 | } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { | ||
1931 | print_duplicate_syms(dso, sym_name); | ||
1932 | return -EINVAL; | ||
1933 | } | ||
1934 | } else if (dso_sym_match(sym, sym_name, &cnt, idx)) { | ||
1935 | *start = sym->start; | ||
1936 | *size = sym->end - sym->start; | ||
1937 | } | ||
1938 | sym = dso__next_symbol(sym); | ||
1939 | } | ||
1940 | |||
1941 | if (!*start) | ||
1942 | return sym_not_found_error(sym_name, idx); | ||
1943 | |||
1944 | return 0; | ||
1945 | } | ||
1946 | |||
1947 | static int addr_filter__entire_dso(struct addr_filter *filt, struct dso *dso) | ||
1948 | { | ||
1949 | struct symbol *first_sym = dso__first_symbol(dso, MAP__FUNCTION); | ||
1950 | struct symbol *last_sym = dso__last_symbol(dso, MAP__FUNCTION); | ||
1951 | |||
1952 | if (!first_sym || !last_sym) { | ||
1953 | pr_err("Failed to determine filter for %s\nNo symbols found.\n", | ||
1954 | filt->filename); | ||
1955 | return -EINVAL; | ||
1956 | } | ||
1957 | |||
1958 | filt->addr = first_sym->start; | ||
1959 | filt->size = last_sym->end - first_sym->start; | ||
1960 | |||
1961 | return 0; | ||
1962 | } | ||
1963 | |||
1964 | static int addr_filter__resolve_syms(struct addr_filter *filt) | ||
1965 | { | ||
1966 | u64 start, size; | ||
1967 | struct dso *dso; | ||
1968 | int err = 0; | ||
1969 | |||
1970 | if (!filt->sym_from && !filt->sym_to) | ||
1971 | return 0; | ||
1972 | |||
1973 | if (!filt->filename) | ||
1974 | return addr_filter__resolve_kernel_syms(filt); | ||
1975 | |||
1976 | dso = load_dso(filt->filename); | ||
1977 | if (!dso) { | ||
1978 | pr_err("Failed to load symbols from: %s\n", filt->filename); | ||
1979 | return -EINVAL; | ||
1980 | } | ||
1981 | |||
1982 | if (filt->sym_from && !strcmp(filt->sym_from, "*")) { | ||
1983 | err = addr_filter__entire_dso(filt, dso); | ||
1984 | goto put_dso; | ||
1985 | } | ||
1986 | |||
1987 | if (filt->sym_from) { | ||
1988 | err = find_dso_sym(dso, filt->sym_from, &start, &size, | ||
1989 | filt->sym_from_idx); | ||
1990 | if (err) | ||
1991 | goto put_dso; | ||
1992 | filt->addr = start; | ||
1993 | if (filt->range && !filt->size && !filt->sym_to) | ||
1994 | filt->size = size; | ||
1995 | } | ||
1996 | |||
1997 | if (filt->sym_to) { | ||
1998 | err = find_dso_sym(dso, filt->sym_to, &start, &size, | ||
1999 | filt->sym_to_idx); | ||
2000 | if (err) | ||
2001 | goto put_dso; | ||
2002 | |||
2003 | err = check_end_after_start(filt, start, size); | ||
2004 | if (err) | ||
2005 | return err; | ||
2006 | |||
2007 | filt->size = start + size - filt->addr; | ||
2008 | } | ||
2009 | |||
2010 | put_dso: | ||
2011 | dso__put(dso); | ||
2012 | |||
2013 | return err; | ||
2014 | } | ||
2015 | |||
2016 | static char *addr_filter__to_str(struct addr_filter *filt) | ||
2017 | { | ||
2018 | char filename_buf[PATH_MAX]; | ||
2019 | const char *at = ""; | ||
2020 | const char *fn = ""; | ||
2021 | char *filter; | ||
2022 | int err; | ||
2023 | |||
2024 | if (filt->filename) { | ||
2025 | at = "@"; | ||
2026 | fn = realpath(filt->filename, filename_buf); | ||
2027 | if (!fn) | ||
2028 | return NULL; | ||
2029 | } | ||
2030 | |||
2031 | if (filt->range) { | ||
2032 | err = asprintf(&filter, "%s 0x%"PRIx64"/0x%"PRIx64"%s%s", | ||
2033 | filt->action, filt->addr, filt->size, at, fn); | ||
2034 | } else { | ||
2035 | err = asprintf(&filter, "%s 0x%"PRIx64"%s%s", | ||
2036 | filt->action, filt->addr, at, fn); | ||
2037 | } | ||
2038 | |||
2039 | return err < 0 ? NULL : filter; | ||
2040 | } | ||
2041 | |||
2042 | static int parse_addr_filter(struct perf_evsel *evsel, const char *filter, | ||
2043 | int max_nr) | ||
2044 | { | ||
2045 | struct addr_filters filts; | ||
2046 | struct addr_filter *filt; | ||
2047 | int err; | ||
2048 | |||
2049 | addr_filters__init(&filts); | ||
2050 | |||
2051 | err = addr_filters__parse_bare_filter(&filts, filter); | ||
2052 | if (err) | ||
2053 | goto out_exit; | ||
2054 | |||
2055 | if (filts.cnt > max_nr) { | ||
2056 | pr_err("Error: number of address filters (%d) exceeds maximum (%d)\n", | ||
2057 | filts.cnt, max_nr); | ||
2058 | err = -EINVAL; | ||
2059 | goto out_exit; | ||
2060 | } | ||
2061 | |||
2062 | list_for_each_entry(filt, &filts.head, list) { | ||
2063 | char *new_filter; | ||
2064 | |||
2065 | err = addr_filter__resolve_syms(filt); | ||
2066 | if (err) | ||
2067 | goto out_exit; | ||
2068 | |||
2069 | new_filter = addr_filter__to_str(filt); | ||
2070 | if (!new_filter) { | ||
2071 | err = -ENOMEM; | ||
2072 | goto out_exit; | ||
2073 | } | ||
2074 | |||
2075 | if (perf_evsel__append_addr_filter(evsel, new_filter)) { | ||
2076 | err = -ENOMEM; | ||
2077 | goto out_exit; | ||
2078 | } | ||
2079 | } | ||
2080 | |||
2081 | out_exit: | ||
2082 | addr_filters__exit(&filts); | ||
2083 | |||
2084 | if (err) { | ||
2085 | pr_err("Failed to parse address filter: '%s'\n", filter); | ||
2086 | pr_err("Filter format is: filter|start|stop|tracestop <start symbol or address> [/ <end symbol or size>] [@<file name>]\n"); | ||
2087 | pr_err("Where multiple filters are separated by space or comma.\n"); | ||
2088 | } | ||
2089 | |||
2090 | return err; | ||
2091 | } | ||
2092 | |||
2093 | static struct perf_pmu *perf_evsel__find_pmu(struct perf_evsel *evsel) | ||
2094 | { | ||
2095 | struct perf_pmu *pmu = NULL; | ||
2096 | |||
2097 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | ||
2098 | if (pmu->type == evsel->attr.type) | ||
2099 | break; | ||
2100 | } | ||
2101 | |||
2102 | return pmu; | ||
2103 | } | ||
2104 | |||
2105 | static int perf_evsel__nr_addr_filter(struct perf_evsel *evsel) | ||
2106 | { | ||
2107 | struct perf_pmu *pmu = perf_evsel__find_pmu(evsel); | ||
2108 | int nr_addr_filters = 0; | ||
2109 | |||
2110 | if (!pmu) | ||
2111 | return 0; | ||
2112 | |||
2113 | perf_pmu__scan_file(pmu, "nr_addr_filters", "%d", &nr_addr_filters); | ||
2114 | |||
2115 | return nr_addr_filters; | ||
2116 | } | ||
2117 | |||
2118 | int auxtrace_parse_filters(struct perf_evlist *evlist) | ||
2119 | { | ||
2120 | struct perf_evsel *evsel; | ||
2121 | char *filter; | ||
2122 | int err, max_nr; | ||
2123 | |||
2124 | evlist__for_each_entry(evlist, evsel) { | ||
2125 | filter = evsel->filter; | ||
2126 | max_nr = perf_evsel__nr_addr_filter(evsel); | ||
2127 | if (!filter || !max_nr) | ||
2128 | continue; | ||
2129 | evsel->filter = NULL; | ||
2130 | err = parse_addr_filter(evsel, filter, max_nr); | ||
2131 | free(filter); | ||
2132 | if (err) | ||
2133 | return err; | ||
2134 | pr_debug("Address filter: %s\n", evsel->filter); | ||
2135 | } | ||
2136 | |||
2137 | return 0; | ||
2138 | } | ||
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 09286f193532..26fb1ee5746a 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h | |||
@@ -318,6 +318,48 @@ struct auxtrace_record { | |||
318 | unsigned int alignment; | 318 | unsigned int alignment; |
319 | }; | 319 | }; |
320 | 320 | ||
321 | /** | ||
322 | * struct addr_filter - address filter. | ||
323 | * @list: list node | ||
324 | * @range: true if it is a range filter | ||
325 | * @start: true if action is 'filter' or 'start' | ||
326 | * @action: 'filter', 'start' or 'stop' ('tracestop' is accepted but converted | ||
327 | * to 'stop') | ||
328 | * @sym_from: symbol name for the filter address | ||
329 | * @sym_to: symbol name that determines the filter size | ||
330 | * @sym_from_idx: selects n'th from symbols with the same name (0 means global | ||
331 | * and less than 0 means symbol must be unique) | ||
332 | * @sym_to_idx: same as @sym_from_idx but for @sym_to | ||
333 | * @addr: filter address | ||
334 | * @size: filter region size (for range filters) | ||
335 | * @filename: DSO file name or NULL for the kernel | ||
336 | * @str: allocated string that contains the other string members | ||
337 | */ | ||
338 | struct addr_filter { | ||
339 | struct list_head list; | ||
340 | bool range; | ||
341 | bool start; | ||
342 | const char *action; | ||
343 | const char *sym_from; | ||
344 | const char *sym_to; | ||
345 | int sym_from_idx; | ||
346 | int sym_to_idx; | ||
347 | u64 addr; | ||
348 | u64 size; | ||
349 | const char *filename; | ||
350 | char *str; | ||
351 | }; | ||
352 | |||
353 | /** | ||
354 | * struct addr_filters - list of address filters. | ||
355 | * @head: list of address filters | ||
356 | * @cnt: number of address filters | ||
357 | */ | ||
358 | struct addr_filters { | ||
359 | struct list_head head; | ||
360 | int cnt; | ||
361 | }; | ||
362 | |||
321 | #ifdef HAVE_AUXTRACE_SUPPORT | 363 | #ifdef HAVE_AUXTRACE_SUPPORT |
322 | 364 | ||
323 | /* | 365 | /* |
@@ -482,6 +524,12 @@ void perf_session__auxtrace_error_inc(struct perf_session *session, | |||
482 | union perf_event *event); | 524 | union perf_event *event); |
483 | void events_stats__auxtrace_error_warn(const struct events_stats *stats); | 525 | void events_stats__auxtrace_error_warn(const struct events_stats *stats); |
484 | 526 | ||
527 | void addr_filters__init(struct addr_filters *filts); | ||
528 | void addr_filters__exit(struct addr_filters *filts); | ||
529 | int addr_filters__parse_bare_filter(struct addr_filters *filts, | ||
530 | const char *filter); | ||
531 | int auxtrace_parse_filters(struct perf_evlist *evlist); | ||
532 | |||
485 | static inline int auxtrace__process_event(struct perf_session *session, | 533 | static inline int auxtrace__process_event(struct perf_session *session, |
486 | union perf_event *event, | 534 | union perf_event *event, |
487 | struct perf_sample *sample, | 535 | struct perf_sample *sample, |
@@ -640,6 +688,12 @@ void auxtrace_index__free(struct list_head *head __maybe_unused) | |||
640 | { | 688 | { |
641 | } | 689 | } |
642 | 690 | ||
691 | static inline | ||
692 | int auxtrace_parse_filters(struct perf_evlist *evlist __maybe_unused) | ||
693 | { | ||
694 | return 0; | ||
695 | } | ||
696 | |||
643 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, | 697 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, |
644 | struct auxtrace_mmap_params *mp, | 698 | struct auxtrace_mmap_params *mp, |
645 | void *userpg, int fd); | 699 | void *userpg, int fd); |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5651f3c12f93..e528c40739cc 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -620,7 +620,7 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id, | |||
620 | 620 | ||
621 | ret = probe_cache__scan_sdt(cache, realname); | 621 | ret = probe_cache__scan_sdt(cache, realname); |
622 | if (ret >= 0) { | 622 | if (ret >= 0) { |
623 | pr_debug("Found %d SDTs in %s\n", ret, realname); | 623 | pr_debug4("Found %d SDTs in %s\n", ret, realname); |
624 | if (probe_cache__commit(cache) < 0) | 624 | if (probe_cache__commit(cache) < 0) |
625 | ret = -1; | 625 | ret = -1; |
626 | } | 626 | } |
@@ -691,7 +691,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, | |||
691 | 691 | ||
692 | /* Update SDT cache : error is just warned */ | 692 | /* Update SDT cache : error is just warned */ |
693 | if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0) | 693 | if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0) |
694 | pr_debug("Failed to update/scan SDT cache for %s\n", realname); | 694 | pr_debug4("Failed to update/scan SDT cache for %s\n", realname); |
695 | 695 | ||
696 | out_free: | 696 | out_free: |
697 | if (!is_kallsyms) | 697 | if (!is_kallsyms) |
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 4f979bb27b6c..7123f4de32cc 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c | |||
@@ -437,7 +437,7 @@ add_bpf_output_values(struct bt_ctf_event_class *event_class, | |||
437 | int ret; | 437 | int ret; |
438 | 438 | ||
439 | if (nr_elements * sizeof(u32) != raw_size) | 439 | if (nr_elements * sizeof(u32) != raw_size) |
440 | pr_warning("Incorrect raw_size (%u) in bpf output event, skip %lu bytes\n", | 440 | pr_warning("Incorrect raw_size (%u) in bpf output event, skip %zu bytes\n", |
441 | raw_size, nr_elements * sizeof(u32) - raw_size); | 441 | raw_size, nr_elements * sizeof(u32) - raw_size); |
442 | 442 | ||
443 | len_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_len"); | 443 | len_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_len"); |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index faec899435f2..41e068e94349 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -130,6 +130,22 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | |||
130 | } | 130 | } |
131 | 131 | ||
132 | /** | 132 | /** |
133 | * die_get_linkage_name - Get the linkage name of the object | ||
134 | * @dw_die: A DIE of the object | ||
135 | * | ||
136 | * Get the linkage name attiribute of given @dw_die. | ||
137 | * For C++ binary, the linkage name will be the mangled symbol. | ||
138 | */ | ||
139 | const char *die_get_linkage_name(Dwarf_Die *dw_die) | ||
140 | { | ||
141 | Dwarf_Attribute attr; | ||
142 | |||
143 | if (dwarf_attr_integrate(dw_die, DW_AT_linkage_name, &attr) == NULL) | ||
144 | return NULL; | ||
145 | return dwarf_formstring(&attr); | ||
146 | } | ||
147 | |||
148 | /** | ||
133 | * die_compare_name - Compare diename and tname | 149 | * die_compare_name - Compare diename and tname |
134 | * @dw_die: a DIE | 150 | * @dw_die: a DIE |
135 | * @tname: a string of target name | 151 | * @tname: a string of target name |
@@ -145,18 +161,26 @@ bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
145 | } | 161 | } |
146 | 162 | ||
147 | /** | 163 | /** |
148 | * die_match_name - Match diename and glob | 164 | * die_match_name - Match diename/linkage name and glob |
149 | * @dw_die: a DIE | 165 | * @dw_die: a DIE |
150 | * @glob: a string of target glob pattern | 166 | * @glob: a string of target glob pattern |
151 | * | 167 | * |
152 | * Glob matching the name of @dw_die and @glob. Return false if matching fail. | 168 | * Glob matching the name of @dw_die and @glob. Return false if matching fail. |
169 | * This also match linkage name. | ||
153 | */ | 170 | */ |
154 | bool die_match_name(Dwarf_Die *dw_die, const char *glob) | 171 | bool die_match_name(Dwarf_Die *dw_die, const char *glob) |
155 | { | 172 | { |
156 | const char *name; | 173 | const char *name; |
157 | 174 | ||
158 | name = dwarf_diename(dw_die); | 175 | name = dwarf_diename(dw_die); |
159 | return name ? strglobmatch(name, glob) : false; | 176 | if (name && strglobmatch(name, glob)) |
177 | return true; | ||
178 | /* fall back to check linkage name */ | ||
179 | name = die_get_linkage_name(dw_die); | ||
180 | if (name && strglobmatch(name, glob)) | ||
181 | return true; | ||
182 | |||
183 | return false; | ||
160 | } | 184 | } |
161 | 185 | ||
162 | /** | 186 | /** |
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 8b6d2f83af02..8ac53bf1ec4e 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -38,6 +38,9 @@ int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | |||
38 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | 38 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, |
39 | int (*callback)(Dwarf_Die *, void *), void *data); | 39 | int (*callback)(Dwarf_Die *, void *), void *data); |
40 | 40 | ||
41 | /* Get DW_AT_linkage_name (should be NULL for C binary) */ | ||
42 | const char *die_get_linkage_name(Dwarf_Die *dw_die); | ||
43 | |||
41 | /* Ensure that this DIE is a subprogram and definition (not declaration) */ | 44 | /* Ensure that this DIE is a subprogram and definition (not declaration) */ |
42 | bool die_is_func_def(Dwarf_Die *dw_die); | 45 | bool die_is_func_def(Dwarf_Die *dw_die); |
43 | 46 | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2880e2226fdb..8ab0d7da956b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -346,7 +346,8 @@ out: | |||
346 | if (!strcmp(execname, "")) | 346 | if (!strcmp(execname, "")) |
347 | strcpy(execname, anonstr); | 347 | strcpy(execname, anonstr); |
348 | 348 | ||
349 | if (!strncmp(execname, hugetlbfs_mnt, hugetlbfs_mnt_len)) { | 349 | if (hugetlbfs_mnt_len && |
350 | !strncmp(execname, hugetlbfs_mnt, hugetlbfs_mnt_len)) { | ||
350 | strcpy(execname, anonstr); | 351 | strcpy(execname, anonstr); |
351 | event->mmap2.flags |= MAP_HUGETLB; | 352 | event->mmap2.flags |= MAP_HUGETLB; |
352 | } | 353 | } |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f3225a2e6eee..380e84c3af3d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1045,15 +1045,15 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter) | |||
1045 | return -1; | 1045 | return -1; |
1046 | } | 1046 | } |
1047 | 1047 | ||
1048 | int perf_evsel__append_filter(struct perf_evsel *evsel, | 1048 | static int perf_evsel__append_filter(struct perf_evsel *evsel, |
1049 | const char *op, const char *filter) | 1049 | const char *fmt, const char *filter) |
1050 | { | 1050 | { |
1051 | char *new_filter; | 1051 | char *new_filter; |
1052 | 1052 | ||
1053 | if (evsel->filter == NULL) | 1053 | if (evsel->filter == NULL) |
1054 | return perf_evsel__set_filter(evsel, filter); | 1054 | return perf_evsel__set_filter(evsel, filter); |
1055 | 1055 | ||
1056 | if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) { | 1056 | if (asprintf(&new_filter, fmt, evsel->filter, filter) > 0) { |
1057 | free(evsel->filter); | 1057 | free(evsel->filter); |
1058 | evsel->filter = new_filter; | 1058 | evsel->filter = new_filter; |
1059 | return 0; | 1059 | return 0; |
@@ -1062,6 +1062,16 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, | |||
1062 | return -1; | 1062 | return -1; |
1063 | } | 1063 | } |
1064 | 1064 | ||
1065 | int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter) | ||
1066 | { | ||
1067 | return perf_evsel__append_filter(evsel, "(%s) && (%s)", filter); | ||
1068 | } | ||
1069 | |||
1070 | int perf_evsel__append_addr_filter(struct perf_evsel *evsel, const char *filter) | ||
1071 | { | ||
1072 | return perf_evsel__append_filter(evsel, "%s,%s", filter); | ||
1073 | } | ||
1074 | |||
1065 | int perf_evsel__enable(struct perf_evsel *evsel) | 1075 | int perf_evsel__enable(struct perf_evsel *evsel) |
1066 | { | 1076 | { |
1067 | int nthreads = thread_map__nr(evsel->threads); | 1077 | int nthreads = thread_map__nr(evsel->threads); |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 323806082c58..b1503b0ecdff 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -235,8 +235,9 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel, | |||
235 | bool use_sample_identifier); | 235 | bool use_sample_identifier); |
236 | 236 | ||
237 | int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter); | 237 | int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter); |
238 | int perf_evsel__append_filter(struct perf_evsel *evsel, | 238 | int perf_evsel__append_tp_filter(struct perf_evsel *evsel, const char *filter); |
239 | const char *op, const char *filter); | 239 | int perf_evsel__append_addr_filter(struct perf_evsel *evsel, |
240 | const char *filter); | ||
240 | int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 241 | int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, |
241 | const char *filter); | 242 | const char *filter); |
242 | int perf_evsel__enable(struct perf_evsel *evsel); | 243 | int perf_evsel__enable(struct perf_evsel *evsel); |
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 9111e0666950..662a0a6182e7 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -122,9 +122,6 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
122 | if (!node) | 122 | if (!node) |
123 | break; | 123 | break; |
124 | 124 | ||
125 | if (node->sym && node->sym->idle) | ||
126 | goto next; | ||
127 | |||
128 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | 125 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
129 | 126 | ||
130 | if (print_ip) | 127 | if (print_ip) |
@@ -158,7 +155,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
158 | 155 | ||
159 | if (!print_oneline) | 156 | if (!print_oneline) |
160 | printed += fprintf(fp, "\n"); | 157 | printed += fprintf(fp, "\n"); |
161 | next: | 158 | |
162 | callchain_cursor_advance(cursor); | 159 | callchain_cursor_advance(cursor); |
163 | } | 160 | } |
164 | } | 161 | } |
@@ -181,7 +178,7 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, | |||
181 | if (cursor != NULL) { | 178 | if (cursor != NULL) { |
182 | printed += sample__fprintf_callchain(sample, left_alignment, | 179 | printed += sample__fprintf_callchain(sample, left_alignment, |
183 | print_opts, cursor, fp); | 180 | print_opts, cursor, fp); |
184 | } else if (!(al->sym && al->sym->idle)) { | 181 | } else { |
185 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | 182 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
186 | 183 | ||
187 | if (print_ip) | 184 | if (print_ip) |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 8ff6c6a61291..7591a0c37473 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
@@ -80,6 +80,7 @@ struct intel_pt_decoder { | |||
80 | int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, | 80 | int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, |
81 | uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, | 81 | uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, |
82 | uint64_t max_insn_cnt, void *data); | 82 | uint64_t max_insn_cnt, void *data); |
83 | bool (*pgd_ip)(uint64_t ip, void *data); | ||
83 | void *data; | 84 | void *data; |
84 | struct intel_pt_state state; | 85 | struct intel_pt_state state; |
85 | const unsigned char *buf; | 86 | const unsigned char *buf; |
@@ -186,6 +187,7 @@ struct intel_pt_decoder *intel_pt_decoder_new(struct intel_pt_params *params) | |||
186 | 187 | ||
187 | decoder->get_trace = params->get_trace; | 188 | decoder->get_trace = params->get_trace; |
188 | decoder->walk_insn = params->walk_insn; | 189 | decoder->walk_insn = params->walk_insn; |
190 | decoder->pgd_ip = params->pgd_ip; | ||
189 | decoder->data = params->data; | 191 | decoder->data = params->data; |
190 | decoder->return_compression = params->return_compression; | 192 | decoder->return_compression = params->return_compression; |
191 | 193 | ||
@@ -1008,6 +1010,19 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) | |||
1008 | int err; | 1010 | int err; |
1009 | 1011 | ||
1010 | err = intel_pt_walk_insn(decoder, &intel_pt_insn, 0); | 1012 | err = intel_pt_walk_insn(decoder, &intel_pt_insn, 0); |
1013 | if (err == INTEL_PT_RETURN && | ||
1014 | decoder->pgd_ip && | ||
1015 | decoder->pkt_state == INTEL_PT_STATE_TIP_PGD && | ||
1016 | (decoder->state.type & INTEL_PT_BRANCH) && | ||
1017 | decoder->pgd_ip(decoder->state.to_ip, decoder->data)) { | ||
1018 | /* Unconditional branch leaving filter region */ | ||
1019 | decoder->no_progress = 0; | ||
1020 | decoder->pge = false; | ||
1021 | decoder->continuous_period = false; | ||
1022 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | ||
1023 | decoder->state.to_ip = 0; | ||
1024 | return 0; | ||
1025 | } | ||
1011 | if (err == INTEL_PT_RETURN) | 1026 | if (err == INTEL_PT_RETURN) |
1012 | return 0; | 1027 | return 0; |
1013 | if (err) | 1028 | if (err) |
@@ -1036,6 +1051,21 @@ static int intel_pt_walk_tip(struct intel_pt_decoder *decoder) | |||
1036 | } | 1051 | } |
1037 | 1052 | ||
1038 | if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) { | 1053 | if (intel_pt_insn.branch == INTEL_PT_BR_CONDITIONAL) { |
1054 | uint64_t to_ip = decoder->ip + intel_pt_insn.length + | ||
1055 | intel_pt_insn.rel; | ||
1056 | |||
1057 | if (decoder->pgd_ip && | ||
1058 | decoder->pkt_state == INTEL_PT_STATE_TIP_PGD && | ||
1059 | decoder->pgd_ip(to_ip, decoder->data)) { | ||
1060 | /* Conditional branch leaving filter region */ | ||
1061 | decoder->pge = false; | ||
1062 | decoder->continuous_period = false; | ||
1063 | decoder->pkt_state = INTEL_PT_STATE_IN_SYNC; | ||
1064 | decoder->ip = to_ip; | ||
1065 | decoder->state.from_ip = decoder->ip; | ||
1066 | decoder->state.to_ip = 0; | ||
1067 | return 0; | ||
1068 | } | ||
1039 | intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch", | 1069 | intel_pt_log_at("ERROR: Conditional branch when expecting indirect branch", |
1040 | decoder->ip); | 1070 | decoder->ip); |
1041 | decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC; | 1071 | decoder->pkt_state = INTEL_PT_STATE_ERR_RESYNC; |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h index 02c38fec1c37..89399985fa4d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h | |||
@@ -83,6 +83,7 @@ struct intel_pt_params { | |||
83 | int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, | 83 | int (*walk_insn)(struct intel_pt_insn *intel_pt_insn, |
84 | uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, | 84 | uint64_t *insn_cnt_ptr, uint64_t *ip, uint64_t to_ip, |
85 | uint64_t max_insn_cnt, void *data); | 85 | uint64_t max_insn_cnt, void *data); |
86 | bool (*pgd_ip)(uint64_t ip, void *data); | ||
86 | void *data; | 87 | void *data; |
87 | bool return_compression; | 88 | bool return_compression; |
88 | uint64_t period; | 89 | uint64_t period; |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index b9cc353cace2..dc041d4368c8 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -103,6 +103,9 @@ struct intel_pt { | |||
103 | unsigned max_non_turbo_ratio; | 103 | unsigned max_non_turbo_ratio; |
104 | 104 | ||
105 | unsigned long num_events; | 105 | unsigned long num_events; |
106 | |||
107 | char *filter; | ||
108 | struct addr_filters filts; | ||
106 | }; | 109 | }; |
107 | 110 | ||
108 | enum switch_state { | 111 | enum switch_state { |
@@ -241,7 +244,7 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
241 | } | 244 | } |
242 | 245 | ||
243 | queue = &ptq->pt->queues.queue_array[ptq->queue_nr]; | 246 | queue = &ptq->pt->queues.queue_array[ptq->queue_nr]; |
244 | 247 | next: | |
245 | buffer = auxtrace_buffer__next(queue, buffer); | 248 | buffer = auxtrace_buffer__next(queue, buffer); |
246 | if (!buffer) { | 249 | if (!buffer) { |
247 | if (old_buffer) | 250 | if (old_buffer) |
@@ -264,9 +267,6 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
264 | intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer)) | 267 | intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer)) |
265 | return -ENOMEM; | 268 | return -ENOMEM; |
266 | 269 | ||
267 | if (old_buffer) | ||
268 | auxtrace_buffer__drop_data(old_buffer); | ||
269 | |||
270 | if (buffer->use_data) { | 270 | if (buffer->use_data) { |
271 | b->len = buffer->use_size; | 271 | b->len = buffer->use_size; |
272 | b->buf = buffer->use_data; | 272 | b->buf = buffer->use_data; |
@@ -276,6 +276,16 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data) | |||
276 | } | 276 | } |
277 | b->ref_timestamp = buffer->reference; | 277 | b->ref_timestamp = buffer->reference; |
278 | 278 | ||
279 | /* | ||
280 | * If in snapshot mode and the buffer has no usable data, get next | ||
281 | * buffer and again check overlap against old_buffer. | ||
282 | */ | ||
283 | if (ptq->pt->snapshot_mode && !b->len) | ||
284 | goto next; | ||
285 | |||
286 | if (old_buffer) | ||
287 | auxtrace_buffer__drop_data(old_buffer); | ||
288 | |||
279 | if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode && | 289 | if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode && |
280 | !buffer->consecutive)) { | 290 | !buffer->consecutive)) { |
281 | b->consecutive = false; | 291 | b->consecutive = false; |
@@ -541,6 +551,76 @@ out_no_cache: | |||
541 | return 0; | 551 | return 0; |
542 | } | 552 | } |
543 | 553 | ||
554 | static bool intel_pt_match_pgd_ip(struct intel_pt *pt, uint64_t ip, | ||
555 | uint64_t offset, const char *filename) | ||
556 | { | ||
557 | struct addr_filter *filt; | ||
558 | bool have_filter = false; | ||
559 | bool hit_tracestop = false; | ||
560 | bool hit_filter = false; | ||
561 | |||
562 | list_for_each_entry(filt, &pt->filts.head, list) { | ||
563 | if (filt->start) | ||
564 | have_filter = true; | ||
565 | |||
566 | if ((filename && !filt->filename) || | ||
567 | (!filename && filt->filename) || | ||
568 | (filename && strcmp(filename, filt->filename))) | ||
569 | continue; | ||
570 | |||
571 | if (!(offset >= filt->addr && offset < filt->addr + filt->size)) | ||
572 | continue; | ||
573 | |||
574 | intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s hit filter: %s offset %#"PRIx64" size %#"PRIx64"\n", | ||
575 | ip, offset, filename ? filename : "[kernel]", | ||
576 | filt->start ? "filter" : "stop", | ||
577 | filt->addr, filt->size); | ||
578 | |||
579 | if (filt->start) | ||
580 | hit_filter = true; | ||
581 | else | ||
582 | hit_tracestop = true; | ||
583 | } | ||
584 | |||
585 | if (!hit_tracestop && !hit_filter) | ||
586 | intel_pt_log("TIP.PGD ip %#"PRIx64" offset %#"PRIx64" in %s is not in a filter region\n", | ||
587 | ip, offset, filename ? filename : "[kernel]"); | ||
588 | |||
589 | return hit_tracestop || (have_filter && !hit_filter); | ||
590 | } | ||
591 | |||
592 | static int __intel_pt_pgd_ip(uint64_t ip, void *data) | ||
593 | { | ||
594 | struct intel_pt_queue *ptq = data; | ||
595 | struct thread *thread; | ||
596 | struct addr_location al; | ||
597 | u8 cpumode; | ||
598 | u64 offset; | ||
599 | |||
600 | if (ip >= ptq->pt->kernel_start) | ||
601 | return intel_pt_match_pgd_ip(ptq->pt, ip, ip, NULL); | ||
602 | |||
603 | cpumode = PERF_RECORD_MISC_USER; | ||
604 | |||
605 | thread = ptq->thread; | ||
606 | if (!thread) | ||
607 | return -EINVAL; | ||
608 | |||
609 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, ip, &al); | ||
610 | if (!al.map || !al.map->dso) | ||
611 | return -EINVAL; | ||
612 | |||
613 | offset = al.map->map_ip(al.map, ip); | ||
614 | |||
615 | return intel_pt_match_pgd_ip(ptq->pt, ip, offset, | ||
616 | al.map->dso->long_name); | ||
617 | } | ||
618 | |||
619 | static bool intel_pt_pgd_ip(uint64_t ip, void *data) | ||
620 | { | ||
621 | return __intel_pt_pgd_ip(ip, data) > 0; | ||
622 | } | ||
623 | |||
544 | static bool intel_pt_get_config(struct intel_pt *pt, | 624 | static bool intel_pt_get_config(struct intel_pt *pt, |
545 | struct perf_event_attr *attr, u64 *config) | 625 | struct perf_event_attr *attr, u64 *config) |
546 | { | 626 | { |
@@ -717,6 +797,9 @@ static struct intel_pt_queue *intel_pt_alloc_queue(struct intel_pt *pt, | |||
717 | params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n; | 797 | params.tsc_ctc_ratio_n = pt->tsc_ctc_ratio_n; |
718 | params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d; | 798 | params.tsc_ctc_ratio_d = pt->tsc_ctc_ratio_d; |
719 | 799 | ||
800 | if (pt->filts.cnt > 0) | ||
801 | params.pgd_ip = intel_pt_pgd_ip; | ||
802 | |||
720 | if (pt->synth_opts.instructions) { | 803 | if (pt->synth_opts.instructions) { |
721 | if (pt->synth_opts.period) { | 804 | if (pt->synth_opts.period) { |
722 | switch (pt->synth_opts.period_type) { | 805 | switch (pt->synth_opts.period_type) { |
@@ -1767,6 +1850,8 @@ static void intel_pt_free(struct perf_session *session) | |||
1767 | intel_pt_free_events(session); | 1850 | intel_pt_free_events(session); |
1768 | session->auxtrace = NULL; | 1851 | session->auxtrace = NULL; |
1769 | thread__put(pt->unknown_thread); | 1852 | thread__put(pt->unknown_thread); |
1853 | addr_filters__exit(&pt->filts); | ||
1854 | zfree(&pt->filter); | ||
1770 | free(pt); | 1855 | free(pt); |
1771 | } | 1856 | } |
1772 | 1857 | ||
@@ -2016,6 +2101,8 @@ static const char * const intel_pt_info_fmts[] = { | |||
2016 | [INTEL_PT_TSC_CTC_N] = " TSC:CTC numerator %"PRIu64"\n", | 2101 | [INTEL_PT_TSC_CTC_N] = " TSC:CTC numerator %"PRIu64"\n", |
2017 | [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", | 2102 | [INTEL_PT_TSC_CTC_D] = " TSC:CTC denominator %"PRIu64"\n", |
2018 | [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", | 2103 | [INTEL_PT_CYC_BIT] = " CYC bit %#"PRIx64"\n", |
2104 | [INTEL_PT_MAX_NONTURBO_RATIO] = " Max non-turbo ratio %"PRIu64"\n", | ||
2105 | [INTEL_PT_FILTER_STR_LEN] = " Filter string len. %"PRIu64"\n", | ||
2019 | }; | 2106 | }; |
2020 | 2107 | ||
2021 | static void intel_pt_print_info(u64 *arr, int start, int finish) | 2108 | static void intel_pt_print_info(u64 *arr, int start, int finish) |
@@ -2029,12 +2116,28 @@ static void intel_pt_print_info(u64 *arr, int start, int finish) | |||
2029 | fprintf(stdout, intel_pt_info_fmts[i], arr[i]); | 2116 | fprintf(stdout, intel_pt_info_fmts[i], arr[i]); |
2030 | } | 2117 | } |
2031 | 2118 | ||
2119 | static void intel_pt_print_info_str(const char *name, const char *str) | ||
2120 | { | ||
2121 | if (!dump_trace) | ||
2122 | return; | ||
2123 | |||
2124 | fprintf(stdout, " %-20s%s\n", name, str ? str : ""); | ||
2125 | } | ||
2126 | |||
2127 | static bool intel_pt_has(struct auxtrace_info_event *auxtrace_info, int pos) | ||
2128 | { | ||
2129 | return auxtrace_info->header.size >= | ||
2130 | sizeof(struct auxtrace_info_event) + (sizeof(u64) * (pos + 1)); | ||
2131 | } | ||
2132 | |||
2032 | int intel_pt_process_auxtrace_info(union perf_event *event, | 2133 | int intel_pt_process_auxtrace_info(union perf_event *event, |
2033 | struct perf_session *session) | 2134 | struct perf_session *session) |
2034 | { | 2135 | { |
2035 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; | 2136 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; |
2036 | size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS; | 2137 | size_t min_sz = sizeof(u64) * INTEL_PT_PER_CPU_MMAPS; |
2037 | struct intel_pt *pt; | 2138 | struct intel_pt *pt; |
2139 | void *info_end; | ||
2140 | u64 *info; | ||
2038 | int err; | 2141 | int err; |
2039 | 2142 | ||
2040 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + | 2143 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + |
@@ -2045,6 +2148,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2045 | if (!pt) | 2148 | if (!pt) |
2046 | return -ENOMEM; | 2149 | return -ENOMEM; |
2047 | 2150 | ||
2151 | addr_filters__init(&pt->filts); | ||
2152 | |||
2048 | perf_config(intel_pt_perf_config, pt); | 2153 | perf_config(intel_pt_perf_config, pt); |
2049 | 2154 | ||
2050 | err = auxtrace_queues__init(&pt->queues); | 2155 | err = auxtrace_queues__init(&pt->queues); |
@@ -2069,8 +2174,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2069 | intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE, | 2174 | intel_pt_print_info(&auxtrace_info->priv[0], INTEL_PT_PMU_TYPE, |
2070 | INTEL_PT_PER_CPU_MMAPS); | 2175 | INTEL_PT_PER_CPU_MMAPS); |
2071 | 2176 | ||
2072 | if (auxtrace_info->header.size >= sizeof(struct auxtrace_info_event) + | 2177 | if (intel_pt_has(auxtrace_info, INTEL_PT_CYC_BIT)) { |
2073 | (sizeof(u64) * INTEL_PT_CYC_BIT)) { | ||
2074 | pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT]; | 2178 | pt->mtc_bit = auxtrace_info->priv[INTEL_PT_MTC_BIT]; |
2075 | pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS]; | 2179 | pt->mtc_freq_bits = auxtrace_info->priv[INTEL_PT_MTC_FREQ_BITS]; |
2076 | pt->tsc_ctc_ratio_n = auxtrace_info->priv[INTEL_PT_TSC_CTC_N]; | 2180 | pt->tsc_ctc_ratio_n = auxtrace_info->priv[INTEL_PT_TSC_CTC_N]; |
@@ -2080,6 +2184,54 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2080 | INTEL_PT_CYC_BIT); | 2184 | INTEL_PT_CYC_BIT); |
2081 | } | 2185 | } |
2082 | 2186 | ||
2187 | if (intel_pt_has(auxtrace_info, INTEL_PT_MAX_NONTURBO_RATIO)) { | ||
2188 | pt->max_non_turbo_ratio = | ||
2189 | auxtrace_info->priv[INTEL_PT_MAX_NONTURBO_RATIO]; | ||
2190 | intel_pt_print_info(&auxtrace_info->priv[0], | ||
2191 | INTEL_PT_MAX_NONTURBO_RATIO, | ||
2192 | INTEL_PT_MAX_NONTURBO_RATIO); | ||
2193 | } | ||
2194 | |||
2195 | info = &auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN] + 1; | ||
2196 | info_end = (void *)info + auxtrace_info->header.size; | ||
2197 | |||
2198 | if (intel_pt_has(auxtrace_info, INTEL_PT_FILTER_STR_LEN)) { | ||
2199 | size_t len; | ||
2200 | |||
2201 | len = auxtrace_info->priv[INTEL_PT_FILTER_STR_LEN]; | ||
2202 | intel_pt_print_info(&auxtrace_info->priv[0], | ||
2203 | INTEL_PT_FILTER_STR_LEN, | ||
2204 | INTEL_PT_FILTER_STR_LEN); | ||
2205 | if (len) { | ||
2206 | const char *filter = (const char *)info; | ||
2207 | |||
2208 | len = roundup(len + 1, 8); | ||
2209 | info += len >> 3; | ||
2210 | if ((void *)info > info_end) { | ||
2211 | pr_err("%s: bad filter string length\n", __func__); | ||
2212 | err = -EINVAL; | ||
2213 | goto err_free_queues; | ||
2214 | } | ||
2215 | pt->filter = memdup(filter, len); | ||
2216 | if (!pt->filter) { | ||
2217 | err = -ENOMEM; | ||
2218 | goto err_free_queues; | ||
2219 | } | ||
2220 | if (session->header.needs_swap) | ||
2221 | mem_bswap_64(pt->filter, len); | ||
2222 | if (pt->filter[len - 1]) { | ||
2223 | pr_err("%s: filter string not null terminated\n", __func__); | ||
2224 | err = -EINVAL; | ||
2225 | goto err_free_queues; | ||
2226 | } | ||
2227 | err = addr_filters__parse_bare_filter(&pt->filts, | ||
2228 | filter); | ||
2229 | if (err) | ||
2230 | goto err_free_queues; | ||
2231 | } | ||
2232 | intel_pt_print_info_str("Filter string", pt->filter); | ||
2233 | } | ||
2234 | |||
2083 | pt->timeless_decoding = intel_pt_timeless_decoding(pt); | 2235 | pt->timeless_decoding = intel_pt_timeless_decoding(pt); |
2084 | pt->have_tsc = intel_pt_have_tsc(pt); | 2236 | pt->have_tsc = intel_pt_have_tsc(pt); |
2085 | pt->sampling_mode = false; | 2237 | pt->sampling_mode = false; |
@@ -2121,11 +2273,13 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2121 | pt->switch_evsel = intel_pt_find_sched_switch(session->evlist); | 2273 | pt->switch_evsel = intel_pt_find_sched_switch(session->evlist); |
2122 | if (!pt->switch_evsel) { | 2274 | if (!pt->switch_evsel) { |
2123 | pr_err("%s: missing sched_switch event\n", __func__); | 2275 | pr_err("%s: missing sched_switch event\n", __func__); |
2276 | err = -EINVAL; | ||
2124 | goto err_delete_thread; | 2277 | goto err_delete_thread; |
2125 | } | 2278 | } |
2126 | } else if (pt->have_sched_switch == 2 && | 2279 | } else if (pt->have_sched_switch == 2 && |
2127 | !intel_pt_find_switch(session->evlist)) { | 2280 | !intel_pt_find_switch(session->evlist)) { |
2128 | pr_err("%s: missing context_switch attribute flag\n", __func__); | 2281 | pr_err("%s: missing context_switch attribute flag\n", __func__); |
2282 | err = -EINVAL; | ||
2129 | goto err_delete_thread; | 2283 | goto err_delete_thread; |
2130 | } | 2284 | } |
2131 | 2285 | ||
@@ -2149,7 +2303,9 @@ int intel_pt_process_auxtrace_info(union perf_event *event, | |||
2149 | if (pt->tc.time_mult) { | 2303 | if (pt->tc.time_mult) { |
2150 | u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000); | 2304 | u64 tsc_freq = intel_pt_ns_to_ticks(pt, 1000000000); |
2151 | 2305 | ||
2152 | pt->max_non_turbo_ratio = (tsc_freq + 50000000) / 100000000; | 2306 | if (!pt->max_non_turbo_ratio) |
2307 | pt->max_non_turbo_ratio = | ||
2308 | (tsc_freq + 50000000) / 100000000; | ||
2153 | intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq); | 2309 | intel_pt_log("TSC frequency %"PRIu64"\n", tsc_freq); |
2154 | intel_pt_log("Maximum non-turbo ratio %u\n", | 2310 | intel_pt_log("Maximum non-turbo ratio %u\n", |
2155 | pt->max_non_turbo_ratio); | 2311 | pt->max_non_turbo_ratio); |
@@ -2193,6 +2349,8 @@ err_free_queues: | |||
2193 | auxtrace_queues__free(&pt->queues); | 2349 | auxtrace_queues__free(&pt->queues); |
2194 | session->auxtrace = NULL; | 2350 | session->auxtrace = NULL; |
2195 | err_free: | 2351 | err_free: |
2352 | addr_filters__exit(&pt->filts); | ||
2353 | zfree(&pt->filter); | ||
2196 | free(pt); | 2354 | free(pt); |
2197 | return err; | 2355 | return err; |
2198 | } | 2356 | } |
diff --git a/tools/perf/util/intel-pt.h b/tools/perf/util/intel-pt.h index 0065949df693..e13b14e5a37b 100644 --- a/tools/perf/util/intel-pt.h +++ b/tools/perf/util/intel-pt.h | |||
@@ -34,11 +34,11 @@ enum { | |||
34 | INTEL_PT_TSC_CTC_N, | 34 | INTEL_PT_TSC_CTC_N, |
35 | INTEL_PT_TSC_CTC_D, | 35 | INTEL_PT_TSC_CTC_D, |
36 | INTEL_PT_CYC_BIT, | 36 | INTEL_PT_CYC_BIT, |
37 | INTEL_PT_MAX_NONTURBO_RATIO, | ||
38 | INTEL_PT_FILTER_STR_LEN, | ||
37 | INTEL_PT_AUXTRACE_PRIV_MAX, | 39 | INTEL_PT_AUXTRACE_PRIV_MAX, |
38 | }; | 40 | }; |
39 | 41 | ||
40 | #define INTEL_PT_AUXTRACE_PRIV_SIZE (INTEL_PT_AUXTRACE_PRIV_MAX * sizeof(u64)) | ||
41 | |||
42 | struct auxtrace_record; | 42 | struct auxtrace_record; |
43 | struct perf_tool; | 43 | struct perf_tool; |
44 | union perf_event; | 44 | union perf_event; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 2eb8b1ed4cc8..33546c3ac1fe 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1760,20 +1760,49 @@ foreach_evsel_in_last_glob(struct perf_evlist *evlist, | |||
1760 | static int set_filter(struct perf_evsel *evsel, const void *arg) | 1760 | static int set_filter(struct perf_evsel *evsel, const void *arg) |
1761 | { | 1761 | { |
1762 | const char *str = arg; | 1762 | const char *str = arg; |
1763 | bool found = false; | ||
1764 | int nr_addr_filters = 0; | ||
1765 | struct perf_pmu *pmu = NULL; | ||
1763 | 1766 | ||
1764 | if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) { | 1767 | if (evsel == NULL) |
1765 | fprintf(stderr, | 1768 | goto err; |
1766 | "--filter option should follow a -e tracepoint option\n"); | 1769 | |
1767 | return -1; | 1770 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT) { |
1771 | if (perf_evsel__append_tp_filter(evsel, str) < 0) { | ||
1772 | fprintf(stderr, | ||
1773 | "not enough memory to hold filter string\n"); | ||
1774 | return -1; | ||
1775 | } | ||
1776 | |||
1777 | return 0; | ||
1768 | } | 1778 | } |
1769 | 1779 | ||
1770 | if (perf_evsel__append_filter(evsel, "&&", str) < 0) { | 1780 | while ((pmu = perf_pmu__scan(pmu)) != NULL) |
1781 | if (pmu->type == evsel->attr.type) { | ||
1782 | found = true; | ||
1783 | break; | ||
1784 | } | ||
1785 | |||
1786 | if (found) | ||
1787 | perf_pmu__scan_file(pmu, "nr_addr_filters", | ||
1788 | "%d", &nr_addr_filters); | ||
1789 | |||
1790 | if (!nr_addr_filters) | ||
1791 | goto err; | ||
1792 | |||
1793 | if (perf_evsel__append_addr_filter(evsel, str) < 0) { | ||
1771 | fprintf(stderr, | 1794 | fprintf(stderr, |
1772 | "not enough memory to hold filter string\n"); | 1795 | "not enough memory to hold filter string\n"); |
1773 | return -1; | 1796 | return -1; |
1774 | } | 1797 | } |
1775 | 1798 | ||
1776 | return 0; | 1799 | return 0; |
1800 | |||
1801 | err: | ||
1802 | fprintf(stderr, | ||
1803 | "--filter option should follow a -e tracepoint or HW tracer option\n"); | ||
1804 | |||
1805 | return -1; | ||
1777 | } | 1806 | } |
1778 | 1807 | ||
1779 | int parse_filter(const struct option *opt, const char *str, | 1808 | int parse_filter(const struct option *opt, const char *str, |
@@ -1798,7 +1827,7 @@ static int add_exclude_perf_filter(struct perf_evsel *evsel, | |||
1798 | 1827 | ||
1799 | snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid()); | 1828 | snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid()); |
1800 | 1829 | ||
1801 | if (perf_evsel__append_filter(evsel, "&&", new_filter) < 0) { | 1830 | if (perf_evsel__append_tp_filter(evsel, new_filter) < 0) { |
1802 | fprintf(stderr, | 1831 | fprintf(stderr, |
1803 | "not enough memory to hold filter string\n"); | 1832 | "not enough memory to hold filter string\n"); |
1804 | return -1; | 1833 | return -1; |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bc60ce49720b..fcfbef07b92d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -213,9 +213,13 @@ static int convert_exec_to_group(const char *exec, char **result) | |||
213 | goto out; | 213 | goto out; |
214 | } | 214 | } |
215 | 215 | ||
216 | ptr2 = strpbrk(ptr1, "-._"); | 216 | for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) { |
217 | if (ptr2) | 217 | if (!isalnum(*ptr2) && *ptr2 != '_') { |
218 | *ptr2 = '\0'; | 218 | *ptr2 = '\0'; |
219 | break; | ||
220 | } | ||
221 | } | ||
222 | |||
219 | ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); | 223 | ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); |
220 | if (ret < 0) | 224 | if (ret < 0) |
221 | goto out; | 225 | goto out; |
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 6f931e442f14..436b64731f65 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
@@ -699,7 +699,7 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) | |||
699 | INIT_LIST_HEAD(&sdtlist); | 699 | INIT_LIST_HEAD(&sdtlist); |
700 | ret = get_sdt_note_list(&sdtlist, pathname); | 700 | ret = get_sdt_note_list(&sdtlist, pathname); |
701 | if (ret < 0) { | 701 | if (ret < 0) { |
702 | pr_debug("Failed to get sdt note: %d\n", ret); | 702 | pr_debug4("Failed to get sdt note: %d\n", ret); |
703 | return ret; | 703 | return ret; |
704 | } | 704 | } |
705 | list_for_each_entry(note, &sdtlist, note_list) { | 705 | list_for_each_entry(note, &sdtlist, note_list) { |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 8daca4fc1f8d..df4debe564da 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -955,6 +955,11 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | |||
955 | dwarf_diename(in_die)); | 955 | dwarf_diename(in_die)); |
956 | return -ENOENT; | 956 | return -ENOENT; |
957 | } | 957 | } |
958 | if (addr == 0) { | ||
959 | pr_debug("%s has no valid entry address. skipped.\n", | ||
960 | dwarf_diename(in_die)); | ||
961 | return -ENOENT; | ||
962 | } | ||
958 | pf->addr = addr; | 963 | pf->addr = addr; |
959 | pf->addr += pp->offset; | 964 | pf->addr += pp->offset; |
960 | pr_debug("found inline addr: 0x%jx\n", | 965 | pr_debug("found inline addr: 0x%jx\n", |
@@ -988,7 +993,8 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
988 | if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) | 993 | if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) |
989 | return DWARF_CB_OK; | 994 | return DWARF_CB_OK; |
990 | 995 | ||
991 | pr_debug("Matched function: %s\n", dwarf_diename(sp_die)); | 996 | pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), |
997 | (unsigned long)dwarf_dieoffset(sp_die)); | ||
992 | pf->fname = dwarf_decl_file(sp_die); | 998 | pf->fname = dwarf_decl_file(sp_die); |
993 | if (pp->line) { /* Function relative line */ | 999 | if (pp->line) { /* Function relative line */ |
994 | dwarf_decl_line(sp_die, &pf->lno); | 1000 | dwarf_decl_line(sp_die, &pf->lno); |
@@ -997,8 +1003,13 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
997 | } else if (die_is_func_instance(sp_die)) { | 1003 | } else if (die_is_func_instance(sp_die)) { |
998 | /* Instances always have the entry address */ | 1004 | /* Instances always have the entry address */ |
999 | dwarf_entrypc(sp_die, &pf->addr); | 1005 | dwarf_entrypc(sp_die, &pf->addr); |
1006 | /* But in some case the entry address is 0 */ | ||
1007 | if (pf->addr == 0) { | ||
1008 | pr_debug("%s has no entry PC. Skipped\n", | ||
1009 | dwarf_diename(sp_die)); | ||
1010 | param->retval = 0; | ||
1000 | /* Real function */ | 1011 | /* Real function */ |
1001 | if (pp->lazy_line) | 1012 | } else if (pp->lazy_line) |
1002 | param->retval = find_probe_point_lazy(sp_die, pf); | 1013 | param->retval = find_probe_point_lazy(sp_die, pf); |
1003 | else { | 1014 | else { |
1004 | skip_prologue(sp_die, pf); | 1015 | skip_prologue(sp_die, pf); |
@@ -1011,7 +1022,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
1011 | param->retval = die_walk_instances(sp_die, | 1022 | param->retval = die_walk_instances(sp_die, |
1012 | probe_point_inline_cb, (void *)pf); | 1023 | probe_point_inline_cb, (void *)pf); |
1013 | /* This could be a non-existed inline definition */ | 1024 | /* This could be a non-existed inline definition */ |
1014 | if (param->retval == -ENOENT && strisglob(pp->function)) | 1025 | if (param->retval == -ENOENT) |
1015 | param->retval = 0; | 1026 | param->retval = 0; |
1016 | } | 1027 | } |
1017 | 1028 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 19c9c558454f..aecff69a510d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -345,6 +345,16 @@ static struct symbol *symbols__first(struct rb_root *symbols) | |||
345 | return NULL; | 345 | return NULL; |
346 | } | 346 | } |
347 | 347 | ||
348 | static struct symbol *symbols__last(struct rb_root *symbols) | ||
349 | { | ||
350 | struct rb_node *n = rb_last(symbols); | ||
351 | |||
352 | if (n) | ||
353 | return rb_entry(n, struct symbol, rb_node); | ||
354 | |||
355 | return NULL; | ||
356 | } | ||
357 | |||
348 | static struct symbol *symbols__next(struct symbol *sym) | 358 | static struct symbol *symbols__next(struct symbol *sym) |
349 | { | 359 | { |
350 | struct rb_node *n = rb_next(&sym->rb_node); | 360 | struct rb_node *n = rb_next(&sym->rb_node); |
@@ -466,6 +476,11 @@ struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) | |||
466 | return symbols__first(&dso->symbols[type]); | 476 | return symbols__first(&dso->symbols[type]); |
467 | } | 477 | } |
468 | 478 | ||
479 | struct symbol *dso__last_symbol(struct dso *dso, enum map_type type) | ||
480 | { | ||
481 | return symbols__last(&dso->symbols[type]); | ||
482 | } | ||
483 | |||
469 | struct symbol *dso__next_symbol(struct symbol *sym) | 484 | struct symbol *dso__next_symbol(struct symbol *sym) |
470 | { | 485 | { |
471 | return symbols__next(sym); | 486 | return symbols__next(sym); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 0dacfb7d5b67..d964844eb314 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -259,6 +259,7 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | |||
259 | struct symbol *symbol__next_by_name(struct symbol *sym); | 259 | struct symbol *symbol__next_by_name(struct symbol *sym); |
260 | 260 | ||
261 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); | 261 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); |
262 | struct symbol *dso__last_symbol(struct dso *dso, enum map_type type); | ||
262 | struct symbol *dso__next_symbol(struct symbol *sym); | 263 | struct symbol *dso__next_symbol(struct symbol *sym); |
263 | 264 | ||
264 | enum dso_type dso__type_fd(int fd); | 265 | enum dso_type dso__type_fd(int fd); |