diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-08-04 05:02:38 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-08-04 05:02:38 -0400 |
commit | f282f7a0ecc3e0b8fd8532a6c3e9401534cb907c (patch) | |
tree | 93dacb43169c8339d47282d06b4eb4a8b42bcd18 /tools | |
parent | 0d87d7ec22a0879d3926faa4f4f4412a5dee1fba (diff) | |
parent | c369e0a1a8fa6ca80e6c37c8735d9427b623ae62 (diff) |
Merge tag 'perf-core-for-mingo-20160803' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
New features:
- Add --sample-cpu to 'perf record', to explicitely ask for sampling
the CPU (Jiri Olsa)
Fixes:
- Fix processing of multi byte chunks in objdump output, fixing
disassemble processing for annotation on at least ARM64 (Jan Stancek)
- Use SyS_epoll_wait in a BPF 'perf test' entry instead of sys_epoll_wait, that
is not present in the DWARF info in vmlinux files (Arnaldo Carvalho de Melo)
- Add -wno-shadow when processing files using perl headers, fixing
the build on Fedora Rawhide and Arch Linux (Namhyung Kim)
Infrastructure changes:
- Annotate prep work to better catch and report errors related to
using objdump to disassemble DSOs (Arnaldo Carvalho de Melo)
- Add 'alloc', 'scnprintf' and 'and' methods for bitmap processing (Jiri Olsa)
- Add nested output resorting callback in hists processing (Jiri Olsa)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
26 files changed, 394 insertions, 106 deletions
diff --git a/tools/include/linux/bitmap.h b/tools/include/linux/bitmap.h index 28f5493da491..43c1c5021e4b 100644 --- a/tools/include/linux/bitmap.h +++ b/tools/include/linux/bitmap.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <linux/bitops.h> | 5 | #include <linux/bitops.h> |
6 | #include <stdlib.h> | ||
6 | 7 | ||
7 | #define DECLARE_BITMAP(name,bits) \ | 8 | #define DECLARE_BITMAP(name,bits) \ |
8 | unsigned long name[BITS_TO_LONGS(bits)] | 9 | unsigned long name[BITS_TO_LONGS(bits)] |
@@ -10,6 +11,8 @@ | |||
10 | int __bitmap_weight(const unsigned long *bitmap, int bits); | 11 | int __bitmap_weight(const unsigned long *bitmap, int bits); |
11 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | 12 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, |
12 | const unsigned long *bitmap2, int bits); | 13 | const unsigned long *bitmap2, int bits); |
14 | int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, | ||
15 | const unsigned long *bitmap2, unsigned int bits); | ||
13 | 16 | ||
14 | #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) | 17 | #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) |
15 | 18 | ||
@@ -65,4 +68,38 @@ static inline int test_and_set_bit(int nr, unsigned long *addr) | |||
65 | return (old & mask) != 0; | 68 | return (old & mask) != 0; |
66 | } | 69 | } |
67 | 70 | ||
71 | /** | ||
72 | * bitmap_alloc - Allocate bitmap | ||
73 | * @nr: Bit to set | ||
74 | */ | ||
75 | static inline unsigned long *bitmap_alloc(int nbits) | ||
76 | { | ||
77 | return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * bitmap_scnprintf - print bitmap list into buffer | ||
82 | * @bitmap: bitmap | ||
83 | * @nbits: size of bitmap | ||
84 | * @buf: buffer to store output | ||
85 | * @size: size of @buf | ||
86 | */ | ||
87 | size_t bitmap_scnprintf(unsigned long *bitmap, int nbits, | ||
88 | char *buf, size_t size); | ||
89 | |||
90 | /** | ||
91 | * bitmap_and - Do logical and on bitmaps | ||
92 | * @dst: resulting bitmap | ||
93 | * @src1: operand 1 | ||
94 | * @src2: operand 2 | ||
95 | * @nbits: size of bitmap | ||
96 | */ | ||
97 | static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, | ||
98 | const unsigned long *src2, unsigned int nbits) | ||
99 | { | ||
100 | if (small_const_nbits(nbits)) | ||
101 | return (*dst = *src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits)) != 0; | ||
102 | return __bitmap_and(dst, src1, src2, nbits); | ||
103 | } | ||
104 | |||
68 | #endif /* _PERF_BITOPS_H */ | 105 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/lib/bitmap.c b/tools/lib/bitmap.c index 0a1adc1111fd..38748b0e342f 100644 --- a/tools/lib/bitmap.c +++ b/tools/lib/bitmap.c | |||
@@ -29,3 +29,47 @@ void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | |||
29 | for (k = 0; k < nr; k++) | 29 | for (k = 0; k < nr; k++) |
30 | dst[k] = bitmap1[k] | bitmap2[k]; | 30 | dst[k] = bitmap1[k] | bitmap2[k]; |
31 | } | 31 | } |
32 | |||
33 | size_t bitmap_scnprintf(unsigned long *bitmap, int nbits, | ||
34 | char *buf, size_t size) | ||
35 | { | ||
36 | /* current bit is 'cur', most recently seen range is [rbot, rtop] */ | ||
37 | int cur, rbot, rtop; | ||
38 | bool first = true; | ||
39 | size_t ret = 0; | ||
40 | |||
41 | rbot = cur = find_first_bit(bitmap, nbits); | ||
42 | while (cur < nbits) { | ||
43 | rtop = cur; | ||
44 | cur = find_next_bit(bitmap, nbits, cur + 1); | ||
45 | if (cur < nbits && cur <= rtop + 1) | ||
46 | continue; | ||
47 | |||
48 | if (!first) | ||
49 | ret += scnprintf(buf + ret, size - ret, ","); | ||
50 | |||
51 | first = false; | ||
52 | |||
53 | ret += scnprintf(buf + ret, size - ret, "%d", rbot); | ||
54 | if (rbot < rtop) | ||
55 | ret += scnprintf(buf + ret, size - ret, "-%d", rtop); | ||
56 | |||
57 | rbot = cur; | ||
58 | } | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, | ||
63 | const unsigned long *bitmap2, unsigned int bits) | ||
64 | { | ||
65 | unsigned int k; | ||
66 | unsigned int lim = bits/BITS_PER_LONG; | ||
67 | unsigned long result = 0; | ||
68 | |||
69 | for (k = 0; k < lim; k++) | ||
70 | result |= (dst[k] = bitmap1[k] & bitmap2[k]); | ||
71 | if (bits % BITS_PER_LONG) | ||
72 | result |= (dst[k] = bitmap1[k] & bitmap2[k] & | ||
73 | BITMAP_LAST_WORD_MASK(bits)); | ||
74 | return result != 0; | ||
75 | } | ||
diff --git a/tools/lib/traceevent/.gitignore b/tools/lib/traceevent/.gitignore index 3c60335fe7be..9e9f25fb1922 100644 --- a/tools/lib/traceevent/.gitignore +++ b/tools/lib/traceevent/.gitignore | |||
@@ -1,2 +1,3 @@ | |||
1 | TRACEEVENT-CFLAGS | 1 | TRACEEVENT-CFLAGS |
2 | libtraceevent-dynamic-list | 2 | libtraceevent-dynamic-list |
3 | libtraceevent.so.* | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 69966abf65d1..379a2bed07c0 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -192,6 +192,9 @@ OPTIONS | |||
192 | --period:: | 192 | --period:: |
193 | Record the sample period. | 193 | Record the sample period. |
194 | 194 | ||
195 | --sample-cpu:: | ||
196 | Record the sample cpu. | ||
197 | |||
195 | -n:: | 198 | -n:: |
196 | --no-samples:: | 199 | --no-samples:: |
197 | Don't sample. | 200 | Don't sample. |
diff --git a/tools/perf/config/Makefile b/tools/perf/Makefile.config index 24803c58049a..24803c58049a 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/Makefile.config | |||
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 6641abb97f0a..2d9087501633 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -161,7 +161,7 @@ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ | |||
161 | BPF_DIR = $(srctree)/tools/lib/bpf/ | 161 | BPF_DIR = $(srctree)/tools/lib/bpf/ |
162 | SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ | 162 | SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ |
163 | 163 | ||
164 | # include config/Makefile by default and rule out | 164 | # include Makefile.config by default and rule out |
165 | # non-config cases | 165 | # non-config cases |
166 | config := 1 | 166 | config := 1 |
167 | 167 | ||
@@ -183,7 +183,7 @@ ifeq ($(filter feature-dump,$(MAKECMDGOALS)),feature-dump) | |||
183 | FEATURE_TESTS := all | 183 | FEATURE_TESTS := all |
184 | endif | 184 | endif |
185 | endif | 185 | endif |
186 | include config/Makefile | 186 | include Makefile.config |
187 | endif | 187 | endif |
188 | 188 | ||
189 | ifeq ($(config),0) | 189 | ifeq ($(config),0) |
@@ -706,7 +706,7 @@ $(INSTALL_DOC_TARGETS): | |||
706 | ### Cleaning rules | 706 | ### Cleaning rules |
707 | 707 | ||
708 | # | 708 | # |
709 | # This is here, not in config/Makefile, because config/Makefile does | 709 | # This is here, not in Makefile.config, because Makefile.config does |
710 | # not get included for the clean target: | 710 | # not get included for the clean target: |
711 | # | 711 | # |
712 | config-clean: | 712 | config-clean: |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8f2c16d9275f..6355902fbfc8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -1434,6 +1434,7 @@ struct option __record_options[] = { | |||
1434 | OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat, | 1434 | OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat, |
1435 | "per thread counts"), | 1435 | "per thread counts"), |
1436 | OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"), | 1436 | OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"), |
1437 | OPT_BOOLEAN(0, "sample-cpu", &record.opts.sample_cpu, "Record the sample cpu"), | ||
1437 | OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time, | 1438 | OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time, |
1438 | &record.opts.sample_time_set, | 1439 | &record.opts.sample_time_set, |
1439 | "Record the sample timestamps"), | 1440 | "Record the sample timestamps"), |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index bd108683fcb8..418ed94756d3 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -128,10 +128,14 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) | |||
128 | return err; | 128 | return err; |
129 | } | 129 | } |
130 | 130 | ||
131 | err = symbol__annotate(sym, map, 0); | 131 | err = symbol__disassemble(sym, map, 0); |
132 | if (err == 0) { | 132 | if (err == 0) { |
133 | out_assign: | 133 | out_assign: |
134 | top->sym_filter_entry = he; | 134 | top->sym_filter_entry = he; |
135 | } else { | ||
136 | char msg[BUFSIZ]; | ||
137 | symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); | ||
138 | pr_err("Couldn't annotate %s: %s\n", sym->name, msg); | ||
135 | } | 139 | } |
136 | 140 | ||
137 | pthread_mutex_unlock(¬es->lock); | 141 | pthread_mutex_unlock(¬es->lock); |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a7e0f1497244..cb0f1356ff81 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -52,6 +52,7 @@ struct record_opts { | |||
52 | bool sample_weight; | 52 | bool sample_weight; |
53 | bool sample_time; | 53 | bool sample_time; |
54 | bool sample_time_set; | 54 | bool sample_time_set; |
55 | bool sample_cpu; | ||
55 | bool period; | 56 | bool period; |
56 | bool running_time; | 57 | bool running_time; |
57 | bool full_auxtrace; | 58 | bool full_auxtrace; |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Build b/tools/perf/scripts/perl/Perf-Trace-Util/Build index 928e110179cb..34faecf774ae 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Build +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Build | |||
@@ -1,3 +1,5 @@ | |||
1 | libperf-y += Context.o | 1 | libperf-y += Context.o |
2 | 2 | ||
3 | CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default | 3 | CFLAGS_Context.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes |
4 | CFLAGS_Context.o += -Wno-unused-parameter -Wno-nested-externs -Wno-undef | ||
5 | CFLAGS_Context.o += -Wno-switch-default -Wno-shadow | ||
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index cb20ae1c0d35..dc51bc570e51 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -41,6 +41,7 @@ perf-y += event-times.o | |||
41 | perf-y += backward-ring-buffer.o | 41 | perf-y += backward-ring-buffer.o |
42 | perf-y += sdt.o | 42 | perf-y += sdt.o |
43 | perf-y += is_printable_array.o | 43 | perf-y += is_printable_array.o |
44 | perf-y += bitmap.o | ||
44 | 45 | ||
45 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build | 46 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build |
46 | $(call rule_mkdir) | 47 | $(call rule_mkdir) |
diff --git a/tools/perf/tests/bitmap.c b/tools/perf/tests/bitmap.c new file mode 100644 index 000000000000..9abe6c13090f --- /dev/null +++ b/tools/perf/tests/bitmap.c | |||
@@ -0,0 +1,53 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/bitmap.h> | ||
3 | #include "tests.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "debug.h" | ||
6 | |||
7 | #define NBITS 100 | ||
8 | |||
9 | static unsigned long *get_bitmap(const char *str, int nbits) | ||
10 | { | ||
11 | struct cpu_map *map = cpu_map__new(str); | ||
12 | unsigned long *bm = NULL; | ||
13 | int i; | ||
14 | |||
15 | bm = bitmap_alloc(nbits); | ||
16 | |||
17 | if (map && bm) { | ||
18 | bitmap_zero(bm, nbits); | ||
19 | |||
20 | for (i = 0; i < map->nr; i++) | ||
21 | set_bit(map->map[i], bm); | ||
22 | } | ||
23 | |||
24 | if (map) | ||
25 | cpu_map__put(map); | ||
26 | return bm; | ||
27 | } | ||
28 | |||
29 | static int test_bitmap(const char *str) | ||
30 | { | ||
31 | unsigned long *bm = get_bitmap(str, NBITS); | ||
32 | char buf[100]; | ||
33 | int ret; | ||
34 | |||
35 | bitmap_scnprintf(bm, NBITS, buf, sizeof(buf)); | ||
36 | pr_debug("bitmap: %s\n", buf); | ||
37 | |||
38 | ret = !strcmp(buf, str); | ||
39 | free(bm); | ||
40 | return ret; | ||
41 | } | ||
42 | |||
43 | int test__bitmap_print(int subtest __maybe_unused) | ||
44 | { | ||
45 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1")); | ||
46 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,5")); | ||
47 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3,5,7,9,11,13,15,17,19,21-40")); | ||
48 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("2-5")); | ||
49 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3-6,8-10,24,35-37")); | ||
50 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1,3-6,8-10,24,35-37")); | ||
51 | TEST_ASSERT_VAL("failed to convert map", test_bitmap("1-10,12-20,22-30,32-40")); | ||
52 | return 0; | ||
53 | } | ||
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c index e53bc91fa260..268e5f8e4aa2 100644 --- a/tools/perf/tests/bpf-script-example.c +++ b/tools/perf/tests/bpf-script-example.c | |||
@@ -31,8 +31,8 @@ struct bpf_map_def SEC("maps") flip_table = { | |||
31 | .max_entries = 1, | 31 | .max_entries = 1, |
32 | }; | 32 | }; |
33 | 33 | ||
34 | SEC("func=sys_epoll_wait") | 34 | SEC("func=SyS_epoll_wait") |
35 | int bpf_func__sys_epoll_wait(void *ctx) | 35 | int bpf_func__SyS_epoll_wait(void *ctx) |
36 | { | 36 | { |
37 | int ind =0; | 37 | int ind =0; |
38 | int *flag = bpf_map_lookup_elem(&flip_table, &ind); | 38 | int *flag = bpf_map_lookup_elem(&flip_table, &ind); |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 10eb30686c9c..778668a2a966 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -226,6 +226,10 @@ static struct test generic_tests[] = { | |||
226 | .func = test__is_printable_array, | 226 | .func = test__is_printable_array, |
227 | }, | 227 | }, |
228 | { | 228 | { |
229 | .desc = "Test bitmap print", | ||
230 | .func = test__bitmap_print, | ||
231 | }, | ||
232 | { | ||
229 | .func = NULL, | 233 | .func = NULL, |
230 | }, | 234 | }, |
231 | }; | 235 | }; |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 68a69a195545..2af156a8d4e5 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -33,44 +33,86 @@ static unsigned int hex(char c) | |||
33 | return c - 'A' + 10; | 33 | return c - 'A' + 10; |
34 | } | 34 | } |
35 | 35 | ||
36 | static size_t read_objdump_line(const char *line, size_t line_len, void *buf, | 36 | static size_t read_objdump_chunk(const char **line, unsigned char **buf, |
37 | size_t len) | 37 | size_t *buf_len) |
38 | { | 38 | { |
39 | const char *p; | 39 | size_t bytes_read = 0; |
40 | size_t i, j = 0; | 40 | unsigned char *chunk_start = *buf; |
41 | |||
42 | /* Skip to a colon */ | ||
43 | p = strchr(line, ':'); | ||
44 | if (!p) | ||
45 | return 0; | ||
46 | i = p + 1 - line; | ||
47 | 41 | ||
48 | /* Read bytes */ | 42 | /* Read bytes */ |
49 | while (j < len) { | 43 | while (*buf_len > 0) { |
50 | char c1, c2; | 44 | char c1, c2; |
51 | 45 | ||
52 | /* Skip spaces */ | ||
53 | for (; i < line_len; i++) { | ||
54 | if (!isspace(line[i])) | ||
55 | break; | ||
56 | } | ||
57 | /* Get 2 hex digits */ | 46 | /* Get 2 hex digits */ |
58 | if (i >= line_len || !isxdigit(line[i])) | 47 | c1 = *(*line)++; |
48 | if (!isxdigit(c1)) | ||
59 | break; | 49 | break; |
60 | c1 = line[i++]; | 50 | c2 = *(*line)++; |
61 | if (i >= line_len || !isxdigit(line[i])) | 51 | if (!isxdigit(c2)) |
62 | break; | 52 | break; |
63 | c2 = line[i++]; | 53 | |
64 | /* Followed by a space */ | 54 | /* Store byte and advance buf */ |
65 | if (i < line_len && line[i] && !isspace(line[i])) | 55 | **buf = (hex(c1) << 4) | hex(c2); |
56 | (*buf)++; | ||
57 | (*buf_len)--; | ||
58 | bytes_read++; | ||
59 | |||
60 | /* End of chunk? */ | ||
61 | if (isspace(**line)) | ||
66 | break; | 62 | break; |
67 | /* Store byte */ | ||
68 | *(unsigned char *)buf = (hex(c1) << 4) | hex(c2); | ||
69 | buf += 1; | ||
70 | j++; | ||
71 | } | 63 | } |
64 | |||
65 | /* | ||
66 | * objdump will display raw insn as LE if code endian | ||
67 | * is LE and bytes_per_chunk > 1. In that case reverse | ||
68 | * the chunk we just read. | ||
69 | * | ||
70 | * see disassemble_bytes() at binutils/objdump.c for details | ||
71 | * how objdump chooses display endian) | ||
72 | */ | ||
73 | if (bytes_read > 1 && !bigendian()) { | ||
74 | unsigned char *chunk_end = chunk_start + bytes_read - 1; | ||
75 | unsigned char tmp; | ||
76 | |||
77 | while (chunk_start < chunk_end) { | ||
78 | tmp = *chunk_start; | ||
79 | *chunk_start = *chunk_end; | ||
80 | *chunk_end = tmp; | ||
81 | chunk_start++; | ||
82 | chunk_end--; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | return bytes_read; | ||
87 | } | ||
88 | |||
89 | static size_t read_objdump_line(const char *line, unsigned char *buf, | ||
90 | size_t buf_len) | ||
91 | { | ||
92 | const char *p; | ||
93 | size_t ret, bytes_read = 0; | ||
94 | |||
95 | /* Skip to a colon */ | ||
96 | p = strchr(line, ':'); | ||
97 | if (!p) | ||
98 | return 0; | ||
99 | p++; | ||
100 | |||
101 | /* Skip initial spaces */ | ||
102 | while (*p) { | ||
103 | if (!isspace(*p)) | ||
104 | break; | ||
105 | p++; | ||
106 | } | ||
107 | |||
108 | do { | ||
109 | ret = read_objdump_chunk(&p, &buf, &buf_len); | ||
110 | bytes_read += ret; | ||
111 | p++; | ||
112 | } while (ret > 0); | ||
113 | |||
72 | /* return number of successfully read bytes */ | 114 | /* return number of successfully read bytes */ |
73 | return j; | 115 | return bytes_read; |
74 | } | 116 | } |
75 | 117 | ||
76 | static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) | 118 | static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) |
@@ -95,7 +137,7 @@ static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) | |||
95 | } | 137 | } |
96 | 138 | ||
97 | /* read objdump data into temporary buffer */ | 139 | /* read objdump data into temporary buffer */ |
98 | read_bytes = read_objdump_line(line, ret, tmp, sizeof(tmp)); | 140 | read_bytes = read_objdump_line(line, tmp, sizeof(tmp)); |
99 | if (!read_bytes) | 141 | if (!read_bytes) |
100 | continue; | 142 | continue; |
101 | 143 | ||
@@ -152,7 +194,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf, | |||
152 | 194 | ||
153 | ret = read_objdump_output(f, buf, &len, addr); | 195 | ret = read_objdump_output(f, buf, &len, addr); |
154 | if (len) { | 196 | if (len) { |
155 | pr_debug("objdump read too few bytes\n"); | 197 | pr_debug("objdump read too few bytes: %zd\n", len); |
156 | if (!ret) | 198 | if (!ret) |
157 | ret = len; | 199 | ret = len; |
158 | } | 200 | } |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 9bfc0e06c61a..7c196c585472 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -90,6 +90,7 @@ int test__backward_ring_buffer(int subtest); | |||
90 | int test__cpu_map_print(int subtest); | 90 | int test__cpu_map_print(int subtest); |
91 | int test__sdt_event(int subtest); | 91 | int test__sdt_event(int subtest); |
92 | int test__is_printable_array(int subtest); | 92 | int test__is_printable_array(int subtest); |
93 | int test__bitmap_print(int subtest); | ||
93 | 94 | ||
94 | #if defined(__arm__) || defined(__aarch64__) | 95 | #if defined(__arm__) || defined(__aarch64__) |
95 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 96 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 29dc6d20364e..2e2d10022355 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -1026,7 +1026,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, | |||
1026 | .use_navkeypressed = true, | 1026 | .use_navkeypressed = true, |
1027 | }, | 1027 | }, |
1028 | }; | 1028 | }; |
1029 | int ret = -1; | 1029 | int ret = -1, err; |
1030 | int nr_pcnt = 1; | 1030 | int nr_pcnt = 1; |
1031 | size_t sizeof_bdl = sizeof(struct browser_disasm_line); | 1031 | size_t sizeof_bdl = sizeof(struct browser_disasm_line); |
1032 | 1032 | ||
@@ -1050,8 +1050,11 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, | |||
1050 | (nr_pcnt - 1); | 1050 | (nr_pcnt - 1); |
1051 | } | 1051 | } |
1052 | 1052 | ||
1053 | if (symbol__annotate(sym, map, sizeof_bdl) < 0) { | 1053 | err = symbol__disassemble(sym, map, sizeof_bdl); |
1054 | ui__error("%s", ui_helpline__last_msg); | 1054 | if (err) { |
1055 | char msg[BUFSIZ]; | ||
1056 | symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); | ||
1057 | ui__error("Couldn't annotate %s:\n%s", sym->name, msg); | ||
1055 | goto out_free_offsets; | 1058 | goto out_free_offsets; |
1056 | } | 1059 | } |
1057 | 1060 | ||
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 9c7ff8d31b27..42d319927762 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c | |||
@@ -162,12 +162,16 @@ static int symbol__gtk_annotate(struct symbol *sym, struct map *map, | |||
162 | GtkWidget *notebook; | 162 | GtkWidget *notebook; |
163 | GtkWidget *scrolled_window; | 163 | GtkWidget *scrolled_window; |
164 | GtkWidget *tab_label; | 164 | GtkWidget *tab_label; |
165 | int err; | ||
165 | 166 | ||
166 | if (map->dso->annotate_warned) | 167 | if (map->dso->annotate_warned) |
167 | return -1; | 168 | return -1; |
168 | 169 | ||
169 | if (symbol__annotate(sym, map, 0) < 0) { | 170 | err = symbol__disassemble(sym, map, 0); |
170 | ui__error("%s", ui_helpline__current); | 171 | if (err) { |
172 | char msg[BUFSIZ]; | ||
173 | symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg)); | ||
174 | ui__error("Couldn't annotate %s: %s\n", sym->name, msg); | ||
171 | return -1; | 175 | return -1; |
172 | } | 176 | } |
173 | 177 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e9825fe825fd..4024d309bb00 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -1123,7 +1123,46 @@ static void delete_last_nop(struct symbol *sym) | |||
1123 | } | 1123 | } |
1124 | } | 1124 | } |
1125 | 1125 | ||
1126 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | 1126 | int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map, |
1127 | int errnum, char *buf, size_t buflen) | ||
1128 | { | ||
1129 | struct dso *dso = map->dso; | ||
1130 | |||
1131 | BUG_ON(buflen == 0); | ||
1132 | |||
1133 | if (errnum >= 0) { | ||
1134 | str_error_r(errnum, buf, buflen); | ||
1135 | return 0; | ||
1136 | } | ||
1137 | |||
1138 | switch (errnum) { | ||
1139 | case SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX: { | ||
1140 | char bf[SBUILD_ID_SIZE + 15] = " with build id "; | ||
1141 | char *build_id_msg = NULL; | ||
1142 | |||
1143 | if (dso->has_build_id) { | ||
1144 | build_id__sprintf(dso->build_id, | ||
1145 | sizeof(dso->build_id), bf + 15); | ||
1146 | build_id_msg = bf; | ||
1147 | } | ||
1148 | scnprintf(buf, buflen, | ||
1149 | "No vmlinux file%s\nwas found in the path.\n\n" | ||
1150 | "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" | ||
1151 | "Please use:\n\n" | ||
1152 | " perf buildid-cache -vu vmlinux\n\n" | ||
1153 | "or:\n\n" | ||
1154 | " --vmlinux vmlinux\n", build_id_msg ?: ""); | ||
1155 | } | ||
1156 | break; | ||
1157 | default: | ||
1158 | scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); | ||
1159 | break; | ||
1160 | } | ||
1161 | |||
1162 | return 0; | ||
1163 | } | ||
1164 | |||
1165 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) | ||
1127 | { | 1166 | { |
1128 | struct dso *dso = map->dso; | 1167 | struct dso *dso = map->dso; |
1129 | char *filename = dso__build_id_filename(dso, NULL, 0); | 1168 | char *filename = dso__build_id_filename(dso, NULL, 0); |
@@ -1134,22 +1173,20 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
1134 | char symfs_filename[PATH_MAX]; | 1173 | char symfs_filename[PATH_MAX]; |
1135 | struct kcore_extract kce; | 1174 | struct kcore_extract kce; |
1136 | bool delete_extract = false; | 1175 | bool delete_extract = false; |
1176 | int stdout_fd[2]; | ||
1137 | int lineno = 0; | 1177 | int lineno = 0; |
1138 | int nline; | 1178 | int nline; |
1179 | pid_t pid; | ||
1139 | 1180 | ||
1140 | if (filename) | 1181 | if (filename) |
1141 | symbol__join_symfs(symfs_filename, filename); | 1182 | symbol__join_symfs(symfs_filename, filename); |
1142 | 1183 | ||
1143 | if (filename == NULL) { | 1184 | if (filename == NULL) { |
1144 | if (dso->has_build_id) { | 1185 | if (dso->has_build_id) |
1145 | pr_err("Can't annotate %s: not enough memory\n", | 1186 | return ENOMEM; |
1146 | sym->name); | ||
1147 | return -ENOMEM; | ||
1148 | } | ||
1149 | goto fallback; | 1187 | goto fallback; |
1150 | } else if (dso__is_kcore(dso)) { | 1188 | } else if (dso__is_kcore(dso) || |
1151 | goto fallback; | 1189 | readlink(symfs_filename, command, sizeof(command)) < 0 || |
1152 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
1153 | strstr(command, DSO__NAME_KALLSYMS) || | 1190 | strstr(command, DSO__NAME_KALLSYMS) || |
1154 | access(symfs_filename, R_OK)) { | 1191 | access(symfs_filename, R_OK)) { |
1155 | free(filename); | 1192 | free(filename); |
@@ -1166,27 +1203,7 @@ fallback: | |||
1166 | 1203 | ||
1167 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && | 1204 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
1168 | !dso__is_kcore(dso)) { | 1205 | !dso__is_kcore(dso)) { |
1169 | char bf[SBUILD_ID_SIZE + 15] = " with build id "; | 1206 | err = SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX; |
1170 | char *build_id_msg = NULL; | ||
1171 | |||
1172 | if (dso->annotate_warned) | ||
1173 | goto out_free_filename; | ||
1174 | |||
1175 | if (dso->has_build_id) { | ||
1176 | build_id__sprintf(dso->build_id, | ||
1177 | sizeof(dso->build_id), bf + 15); | ||
1178 | build_id_msg = bf; | ||
1179 | } | ||
1180 | err = -ENOENT; | ||
1181 | dso->annotate_warned = 1; | ||
1182 | pr_err("Can't annotate %s:\n\n" | ||
1183 | "No vmlinux file%s\nwas found in the path.\n\n" | ||
1184 | "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" | ||
1185 | "Please use:\n\n" | ||
1186 | " perf buildid-cache -vu vmlinux\n\n" | ||
1187 | "or:\n\n" | ||
1188 | " --vmlinux vmlinux\n", | ||
1189 | sym->name, build_id_msg ?: ""); | ||
1190 | goto out_free_filename; | 1207 | goto out_free_filename; |
1191 | } | 1208 | } |
1192 | 1209 | ||
@@ -1258,9 +1275,32 @@ fallback: | |||
1258 | 1275 | ||
1259 | pr_debug("Executing: %s\n", command); | 1276 | pr_debug("Executing: %s\n", command); |
1260 | 1277 | ||
1261 | file = popen(command, "r"); | 1278 | err = -1; |
1279 | if (pipe(stdout_fd) < 0) { | ||
1280 | pr_err("Failure creating the pipe to run %s\n", command); | ||
1281 | goto out_remove_tmp; | ||
1282 | } | ||
1283 | |||
1284 | pid = fork(); | ||
1285 | if (pid < 0) { | ||
1286 | pr_err("Failure forking to run %s\n", command); | ||
1287 | goto out_close_stdout; | ||
1288 | } | ||
1289 | |||
1290 | if (pid == 0) { | ||
1291 | close(stdout_fd[0]); | ||
1292 | dup2(stdout_fd[1], 1); | ||
1293 | close(stdout_fd[1]); | ||
1294 | execl("/bin/sh", "sh", "-c", command, NULL); | ||
1295 | perror(command); | ||
1296 | exit(-1); | ||
1297 | } | ||
1298 | |||
1299 | close(stdout_fd[1]); | ||
1300 | |||
1301 | file = fdopen(stdout_fd[0], "r"); | ||
1262 | if (!file) { | 1302 | if (!file) { |
1263 | pr_err("Failure running %s\n", command); | 1303 | pr_err("Failure creating FILE stream for %s\n", command); |
1264 | /* | 1304 | /* |
1265 | * If we were using debug info should retry with | 1305 | * If we were using debug info should retry with |
1266 | * original binary. | 1306 | * original binary. |
@@ -1286,9 +1326,11 @@ fallback: | |||
1286 | if (dso__is_kcore(dso)) | 1326 | if (dso__is_kcore(dso)) |
1287 | delete_last_nop(sym); | 1327 | delete_last_nop(sym); |
1288 | 1328 | ||
1289 | pclose(file); | 1329 | fclose(file); |
1290 | 1330 | err = 0; | |
1291 | out_remove_tmp: | 1331 | out_remove_tmp: |
1332 | close(stdout_fd[0]); | ||
1333 | |||
1292 | if (dso__needs_decompress(dso)) | 1334 | if (dso__needs_decompress(dso)) |
1293 | unlink(symfs_filename); | 1335 | unlink(symfs_filename); |
1294 | out_free_filename: | 1336 | out_free_filename: |
@@ -1297,6 +1339,10 @@ out_free_filename: | |||
1297 | if (free_filename) | 1339 | if (free_filename) |
1298 | free(filename); | 1340 | free(filename); |
1299 | return err; | 1341 | return err; |
1342 | |||
1343 | out_close_stdout: | ||
1344 | close(stdout_fd[1]); | ||
1345 | goto out_remove_tmp; | ||
1300 | } | 1346 | } |
1301 | 1347 | ||
1302 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) | 1348 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) |
@@ -1663,7 +1709,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
1663 | struct rb_root source_line = RB_ROOT; | 1709 | struct rb_root source_line = RB_ROOT; |
1664 | u64 len; | 1710 | u64 len; |
1665 | 1711 | ||
1666 | if (symbol__annotate(sym, map, 0) < 0) | 1712 | if (symbol__disassemble(sym, map, 0) < 0) |
1667 | return -1; | 1713 | return -1; |
1668 | 1714 | ||
1669 | len = symbol__size(sym); | 1715 | len = symbol__size(sym); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index a23084f54128..f67ccb027561 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -155,7 +155,27 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); | |||
155 | int symbol__alloc_hist(struct symbol *sym); | 155 | int symbol__alloc_hist(struct symbol *sym); |
156 | void symbol__annotate_zero_histograms(struct symbol *sym); | 156 | void symbol__annotate_zero_histograms(struct symbol *sym); |
157 | 157 | ||
158 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | 158 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize); |
159 | |||
160 | enum symbol_disassemble_errno { | ||
161 | SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, | ||
162 | |||
163 | /* | ||
164 | * Choose an arbitrary negative big number not to clash with standard | ||
165 | * errno since SUS requires the errno has distinct positive values. | ||
166 | * See 'Issue 6' in the link below. | ||
167 | * | ||
168 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | ||
169 | */ | ||
170 | __SYMBOL_ANNOTATE_ERRNO__START = -10000, | ||
171 | |||
172 | SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START, | ||
173 | |||
174 | __SYMBOL_ANNOTATE_ERRNO__END, | ||
175 | }; | ||
176 | |||
177 | int symbol__strerror_disassemble(struct symbol *sym, struct map *map, | ||
178 | int errnum, char *buf, size_t buflen); | ||
159 | 179 | ||
160 | int symbol__annotate_init(struct map *map, struct symbol *sym); | 180 | int symbol__annotate_init(struct map *map, struct symbol *sym); |
161 | int symbol__annotate_printf(struct symbol *sym, struct map *map, | 181 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 2a40b8e1def7..097b3ed77fdd 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -239,31 +239,13 @@ void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr) | |||
239 | 239 | ||
240 | int perf_evlist__add_default(struct perf_evlist *evlist) | 240 | int perf_evlist__add_default(struct perf_evlist *evlist) |
241 | { | 241 | { |
242 | struct perf_event_attr attr = { | 242 | struct perf_evsel *evsel = perf_evsel__new_cycles(); |
243 | .type = PERF_TYPE_HARDWARE, | ||
244 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
245 | }; | ||
246 | struct perf_evsel *evsel; | ||
247 | |||
248 | event_attr_init(&attr); | ||
249 | 243 | ||
250 | perf_event_attr__set_max_precise_ip(&attr); | ||
251 | |||
252 | evsel = perf_evsel__new(&attr); | ||
253 | if (evsel == NULL) | 244 | if (evsel == NULL) |
254 | goto error; | 245 | return -ENOMEM; |
255 | |||
256 | /* use asprintf() because free(evsel) assumes name is allocated */ | ||
257 | if (asprintf(&evsel->name, "cycles%.*s", | ||
258 | attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0) | ||
259 | goto error_free; | ||
260 | 246 | ||
261 | perf_evlist__add(evlist, evsel); | 247 | perf_evlist__add(evlist, evsel); |
262 | return 0; | 248 | return 0; |
263 | error_free: | ||
264 | perf_evsel__delete(evsel); | ||
265 | error: | ||
266 | return -ENOMEM; | ||
267 | } | 249 | } |
268 | 250 | ||
269 | int perf_evlist__add_dummy(struct perf_evlist *evlist) | 251 | int perf_evlist__add_dummy(struct perf_evlist *evlist) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8c54df61fe64..d9b80ef881cd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -253,6 +253,34 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
253 | return evsel; | 253 | return evsel; |
254 | } | 254 | } |
255 | 255 | ||
256 | struct perf_evsel *perf_evsel__new_cycles(void) | ||
257 | { | ||
258 | struct perf_event_attr attr = { | ||
259 | .type = PERF_TYPE_HARDWARE, | ||
260 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
261 | }; | ||
262 | struct perf_evsel *evsel; | ||
263 | |||
264 | event_attr_init(&attr); | ||
265 | |||
266 | perf_event_attr__set_max_precise_ip(&attr); | ||
267 | |||
268 | evsel = perf_evsel__new(&attr); | ||
269 | if (evsel == NULL) | ||
270 | goto out; | ||
271 | |||
272 | /* use asprintf() because free(evsel) assumes name is allocated */ | ||
273 | if (asprintf(&evsel->name, "cycles%.*s", | ||
274 | attr.precise_ip ? attr.precise_ip + 1 : 0, ":ppp") < 0) | ||
275 | goto error_free; | ||
276 | out: | ||
277 | return evsel; | ||
278 | error_free: | ||
279 | perf_evsel__delete(evsel); | ||
280 | evsel = NULL; | ||
281 | goto out; | ||
282 | } | ||
283 | |||
256 | /* | 284 | /* |
257 | * Returns pointer with encoded error via <linux/err.h> interface. | 285 | * Returns pointer with encoded error via <linux/err.h> interface. |
258 | */ | 286 | */ |
@@ -854,7 +882,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
854 | perf_evsel__set_sample_bit(evsel, REGS_INTR); | 882 | perf_evsel__set_sample_bit(evsel, REGS_INTR); |
855 | } | 883 | } |
856 | 884 | ||
857 | if (target__has_cpu(&opts->target)) | 885 | if (target__has_cpu(&opts->target) || opts->sample_cpu) |
858 | perf_evsel__set_sample_bit(evsel, CPU); | 886 | perf_evsel__set_sample_bit(evsel, CPU); |
859 | 887 | ||
860 | if (opts->period) | 888 | if (opts->period) |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8a4a6c9f1480..4d44129e050b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -175,6 +175,8 @@ static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char * | |||
175 | return perf_evsel__newtp_idx(sys, name, 0); | 175 | return perf_evsel__newtp_idx(sys, name, 0); |
176 | } | 176 | } |
177 | 177 | ||
178 | struct perf_evsel *perf_evsel__new_cycles(void); | ||
179 | |||
178 | struct event_format *event_format__new(const char *sys, const char *name); | 180 | struct event_format *event_format__new(const char *sys, const char *name); |
179 | 181 | ||
180 | void perf_evsel__init(struct perf_evsel *evsel, | 182 | void perf_evsel__init(struct perf_evsel *evsel, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index a18d142cdca3..de15dbcdcecf 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1672,7 +1672,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1672 | } | 1672 | } |
1673 | 1673 | ||
1674 | static void output_resort(struct hists *hists, struct ui_progress *prog, | 1674 | static void output_resort(struct hists *hists, struct ui_progress *prog, |
1675 | bool use_callchain) | 1675 | bool use_callchain, hists__resort_cb_t cb) |
1676 | { | 1676 | { |
1677 | struct rb_root *root; | 1677 | struct rb_root *root; |
1678 | struct rb_node *next; | 1678 | struct rb_node *next; |
@@ -1711,6 +1711,9 @@ static void output_resort(struct hists *hists, struct ui_progress *prog, | |||
1711 | n = rb_entry(next, struct hist_entry, rb_node_in); | 1711 | n = rb_entry(next, struct hist_entry, rb_node_in); |
1712 | next = rb_next(&n->rb_node_in); | 1712 | next = rb_next(&n->rb_node_in); |
1713 | 1713 | ||
1714 | if (cb && cb(n)) | ||
1715 | continue; | ||
1716 | |||
1714 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits, use_callchain); | 1717 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits, use_callchain); |
1715 | hists__inc_stats(hists, n); | 1718 | hists__inc_stats(hists, n); |
1716 | 1719 | ||
@@ -1731,12 +1734,18 @@ void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *pro | |||
1731 | else | 1734 | else |
1732 | use_callchain = symbol_conf.use_callchain; | 1735 | use_callchain = symbol_conf.use_callchain; |
1733 | 1736 | ||
1734 | output_resort(evsel__hists(evsel), prog, use_callchain); | 1737 | output_resort(evsel__hists(evsel), prog, use_callchain, NULL); |
1735 | } | 1738 | } |
1736 | 1739 | ||
1737 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) | 1740 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) |
1738 | { | 1741 | { |
1739 | output_resort(hists, prog, symbol_conf.use_callchain); | 1742 | output_resort(hists, prog, symbol_conf.use_callchain, NULL); |
1743 | } | ||
1744 | |||
1745 | void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog, | ||
1746 | hists__resort_cb_t cb) | ||
1747 | { | ||
1748 | output_resort(hists, prog, symbol_conf.use_callchain, cb); | ||
1740 | } | 1749 | } |
1741 | 1750 | ||
1742 | static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd) | 1751 | static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd) |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 49aa4fac148f..0a1edf1ab450 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -153,8 +153,12 @@ int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp, | |||
153 | struct perf_hpp_fmt *fmt, int printed); | 153 | struct perf_hpp_fmt *fmt, int printed); |
154 | void hist_entry__delete(struct hist_entry *he); | 154 | void hist_entry__delete(struct hist_entry *he); |
155 | 155 | ||
156 | typedef int (*hists__resort_cb_t)(struct hist_entry *he); | ||
157 | |||
156 | void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog); | 158 | void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog); |
157 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); | 159 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); |
160 | void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog, | ||
161 | hists__resort_cb_t cb); | ||
158 | int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 162 | int hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
159 | 163 | ||
160 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 164 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 8cdcf4641c51..21c4d9b23c24 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c | |||
@@ -122,11 +122,7 @@ int target__strerror(struct target *target, int errnum, | |||
122 | BUG_ON(buflen == 0); | 122 | BUG_ON(buflen == 0); |
123 | 123 | ||
124 | if (errnum >= 0) { | 124 | if (errnum >= 0) { |
125 | const char *err = str_error_r(errnum, buf, buflen); | 125 | str_error_r(errnum, buf, buflen); |
126 | |||
127 | if (err != buf) | ||
128 | scnprintf(buf, buflen, "%s", err); | ||
129 | |||
130 | return 0; | 126 | return 0; |
131 | } | 127 | } |
132 | 128 | ||