diff options
author | Jonathan Corbet <corbet@lwn.net> | 2016-12-27 14:53:44 -0500 |
---|---|---|
committer | Jonathan Corbet <corbet@lwn.net> | 2016-12-27 14:53:44 -0500 |
commit | 54ab6db0909061ab7ee07233d3cab86d29f86e6c (patch) | |
tree | a7650ab5c0fa3a6a3841de8e8693041b3e009054 /tools/perf/util | |
parent | 217e2bfab22e740227df09f22165e834cddd8a3b (diff) | |
parent | 7ce7d89f48834cefece7804d38fc5d85382edf77 (diff) |
Merge tag 'v4.10-rc1' into docs-next
Linux 4.10-rc1
Diffstat (limited to 'tools/perf/util')
65 files changed, 2136 insertions, 446 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index eb60e613d795..3840e3a87057 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -87,6 +87,7 @@ libperf-y += help-unknown-cmd.o | |||
87 | libperf-y += mem-events.o | 87 | libperf-y += mem-events.o |
88 | libperf-y += vsprintf.o | 88 | libperf-y += vsprintf.o |
89 | libperf-y += drv_configs.o | 89 | libperf-y += drv_configs.o |
90 | libperf-y += time-utils.o | ||
90 | 91 | ||
91 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o | 92 | libperf-$(CONFIG_LIBBPF) += bpf-loader.o |
92 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o | 93 | libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o |
@@ -120,9 +121,13 @@ libperf-y += demangle-rust.o | |||
120 | ifdef CONFIG_JITDUMP | 121 | ifdef CONFIG_JITDUMP |
121 | libperf-$(CONFIG_LIBELF) += jitdump.o | 122 | libperf-$(CONFIG_LIBELF) += jitdump.o |
122 | libperf-$(CONFIG_LIBELF) += genelf.o | 123 | libperf-$(CONFIG_LIBELF) += genelf.o |
123 | libperf-$(CONFIG_LIBELF) += genelf_debug.o | 124 | libperf-$(CONFIG_DWARF) += genelf_debug.o |
124 | endif | 125 | endif |
125 | 126 | ||
127 | libperf-y += perf-hooks.o | ||
128 | |||
129 | libperf-$(CONFIG_CXX) += c++/ | ||
130 | |||
126 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 131 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" |
127 | # avoid compiler warnings in 32-bit mode | 132 | # avoid compiler warnings in 32-bit mode |
128 | CFLAGS_genelf_debug.o += -Wno-packed | 133 | CFLAGS_genelf_debug.o += -Wno-packed |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index aeb5a441bd74..06cc04e5806a 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -18,16 +18,119 @@ | |||
18 | #include "annotate.h" | 18 | #include "annotate.h" |
19 | #include "evsel.h" | 19 | #include "evsel.h" |
20 | #include "block-range.h" | 20 | #include "block-range.h" |
21 | #include "arch/common.h" | ||
21 | #include <regex.h> | 22 | #include <regex.h> |
22 | #include <pthread.h> | 23 | #include <pthread.h> |
23 | #include <linux/bitops.h> | 24 | #include <linux/bitops.h> |
25 | #include <sys/utsname.h> | ||
24 | 26 | ||
25 | const char *disassembler_style; | 27 | const char *disassembler_style; |
26 | const char *objdump_path; | 28 | const char *objdump_path; |
27 | static regex_t file_lineno; | 29 | static regex_t file_lineno; |
28 | 30 | ||
29 | static struct ins *ins__find(const char *name); | 31 | static struct ins_ops *ins__find(struct arch *arch, const char *name); |
30 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 32 | static void ins__sort(struct arch *arch); |
33 | static int disasm_line__parse(char *line, const char **namep, char **rawp); | ||
34 | |||
35 | struct arch { | ||
36 | const char *name; | ||
37 | struct ins *instructions; | ||
38 | size_t nr_instructions; | ||
39 | size_t nr_instructions_allocated; | ||
40 | struct ins_ops *(*associate_instruction_ops)(struct arch *arch, const char *name); | ||
41 | bool sorted_instructions; | ||
42 | bool initialized; | ||
43 | void *priv; | ||
44 | int (*init)(struct arch *arch); | ||
45 | struct { | ||
46 | char comment_char; | ||
47 | char skip_functions_char; | ||
48 | } objdump; | ||
49 | }; | ||
50 | |||
51 | static struct ins_ops call_ops; | ||
52 | static struct ins_ops dec_ops; | ||
53 | static struct ins_ops jump_ops; | ||
54 | static struct ins_ops mov_ops; | ||
55 | static struct ins_ops nop_ops; | ||
56 | static struct ins_ops lock_ops; | ||
57 | static struct ins_ops ret_ops; | ||
58 | |||
59 | static int arch__grow_instructions(struct arch *arch) | ||
60 | { | ||
61 | struct ins *new_instructions; | ||
62 | size_t new_nr_allocated; | ||
63 | |||
64 | if (arch->nr_instructions_allocated == 0 && arch->instructions) | ||
65 | goto grow_from_non_allocated_table; | ||
66 | |||
67 | new_nr_allocated = arch->nr_instructions_allocated + 128; | ||
68 | new_instructions = realloc(arch->instructions, new_nr_allocated * sizeof(struct ins)); | ||
69 | if (new_instructions == NULL) | ||
70 | return -1; | ||
71 | |||
72 | out_update_instructions: | ||
73 | arch->instructions = new_instructions; | ||
74 | arch->nr_instructions_allocated = new_nr_allocated; | ||
75 | return 0; | ||
76 | |||
77 | grow_from_non_allocated_table: | ||
78 | new_nr_allocated = arch->nr_instructions + 128; | ||
79 | new_instructions = calloc(new_nr_allocated, sizeof(struct ins)); | ||
80 | if (new_instructions == NULL) | ||
81 | return -1; | ||
82 | |||
83 | memcpy(new_instructions, arch->instructions, arch->nr_instructions); | ||
84 | goto out_update_instructions; | ||
85 | } | ||
86 | |||
87 | static int arch__associate_ins_ops(struct arch* arch, const char *name, struct ins_ops *ops) | ||
88 | { | ||
89 | struct ins *ins; | ||
90 | |||
91 | if (arch->nr_instructions == arch->nr_instructions_allocated && | ||
92 | arch__grow_instructions(arch)) | ||
93 | return -1; | ||
94 | |||
95 | ins = &arch->instructions[arch->nr_instructions]; | ||
96 | ins->name = strdup(name); | ||
97 | if (!ins->name) | ||
98 | return -1; | ||
99 | |||
100 | ins->ops = ops; | ||
101 | arch->nr_instructions++; | ||
102 | |||
103 | ins__sort(arch); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | #include "arch/arm/annotate/instructions.c" | ||
108 | #include "arch/arm64/annotate/instructions.c" | ||
109 | #include "arch/x86/annotate/instructions.c" | ||
110 | #include "arch/powerpc/annotate/instructions.c" | ||
111 | |||
112 | static struct arch architectures[] = { | ||
113 | { | ||
114 | .name = "arm", | ||
115 | .init = arm__annotate_init, | ||
116 | }, | ||
117 | { | ||
118 | .name = "arm64", | ||
119 | .init = arm64__annotate_init, | ||
120 | }, | ||
121 | { | ||
122 | .name = "x86", | ||
123 | .instructions = x86__instructions, | ||
124 | .nr_instructions = ARRAY_SIZE(x86__instructions), | ||
125 | .objdump = { | ||
126 | .comment_char = '#', | ||
127 | }, | ||
128 | }, | ||
129 | { | ||
130 | .name = "powerpc", | ||
131 | .init = powerpc__annotate_init, | ||
132 | }, | ||
133 | }; | ||
31 | 134 | ||
32 | static void ins__delete(struct ins_operands *ops) | 135 | static void ins__delete(struct ins_operands *ops) |
33 | { | 136 | { |
@@ -54,7 +157,7 @@ int ins__scnprintf(struct ins *ins, char *bf, size_t size, | |||
54 | return ins__raw_scnprintf(ins, bf, size, ops); | 157 | return ins__raw_scnprintf(ins, bf, size, ops); |
55 | } | 158 | } |
56 | 159 | ||
57 | static int call__parse(struct ins_operands *ops, struct map *map) | 160 | static int call__parse(struct arch *arch, struct ins_operands *ops, struct map *map) |
58 | { | 161 | { |
59 | char *endptr, *tok, *name; | 162 | char *endptr, *tok, *name; |
60 | 163 | ||
@@ -66,10 +169,9 @@ static int call__parse(struct ins_operands *ops, struct map *map) | |||
66 | 169 | ||
67 | name++; | 170 | name++; |
68 | 171 | ||
69 | #ifdef __arm__ | 172 | if (arch->objdump.skip_functions_char && |
70 | if (strchr(name, '+')) | 173 | strchr(name, arch->objdump.skip_functions_char)) |
71 | return -1; | 174 | return -1; |
72 | #endif | ||
73 | 175 | ||
74 | tok = strchr(name, '>'); | 176 | tok = strchr(name, '>'); |
75 | if (tok == NULL) | 177 | if (tok == NULL) |
@@ -118,16 +220,22 @@ bool ins__is_call(const struct ins *ins) | |||
118 | return ins->ops == &call_ops; | 220 | return ins->ops == &call_ops; |
119 | } | 221 | } |
120 | 222 | ||
121 | static int jump__parse(struct ins_operands *ops, struct map *map __maybe_unused) | 223 | static int jump__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) |
122 | { | 224 | { |
123 | const char *s = strchr(ops->raw, '+'); | 225 | const char *s = strchr(ops->raw, '+'); |
226 | const char *c = strchr(ops->raw, ','); | ||
124 | 227 | ||
125 | ops->target.addr = strtoull(ops->raw, NULL, 16); | 228 | if (c++ != NULL) |
229 | ops->target.addr = strtoull(c, NULL, 16); | ||
230 | else | ||
231 | ops->target.addr = strtoull(ops->raw, NULL, 16); | ||
126 | 232 | ||
127 | if (s++ != NULL) | 233 | if (s++ != NULL) { |
128 | ops->target.offset = strtoull(s, NULL, 16); | 234 | ops->target.offset = strtoull(s, NULL, 16); |
129 | else | 235 | ops->target.offset_avail = true; |
130 | ops->target.offset = UINT64_MAX; | 236 | } else { |
237 | ops->target.offset_avail = false; | ||
238 | } | ||
131 | 239 | ||
132 | return 0; | 240 | return 0; |
133 | } | 241 | } |
@@ -135,6 +243,9 @@ static int jump__parse(struct ins_operands *ops, struct map *map __maybe_unused) | |||
135 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, | 243 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, |
136 | struct ins_operands *ops) | 244 | struct ins_operands *ops) |
137 | { | 245 | { |
246 | if (!ops->target.addr || ops->target.offset < 0) | ||
247 | return ins__raw_scnprintf(ins, bf, size, ops); | ||
248 | |||
138 | return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); | 249 | return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); |
139 | } | 250 | } |
140 | 251 | ||
@@ -173,28 +284,22 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) | |||
173 | return 0; | 284 | return 0; |
174 | } | 285 | } |
175 | 286 | ||
176 | static int lock__parse(struct ins_operands *ops, struct map *map) | 287 | static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) |
177 | { | 288 | { |
178 | char *name; | ||
179 | |||
180 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); | 289 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); |
181 | if (ops->locked.ops == NULL) | 290 | if (ops->locked.ops == NULL) |
182 | return 0; | 291 | return 0; |
183 | 292 | ||
184 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | 293 | if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0) |
185 | goto out_free_ops; | 294 | goto out_free_ops; |
186 | 295 | ||
187 | ops->locked.ins = ins__find(name); | 296 | ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name); |
188 | free(name); | ||
189 | 297 | ||
190 | if (ops->locked.ins == NULL) | 298 | if (ops->locked.ins.ops == NULL) |
191 | goto out_free_ops; | 299 | goto out_free_ops; |
192 | 300 | ||
193 | if (!ops->locked.ins->ops) | 301 | if (ops->locked.ins.ops->parse && |
194 | return 0; | 302 | ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0) |
195 | |||
196 | if (ops->locked.ins->ops->parse && | ||
197 | ops->locked.ins->ops->parse(ops->locked.ops, map) < 0) | ||
198 | goto out_free_ops; | 303 | goto out_free_ops; |
199 | 304 | ||
200 | return 0; | 305 | return 0; |
@@ -209,19 +314,19 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size, | |||
209 | { | 314 | { |
210 | int printed; | 315 | int printed; |
211 | 316 | ||
212 | if (ops->locked.ins == NULL) | 317 | if (ops->locked.ins.ops == NULL) |
213 | return ins__raw_scnprintf(ins, bf, size, ops); | 318 | return ins__raw_scnprintf(ins, bf, size, ops); |
214 | 319 | ||
215 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); | 320 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); |
216 | return printed + ins__scnprintf(ops->locked.ins, bf + printed, | 321 | return printed + ins__scnprintf(&ops->locked.ins, bf + printed, |
217 | size - printed, ops->locked.ops); | 322 | size - printed, ops->locked.ops); |
218 | } | 323 | } |
219 | 324 | ||
220 | static void lock__delete(struct ins_operands *ops) | 325 | static void lock__delete(struct ins_operands *ops) |
221 | { | 326 | { |
222 | struct ins *ins = ops->locked.ins; | 327 | struct ins *ins = &ops->locked.ins; |
223 | 328 | ||
224 | if (ins && ins->ops->free) | 329 | if (ins->ops && ins->ops->free) |
225 | ins->ops->free(ops->locked.ops); | 330 | ins->ops->free(ops->locked.ops); |
226 | else | 331 | else |
227 | ins__delete(ops->locked.ops); | 332 | ins__delete(ops->locked.ops); |
@@ -237,7 +342,7 @@ static struct ins_ops lock_ops = { | |||
237 | .scnprintf = lock__scnprintf, | 342 | .scnprintf = lock__scnprintf, |
238 | }; | 343 | }; |
239 | 344 | ||
240 | static int mov__parse(struct ins_operands *ops, struct map *map __maybe_unused) | 345 | static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map *map __maybe_unused) |
241 | { | 346 | { |
242 | char *s = strchr(ops->raw, ','), *target, *comment, prev; | 347 | char *s = strchr(ops->raw, ','), *target, *comment, prev; |
243 | 348 | ||
@@ -252,11 +357,7 @@ static int mov__parse(struct ins_operands *ops, struct map *map __maybe_unused) | |||
252 | return -1; | 357 | return -1; |
253 | 358 | ||
254 | target = ++s; | 359 | target = ++s; |
255 | #ifdef __arm__ | 360 | comment = strchr(s, arch->objdump.comment_char); |
256 | comment = strchr(s, ';'); | ||
257 | #else | ||
258 | comment = strchr(s, '#'); | ||
259 | #endif | ||
260 | 361 | ||
261 | if (comment != NULL) | 362 | if (comment != NULL) |
262 | s = comment - 1; | 363 | s = comment - 1; |
@@ -304,7 +405,7 @@ static struct ins_ops mov_ops = { | |||
304 | .scnprintf = mov__scnprintf, | 405 | .scnprintf = mov__scnprintf, |
305 | }; | 406 | }; |
306 | 407 | ||
307 | static int dec__parse(struct ins_operands *ops, struct map *map __maybe_unused) | 408 | static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map *map __maybe_unused) |
308 | { | 409 | { |
309 | char *target, *comment, *s, prev; | 410 | char *target, *comment, *s, prev; |
310 | 411 | ||
@@ -321,7 +422,7 @@ static int dec__parse(struct ins_operands *ops, struct map *map __maybe_unused) | |||
321 | if (ops->target.raw == NULL) | 422 | if (ops->target.raw == NULL) |
322 | return -1; | 423 | return -1; |
323 | 424 | ||
324 | comment = strchr(s, '#'); | 425 | comment = strchr(s, arch->objdump.comment_char); |
325 | if (comment == NULL) | 426 | if (comment == NULL) |
326 | return 0; | 427 | return 0; |
327 | 428 | ||
@@ -364,99 +465,6 @@ bool ins__is_ret(const struct ins *ins) | |||
364 | return ins->ops == &ret_ops; | 465 | return ins->ops == &ret_ops; |
365 | } | 466 | } |
366 | 467 | ||
367 | static struct ins instructions[] = { | ||
368 | { .name = "add", .ops = &mov_ops, }, | ||
369 | { .name = "addl", .ops = &mov_ops, }, | ||
370 | { .name = "addq", .ops = &mov_ops, }, | ||
371 | { .name = "addw", .ops = &mov_ops, }, | ||
372 | { .name = "and", .ops = &mov_ops, }, | ||
373 | #ifdef __arm__ | ||
374 | { .name = "b", .ops = &jump_ops, }, // might also be a call | ||
375 | { .name = "bcc", .ops = &jump_ops, }, | ||
376 | { .name = "bcs", .ops = &jump_ops, }, | ||
377 | { .name = "beq", .ops = &jump_ops, }, | ||
378 | { .name = "bge", .ops = &jump_ops, }, | ||
379 | { .name = "bgt", .ops = &jump_ops, }, | ||
380 | { .name = "bhi", .ops = &jump_ops, }, | ||
381 | { .name = "bl", .ops = &call_ops, }, | ||
382 | { .name = "bls", .ops = &jump_ops, }, | ||
383 | { .name = "blt", .ops = &jump_ops, }, | ||
384 | { .name = "blx", .ops = &call_ops, }, | ||
385 | { .name = "bne", .ops = &jump_ops, }, | ||
386 | #endif | ||
387 | { .name = "bts", .ops = &mov_ops, }, | ||
388 | { .name = "call", .ops = &call_ops, }, | ||
389 | { .name = "callq", .ops = &call_ops, }, | ||
390 | { .name = "cmp", .ops = &mov_ops, }, | ||
391 | { .name = "cmpb", .ops = &mov_ops, }, | ||
392 | { .name = "cmpl", .ops = &mov_ops, }, | ||
393 | { .name = "cmpq", .ops = &mov_ops, }, | ||
394 | { .name = "cmpw", .ops = &mov_ops, }, | ||
395 | { .name = "cmpxch", .ops = &mov_ops, }, | ||
396 | { .name = "dec", .ops = &dec_ops, }, | ||
397 | { .name = "decl", .ops = &dec_ops, }, | ||
398 | { .name = "imul", .ops = &mov_ops, }, | ||
399 | { .name = "inc", .ops = &dec_ops, }, | ||
400 | { .name = "incl", .ops = &dec_ops, }, | ||
401 | { .name = "ja", .ops = &jump_ops, }, | ||
402 | { .name = "jae", .ops = &jump_ops, }, | ||
403 | { .name = "jb", .ops = &jump_ops, }, | ||
404 | { .name = "jbe", .ops = &jump_ops, }, | ||
405 | { .name = "jc", .ops = &jump_ops, }, | ||
406 | { .name = "jcxz", .ops = &jump_ops, }, | ||
407 | { .name = "je", .ops = &jump_ops, }, | ||
408 | { .name = "jecxz", .ops = &jump_ops, }, | ||
409 | { .name = "jg", .ops = &jump_ops, }, | ||
410 | { .name = "jge", .ops = &jump_ops, }, | ||
411 | { .name = "jl", .ops = &jump_ops, }, | ||
412 | { .name = "jle", .ops = &jump_ops, }, | ||
413 | { .name = "jmp", .ops = &jump_ops, }, | ||
414 | { .name = "jmpq", .ops = &jump_ops, }, | ||
415 | { .name = "jna", .ops = &jump_ops, }, | ||
416 | { .name = "jnae", .ops = &jump_ops, }, | ||
417 | { .name = "jnb", .ops = &jump_ops, }, | ||
418 | { .name = "jnbe", .ops = &jump_ops, }, | ||
419 | { .name = "jnc", .ops = &jump_ops, }, | ||
420 | { .name = "jne", .ops = &jump_ops, }, | ||
421 | { .name = "jng", .ops = &jump_ops, }, | ||
422 | { .name = "jnge", .ops = &jump_ops, }, | ||
423 | { .name = "jnl", .ops = &jump_ops, }, | ||
424 | { .name = "jnle", .ops = &jump_ops, }, | ||
425 | { .name = "jno", .ops = &jump_ops, }, | ||
426 | { .name = "jnp", .ops = &jump_ops, }, | ||
427 | { .name = "jns", .ops = &jump_ops, }, | ||
428 | { .name = "jnz", .ops = &jump_ops, }, | ||
429 | { .name = "jo", .ops = &jump_ops, }, | ||
430 | { .name = "jp", .ops = &jump_ops, }, | ||
431 | { .name = "jpe", .ops = &jump_ops, }, | ||
432 | { .name = "jpo", .ops = &jump_ops, }, | ||
433 | { .name = "jrcxz", .ops = &jump_ops, }, | ||
434 | { .name = "js", .ops = &jump_ops, }, | ||
435 | { .name = "jz", .ops = &jump_ops, }, | ||
436 | { .name = "lea", .ops = &mov_ops, }, | ||
437 | { .name = "lock", .ops = &lock_ops, }, | ||
438 | { .name = "mov", .ops = &mov_ops, }, | ||
439 | { .name = "movb", .ops = &mov_ops, }, | ||
440 | { .name = "movdqa",.ops = &mov_ops, }, | ||
441 | { .name = "movl", .ops = &mov_ops, }, | ||
442 | { .name = "movq", .ops = &mov_ops, }, | ||
443 | { .name = "movslq", .ops = &mov_ops, }, | ||
444 | { .name = "movzbl", .ops = &mov_ops, }, | ||
445 | { .name = "movzwl", .ops = &mov_ops, }, | ||
446 | { .name = "nop", .ops = &nop_ops, }, | ||
447 | { .name = "nopl", .ops = &nop_ops, }, | ||
448 | { .name = "nopw", .ops = &nop_ops, }, | ||
449 | { .name = "or", .ops = &mov_ops, }, | ||
450 | { .name = "orl", .ops = &mov_ops, }, | ||
451 | { .name = "test", .ops = &mov_ops, }, | ||
452 | { .name = "testb", .ops = &mov_ops, }, | ||
453 | { .name = "testl", .ops = &mov_ops, }, | ||
454 | { .name = "xadd", .ops = &mov_ops, }, | ||
455 | { .name = "xbeginl", .ops = &jump_ops, }, | ||
456 | { .name = "xbeginq", .ops = &jump_ops, }, | ||
457 | { .name = "retq", .ops = &ret_ops, }, | ||
458 | }; | ||
459 | |||
460 | static int ins__key_cmp(const void *name, const void *insp) | 468 | static int ins__key_cmp(const void *name, const void *insp) |
461 | { | 469 | { |
462 | const struct ins *ins = insp; | 470 | const struct ins *ins = insp; |
@@ -472,24 +480,70 @@ static int ins__cmp(const void *a, const void *b) | |||
472 | return strcmp(ia->name, ib->name); | 480 | return strcmp(ia->name, ib->name); |
473 | } | 481 | } |
474 | 482 | ||
475 | static void ins__sort(void) | 483 | static void ins__sort(struct arch *arch) |
476 | { | 484 | { |
477 | const int nmemb = ARRAY_SIZE(instructions); | 485 | const int nmemb = arch->nr_instructions; |
478 | 486 | ||
479 | qsort(instructions, nmemb, sizeof(struct ins), ins__cmp); | 487 | qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); |
480 | } | 488 | } |
481 | 489 | ||
482 | static struct ins *ins__find(const char *name) | 490 | static struct ins_ops *__ins__find(struct arch *arch, const char *name) |
483 | { | 491 | { |
484 | const int nmemb = ARRAY_SIZE(instructions); | 492 | struct ins *ins; |
493 | const int nmemb = arch->nr_instructions; | ||
494 | |||
495 | if (!arch->sorted_instructions) { | ||
496 | ins__sort(arch); | ||
497 | arch->sorted_instructions = true; | ||
498 | } | ||
499 | |||
500 | ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); | ||
501 | return ins ? ins->ops : NULL; | ||
502 | } | ||
503 | |||
504 | static struct ins_ops *ins__find(struct arch *arch, const char *name) | ||
505 | { | ||
506 | struct ins_ops *ops = __ins__find(arch, name); | ||
507 | |||
508 | if (!ops && arch->associate_instruction_ops) | ||
509 | ops = arch->associate_instruction_ops(arch, name); | ||
510 | |||
511 | return ops; | ||
512 | } | ||
513 | |||
514 | static int arch__key_cmp(const void *name, const void *archp) | ||
515 | { | ||
516 | const struct arch *arch = archp; | ||
517 | |||
518 | return strcmp(name, arch->name); | ||
519 | } | ||
520 | |||
521 | static int arch__cmp(const void *a, const void *b) | ||
522 | { | ||
523 | const struct arch *aa = a; | ||
524 | const struct arch *ab = b; | ||
525 | |||
526 | return strcmp(aa->name, ab->name); | ||
527 | } | ||
528 | |||
529 | static void arch__sort(void) | ||
530 | { | ||
531 | const int nmemb = ARRAY_SIZE(architectures); | ||
532 | |||
533 | qsort(architectures, nmemb, sizeof(struct arch), arch__cmp); | ||
534 | } | ||
535 | |||
536 | static struct arch *arch__find(const char *name) | ||
537 | { | ||
538 | const int nmemb = ARRAY_SIZE(architectures); | ||
485 | static bool sorted; | 539 | static bool sorted; |
486 | 540 | ||
487 | if (!sorted) { | 541 | if (!sorted) { |
488 | ins__sort(); | 542 | arch__sort(); |
489 | sorted = true; | 543 | sorted = true; |
490 | } | 544 | } |
491 | 545 | ||
492 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__key_cmp); | 546 | return bsearch(name, architectures, nmemb, sizeof(struct arch), arch__key_cmp); |
493 | } | 547 | } |
494 | 548 | ||
495 | int symbol__alloc_hist(struct symbol *sym) | 549 | int symbol__alloc_hist(struct symbol *sym) |
@@ -593,7 +647,8 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
593 | 647 | ||
594 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 648 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
595 | 649 | ||
596 | if (addr < sym->start || addr >= sym->end) { | 650 | if ((addr < sym->start || addr >= sym->end) && |
651 | (addr != sym->end || sym->start != sym->end)) { | ||
597 | pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n", | 652 | pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n", |
598 | __func__, __LINE__, sym->name, sym->start, addr, sym->end); | 653 | __func__, __LINE__, sym->name, sym->start, addr, sym->end); |
599 | return -ERANGE; | 654 | return -ERANGE; |
@@ -709,21 +764,18 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | |||
709 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 764 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
710 | } | 765 | } |
711 | 766 | ||
712 | static void disasm_line__init_ins(struct disasm_line *dl, struct map *map) | 767 | static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) |
713 | { | 768 | { |
714 | dl->ins = ins__find(dl->name); | 769 | dl->ins.ops = ins__find(arch, dl->ins.name); |
715 | |||
716 | if (dl->ins == NULL) | ||
717 | return; | ||
718 | 770 | ||
719 | if (!dl->ins->ops) | 771 | if (!dl->ins.ops) |
720 | return; | 772 | return; |
721 | 773 | ||
722 | if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops, map) < 0) | 774 | if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0) |
723 | dl->ins = NULL; | 775 | dl->ins.ops = NULL; |
724 | } | 776 | } |
725 | 777 | ||
726 | static int disasm_line__parse(char *line, char **namep, char **rawp) | 778 | static int disasm_line__parse(char *line, const char **namep, char **rawp) |
727 | { | 779 | { |
728 | char *name = line, tmp; | 780 | char *name = line, tmp; |
729 | 781 | ||
@@ -756,12 +808,14 @@ static int disasm_line__parse(char *line, char **namep, char **rawp) | |||
756 | return 0; | 808 | return 0; |
757 | 809 | ||
758 | out_free_name: | 810 | out_free_name: |
759 | zfree(namep); | 811 | free((void *)namep); |
812 | *namep = NULL; | ||
760 | return -1; | 813 | return -1; |
761 | } | 814 | } |
762 | 815 | ||
763 | static struct disasm_line *disasm_line__new(s64 offset, char *line, | 816 | static struct disasm_line *disasm_line__new(s64 offset, char *line, |
764 | size_t privsize, int line_nr, | 817 | size_t privsize, int line_nr, |
818 | struct arch *arch, | ||
765 | struct map *map) | 819 | struct map *map) |
766 | { | 820 | { |
767 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 821 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
@@ -774,10 +828,10 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, | |||
774 | goto out_delete; | 828 | goto out_delete; |
775 | 829 | ||
776 | if (offset != -1) { | 830 | if (offset != -1) { |
777 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) | 831 | if (disasm_line__parse(dl->line, &dl->ins.name, &dl->ops.raw) < 0) |
778 | goto out_free_line; | 832 | goto out_free_line; |
779 | 833 | ||
780 | disasm_line__init_ins(dl, map); | 834 | disasm_line__init_ins(dl, arch, map); |
781 | } | 835 | } |
782 | } | 836 | } |
783 | 837 | ||
@@ -793,20 +847,21 @@ out_delete: | |||
793 | void disasm_line__free(struct disasm_line *dl) | 847 | void disasm_line__free(struct disasm_line *dl) |
794 | { | 848 | { |
795 | zfree(&dl->line); | 849 | zfree(&dl->line); |
796 | zfree(&dl->name); | 850 | if (dl->ins.ops && dl->ins.ops->free) |
797 | if (dl->ins && dl->ins->ops->free) | 851 | dl->ins.ops->free(&dl->ops); |
798 | dl->ins->ops->free(&dl->ops); | ||
799 | else | 852 | else |
800 | ins__delete(&dl->ops); | 853 | ins__delete(&dl->ops); |
854 | free((void *)dl->ins.name); | ||
855 | dl->ins.name = NULL; | ||
801 | free(dl); | 856 | free(dl); |
802 | } | 857 | } |
803 | 858 | ||
804 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) | 859 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) |
805 | { | 860 | { |
806 | if (raw || !dl->ins) | 861 | if (raw || !dl->ins.ops) |
807 | return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); | 862 | return scnprintf(bf, size, "%-6.6s %s", dl->ins.name, dl->ops.raw); |
808 | 863 | ||
809 | return ins__scnprintf(dl->ins, bf, size, &dl->ops); | 864 | return ins__scnprintf(&dl->ins, bf, size, &dl->ops); |
810 | } | 865 | } |
811 | 866 | ||
812 | static void disasm__add(struct list_head *head, struct disasm_line *line) | 867 | static void disasm__add(struct list_head *head, struct disasm_line *line) |
@@ -1087,6 +1142,7 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
1087 | * The ops.raw part will be parsed further according to type of the instruction. | 1142 | * The ops.raw part will be parsed further according to type of the instruction. |
1088 | */ | 1143 | */ |
1089 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 1144 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
1145 | struct arch *arch, | ||
1090 | FILE *file, size_t privsize, | 1146 | FILE *file, size_t privsize, |
1091 | int *line_nr) | 1147 | int *line_nr) |
1092 | { | 1148 | { |
@@ -1149,19 +1205,21 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
1149 | parsed_line = tmp2 + 1; | 1205 | parsed_line = tmp2 + 1; |
1150 | } | 1206 | } |
1151 | 1207 | ||
1152 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr, map); | 1208 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr, arch, map); |
1153 | free(line); | 1209 | free(line); |
1154 | (*line_nr)++; | 1210 | (*line_nr)++; |
1155 | 1211 | ||
1156 | if (dl == NULL) | 1212 | if (dl == NULL) |
1157 | return -1; | 1213 | return -1; |
1158 | 1214 | ||
1159 | if (dl->ops.target.offset == UINT64_MAX) | 1215 | if (!disasm_line__has_offset(dl)) { |
1160 | dl->ops.target.offset = dl->ops.target.addr - | 1216 | dl->ops.target.offset = dl->ops.target.addr - |
1161 | map__rip_2objdump(map, sym->start); | 1217 | map__rip_2objdump(map, sym->start); |
1218 | dl->ops.target.offset_avail = true; | ||
1219 | } | ||
1162 | 1220 | ||
1163 | /* kcore has no symbols, so add the call target name */ | 1221 | /* kcore has no symbols, so add the call target name */ |
1164 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { | 1222 | if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) { |
1165 | struct addr_map_symbol target = { | 1223 | struct addr_map_symbol target = { |
1166 | .map = map, | 1224 | .map = map, |
1167 | .addr = dl->ops.target.addr, | 1225 | .addr = dl->ops.target.addr, |
@@ -1191,8 +1249,8 @@ static void delete_last_nop(struct symbol *sym) | |||
1191 | while (!list_empty(list)) { | 1249 | while (!list_empty(list)) { |
1192 | dl = list_entry(list->prev, struct disasm_line, node); | 1250 | dl = list_entry(list->prev, struct disasm_line, node); |
1193 | 1251 | ||
1194 | if (dl->ins && dl->ins->ops) { | 1252 | if (dl->ins.ops) { |
1195 | if (dl->ins->ops != &nop_ops) | 1253 | if (dl->ins.ops != &nop_ops) |
1196 | return; | 1254 | return; |
1197 | } else { | 1255 | } else { |
1198 | if (!strstr(dl->line, " nop ") && | 1256 | if (!strstr(dl->line, " nop ") && |
@@ -1280,10 +1338,23 @@ fallback: | |||
1280 | return 0; | 1338 | return 0; |
1281 | } | 1339 | } |
1282 | 1340 | ||
1283 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) | 1341 | static const char *annotate__norm_arch(const char *arch_name) |
1342 | { | ||
1343 | struct utsname uts; | ||
1344 | |||
1345 | if (!arch_name) { /* Assume we are annotating locally. */ | ||
1346 | if (uname(&uts) < 0) | ||
1347 | return NULL; | ||
1348 | arch_name = uts.machine; | ||
1349 | } | ||
1350 | return normalize_arch((char *)arch_name); | ||
1351 | } | ||
1352 | |||
1353 | int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_name, size_t privsize) | ||
1284 | { | 1354 | { |
1285 | struct dso *dso = map->dso; | 1355 | struct dso *dso = map->dso; |
1286 | char command[PATH_MAX * 2]; | 1356 | char command[PATH_MAX * 2]; |
1357 | struct arch *arch = NULL; | ||
1287 | FILE *file; | 1358 | FILE *file; |
1288 | char symfs_filename[PATH_MAX]; | 1359 | char symfs_filename[PATH_MAX]; |
1289 | struct kcore_extract kce; | 1360 | struct kcore_extract kce; |
@@ -1297,6 +1368,22 @@ int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) | |||
1297 | if (err) | 1368 | if (err) |
1298 | return err; | 1369 | return err; |
1299 | 1370 | ||
1371 | arch_name = annotate__norm_arch(arch_name); | ||
1372 | if (!arch_name) | ||
1373 | return -1; | ||
1374 | |||
1375 | arch = arch__find(arch_name); | ||
1376 | if (arch == NULL) | ||
1377 | return -ENOTSUP; | ||
1378 | |||
1379 | if (arch->init) { | ||
1380 | err = arch->init(arch); | ||
1381 | if (err) { | ||
1382 | pr_err("%s: failed to initialize %s arch priv area\n", __func__, arch->name); | ||
1383 | return err; | ||
1384 | } | ||
1385 | } | ||
1386 | |||
1300 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | 1387 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, |
1301 | symfs_filename, sym->name, map->unmap_ip(map, sym->start), | 1388 | symfs_filename, sym->name, map->unmap_ip(map, sym->start), |
1302 | map->unmap_ip(map, sym->end)); | 1389 | map->unmap_ip(map, sym->end)); |
@@ -1395,7 +1482,7 @@ int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize) | |||
1395 | 1482 | ||
1396 | nline = 0; | 1483 | nline = 0; |
1397 | while (!feof(file)) { | 1484 | while (!feof(file)) { |
1398 | if (symbol__parse_objdump_line(sym, map, file, privsize, | 1485 | if (symbol__parse_objdump_line(sym, map, arch, file, privsize, |
1399 | &lineno) < 0) | 1486 | &lineno) < 0) |
1400 | break; | 1487 | break; |
1401 | nline++; | 1488 | nline++; |
@@ -1764,7 +1851,7 @@ static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) | |||
1764 | if (dl->offset == -1) | 1851 | if (dl->offset == -1) |
1765 | return fprintf(fp, "%s\n", dl->line); | 1852 | return fprintf(fp, "%s\n", dl->line); |
1766 | 1853 | ||
1767 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); | 1854 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->ins.name); |
1768 | 1855 | ||
1769 | if (dl->ops.raw[0] != '\0') { | 1856 | if (dl->ops.raw[0] != '\0') { |
1770 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", | 1857 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", |
@@ -1793,7 +1880,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
1793 | struct rb_root source_line = RB_ROOT; | 1880 | struct rb_root source_line = RB_ROOT; |
1794 | u64 len; | 1881 | u64 len; |
1795 | 1882 | ||
1796 | if (symbol__disassemble(sym, map, 0) < 0) | 1883 | if (symbol__disassemble(sym, map, perf_evsel__env_arch(evsel), 0) < 0) |
1797 | return -1; | 1884 | return -1; |
1798 | 1885 | ||
1799 | len = symbol__size(sym); | 1886 | len = symbol__size(sym); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 5bbcec173b82..09776b5af991 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -11,7 +11,12 @@ | |||
11 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
12 | #include <pthread.h> | 12 | #include <pthread.h> |
13 | 13 | ||
14 | struct ins; | 14 | struct ins_ops; |
15 | |||
16 | struct ins { | ||
17 | const char *name; | ||
18 | struct ins_ops *ops; | ||
19 | }; | ||
15 | 20 | ||
16 | struct ins_operands { | 21 | struct ins_operands { |
17 | char *raw; | 22 | char *raw; |
@@ -19,7 +24,8 @@ struct ins_operands { | |||
19 | char *raw; | 24 | char *raw; |
20 | char *name; | 25 | char *name; |
21 | u64 addr; | 26 | u64 addr; |
22 | u64 offset; | 27 | s64 offset; |
28 | bool offset_avail; | ||
23 | } target; | 29 | } target; |
24 | union { | 30 | union { |
25 | struct { | 31 | struct { |
@@ -28,24 +34,21 @@ struct ins_operands { | |||
28 | u64 addr; | 34 | u64 addr; |
29 | } source; | 35 | } source; |
30 | struct { | 36 | struct { |
31 | struct ins *ins; | 37 | struct ins ins; |
32 | struct ins_operands *ops; | 38 | struct ins_operands *ops; |
33 | } locked; | 39 | } locked; |
34 | }; | 40 | }; |
35 | }; | 41 | }; |
36 | 42 | ||
43 | struct arch; | ||
44 | |||
37 | struct ins_ops { | 45 | struct ins_ops { |
38 | void (*free)(struct ins_operands *ops); | 46 | void (*free)(struct ins_operands *ops); |
39 | int (*parse)(struct ins_operands *ops, struct map *map); | 47 | int (*parse)(struct arch *arch, struct ins_operands *ops, struct map *map); |
40 | int (*scnprintf)(struct ins *ins, char *bf, size_t size, | 48 | int (*scnprintf)(struct ins *ins, char *bf, size_t size, |
41 | struct ins_operands *ops); | 49 | struct ins_operands *ops); |
42 | }; | 50 | }; |
43 | 51 | ||
44 | struct ins { | ||
45 | const char *name; | ||
46 | struct ins_ops *ops; | ||
47 | }; | ||
48 | |||
49 | bool ins__is_jump(const struct ins *ins); | 52 | bool ins__is_jump(const struct ins *ins); |
50 | bool ins__is_call(const struct ins *ins); | 53 | bool ins__is_call(const struct ins *ins); |
51 | bool ins__is_ret(const struct ins *ins); | 54 | bool ins__is_ret(const struct ins *ins); |
@@ -57,8 +60,7 @@ struct disasm_line { | |||
57 | struct list_head node; | 60 | struct list_head node; |
58 | s64 offset; | 61 | s64 offset; |
59 | char *line; | 62 | char *line; |
60 | char *name; | 63 | struct ins ins; |
61 | struct ins *ins; | ||
62 | int line_nr; | 64 | int line_nr; |
63 | float ipc; | 65 | float ipc; |
64 | u64 cycles; | 66 | u64 cycles; |
@@ -67,7 +69,7 @@ struct disasm_line { | |||
67 | 69 | ||
68 | static inline bool disasm_line__has_offset(const struct disasm_line *dl) | 70 | static inline bool disasm_line__has_offset(const struct disasm_line *dl) |
69 | { | 71 | { |
70 | return dl->ops.target.offset != UINT64_MAX; | 72 | return dl->ops.target.offset_avail; |
71 | } | 73 | } |
72 | 74 | ||
73 | void disasm_line__free(struct disasm_line *dl); | 75 | void disasm_line__free(struct disasm_line *dl); |
@@ -156,7 +158,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); | |||
156 | int symbol__alloc_hist(struct symbol *sym); | 158 | int symbol__alloc_hist(struct symbol *sym); |
157 | void symbol__annotate_zero_histograms(struct symbol *sym); | 159 | void symbol__annotate_zero_histograms(struct symbol *sym); |
158 | 160 | ||
159 | int symbol__disassemble(struct symbol *sym, struct map *map, size_t privsize); | 161 | int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_name, size_t privsize); |
160 | 162 | ||
161 | enum symbol_disassemble_errno { | 163 | enum symbol_disassemble_errno { |
162 | SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, | 164 | SYMBOL_ANNOTATE_ERRNO__SUCCESS = 0, |
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 2b2c9b82f5ab..36c861103291 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
@@ -14,11 +14,11 @@ | |||
14 | #include "debug.h" | 14 | #include "debug.h" |
15 | #include "bpf-loader.h" | 15 | #include "bpf-loader.h" |
16 | #include "bpf-prologue.h" | 16 | #include "bpf-prologue.h" |
17 | #include "llvm-utils.h" | ||
18 | #include "probe-event.h" | 17 | #include "probe-event.h" |
19 | #include "probe-finder.h" // for MAX_PROBES | 18 | #include "probe-finder.h" // for MAX_PROBES |
20 | #include "parse-events.h" | 19 | #include "parse-events.h" |
21 | #include "llvm-utils.h" | 20 | #include "llvm-utils.h" |
21 | #include "c++/clang-c.h" | ||
22 | 22 | ||
23 | #define DEFINE_PRINT_FN(name, level) \ | 23 | #define DEFINE_PRINT_FN(name, level) \ |
24 | static int libbpf_##name(const char *fmt, ...) \ | 24 | static int libbpf_##name(const char *fmt, ...) \ |
@@ -86,10 +86,21 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) | |||
86 | void *obj_buf; | 86 | void *obj_buf; |
87 | size_t obj_buf_sz; | 87 | size_t obj_buf_sz; |
88 | 88 | ||
89 | err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); | 89 | perf_clang__init(); |
90 | if (err) | 90 | err = perf_clang__compile_bpf(filename, &obj_buf, &obj_buf_sz); |
91 | return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); | 91 | perf_clang__cleanup(); |
92 | if (err) { | ||
93 | pr_warning("bpf: builtin compilation failed: %d, try external compiler\n", err); | ||
94 | err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); | ||
95 | if (err) | ||
96 | return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); | ||
97 | } else | ||
98 | pr_debug("bpf: successfull builtin compilation\n"); | ||
92 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); | 99 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); |
100 | |||
101 | if (!IS_ERR(obj) && llvm_param.dump_obj) | ||
102 | llvm__dump_obj(filename, obj_buf, obj_buf_sz); | ||
103 | |||
93 | free(obj_buf); | 104 | free(obj_buf); |
94 | } else | 105 | } else |
95 | obj = bpf_object__open(filename); | 106 | obj = bpf_object__open(filename); |
@@ -241,7 +252,7 @@ parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) | |||
241 | int err = 0; | 252 | int err = 0; |
242 | 253 | ||
243 | if (!text) { | 254 | if (!text) { |
244 | pr_debug("No enough memory: dup config_str failed\n"); | 255 | pr_debug("Not enough memory: dup config_str failed\n"); |
245 | return ERR_PTR(-ENOMEM); | 256 | return ERR_PTR(-ENOMEM); |
246 | } | 257 | } |
247 | 258 | ||
@@ -531,7 +542,7 @@ static int map_prologue(struct perf_probe_event *pev, int *mapping, | |||
531 | 542 | ||
532 | ptevs = malloc(array_sz); | 543 | ptevs = malloc(array_sz); |
533 | if (!ptevs) { | 544 | if (!ptevs) { |
534 | pr_debug("No enough memory: alloc ptevs failed\n"); | 545 | pr_debug("Not enough memory: alloc ptevs failed\n"); |
535 | return -ENOMEM; | 546 | return -ENOMEM; |
536 | } | 547 | } |
537 | 548 | ||
@@ -604,13 +615,13 @@ static int hook_load_preprocessor(struct bpf_program *prog) | |||
604 | priv->need_prologue = true; | 615 | priv->need_prologue = true; |
605 | priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); | 616 | priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); |
606 | if (!priv->insns_buf) { | 617 | if (!priv->insns_buf) { |
607 | pr_debug("No enough memory: alloc insns_buf failed\n"); | 618 | pr_debug("Not enough memory: alloc insns_buf failed\n"); |
608 | return -ENOMEM; | 619 | return -ENOMEM; |
609 | } | 620 | } |
610 | 621 | ||
611 | priv->type_mapping = malloc(sizeof(int) * pev->ntevs); | 622 | priv->type_mapping = malloc(sizeof(int) * pev->ntevs); |
612 | if (!priv->type_mapping) { | 623 | if (!priv->type_mapping) { |
613 | pr_debug("No enough memory: alloc type_mapping failed\n"); | 624 | pr_debug("Not enough memory: alloc type_mapping failed\n"); |
614 | return -ENOMEM; | 625 | return -ENOMEM; |
615 | } | 626 | } |
616 | memset(priv->type_mapping, -1, | 627 | memset(priv->type_mapping, -1, |
@@ -864,7 +875,7 @@ bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term) | |||
864 | 875 | ||
865 | op->k.array.ranges = memdup(term->array.ranges, memsz); | 876 | op->k.array.ranges = memdup(term->array.ranges, memsz); |
866 | if (!op->k.array.ranges) { | 877 | if (!op->k.array.ranges) { |
867 | pr_debug("No enough memory to alloc indices for map\n"); | 878 | pr_debug("Not enough memory to alloc indices for map\n"); |
868 | return -ENOMEM; | 879 | return -ENOMEM; |
869 | } | 880 | } |
870 | op->key_type = BPF_MAP_KEY_RANGES; | 881 | op->key_type = BPF_MAP_KEY_RANGES; |
@@ -929,7 +940,7 @@ bpf_map_priv__clone(struct bpf_map_priv *priv) | |||
929 | 940 | ||
930 | newpriv = zalloc(sizeof(*newpriv)); | 941 | newpriv = zalloc(sizeof(*newpriv)); |
931 | if (!newpriv) { | 942 | if (!newpriv) { |
932 | pr_debug("No enough memory to alloc map private\n"); | 943 | pr_debug("Not enough memory to alloc map private\n"); |
933 | return NULL; | 944 | return NULL; |
934 | } | 945 | } |
935 | INIT_LIST_HEAD(&newpriv->ops_list); | 946 | INIT_LIST_HEAD(&newpriv->ops_list); |
@@ -960,7 +971,7 @@ bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) | |||
960 | if (!priv) { | 971 | if (!priv) { |
961 | priv = zalloc(sizeof(*priv)); | 972 | priv = zalloc(sizeof(*priv)); |
962 | if (!priv) { | 973 | if (!priv) { |
963 | pr_debug("No enough memory to alloc map private\n"); | 974 | pr_debug("Not enough memory to alloc map private\n"); |
964 | return -ENOMEM; | 975 | return -ENOMEM; |
965 | } | 976 | } |
966 | INIT_LIST_HEAD(&priv->ops_list); | 977 | INIT_LIST_HEAD(&priv->ops_list); |
diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build new file mode 100644 index 000000000000..988fef1b11d7 --- /dev/null +++ b/tools/perf/util/c++/Build | |||
@@ -0,0 +1,2 @@ | |||
1 | libperf-$(CONFIG_CLANGLLVM) += clang.o | ||
2 | libperf-$(CONFIG_CLANGLLVM) += clang-test.o | ||
diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h new file mode 100644 index 000000000000..0eadd792ab1f --- /dev/null +++ b/tools/perf/util/c++/clang-c.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #ifndef PERF_UTIL_CLANG_C_H | ||
2 | #define PERF_UTIL_CLANG_C_H | ||
3 | |||
4 | #include <stddef.h> /* for size_t */ | ||
5 | #include <util-cxx.h> /* for __maybe_unused */ | ||
6 | |||
7 | #ifdef __cplusplus | ||
8 | extern "C" { | ||
9 | #endif | ||
10 | |||
11 | #ifdef HAVE_LIBCLANGLLVM_SUPPORT | ||
12 | extern void perf_clang__init(void); | ||
13 | extern void perf_clang__cleanup(void); | ||
14 | |||
15 | extern int test__clang_to_IR(void); | ||
16 | extern int test__clang_to_obj(void); | ||
17 | |||
18 | extern int perf_clang__compile_bpf(const char *filename, | ||
19 | void **p_obj_buf, | ||
20 | size_t *p_obj_buf_sz); | ||
21 | #else | ||
22 | |||
23 | |||
24 | static inline void perf_clang__init(void) { } | ||
25 | static inline void perf_clang__cleanup(void) { } | ||
26 | |||
27 | static inline int test__clang_to_IR(void) { return -1; } | ||
28 | static inline int test__clang_to_obj(void) { return -1;} | ||
29 | |||
30 | static inline int | ||
31 | perf_clang__compile_bpf(const char *filename __maybe_unused, | ||
32 | void **p_obj_buf __maybe_unused, | ||
33 | size_t *p_obj_buf_sz __maybe_unused) | ||
34 | { | ||
35 | return -ENOTSUP; | ||
36 | } | ||
37 | |||
38 | #endif | ||
39 | |||
40 | #ifdef __cplusplus | ||
41 | } | ||
42 | #endif | ||
43 | #endif | ||
diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp new file mode 100644 index 000000000000..9b11e8c82798 --- /dev/null +++ b/tools/perf/util/c++/clang-test.cpp | |||
@@ -0,0 +1,62 @@ | |||
1 | #include "clang.h" | ||
2 | #include "clang-c.h" | ||
3 | #include "llvm/IR/Function.h" | ||
4 | #include "llvm/IR/LLVMContext.h" | ||
5 | |||
6 | #include <util-cxx.h> | ||
7 | #include <tests/llvm.h> | ||
8 | #include <string> | ||
9 | |||
10 | class perf_clang_scope { | ||
11 | public: | ||
12 | explicit perf_clang_scope() {perf_clang__init();} | ||
13 | ~perf_clang_scope() {perf_clang__cleanup();} | ||
14 | }; | ||
15 | |||
16 | static std::unique_ptr<llvm::Module> | ||
17 | __test__clang_to_IR(void) | ||
18 | { | ||
19 | unsigned int kernel_version; | ||
20 | |||
21 | if (fetch_kernel_version(&kernel_version, NULL, 0)) | ||
22 | return std::unique_ptr<llvm::Module>(nullptr); | ||
23 | |||
24 | std::string cflag_kver("-DLINUX_VERSION_CODE=" + | ||
25 | std::to_string(kernel_version)); | ||
26 | |||
27 | std::unique_ptr<llvm::Module> M = | ||
28 | perf::getModuleFromSource({cflag_kver.c_str()}, | ||
29 | "perf-test.c", | ||
30 | test_llvm__bpf_base_prog); | ||
31 | return M; | ||
32 | } | ||
33 | |||
34 | extern "C" { | ||
35 | int test__clang_to_IR(void) | ||
36 | { | ||
37 | perf_clang_scope _scope; | ||
38 | |||
39 | auto M = __test__clang_to_IR(); | ||
40 | if (!M) | ||
41 | return -1; | ||
42 | for (llvm::Function& F : *M) | ||
43 | if (F.getName() == "bpf_func__SyS_epoll_wait") | ||
44 | return 0; | ||
45 | return -1; | ||
46 | } | ||
47 | |||
48 | int test__clang_to_obj(void) | ||
49 | { | ||
50 | perf_clang_scope _scope; | ||
51 | |||
52 | auto M = __test__clang_to_IR(); | ||
53 | if (!M) | ||
54 | return -1; | ||
55 | |||
56 | auto Buffer = perf::getBPFObjectFromModule(&*M); | ||
57 | if (!Buffer) | ||
58 | return -1; | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | } | ||
diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp new file mode 100644 index 000000000000..1e974152cac2 --- /dev/null +++ b/tools/perf/util/c++/clang.cpp | |||
@@ -0,0 +1,195 @@ | |||
1 | /* | ||
2 | * llvm C frontend for perf. Support dynamically compile C file | ||
3 | * | ||
4 | * Inspired by clang example code: | ||
5 | * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp | ||
6 | * | ||
7 | * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> | ||
8 | * Copyright (C) 2016 Huawei Inc. | ||
9 | */ | ||
10 | |||
11 | #include "clang/CodeGen/CodeGenAction.h" | ||
12 | #include "clang/Frontend/CompilerInvocation.h" | ||
13 | #include "clang/Frontend/CompilerInstance.h" | ||
14 | #include "clang/Frontend/TextDiagnosticPrinter.h" | ||
15 | #include "clang/Tooling/Tooling.h" | ||
16 | #include "llvm/IR/LegacyPassManager.h" | ||
17 | #include "llvm/IR/Module.h" | ||
18 | #include "llvm/Option/Option.h" | ||
19 | #include "llvm/Support/FileSystem.h" | ||
20 | #include "llvm/Support/ManagedStatic.h" | ||
21 | #include "llvm/Support/TargetRegistry.h" | ||
22 | #include "llvm/Support/TargetSelect.h" | ||
23 | #include "llvm/Target/TargetMachine.h" | ||
24 | #include "llvm/Target/TargetOptions.h" | ||
25 | #include <memory> | ||
26 | |||
27 | #include "clang.h" | ||
28 | #include "clang-c.h" | ||
29 | |||
30 | namespace perf { | ||
31 | |||
32 | static std::unique_ptr<llvm::LLVMContext> LLVMCtx; | ||
33 | |||
34 | using namespace clang; | ||
35 | |||
36 | static CompilerInvocation * | ||
37 | createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, | ||
38 | DiagnosticsEngine& Diags) | ||
39 | { | ||
40 | llvm::opt::ArgStringList CCArgs { | ||
41 | "-cc1", | ||
42 | "-triple", "bpf-pc-linux", | ||
43 | "-fsyntax-only", | ||
44 | "-ferror-limit", "19", | ||
45 | "-fmessage-length", "127", | ||
46 | "-O2", | ||
47 | "-nostdsysteminc", | ||
48 | "-nobuiltininc", | ||
49 | "-vectorize-loops", | ||
50 | "-vectorize-slp", | ||
51 | "-Wno-unused-value", | ||
52 | "-Wno-pointer-sign", | ||
53 | "-x", "c"}; | ||
54 | |||
55 | CCArgs.append(CFlags.begin(), CFlags.end()); | ||
56 | CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); | ||
57 | |||
58 | FrontendOptions& Opts = CI->getFrontendOpts(); | ||
59 | Opts.Inputs.clear(); | ||
60 | Opts.Inputs.emplace_back(Path, IK_C); | ||
61 | return CI; | ||
62 | } | ||
63 | |||
64 | static std::unique_ptr<llvm::Module> | ||
65 | getModuleFromSource(llvm::opt::ArgStringList CFlags, | ||
66 | StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) | ||
67 | { | ||
68 | CompilerInstance Clang; | ||
69 | Clang.createDiagnostics(); | ||
70 | |||
71 | Clang.setVirtualFileSystem(&*VFS); | ||
72 | |||
73 | IntrusiveRefCntPtr<CompilerInvocation> CI = | ||
74 | createCompilerInvocation(std::move(CFlags), Path, | ||
75 | Clang.getDiagnostics()); | ||
76 | Clang.setInvocation(&*CI); | ||
77 | |||
78 | std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); | ||
79 | if (!Clang.ExecuteAction(*Act)) | ||
80 | return std::unique_ptr<llvm::Module>(nullptr); | ||
81 | |||
82 | return Act->takeModule(); | ||
83 | } | ||
84 | |||
85 | std::unique_ptr<llvm::Module> | ||
86 | getModuleFromSource(llvm::opt::ArgStringList CFlags, | ||
87 | StringRef Name, StringRef Content) | ||
88 | { | ||
89 | using namespace vfs; | ||
90 | |||
91 | llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( | ||
92 | new OverlayFileSystem(getRealFileSystem())); | ||
93 | llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( | ||
94 | new InMemoryFileSystem(true)); | ||
95 | |||
96 | /* | ||
97 | * pushOverlay helps setting working dir for MemFS. Must call | ||
98 | * before addFile. | ||
99 | */ | ||
100 | OverlayFS->pushOverlay(MemFS); | ||
101 | MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); | ||
102 | |||
103 | return getModuleFromSource(std::move(CFlags), Name, OverlayFS); | ||
104 | } | ||
105 | |||
106 | std::unique_ptr<llvm::Module> | ||
107 | getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) | ||
108 | { | ||
109 | IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); | ||
110 | return getModuleFromSource(std::move(CFlags), Path, VFS); | ||
111 | } | ||
112 | |||
113 | std::unique_ptr<llvm::SmallVectorImpl<char>> | ||
114 | getBPFObjectFromModule(llvm::Module *Module) | ||
115 | { | ||
116 | using namespace llvm; | ||
117 | |||
118 | std::string TargetTriple("bpf-pc-linux"); | ||
119 | std::string Error; | ||
120 | const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); | ||
121 | if (!Target) { | ||
122 | llvm::errs() << Error; | ||
123 | return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); | ||
124 | } | ||
125 | |||
126 | llvm::TargetOptions Opt; | ||
127 | TargetMachine *TargetMachine = | ||
128 | Target->createTargetMachine(TargetTriple, | ||
129 | "generic", "", | ||
130 | Opt, Reloc::Static); | ||
131 | |||
132 | Module->setDataLayout(TargetMachine->createDataLayout()); | ||
133 | Module->setTargetTriple(TargetTriple); | ||
134 | |||
135 | std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); | ||
136 | raw_svector_ostream ostream(*Buffer); | ||
137 | |||
138 | legacy::PassManager PM; | ||
139 | if (TargetMachine->addPassesToEmitFile(PM, ostream, | ||
140 | TargetMachine::CGFT_ObjectFile)) { | ||
141 | llvm::errs() << "TargetMachine can't emit a file of this type\n"; | ||
142 | return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);; | ||
143 | } | ||
144 | PM.run(*Module); | ||
145 | |||
146 | return std::move(Buffer); | ||
147 | } | ||
148 | |||
149 | } | ||
150 | |||
151 | extern "C" { | ||
152 | void perf_clang__init(void) | ||
153 | { | ||
154 | perf::LLVMCtx.reset(new llvm::LLVMContext()); | ||
155 | LLVMInitializeBPFTargetInfo(); | ||
156 | LLVMInitializeBPFTarget(); | ||
157 | LLVMInitializeBPFTargetMC(); | ||
158 | LLVMInitializeBPFAsmPrinter(); | ||
159 | } | ||
160 | |||
161 | void perf_clang__cleanup(void) | ||
162 | { | ||
163 | perf::LLVMCtx.reset(nullptr); | ||
164 | llvm::llvm_shutdown(); | ||
165 | } | ||
166 | |||
167 | int perf_clang__compile_bpf(const char *filename, | ||
168 | void **p_obj_buf, | ||
169 | size_t *p_obj_buf_sz) | ||
170 | { | ||
171 | using namespace perf; | ||
172 | |||
173 | if (!p_obj_buf || !p_obj_buf_sz) | ||
174 | return -EINVAL; | ||
175 | |||
176 | llvm::opt::ArgStringList CFlags; | ||
177 | auto M = getModuleFromSource(std::move(CFlags), filename); | ||
178 | if (!M) | ||
179 | return -EINVAL; | ||
180 | auto O = getBPFObjectFromModule(&*M); | ||
181 | if (!O) | ||
182 | return -EINVAL; | ||
183 | |||
184 | size_t size = O->size_in_bytes(); | ||
185 | void *buffer; | ||
186 | |||
187 | buffer = malloc(size); | ||
188 | if (!buffer) | ||
189 | return -ENOMEM; | ||
190 | memcpy(buffer, O->data(), size); | ||
191 | *p_obj_buf = buffer; | ||
192 | *p_obj_buf_sz = size; | ||
193 | return 0; | ||
194 | } | ||
195 | } | ||
diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h new file mode 100644 index 000000000000..dd8b0427550d --- /dev/null +++ b/tools/perf/util/c++/clang.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef PERF_UTIL_CLANG_H | ||
2 | #define PERF_UTIL_CLANG_H | ||
3 | |||
4 | #include "llvm/ADT/StringRef.h" | ||
5 | #include "llvm/IR/LLVMContext.h" | ||
6 | #include "llvm/IR/Module.h" | ||
7 | #include "llvm/Option/Option.h" | ||
8 | #include <memory> | ||
9 | |||
10 | namespace perf { | ||
11 | |||
12 | using namespace llvm; | ||
13 | |||
14 | std::unique_ptr<Module> | ||
15 | getModuleFromSource(opt::ArgStringList CFlags, | ||
16 | StringRef Name, StringRef Content); | ||
17 | |||
18 | std::unique_ptr<Module> | ||
19 | getModuleFromSource(opt::ArgStringList CFlags, | ||
20 | StringRef Path); | ||
21 | |||
22 | std::unique_ptr<llvm::SmallVectorImpl<char>> | ||
23 | getBPFObjectFromModule(llvm::Module *Module); | ||
24 | |||
25 | } | ||
26 | #endif | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 07fd30bc2f81..42922512c1c6 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -193,7 +193,6 @@ int perf_callchain_config(const char *var, const char *value) | |||
193 | 193 | ||
194 | if (!strcmp(var, "record-mode")) | 194 | if (!strcmp(var, "record-mode")) |
195 | return parse_callchain_record_opt(value, &callchain_param); | 195 | return parse_callchain_record_opt(value, &callchain_param); |
196 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
197 | if (!strcmp(var, "dump-size")) { | 196 | if (!strcmp(var, "dump-size")) { |
198 | unsigned long size = 0; | 197 | unsigned long size = 0; |
199 | int ret; | 198 | int ret; |
@@ -203,7 +202,6 @@ int perf_callchain_config(const char *var, const char *value) | |||
203 | 202 | ||
204 | return ret; | 203 | return ret; |
205 | } | 204 | } |
206 | #endif | ||
207 | if (!strcmp(var, "print-type")) | 205 | if (!strcmp(var, "print-type")) |
208 | return parse_callchain_mode(value); | 206 | return parse_callchain_mode(value); |
209 | if (!strcmp(var, "order")) | 207 | if (!strcmp(var, "order")) |
@@ -440,6 +438,21 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
440 | call->ip = cursor_node->ip; | 438 | call->ip = cursor_node->ip; |
441 | call->ms.sym = cursor_node->sym; | 439 | call->ms.sym = cursor_node->sym; |
442 | call->ms.map = cursor_node->map; | 440 | call->ms.map = cursor_node->map; |
441 | |||
442 | if (cursor_node->branch) { | ||
443 | call->branch_count = 1; | ||
444 | |||
445 | if (cursor_node->branch_flags.predicted) | ||
446 | call->predicted_count = 1; | ||
447 | |||
448 | if (cursor_node->branch_flags.abort) | ||
449 | call->abort_count = 1; | ||
450 | |||
451 | call->cycles_count = cursor_node->branch_flags.cycles; | ||
452 | call->iter_count = cursor_node->nr_loop_iter; | ||
453 | call->samples_count = cursor_node->samples; | ||
454 | } | ||
455 | |||
443 | list_add_tail(&call->list, &node->val); | 456 | list_add_tail(&call->list, &node->val); |
444 | 457 | ||
445 | callchain_cursor_advance(cursor); | 458 | callchain_cursor_advance(cursor); |
@@ -499,8 +512,23 @@ static enum match_result match_chain(struct callchain_cursor_node *node, | |||
499 | right = node->ip; | 512 | right = node->ip; |
500 | } | 513 | } |
501 | 514 | ||
502 | if (left == right) | 515 | if (left == right) { |
516 | if (node->branch) { | ||
517 | cnode->branch_count++; | ||
518 | |||
519 | if (node->branch_flags.predicted) | ||
520 | cnode->predicted_count++; | ||
521 | |||
522 | if (node->branch_flags.abort) | ||
523 | cnode->abort_count++; | ||
524 | |||
525 | cnode->cycles_count += node->branch_flags.cycles; | ||
526 | cnode->iter_count += node->nr_loop_iter; | ||
527 | cnode->samples_count += node->samples; | ||
528 | } | ||
529 | |||
503 | return MATCH_EQ; | 530 | return MATCH_EQ; |
531 | } | ||
504 | 532 | ||
505 | return left > right ? MATCH_GT : MATCH_LT; | 533 | return left > right ? MATCH_GT : MATCH_LT; |
506 | } | 534 | } |
@@ -730,7 +758,8 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
730 | 758 | ||
731 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 759 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
732 | callchain_cursor_append(cursor, list->ip, | 760 | callchain_cursor_append(cursor, list->ip, |
733 | list->ms.map, list->ms.sym); | 761 | list->ms.map, list->ms.sym, |
762 | false, NULL, 0, 0); | ||
734 | list_del(&list->list); | 763 | list_del(&list->list); |
735 | free(list); | 764 | free(list); |
736 | } | 765 | } |
@@ -767,7 +796,9 @@ int callchain_merge(struct callchain_cursor *cursor, | |||
767 | } | 796 | } |
768 | 797 | ||
769 | int callchain_cursor_append(struct callchain_cursor *cursor, | 798 | int callchain_cursor_append(struct callchain_cursor *cursor, |
770 | u64 ip, struct map *map, struct symbol *sym) | 799 | u64 ip, struct map *map, struct symbol *sym, |
800 | bool branch, struct branch_flags *flags, | ||
801 | int nr_loop_iter, int samples) | ||
771 | { | 802 | { |
772 | struct callchain_cursor_node *node = *cursor->last; | 803 | struct callchain_cursor_node *node = *cursor->last; |
773 | 804 | ||
@@ -782,6 +813,13 @@ int callchain_cursor_append(struct callchain_cursor *cursor, | |||
782 | node->ip = ip; | 813 | node->ip = ip; |
783 | node->map = map; | 814 | node->map = map; |
784 | node->sym = sym; | 815 | node->sym = sym; |
816 | node->branch = branch; | ||
817 | node->nr_loop_iter = nr_loop_iter; | ||
818 | node->samples = samples; | ||
819 | |||
820 | if (flags) | ||
821 | memcpy(&node->branch_flags, flags, | ||
822 | sizeof(struct branch_flags)); | ||
785 | 823 | ||
786 | cursor->nr++; | 824 | cursor->nr++; |
787 | 825 | ||
@@ -939,6 +977,163 @@ int callchain_node__fprintf_value(struct callchain_node *node, | |||
939 | return 0; | 977 | return 0; |
940 | } | 978 | } |
941 | 979 | ||
980 | static void callchain_counts_value(struct callchain_node *node, | ||
981 | u64 *branch_count, u64 *predicted_count, | ||
982 | u64 *abort_count, u64 *cycles_count) | ||
983 | { | ||
984 | struct callchain_list *clist; | ||
985 | |||
986 | list_for_each_entry(clist, &node->val, list) { | ||
987 | if (branch_count) | ||
988 | *branch_count += clist->branch_count; | ||
989 | |||
990 | if (predicted_count) | ||
991 | *predicted_count += clist->predicted_count; | ||
992 | |||
993 | if (abort_count) | ||
994 | *abort_count += clist->abort_count; | ||
995 | |||
996 | if (cycles_count) | ||
997 | *cycles_count += clist->cycles_count; | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | static int callchain_node_branch_counts_cumul(struct callchain_node *node, | ||
1002 | u64 *branch_count, | ||
1003 | u64 *predicted_count, | ||
1004 | u64 *abort_count, | ||
1005 | u64 *cycles_count) | ||
1006 | { | ||
1007 | struct callchain_node *child; | ||
1008 | struct rb_node *n; | ||
1009 | |||
1010 | n = rb_first(&node->rb_root_in); | ||
1011 | while (n) { | ||
1012 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
1013 | n = rb_next(n); | ||
1014 | |||
1015 | callchain_node_branch_counts_cumul(child, branch_count, | ||
1016 | predicted_count, | ||
1017 | abort_count, | ||
1018 | cycles_count); | ||
1019 | |||
1020 | callchain_counts_value(child, branch_count, | ||
1021 | predicted_count, abort_count, | ||
1022 | cycles_count); | ||
1023 | } | ||
1024 | |||
1025 | return 0; | ||
1026 | } | ||
1027 | |||
1028 | int callchain_branch_counts(struct callchain_root *root, | ||
1029 | u64 *branch_count, u64 *predicted_count, | ||
1030 | u64 *abort_count, u64 *cycles_count) | ||
1031 | { | ||
1032 | if (branch_count) | ||
1033 | *branch_count = 0; | ||
1034 | |||
1035 | if (predicted_count) | ||
1036 | *predicted_count = 0; | ||
1037 | |||
1038 | if (abort_count) | ||
1039 | *abort_count = 0; | ||
1040 | |||
1041 | if (cycles_count) | ||
1042 | *cycles_count = 0; | ||
1043 | |||
1044 | return callchain_node_branch_counts_cumul(&root->node, | ||
1045 | branch_count, | ||
1046 | predicted_count, | ||
1047 | abort_count, | ||
1048 | cycles_count); | ||
1049 | } | ||
1050 | |||
1051 | static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, | ||
1052 | u64 branch_count, u64 predicted_count, | ||
1053 | u64 abort_count, u64 cycles_count, | ||
1054 | u64 iter_count, u64 samples_count) | ||
1055 | { | ||
1056 | double predicted_percent = 0.0; | ||
1057 | const char *null_str = ""; | ||
1058 | char iter_str[32]; | ||
1059 | char *str; | ||
1060 | u64 cycles = 0; | ||
1061 | |||
1062 | if (branch_count == 0) { | ||
1063 | if (fp) | ||
1064 | return fprintf(fp, " (calltrace)"); | ||
1065 | |||
1066 | return scnprintf(bf, bfsize, " (calltrace)"); | ||
1067 | } | ||
1068 | |||
1069 | if (iter_count && samples_count) { | ||
1070 | scnprintf(iter_str, sizeof(iter_str), | ||
1071 | ", iterations:%" PRId64 "", | ||
1072 | iter_count / samples_count); | ||
1073 | str = iter_str; | ||
1074 | } else | ||
1075 | str = (char *)null_str; | ||
1076 | |||
1077 | predicted_percent = predicted_count * 100.0 / branch_count; | ||
1078 | cycles = cycles_count / branch_count; | ||
1079 | |||
1080 | if ((predicted_percent >= 100.0) && (abort_count == 0)) { | ||
1081 | if (fp) | ||
1082 | return fprintf(fp, " (cycles:%" PRId64 "%s)", | ||
1083 | cycles, str); | ||
1084 | |||
1085 | return scnprintf(bf, bfsize, " (cycles:%" PRId64 "%s)", | ||
1086 | cycles, str); | ||
1087 | } | ||
1088 | |||
1089 | if ((predicted_percent < 100.0) && (abort_count == 0)) { | ||
1090 | if (fp) | ||
1091 | return fprintf(fp, | ||
1092 | " (predicted:%.1f%%, cycles:%" PRId64 "%s)", | ||
1093 | predicted_percent, cycles, str); | ||
1094 | |||
1095 | return scnprintf(bf, bfsize, | ||
1096 | " (predicted:%.1f%%, cycles:%" PRId64 "%s)", | ||
1097 | predicted_percent, cycles, str); | ||
1098 | } | ||
1099 | |||
1100 | if (fp) | ||
1101 | return fprintf(fp, | ||
1102 | " (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", | ||
1103 | predicted_percent, abort_count, cycles, str); | ||
1104 | |||
1105 | return scnprintf(bf, bfsize, | ||
1106 | " (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", | ||
1107 | predicted_percent, abort_count, cycles, str); | ||
1108 | } | ||
1109 | |||
1110 | int callchain_list_counts__printf_value(struct callchain_node *node, | ||
1111 | struct callchain_list *clist, | ||
1112 | FILE *fp, char *bf, int bfsize) | ||
1113 | { | ||
1114 | u64 branch_count, predicted_count; | ||
1115 | u64 abort_count, cycles_count; | ||
1116 | u64 iter_count = 0, samples_count = 0; | ||
1117 | |||
1118 | branch_count = clist->branch_count; | ||
1119 | predicted_count = clist->predicted_count; | ||
1120 | abort_count = clist->abort_count; | ||
1121 | cycles_count = clist->cycles_count; | ||
1122 | |||
1123 | if (node) { | ||
1124 | struct callchain_list *call; | ||
1125 | |||
1126 | list_for_each_entry(call, &node->val, list) { | ||
1127 | iter_count += call->iter_count; | ||
1128 | samples_count += call->samples_count; | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | return callchain_counts_printf(fp, bf, bfsize, branch_count, | ||
1133 | predicted_count, abort_count, | ||
1134 | cycles_count, iter_count, samples_count); | ||
1135 | } | ||
1136 | |||
942 | static void free_callchain_node(struct callchain_node *node) | 1137 | static void free_callchain_node(struct callchain_node *node) |
943 | { | 1138 | { |
944 | struct callchain_list *list, *tmp; | 1139 | struct callchain_list *list, *tmp; |
@@ -1039,3 +1234,30 @@ out: | |||
1039 | } | 1234 | } |
1040 | return -ENOMEM; | 1235 | return -ENOMEM; |
1041 | } | 1236 | } |
1237 | |||
1238 | int callchain_cursor__copy(struct callchain_cursor *dst, | ||
1239 | struct callchain_cursor *src) | ||
1240 | { | ||
1241 | int rc = 0; | ||
1242 | |||
1243 | callchain_cursor_reset(dst); | ||
1244 | callchain_cursor_commit(src); | ||
1245 | |||
1246 | while (true) { | ||
1247 | struct callchain_cursor_node *node; | ||
1248 | |||
1249 | node = callchain_cursor_current(src); | ||
1250 | if (node == NULL) | ||
1251 | break; | ||
1252 | |||
1253 | rc = callchain_cursor_append(dst, node->ip, node->map, node->sym, | ||
1254 | node->branch, &node->branch_flags, | ||
1255 | node->nr_loop_iter, node->samples); | ||
1256 | if (rc) | ||
1257 | break; | ||
1258 | |||
1259 | callchain_cursor_advance(src); | ||
1260 | } | ||
1261 | |||
1262 | return rc; | ||
1263 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 13e75549c440..35c8e379530f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -11,11 +11,7 @@ | |||
11 | 11 | ||
12 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n" | 12 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace):\n\n" |
13 | 13 | ||
14 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
15 | # define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|dwarf|lbr)\n" | 14 | # define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|dwarf|lbr)\n" |
16 | #else | ||
17 | # define RECORD_MODE_HELP HELP_PAD "record_mode:\tcall graph recording mode (fp|lbr)\n" | ||
18 | #endif | ||
19 | 15 | ||
20 | #define RECORD_SIZE_HELP \ | 16 | #define RECORD_SIZE_HELP \ |
21 | HELP_PAD "record_size:\tif record_mode is 'dwarf', max size of stack recording (<bytes>)\n" \ | 17 | HELP_PAD "record_size:\tif record_mode is 'dwarf', max size of stack recording (<bytes>)\n" \ |
@@ -115,6 +111,12 @@ struct callchain_list { | |||
115 | bool unfolded; | 111 | bool unfolded; |
116 | bool has_children; | 112 | bool has_children; |
117 | }; | 113 | }; |
114 | u64 branch_count; | ||
115 | u64 predicted_count; | ||
116 | u64 abort_count; | ||
117 | u64 cycles_count; | ||
118 | u64 iter_count; | ||
119 | u64 samples_count; | ||
118 | char *srcline; | 120 | char *srcline; |
119 | struct list_head list; | 121 | struct list_head list; |
120 | }; | 122 | }; |
@@ -129,6 +131,10 @@ struct callchain_cursor_node { | |||
129 | u64 ip; | 131 | u64 ip; |
130 | struct map *map; | 132 | struct map *map; |
131 | struct symbol *sym; | 133 | struct symbol *sym; |
134 | bool branch; | ||
135 | struct branch_flags branch_flags; | ||
136 | int nr_loop_iter; | ||
137 | int samples; | ||
132 | struct callchain_cursor_node *next; | 138 | struct callchain_cursor_node *next; |
133 | }; | 139 | }; |
134 | 140 | ||
@@ -183,7 +189,9 @@ static inline void callchain_cursor_reset(struct callchain_cursor *cursor) | |||
183 | } | 189 | } |
184 | 190 | ||
185 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, | 191 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, |
186 | struct map *map, struct symbol *sym); | 192 | struct map *map, struct symbol *sym, |
193 | bool branch, struct branch_flags *flags, | ||
194 | int nr_loop_iter, int samples); | ||
187 | 195 | ||
188 | /* Close a cursor writing session. Initialize for the reader */ | 196 | /* Close a cursor writing session. Initialize for the reader */ |
189 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) | 197 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) |
@@ -208,6 +216,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
208 | cursor->pos++; | 216 | cursor->pos++; |
209 | } | 217 | } |
210 | 218 | ||
219 | int callchain_cursor__copy(struct callchain_cursor *dst, | ||
220 | struct callchain_cursor *src); | ||
221 | |||
211 | struct option; | 222 | struct option; |
212 | struct hist_entry; | 223 | struct hist_entry; |
213 | 224 | ||
@@ -261,8 +272,16 @@ char *callchain_node__scnprintf_value(struct callchain_node *node, | |||
261 | int callchain_node__fprintf_value(struct callchain_node *node, | 272 | int callchain_node__fprintf_value(struct callchain_node *node, |
262 | FILE *fp, u64 total); | 273 | FILE *fp, u64 total); |
263 | 274 | ||
275 | int callchain_list_counts__printf_value(struct callchain_node *node, | ||
276 | struct callchain_list *clist, | ||
277 | FILE *fp, char *bf, int bfsize); | ||
278 | |||
264 | void free_callchain(struct callchain_root *root); | 279 | void free_callchain(struct callchain_root *root); |
265 | void decay_callchain(struct callchain_root *root); | 280 | void decay_callchain(struct callchain_root *root); |
266 | int callchain_node__make_parent_list(struct callchain_node *node); | 281 | int callchain_node__make_parent_list(struct callchain_node *node); |
267 | 282 | ||
283 | int callchain_branch_counts(struct callchain_root *root, | ||
284 | u64 *branch_count, u64 *predicted_count, | ||
285 | u64 *abort_count, u64 *cycles_count); | ||
286 | |||
268 | #endif /* __PERF_CALLCHAIN_H */ | 287 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 18dae745034f..3d906dbbef74 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -594,6 +594,19 @@ static int collect_config(const char *var, const char *value, | |||
594 | goto out_free; | 594 | goto out_free; |
595 | } | 595 | } |
596 | 596 | ||
597 | /* perf_config_set can contain both user and system config items. | ||
598 | * So we should know where each value is from. | ||
599 | * The classification would be needed when a particular config file | ||
600 | * is overwrited by setting feature i.e. set_config(). | ||
601 | */ | ||
602 | if (strcmp(config_file_name, perf_etc_perfconfig()) == 0) { | ||
603 | section->from_system_config = true; | ||
604 | item->from_system_config = true; | ||
605 | } else { | ||
606 | section->from_system_config = false; | ||
607 | item->from_system_config = false; | ||
608 | } | ||
609 | |||
597 | ret = set_value(item, value); | 610 | ret = set_value(item, value); |
598 | return ret; | 611 | return ret; |
599 | 612 | ||
@@ -602,6 +615,13 @@ out_free: | |||
602 | return -1; | 615 | return -1; |
603 | } | 616 | } |
604 | 617 | ||
618 | int perf_config_set__collect(struct perf_config_set *set, const char *file_name, | ||
619 | const char *var, const char *value) | ||
620 | { | ||
621 | config_file_name = file_name; | ||
622 | return collect_config(var, value, set); | ||
623 | } | ||
624 | |||
605 | static int perf_config_set__init(struct perf_config_set *set) | 625 | static int perf_config_set__init(struct perf_config_set *set) |
606 | { | 626 | { |
607 | int ret = -1; | 627 | int ret = -1; |
diff --git a/tools/perf/util/config.h b/tools/perf/util/config.h index 6f813d46045e..1a59a6b43f8b 100644 --- a/tools/perf/util/config.h +++ b/tools/perf/util/config.h | |||
@@ -7,12 +7,14 @@ | |||
7 | struct perf_config_item { | 7 | struct perf_config_item { |
8 | char *name; | 8 | char *name; |
9 | char *value; | 9 | char *value; |
10 | bool from_system_config; | ||
10 | struct list_head node; | 11 | struct list_head node; |
11 | }; | 12 | }; |
12 | 13 | ||
13 | struct perf_config_section { | 14 | struct perf_config_section { |
14 | char *name; | 15 | char *name; |
15 | struct list_head items; | 16 | struct list_head items; |
17 | bool from_system_config; | ||
16 | struct list_head node; | 18 | struct list_head node; |
17 | }; | 19 | }; |
18 | 20 | ||
@@ -33,6 +35,8 @@ const char *perf_etc_perfconfig(void); | |||
33 | 35 | ||
34 | struct perf_config_set *perf_config_set__new(void); | 36 | struct perf_config_set *perf_config_set__new(void); |
35 | void perf_config_set__delete(struct perf_config_set *set); | 37 | void perf_config_set__delete(struct perf_config_set *set); |
38 | int perf_config_set__collect(struct perf_config_set *set, const char *file_name, | ||
39 | const char *var, const char *value); | ||
36 | void perf_config__init(void); | 40 | void perf_config__init(void); |
37 | void perf_config__exit(void); | 41 | void perf_config__exit(void); |
38 | void perf_config__refresh(void); | 42 | void perf_config__refresh(void); |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8d363d5e65a2..c735c53a26f8 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -177,6 +177,8 @@ enum { | |||
177 | PERF_IP_FLAG_TRACE_BEGIN |\ | 177 | PERF_IP_FLAG_TRACE_BEGIN |\ |
178 | PERF_IP_FLAG_TRACE_END) | 178 | PERF_IP_FLAG_TRACE_END) |
179 | 179 | ||
180 | #define MAX_INSN 16 | ||
181 | |||
180 | struct perf_sample { | 182 | struct perf_sample { |
181 | u64 ip; | 183 | u64 ip; |
182 | u32 pid, tid; | 184 | u32 pid, tid; |
@@ -193,6 +195,7 @@ struct perf_sample { | |||
193 | u32 flags; | 195 | u32 flags; |
194 | u16 insn_len; | 196 | u16 insn_len; |
195 | u8 cpumode; | 197 | u8 cpumode; |
198 | char insn[MAX_INSN]; | ||
196 | void *raw_data; | 199 | void *raw_data; |
197 | struct ip_callchain *callchain; | 200 | struct ip_callchain *callchain; |
198 | struct branch_stack *branch_stack; | 201 | struct branch_stack *branch_stack; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8bc271141d9d..04e536ae4d88 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "debug.h" | 28 | #include "debug.h" |
29 | #include "trace-event.h" | 29 | #include "trace-event.h" |
30 | #include "stat.h" | 30 | #include "stat.h" |
31 | #include "util/parse-branch-options.h" | ||
31 | 32 | ||
32 | static struct { | 33 | static struct { |
33 | bool sample_id_all; | 34 | bool sample_id_all; |
@@ -708,6 +709,14 @@ static void apply_config_terms(struct perf_evsel *evsel, | |||
708 | case PERF_EVSEL__CONFIG_TERM_CALLGRAPH: | 709 | case PERF_EVSEL__CONFIG_TERM_CALLGRAPH: |
709 | callgraph_buf = term->val.callgraph; | 710 | callgraph_buf = term->val.callgraph; |
710 | break; | 711 | break; |
712 | case PERF_EVSEL__CONFIG_TERM_BRANCH: | ||
713 | if (term->val.branch && strcmp(term->val.branch, "no")) { | ||
714 | perf_evsel__set_sample_bit(evsel, BRANCH_STACK); | ||
715 | parse_branch_str(term->val.branch, | ||
716 | &attr->branch_sample_type); | ||
717 | } else | ||
718 | perf_evsel__reset_sample_bit(evsel, BRANCH_STACK); | ||
719 | break; | ||
711 | case PERF_EVSEL__CONFIG_TERM_STACK_USER: | 720 | case PERF_EVSEL__CONFIG_TERM_STACK_USER: |
712 | dump_size = term->val.stack_user; | 721 | dump_size = term->val.stack_user; |
713 | break; | 722 | break; |
@@ -981,6 +990,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
981 | * it overloads any global configuration. | 990 | * it overloads any global configuration. |
982 | */ | 991 | */ |
983 | apply_config_terms(evsel, opts); | 992 | apply_config_terms(evsel, opts); |
993 | |||
994 | evsel->ignore_missing_thread = opts->ignore_missing_thread; | ||
984 | } | 995 | } |
985 | 996 | ||
986 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 997 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
@@ -1410,6 +1421,33 @@ static int __open_attr__fprintf(FILE *fp, const char *name, const char *val, | |||
1410 | return fprintf(fp, " %-32s %s\n", name, val); | 1421 | return fprintf(fp, " %-32s %s\n", name, val); |
1411 | } | 1422 | } |
1412 | 1423 | ||
1424 | static bool ignore_missing_thread(struct perf_evsel *evsel, | ||
1425 | struct thread_map *threads, | ||
1426 | int thread, int err) | ||
1427 | { | ||
1428 | if (!evsel->ignore_missing_thread) | ||
1429 | return false; | ||
1430 | |||
1431 | /* The system wide setup does not work with threads. */ | ||
1432 | if (evsel->system_wide) | ||
1433 | return false; | ||
1434 | |||
1435 | /* The -ESRCH is perf event syscall errno for pid's not found. */ | ||
1436 | if (err != -ESRCH) | ||
1437 | return false; | ||
1438 | |||
1439 | /* If there's only one thread, let it fail. */ | ||
1440 | if (threads->nr == 1) | ||
1441 | return false; | ||
1442 | |||
1443 | if (thread_map__remove(threads, thread)) | ||
1444 | return false; | ||
1445 | |||
1446 | pr_warning("WARNING: Ignored open failure for pid %d\n", | ||
1447 | thread_map__pid(threads, thread)); | ||
1448 | return true; | ||
1449 | } | ||
1450 | |||
1413 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1451 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
1414 | struct thread_map *threads) | 1452 | struct thread_map *threads) |
1415 | { | 1453 | { |
@@ -1465,29 +1503,47 @@ retry_sample_id: | |||
1465 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1503 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
1466 | 1504 | ||
1467 | for (thread = 0; thread < nthreads; thread++) { | 1505 | for (thread = 0; thread < nthreads; thread++) { |
1468 | int group_fd; | 1506 | int fd, group_fd; |
1469 | 1507 | ||
1470 | if (!evsel->cgrp && !evsel->system_wide) | 1508 | if (!evsel->cgrp && !evsel->system_wide) |
1471 | pid = thread_map__pid(threads, thread); | 1509 | pid = thread_map__pid(threads, thread); |
1472 | 1510 | ||
1473 | group_fd = get_group_fd(evsel, cpu, thread); | 1511 | group_fd = get_group_fd(evsel, cpu, thread); |
1474 | retry_open: | 1512 | retry_open: |
1475 | pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx\n", | 1513 | pr_debug2("sys_perf_event_open: pid %d cpu %d group_fd %d flags %#lx", |
1476 | pid, cpus->map[cpu], group_fd, flags); | 1514 | pid, cpus->map[cpu], group_fd, flags); |
1477 | 1515 | ||
1478 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 1516 | fd = sys_perf_event_open(&evsel->attr, pid, cpus->map[cpu], |
1479 | pid, | 1517 | group_fd, flags); |
1480 | cpus->map[cpu], | 1518 | |
1481 | group_fd, flags); | 1519 | FD(evsel, cpu, thread) = fd; |
1482 | if (FD(evsel, cpu, thread) < 0) { | 1520 | |
1521 | if (fd < 0) { | ||
1483 | err = -errno; | 1522 | err = -errno; |
1484 | pr_debug2("sys_perf_event_open failed, error %d\n", | 1523 | |
1524 | if (ignore_missing_thread(evsel, threads, thread, err)) { | ||
1525 | /* | ||
1526 | * We just removed 1 thread, so take a step | ||
1527 | * back on thread index and lower the upper | ||
1528 | * nthreads limit. | ||
1529 | */ | ||
1530 | nthreads--; | ||
1531 | thread--; | ||
1532 | |||
1533 | /* ... and pretend like nothing have happened. */ | ||
1534 | err = 0; | ||
1535 | continue; | ||
1536 | } | ||
1537 | |||
1538 | pr_debug2("\nsys_perf_event_open failed, error %d\n", | ||
1485 | err); | 1539 | err); |
1486 | goto try_fallback; | 1540 | goto try_fallback; |
1487 | } | 1541 | } |
1488 | 1542 | ||
1543 | pr_debug2(" = %d\n", fd); | ||
1544 | |||
1489 | if (evsel->bpf_fd >= 0) { | 1545 | if (evsel->bpf_fd >= 0) { |
1490 | int evt_fd = FD(evsel, cpu, thread); | 1546 | int evt_fd = fd; |
1491 | int bpf_fd = evsel->bpf_fd; | 1547 | int bpf_fd = evsel->bpf_fd; |
1492 | 1548 | ||
1493 | err = ioctl(evt_fd, | 1549 | err = ioctl(evt_fd, |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b1503b0ecdff..06ef6f29efa1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -47,6 +47,7 @@ enum { | |||
47 | PERF_EVSEL__CONFIG_TERM_MAX_STACK, | 47 | PERF_EVSEL__CONFIG_TERM_MAX_STACK, |
48 | PERF_EVSEL__CONFIG_TERM_OVERWRITE, | 48 | PERF_EVSEL__CONFIG_TERM_OVERWRITE, |
49 | PERF_EVSEL__CONFIG_TERM_DRV_CFG, | 49 | PERF_EVSEL__CONFIG_TERM_DRV_CFG, |
50 | PERF_EVSEL__CONFIG_TERM_BRANCH, | ||
50 | PERF_EVSEL__CONFIG_TERM_MAX, | 51 | PERF_EVSEL__CONFIG_TERM_MAX, |
51 | }; | 52 | }; |
52 | 53 | ||
@@ -63,6 +64,7 @@ struct perf_evsel_config_term { | |||
63 | int max_stack; | 64 | int max_stack; |
64 | bool inherit; | 65 | bool inherit; |
65 | bool overwrite; | 66 | bool overwrite; |
67 | char *branch; | ||
66 | } val; | 68 | } val; |
67 | }; | 69 | }; |
68 | 70 | ||
@@ -118,6 +120,7 @@ struct perf_evsel { | |||
118 | bool tracking; | 120 | bool tracking; |
119 | bool per_pkg; | 121 | bool per_pkg; |
120 | bool precise_max; | 122 | bool precise_max; |
123 | bool ignore_missing_thread; | ||
121 | /* parse modifier helper */ | 124 | /* parse modifier helper */ |
122 | int exclude_GH; | 125 | int exclude_GH; |
123 | int nr_members; | 126 | int nr_members; |
@@ -389,6 +392,8 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
389 | #define EVSEL__PRINT_ONELINE (1<<4) | 392 | #define EVSEL__PRINT_ONELINE (1<<4) |
390 | #define EVSEL__PRINT_SRCLINE (1<<5) | 393 | #define EVSEL__PRINT_SRCLINE (1<<5) |
391 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) | 394 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) |
395 | #define EVSEL__PRINT_CALLCHAIN_ARROW (1<<7) | ||
396 | #define EVSEL__PRINT_SKIP_IGNORED (1<<8) | ||
392 | 397 | ||
393 | struct callchain_cursor; | 398 | struct callchain_cursor; |
394 | 399 | ||
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 662a0a6182e7..6b2925542c0a 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -108,7 +108,10 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
108 | int print_oneline = print_opts & EVSEL__PRINT_ONELINE; | 108 | int print_oneline = print_opts & EVSEL__PRINT_ONELINE; |
109 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | 109 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
110 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | 110 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
111 | int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW; | ||
112 | int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED; | ||
111 | char s = print_oneline ? ' ' : '\t'; | 113 | char s = print_oneline ? ' ' : '\t'; |
114 | bool first = true; | ||
112 | 115 | ||
113 | if (sample->callchain) { | 116 | if (sample->callchain) { |
114 | struct addr_location node_al; | 117 | struct addr_location node_al; |
@@ -122,8 +125,14 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
122 | if (!node) | 125 | if (!node) |
123 | break; | 126 | break; |
124 | 127 | ||
128 | if (node->sym && node->sym->ignore && print_skip_ignored) | ||
129 | goto next; | ||
130 | |||
125 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | 131 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
126 | 132 | ||
133 | if (print_arrow && !first) | ||
134 | printed += fprintf(fp, " <-"); | ||
135 | |||
127 | if (print_ip) | 136 | if (print_ip) |
128 | printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); | 137 | printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); |
129 | 138 | ||
@@ -137,7 +146,8 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
137 | 146 | ||
138 | if (print_symoffset) { | 147 | if (print_symoffset) { |
139 | printed += __symbol__fprintf_symname_offs(node->sym, &node_al, | 148 | printed += __symbol__fprintf_symname_offs(node->sym, &node_al, |
140 | print_unknown_as_addr, fp); | 149 | print_unknown_as_addr, |
150 | true, fp); | ||
141 | } else { | 151 | } else { |
142 | printed += __symbol__fprintf_symname(node->sym, &node_al, | 152 | printed += __symbol__fprintf_symname(node->sym, &node_al, |
143 | print_unknown_as_addr, fp); | 153 | print_unknown_as_addr, fp); |
@@ -156,6 +166,16 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
156 | if (!print_oneline) | 166 | if (!print_oneline) |
157 | printed += fprintf(fp, "\n"); | 167 | printed += fprintf(fp, "\n"); |
158 | 168 | ||
169 | if (symbol_conf.bt_stop_list && | ||
170 | node->sym && | ||
171 | node->sym->name && | ||
172 | strlist__has_entry(symbol_conf.bt_stop_list, | ||
173 | node->sym->name)) { | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | first = false; | ||
178 | next: | ||
159 | callchain_cursor_advance(cursor); | 179 | callchain_cursor_advance(cursor); |
160 | } | 180 | } |
161 | } | 181 | } |
@@ -188,7 +208,8 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, | |||
188 | printed += fprintf(fp, " "); | 208 | printed += fprintf(fp, " "); |
189 | if (print_symoffset) { | 209 | if (print_symoffset) { |
190 | printed += __symbol__fprintf_symname_offs(al->sym, al, | 210 | printed += __symbol__fprintf_symname_offs(al->sym, al, |
191 | print_unknown_as_addr, fp); | 211 | print_unknown_as_addr, |
212 | true, fp); | ||
192 | } else { | 213 | } else { |
193 | printed += __symbol__fprintf_symname(al->sym, al, | 214 | printed += __symbol__fprintf_symname(al->sym, al, |
194 | print_unknown_as_addr, fp); | 215 | print_unknown_as_addr, fp); |
diff --git a/tools/perf/util/genelf.c b/tools/perf/util/genelf.c index c1ef805c6a8f..c540d47583e7 100644 --- a/tools/perf/util/genelf.c +++ b/tools/perf/util/genelf.c | |||
@@ -19,12 +19,18 @@ | |||
19 | #include <limits.h> | 19 | #include <limits.h> |
20 | #include <fcntl.h> | 20 | #include <fcntl.h> |
21 | #include <err.h> | 21 | #include <err.h> |
22 | #ifdef HAVE_DWARF_SUPPORT | ||
22 | #include <dwarf.h> | 23 | #include <dwarf.h> |
24 | #endif | ||
23 | 25 | ||
24 | #include "perf.h" | 26 | #include "perf.h" |
25 | #include "genelf.h" | 27 | #include "genelf.h" |
26 | #include "../util/jitdump.h" | 28 | #include "../util/jitdump.h" |
27 | 29 | ||
30 | #ifndef NT_GNU_BUILD_ID | ||
31 | #define NT_GNU_BUILD_ID 3 | ||
32 | #endif | ||
33 | |||
28 | #define JVMTI | 34 | #define JVMTI |
29 | 35 | ||
30 | #define BUILD_ID_URANDOM /* different uuid for each run */ | 36 | #define BUILD_ID_URANDOM /* different uuid for each run */ |
@@ -67,6 +73,8 @@ static char shd_string_table[] = { | |||
67 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ | 73 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */ |
68 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ | 74 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */ |
69 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ | 75 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */ |
76 | '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', '_', 'h', 'd', 'r', 0, /* 90 */ | ||
77 | '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, /* 104 */ | ||
70 | }; | 78 | }; |
71 | 79 | ||
72 | static struct buildid_note { | 80 | static struct buildid_note { |
@@ -147,6 +155,86 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod | |||
147 | } | 155 | } |
148 | #endif | 156 | #endif |
149 | 157 | ||
158 | static int | ||
159 | jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size, | ||
160 | uint64_t unwinding_size, uint64_t base_offset) | ||
161 | { | ||
162 | Elf_Data *d; | ||
163 | Elf_Scn *scn; | ||
164 | Elf_Shdr *shdr; | ||
165 | uint64_t unwinding_table_size = unwinding_size - unwinding_header_size; | ||
166 | |||
167 | /* | ||
168 | * setup eh_frame section | ||
169 | */ | ||
170 | scn = elf_newscn(e); | ||
171 | if (!scn) { | ||
172 | warnx("cannot create section"); | ||
173 | return -1; | ||
174 | } | ||
175 | |||
176 | d = elf_newdata(scn); | ||
177 | if (!d) { | ||
178 | warnx("cannot get new data"); | ||
179 | return -1; | ||
180 | } | ||
181 | |||
182 | d->d_align = 8; | ||
183 | d->d_off = 0LL; | ||
184 | d->d_buf = unwinding; | ||
185 | d->d_type = ELF_T_BYTE; | ||
186 | d->d_size = unwinding_table_size; | ||
187 | d->d_version = EV_CURRENT; | ||
188 | |||
189 | shdr = elf_getshdr(scn); | ||
190 | if (!shdr) { | ||
191 | warnx("cannot get section header"); | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | shdr->sh_name = 104; | ||
196 | shdr->sh_type = SHT_PROGBITS; | ||
197 | shdr->sh_addr = base_offset; | ||
198 | shdr->sh_flags = SHF_ALLOC; | ||
199 | shdr->sh_entsize = 0; | ||
200 | |||
201 | /* | ||
202 | * setup eh_frame_hdr section | ||
203 | */ | ||
204 | scn = elf_newscn(e); | ||
205 | if (!scn) { | ||
206 | warnx("cannot create section"); | ||
207 | return -1; | ||
208 | } | ||
209 | |||
210 | d = elf_newdata(scn); | ||
211 | if (!d) { | ||
212 | warnx("cannot get new data"); | ||
213 | return -1; | ||
214 | } | ||
215 | |||
216 | d->d_align = 4; | ||
217 | d->d_off = 0LL; | ||
218 | d->d_buf = unwinding + unwinding_table_size; | ||
219 | d->d_type = ELF_T_BYTE; | ||
220 | d->d_size = unwinding_header_size; | ||
221 | d->d_version = EV_CURRENT; | ||
222 | |||
223 | shdr = elf_getshdr(scn); | ||
224 | if (!shdr) { | ||
225 | warnx("cannot get section header"); | ||
226 | return -1; | ||
227 | } | ||
228 | |||
229 | shdr->sh_name = 90; | ||
230 | shdr->sh_type = SHT_PROGBITS; | ||
231 | shdr->sh_addr = base_offset + unwinding_table_size; | ||
232 | shdr->sh_flags = SHF_ALLOC; | ||
233 | shdr->sh_entsize = 0; | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
150 | /* | 238 | /* |
151 | * fd: file descriptor open for writing for the output file | 239 | * fd: file descriptor open for writing for the output file |
152 | * load_addr: code load address (could be zero, just used for buildid) | 240 | * load_addr: code load address (could be zero, just used for buildid) |
@@ -157,13 +245,15 @@ gen_build_id(struct buildid_note *note, unsigned long load_addr, const void *cod | |||
157 | int | 245 | int |
158 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, | 246 | jit_write_elf(int fd, uint64_t load_addr, const char *sym, |
159 | const void *code, int csize, | 247 | const void *code, int csize, |
160 | void *debug, int nr_debug_entries) | 248 | void *debug __maybe_unused, int nr_debug_entries __maybe_unused, |
249 | void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size) | ||
161 | { | 250 | { |
162 | Elf *e; | 251 | Elf *e; |
163 | Elf_Data *d; | 252 | Elf_Data *d; |
164 | Elf_Scn *scn; | 253 | Elf_Scn *scn; |
165 | Elf_Ehdr *ehdr; | 254 | Elf_Ehdr *ehdr; |
166 | Elf_Shdr *shdr; | 255 | Elf_Shdr *shdr; |
256 | uint64_t eh_frame_base_offset; | ||
167 | char *strsym = NULL; | 257 | char *strsym = NULL; |
168 | int symlen; | 258 | int symlen; |
169 | int retval = -1; | 259 | int retval = -1; |
@@ -194,7 +284,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, | |||
194 | ehdr->e_type = ET_DYN; | 284 | ehdr->e_type = ET_DYN; |
195 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET; | 285 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET; |
196 | ehdr->e_version = EV_CURRENT; | 286 | ehdr->e_version = EV_CURRENT; |
197 | ehdr->e_shstrndx= 2; /* shdr index for section name */ | 287 | ehdr->e_shstrndx= unwinding ? 4 : 2; /* shdr index for section name */ |
198 | 288 | ||
199 | /* | 289 | /* |
200 | * setup text section | 290 | * setup text section |
@@ -231,6 +321,18 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, | |||
231 | shdr->sh_entsize = 0; | 321 | shdr->sh_entsize = 0; |
232 | 322 | ||
233 | /* | 323 | /* |
324 | * Setup .eh_frame_hdr and .eh_frame | ||
325 | */ | ||
326 | if (unwinding) { | ||
327 | eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize); | ||
328 | retval = jit_add_eh_frame_info(e, unwinding, | ||
329 | unwinding_header_size, unwinding_size, | ||
330 | eh_frame_base_offset); | ||
331 | if (retval) | ||
332 | goto error; | ||
333 | } | ||
334 | |||
335 | /* | ||
234 | * setup section headers string table | 336 | * setup section headers string table |
235 | */ | 337 | */ |
236 | scn = elf_newscn(e); | 338 | scn = elf_newscn(e); |
@@ -298,7 +400,7 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, | |||
298 | shdr->sh_type = SHT_SYMTAB; | 400 | shdr->sh_type = SHT_SYMTAB; |
299 | shdr->sh_flags = 0; | 401 | shdr->sh_flags = 0; |
300 | shdr->sh_entsize = sizeof(Elf_Sym); | 402 | shdr->sh_entsize = sizeof(Elf_Sym); |
301 | shdr->sh_link = 4; /* index of .strtab section */ | 403 | shdr->sh_link = unwinding ? 6 : 4; /* index of .strtab section */ |
302 | 404 | ||
303 | /* | 405 | /* |
304 | * setup symbols string table | 406 | * setup symbols string table |
@@ -386,11 +488,14 @@ jit_write_elf(int fd, uint64_t load_addr, const char *sym, | |||
386 | shdr->sh_size = sizeof(bnote); | 488 | shdr->sh_size = sizeof(bnote); |
387 | shdr->sh_entsize = 0; | 489 | shdr->sh_entsize = 0; |
388 | 490 | ||
491 | #ifdef HAVE_DWARF_SUPPORT | ||
389 | if (debug && nr_debug_entries) { | 492 | if (debug && nr_debug_entries) { |
390 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); | 493 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries); |
391 | if (retval) | 494 | if (retval) |
392 | goto error; | 495 | goto error; |
393 | } else { | 496 | } else |
497 | #endif | ||
498 | { | ||
394 | if (elf_update(e, ELF_C_WRITE) < 0) { | 499 | if (elf_update(e, ELF_C_WRITE) < 0) { |
395 | warnx("elf_update 4 failed"); | 500 | warnx("elf_update 4 failed"); |
396 | goto error; | 501 | goto error; |
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index 2fbeb59c4bdd..2424bd9862a3 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h | |||
@@ -3,9 +3,12 @@ | |||
3 | 3 | ||
4 | /* genelf.c */ | 4 | /* genelf.c */ |
5 | int jit_write_elf(int fd, uint64_t code_addr, const char *sym, | 5 | int jit_write_elf(int fd, uint64_t code_addr, const char *sym, |
6 | const void *code, int csize, void *debug, int nr_debug_entries); | 6 | const void *code, int csize, void *debug, int nr_debug_entries, |
7 | void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size); | ||
8 | #ifdef HAVE_DWARF_SUPPORT | ||
7 | /* genelf_debug.c */ | 9 | /* genelf_debug.c */ |
8 | int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); | 10 | int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries); |
11 | #endif | ||
9 | 12 | ||
10 | #if defined(__arm__) | 13 | #if defined(__arm__) |
11 | #define GEN_ELF_ARCH EM_ARM | 14 | #define GEN_ELF_ARCH EM_ARM |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 2f3eded54b0c..d89c9c7ef4e5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -2250,11 +2250,28 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | |||
2250 | struct header_print_data hd; | 2250 | struct header_print_data hd; |
2251 | struct perf_header *header = &session->header; | 2251 | struct perf_header *header = &session->header; |
2252 | int fd = perf_data_file__fd(session->file); | 2252 | int fd = perf_data_file__fd(session->file); |
2253 | struct stat st; | ||
2254 | int ret, bit; | ||
2255 | |||
2253 | hd.fp = fp; | 2256 | hd.fp = fp; |
2254 | hd.full = full; | 2257 | hd.full = full; |
2255 | 2258 | ||
2259 | ret = fstat(fd, &st); | ||
2260 | if (ret == -1) | ||
2261 | return -1; | ||
2262 | |||
2263 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
2264 | |||
2256 | perf_header__process_sections(header, fd, &hd, | 2265 | perf_header__process_sections(header, fd, &hd, |
2257 | perf_file_section__fprintf_info); | 2266 | perf_file_section__fprintf_info); |
2267 | |||
2268 | fprintf(fp, "# missing features: "); | ||
2269 | for_each_clear_bit(bit, header->adds_features, HEADER_LAST_FEATURE) { | ||
2270 | if (bit) | ||
2271 | fprintf(fp, "%s ", feat_ops[bit].name); | ||
2272 | } | ||
2273 | |||
2274 | fprintf(fp, "\n"); | ||
2258 | return 0; | 2275 | return 0; |
2259 | } | 2276 | } |
2260 | 2277 | ||
@@ -2273,7 +2290,7 @@ static int do_write_feat(int fd, struct perf_header *h, int type, | |||
2273 | 2290 | ||
2274 | err = feat_ops[type].write(fd, h, evlist); | 2291 | err = feat_ops[type].write(fd, h, evlist); |
2275 | if (err < 0) { | 2292 | if (err < 0) { |
2276 | pr_debug("failed to write feature %d\n", type); | 2293 | pr_debug("failed to write feature %s\n", feat_ops[type].name); |
2277 | 2294 | ||
2278 | /* undo anything written */ | 2295 | /* undo anything written */ |
2279 | lseek(fd, (*p)->offset, SEEK_SET); | 2296 | lseek(fd, (*p)->offset, SEEK_SET); |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index b02992efb513..6770a9645609 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1195,6 +1195,7 @@ static void hist_entry__check_and_remove_filter(struct hist_entry *he, | |||
1195 | case HIST_FILTER__GUEST: | 1195 | case HIST_FILTER__GUEST: |
1196 | case HIST_FILTER__HOST: | 1196 | case HIST_FILTER__HOST: |
1197 | case HIST_FILTER__SOCKET: | 1197 | case HIST_FILTER__SOCKET: |
1198 | case HIST_FILTER__C2C: | ||
1198 | default: | 1199 | default: |
1199 | return; | 1200 | return; |
1200 | } | 1201 | } |
@@ -1600,18 +1601,18 @@ static void hists__hierarchy_output_resort(struct hists *hists, | |||
1600 | if (prog) | 1601 | if (prog) |
1601 | ui_progress__update(prog, 1); | 1602 | ui_progress__update(prog, 1); |
1602 | 1603 | ||
1604 | hists->nr_entries++; | ||
1605 | if (!he->filtered) { | ||
1606 | hists->nr_non_filtered_entries++; | ||
1607 | hists__calc_col_len(hists, he); | ||
1608 | } | ||
1609 | |||
1603 | if (!he->leaf) { | 1610 | if (!he->leaf) { |
1604 | hists__hierarchy_output_resort(hists, prog, | 1611 | hists__hierarchy_output_resort(hists, prog, |
1605 | &he->hroot_in, | 1612 | &he->hroot_in, |
1606 | &he->hroot_out, | 1613 | &he->hroot_out, |
1607 | min_callchain_hits, | 1614 | min_callchain_hits, |
1608 | use_callchain); | 1615 | use_callchain); |
1609 | hists->nr_entries++; | ||
1610 | if (!he->filtered) { | ||
1611 | hists->nr_non_filtered_entries++; | ||
1612 | hists__calc_col_len(hists, he); | ||
1613 | } | ||
1614 | |||
1615 | continue; | 1616 | continue; |
1616 | } | 1617 | } |
1617 | 1618 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9928fed8bc59..d4b6514eeef5 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -22,6 +22,7 @@ enum hist_filter { | |||
22 | HIST_FILTER__GUEST, | 22 | HIST_FILTER__GUEST, |
23 | HIST_FILTER__HOST, | 23 | HIST_FILTER__HOST, |
24 | HIST_FILTER__SOCKET, | 24 | HIST_FILTER__SOCKET, |
25 | HIST_FILTER__C2C, | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | enum hist_column { | 28 | enum hist_column { |
diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index f545ec1e758a..6c2eb5da4afc 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c | |||
@@ -295,6 +295,7 @@ static int intel_bts_synth_branch_sample(struct intel_bts_queue *btsq, | |||
295 | sample.cpu = btsq->cpu; | 295 | sample.cpu = btsq->cpu; |
296 | sample.flags = btsq->sample_flags; | 296 | sample.flags = btsq->sample_flags; |
297 | sample.insn_len = btsq->intel_pt_insn.length; | 297 | sample.insn_len = btsq->intel_pt_insn.length; |
298 | memcpy(sample.insn, btsq->intel_pt_insn.buf, INTEL_PT_INSN_BUF_SZ); | ||
298 | 299 | ||
299 | if (bts->synth_opts.inject) { | 300 | if (bts->synth_opts.inject) { |
300 | event.sample.header.size = bts->branches_event_size; | 301 | event.sample.header.size = bts->branches_event_size; |
@@ -319,15 +320,12 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) | |||
319 | struct machine *machine = btsq->bts->machine; | 320 | struct machine *machine = btsq->bts->machine; |
320 | struct thread *thread; | 321 | struct thread *thread; |
321 | struct addr_location al; | 322 | struct addr_location al; |
322 | unsigned char buf[1024]; | 323 | unsigned char buf[INTEL_PT_INSN_BUF_SZ]; |
323 | size_t bufsz; | ||
324 | ssize_t len; | 324 | ssize_t len; |
325 | int x86_64; | 325 | int x86_64; |
326 | uint8_t cpumode; | 326 | uint8_t cpumode; |
327 | int err = -1; | 327 | int err = -1; |
328 | 328 | ||
329 | bufsz = intel_pt_insn_max_size(); | ||
330 | |||
331 | if (machine__kernel_ip(machine, ip)) | 329 | if (machine__kernel_ip(machine, ip)) |
332 | cpumode = PERF_RECORD_MISC_KERNEL; | 330 | cpumode = PERF_RECORD_MISC_KERNEL; |
333 | else | 331 | else |
@@ -341,7 +339,8 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip) | |||
341 | if (!al.map || !al.map->dso) | 339 | if (!al.map || !al.map->dso) |
342 | goto out_put; | 340 | goto out_put; |
343 | 341 | ||
344 | len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, bufsz); | 342 | len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf, |
343 | INTEL_PT_INSN_BUF_SZ); | ||
345 | if (len <= 0) | 344 | if (len <= 0) |
346 | goto out_put; | 345 | goto out_put; |
347 | 346 | ||
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 16c06d3ae577..e4e7dc781d21 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
@@ -980,6 +980,8 @@ out: | |||
980 | out_no_progress: | 980 | out_no_progress: |
981 | decoder->state.insn_op = intel_pt_insn->op; | 981 | decoder->state.insn_op = intel_pt_insn->op; |
982 | decoder->state.insn_len = intel_pt_insn->length; | 982 | decoder->state.insn_len = intel_pt_insn->length; |
983 | memcpy(decoder->state.insn, intel_pt_insn->buf, | ||
984 | INTEL_PT_INSN_BUF_SZ); | ||
983 | 985 | ||
984 | if (decoder->tx_flags & INTEL_PT_IN_TX) | 986 | if (decoder->tx_flags & INTEL_PT_IN_TX) |
985 | decoder->state.flags |= INTEL_PT_IN_TX; | 987 | decoder->state.flags |= INTEL_PT_IN_TX; |
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 89399985fa4d..e90619a43c0c 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.h | |||
@@ -66,6 +66,7 @@ struct intel_pt_state { | |||
66 | uint32_t flags; | 66 | uint32_t flags; |
67 | enum intel_pt_insn_op insn_op; | 67 | enum intel_pt_insn_op insn_op; |
68 | int insn_len; | 68 | int insn_len; |
69 | char insn[INTEL_PT_INSN_BUF_SZ]; | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | struct intel_pt_insn; | 72 | struct intel_pt_insn; |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index d23138c06665..7913363bde5c 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c | |||
@@ -27,6 +27,10 @@ | |||
27 | 27 | ||
28 | #include "intel-pt-insn-decoder.h" | 28 | #include "intel-pt-insn-decoder.h" |
29 | 29 | ||
30 | #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN | ||
31 | #error Instruction buffer size too small | ||
32 | #endif | ||
33 | |||
30 | /* Based on branch_type() from perf_event_intel_lbr.c */ | 34 | /* Based on branch_type() from perf_event_intel_lbr.c */ |
31 | static void intel_pt_insn_decoder(struct insn *insn, | 35 | static void intel_pt_insn_decoder(struct insn *insn, |
32 | struct intel_pt_insn *intel_pt_insn) | 36 | struct intel_pt_insn *intel_pt_insn) |
@@ -166,10 +170,10 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, | |||
166 | if (!insn_complete(&insn) || insn.length > len) | 170 | if (!insn_complete(&insn) || insn.length > len) |
167 | return -1; | 171 | return -1; |
168 | intel_pt_insn_decoder(&insn, intel_pt_insn); | 172 | intel_pt_insn_decoder(&insn, intel_pt_insn); |
169 | if (insn.length < INTEL_PT_INSN_DBG_BUF_SZ) | 173 | if (insn.length < INTEL_PT_INSN_BUF_SZ) |
170 | memcpy(intel_pt_insn->buf, buf, insn.length); | 174 | memcpy(intel_pt_insn->buf, buf, insn.length); |
171 | else | 175 | else |
172 | memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_DBG_BUF_SZ); | 176 | memcpy(intel_pt_insn->buf, buf, INTEL_PT_INSN_BUF_SZ); |
173 | return 0; | 177 | return 0; |
174 | } | 178 | } |
175 | 179 | ||
@@ -211,11 +215,6 @@ int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, | |||
211 | return 0; | 215 | return 0; |
212 | } | 216 | } |
213 | 217 | ||
214 | size_t intel_pt_insn_max_size(void) | ||
215 | { | ||
216 | return MAX_INSN_SIZE; | ||
217 | } | ||
218 | |||
219 | int intel_pt_insn_type(enum intel_pt_insn_op op) | 218 | int intel_pt_insn_type(enum intel_pt_insn_op op) |
220 | { | 219 | { |
221 | switch (op) { | 220 | switch (op) { |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h index b0adbf37323e..37ec5627ae9b 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <stdint.h> | 20 | #include <stdint.h> |
21 | 21 | ||
22 | #define INTEL_PT_INSN_DESC_MAX 32 | 22 | #define INTEL_PT_INSN_DESC_MAX 32 |
23 | #define INTEL_PT_INSN_DBG_BUF_SZ 16 | 23 | #define INTEL_PT_INSN_BUF_SZ 16 |
24 | 24 | ||
25 | enum intel_pt_insn_op { | 25 | enum intel_pt_insn_op { |
26 | INTEL_PT_OP_OTHER, | 26 | INTEL_PT_OP_OTHER, |
@@ -47,7 +47,7 @@ struct intel_pt_insn { | |||
47 | enum intel_pt_insn_branch branch; | 47 | enum intel_pt_insn_branch branch; |
48 | int length; | 48 | int length; |
49 | int32_t rel; | 49 | int32_t rel; |
50 | unsigned char buf[INTEL_PT_INSN_DBG_BUF_SZ]; | 50 | unsigned char buf[INTEL_PT_INSN_BUF_SZ]; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, | 53 | int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, |
@@ -58,8 +58,6 @@ const char *intel_pt_insn_name(enum intel_pt_insn_op op); | |||
58 | int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, | 58 | int intel_pt_insn_desc(const struct intel_pt_insn *intel_pt_insn, char *buf, |
59 | size_t buf_len); | 59 | size_t buf_len); |
60 | 60 | ||
61 | size_t intel_pt_insn_max_size(void); | ||
62 | |||
63 | int intel_pt_insn_type(enum intel_pt_insn_op op); | 61 | int intel_pt_insn_type(enum intel_pt_insn_op op); |
64 | 62 | ||
65 | #endif | 63 | #endif |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c index 319bef33a64b..e02bc7b166a0 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c | |||
@@ -119,8 +119,8 @@ void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip) | |||
119 | if (intel_pt_log_open()) | 119 | if (intel_pt_log_open()) |
120 | return; | 120 | return; |
121 | 121 | ||
122 | if (len > INTEL_PT_INSN_DBG_BUF_SZ) | 122 | if (len > INTEL_PT_INSN_BUF_SZ) |
123 | len = INTEL_PT_INSN_DBG_BUF_SZ; | 123 | len = INTEL_PT_INSN_BUF_SZ; |
124 | intel_pt_print_data(intel_pt_insn->buf, len, ip, 8); | 124 | intel_pt_print_data(intel_pt_insn->buf, len, ip, 8); |
125 | if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) | 125 | if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) |
126 | fprintf(f, "%s\n", desc); | 126 | fprintf(f, "%s\n", desc); |
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index dc041d4368c8..85d5eeb66c75 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c | |||
@@ -143,6 +143,7 @@ struct intel_pt_queue { | |||
143 | u32 flags; | 143 | u32 flags; |
144 | u16 insn_len; | 144 | u16 insn_len; |
145 | u64 last_insn_cnt; | 145 | u64 last_insn_cnt; |
146 | char insn[INTEL_PT_INSN_BUF_SZ]; | ||
146 | }; | 147 | }; |
147 | 148 | ||
148 | static void intel_pt_dump(struct intel_pt *pt __maybe_unused, | 149 | static void intel_pt_dump(struct intel_pt *pt __maybe_unused, |
@@ -315,6 +316,7 @@ struct intel_pt_cache_entry { | |||
315 | enum intel_pt_insn_branch branch; | 316 | enum intel_pt_insn_branch branch; |
316 | int length; | 317 | int length; |
317 | int32_t rel; | 318 | int32_t rel; |
319 | char insn[INTEL_PT_INSN_BUF_SZ]; | ||
318 | }; | 320 | }; |
319 | 321 | ||
320 | static int intel_pt_config_div(const char *var, const char *value, void *data) | 322 | static int intel_pt_config_div(const char *var, const char *value, void *data) |
@@ -400,6 +402,7 @@ static int intel_pt_cache_add(struct dso *dso, struct machine *machine, | |||
400 | e->branch = intel_pt_insn->branch; | 402 | e->branch = intel_pt_insn->branch; |
401 | e->length = intel_pt_insn->length; | 403 | e->length = intel_pt_insn->length; |
402 | e->rel = intel_pt_insn->rel; | 404 | e->rel = intel_pt_insn->rel; |
405 | memcpy(e->insn, intel_pt_insn->buf, INTEL_PT_INSN_BUF_SZ); | ||
403 | 406 | ||
404 | err = auxtrace_cache__add(c, offset, &e->entry); | 407 | err = auxtrace_cache__add(c, offset, &e->entry); |
405 | if (err) | 408 | if (err) |
@@ -428,8 +431,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
428 | struct machine *machine = ptq->pt->machine; | 431 | struct machine *machine = ptq->pt->machine; |
429 | struct thread *thread; | 432 | struct thread *thread; |
430 | struct addr_location al; | 433 | struct addr_location al; |
431 | unsigned char buf[1024]; | 434 | unsigned char buf[INTEL_PT_INSN_BUF_SZ]; |
432 | size_t bufsz; | ||
433 | ssize_t len; | 435 | ssize_t len; |
434 | int x86_64; | 436 | int x86_64; |
435 | u8 cpumode; | 437 | u8 cpumode; |
@@ -437,11 +439,11 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
437 | u64 insn_cnt = 0; | 439 | u64 insn_cnt = 0; |
438 | bool one_map = true; | 440 | bool one_map = true; |
439 | 441 | ||
442 | intel_pt_insn->length = 0; | ||
443 | |||
440 | if (to_ip && *ip == to_ip) | 444 | if (to_ip && *ip == to_ip) |
441 | goto out_no_cache; | 445 | goto out_no_cache; |
442 | 446 | ||
443 | bufsz = intel_pt_insn_max_size(); | ||
444 | |||
445 | if (*ip >= ptq->pt->kernel_start) | 447 | if (*ip >= ptq->pt->kernel_start) |
446 | cpumode = PERF_RECORD_MISC_KERNEL; | 448 | cpumode = PERF_RECORD_MISC_KERNEL; |
447 | else | 449 | else |
@@ -478,6 +480,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
478 | intel_pt_insn->branch = e->branch; | 480 | intel_pt_insn->branch = e->branch; |
479 | intel_pt_insn->length = e->length; | 481 | intel_pt_insn->length = e->length; |
480 | intel_pt_insn->rel = e->rel; | 482 | intel_pt_insn->rel = e->rel; |
483 | memcpy(intel_pt_insn->buf, e->insn, | ||
484 | INTEL_PT_INSN_BUF_SZ); | ||
481 | intel_pt_log_insn_no_data(intel_pt_insn, *ip); | 485 | intel_pt_log_insn_no_data(intel_pt_insn, *ip); |
482 | return 0; | 486 | return 0; |
483 | } | 487 | } |
@@ -493,7 +497,8 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn, | |||
493 | 497 | ||
494 | while (1) { | 498 | while (1) { |
495 | len = dso__data_read_offset(al.map->dso, machine, | 499 | len = dso__data_read_offset(al.map->dso, machine, |
496 | offset, buf, bufsz); | 500 | offset, buf, |
501 | INTEL_PT_INSN_BUF_SZ); | ||
497 | if (len <= 0) | 502 | if (len <= 0) |
498 | return -EINVAL; | 503 | return -EINVAL; |
499 | 504 | ||
@@ -900,6 +905,7 @@ static void intel_pt_sample_flags(struct intel_pt_queue *ptq) | |||
900 | if (ptq->state->flags & INTEL_PT_IN_TX) | 905 | if (ptq->state->flags & INTEL_PT_IN_TX) |
901 | ptq->flags |= PERF_IP_FLAG_IN_TX; | 906 | ptq->flags |= PERF_IP_FLAG_IN_TX; |
902 | ptq->insn_len = ptq->state->insn_len; | 907 | ptq->insn_len = ptq->state->insn_len; |
908 | memcpy(ptq->insn, ptq->state->insn, INTEL_PT_INSN_BUF_SZ); | ||
903 | } | 909 | } |
904 | } | 910 | } |
905 | 911 | ||
@@ -1080,6 +1086,7 @@ static int intel_pt_synth_branch_sample(struct intel_pt_queue *ptq) | |||
1080 | sample.cpu = ptq->cpu; | 1086 | sample.cpu = ptq->cpu; |
1081 | sample.flags = ptq->flags; | 1087 | sample.flags = ptq->flags; |
1082 | sample.insn_len = ptq->insn_len; | 1088 | sample.insn_len = ptq->insn_len; |
1089 | memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); | ||
1083 | 1090 | ||
1084 | /* | 1091 | /* |
1085 | * perf report cannot handle events without a branch stack when using | 1092 | * perf report cannot handle events without a branch stack when using |
@@ -1141,6 +1148,7 @@ static int intel_pt_synth_instruction_sample(struct intel_pt_queue *ptq) | |||
1141 | sample.cpu = ptq->cpu; | 1148 | sample.cpu = ptq->cpu; |
1142 | sample.flags = ptq->flags; | 1149 | sample.flags = ptq->flags; |
1143 | sample.insn_len = ptq->insn_len; | 1150 | sample.insn_len = ptq->insn_len; |
1151 | memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); | ||
1144 | 1152 | ||
1145 | ptq->last_insn_cnt = ptq->state->tot_insn_cnt; | 1153 | ptq->last_insn_cnt = ptq->state->tot_insn_cnt; |
1146 | 1154 | ||
@@ -1203,6 +1211,7 @@ static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) | |||
1203 | sample.cpu = ptq->cpu; | 1211 | sample.cpu = ptq->cpu; |
1204 | sample.flags = ptq->flags; | 1212 | sample.flags = ptq->flags; |
1205 | sample.insn_len = ptq->insn_len; | 1213 | sample.insn_len = ptq->insn_len; |
1214 | memcpy(sample.insn, ptq->insn, INTEL_PT_INSN_BUF_SZ); | ||
1206 | 1215 | ||
1207 | if (pt->synth_opts.callchain) { | 1216 | if (pt->synth_opts.callchain) { |
1208 | thread_stack__sample(ptq->thread, ptq->chain, | 1217 | thread_stack__sample(ptq->thread, ptq->chain, |
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 95f0884aae02..c9a941ef0f6d 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c | |||
@@ -37,6 +37,10 @@ struct jit_buf_desc { | |||
37 | bool needs_bswap; /* handles cross-endianess */ | 37 | bool needs_bswap; /* handles cross-endianess */ |
38 | bool use_arch_timestamp; | 38 | bool use_arch_timestamp; |
39 | void *debug_data; | 39 | void *debug_data; |
40 | void *unwinding_data; | ||
41 | uint64_t unwinding_size; | ||
42 | uint64_t unwinding_mapped_size; | ||
43 | uint64_t eh_frame_hdr_size; | ||
40 | size_t nr_debug_entries; | 44 | size_t nr_debug_entries; |
41 | uint32_t code_load_count; | 45 | uint32_t code_load_count; |
42 | u64 bytes_written; | 46 | u64 bytes_written; |
@@ -68,7 +72,10 @@ jit_emit_elf(char *filename, | |||
68 | const void *code, | 72 | const void *code, |
69 | int csize, | 73 | int csize, |
70 | void *debug, | 74 | void *debug, |
71 | int nr_debug_entries) | 75 | int nr_debug_entries, |
76 | void *unwinding, | ||
77 | uint32_t unwinding_header_size, | ||
78 | uint32_t unwinding_size) | ||
72 | { | 79 | { |
73 | int ret, fd; | 80 | int ret, fd; |
74 | 81 | ||
@@ -81,7 +88,8 @@ jit_emit_elf(char *filename, | |||
81 | return -1; | 88 | return -1; |
82 | } | 89 | } |
83 | 90 | ||
84 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries); | 91 | ret = jit_write_elf(fd, code_addr, sym, (const void *)code, csize, debug, nr_debug_entries, |
92 | unwinding, unwinding_header_size, unwinding_size); | ||
85 | 93 | ||
86 | close(fd); | 94 | close(fd); |
87 | 95 | ||
@@ -172,6 +180,12 @@ jit_open(struct jit_buf_desc *jd, const char *name) | |||
172 | header.elf_mach, | 180 | header.elf_mach, |
173 | jd->use_arch_timestamp); | 181 | jd->use_arch_timestamp); |
174 | 182 | ||
183 | if (header.version > JITHEADER_VERSION) { | ||
184 | pr_err("wrong jitdump version %u, expected " STR(JITHEADER_VERSION), | ||
185 | header.version); | ||
186 | goto error; | ||
187 | } | ||
188 | |||
175 | if (header.flags & JITDUMP_FLAGS_RESERVED) { | 189 | if (header.flags & JITDUMP_FLAGS_RESERVED) { |
176 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", | 190 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", |
177 | (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); | 191 | (unsigned long long)header.flags & JITDUMP_FLAGS_RESERVED); |
@@ -263,8 +277,7 @@ jit_get_next_entry(struct jit_buf_desc *jd) | |||
263 | return NULL; | 277 | return NULL; |
264 | 278 | ||
265 | if (id >= JIT_CODE_MAX) { | 279 | if (id >= JIT_CODE_MAX) { |
266 | pr_warning("next_entry: unknown prefix %d, skipping\n", id); | 280 | pr_warning("next_entry: unknown record type %d, skipping\n", id); |
267 | return NULL; | ||
268 | } | 281 | } |
269 | if (bs > jd->bufsize) { | 282 | if (bs > jd->bufsize) { |
270 | void *n; | 283 | void *n; |
@@ -296,6 +309,13 @@ jit_get_next_entry(struct jit_buf_desc *jd) | |||
296 | } | 309 | } |
297 | } | 310 | } |
298 | break; | 311 | break; |
312 | case JIT_CODE_UNWINDING_INFO: | ||
313 | if (jd->needs_bswap) { | ||
314 | jr->unwinding.unwinding_size = bswap_64(jr->unwinding.unwinding_size); | ||
315 | jr->unwinding.eh_frame_hdr_size = bswap_64(jr->unwinding.eh_frame_hdr_size); | ||
316 | jr->unwinding.mapped_size = bswap_64(jr->unwinding.mapped_size); | ||
317 | } | ||
318 | break; | ||
299 | case JIT_CODE_CLOSE: | 319 | case JIT_CODE_CLOSE: |
300 | break; | 320 | break; |
301 | case JIT_CODE_LOAD: | 321 | case JIT_CODE_LOAD: |
@@ -322,7 +342,8 @@ jit_get_next_entry(struct jit_buf_desc *jd) | |||
322 | break; | 342 | break; |
323 | case JIT_CODE_MAX: | 343 | case JIT_CODE_MAX: |
324 | default: | 344 | default: |
325 | return NULL; | 345 | /* skip unknown record (we have read them) */ |
346 | break; | ||
326 | } | 347 | } |
327 | return jr; | 348 | return jr; |
328 | } | 349 | } |
@@ -370,7 +391,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
370 | u16 idr_size; | 391 | u16 idr_size; |
371 | const char *sym; | 392 | const char *sym; |
372 | uint32_t count; | 393 | uint32_t count; |
373 | int ret, csize; | 394 | int ret, csize, usize; |
374 | pid_t pid, tid; | 395 | pid_t pid, tid; |
375 | struct { | 396 | struct { |
376 | u32 pid, tid; | 397 | u32 pid, tid; |
@@ -380,6 +401,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
380 | pid = jr->load.pid; | 401 | pid = jr->load.pid; |
381 | tid = jr->load.tid; | 402 | tid = jr->load.tid; |
382 | csize = jr->load.code_size; | 403 | csize = jr->load.code_size; |
404 | usize = jd->unwinding_mapped_size; | ||
383 | addr = jr->load.code_addr; | 405 | addr = jr->load.code_addr; |
384 | sym = (void *)((unsigned long)jr + sizeof(jr->load)); | 406 | sym = (void *)((unsigned long)jr + sizeof(jr->load)); |
385 | code = (unsigned long)jr + jr->load.p.total_size - csize; | 407 | code = (unsigned long)jr + jr->load.p.total_size - csize; |
@@ -400,7 +422,8 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
400 | 422 | ||
401 | size = PERF_ALIGN(size, sizeof(u64)); | 423 | size = PERF_ALIGN(size, sizeof(u64)); |
402 | uaddr = (uintptr_t)code; | 424 | uaddr = (uintptr_t)code; |
403 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries); | 425 | ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries, |
426 | jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size); | ||
404 | 427 | ||
405 | if (jd->debug_data && jd->nr_debug_entries) { | 428 | if (jd->debug_data && jd->nr_debug_entries) { |
406 | free(jd->debug_data); | 429 | free(jd->debug_data); |
@@ -408,6 +431,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
408 | jd->nr_debug_entries = 0; | 431 | jd->nr_debug_entries = 0; |
409 | } | 432 | } |
410 | 433 | ||
434 | if (jd->unwinding_data && jd->eh_frame_hdr_size) { | ||
435 | free(jd->unwinding_data); | ||
436 | jd->unwinding_data = NULL; | ||
437 | jd->eh_frame_hdr_size = 0; | ||
438 | jd->unwinding_mapped_size = 0; | ||
439 | jd->unwinding_size = 0; | ||
440 | } | ||
441 | |||
411 | if (ret) { | 442 | if (ret) { |
412 | free(event); | 443 | free(event); |
413 | return -1; | 444 | return -1; |
@@ -422,7 +453,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
422 | 453 | ||
423 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | 454 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; |
424 | event->mmap2.start = addr; | 455 | event->mmap2.start = addr; |
425 | event->mmap2.len = csize; | 456 | event->mmap2.len = usize ? ALIGN_8(csize) + usize : csize; |
426 | event->mmap2.pid = pid; | 457 | event->mmap2.pid = pid; |
427 | event->mmap2.tid = tid; | 458 | event->mmap2.tid = tid; |
428 | event->mmap2.ino = st.st_ino; | 459 | event->mmap2.ino = st.st_ino; |
@@ -473,6 +504,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | |||
473 | char *filename; | 504 | char *filename; |
474 | size_t size; | 505 | size_t size; |
475 | struct stat st; | 506 | struct stat st; |
507 | int usize; | ||
476 | u16 idr_size; | 508 | u16 idr_size; |
477 | int ret; | 509 | int ret; |
478 | pid_t pid, tid; | 510 | pid_t pid, tid; |
@@ -483,6 +515,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | |||
483 | 515 | ||
484 | pid = jr->move.pid; | 516 | pid = jr->move.pid; |
485 | tid = jr->move.tid; | 517 | tid = jr->move.tid; |
518 | usize = jd->unwinding_mapped_size; | ||
486 | idr_size = jd->machine->id_hdr_size; | 519 | idr_size = jd->machine->id_hdr_size; |
487 | 520 | ||
488 | /* | 521 | /* |
@@ -511,7 +544,8 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | |||
511 | (sizeof(event->mmap2.filename) - size) + idr_size); | 544 | (sizeof(event->mmap2.filename) - size) + idr_size); |
512 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; | 545 | event->mmap2.pgoff = GEN_ELF_TEXT_OFFSET; |
513 | event->mmap2.start = jr->move.new_code_addr; | 546 | event->mmap2.start = jr->move.new_code_addr; |
514 | event->mmap2.len = jr->move.code_size; | 547 | event->mmap2.len = usize ? ALIGN_8(jr->move.code_size) + usize |
548 | : jr->move.code_size; | ||
515 | event->mmap2.pid = pid; | 549 | event->mmap2.pid = pid; |
516 | event->mmap2.tid = tid; | 550 | event->mmap2.tid = tid; |
517 | event->mmap2.ino = st.st_ino; | 551 | event->mmap2.ino = st.st_ino; |
@@ -578,10 +612,35 @@ static int jit_repipe_debug_info(struct jit_buf_desc *jd, union jr_entry *jr) | |||
578 | } | 612 | } |
579 | 613 | ||
580 | static int | 614 | static int |
615 | jit_repipe_unwinding_info(struct jit_buf_desc *jd, union jr_entry *jr) | ||
616 | { | ||
617 | void *unwinding_data; | ||
618 | uint32_t unwinding_data_size; | ||
619 | |||
620 | if (!(jd && jr)) | ||
621 | return -1; | ||
622 | |||
623 | unwinding_data_size = jr->prefix.total_size - sizeof(jr->unwinding); | ||
624 | unwinding_data = malloc(unwinding_data_size); | ||
625 | if (!unwinding_data) | ||
626 | return -1; | ||
627 | |||
628 | memcpy(unwinding_data, &jr->unwinding.unwinding_data, | ||
629 | unwinding_data_size); | ||
630 | |||
631 | jd->eh_frame_hdr_size = jr->unwinding.eh_frame_hdr_size; | ||
632 | jd->unwinding_size = jr->unwinding.unwinding_size; | ||
633 | jd->unwinding_mapped_size = jr->unwinding.mapped_size; | ||
634 | jd->unwinding_data = unwinding_data; | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int | ||
581 | jit_process_dump(struct jit_buf_desc *jd) | 640 | jit_process_dump(struct jit_buf_desc *jd) |
582 | { | 641 | { |
583 | union jr_entry *jr; | 642 | union jr_entry *jr; |
584 | int ret; | 643 | int ret = 0; |
585 | 644 | ||
586 | while ((jr = jit_get_next_entry(jd))) { | 645 | while ((jr = jit_get_next_entry(jd))) { |
587 | switch(jr->prefix.id) { | 646 | switch(jr->prefix.id) { |
@@ -594,6 +653,9 @@ jit_process_dump(struct jit_buf_desc *jd) | |||
594 | case JIT_CODE_DEBUG_INFO: | 653 | case JIT_CODE_DEBUG_INFO: |
595 | ret = jit_repipe_debug_info(jd, jr); | 654 | ret = jit_repipe_debug_info(jd, jr); |
596 | break; | 655 | break; |
656 | case JIT_CODE_UNWINDING_INFO: | ||
657 | ret = jit_repipe_unwinding_info(jd, jr); | ||
658 | break; | ||
597 | default: | 659 | default: |
598 | ret = 0; | 660 | ret = 0; |
599 | continue; | 661 | continue; |
diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index bcacd20d0c1c..c6b9b67f43bf 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h | |||
@@ -19,6 +19,7 @@ | |||
19 | #define JITHEADER_MAGIC_SW 0x4454694A | 19 | #define JITHEADER_MAGIC_SW 0x4454694A |
20 | 20 | ||
21 | #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) | 21 | #define PADDING_8ALIGNED(x) ((((x) + 7) & 7) ^ 7) |
22 | #define ALIGN_8(x) (((x) + 7) & (~7)) | ||
22 | 23 | ||
23 | #define JITHEADER_VERSION 1 | 24 | #define JITHEADER_VERSION 1 |
24 | 25 | ||
@@ -48,6 +49,7 @@ enum jit_record_type { | |||
48 | JIT_CODE_MOVE = 1, | 49 | JIT_CODE_MOVE = 1, |
49 | JIT_CODE_DEBUG_INFO = 2, | 50 | JIT_CODE_DEBUG_INFO = 2, |
50 | JIT_CODE_CLOSE = 3, | 51 | JIT_CODE_CLOSE = 3, |
52 | JIT_CODE_UNWINDING_INFO = 4, | ||
51 | 53 | ||
52 | JIT_CODE_MAX, | 54 | JIT_CODE_MAX, |
53 | }; | 55 | }; |
@@ -101,12 +103,22 @@ struct jr_code_debug_info { | |||
101 | struct debug_entry entries[0]; | 103 | struct debug_entry entries[0]; |
102 | }; | 104 | }; |
103 | 105 | ||
106 | struct jr_code_unwinding_info { | ||
107 | struct jr_prefix p; | ||
108 | |||
109 | uint64_t unwinding_size; | ||
110 | uint64_t eh_frame_hdr_size; | ||
111 | uint64_t mapped_size; | ||
112 | const char unwinding_data[0]; | ||
113 | }; | ||
114 | |||
104 | union jr_entry { | 115 | union jr_entry { |
105 | struct jr_code_debug_info info; | 116 | struct jr_code_debug_info info; |
106 | struct jr_code_close close; | 117 | struct jr_code_close close; |
107 | struct jr_code_load load; | 118 | struct jr_code_load load; |
108 | struct jr_code_move move; | 119 | struct jr_code_move move; |
109 | struct jr_prefix prefix; | 120 | struct jr_prefix prefix; |
121 | struct jr_code_unwinding_info unwinding; | ||
110 | }; | 122 | }; |
111 | 123 | ||
112 | static inline struct debug_entry * | 124 | static inline struct debug_entry * |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index bf7216b8731d..b23ff44cf214 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <limits.h> | 7 | #include <limits.h> |
8 | #include <stdio.h> | 8 | #include <stdio.h> |
9 | #include <stdlib.h> | 9 | #include <stdlib.h> |
10 | #include <linux/err.h> | ||
10 | #include "debug.h" | 11 | #include "debug.h" |
11 | #include "llvm-utils.h" | 12 | #include "llvm-utils.h" |
12 | #include "config.h" | 13 | #include "config.h" |
@@ -282,9 +283,10 @@ static const char *kinc_fetch_script = | |||
282 | "rm -rf $TMPDIR\n" | 283 | "rm -rf $TMPDIR\n" |
283 | "exit $RET\n"; | 284 | "exit $RET\n"; |
284 | 285 | ||
285 | static inline void | 286 | void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) |
286 | get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | ||
287 | { | 287 | { |
288 | static char *saved_kbuild_dir; | ||
289 | static char *saved_kbuild_include_opts; | ||
288 | int err; | 290 | int err; |
289 | 291 | ||
290 | if (!kbuild_dir || !kbuild_include_opts) | 292 | if (!kbuild_dir || !kbuild_include_opts) |
@@ -293,10 +295,28 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | |||
293 | *kbuild_dir = NULL; | 295 | *kbuild_dir = NULL; |
294 | *kbuild_include_opts = NULL; | 296 | *kbuild_include_opts = NULL; |
295 | 297 | ||
298 | if (saved_kbuild_dir && saved_kbuild_include_opts && | ||
299 | !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) { | ||
300 | *kbuild_dir = strdup(saved_kbuild_dir); | ||
301 | *kbuild_include_opts = strdup(saved_kbuild_include_opts); | ||
302 | |||
303 | if (*kbuild_dir && *kbuild_include_opts) | ||
304 | return; | ||
305 | |||
306 | zfree(kbuild_dir); | ||
307 | zfree(kbuild_include_opts); | ||
308 | /* | ||
309 | * Don't fall through: it may breaks saved_kbuild_dir and | ||
310 | * saved_kbuild_include_opts if detect them again when | ||
311 | * memory is low. | ||
312 | */ | ||
313 | return; | ||
314 | } | ||
315 | |||
296 | if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { | 316 | if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { |
297 | pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); | 317 | pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); |
298 | pr_debug("Skip kbuild options detection.\n"); | 318 | pr_debug("Skip kbuild options detection.\n"); |
299 | return; | 319 | goto errout; |
300 | } | 320 | } |
301 | 321 | ||
302 | err = detect_kbuild_dir(kbuild_dir); | 322 | err = detect_kbuild_dir(kbuild_dir); |
@@ -306,7 +326,7 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | |||
306 | "Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" | 326 | "Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" |
307 | " \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" | 327 | " \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" |
308 | " \tdetection.\n\n"); | 328 | " \tdetection.\n\n"); |
309 | return; | 329 | goto errout; |
310 | } | 330 | } |
311 | 331 | ||
312 | pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); | 332 | pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); |
@@ -325,21 +345,50 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | |||
325 | 345 | ||
326 | free(*kbuild_dir); | 346 | free(*kbuild_dir); |
327 | *kbuild_dir = NULL; | 347 | *kbuild_dir = NULL; |
328 | return; | 348 | goto errout; |
329 | } | 349 | } |
330 | 350 | ||
331 | pr_debug("include option is set to %s\n", *kbuild_include_opts); | 351 | pr_debug("include option is set to %s\n", *kbuild_include_opts); |
352 | |||
353 | saved_kbuild_dir = strdup(*kbuild_dir); | ||
354 | saved_kbuild_include_opts = strdup(*kbuild_include_opts); | ||
355 | |||
356 | if (!saved_kbuild_dir || !saved_kbuild_include_opts) { | ||
357 | zfree(&saved_kbuild_dir); | ||
358 | zfree(&saved_kbuild_include_opts); | ||
359 | } | ||
360 | return; | ||
361 | errout: | ||
362 | saved_kbuild_dir = ERR_PTR(-EINVAL); | ||
363 | saved_kbuild_include_opts = ERR_PTR(-EINVAL); | ||
332 | } | 364 | } |
333 | 365 | ||
334 | static void | 366 | int llvm__get_nr_cpus(void) |
335 | dump_obj(const char *path, void *obj_buf, size_t size) | 367 | { |
368 | static int nr_cpus_avail = 0; | ||
369 | char serr[STRERR_BUFSIZE]; | ||
370 | |||
371 | if (nr_cpus_avail > 0) | ||
372 | return nr_cpus_avail; | ||
373 | |||
374 | nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); | ||
375 | if (nr_cpus_avail <= 0) { | ||
376 | pr_err( | ||
377 | "WARNING:\tunable to get available CPUs in this system: %s\n" | ||
378 | " \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); | ||
379 | nr_cpus_avail = 128; | ||
380 | } | ||
381 | return nr_cpus_avail; | ||
382 | } | ||
383 | |||
384 | void llvm__dump_obj(const char *path, void *obj_buf, size_t size) | ||
336 | { | 385 | { |
337 | char *obj_path = strdup(path); | 386 | char *obj_path = strdup(path); |
338 | FILE *fp; | 387 | FILE *fp; |
339 | char *p; | 388 | char *p; |
340 | 389 | ||
341 | if (!obj_path) { | 390 | if (!obj_path) { |
342 | pr_warning("WARNING: No enough memory, skip object dumping\n"); | 391 | pr_warning("WARNING: Not enough memory, skip object dumping\n"); |
343 | return; | 392 | return; |
344 | } | 393 | } |
345 | 394 | ||
@@ -406,15 +455,9 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
406 | * This is an optional work. Even it fail we can continue our | 455 | * This is an optional work. Even it fail we can continue our |
407 | * work. Needn't to check error return. | 456 | * work. Needn't to check error return. |
408 | */ | 457 | */ |
409 | get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); | 458 | llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); |
410 | 459 | ||
411 | nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); | 460 | nr_cpus_avail = llvm__get_nr_cpus(); |
412 | if (nr_cpus_avail <= 0) { | ||
413 | pr_err( | ||
414 | "WARNING:\tunable to get available CPUs in this system: %s\n" | ||
415 | " \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); | ||
416 | nr_cpus_avail = 128; | ||
417 | } | ||
418 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", | 461 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", |
419 | nr_cpus_avail); | 462 | nr_cpus_avail); |
420 | 463 | ||
@@ -453,9 +496,6 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
453 | free(kbuild_dir); | 496 | free(kbuild_dir); |
454 | free(kbuild_include_opts); | 497 | free(kbuild_include_opts); |
455 | 498 | ||
456 | if (llvm_param.dump_obj) | ||
457 | dump_obj(path, obj_buf, obj_buf_sz); | ||
458 | |||
459 | if (!p_obj_buf) | 499 | if (!p_obj_buf) |
460 | free(obj_buf); | 500 | free(obj_buf); |
461 | else | 501 | else |
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index 9f501cef06a1..c87a2a92a88f 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h | |||
@@ -50,4 +50,10 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz); | |||
50 | 50 | ||
51 | /* This function is for test__llvm() use only */ | 51 | /* This function is for test__llvm() use only */ |
52 | int llvm__search_clang(void); | 52 | int llvm__search_clang(void); |
53 | |||
54 | /* Following functions are reused by builtin clang support */ | ||
55 | void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts); | ||
56 | int llvm__get_nr_cpus(void); | ||
57 | |||
58 | void llvm__dump_obj(const char *path, void *obj_buf, size_t size); | ||
53 | #endif | 59 | #endif |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index df85b9efd80f..9b33bef54581 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1616,7 +1616,11 @@ static int add_callchain_ip(struct thread *thread, | |||
1616 | struct symbol **parent, | 1616 | struct symbol **parent, |
1617 | struct addr_location *root_al, | 1617 | struct addr_location *root_al, |
1618 | u8 *cpumode, | 1618 | u8 *cpumode, |
1619 | u64 ip) | 1619 | u64 ip, |
1620 | bool branch, | ||
1621 | struct branch_flags *flags, | ||
1622 | int nr_loop_iter, | ||
1623 | int samples) | ||
1620 | { | 1624 | { |
1621 | struct addr_location al; | 1625 | struct addr_location al; |
1622 | 1626 | ||
@@ -1668,7 +1672,8 @@ static int add_callchain_ip(struct thread *thread, | |||
1668 | 1672 | ||
1669 | if (symbol_conf.hide_unresolved && al.sym == NULL) | 1673 | if (symbol_conf.hide_unresolved && al.sym == NULL) |
1670 | return 0; | 1674 | return 0; |
1671 | return callchain_cursor_append(cursor, al.addr, al.map, al.sym); | 1675 | return callchain_cursor_append(cursor, al.addr, al.map, al.sym, |
1676 | branch, flags, nr_loop_iter, samples); | ||
1672 | } | 1677 | } |
1673 | 1678 | ||
1674 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | 1679 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, |
@@ -1757,7 +1762,9 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1757 | /* LBR only affects the user callchain */ | 1762 | /* LBR only affects the user callchain */ |
1758 | if (i != chain_nr) { | 1763 | if (i != chain_nr) { |
1759 | struct branch_stack *lbr_stack = sample->branch_stack; | 1764 | struct branch_stack *lbr_stack = sample->branch_stack; |
1760 | int lbr_nr = lbr_stack->nr, j; | 1765 | int lbr_nr = lbr_stack->nr, j, k; |
1766 | bool branch; | ||
1767 | struct branch_flags *flags; | ||
1761 | /* | 1768 | /* |
1762 | * LBR callstack can only get user call chain. | 1769 | * LBR callstack can only get user call chain. |
1763 | * The mix_chain_nr is kernel call chain | 1770 | * The mix_chain_nr is kernel call chain |
@@ -1772,23 +1779,41 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1772 | 1779 | ||
1773 | for (j = 0; j < mix_chain_nr; j++) { | 1780 | for (j = 0; j < mix_chain_nr; j++) { |
1774 | int err; | 1781 | int err; |
1782 | branch = false; | ||
1783 | flags = NULL; | ||
1784 | |||
1775 | if (callchain_param.order == ORDER_CALLEE) { | 1785 | if (callchain_param.order == ORDER_CALLEE) { |
1776 | if (j < i + 1) | 1786 | if (j < i + 1) |
1777 | ip = chain->ips[j]; | 1787 | ip = chain->ips[j]; |
1778 | else if (j > i + 1) | 1788 | else if (j > i + 1) { |
1779 | ip = lbr_stack->entries[j - i - 2].from; | 1789 | k = j - i - 2; |
1780 | else | 1790 | ip = lbr_stack->entries[k].from; |
1791 | branch = true; | ||
1792 | flags = &lbr_stack->entries[k].flags; | ||
1793 | } else { | ||
1781 | ip = lbr_stack->entries[0].to; | 1794 | ip = lbr_stack->entries[0].to; |
1795 | branch = true; | ||
1796 | flags = &lbr_stack->entries[0].flags; | ||
1797 | } | ||
1782 | } else { | 1798 | } else { |
1783 | if (j < lbr_nr) | 1799 | if (j < lbr_nr) { |
1784 | ip = lbr_stack->entries[lbr_nr - j - 1].from; | 1800 | k = lbr_nr - j - 1; |
1801 | ip = lbr_stack->entries[k].from; | ||
1802 | branch = true; | ||
1803 | flags = &lbr_stack->entries[k].flags; | ||
1804 | } | ||
1785 | else if (j > lbr_nr) | 1805 | else if (j > lbr_nr) |
1786 | ip = chain->ips[i + 1 - (j - lbr_nr)]; | 1806 | ip = chain->ips[i + 1 - (j - lbr_nr)]; |
1787 | else | 1807 | else { |
1788 | ip = lbr_stack->entries[0].to; | 1808 | ip = lbr_stack->entries[0].to; |
1809 | branch = true; | ||
1810 | flags = &lbr_stack->entries[0].flags; | ||
1811 | } | ||
1789 | } | 1812 | } |
1790 | 1813 | ||
1791 | err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); | 1814 | err = add_callchain_ip(thread, cursor, parent, |
1815 | root_al, &cpumode, ip, | ||
1816 | branch, flags, 0, 0); | ||
1792 | if (err) | 1817 | if (err) |
1793 | return (err < 0) ? err : 0; | 1818 | return (err < 0) ? err : 0; |
1794 | } | 1819 | } |
@@ -1813,6 +1838,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, | |||
1813 | int i, j, err, nr_entries; | 1838 | int i, j, err, nr_entries; |
1814 | int skip_idx = -1; | 1839 | int skip_idx = -1; |
1815 | int first_call = 0; | 1840 | int first_call = 0; |
1841 | int nr_loop_iter; | ||
1816 | 1842 | ||
1817 | if (perf_evsel__has_branch_callstack(evsel)) { | 1843 | if (perf_evsel__has_branch_callstack(evsel)) { |
1818 | err = resolve_lbr_callchain_sample(thread, cursor, sample, parent, | 1844 | err = resolve_lbr_callchain_sample(thread, cursor, sample, parent, |
@@ -1868,14 +1894,37 @@ static int thread__resolve_callchain_sample(struct thread *thread, | |||
1868 | be[i] = branch->entries[branch->nr - i - 1]; | 1894 | be[i] = branch->entries[branch->nr - i - 1]; |
1869 | } | 1895 | } |
1870 | 1896 | ||
1897 | nr_loop_iter = nr; | ||
1871 | nr = remove_loops(be, nr); | 1898 | nr = remove_loops(be, nr); |
1872 | 1899 | ||
1900 | /* | ||
1901 | * Get the number of iterations. | ||
1902 | * It's only approximation, but good enough in practice. | ||
1903 | */ | ||
1904 | if (nr_loop_iter > nr) | ||
1905 | nr_loop_iter = nr_loop_iter - nr + 1; | ||
1906 | else | ||
1907 | nr_loop_iter = 0; | ||
1908 | |||
1873 | for (i = 0; i < nr; i++) { | 1909 | for (i = 0; i < nr; i++) { |
1874 | err = add_callchain_ip(thread, cursor, parent, root_al, | 1910 | if (i == nr - 1) |
1875 | NULL, be[i].to); | 1911 | err = add_callchain_ip(thread, cursor, parent, |
1912 | root_al, | ||
1913 | NULL, be[i].to, | ||
1914 | true, &be[i].flags, | ||
1915 | nr_loop_iter, 1); | ||
1916 | else | ||
1917 | err = add_callchain_ip(thread, cursor, parent, | ||
1918 | root_al, | ||
1919 | NULL, be[i].to, | ||
1920 | true, &be[i].flags, | ||
1921 | 0, 0); | ||
1922 | |||
1876 | if (!err) | 1923 | if (!err) |
1877 | err = add_callchain_ip(thread, cursor, parent, root_al, | 1924 | err = add_callchain_ip(thread, cursor, parent, root_al, |
1878 | NULL, be[i].from); | 1925 | NULL, be[i].from, |
1926 | true, &be[i].flags, | ||
1927 | 0, 0); | ||
1879 | if (err == -EINVAL) | 1928 | if (err == -EINVAL) |
1880 | break; | 1929 | break; |
1881 | if (err) | 1930 | if (err) |
@@ -1903,7 +1952,9 @@ check_calls: | |||
1903 | if (ip < PERF_CONTEXT_MAX) | 1952 | if (ip < PERF_CONTEXT_MAX) |
1904 | ++nr_entries; | 1953 | ++nr_entries; |
1905 | 1954 | ||
1906 | err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip); | 1955 | err = add_callchain_ip(thread, cursor, parent, |
1956 | root_al, &cpumode, ip, | ||
1957 | false, NULL, 0, 0); | ||
1907 | 1958 | ||
1908 | if (err) | 1959 | if (err) |
1909 | return (err < 0) ? err : 0; | 1960 | return (err < 0) ? err : 0; |
@@ -1919,7 +1970,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
1919 | if (symbol_conf.hide_unresolved && entry->sym == NULL) | 1970 | if (symbol_conf.hide_unresolved && entry->sym == NULL) |
1920 | return 0; | 1971 | return 0; |
1921 | return callchain_cursor_append(cursor, entry->ip, | 1972 | return callchain_cursor_append(cursor, entry->ip, |
1922 | entry->map, entry->sym); | 1973 | entry->map, entry->sym, |
1974 | false, NULL, 0, 0); | ||
1923 | } | 1975 | } |
1924 | 1976 | ||
1925 | static int thread__resolve_callchain_unwind(struct thread *thread, | 1977 | static int thread__resolve_callchain_unwind(struct thread *thread, |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c662fef95d14..4f9a71c63026 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -682,9 +682,16 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
682 | continue; | 682 | continue; |
683 | 683 | ||
684 | if (verbose >= 2) { | 684 | if (verbose >= 2) { |
685 | fputs("overlapping maps:\n", fp); | 685 | |
686 | map__fprintf(map, fp); | 686 | if (use_browser) { |
687 | map__fprintf(pos, fp); | 687 | pr_warning("overlapping maps in %s " |
688 | "(disable tui for more info)\n", | ||
689 | map->dso->name); | ||
690 | } else { | ||
691 | fputs("overlapping maps:\n", fp); | ||
692 | map__fprintf(map, fp); | ||
693 | map__fprintf(pos, fp); | ||
694 | } | ||
688 | } | 695 | } |
689 | 696 | ||
690 | rb_erase_init(&pos->rb_node, root); | 697 | rb_erase_init(&pos->rb_node, root); |
@@ -702,7 +709,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
702 | 709 | ||
703 | before->end = map->start; | 710 | before->end = map->start; |
704 | __map_groups__insert(pos->groups, before); | 711 | __map_groups__insert(pos->groups, before); |
705 | if (verbose >= 2) | 712 | if (verbose >= 2 && !use_browser) |
706 | map__fprintf(before, fp); | 713 | map__fprintf(before, fp); |
707 | map__put(before); | 714 | map__put(before); |
708 | } | 715 | } |
@@ -717,7 +724,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
717 | 724 | ||
718 | after->start = map->end; | 725 | after->start = map->end; |
719 | __map_groups__insert(pos->groups, after); | 726 | __map_groups__insert(pos->groups, after); |
720 | if (verbose >= 2) | 727 | if (verbose >= 2 && !use_browser) |
721 | map__fprintf(after, fp); | 728 | map__fprintf(after, fp); |
722 | map__put(after); | 729 | map__put(after); |
723 | } | 730 | } |
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index bbc368e7d1e4..1d4ab53c60ca 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "mem-events.h" | 9 | #include "mem-events.h" |
10 | #include "debug.h" | 10 | #include "debug.h" |
11 | #include "symbol.h" | 11 | #include "symbol.h" |
12 | #include "sort.h" | ||
12 | 13 | ||
13 | unsigned int perf_mem_events__loads_ldlat = 30; | 14 | unsigned int perf_mem_events__loads_ldlat = 30; |
14 | 15 | ||
@@ -268,3 +269,138 @@ int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_in | |||
268 | 269 | ||
269 | return i; | 270 | return i; |
270 | } | 271 | } |
272 | |||
273 | int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi) | ||
274 | { | ||
275 | union perf_mem_data_src *data_src = &mi->data_src; | ||
276 | u64 daddr = mi->daddr.addr; | ||
277 | u64 op = data_src->mem_op; | ||
278 | u64 lvl = data_src->mem_lvl; | ||
279 | u64 snoop = data_src->mem_snoop; | ||
280 | u64 lock = data_src->mem_lock; | ||
281 | int err = 0; | ||
282 | |||
283 | #define HITM_INC(__f) \ | ||
284 | do { \ | ||
285 | stats->__f++; \ | ||
286 | stats->tot_hitm++; \ | ||
287 | } while (0) | ||
288 | |||
289 | #define P(a, b) PERF_MEM_##a##_##b | ||
290 | |||
291 | stats->nr_entries++; | ||
292 | |||
293 | if (lock & P(LOCK, LOCKED)) stats->locks++; | ||
294 | |||
295 | if (op & P(OP, LOAD)) { | ||
296 | /* load */ | ||
297 | stats->load++; | ||
298 | |||
299 | if (!daddr) { | ||
300 | stats->ld_noadrs++; | ||
301 | return -1; | ||
302 | } | ||
303 | |||
304 | if (lvl & P(LVL, HIT)) { | ||
305 | if (lvl & P(LVL, UNC)) stats->ld_uncache++; | ||
306 | if (lvl & P(LVL, IO)) stats->ld_io++; | ||
307 | if (lvl & P(LVL, LFB)) stats->ld_fbhit++; | ||
308 | if (lvl & P(LVL, L1 )) stats->ld_l1hit++; | ||
309 | if (lvl & P(LVL, L2 )) stats->ld_l2hit++; | ||
310 | if (lvl & P(LVL, L3 )) { | ||
311 | if (snoop & P(SNOOP, HITM)) | ||
312 | HITM_INC(lcl_hitm); | ||
313 | else | ||
314 | stats->ld_llchit++; | ||
315 | } | ||
316 | |||
317 | if (lvl & P(LVL, LOC_RAM)) { | ||
318 | stats->lcl_dram++; | ||
319 | if (snoop & P(SNOOP, HIT)) | ||
320 | stats->ld_shared++; | ||
321 | else | ||
322 | stats->ld_excl++; | ||
323 | } | ||
324 | |||
325 | if ((lvl & P(LVL, REM_RAM1)) || | ||
326 | (lvl & P(LVL, REM_RAM2))) { | ||
327 | stats->rmt_dram++; | ||
328 | if (snoop & P(SNOOP, HIT)) | ||
329 | stats->ld_shared++; | ||
330 | else | ||
331 | stats->ld_excl++; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | if ((lvl & P(LVL, REM_CCE1)) || | ||
336 | (lvl & P(LVL, REM_CCE2))) { | ||
337 | if (snoop & P(SNOOP, HIT)) | ||
338 | stats->rmt_hit++; | ||
339 | else if (snoop & P(SNOOP, HITM)) | ||
340 | HITM_INC(rmt_hitm); | ||
341 | } | ||
342 | |||
343 | if ((lvl & P(LVL, MISS))) | ||
344 | stats->ld_miss++; | ||
345 | |||
346 | } else if (op & P(OP, STORE)) { | ||
347 | /* store */ | ||
348 | stats->store++; | ||
349 | |||
350 | if (!daddr) { | ||
351 | stats->st_noadrs++; | ||
352 | return -1; | ||
353 | } | ||
354 | |||
355 | if (lvl & P(LVL, HIT)) { | ||
356 | if (lvl & P(LVL, UNC)) stats->st_uncache++; | ||
357 | if (lvl & P(LVL, L1 )) stats->st_l1hit++; | ||
358 | } | ||
359 | if (lvl & P(LVL, MISS)) | ||
360 | if (lvl & P(LVL, L1)) stats->st_l1miss++; | ||
361 | } else { | ||
362 | /* unparsable data_src? */ | ||
363 | stats->noparse++; | ||
364 | return -1; | ||
365 | } | ||
366 | |||
367 | if (!mi->daddr.map || !mi->iaddr.map) { | ||
368 | stats->nomap++; | ||
369 | return -1; | ||
370 | } | ||
371 | |||
372 | #undef P | ||
373 | #undef HITM_INC | ||
374 | return err; | ||
375 | } | ||
376 | |||
377 | void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add) | ||
378 | { | ||
379 | stats->nr_entries += add->nr_entries; | ||
380 | |||
381 | stats->locks += add->locks; | ||
382 | stats->store += add->store; | ||
383 | stats->st_uncache += add->st_uncache; | ||
384 | stats->st_noadrs += add->st_noadrs; | ||
385 | stats->st_l1hit += add->st_l1hit; | ||
386 | stats->st_l1miss += add->st_l1miss; | ||
387 | stats->load += add->load; | ||
388 | stats->ld_excl += add->ld_excl; | ||
389 | stats->ld_shared += add->ld_shared; | ||
390 | stats->ld_uncache += add->ld_uncache; | ||
391 | stats->ld_io += add->ld_io; | ||
392 | stats->ld_miss += add->ld_miss; | ||
393 | stats->ld_noadrs += add->ld_noadrs; | ||
394 | stats->ld_fbhit += add->ld_fbhit; | ||
395 | stats->ld_l1hit += add->ld_l1hit; | ||
396 | stats->ld_l2hit += add->ld_l2hit; | ||
397 | stats->ld_llchit += add->ld_llchit; | ||
398 | stats->lcl_hitm += add->lcl_hitm; | ||
399 | stats->rmt_hitm += add->rmt_hitm; | ||
400 | stats->tot_hitm += add->tot_hitm; | ||
401 | stats->rmt_hit += add->rmt_hit; | ||
402 | stats->lcl_dram += add->lcl_dram; | ||
403 | stats->rmt_dram += add->rmt_dram; | ||
404 | stats->nomap += add->nomap; | ||
405 | stats->noparse += add->noparse; | ||
406 | } | ||
diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index 7f69bf9d789d..40f72ee4f42a 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h | |||
@@ -2,6 +2,10 @@ | |||
2 | #define __PERF_MEM_EVENTS_H | 2 | #define __PERF_MEM_EVENTS_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stdint.h> | ||
6 | #include <stdio.h> | ||
7 | #include <linux/types.h> | ||
8 | #include "stat.h" | ||
5 | 9 | ||
6 | struct perf_mem_event { | 10 | struct perf_mem_event { |
7 | bool record; | 11 | bool record; |
@@ -33,4 +37,38 @@ int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info); | |||
33 | 37 | ||
34 | int perf_script__meminfo_scnprintf(char *bf, size_t size, struct mem_info *mem_info); | 38 | int perf_script__meminfo_scnprintf(char *bf, size_t size, struct mem_info *mem_info); |
35 | 39 | ||
40 | struct c2c_stats { | ||
41 | u32 nr_entries; | ||
42 | |||
43 | u32 locks; /* count of 'lock' transactions */ | ||
44 | u32 store; /* count of all stores in trace */ | ||
45 | u32 st_uncache; /* stores to uncacheable address */ | ||
46 | u32 st_noadrs; /* cacheable store with no address */ | ||
47 | u32 st_l1hit; /* count of stores that hit L1D */ | ||
48 | u32 st_l1miss; /* count of stores that miss L1D */ | ||
49 | u32 load; /* count of all loads in trace */ | ||
50 | u32 ld_excl; /* exclusive loads, rmt/lcl DRAM - snp none/miss */ | ||
51 | u32 ld_shared; /* shared loads, rmt/lcl DRAM - snp hit */ | ||
52 | u32 ld_uncache; /* loads to uncacheable address */ | ||
53 | u32 ld_io; /* loads to io address */ | ||
54 | u32 ld_miss; /* loads miss */ | ||
55 | u32 ld_noadrs; /* cacheable load with no address */ | ||
56 | u32 ld_fbhit; /* count of loads hitting Fill Buffer */ | ||
57 | u32 ld_l1hit; /* count of loads that hit L1D */ | ||
58 | u32 ld_l2hit; /* count of loads that hit L2D */ | ||
59 | u32 ld_llchit; /* count of loads that hit LLC */ | ||
60 | u32 lcl_hitm; /* count of loads with local HITM */ | ||
61 | u32 rmt_hitm; /* count of loads with remote HITM */ | ||
62 | u32 tot_hitm; /* count of loads with local and remote HITM */ | ||
63 | u32 rmt_hit; /* count of loads with remote hit clean; */ | ||
64 | u32 lcl_dram; /* count of loads miss to local DRAM */ | ||
65 | u32 rmt_dram; /* count of loads miss to remote DRAM */ | ||
66 | u32 nomap; /* count of load/stores with no phys adrs */ | ||
67 | u32 noparse; /* count of unparsable data sources */ | ||
68 | }; | ||
69 | |||
70 | struct hist_entry; | ||
71 | int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi); | ||
72 | void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add); | ||
73 | |||
36 | #endif /* __PERF_MEM_EVENTS_H */ | 74 | #endif /* __PERF_MEM_EVENTS_H */ |
diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index afc088dd7d20..38fd11504015 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c | |||
@@ -31,59 +31,51 @@ static const struct branch_mode branch_modes[] = { | |||
31 | BRANCH_END | 31 | BRANCH_END |
32 | }; | 32 | }; |
33 | 33 | ||
34 | int | 34 | int parse_branch_str(const char *str, __u64 *mode) |
35 | parse_branch_stack(const struct option *opt, const char *str, int unset) | ||
36 | { | 35 | { |
37 | #define ONLY_PLM \ | 36 | #define ONLY_PLM \ |
38 | (PERF_SAMPLE_BRANCH_USER |\ | 37 | (PERF_SAMPLE_BRANCH_USER |\ |
39 | PERF_SAMPLE_BRANCH_KERNEL |\ | 38 | PERF_SAMPLE_BRANCH_KERNEL |\ |
40 | PERF_SAMPLE_BRANCH_HV) | 39 | PERF_SAMPLE_BRANCH_HV) |
41 | 40 | ||
42 | uint64_t *mode = (uint64_t *)opt->value; | 41 | int ret = 0; |
42 | char *p, *s; | ||
43 | char *os = NULL; | ||
43 | const struct branch_mode *br; | 44 | const struct branch_mode *br; |
44 | char *s, *os = NULL, *p; | ||
45 | int ret = -1; | ||
46 | 45 | ||
47 | if (unset) | 46 | if (str == NULL) { |
47 | *mode = PERF_SAMPLE_BRANCH_ANY; | ||
48 | return 0; | 48 | return 0; |
49 | } | ||
49 | 50 | ||
50 | /* | 51 | /* because str is read-only */ |
51 | * cannot set it twice, -b + --branch-filter for instance | 52 | s = os = strdup(str); |
52 | */ | 53 | if (!s) |
53 | if (*mode) | ||
54 | return -1; | 54 | return -1; |
55 | 55 | ||
56 | /* str may be NULL in case no arg is passed to -b */ | 56 | for (;;) { |
57 | if (str) { | 57 | p = strchr(s, ','); |
58 | /* because str is read-only */ | 58 | if (p) |
59 | s = os = strdup(str); | 59 | *p = '\0'; |
60 | if (!s) | ||
61 | return -1; | ||
62 | |||
63 | for (;;) { | ||
64 | p = strchr(s, ','); | ||
65 | if (p) | ||
66 | *p = '\0'; | ||
67 | |||
68 | for (br = branch_modes; br->name; br++) { | ||
69 | if (!strcasecmp(s, br->name)) | ||
70 | break; | ||
71 | } | ||
72 | if (!br->name) { | ||
73 | ui__warning("unknown branch filter %s," | ||
74 | " check man page\n", s); | ||
75 | goto error; | ||
76 | } | ||
77 | |||
78 | *mode |= br->mode; | ||
79 | |||
80 | if (!p) | ||
81 | break; | ||
82 | 60 | ||
83 | s = p + 1; | 61 | for (br = branch_modes; br->name; br++) { |
62 | if (!strcasecmp(s, br->name)) | ||
63 | break; | ||
64 | } | ||
65 | if (!br->name) { | ||
66 | ret = -1; | ||
67 | pr_warning("unknown branch filter %s," | ||
68 | " check man page\n", s); | ||
69 | goto error; | ||
84 | } | 70 | } |
71 | |||
72 | *mode |= br->mode; | ||
73 | |||
74 | if (!p) | ||
75 | break; | ||
76 | |||
77 | s = p + 1; | ||
85 | } | 78 | } |
86 | ret = 0; | ||
87 | 79 | ||
88 | /* default to any branch */ | 80 | /* default to any branch */ |
89 | if ((*mode & ~ONLY_PLM) == 0) { | 81 | if ((*mode & ~ONLY_PLM) == 0) { |
@@ -93,3 +85,20 @@ error: | |||
93 | free(os); | 85 | free(os); |
94 | return ret; | 86 | return ret; |
95 | } | 87 | } |
88 | |||
89 | int | ||
90 | parse_branch_stack(const struct option *opt, const char *str, int unset) | ||
91 | { | ||
92 | __u64 *mode = (__u64 *)opt->value; | ||
93 | |||
94 | if (unset) | ||
95 | return 0; | ||
96 | |||
97 | /* | ||
98 | * cannot set it twice, -b + --branch-filter for instance | ||
99 | */ | ||
100 | if (*mode) | ||
101 | return -1; | ||
102 | |||
103 | return parse_branch_str(str, mode); | ||
104 | } | ||
diff --git a/tools/perf/util/parse-branch-options.h b/tools/perf/util/parse-branch-options.h index b9d9470c2e82..6086fd90eb23 100644 --- a/tools/perf/util/parse-branch-options.h +++ b/tools/perf/util/parse-branch-options.h | |||
@@ -1,5 +1,6 @@ | |||
1 | #ifndef _PERF_PARSE_BRANCH_OPTIONS_H | 1 | #ifndef _PERF_PARSE_BRANCH_OPTIONS_H |
2 | #define _PERF_PARSE_BRANCH_OPTIONS_H 1 | 2 | #define _PERF_PARSE_BRANCH_OPTIONS_H 1 |
3 | struct option; | 3 | #include <stdint.h> |
4 | int parse_branch_stack(const struct option *opt, const char *str, int unset); | 4 | int parse_branch_stack(const struct option *opt, const char *str, int unset); |
5 | int parse_branch_str(const char *str, __u64 *mode); | ||
5 | #endif /* _PERF_PARSE_BRANCH_OPTIONS_H */ | 6 | #endif /* _PERF_PARSE_BRANCH_OPTIONS_H */ |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4e778eae1510..3c876b8ba4de 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "cpumap.h" | 22 | #include "cpumap.h" |
23 | #include "probe-file.h" | 23 | #include "probe-file.h" |
24 | #include "asm/bug.h" | 24 | #include "asm/bug.h" |
25 | #include "util/parse-branch-options.h" | ||
25 | 26 | ||
26 | #define MAX_NAME_LEN 100 | 27 | #define MAX_NAME_LEN 100 |
27 | 28 | ||
@@ -973,10 +974,13 @@ do { \ | |||
973 | CHECK_TYPE_VAL(NUM); | 974 | CHECK_TYPE_VAL(NUM); |
974 | break; | 975 | break; |
975 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | 976 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: |
976 | /* | 977 | CHECK_TYPE_VAL(STR); |
977 | * TODO uncomment when the field is available | 978 | if (strcmp(term->val.str, "no") && |
978 | * attr->branch_sample_type = term->val.num; | 979 | parse_branch_str(term->val.str, &attr->branch_sample_type)) { |
979 | */ | 980 | err->str = strdup("invalid branch sample type"); |
981 | err->idx = term->err_val; | ||
982 | return -EINVAL; | ||
983 | } | ||
980 | break; | 984 | break; |
981 | case PARSE_EVENTS__TERM_TYPE_TIME: | 985 | case PARSE_EVENTS__TERM_TYPE_TIME: |
982 | CHECK_TYPE_VAL(NUM); | 986 | CHECK_TYPE_VAL(NUM); |
@@ -1119,6 +1123,9 @@ do { \ | |||
1119 | case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: | 1123 | case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: |
1120 | ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str); | 1124 | ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str); |
1121 | break; | 1125 | break; |
1126 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
1127 | ADD_CONFIG_TERM(BRANCH, branch, term->val.str); | ||
1128 | break; | ||
1122 | case PARSE_EVENTS__TERM_TYPE_STACKSIZE: | 1129 | case PARSE_EVENTS__TERM_TYPE_STACKSIZE: |
1123 | ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num); | 1130 | ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num); |
1124 | break; | 1131 | break; |
diff --git a/tools/perf/util/perf-hooks-list.h b/tools/perf/util/perf-hooks-list.h new file mode 100644 index 000000000000..2867c07ee84e --- /dev/null +++ b/tools/perf/util/perf-hooks-list.h | |||
@@ -0,0 +1,3 @@ | |||
1 | PERF_HOOK(record_start) | ||
2 | PERF_HOOK(record_end) | ||
3 | PERF_HOOK(test) | ||
diff --git a/tools/perf/util/perf-hooks.c b/tools/perf/util/perf-hooks.c new file mode 100644 index 000000000000..cb368306b12b --- /dev/null +++ b/tools/perf/util/perf-hooks.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * perf_hooks.c | ||
3 | * | ||
4 | * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> | ||
5 | * Copyright (C) 2016 Huawei Inc. | ||
6 | */ | ||
7 | |||
8 | #include <errno.h> | ||
9 | #include <stdlib.h> | ||
10 | #include <setjmp.h> | ||
11 | #include <linux/err.h> | ||
12 | #include "util/util.h" | ||
13 | #include "util/debug.h" | ||
14 | #include "util/perf-hooks.h" | ||
15 | |||
16 | static sigjmp_buf jmpbuf; | ||
17 | static const struct perf_hook_desc *current_perf_hook; | ||
18 | |||
19 | void perf_hooks__invoke(const struct perf_hook_desc *desc) | ||
20 | { | ||
21 | if (!(desc && desc->p_hook_func && *desc->p_hook_func)) | ||
22 | return; | ||
23 | |||
24 | if (sigsetjmp(jmpbuf, 1)) { | ||
25 | pr_warning("Fatal error (SEGFAULT) in perf hook '%s'\n", | ||
26 | desc->hook_name); | ||
27 | *(current_perf_hook->p_hook_func) = NULL; | ||
28 | } else { | ||
29 | current_perf_hook = desc; | ||
30 | (**desc->p_hook_func)(desc->hook_ctx); | ||
31 | } | ||
32 | current_perf_hook = NULL; | ||
33 | } | ||
34 | |||
35 | void perf_hooks__recover(void) | ||
36 | { | ||
37 | if (current_perf_hook) | ||
38 | siglongjmp(jmpbuf, 1); | ||
39 | } | ||
40 | |||
41 | #define PERF_HOOK(name) \ | ||
42 | perf_hook_func_t __perf_hook_func_##name = NULL; \ | ||
43 | struct perf_hook_desc __perf_hook_desc_##name = \ | ||
44 | {.hook_name = #name, \ | ||
45 | .p_hook_func = &__perf_hook_func_##name, \ | ||
46 | .hook_ctx = NULL}; | ||
47 | #include "perf-hooks-list.h" | ||
48 | #undef PERF_HOOK | ||
49 | |||
50 | #define PERF_HOOK(name) \ | ||
51 | &__perf_hook_desc_##name, | ||
52 | |||
53 | static struct perf_hook_desc *perf_hooks[] = { | ||
54 | #include "perf-hooks-list.h" | ||
55 | }; | ||
56 | #undef PERF_HOOK | ||
57 | |||
58 | int perf_hooks__set_hook(const char *hook_name, | ||
59 | perf_hook_func_t hook_func, | ||
60 | void *hook_ctx) | ||
61 | { | ||
62 | unsigned int i; | ||
63 | |||
64 | for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) { | ||
65 | if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0) | ||
66 | continue; | ||
67 | |||
68 | if (*(perf_hooks[i]->p_hook_func)) | ||
69 | pr_warning("Overwrite existing hook: %s\n", hook_name); | ||
70 | *(perf_hooks[i]->p_hook_func) = hook_func; | ||
71 | perf_hooks[i]->hook_ctx = hook_ctx; | ||
72 | return 0; | ||
73 | } | ||
74 | return -ENOENT; | ||
75 | } | ||
76 | |||
77 | perf_hook_func_t perf_hooks__get_hook(const char *hook_name) | ||
78 | { | ||
79 | unsigned int i; | ||
80 | |||
81 | for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) { | ||
82 | if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0) | ||
83 | continue; | ||
84 | |||
85 | return *(perf_hooks[i]->p_hook_func); | ||
86 | } | ||
87 | return ERR_PTR(-ENOENT); | ||
88 | } | ||
diff --git a/tools/perf/util/perf-hooks.h b/tools/perf/util/perf-hooks.h new file mode 100644 index 000000000000..838d5797bc1e --- /dev/null +++ b/tools/perf/util/perf-hooks.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef PERF_UTIL_PERF_HOOKS_H | ||
2 | #define PERF_UTIL_PERF_HOOKS_H | ||
3 | |||
4 | #ifdef __cplusplus | ||
5 | extern "C" { | ||
6 | #endif | ||
7 | |||
8 | typedef void (*perf_hook_func_t)(void *ctx); | ||
9 | struct perf_hook_desc { | ||
10 | const char * const hook_name; | ||
11 | perf_hook_func_t * const p_hook_func; | ||
12 | void *hook_ctx; | ||
13 | }; | ||
14 | |||
15 | extern void perf_hooks__invoke(const struct perf_hook_desc *); | ||
16 | extern void perf_hooks__recover(void); | ||
17 | |||
18 | #define PERF_HOOK(name) \ | ||
19 | extern struct perf_hook_desc __perf_hook_desc_##name; \ | ||
20 | static inline void perf_hooks__invoke_##name(void) \ | ||
21 | { \ | ||
22 | perf_hooks__invoke(&__perf_hook_desc_##name); \ | ||
23 | } | ||
24 | |||
25 | #include "perf-hooks-list.h" | ||
26 | #undef PERF_HOOK | ||
27 | |||
28 | extern int | ||
29 | perf_hooks__set_hook(const char *hook_name, | ||
30 | perf_hook_func_t hook_func, | ||
31 | void *hook_ctx); | ||
32 | |||
33 | extern perf_hook_func_t | ||
34 | perf_hooks__get_hook(const char *hook_name); | ||
35 | |||
36 | #ifdef __cplusplus | ||
37 | } | ||
38 | #endif | ||
39 | #endif | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index b1474dcadfa2..dc6ccaa4e927 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -504,6 +504,7 @@ static void pmu_add_cpu_aliases(struct list_head *head) | |||
504 | struct pmu_events_map *map; | 504 | struct pmu_events_map *map; |
505 | struct pmu_event *pe; | 505 | struct pmu_event *pe; |
506 | char *cpuid; | 506 | char *cpuid; |
507 | static bool printed; | ||
507 | 508 | ||
508 | cpuid = getenv("PERF_CPUID"); | 509 | cpuid = getenv("PERF_CPUID"); |
509 | if (cpuid) | 510 | if (cpuid) |
@@ -513,7 +514,10 @@ static void pmu_add_cpu_aliases(struct list_head *head) | |||
513 | if (!cpuid) | 514 | if (!cpuid) |
514 | return; | 515 | return; |
515 | 516 | ||
516 | pr_debug("Using CPUID %s\n", cpuid); | 517 | if (!printed) { |
518 | pr_debug("Using CPUID %s\n", cpuid); | ||
519 | printed = true; | ||
520 | } | ||
517 | 521 | ||
518 | i = 0; | 522 | i = 0; |
519 | while (1) { | 523 | while (1) { |
@@ -1135,9 +1139,11 @@ void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, | |||
1135 | bool is_cpu = !strcmp(pmu->name, "cpu"); | 1139 | bool is_cpu = !strcmp(pmu->name, "cpu"); |
1136 | 1140 | ||
1137 | if (event_glob != NULL && | 1141 | if (event_glob != NULL && |
1138 | !(strglobmatch(name, event_glob) || | 1142 | !(strglobmatch_nocase(name, event_glob) || |
1139 | (!is_cpu && strglobmatch(alias->name, | 1143 | (!is_cpu && strglobmatch_nocase(alias->name, |
1140 | event_glob)))) | 1144 | event_glob)) || |
1145 | (alias->topic && | ||
1146 | strglobmatch_nocase(alias->topic, event_glob)))) | ||
1141 | continue; | 1147 | continue; |
1142 | 1148 | ||
1143 | if (is_cpu && !name_only && !alias->desc) | 1149 | if (is_cpu && !name_only && !alias->desc) |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 8091d15113f7..5d4e94061402 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -18,6 +18,8 @@ struct probe_conf { | |||
18 | extern struct probe_conf probe_conf; | 18 | extern struct probe_conf probe_conf; |
19 | extern bool probe_event_dry_run; | 19 | extern bool probe_event_dry_run; |
20 | 20 | ||
21 | struct symbol; | ||
22 | |||
21 | /* kprobe-tracer and uprobe-tracer tracing point */ | 23 | /* kprobe-tracer and uprobe-tracer tracing point */ |
22 | struct probe_trace_point { | 24 | struct probe_trace_point { |
23 | char *realname; /* function real name (if needed) */ | 25 | char *realname; /* function real name (if needed) */ |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index b7d4f4aeee61..0546a4304347 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -18,6 +18,7 @@ util/thread_map.c | |||
18 | util/util.c | 18 | util/util.c |
19 | util/xyarray.c | 19 | util/xyarray.c |
20 | util/cgroup.c | 20 | util/cgroup.c |
21 | util/parse-branch-options.c | ||
21 | util/rblist.c | 22 | util/rblist.c |
22 | util/counts.c | 23 | util/counts.c |
23 | util/strlist.c | 24 | util/strlist.c |
diff --git a/tools/perf/util/quote.c b/tools/perf/util/quote.c index 639d1da2f978..293534c1a474 100644 --- a/tools/perf/util/quote.c +++ b/tools/perf/util/quote.c | |||
@@ -54,7 +54,7 @@ int sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen) | |||
54 | break; | 54 | break; |
55 | ret = sq_quote_buf(dst, argv[i]); | 55 | ret = sq_quote_buf(dst, argv[i]); |
56 | if (maxlen && dst->len > maxlen) | 56 | if (maxlen && dst->len > maxlen) |
57 | die("Too many or long arguments"); | 57 | return -ENOSPC; |
58 | } | 58 | } |
59 | return ret; | 59 | return ret; |
60 | } | 60 | } |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5d61242a6e64..f268201048a0 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -2025,20 +2025,10 @@ out_delete_map: | |||
2025 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | 2025 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, |
2026 | bool full) | 2026 | bool full) |
2027 | { | 2027 | { |
2028 | struct stat st; | ||
2029 | int fd, ret; | ||
2030 | |||
2031 | if (session == NULL || fp == NULL) | 2028 | if (session == NULL || fp == NULL) |
2032 | return; | 2029 | return; |
2033 | 2030 | ||
2034 | fd = perf_data_file__fd(session->file); | ||
2035 | |||
2036 | ret = fstat(fd, &st); | ||
2037 | if (ret == -1) | ||
2038 | return; | ||
2039 | |||
2040 | fprintf(fp, "# ========\n"); | 2031 | fprintf(fp, "# ========\n"); |
2041 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
2042 | perf_header__fprintf_info(session, fp, full); | 2032 | perf_header__fprintf_info(session, fp, full); |
2043 | fprintf(fp, "# ========\n#\n"); | 2033 | fprintf(fp, "# ========\n#\n"); |
2044 | } | 2034 | } |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 452e15a10dd2..df622f4e301e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -315,7 +315,7 @@ struct sort_entry sort_sym = { | |||
315 | 315 | ||
316 | /* --sort srcline */ | 316 | /* --sort srcline */ |
317 | 317 | ||
318 | static char *hist_entry__get_srcline(struct hist_entry *he) | 318 | char *hist_entry__get_srcline(struct hist_entry *he) |
319 | { | 319 | { |
320 | struct map *map = he->ms.map; | 320 | struct map *map = he->ms.map; |
321 | 321 | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 099c97557d33..7aff317fc7c4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -280,4 +280,5 @@ int64_t | |||
280 | sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right); | 280 | sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right); |
281 | int64_t | 281 | int64_t |
282 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right); | 282 | sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right); |
283 | char *hist_entry__get_srcline(struct hist_entry *he); | ||
283 | #endif /* __PERF_SORT_H */ | 284 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 7f7e072be746..d8dfaf64b32e 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -193,7 +193,8 @@ error: | |||
193 | } | 193 | } |
194 | 194 | ||
195 | /* Glob/lazy pattern matching */ | 195 | /* Glob/lazy pattern matching */ |
196 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) | 196 | static bool __match_glob(const char *str, const char *pat, bool ignore_space, |
197 | bool case_ins) | ||
197 | { | 198 | { |
198 | while (*str && *pat && *pat != '*') { | 199 | while (*str && *pat && *pat != '*') { |
199 | if (ignore_space) { | 200 | if (ignore_space) { |
@@ -219,8 +220,13 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) | |||
219 | return false; | 220 | return false; |
220 | else if (*pat == '\\') /* Escaped char match as normal char */ | 221 | else if (*pat == '\\') /* Escaped char match as normal char */ |
221 | pat++; | 222 | pat++; |
222 | if (*str++ != *pat++) | 223 | if (case_ins) { |
224 | if (tolower(*str) != tolower(*pat)) | ||
225 | return false; | ||
226 | } else if (*str != *pat) | ||
223 | return false; | 227 | return false; |
228 | str++; | ||
229 | pat++; | ||
224 | } | 230 | } |
225 | /* Check wild card */ | 231 | /* Check wild card */ |
226 | if (*pat == '*') { | 232 | if (*pat == '*') { |
@@ -229,7 +235,7 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) | |||
229 | if (!*pat) /* Tail wild card matches all */ | 235 | if (!*pat) /* Tail wild card matches all */ |
230 | return true; | 236 | return true; |
231 | while (*str) | 237 | while (*str) |
232 | if (__match_glob(str++, pat, ignore_space)) | 238 | if (__match_glob(str++, pat, ignore_space, case_ins)) |
233 | return true; | 239 | return true; |
234 | } | 240 | } |
235 | return !*str && !*pat; | 241 | return !*str && !*pat; |
@@ -249,7 +255,12 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space) | |||
249 | */ | 255 | */ |
250 | bool strglobmatch(const char *str, const char *pat) | 256 | bool strglobmatch(const char *str, const char *pat) |
251 | { | 257 | { |
252 | return __match_glob(str, pat, false); | 258 | return __match_glob(str, pat, false, false); |
259 | } | ||
260 | |||
261 | bool strglobmatch_nocase(const char *str, const char *pat) | ||
262 | { | ||
263 | return __match_glob(str, pat, false, true); | ||
253 | } | 264 | } |
254 | 265 | ||
255 | /** | 266 | /** |
@@ -262,7 +273,7 @@ bool strglobmatch(const char *str, const char *pat) | |||
262 | */ | 273 | */ |
263 | bool strlazymatch(const char *str, const char *pat) | 274 | bool strlazymatch(const char *str, const char *pat) |
264 | { | 275 | { |
265 | return __match_glob(str, pat, true); | 276 | return __match_glob(str, pat, true, false); |
266 | } | 277 | } |
267 | 278 | ||
268 | /** | 279 | /** |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index aecff69a510d..dc93940de351 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1459,7 +1459,8 @@ int dso__load(struct dso *dso, struct map *map) | |||
1459 | * Read the build id if possible. This is required for | 1459 | * Read the build id if possible. This is required for |
1460 | * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work | 1460 | * DSO_BINARY_TYPE__BUILDID_DEBUGINFO to work |
1461 | */ | 1461 | */ |
1462 | if (is_regular_file(dso->long_name) && | 1462 | if (!dso->has_build_id && |
1463 | is_regular_file(dso->long_name) && | ||
1463 | filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0) | 1464 | filename__read_build_id(dso->long_name, build_id, BUILD_ID_SIZE) > 0) |
1464 | dso__set_build_id(dso, build_id); | 1465 | dso__set_build_id(dso, build_id); |
1465 | 1466 | ||
@@ -1962,7 +1963,7 @@ static bool symbol__read_kptr_restrict(void) | |||
1962 | char line[8]; | 1963 | char line[8]; |
1963 | 1964 | ||
1964 | if (fgets(line, sizeof(line), fp) != NULL) | 1965 | if (fgets(line, sizeof(line), fp) != NULL) |
1965 | value = (geteuid() != 0) ? | 1966 | value = ((geteuid() != 0) || (getuid() != 0)) ? |
1966 | (atoi(line) != 0) : | 1967 | (atoi(line) != 0) : |
1967 | (atoi(line) == 2); | 1968 | (atoi(line) == 2); |
1968 | 1969 | ||
@@ -2032,6 +2033,10 @@ int symbol__init(struct perf_env *env) | |||
2032 | symbol_conf.sym_list_str, "symbol") < 0) | 2033 | symbol_conf.sym_list_str, "symbol") < 0) |
2033 | goto out_free_tid_list; | 2034 | goto out_free_tid_list; |
2034 | 2035 | ||
2036 | if (setup_list(&symbol_conf.bt_stop_list, | ||
2037 | symbol_conf.bt_stop_list_str, "symbol") < 0) | ||
2038 | goto out_free_sym_list; | ||
2039 | |||
2035 | /* | 2040 | /* |
2036 | * A path to symbols of "/" is identical to "" | 2041 | * A path to symbols of "/" is identical to "" |
2037 | * reset here for simplicity. | 2042 | * reset here for simplicity. |
@@ -2049,6 +2054,8 @@ int symbol__init(struct perf_env *env) | |||
2049 | symbol_conf.initialized = true; | 2054 | symbol_conf.initialized = true; |
2050 | return 0; | 2055 | return 0; |
2051 | 2056 | ||
2057 | out_free_sym_list: | ||
2058 | strlist__delete(symbol_conf.sym_list); | ||
2052 | out_free_tid_list: | 2059 | out_free_tid_list: |
2053 | intlist__delete(symbol_conf.tid_list); | 2060 | intlist__delete(symbol_conf.tid_list); |
2054 | out_free_pid_list: | 2061 | out_free_pid_list: |
@@ -2064,6 +2071,7 @@ void symbol__exit(void) | |||
2064 | { | 2071 | { |
2065 | if (!symbol_conf.initialized) | 2072 | if (!symbol_conf.initialized) |
2066 | return; | 2073 | return; |
2074 | strlist__delete(symbol_conf.bt_stop_list); | ||
2067 | strlist__delete(symbol_conf.sym_list); | 2075 | strlist__delete(symbol_conf.sym_list); |
2068 | strlist__delete(symbol_conf.dso_list); | 2076 | strlist__delete(symbol_conf.dso_list); |
2069 | strlist__delete(symbol_conf.comm_list); | 2077 | strlist__delete(symbol_conf.comm_list); |
@@ -2071,6 +2079,7 @@ void symbol__exit(void) | |||
2071 | intlist__delete(symbol_conf.pid_list); | 2079 | intlist__delete(symbol_conf.pid_list); |
2072 | vmlinux_path__exit(); | 2080 | vmlinux_path__exit(); |
2073 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 2081 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
2082 | symbol_conf.bt_stop_list = NULL; | ||
2074 | symbol_conf.initialized = false; | 2083 | symbol_conf.initialized = false; |
2075 | } | 2084 | } |
2076 | 2085 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index d964844eb314..6c358b7ed336 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -58,6 +58,7 @@ struct symbol { | |||
58 | u16 namelen; | 58 | u16 namelen; |
59 | u8 binding; | 59 | u8 binding; |
60 | u8 idle:1; | 60 | u8 idle:1; |
61 | u8 ignore:1; | ||
61 | u8 arch_sym; | 62 | u8 arch_sym; |
62 | char name[0]; | 63 | char name[0]; |
63 | }; | 64 | }; |
@@ -100,6 +101,7 @@ struct symbol_conf { | |||
100 | show_total_period, | 101 | show_total_period, |
101 | use_callchain, | 102 | use_callchain, |
102 | cumulate_callchain, | 103 | cumulate_callchain, |
104 | show_branchflag_count, | ||
103 | exclude_other, | 105 | exclude_other, |
104 | show_cpu_utilization, | 106 | show_cpu_utilization, |
105 | initialized, | 107 | initialized, |
@@ -130,14 +132,16 @@ struct symbol_conf { | |||
130 | *pid_list_str, | 132 | *pid_list_str, |
131 | *tid_list_str, | 133 | *tid_list_str, |
132 | *sym_list_str, | 134 | *sym_list_str, |
133 | *col_width_list_str; | 135 | *col_width_list_str, |
136 | *bt_stop_list_str; | ||
134 | struct strlist *dso_list, | 137 | struct strlist *dso_list, |
135 | *comm_list, | 138 | *comm_list, |
136 | *sym_list, | 139 | *sym_list, |
137 | *dso_from_list, | 140 | *dso_from_list, |
138 | *dso_to_list, | 141 | *dso_to_list, |
139 | *sym_from_list, | 142 | *sym_from_list, |
140 | *sym_to_list; | 143 | *sym_to_list, |
144 | *bt_stop_list; | ||
141 | struct intlist *pid_list, | 145 | struct intlist *pid_list, |
142 | *tid_list; | 146 | *tid_list; |
143 | const char *symfs; | 147 | const char *symfs; |
@@ -281,7 +285,8 @@ int symbol__annotation_init(void); | |||
281 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | 285 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); |
282 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, | 286 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, |
283 | const struct addr_location *al, | 287 | const struct addr_location *al, |
284 | bool unknown_as_addr, FILE *fp); | 288 | bool unknown_as_addr, |
289 | bool print_offsets, FILE *fp); | ||
285 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 290 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
286 | const struct addr_location *al, FILE *fp); | 291 | const struct addr_location *al, FILE *fp); |
287 | size_t __symbol__fprintf_symname(const struct symbol *sym, | 292 | size_t __symbol__fprintf_symname(const struct symbol *sym, |
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c index a680bdaa65dc..7c6b33e8e2d2 100644 --- a/tools/perf/util/symbol_fprintf.c +++ b/tools/perf/util/symbol_fprintf.c | |||
@@ -15,14 +15,15 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp) | |||
15 | 15 | ||
16 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, | 16 | size_t __symbol__fprintf_symname_offs(const struct symbol *sym, |
17 | const struct addr_location *al, | 17 | const struct addr_location *al, |
18 | bool unknown_as_addr, FILE *fp) | 18 | bool unknown_as_addr, |
19 | bool print_offsets, FILE *fp) | ||
19 | { | 20 | { |
20 | unsigned long offset; | 21 | unsigned long offset; |
21 | size_t length; | 22 | size_t length; |
22 | 23 | ||
23 | if (sym && sym->name) { | 24 | if (sym && sym->name) { |
24 | length = fprintf(fp, "%s", sym->name); | 25 | length = fprintf(fp, "%s", sym->name); |
25 | if (al) { | 26 | if (al && print_offsets) { |
26 | if (al->addr < sym->end) | 27 | if (al->addr < sym->end) |
27 | offset = al->addr - sym->start; | 28 | offset = al->addr - sym->start; |
28 | else | 29 | else |
@@ -40,19 +41,19 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym, | |||
40 | const struct addr_location *al, | 41 | const struct addr_location *al, |
41 | FILE *fp) | 42 | FILE *fp) |
42 | { | 43 | { |
43 | return __symbol__fprintf_symname_offs(sym, al, false, fp); | 44 | return __symbol__fprintf_symname_offs(sym, al, false, true, fp); |
44 | } | 45 | } |
45 | 46 | ||
46 | size_t __symbol__fprintf_symname(const struct symbol *sym, | 47 | size_t __symbol__fprintf_symname(const struct symbol *sym, |
47 | const struct addr_location *al, | 48 | const struct addr_location *al, |
48 | bool unknown_as_addr, FILE *fp) | 49 | bool unknown_as_addr, FILE *fp) |
49 | { | 50 | { |
50 | return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp); | 51 | return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, false, fp); |
51 | } | 52 | } |
52 | 53 | ||
53 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | 54 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) |
54 | { | 55 | { |
55 | return __symbol__fprintf_symname_offs(sym, NULL, false, fp); | 56 | return __symbol__fprintf_symname_offs(sym, NULL, false, false, fp); |
56 | } | 57 | } |
57 | 58 | ||
58 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 59 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 40585f5b7027..f9eab200fd75 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
@@ -448,3 +448,25 @@ bool thread_map__has(struct thread_map *threads, pid_t pid) | |||
448 | 448 | ||
449 | return false; | 449 | return false; |
450 | } | 450 | } |
451 | |||
452 | int thread_map__remove(struct thread_map *threads, int idx) | ||
453 | { | ||
454 | int i; | ||
455 | |||
456 | if (threads->nr < 1) | ||
457 | return -EINVAL; | ||
458 | |||
459 | if (idx >= threads->nr) | ||
460 | return -EINVAL; | ||
461 | |||
462 | /* | ||
463 | * Free the 'idx' item and shift the rest up. | ||
464 | */ | ||
465 | free(threads->map[idx].comm); | ||
466 | |||
467 | for (i = idx; i < threads->nr - 1; i++) | ||
468 | threads->map[i] = threads->map[i + 1]; | ||
469 | |||
470 | threads->nr--; | ||
471 | return 0; | ||
472 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index bd3b971588da..ea0ef08c6303 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -58,4 +58,5 @@ static inline char *thread_map__comm(struct thread_map *map, int thread) | |||
58 | 58 | ||
59 | void thread_map__read_comms(struct thread_map *threads); | 59 | void thread_map__read_comms(struct thread_map *threads); |
60 | bool thread_map__has(struct thread_map *threads, pid_t pid); | 60 | bool thread_map__has(struct thread_map *threads, pid_t pid); |
61 | int thread_map__remove(struct thread_map *threads, int idx); | ||
61 | #endif /* __PERF_THREAD_MAP_H */ | 62 | #endif /* __PERF_THREAD_MAP_H */ |
diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c new file mode 100644 index 000000000000..d1b21c72206d --- /dev/null +++ b/tools/perf/util/time-utils.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include <sys/time.h> | ||
4 | #include <linux/time64.h> | ||
5 | #include <time.h> | ||
6 | #include <errno.h> | ||
7 | #include <inttypes.h> | ||
8 | |||
9 | #include "perf.h" | ||
10 | #include "debug.h" | ||
11 | #include "time-utils.h" | ||
12 | |||
13 | int parse_nsec_time(const char *str, u64 *ptime) | ||
14 | { | ||
15 | u64 time_sec, time_nsec; | ||
16 | char *end; | ||
17 | |||
18 | time_sec = strtoul(str, &end, 10); | ||
19 | if (*end != '.' && *end != '\0') | ||
20 | return -1; | ||
21 | |||
22 | if (*end == '.') { | ||
23 | int i; | ||
24 | char nsec_buf[10]; | ||
25 | |||
26 | if (strlen(++end) > 9) | ||
27 | return -1; | ||
28 | |||
29 | strncpy(nsec_buf, end, 9); | ||
30 | nsec_buf[9] = '\0'; | ||
31 | |||
32 | /* make it nsec precision */ | ||
33 | for (i = strlen(nsec_buf); i < 9; i++) | ||
34 | nsec_buf[i] = '0'; | ||
35 | |||
36 | time_nsec = strtoul(nsec_buf, &end, 10); | ||
37 | if (*end != '\0') | ||
38 | return -1; | ||
39 | } else | ||
40 | time_nsec = 0; | ||
41 | |||
42 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static int parse_timestr_sec_nsec(struct perf_time_interval *ptime, | ||
47 | char *start_str, char *end_str) | ||
48 | { | ||
49 | if (start_str && (*start_str != '\0') && | ||
50 | (parse_nsec_time(start_str, &ptime->start) != 0)) { | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | if (end_str && (*end_str != '\0') && | ||
55 | (parse_nsec_time(end_str, &ptime->end) != 0)) { | ||
56 | return -1; | ||
57 | } | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr) | ||
63 | { | ||
64 | char *start_str, *end_str; | ||
65 | char *d, *str; | ||
66 | int rc = 0; | ||
67 | |||
68 | if (ostr == NULL || *ostr == '\0') | ||
69 | return 0; | ||
70 | |||
71 | /* copy original string because we need to modify it */ | ||
72 | str = strdup(ostr); | ||
73 | if (str == NULL) | ||
74 | return -ENOMEM; | ||
75 | |||
76 | ptime->start = 0; | ||
77 | ptime->end = 0; | ||
78 | |||
79 | /* str has the format: <start>,<stop> | ||
80 | * variations: <start>, | ||
81 | * ,<stop> | ||
82 | * , | ||
83 | */ | ||
84 | start_str = str; | ||
85 | d = strchr(start_str, ','); | ||
86 | if (d) { | ||
87 | *d = '\0'; | ||
88 | ++d; | ||
89 | } | ||
90 | end_str = d; | ||
91 | |||
92 | rc = parse_timestr_sec_nsec(ptime, start_str, end_str); | ||
93 | |||
94 | free(str); | ||
95 | |||
96 | /* make sure end time is after start time if it was given */ | ||
97 | if (rc == 0 && ptime->end && ptime->end < ptime->start) | ||
98 | return -EINVAL; | ||
99 | |||
100 | pr_debug("start time %" PRIu64 ", ", ptime->start); | ||
101 | pr_debug("end time %" PRIu64 "\n", ptime->end); | ||
102 | |||
103 | return rc; | ||
104 | } | ||
105 | |||
106 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp) | ||
107 | { | ||
108 | /* if time is not set don't drop sample */ | ||
109 | if (timestamp == 0) | ||
110 | return false; | ||
111 | |||
112 | /* otherwise compare sample time to time window */ | ||
113 | if ((ptime->start && timestamp < ptime->start) || | ||
114 | (ptime->end && timestamp > ptime->end)) { | ||
115 | return true; | ||
116 | } | ||
117 | |||
118 | return false; | ||
119 | } | ||
diff --git a/tools/perf/util/time-utils.h b/tools/perf/util/time-utils.h new file mode 100644 index 000000000000..c1f197c4af6c --- /dev/null +++ b/tools/perf/util/time-utils.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef _TIME_UTILS_H_ | ||
2 | #define _TIME_UTILS_H_ | ||
3 | |||
4 | struct perf_time_interval { | ||
5 | u64 start, end; | ||
6 | }; | ||
7 | |||
8 | int parse_nsec_time(const char *str, u64 *ptime); | ||
9 | |||
10 | int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr); | ||
11 | |||
12 | bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp); | ||
13 | |||
14 | #endif | ||
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 9df61059a85d..0ac9077f62a2 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <errno.h> | 25 | #include <errno.h> |
26 | 26 | ||
27 | #include "../perf.h" | 27 | #include "../perf.h" |
28 | #include "debug.h" | ||
28 | #include "util.h" | 29 | #include "util.h" |
29 | #include "trace-event.h" | 30 | #include "trace-event.h" |
30 | 31 | ||
@@ -86,16 +87,15 @@ struct scripting_ops python_scripting_unsupported_ops = { | |||
86 | 87 | ||
87 | static void register_python_scripting(struct scripting_ops *scripting_ops) | 88 | static void register_python_scripting(struct scripting_ops *scripting_ops) |
88 | { | 89 | { |
89 | int err; | 90 | if (scripting_context == NULL) |
90 | err = script_spec_register("Python", scripting_ops); | 91 | scripting_context = malloc(sizeof(*scripting_context)); |
91 | if (err) | 92 | |
92 | die("error registering Python script extension"); | 93 | if (scripting_context == NULL || |
93 | 94 | script_spec_register("Python", scripting_ops) || | |
94 | err = script_spec_register("py", scripting_ops); | 95 | script_spec_register("py", scripting_ops)) { |
95 | if (err) | 96 | pr_err("Error registering Python script extension: disabling it\n"); |
96 | die("error registering py script extension"); | 97 | zfree(&scripting_context); |
97 | 98 | } | |
98 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
99 | } | 99 | } |
100 | 100 | ||
101 | #ifdef NO_LIBPYTHON | 101 | #ifdef NO_LIBPYTHON |
@@ -150,16 +150,15 @@ struct scripting_ops perl_scripting_unsupported_ops = { | |||
150 | 150 | ||
151 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | 151 | static void register_perl_scripting(struct scripting_ops *scripting_ops) |
152 | { | 152 | { |
153 | int err; | 153 | if (scripting_context == NULL) |
154 | err = script_spec_register("Perl", scripting_ops); | 154 | scripting_context = malloc(sizeof(*scripting_context)); |
155 | if (err) | 155 | |
156 | die("error registering Perl script extension"); | 156 | if (scripting_context == NULL || |
157 | 157 | script_spec_register("Perl", scripting_ops) || | |
158 | err = script_spec_register("pl", scripting_ops); | 158 | script_spec_register("pl", scripting_ops)) { |
159 | if (err) | 159 | pr_err("Error registering Perl script extension: disabling it\n"); |
160 | die("error registering pl script extension"); | 160 | zfree(&scripting_context); |
161 | 161 | } | |
162 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
163 | } | 162 | } |
164 | 163 | ||
165 | #ifdef NO_LIBPERL | 164 | #ifdef NO_LIBPERL |
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 20c2e5743903..6fec84dff3f7 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c | |||
@@ -357,8 +357,8 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | |||
357 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | 357 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; |
358 | di.start_ip = map->start; | 358 | di.start_ip = map->start; |
359 | di.end_ip = map->end; | 359 | di.end_ip = map->end; |
360 | di.u.rti.segbase = map->start + segbase; | 360 | di.u.rti.segbase = map->start + segbase - map->pgoff; |
361 | di.u.rti.table_data = map->start + table_data; | 361 | di.u.rti.table_data = map->start + table_data - map->pgoff; |
362 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | 362 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) |
363 | / sizeof(unw_word_t); | 363 | / sizeof(unw_word_t); |
364 | ret = dwarf_search_unwind_table(as, ip, &di, pi, | 364 | ret = dwarf_search_unwind_table(as, ip, &di, pi, |
diff --git a/tools/perf/util/util-cxx.h b/tools/perf/util/util-cxx.h new file mode 100644 index 000000000000..0e0e019c9f34 --- /dev/null +++ b/tools/perf/util/util-cxx.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Support C++ source use utilities defined in util.h | ||
3 | */ | ||
4 | |||
5 | #ifndef PERF_UTIL_UTIL_CXX_H | ||
6 | #define PERF_UTIL_UTIL_CXX_H | ||
7 | |||
8 | #ifdef __cplusplus | ||
9 | extern "C" { | ||
10 | #endif | ||
11 | |||
12 | /* | ||
13 | * Now 'new' is the only C++ keyword found in util.h: | ||
14 | * in tools/include/linux/rbtree.h | ||
15 | * | ||
16 | * Other keywords, like class and delete, should be | ||
17 | * redefined if necessary. | ||
18 | */ | ||
19 | #define new _new | ||
20 | #include "util.h" | ||
21 | #undef new | ||
22 | |||
23 | #ifdef __cplusplus | ||
24 | } | ||
25 | #endif | ||
26 | #endif | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 85c56800f17a..9ddd98827d12 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -400,37 +400,12 @@ void sighandler_dump_stack(int sig) | |||
400 | raise(sig); | 400 | raise(sig); |
401 | } | 401 | } |
402 | 402 | ||
403 | int parse_nsec_time(const char *str, u64 *ptime) | 403 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) |
404 | { | 404 | { |
405 | u64 time_sec, time_nsec; | 405 | u64 sec = timestamp / NSEC_PER_SEC; |
406 | char *end; | 406 | u64 usec = (timestamp % NSEC_PER_SEC) / NSEC_PER_USEC; |
407 | 407 | ||
408 | time_sec = strtoul(str, &end, 10); | 408 | return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); |
409 | if (*end != '.' && *end != '\0') | ||
410 | return -1; | ||
411 | |||
412 | if (*end == '.') { | ||
413 | int i; | ||
414 | char nsec_buf[10]; | ||
415 | |||
416 | if (strlen(++end) > 9) | ||
417 | return -1; | ||
418 | |||
419 | strncpy(nsec_buf, end, 9); | ||
420 | nsec_buf[9] = '\0'; | ||
421 | |||
422 | /* make it nsec precision */ | ||
423 | for (i = strlen(nsec_buf); i < 9; i++) | ||
424 | nsec_buf[i] = '0'; | ||
425 | |||
426 | time_nsec = strtoul(nsec_buf, &end, 10); | ||
427 | if (*end != '\0') | ||
428 | return -1; | ||
429 | } else | ||
430 | time_nsec = 0; | ||
431 | |||
432 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; | ||
433 | return 0; | ||
434 | } | 409 | } |
435 | 410 | ||
436 | unsigned long parse_tag_value(const char *str, struct parse_tag *tags) | 411 | unsigned long parse_tag_value(const char *str, struct parse_tag *tags) |
@@ -629,12 +604,63 @@ bool find_process(const char *name) | |||
629 | return ret ? false : true; | 604 | return ret ? false : true; |
630 | } | 605 | } |
631 | 606 | ||
607 | static int | ||
608 | fetch_ubuntu_kernel_version(unsigned int *puint) | ||
609 | { | ||
610 | ssize_t len; | ||
611 | size_t line_len = 0; | ||
612 | char *ptr, *line = NULL; | ||
613 | int version, patchlevel, sublevel, err; | ||
614 | FILE *vsig = fopen("/proc/version_signature", "r"); | ||
615 | |||
616 | if (!vsig) { | ||
617 | pr_debug("Open /proc/version_signature failed: %s\n", | ||
618 | strerror(errno)); | ||
619 | return -1; | ||
620 | } | ||
621 | |||
622 | len = getline(&line, &line_len, vsig); | ||
623 | fclose(vsig); | ||
624 | err = -1; | ||
625 | if (len <= 0) { | ||
626 | pr_debug("Reading from /proc/version_signature failed: %s\n", | ||
627 | strerror(errno)); | ||
628 | goto errout; | ||
629 | } | ||
630 | |||
631 | ptr = strrchr(line, ' '); | ||
632 | if (!ptr) { | ||
633 | pr_debug("Parsing /proc/version_signature failed: %s\n", line); | ||
634 | goto errout; | ||
635 | } | ||
636 | |||
637 | err = sscanf(ptr + 1, "%d.%d.%d", | ||
638 | &version, &patchlevel, &sublevel); | ||
639 | if (err != 3) { | ||
640 | pr_debug("Unable to get kernel version from /proc/version_signature '%s'\n", | ||
641 | line); | ||
642 | goto errout; | ||
643 | } | ||
644 | |||
645 | if (puint) | ||
646 | *puint = (version << 16) + (patchlevel << 8) + sublevel; | ||
647 | err = 0; | ||
648 | errout: | ||
649 | free(line); | ||
650 | return err; | ||
651 | } | ||
652 | |||
632 | int | 653 | int |
633 | fetch_kernel_version(unsigned int *puint, char *str, | 654 | fetch_kernel_version(unsigned int *puint, char *str, |
634 | size_t str_size) | 655 | size_t str_size) |
635 | { | 656 | { |
636 | struct utsname utsname; | 657 | struct utsname utsname; |
637 | int version, patchlevel, sublevel, err; | 658 | int version, patchlevel, sublevel, err; |
659 | bool int_ver_ready = false; | ||
660 | |||
661 | if (access("/proc/version_signature", R_OK) == 0) | ||
662 | if (!fetch_ubuntu_kernel_version(puint)) | ||
663 | int_ver_ready = true; | ||
638 | 664 | ||
639 | if (uname(&utsname)) | 665 | if (uname(&utsname)) |
640 | return -1; | 666 | return -1; |
@@ -648,12 +674,12 @@ fetch_kernel_version(unsigned int *puint, char *str, | |||
648 | &version, &patchlevel, &sublevel); | 674 | &version, &patchlevel, &sublevel); |
649 | 675 | ||
650 | if (err != 3) { | 676 | if (err != 3) { |
651 | pr_debug("Unablt to get kernel version from uname '%s'\n", | 677 | pr_debug("Unable to get kernel version from uname '%s'\n", |
652 | utsname.release); | 678 | utsname.release); |
653 | return -1; | 679 | return -1; |
654 | } | 680 | } |
655 | 681 | ||
656 | if (puint) | 682 | if (puint && !int_ver_ready) |
657 | *puint = (version << 16) + (patchlevel << 8) + sublevel; | 683 | *puint = (version << 16) + (patchlevel << 8) + sublevel; |
658 | return 0; | 684 | return 0; |
659 | } | 685 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 43899e0d6fa1..1d639e38aa82 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -179,8 +179,6 @@ static inline void *zalloc(size_t size) | |||
179 | #undef tolower | 179 | #undef tolower |
180 | #undef toupper | 180 | #undef toupper |
181 | 181 | ||
182 | int parse_nsec_time(const char *str, u64 *ptime); | ||
183 | |||
184 | extern unsigned char sane_ctype[256]; | 182 | extern unsigned char sane_ctype[256]; |
185 | #define GIT_SPACE 0x01 | 183 | #define GIT_SPACE 0x01 |
186 | #define GIT_DIGIT 0x02 | 184 | #define GIT_DIGIT 0x02 |
@@ -222,6 +220,7 @@ s64 perf_atoll(const char *str); | |||
222 | char **argv_split(const char *str, int *argcp); | 220 | char **argv_split(const char *str, int *argcp); |
223 | void argv_free(char **argv); | 221 | void argv_free(char **argv); |
224 | bool strglobmatch(const char *str, const char *pat); | 222 | bool strglobmatch(const char *str, const char *pat); |
223 | bool strglobmatch_nocase(const char *str, const char *pat); | ||
225 | bool strlazymatch(const char *str, const char *pat); | 224 | bool strlazymatch(const char *str, const char *pat); |
226 | static inline bool strisglob(const char *str) | 225 | static inline bool strisglob(const char *str) |
227 | { | 226 | { |
@@ -361,4 +360,7 @@ extern int sched_getcpu(void); | |||
361 | #endif | 360 | #endif |
362 | 361 | ||
363 | int is_printable_array(char *p, unsigned int len); | 362 | int is_printable_array(char *p, unsigned int len); |
363 | |||
364 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); | ||
365 | |||
364 | #endif /* GIT_COMPAT_UTIL_H */ | 366 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 0fb3c1fcd3e6..5074be4ed467 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -2,15 +2,18 @@ | |||
2 | 2 | ||
3 | #include "util.h" | 3 | #include "util.h" |
4 | #include "values.h" | 4 | #include "values.h" |
5 | #include "debug.h" | ||
5 | 6 | ||
6 | void perf_read_values_init(struct perf_read_values *values) | 7 | int perf_read_values_init(struct perf_read_values *values) |
7 | { | 8 | { |
8 | values->threads_max = 16; | 9 | values->threads_max = 16; |
9 | values->pid = malloc(values->threads_max * sizeof(*values->pid)); | 10 | values->pid = malloc(values->threads_max * sizeof(*values->pid)); |
10 | values->tid = malloc(values->threads_max * sizeof(*values->tid)); | 11 | values->tid = malloc(values->threads_max * sizeof(*values->tid)); |
11 | values->value = malloc(values->threads_max * sizeof(*values->value)); | 12 | values->value = malloc(values->threads_max * sizeof(*values->value)); |
12 | if (!values->pid || !values->tid || !values->value) | 13 | if (!values->pid || !values->tid || !values->value) { |
13 | die("failed to allocate read_values threads arrays"); | 14 | pr_debug("failed to allocate read_values threads arrays"); |
15 | goto out_free_pid; | ||
16 | } | ||
14 | values->threads = 0; | 17 | values->threads = 0; |
15 | 18 | ||
16 | values->counters_max = 16; | 19 | values->counters_max = 16; |
@@ -18,9 +21,22 @@ void perf_read_values_init(struct perf_read_values *values) | |||
18 | * sizeof(*values->counterrawid)); | 21 | * sizeof(*values->counterrawid)); |
19 | values->countername = malloc(values->counters_max | 22 | values->countername = malloc(values->counters_max |
20 | * sizeof(*values->countername)); | 23 | * sizeof(*values->countername)); |
21 | if (!values->counterrawid || !values->countername) | 24 | if (!values->counterrawid || !values->countername) { |
22 | die("failed to allocate read_values counters arrays"); | 25 | pr_debug("failed to allocate read_values counters arrays"); |
26 | goto out_free_counter; | ||
27 | } | ||
23 | values->counters = 0; | 28 | values->counters = 0; |
29 | |||
30 | return 0; | ||
31 | |||
32 | out_free_counter: | ||
33 | zfree(&values->counterrawid); | ||
34 | zfree(&values->countername); | ||
35 | out_free_pid: | ||
36 | zfree(&values->pid); | ||
37 | zfree(&values->tid); | ||
38 | zfree(&values->value); | ||
39 | return -ENOMEM; | ||
24 | } | 40 | } |
25 | 41 | ||
26 | void perf_read_values_destroy(struct perf_read_values *values) | 42 | void perf_read_values_destroy(struct perf_read_values *values) |
@@ -41,17 +57,27 @@ void perf_read_values_destroy(struct perf_read_values *values) | |||
41 | zfree(&values->countername); | 57 | zfree(&values->countername); |
42 | } | 58 | } |
43 | 59 | ||
44 | static void perf_read_values__enlarge_threads(struct perf_read_values *values) | 60 | static int perf_read_values__enlarge_threads(struct perf_read_values *values) |
45 | { | 61 | { |
46 | values->threads_max *= 2; | 62 | int nthreads_max = values->threads_max * 2; |
47 | values->pid = realloc(values->pid, | 63 | void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)), |
48 | values->threads_max * sizeof(*values->pid)); | 64 | *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)), |
49 | values->tid = realloc(values->tid, | 65 | *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value)); |
50 | values->threads_max * sizeof(*values->tid)); | 66 | |
51 | values->value = realloc(values->value, | 67 | if (!npid || !ntid || !nvalue) |
52 | values->threads_max * sizeof(*values->value)); | 68 | goto out_err; |
53 | if (!values->pid || !values->tid || !values->value) | 69 | |
54 | die("failed to enlarge read_values threads arrays"); | 70 | values->threads_max = nthreads_max; |
71 | values->pid = npid; | ||
72 | values->tid = ntid; | ||
73 | values->value = nvalue; | ||
74 | return 0; | ||
75 | out_err: | ||
76 | free(npid); | ||
77 | free(ntid); | ||
78 | free(nvalue); | ||
79 | pr_debug("failed to enlarge read_values threads arrays"); | ||
80 | return -ENOMEM; | ||
55 | } | 81 | } |
56 | 82 | ||
57 | static int perf_read_values__findnew_thread(struct perf_read_values *values, | 83 | static int perf_read_values__findnew_thread(struct perf_read_values *values, |
@@ -63,15 +89,21 @@ static int perf_read_values__findnew_thread(struct perf_read_values *values, | |||
63 | if (values->pid[i] == pid && values->tid[i] == tid) | 89 | if (values->pid[i] == pid && values->tid[i] == tid) |
64 | return i; | 90 | return i; |
65 | 91 | ||
66 | if (values->threads == values->threads_max) | 92 | if (values->threads == values->threads_max) { |
67 | perf_read_values__enlarge_threads(values); | 93 | i = perf_read_values__enlarge_threads(values); |
94 | if (i < 0) | ||
95 | return i; | ||
96 | } | ||
68 | 97 | ||
69 | i = values->threads++; | 98 | i = values->threads + 1; |
99 | values->value[i] = malloc(values->counters_max * sizeof(**values->value)); | ||
100 | if (!values->value[i]) { | ||
101 | pr_debug("failed to allocate read_values counters array"); | ||
102 | return -ENOMEM; | ||
103 | } | ||
70 | values->pid[i] = pid; | 104 | values->pid[i] = pid; |
71 | values->tid[i] = tid; | 105 | values->tid[i] = tid; |
72 | values->value[i] = malloc(values->counters_max * sizeof(**values->value)); | 106 | values->threads = i; |
73 | if (!values->value[i]) | ||
74 | die("failed to allocate read_values counters array"); | ||
75 | 107 | ||
76 | return i; | 108 | return i; |
77 | } | 109 | } |
@@ -115,16 +147,21 @@ static int perf_read_values__findnew_counter(struct perf_read_values *values, | |||
115 | return i; | 147 | return i; |
116 | } | 148 | } |
117 | 149 | ||
118 | void perf_read_values_add_value(struct perf_read_values *values, | 150 | int perf_read_values_add_value(struct perf_read_values *values, |
119 | u32 pid, u32 tid, | 151 | u32 pid, u32 tid, |
120 | u64 rawid, const char *name, u64 value) | 152 | u64 rawid, const char *name, u64 value) |
121 | { | 153 | { |
122 | int tindex, cindex; | 154 | int tindex, cindex; |
123 | 155 | ||
124 | tindex = perf_read_values__findnew_thread(values, pid, tid); | 156 | tindex = perf_read_values__findnew_thread(values, pid, tid); |
157 | if (tindex < 0) | ||
158 | return tindex; | ||
125 | cindex = perf_read_values__findnew_counter(values, rawid, name); | 159 | cindex = perf_read_values__findnew_counter(values, rawid, name); |
160 | if (cindex < 0) | ||
161 | return cindex; | ||
126 | 162 | ||
127 | values->value[tindex][cindex] = value; | 163 | values->value[tindex][cindex] = value; |
164 | return 0; | ||
128 | } | 165 | } |
129 | 166 | ||
130 | static void perf_read_values__display_pretty(FILE *fp, | 167 | static void perf_read_values__display_pretty(FILE *fp, |
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index b21a80c6cf8d..808ff9c73bf5 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h | |||
@@ -14,10 +14,10 @@ struct perf_read_values { | |||
14 | u64 **value; | 14 | u64 **value; |
15 | }; | 15 | }; |
16 | 16 | ||
17 | void perf_read_values_init(struct perf_read_values *values); | 17 | int perf_read_values_init(struct perf_read_values *values); |
18 | void perf_read_values_destroy(struct perf_read_values *values); | 18 | void perf_read_values_destroy(struct perf_read_values *values); |
19 | 19 | ||
20 | void perf_read_values_add_value(struct perf_read_values *values, | 20 | int perf_read_values_add_value(struct perf_read_values *values, |
21 | u32 pid, u32 tid, | 21 | u32 pid, u32 tid, |
22 | u64 rawid, const char *name, u64 value); | 22 | u64 rawid, const char *name, u64 value); |
23 | 23 | ||