aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/annotate.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-27 03:00:50 -0400
committerIngo Molnar <mingo@kernel.org>2012-04-27 03:00:50 -0400
commit1fa2e84db3f95adab8d9c2aa245e9a0ebf32248a (patch)
treef7dfb02a99d680c9c577d93dd704b822a66a39a6 /tools/perf/util/annotate.c
parent392d65a9adbe2f09707d2de27110dafb9c8dc08b (diff)
parent38b31bd0cefbb0e69a182d9a94b09a7e648549dc (diff)
Merge tag 'perf-annotate-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Annotation improvements: Now the default annotate browser uses a much more compact format, implementing suggestions made made by several people, notably Linus. Here is part of the new __list_del_entry() annotation: __list_del_entry 8.47 │ push %rbp 8.47 │ mov (%rdi),%rdx 20.34 │ mov $0xdead000000100100,%rcx 3.39 │ mov 0x8(%rdi),%rax 0.00 │ mov %rsp,%rbp 1.69 │ cmp %rcx,%rdx 0.00 │ je 43 1.69 │ mov $0xdead000000200200,%rcx 3.39 │ cmp %rcx,%rax 0.00 │ je a3 5.08 │ mov (%rax),%r8 18.64 │ cmp %r8,%rdi 0.00 │ jne 84 1.69 │ mov 0x8(%rdx),%r8 25.42 │ cmp %r8,%rdi 0.00 │ jne 65 1.69 │ mov %rax,0x8(%rdx) 0.00 │ mov %rdx,(%rax) 0.00 │ leaveq 0.00 │ retq 0.00 │ 43: mov %rdx,%r8 0.00 │ mov %rdi,%rcx 0.00 │ mov $0xffffffff817cd6a8,%rdx 0.00 │ mov $0x31,%esi 0.00 │ mov $0xffffffff817cd6e0,%rdi 0.00 │ xor %eax,%eax 0.00 │ callq ffffffff8104eab0 <warn_slowpath_fmt> 0.00 │ leaveq 0.00 │ retq 0.00 │ 65: mov %rdi,%rcx 0.00 │ mov $0xffffffff817cd780,%rdx 0.00 │ mov $0x3a,%esi 0.00 │ mov $0xffffffff817cd6e0,%rdi 0.00 │ xor %eax,%eax 0.00 │ callq ffffffff8104eab0 <warn_slowpath_fmt> 0.00 │ leaveq 0.00 │ retq The infrastructure is there to provide formatters for any instruction, like the one I'll do for call functions to elide the address. Further fixes on top of the first iteration: - Sometimes a jump points to an offset with no instructions, make the mark jump targets function handle that, for now just ignoring such jump targets, more investigation is needed to figure out how to cope with that. - Handle jump targets that are outside the function, for now just don't try to draw the connector arrow, right thing seems to be to mark this jump with a -> (right arrow) and handle it like a callq. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r--tools/perf/util/annotate.c305
1 files changed, 263 insertions, 42 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 1e7fd52bd29d..5eb34123f55b 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -18,6 +18,149 @@
18 18
19const char *disassembler_style; 19const char *disassembler_style;
20 20
21static int call__parse(struct ins_operands *ops)
22{
23 char *endptr, *tok, *name;
24
25 ops->target.addr = strtoull(ops->raw, &endptr, 16);
26
27 name = strchr(endptr, '<');
28 if (name == NULL)
29 goto indirect_call;
30
31 name++;
32
33 tok = strchr(name, '>');
34 if (tok == NULL)
35 return -1;
36
37 *tok = '\0';
38 ops->target.name = strdup(name);
39 *tok = '>';
40
41 return ops->target.name == NULL ? -1 : 0;
42
43indirect_call:
44 tok = strchr(endptr, '*');
45 if (tok == NULL)
46 return -1;
47
48 ops->target.addr = strtoull(tok + 1, NULL, 16);
49 return 0;
50}
51
52static int call__scnprintf(struct ins *ins, char *bf, size_t size,
53 struct ins_operands *ops, bool addrs)
54{
55 if (addrs)
56 return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
57
58 if (ops->target.name)
59 return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);
60
61 return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
62}
63
64static struct ins_ops call_ops = {
65 .parse = call__parse,
66 .scnprintf = call__scnprintf,
67};
68
69bool ins__is_call(const struct ins *ins)
70{
71 return ins->ops == &call_ops;
72}
73
74static int jump__parse(struct ins_operands *ops)
75{
76 const char *s = strchr(ops->raw, '+');
77
78 ops->target.addr = strtoll(ops->raw, NULL, 16);
79
80 if (s++ != NULL)
81 ops->target.offset = strtoll(s, NULL, 16);
82 else
83 ops->target.offset = UINT64_MAX;
84
85 return 0;
86}
87
88static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
89 struct ins_operands *ops, bool addrs)
90{
91 if (addrs)
92 return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
93
94 return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);
95}
96
97static struct ins_ops jump_ops = {
98 .parse = jump__parse,
99 .scnprintf = jump__scnprintf,
100};
101
102bool ins__is_jump(const struct ins *ins)
103{
104 return ins->ops == &jump_ops;
105}
106
107/*
108 * Must be sorted by name!
109 */
110static struct ins instructions[] = {
111 { .name = "call", .ops = &call_ops, },
112 { .name = "callq", .ops = &call_ops, },
113 { .name = "ja", .ops = &jump_ops, },
114 { .name = "jae", .ops = &jump_ops, },
115 { .name = "jb", .ops = &jump_ops, },
116 { .name = "jbe", .ops = &jump_ops, },
117 { .name = "jc", .ops = &jump_ops, },
118 { .name = "jcxz", .ops = &jump_ops, },
119 { .name = "je", .ops = &jump_ops, },
120 { .name = "jecxz", .ops = &jump_ops, },
121 { .name = "jg", .ops = &jump_ops, },
122 { .name = "jge", .ops = &jump_ops, },
123 { .name = "jl", .ops = &jump_ops, },
124 { .name = "jle", .ops = &jump_ops, },
125 { .name = "jmp", .ops = &jump_ops, },
126 { .name = "jmpq", .ops = &jump_ops, },
127 { .name = "jna", .ops = &jump_ops, },
128 { .name = "jnae", .ops = &jump_ops, },
129 { .name = "jnb", .ops = &jump_ops, },
130 { .name = "jnbe", .ops = &jump_ops, },
131 { .name = "jnc", .ops = &jump_ops, },
132 { .name = "jne", .ops = &jump_ops, },
133 { .name = "jng", .ops = &jump_ops, },
134 { .name = "jnge", .ops = &jump_ops, },
135 { .name = "jnl", .ops = &jump_ops, },
136 { .name = "jnle", .ops = &jump_ops, },
137 { .name = "jno", .ops = &jump_ops, },
138 { .name = "jnp", .ops = &jump_ops, },
139 { .name = "jns", .ops = &jump_ops, },
140 { .name = "jnz", .ops = &jump_ops, },
141 { .name = "jo", .ops = &jump_ops, },
142 { .name = "jp", .ops = &jump_ops, },
143 { .name = "jpe", .ops = &jump_ops, },
144 { .name = "jpo", .ops = &jump_ops, },
145 { .name = "jrcxz", .ops = &jump_ops, },
146 { .name = "js", .ops = &jump_ops, },
147 { .name = "jz", .ops = &jump_ops, },
148};
149
150static int ins__cmp(const void *name, const void *insp)
151{
152 const struct ins *ins = insp;
153
154 return strcmp(name, ins->name);
155}
156
157static struct ins *ins__find(const char *name)
158{
159 const int nmemb = ARRAY_SIZE(instructions);
160
161 return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);
162}
163
21int symbol__annotate_init(struct map *map __used, struct symbol *sym) 164int symbol__annotate_init(struct map *map __used, struct symbol *sym)
22{ 165{
23 struct annotation *notes = symbol__annotation(sym); 166 struct annotation *notes = symbol__annotation(sym);
@@ -28,7 +171,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
28int symbol__alloc_hist(struct symbol *sym) 171int symbol__alloc_hist(struct symbol *sym)
29{ 172{
30 struct annotation *notes = symbol__annotation(sym); 173 struct annotation *notes = symbol__annotation(sym);
31 const size_t size = sym->end - sym->start + 1; 174 const size_t size = symbol__size(sym);
32 size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); 175 size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
33 176
34 notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); 177 notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
@@ -78,36 +221,87 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
78 return 0; 221 return 0;
79} 222}
80 223
81static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) 224static void disasm_line__init_ins(struct disasm_line *dl)
82{ 225{
83 struct objdump_line *self = malloc(sizeof(*self) + privsize); 226 dl->ins = ins__find(dl->name);
227
228 if (dl->ins == NULL)
229 return;
230
231 if (!dl->ins->ops)
232 return;
84 233
85 if (self != NULL) { 234 if (dl->ins->ops->parse)
86 self->offset = offset; 235 dl->ins->ops->parse(&dl->ops);
87 self->line = strdup(line); 236}
88 if (self->line == NULL) 237
238static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
239{
240 struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
241
242 if (dl != NULL) {
243 dl->offset = offset;
244 dl->line = strdup(line);
245 if (dl->line == NULL)
89 goto out_delete; 246 goto out_delete;
247
248 if (offset != -1) {
249 char *name = dl->line, tmp;
250
251 while (isspace(name[0]))
252 ++name;
253
254 if (name[0] == '\0')
255 goto out_delete;
256
257 dl->ops.raw = name + 1;
258
259 while (dl->ops.raw[0] != '\0' &&
260 !isspace(dl->ops.raw[0]))
261 ++dl->ops.raw;
262
263 tmp = dl->ops.raw[0];
264 dl->ops.raw[0] = '\0';
265 dl->name = strdup(name);
266
267 if (dl->name == NULL)
268 goto out_free_line;
269
270 dl->ops.raw[0] = tmp;
271
272 if (dl->ops.raw[0] != '\0') {
273 dl->ops.raw++;
274 while (isspace(dl->ops.raw[0]))
275 ++dl->ops.raw;
276 }
277
278 disasm_line__init_ins(dl);
279 }
90 } 280 }
91 281
92 return self; 282 return dl;
283
284out_free_line:
285 free(dl->line);
93out_delete: 286out_delete:
94 free(self); 287 free(dl);
95 return NULL; 288 return NULL;
96} 289}
97 290
98void objdump_line__free(struct objdump_line *self) 291void disasm_line__free(struct disasm_line *dl)
99{ 292{
100 free(self->line); 293 free(dl->line);
101 free(self); 294 free(dl->name);
295 free(dl->ops.target.name);
296 free(dl);
102} 297}
103 298
104static void objdump__add_line(struct list_head *head, struct objdump_line *line) 299static void disasm__add(struct list_head *head, struct disasm_line *line)
105{ 300{
106 list_add_tail(&line->node, head); 301 list_add_tail(&line->node, head);
107} 302}
108 303
109struct objdump_line *objdump__get_next_ip_line(struct list_head *head, 304struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos)
110 struct objdump_line *pos)
111{ 305{
112 list_for_each_entry_continue(pos, head, node) 306 list_for_each_entry_continue(pos, head, node)
113 if (pos->offset >= 0) 307 if (pos->offset >= 0)
@@ -116,15 +310,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
116 return NULL; 310 return NULL;
117} 311}
118 312
119static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, 313static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
120 u64 start, int evidx, u64 len, int min_pcnt, 314 int evidx, u64 len, int min_pcnt, int printed,
121 int printed, int max_lines, 315 int max_lines, struct disasm_line *queue)
122 struct objdump_line *queue)
123{ 316{
124 static const char *prev_line; 317 static const char *prev_line;
125 static const char *prev_color; 318 static const char *prev_color;
126 319
127 if (oline->offset != -1) { 320 if (dl->offset != -1) {
128 const char *path = NULL; 321 const char *path = NULL;
129 unsigned int hits = 0; 322 unsigned int hits = 0;
130 double percent = 0.0; 323 double percent = 0.0;
@@ -132,11 +325,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
132 struct annotation *notes = symbol__annotation(sym); 325 struct annotation *notes = symbol__annotation(sym);
133 struct source_line *src_line = notes->src->lines; 326 struct source_line *src_line = notes->src->lines;
134 struct sym_hist *h = annotation__histogram(notes, evidx); 327 struct sym_hist *h = annotation__histogram(notes, evidx);
135 s64 offset = oline->offset; 328 s64 offset = dl->offset;
136 const u64 addr = start + offset; 329 const u64 addr = start + offset;
137 struct objdump_line *next; 330 struct disasm_line *next;
138 331
139 next = objdump__get_next_ip_line(&notes->src->source, oline); 332 next = disasm__get_next_ip_line(&notes->src->source, dl);
140 333
141 while (offset < (s64)len && 334 while (offset < (s64)len &&
142 (next == NULL || offset < next->offset)) { 335 (next == NULL || offset < next->offset)) {
@@ -161,9 +354,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
161 354
162 if (queue != NULL) { 355 if (queue != NULL) {
163 list_for_each_entry_from(queue, &notes->src->source, node) { 356 list_for_each_entry_from(queue, &notes->src->source, node) {
164 if (queue == oline) 357 if (queue == dl)
165 break; 358 break;
166 objdump_line__print(queue, sym, start, evidx, len, 359 disasm_line__print(queue, sym, start, evidx, len,
167 0, 0, 1, NULL); 360 0, 0, 1, NULL);
168 } 361 }
169 } 362 }
@@ -187,17 +380,17 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
187 color_fprintf(stdout, color, " %7.2f", percent); 380 color_fprintf(stdout, color, " %7.2f", percent);
188 printf(" : "); 381 printf(" : ");
189 color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); 382 color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr);
190 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); 383 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
191 } else if (max_lines && printed >= max_lines) 384 } else if (max_lines && printed >= max_lines)
192 return 1; 385 return 1;
193 else { 386 else {
194 if (queue) 387 if (queue)
195 return -1; 388 return -1;
196 389
197 if (!*oline->line) 390 if (!*dl->line)
198 printf(" :\n"); 391 printf(" :\n");
199 else 392 else
200 printf(" : %s\n", oline->line); 393 printf(" : %s\n", dl->line);
201 } 394 }
202 395
203 return 0; 396 return 0;
@@ -207,7 +400,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
207 FILE *file, size_t privsize) 400 FILE *file, size_t privsize)
208{ 401{
209 struct annotation *notes = symbol__annotation(sym); 402 struct annotation *notes = symbol__annotation(sym);
210 struct objdump_line *objdump_line; 403 struct disasm_line *dl;
211 char *line = NULL, *parsed_line, *tmp, *tmp2, *c; 404 char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
212 size_t line_len; 405 size_t line_len;
213 s64 line_ip, offset = -1; 406 s64 line_ip, offset = -1;
@@ -258,13 +451,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
258 parsed_line = tmp2 + 1; 451 parsed_line = tmp2 + 1;
259 } 452 }
260 453
261 objdump_line = objdump_line__new(offset, parsed_line, privsize); 454 dl = disasm_line__new(offset, parsed_line, privsize);
262 free(line); 455 free(line);
263 456
264 if (objdump_line == NULL) 457 if (dl == NULL)
265 return -1; 458 return -1;
266 459
267 objdump__add_line(&notes->src->source, objdump_line); 460 disasm__add(&notes->src->source, dl);
268 461
269 return 0; 462 return 0;
270} 463}
@@ -487,7 +680,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx)
487{ 680{
488 struct annotation *notes = symbol__annotation(sym); 681 struct annotation *notes = symbol__annotation(sym);
489 struct sym_hist *h = annotation__histogram(notes, evidx); 682 struct sym_hist *h = annotation__histogram(notes, evidx);
490 u64 len = sym->end - sym->start, offset; 683 u64 len = symbol__size(sym), offset;
491 684
492 for (offset = 0; offset < len; ++offset) 685 for (offset = 0; offset < len; ++offset)
493 if (h->addr[offset] != 0) 686 if (h->addr[offset] != 0)
@@ -503,7 +696,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
503 struct dso *dso = map->dso; 696 struct dso *dso = map->dso;
504 const char *filename = dso->long_name, *d_filename; 697 const char *filename = dso->long_name, *d_filename;
505 struct annotation *notes = symbol__annotation(sym); 698 struct annotation *notes = symbol__annotation(sym);
506 struct objdump_line *pos, *queue = NULL; 699 struct disasm_line *pos, *queue = NULL;
507 u64 start = map__rip_2objdump(map, sym->start); 700 u64 start = map__rip_2objdump(map, sym->start);
508 int printed = 2, queue_len = 0; 701 int printed = 2, queue_len = 0;
509 int more = 0; 702 int more = 0;
@@ -514,7 +707,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
514 else 707 else
515 d_filename = basename(filename); 708 d_filename = basename(filename);
516 709
517 len = sym->end - sym->start; 710 len = symbol__size(sym);
518 711
519 printf(" Percent | Source code & Disassembly of %s\n", d_filename); 712 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
520 printf("------------------------------------------------\n"); 713 printf("------------------------------------------------\n");
@@ -528,7 +721,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
528 queue_len = 0; 721 queue_len = 0;
529 } 722 }
530 723
531 switch (objdump_line__print(pos, sym, start, evidx, len, 724 switch (disasm_line__print(pos, sym, start, evidx, len,
532 min_pcnt, printed, max_lines, 725 min_pcnt, printed, max_lines,
533 queue)) { 726 queue)) {
534 case 0: 727 case 0:
@@ -574,7 +767,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
574{ 767{
575 struct annotation *notes = symbol__annotation(sym); 768 struct annotation *notes = symbol__annotation(sym);
576 struct sym_hist *h = annotation__histogram(notes, evidx); 769 struct sym_hist *h = annotation__histogram(notes, evidx);
577 int len = sym->end - sym->start, offset; 770 int len = symbol__size(sym), offset;
578 771
579 h->sum = 0; 772 h->sum = 0;
580 for (offset = 0; offset < len; ++offset) { 773 for (offset = 0; offset < len; ++offset) {
@@ -583,14 +776,42 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
583 } 776 }
584} 777}
585 778
586void objdump_line_list__purge(struct list_head *head) 779void disasm__purge(struct list_head *head)
587{ 780{
588 struct objdump_line *pos, *n; 781 struct disasm_line *pos, *n;
589 782
590 list_for_each_entry_safe(pos, n, head, node) { 783 list_for_each_entry_safe(pos, n, head, node) {
591 list_del(&pos->node); 784 list_del(&pos->node);
592 objdump_line__free(pos); 785 disasm_line__free(pos);
786 }
787}
788
789static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
790{
791 size_t printed;
792
793 if (dl->offset == -1)
794 return fprintf(fp, "%s\n", dl->line);
795
796 printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);
797
798 if (dl->ops.raw[0] != '\0') {
799 printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
800 dl->ops.raw);
593 } 801 }
802
803 return printed + fprintf(fp, "\n");
804}
805
806size_t disasm__fprintf(struct list_head *head, FILE *fp)
807{
808 struct disasm_line *pos;
809 size_t printed = 0;
810
811 list_for_each_entry(pos, head, node)
812 printed += disasm_line__fprintf(pos, fp);
813
814 return printed;
594} 815}
595 816
596int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, 817int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
@@ -605,7 +826,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
605 if (symbol__annotate(sym, map, 0) < 0) 826 if (symbol__annotate(sym, map, 0) < 0)
606 return -1; 827 return -1;
607 828
608 len = sym->end - sym->start; 829 len = symbol__size(sym);
609 830
610 if (print_lines) { 831 if (print_lines) {
611 symbol__get_source_line(sym, map, evidx, &source_line, 832 symbol__get_source_line(sym, map, evidx, &source_line,
@@ -618,7 +839,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
618 if (print_lines) 839 if (print_lines)
619 symbol__free_source_line(sym, len); 840 symbol__free_source_line(sym, len);
620 841
621 objdump_line_list__purge(&symbol__annotation(sym)->src->source); 842 disasm__purge(&symbol__annotation(sym)->src->source);
622 843
623 return 0; 844 return 0;
624} 845}