diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-04-27 07:00:50 (GMT) |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-04-27 07:00:50 (GMT) |
commit | 1fa2e84db3f95adab8d9c2aa245e9a0ebf32248a (patch) | |
tree | f7dfb02a99d680c9c577d93dd704b822a66a39a6 /tools/perf | |
parent | 392d65a9adbe2f09707d2de27110dafb9c8dc08b (diff) | |
parent | 38b31bd0cefbb0e69a182d9a94b09a7e648549dc (diff) | |
download | linux-fsl-qoriq-1fa2e84db3f95adab8d9c2aa245e9a0ebf32248a.tar.xz |
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')
-rw-r--r-- | tools/perf/ui/browser.c | 46 | ||||
-rw-r--r-- | tools/perf/ui/browser.h | 3 | ||||
-rw-r--r-- | tools/perf/ui/browsers/annotate.c | 417 | ||||
-rw-r--r-- | tools/perf/util/annotate.c | 305 | ||||
-rw-r--r-- | tools/perf/util/annotate.h | 52 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 5 | ||||
-rw-r--r-- | tools/perf/util/util.c | 10 | ||||
-rw-r--r-- | tools/perf/util/util.h | 2 |
8 files changed, 645 insertions, 195 deletions
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index a1b140c..32ac116 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -593,6 +593,52 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser) return row; } +void ui_browser__write_graph(struct ui_browser *browser __used, int graph) +{ + SLsmg_set_char_set(1); + SLsmg_write_char(graph); + SLsmg_set_char_set(0); +} + +void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column, + u64 start, u64 end, int start_width) +{ + unsigned int row, end_row; + + SLsmg_set_char_set(1); + + if (start < browser->top_idx + browser->height) { + row = start - browser->top_idx; + ui_browser__gotorc(browser, row, column); + SLsmg_write_char(SLSMG_LLCORN_CHAR); + ui_browser__gotorc(browser, row, column + 1); + SLsmg_draw_hline(start_width); + + if (row-- == 0) + goto out; + } else + row = browser->height - 1; + + if (end > browser->top_idx) + end_row = end - browser->top_idx; + else + end_row = 0; + + ui_browser__gotorc(browser, end_row, column); + SLsmg_draw_vline(row - end_row + 1); + + ui_browser__gotorc(browser, end_row, column); + if (end >= browser->top_idx) { + SLsmg_write_char(SLSMG_ULCORN_CHAR); + ui_browser__gotorc(browser, end_row, column + 1); + SLsmg_write_char(SLSMG_HLINE_CHAR); + ui_browser__gotorc(browser, end_row, column + 2); + SLsmg_write_char(SLSMG_RARROW_CHAR); + } +out: + SLsmg_set_char_set(0); +} + void ui_browser__init(void) { int i = 0; diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 2550277..2f226cb 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -37,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); void ui_browser__gotorc(struct ui_browser *self, int y, int x); +void ui_browser__write_graph(struct ui_browser *browser, int graph); +void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column, + u64 start, u64 end, int start_width); void __ui_browser__show_title(struct ui_browser *browser, const char *title); void ui_browser__show_title(struct ui_browser *browser, const char *title); int ui_browser__show(struct ui_browser *self, const char *title, diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 4db5186..077380b 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -11,40 +11,42 @@ #include <pthread.h> #include <newt.h> +struct browser_disasm_line { + struct rb_node rb_node; + double percent; + u32 idx; + int idx_asm; + bool jump_target; +}; + struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; - struct objdump_line *selection; + struct disasm_line *selection; + struct disasm_line **offsets; u64 start; int nr_asm_entries; int nr_entries; bool hide_src_code; bool use_offset; bool searching_backwards; + u8 offset_width; char search_bf[128]; }; -struct objdump_line_rb_node { - struct rb_node rb_node; - double percent; - u32 idx; - int idx_asm; -}; - -static inline -struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) +static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) { - return (struct objdump_line_rb_node *)(self + 1); + return (struct browser_disasm_line *)(dl + 1); } -static bool objdump_line__filter(struct ui_browser *browser, void *entry) +static bool disasm_line__filter(struct ui_browser *browser, void *entry) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); if (ab->hide_src_code) { - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); - return ol->offset == -1; + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + return dl->offset == -1; } return false; @@ -53,72 +55,160 @@ static bool objdump_line__filter(struct ui_browser *browser, void *entry) static void annotate_browser__write(struct ui_browser *self, void *entry, int row) { struct annotate_browser *ab = container_of(self, struct annotate_browser, b); - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + struct browser_disasm_line *bdl = disasm_line__browser(dl); bool current_entry = ui_browser__is_current_entry(self, row); bool change_color = (!ab->hide_src_code && (!current_entry || (self->use_navkeypressed && !self->navkeypressed))); int width = self->width; - if (ol->offset != -1) { - struct objdump_line_rb_node *olrb = objdump_line__rb(ol); - ui_browser__set_percent_color(self, olrb->percent, current_entry); - slsmg_printf(" %7.2f ", olrb->percent); + if (dl->offset != -1) { + ui_browser__set_percent_color(self, bdl->percent, current_entry); + slsmg_printf(" %7.2f ", bdl->percent); } else { ui_browser__set_percent_color(self, 0, current_entry); slsmg_write_nstring(" ", 9); } - SLsmg_write_char(':'); - slsmg_write_nstring(" ", 8); + ui_browser__write_graph(self, SLSMG_VLINE_CHAR); + SLsmg_write_char(' '); /* The scroll bar isn't being used */ if (!self->navkeypressed) width += 1; - if (ol->offset != -1 && change_color) + if (dl->offset != -1 && change_color) ui_browser__set_color(self, HE_COLORSET_CODE); - if (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else if (ol->offset == -1) - slsmg_write_nstring(ol->line, width - 18); + if (!*dl->line) + slsmg_write_nstring(" ", width - 10); + else if (dl->offset == -1) + slsmg_write_nstring(dl->line, width - 10); else { - char bf[64]; - u64 addr = ol->offset; + char bf[256]; + u64 addr = dl->offset; int printed, color = -1; if (!ab->use_offset) addr += ab->start; - printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr); + if (!ab->use_offset) { + printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr); + } else { + if (bdl->jump_target) { + printed = scnprintf(bf, sizeof(bf), " %*" PRIx64 ":", + ab->offset_width, addr); + } else { + printed = scnprintf(bf, sizeof(bf), " %*s ", + ab->offset_width, " "); + } + } + if (change_color) color = ui_browser__set_color(self, HE_COLORSET_ADDR); slsmg_write_nstring(bf, printed); if (change_color) ui_browser__set_color(self, color); - slsmg_write_nstring(ol->line, width - 18 - printed); + if (dl->ins && dl->ins->ops->scnprintf) { + if (ins__is_jump(dl->ins)) { + bool fwd = dl->ops.target.offset > (u64)dl->offset; + + ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR : + SLSMG_UARROW_CHAR); + SLsmg_write_char(' '); + } else { + slsmg_write_nstring(" ", 2); + } + + dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops, + !ab->use_offset); + } else { + if (strcmp(dl->name, "retq")) { + slsmg_write_nstring(" ", 2); + } else { + ui_browser__write_graph(self, SLSMG_LARROW_CHAR); + SLsmg_write_char(' '); + } + + scnprintf(bf, sizeof(bf), "%-6.6s %s", dl->name, dl->ops.raw); + } + + slsmg_write_nstring(bf, width - 12 - printed); } if (current_entry) - ab->selection = ol; + ab->selection = dl; +} + +static void annotate_browser__draw_current_loop(struct ui_browser *browser) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct map_symbol *ms = browser->priv; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *cursor = ab->selection, *pos = cursor, *target; + struct browser_disasm_line *bcursor = disasm_line__browser(cursor), + *btarget, *bpos; + unsigned int from, to, start_width = 2; + + list_for_each_entry_from(pos, ¬es->src->source, node) { + if (!pos->ins || !ins__is_jump(pos->ins) || + !disasm_line__has_offset(pos)) + continue; + + target = ab->offsets[pos->ops.target.offset]; + if (!target) + continue; + + btarget = disasm_line__browser(target); + if (btarget->idx <= bcursor->idx) + goto found; + } + + return; + +found: + bpos = disasm_line__browser(pos); + if (ab->hide_src_code) { + from = bpos->idx_asm; + to = btarget->idx_asm; + } else { + from = (u64)bpos->idx; + to = (u64)btarget->idx; + } + + ui_browser__set_color(browser, HE_COLORSET_CODE); + + if (!bpos->jump_target) + start_width += ab->offset_width + 1; + + __ui_browser__line_arrow_up(browser, 10, from, to, start_width); +} + +static unsigned int annotate_browser__refresh(struct ui_browser *browser) +{ + int ret = ui_browser__list_head_refresh(browser); + + annotate_browser__draw_current_loop(browser); + + return ret; } -static double objdump_line__calc_percent(struct objdump_line *self, - struct symbol *sym, int evidx) +static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) { double percent = 0.0; - if (self->offset != -1) { + if (dl->offset != -1) { int len = sym->end - sym->start; unsigned int hits = 0; struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = self->offset; - struct objdump_line *next; + s64 offset = dl->offset; + struct disasm_line *next; - next = objdump__get_next_ip_line(¬es->src->source, self); + next = disasm__get_next_ip_line(¬es->src->source, dl); while (offset < (s64)len && (next == NULL || offset < next->offset)) { if (src_line) { @@ -139,27 +229,26 @@ static double objdump_line__calc_percent(struct objdump_line *self, return percent; } -static void objdump__insert_line(struct rb_root *self, - struct objdump_line_rb_node *line) +static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) { - struct rb_node **p = &self->rb_node; + struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - struct objdump_line_rb_node *l; + struct browser_disasm_line *l; while (*p != NULL) { parent = *p; - l = rb_entry(parent, struct objdump_line_rb_node, rb_node); - if (line->percent < l->percent) + l = rb_entry(parent, struct browser_disasm_line, rb_node); + if (bdl->percent < l->percent) p = &(*p)->rb_left; else p = &(*p)->rb_right; } - rb_link_node(&line->rb_node, parent, p); - rb_insert_color(&line->rb_node, self); + rb_link_node(&bdl->rb_node, parent, p); + rb_insert_color(&bdl->rb_node, root); } static void annotate_browser__set_top(struct annotate_browser *self, - struct objdump_line *pos, u32 idx) + struct disasm_line *pos, u32 idx) { unsigned back; @@ -168,9 +257,9 @@ static void annotate_browser__set_top(struct annotate_browser *self, self->b.top_idx = self->b.index = idx; while (self->b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct objdump_line, node); + pos = list_entry(pos->node.prev, struct disasm_line, node); - if (objdump_line__filter(&self->b, &pos->node)) + if (disasm_line__filter(&self->b, &pos->node)) continue; --self->b.top_idx; @@ -184,12 +273,12 @@ static void annotate_browser__set_top(struct annotate_browser *self, static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd) { - struct objdump_line_rb_node *rbpos; - struct objdump_line *pos; + struct browser_disasm_line *bpos; + struct disasm_line *pos; - rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); - pos = ((struct objdump_line *)rbpos) - 1; - annotate_browser__set_top(browser, pos, rbpos->idx); + bpos = rb_entry(nd, struct browser_disasm_line, rb_node); + pos = ((struct disasm_line *)bpos) - 1; + annotate_browser__set_top(browser, pos, bpos->idx); browser->curr_hot = nd; } @@ -199,20 +288,20 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos; + struct disasm_line *pos; browser->entries = RB_ROOT; pthread_mutex_lock(¬es->lock); list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); - rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); - if (rbpos->percent < 0.01) { - RB_CLEAR_NODE(&rbpos->rb_node); + struct browser_disasm_line *bpos = disasm_line__browser(pos); + bpos->percent = disasm_line__calc_percent(pos, sym, evidx); + if (bpos->percent < 0.01) { + RB_CLEAR_NODE(&bpos->rb_node); continue; } - objdump__insert_line(&browser->entries, rbpos); + disasm_rb_tree__insert(&browser->entries, bpos); } pthread_mutex_unlock(¬es->lock); @@ -221,38 +310,38 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, static bool annotate_browser__toggle_source(struct annotate_browser *browser) { - struct objdump_line *ol; - struct objdump_line_rb_node *olrb; + struct disasm_line *dl; + struct browser_disasm_line *bdl; off_t offset = browser->b.index - browser->b.top_idx; browser->b.seek(&browser->b, offset, SEEK_CUR); - ol = list_entry(browser->b.top, struct objdump_line, node); - olrb = objdump_line__rb(ol); + dl = list_entry(browser->b.top, struct disasm_line, node); + bdl = disasm_line__browser(dl); if (browser->hide_src_code) { - if (olrb->idx_asm < offset) - offset = olrb->idx; + if (bdl->idx_asm < offset) + offset = bdl->idx; browser->b.nr_entries = browser->nr_entries; browser->hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx - offset; - browser->b.index = olrb->idx; + browser->b.top_idx = bdl->idx - offset; + browser->b.index = bdl->idx; } else { - if (olrb->idx_asm < 0) { + if (bdl->idx_asm < 0) { ui_helpline__puts("Only available for assembly lines."); browser->b.seek(&browser->b, -offset, SEEK_CUR); return false; } - if (olrb->idx_asm < offset) - offset = olrb->idx_asm; + if (bdl->idx_asm < offset) + offset = bdl->idx_asm; browser->b.nr_entries = browser->nr_asm_entries; browser->hide_src_code = true; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx_asm - offset; - browser->b.index = olrb->idx_asm; + browser->b.top_idx = bdl->idx_asm - offset; + browser->b.index = bdl->idx_asm; } return true; @@ -263,23 +352,16 @@ static bool annotate_browser__callq(struct annotate_browser *browser, void *arg, int delay_secs) { struct map_symbol *ms = browser->b.priv; + struct disasm_line *dl = browser->selection; struct symbol *sym = ms->sym; struct annotation *notes; struct symbol *target; - char *s = strstr(browser->selection->line, "callq "); u64 ip; - if (s == NULL) + if (!ins__is_call(dl->ins)) return false; - s = strchr(s, ' '); - if (s++ == NULL) { - ui_helpline__puts("Invallid callq instruction."); - return true; - } - - ip = strtoull(s, NULL, 16); - ip = ms->map->map_ip(ms->map, ip); + ip = ms->map->map_ip(ms->map, dl->ops.target.addr); target = map__find_symbol(ms->map, ip, NULL); if (target == NULL) { ui_helpline__puts("The called function was not found."); @@ -302,20 +384,20 @@ static bool annotate_browser__callq(struct annotate_browser *browser, return true; } -static struct objdump_line * - annotate_browser__find_offset(struct annotate_browser *browser, - s64 offset, s64 *idx) +static +struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, + s64 offset, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos; + struct disasm_line *pos; *idx = 0; list_for_each_entry(pos, ¬es->src->source, node) { if (pos->offset == offset) return pos; - if (!objdump_line__filter(&browser->b, &pos->node)) + if (!disasm_line__filter(&browser->b, &pos->node)) ++*idx; } @@ -324,51 +406,35 @@ static struct objdump_line * static bool annotate_browser__jump(struct annotate_browser *browser) { - const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL }; - struct objdump_line *line; - s64 idx, offset; - char *s = NULL; - int i = 0; - - while (jumps[i]) { - s = strstr(browser->selection->line, jumps[i++]); - if (s) - break; - } + struct disasm_line *dl = browser->selection; + s64 idx; - if (s == NULL) + if (!ins__is_jump(dl->ins)) return false; - s = strchr(s, '+'); - if (s++ == NULL) { - ui_helpline__puts("Invallid jump instruction."); - return true; - } - - offset = strtoll(s, NULL, 16); - line = annotate_browser__find_offset(browser, offset, &idx); - if (line == NULL) { + dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); + if (dl == NULL) { ui_helpline__puts("Invallid jump offset"); return true; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); return true; } -static struct objdump_line * - annotate_browser__find_string(struct annotate_browser *browser, - char *s, s64 *idx) +static +struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, + char *s, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos = browser->selection; + struct disasm_line *pos = browser->selection; *idx = browser->b.index; list_for_each_entry_continue(pos, ¬es->src->source, node) { - if (objdump_line__filter(&browser->b, &pos->node)) + if (disasm_line__filter(&browser->b, &pos->node)) continue; ++*idx; @@ -382,32 +448,32 @@ static struct objdump_line * static bool __annotate_browser__search(struct annotate_browser *browser) { - struct objdump_line *line; + struct disasm_line *dl; s64 idx; - line = annotate_browser__find_string(browser, browser->search_bf, &idx); - if (line == NULL) { + dl = annotate_browser__find_string(browser, browser->search_bf, &idx); + if (dl == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); browser->searching_backwards = false; return true; } -static struct objdump_line * - annotate_browser__find_string_reverse(struct annotate_browser *browser, - char *s, s64 *idx) +static +struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, + char *s, s64 *idx) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos = browser->selection; + struct disasm_line *pos = browser->selection; *idx = browser->b.index; list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { - if (objdump_line__filter(&browser->b, &pos->node)) + if (disasm_line__filter(&browser->b, &pos->node)) continue; --*idx; @@ -421,16 +487,16 @@ static struct objdump_line * static bool __annotate_browser__search_reverse(struct annotate_browser *browser) { - struct objdump_line *line; + struct disasm_line *dl; s64 idx; - line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); - if (line == NULL) { + dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); + if (dl == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, line, idx); + annotate_browser__set_top(browser, dl, idx); browser->searching_backwards = true; return true; } @@ -581,9 +647,15 @@ show_help: ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); else if (self->selection->offset == -1) ui_helpline__puts("Actions are only available for assembly lines."); - else if (!(annotate_browser__jump(self) || - annotate_browser__callq(self, evidx, timer, arg, delay_secs))) - ui_helpline__puts("Actions are only available for the 'callq' and jump instructions."); + else if (!self->selection->ins) { + if (strcmp(self->selection->name, "retq")) + goto show_sup_ins; + goto out; + } else if (!(annotate_browser__jump(self) || + annotate_browser__callq(self, evidx, timer, arg, delay_secs))) { +show_sup_ins: + ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); + } continue; case K_LEFT: case K_ESC: @@ -609,27 +681,63 @@ int hist_entry__tui_annotate(struct hist_entry *he, int evidx, timer, arg, delay_secs); } +static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, + size_t size) +{ + u64 offset; + + for (offset = 0; offset < size; ++offset) { + struct disasm_line *dl = browser->offsets[offset], *dlt; + struct browser_disasm_line *bdlt; + + if (!dl || !dl->ins || !ins__is_jump(dl->ins) || + !disasm_line__has_offset(dl)) + continue; + + if (dl->ops.target.offset >= size) { + ui__error("jump to after symbol!\n" + "size: %zx, jump target: %" PRIx64, + size, dl->ops.target.offset); + continue; + } + + dlt = browser->offsets[dl->ops.target.offset]; + /* + * FIXME: Oops, no jump target? Buggy disassembler? Or do we + * have to adjust to the previous offset? + */ + if (dlt == NULL) + continue; + + bdlt = disasm_line__browser(dlt); + bdlt->jump_target = true; + } + +} + int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, void(*timer)(void *arg), void *arg, int delay_secs) { - struct objdump_line *pos, *n; + struct disasm_line *pos, *n; struct annotation *notes; + const size_t size = symbol__size(sym); struct map_symbol ms = { .map = map, .sym = sym, }; struct annotate_browser browser = { .b = { - .refresh = ui_browser__list_head_refresh, + .refresh = annotate_browser__refresh, .seek = ui_browser__list_head_seek, .write = annotate_browser__write, - .filter = objdump_line__filter, + .filter = disasm_line__filter, .priv = &ms, .use_navkeypressed = true, }, + .use_offset = true, }; - int ret; + int ret = -1; if (sym == NULL) return -1; @@ -637,37 +745,58 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, if (map->dso->annotate_warned) return -1; - if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { - ui__error("%s", ui_helpline__last_msg); + browser.offsets = zalloc(size * sizeof(struct disasm_line *)); + if (browser.offsets == NULL) { + ui__error("Not enough memory!"); return -1; } + if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { + ui__error("%s", ui_helpline__last_msg); + goto out_free_offsets; + } + ui_helpline__push("Press <- or ESC to exit"); notes = symbol__annotation(sym); browser.start = map__rip_2objdump(map, sym->start); list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos; + struct browser_disasm_line *bpos; size_t line_len = strlen(pos->line); if (browser.b.width < line_len) browser.b.width = line_len; - rbpos = objdump_line__rb(pos); - rbpos->idx = browser.nr_entries++; - if (pos->offset != -1) - rbpos->idx_asm = browser.nr_asm_entries++; - else - rbpos->idx_asm = -1; + bpos = disasm_line__browser(pos); + bpos->idx = browser.nr_entries++; + if (pos->offset != -1) { + bpos->idx_asm = browser.nr_asm_entries++; + /* + * FIXME: short term bandaid to cope with assembly + * routines that comes with labels in the same column + * as the address in objdump, sigh. + * + * E.g. copy_user_generic_unrolled + */ + if (pos->offset < (s64)size) + browser.offsets[pos->offset] = pos; + } else + bpos->idx_asm = -1; } + annotate_browser__mark_jump_targets(&browser, size); + + browser.offset_width = hex_width(size); browser.b.nr_entries = browser.nr_entries; browser.b.entries = ¬es->src->source, browser.b.width += 18; /* Percentage */ ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); list_for_each_entry_safe(pos, n, ¬es->src->source, node) { list_del(&pos->node); - objdump_line__free(pos); + disasm_line__free(pos); } + +out_free_offsets: + free(browser.offsets); return ret; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 1e7fd52..5eb3412 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -18,6 +18,149 @@ const char *disassembler_style; +static int call__parse(struct ins_operands *ops) +{ + char *endptr, *tok, *name; + + ops->target.addr = strtoull(ops->raw, &endptr, 16); + + name = strchr(endptr, '<'); + if (name == NULL) + goto indirect_call; + + name++; + + tok = strchr(name, '>'); + if (tok == NULL) + return -1; + + *tok = '\0'; + ops->target.name = strdup(name); + *tok = '>'; + + return ops->target.name == NULL ? -1 : 0; + +indirect_call: + tok = strchr(endptr, '*'); + if (tok == NULL) + return -1; + + ops->target.addr = strtoull(tok + 1, NULL, 16); + return 0; +} + +static int call__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, bool addrs) +{ + if (addrs) + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); + + if (ops->target.name) + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); + + return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); +} + +static struct ins_ops call_ops = { + .parse = call__parse, + .scnprintf = call__scnprintf, +}; + +bool ins__is_call(const struct ins *ins) +{ + return ins->ops == &call_ops; +} + +static int jump__parse(struct ins_operands *ops) +{ + const char *s = strchr(ops->raw, '+'); + + ops->target.addr = strtoll(ops->raw, NULL, 16); + + if (s++ != NULL) + ops->target.offset = strtoll(s, NULL, 16); + else + ops->target.offset = UINT64_MAX; + + return 0; +} + +static int jump__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, bool addrs) +{ + if (addrs) + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); + + return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); +} + +static struct ins_ops jump_ops = { + .parse = jump__parse, + .scnprintf = jump__scnprintf, +}; + +bool ins__is_jump(const struct ins *ins) +{ + return ins->ops == &jump_ops; +} + +/* + * Must be sorted by name! + */ +static struct ins instructions[] = { + { .name = "call", .ops = &call_ops, }, + { .name = "callq", .ops = &call_ops, }, + { .name = "ja", .ops = &jump_ops, }, + { .name = "jae", .ops = &jump_ops, }, + { .name = "jb", .ops = &jump_ops, }, + { .name = "jbe", .ops = &jump_ops, }, + { .name = "jc", .ops = &jump_ops, }, + { .name = "jcxz", .ops = &jump_ops, }, + { .name = "je", .ops = &jump_ops, }, + { .name = "jecxz", .ops = &jump_ops, }, + { .name = "jg", .ops = &jump_ops, }, + { .name = "jge", .ops = &jump_ops, }, + { .name = "jl", .ops = &jump_ops, }, + { .name = "jle", .ops = &jump_ops, }, + { .name = "jmp", .ops = &jump_ops, }, + { .name = "jmpq", .ops = &jump_ops, }, + { .name = "jna", .ops = &jump_ops, }, + { .name = "jnae", .ops = &jump_ops, }, + { .name = "jnb", .ops = &jump_ops, }, + { .name = "jnbe", .ops = &jump_ops, }, + { .name = "jnc", .ops = &jump_ops, }, + { .name = "jne", .ops = &jump_ops, }, + { .name = "jng", .ops = &jump_ops, }, + { .name = "jnge", .ops = &jump_ops, }, + { .name = "jnl", .ops = &jump_ops, }, + { .name = "jnle", .ops = &jump_ops, }, + { .name = "jno", .ops = &jump_ops, }, + { .name = "jnp", .ops = &jump_ops, }, + { .name = "jns", .ops = &jump_ops, }, + { .name = "jnz", .ops = &jump_ops, }, + { .name = "jo", .ops = &jump_ops, }, + { .name = "jp", .ops = &jump_ops, }, + { .name = "jpe", .ops = &jump_ops, }, + { .name = "jpo", .ops = &jump_ops, }, + { .name = "jrcxz", .ops = &jump_ops, }, + { .name = "js", .ops = &jump_ops, }, + { .name = "jz", .ops = &jump_ops, }, +}; + +static int ins__cmp(const void *name, const void *insp) +{ + const struct ins *ins = insp; + + return strcmp(name, ins->name); +} + +static struct ins *ins__find(const char *name) +{ + const int nmemb = ARRAY_SIZE(instructions); + + return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); +} + int symbol__annotate_init(struct map *map __used, struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); @@ -28,7 +171,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) int symbol__alloc_hist(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); - const size_t size = sym->end - sym->start + 1; + const size_t size = symbol__size(sym); size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); 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, return 0; } -static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) +static void disasm_line__init_ins(struct disasm_line *dl) { - struct objdump_line *self = malloc(sizeof(*self) + privsize); + dl->ins = ins__find(dl->name); + + if (dl->ins == NULL) + return; + + if (!dl->ins->ops) + return; - if (self != NULL) { - self->offset = offset; - self->line = strdup(line); - if (self->line == NULL) + if (dl->ins->ops->parse) + dl->ins->ops->parse(&dl->ops); +} + +static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) +{ + struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); + + if (dl != NULL) { + dl->offset = offset; + dl->line = strdup(line); + if (dl->line == NULL) goto out_delete; + + if (offset != -1) { + char *name = dl->line, tmp; + + while (isspace(name[0])) + ++name; + + if (name[0] == '\0') + goto out_delete; + + dl->ops.raw = name + 1; + + while (dl->ops.raw[0] != '\0' && + !isspace(dl->ops.raw[0])) + ++dl->ops.raw; + + tmp = dl->ops.raw[0]; + dl->ops.raw[0] = '\0'; + dl->name = strdup(name); + + if (dl->name == NULL) + goto out_free_line; + + dl->ops.raw[0] = tmp; + + if (dl->ops.raw[0] != '\0') { + dl->ops.raw++; + while (isspace(dl->ops.raw[0])) + ++dl->ops.raw; + } + + disasm_line__init_ins(dl); + } } - return self; + return dl; + +out_free_line: + free(dl->line); out_delete: - free(self); + free(dl); return NULL; } -void objdump_line__free(struct objdump_line *self) +void disasm_line__free(struct disasm_line *dl) { - free(self->line); - free(self); + free(dl->line); + free(dl->name); + free(dl->ops.target.name); + free(dl); } -static void objdump__add_line(struct list_head *head, struct objdump_line *line) +static void disasm__add(struct list_head *head, struct disasm_line *line) { list_add_tail(&line->node, head); } -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) @@ -116,15 +310,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, return NULL; } -static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, - u64 start, int evidx, u64 len, int min_pcnt, - int printed, int max_lines, - struct objdump_line *queue) +static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, + int evidx, u64 len, int min_pcnt, int printed, + int max_lines, struct disasm_line *queue) { static const char *prev_line; static const char *prev_color; - if (oline->offset != -1) { + if (dl->offset != -1) { const char *path = NULL; unsigned int hits = 0; double percent = 0.0; @@ -132,11 +325,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = oline->offset; + s64 offset = dl->offset; const u64 addr = start + offset; - struct objdump_line *next; + struct disasm_line *next; - next = objdump__get_next_ip_line(¬es->src->source, oline); + next = disasm__get_next_ip_line(¬es->src->source, dl); while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -161,9 +354,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, if (queue != NULL) { list_for_each_entry_from(queue, ¬es->src->source, node) { - if (queue == oline) + if (queue == dl) break; - objdump_line__print(queue, sym, start, evidx, len, + disasm_line__print(queue, sym, start, evidx, len, 0, 0, 1, NULL); } } @@ -187,17 +380,17 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, color_fprintf(stdout, color, " %7.2f", percent); printf(" : "); color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); - color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); + color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); } else if (max_lines && printed >= max_lines) return 1; else { if (queue) return -1; - if (!*oline->line) + if (!*dl->line) printf(" :\n"); else - printf(" : %s\n", oline->line); + printf(" : %s\n", dl->line); } return 0; @@ -207,7 +400,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, size_t privsize) { struct annotation *notes = symbol__annotation(sym); - struct objdump_line *objdump_line; + struct disasm_line *dl; char *line = NULL, *parsed_line, *tmp, *tmp2, *c; size_t line_len; s64 line_ip, offset = -1; @@ -258,13 +451,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, parsed_line = tmp2 + 1; } - objdump_line = objdump_line__new(offset, parsed_line, privsize); + dl = disasm_line__new(offset, parsed_line, privsize); free(line); - if (objdump_line == NULL) + if (dl == NULL) return -1; - objdump__add_line(¬es->src->source, objdump_line); + disasm__add(¬es->src->source, dl); return 0; } @@ -487,7 +680,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - u64 len = sym->end - sym->start, offset; + u64 len = symbol__size(sym), offset; for (offset = 0; offset < len; ++offset) if (h->addr[offset] != 0) @@ -503,7 +696,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, struct dso *dso = map->dso; const char *filename = dso->long_name, *d_filename; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos, *queue = NULL; + struct disasm_line *pos, *queue = NULL; u64 start = map__rip_2objdump(map, sym->start); int printed = 2, queue_len = 0; int more = 0; @@ -514,7 +707,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, else d_filename = basename(filename); - len = sym->end - sym->start; + len = symbol__size(sym); printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); @@ -528,7 +721,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, queue_len = 0; } - switch (objdump_line__print(pos, sym, start, evidx, len, + switch (disasm_line__print(pos, sym, start, evidx, len, min_pcnt, printed, max_lines, queue)) { case 0: @@ -574,7 +767,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - int len = sym->end - sym->start, offset; + int len = symbol__size(sym), offset; h->sum = 0; for (offset = 0; offset < len; ++offset) { @@ -583,14 +776,42 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) } } -void objdump_line_list__purge(struct list_head *head) +void disasm__purge(struct list_head *head) { - struct objdump_line *pos, *n; + struct disasm_line *pos, *n; list_for_each_entry_safe(pos, n, head, node) { list_del(&pos->node); - objdump_line__free(pos); + disasm_line__free(pos); + } +} + +static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) +{ + size_t printed; + + if (dl->offset == -1) + return fprintf(fp, "%s\n", dl->line); + + printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); + + if (dl->ops.raw[0] != '\0') { + printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", + dl->ops.raw); } + + return printed + fprintf(fp, "\n"); +} + +size_t disasm__fprintf(struct list_head *head, FILE *fp) +{ + struct disasm_line *pos; + size_t printed = 0; + + list_for_each_entry(pos, head, node) + printed += disasm_line__fprintf(pos, fp); + + return printed; } int 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, if (symbol__annotate(sym, map, 0) < 0) return -1; - len = sym->end - sym->start; + len = symbol__size(sym); if (print_lines) { 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, if (print_lines) symbol__free_source_line(sym, len); - objdump_line_list__purge(&symbol__annotation(sym)->src->source); + disasm__purge(&symbol__annotation(sym)->src->source); return 0; } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index efa5dc8..13a21f1 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -2,20 +2,54 @@ #define __PERF_ANNOTATE_H #include <stdbool.h> +#include <stdint.h> #include "types.h" #include "symbol.h" #include <linux/list.h> #include <linux/rbtree.h> -struct objdump_line { - struct list_head node; - s64 offset; - char *line; +struct ins; + +struct ins_operands { + char *raw; + struct { + char *name; + u64 offset; + u64 addr; + } target; +}; + +struct ins_ops { + int (*parse)(struct ins_operands *ops); + int (*scnprintf)(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, bool addrs); }; -void objdump_line__free(struct objdump_line *self); -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos); +struct ins { + const char *name; + struct ins_ops *ops; +}; + +bool ins__is_jump(const struct ins *ins); +bool ins__is_call(const struct ins *ins); + +struct disasm_line { + struct list_head node; + s64 offset; + char *line; + char *name; + struct ins *ins; + struct ins_operands ops; +}; + +static inline bool disasm_line__has_offset(const struct disasm_line *dl) +{ + return dl->ops.target.offset != UINT64_MAX; +} + +void disasm_line__free(struct disasm_line *dl); +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); +size_t disasm__fprintf(struct list_head *head, FILE *fp); struct sym_hist { u64 sum; @@ -32,7 +66,7 @@ struct source_line { * * @histogram: Array of addr hit histograms per event being monitored * @lines: If 'print_lines' is specified, per source code line percentages - * @source: source parsed from objdump -dS + * @source: source parsed from a disassembler like objdump -dS * * lines is allocated, percentages calculated and all sorted by percentage * when the annotation is about to be presented, so the percentages are for @@ -82,7 +116,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, int context); void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); -void objdump_line_list__purge(struct list_head *head); +void disasm__purge(struct list_head *head); int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, bool print_lines, bool full_paths, int min_pcnt, diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ac49ef2..1f00388 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -65,6 +65,11 @@ struct symbol { void symbol__delete(struct symbol *sym); +static inline size_t symbol__size(const struct symbol *sym) +{ + return sym->end - sym->start + 1; +} + struct strlist; struct symbol_conf { diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8109a90..d03599f 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -148,3 +148,13 @@ int readn(int fd, void *buf, size_t n) return buf - buf_start; } + +size_t hex_width(u64 v) +{ + size_t n = 1; + + while ((v >>= 4)) + ++n; + + return n; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f99f39..6121e24 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -265,4 +265,6 @@ bool is_power_of_2(unsigned long n) return (n != 0 && ((n & (n - 1)) == 0)); } +size_t hex_width(u64 v); + #endif |