diff options
Diffstat (limited to 'tools/perf/ui/browsers/annotate.c')
| -rw-r--r-- | tools/perf/ui/browsers/annotate.c | 1282 |
1 files changed, 771 insertions, 511 deletions
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index cc64d3f7fc36..36aca8d6d003 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -1,302 +1,307 @@ -#include "../../util/util.h" +// SPDX-License-Identifier: GPL-2.0 #include "../browser.h" #include "../helpline.h" -#include "../libslang.h" #include "../ui.h" -#include "../util.h" #include "../../util/annotate.h" +#include "../../util/debug.h" +#include "../../util/debuginfo.h" +#include "../../util/dso.h" +#include "../../util/hashmap.h" #include "../../util/hist.h" #include "../../util/sort.h" +#include "../../util/map.h" +#include "../../util/mutex.h" #include "../../util/symbol.h" #include "../../util/evsel.h" -#include <pthread.h> +#include "../../util/evlist.h" +#include "../../util/thread.h" +#include <inttypes.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/zalloc.h> +#include <sys/ttydefaults.h> +#include <asm/bug.h> + +struct arch; -struct browser_disasm_line { - struct rb_node rb_node; - u32 idx; - int idx_asm; - int jump_sources; +struct annotate_browser { + struct ui_browser b; + struct rb_root entries; + struct rb_node *curr_hot; + struct annotation_line *selection; + struct arch *arch; /* - * actual length of this array is saved on the nr_events field - * of the struct annotate_browser + * perf top can delete hist_entry anytime. Callers should make sure + * its lifetime. */ - double percent[1]; + struct hist_entry *he; + struct debuginfo *dbg; + struct evsel *evsel; + struct hashmap *type_hash; + bool searching_backwards; + char search_bf[128]; }; -static struct annotate_browser_opt { - bool hide_src_code, - use_offset, - jump_arrows, - show_nr_jumps; -} annotate_browser__opts = { - .use_offset = true, - .jump_arrows = true, -}; +/* A copy of target hist_entry for perf top. */ +static struct hist_entry annotate_he; -struct annotate_browser { - struct ui_browser b; - struct rb_root entries; - struct rb_node *curr_hot; - struct disasm_line *selection; - struct disasm_line **offsets; - int nr_events; - u64 start; - int nr_asm_entries; - int nr_entries; - int max_jump_sources; - int nr_jumps; - bool searching_backwards; - u8 addr_width; - u8 jumps_width; - u8 target_width; - u8 min_addr_width; - u8 max_addr_width; - char search_bf[128]; -}; +static size_t type_hash(long key, void *ctx __maybe_unused) +{ + return key; +} -static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) +static bool type_equal(long key1, long key2, void *ctx __maybe_unused) { - return (struct browser_disasm_line *)(dl + 1); + return key1 == key2; } -static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, - void *entry) +static inline struct annotation *browser__annotation(struct ui_browser *browser) { - if (annotate_browser__opts.hide_src_code) { - struct disasm_line *dl = list_entry(entry, struct disasm_line, node); - return dl->offset == -1; - } + struct map_symbol *ms = browser->priv; + return symbol__annotation(ms->sym); +} - return false; +static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry) +{ + struct annotation_line *al = list_entry(entry, struct annotation_line, node); + return annotation_line__filter(al); } -static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, - int nr, bool current) +static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current) { - if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) + struct annotation *notes = browser__annotation(browser); + + if (current && (!browser->use_navkeypressed || browser->navkeypressed)) return HE_COLORSET_SELECTED; - if (nr == browser->max_jump_sources) + if (nr == notes->src->max_jump_sources) return HE_COLORSET_TOP; if (nr > 1) return HE_COLORSET_MEDIUM; return HE_COLORSET_NORMAL; } -static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, - int nr, bool current) +static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current) +{ + int color = ui_browser__jumps_percent_color(browser, nr, current); + return ui_browser__set_color(browser, color); +} + +static int annotate_browser__set_color(void *browser, int color) +{ + return ui_browser__set_color(browser, color); +} + +static void annotate_browser__write_graph(void *browser, int graph) { - int color = annotate_browser__jumps_percent_color(browser, nr, current); - return ui_browser__set_color(&browser->b, color); + ui_browser__write_graph(browser, graph); +} + +static void annotate_browser__set_percent_color(void *browser, double percent, bool current) +{ + ui_browser__set_percent_color(browser, percent, current); +} + +static void annotate_browser__printf(void *browser, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ui_browser__vprintf(browser, fmt, args); + va_end(args); } static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); - 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(browser, row); - bool change_color = (!annotate_browser__opts.hide_src_code && - (!current_entry || (browser->use_navkeypressed && - !browser->navkeypressed))); - int width = browser->width, printed; - int i, pcnt_width = 7 * ab->nr_events; - double percent_max = 0.0; - char bf[256]; - - for (i = 0; i < ab->nr_events; i++) { - if (bdl->percent[i] > percent_max) - percent_max = bdl->percent[i]; - } - - if (dl->offset != -1 && percent_max != 0.0) { - for (i = 0; i < ab->nr_events; i++) { - ui_browser__set_percent_color(browser, bdl->percent[i], - current_entry); - slsmg_printf("%6.2f ", bdl->percent[i]); - } - } else { - ui_browser__set_percent_color(browser, 0, current_entry); - slsmg_write_nstring(" ", pcnt_width); - } - - SLsmg_write_char(' '); + struct annotation *notes = browser__annotation(browser); + struct annotation_line *al = list_entry(entry, struct annotation_line, node); + const bool is_current_entry = ui_browser__is_current_entry(browser, row); + struct annotation_write_ops ops = { + .first_line = row == 0, + .current_entry = is_current_entry, + .change_color = (!annotate_opts.hide_src_code && + (!is_current_entry || + (browser->use_navkeypressed && + !browser->navkeypressed))), + .width = browser->width, + .obj = browser, + .set_color = annotate_browser__set_color, + .set_percent_color = annotate_browser__set_percent_color, + .set_jumps_percent_color = ui_browser__set_jumps_percent_color, + .printf = annotate_browser__printf, + .write_graph = annotate_browser__write_graph, + }; + struct annotation_print_data apd = { + .he = ab->he, + .arch = ab->arch, + .evsel = ab->evsel, + .dbg = ab->dbg, + }; /* The scroll bar isn't being used */ if (!browser->navkeypressed) - width += 1; - - if (!*dl->line) - slsmg_write_nstring(" ", width - pcnt_width); - else if (dl->offset == -1) { - printed = scnprintf(bf, sizeof(bf), "%*s ", - ab->addr_width, " "); - slsmg_write_nstring(bf, printed); - slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); - } else { - u64 addr = dl->offset; - int color = -1; - - if (!annotate_browser__opts.use_offset) - addr += ab->start; - - if (!annotate_browser__opts.use_offset) { - printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); - } else { - if (bdl->jump_sources) { - if (annotate_browser__opts.show_nr_jumps) { - int prev; - printed = scnprintf(bf, sizeof(bf), "%*d ", - ab->jumps_width, - bdl->jump_sources); - prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources, - current_entry); - slsmg_write_nstring(bf, printed); - ui_browser__set_color(browser, prev); - } - - printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", - ab->target_width, addr); - } else { - printed = scnprintf(bf, sizeof(bf), "%*s ", - ab->addr_width, " "); - } - } + ops.width += 1; - if (change_color) - color = ui_browser__set_color(browser, HE_COLORSET_ADDR); - slsmg_write_nstring(bf, printed); - if (change_color) - ui_browser__set_color(browser, color); - 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(browser, fwd ? SLSMG_DARROW_CHAR : - SLSMG_UARROW_CHAR); - SLsmg_write_char(' '); - } else if (ins__is_call(dl->ins)) { - ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); - SLsmg_write_char(' '); - } else { - slsmg_write_nstring(" ", 2); - } - } else { - if (strcmp(dl->name, "retq")) { - slsmg_write_nstring(" ", 2); - } else { - ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); - SLsmg_write_char(' '); - } - } + if (!IS_ERR_OR_NULL(ab->type_hash)) + apd.type_hash = ab->type_hash; - disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); - slsmg_write_nstring(bf, width - pcnt_width - 3 - printed); - } + annotation_line__write(al, notes, &ops, &apd); - if (current_entry) - ab->selection = dl; + if (ops.current_entry) + ab->selection = al; } -static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) +static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor) { - if (!dl || !dl->ins || !ins__is_jump(dl->ins) - || !disasm_line__has_offset(dl) - || dl->ops.target.offset >= symbol__size(sym)) - return false; + struct disasm_line *pos = list_prev_entry(cursor, al.node); + const char *name; + int diff = 1; - return true; + while (pos && pos->al.offset == -1) { + pos = list_prev_entry(pos, al.node); + if (!annotate_opts.hide_src_code) + diff++; + } + + if (!pos) + return 0; + + if (ins__is_lock(&pos->ins)) + name = pos->ops.locked.ins.name; + else + name = pos->ins.name; + + if (!name || !cursor->ins.name) + return 0; + + if (ins__is_fused(ab->arch, name, cursor->ins.name)) + return diff; + return 0; } static void annotate_browser__draw_current_jump(struct ui_browser *browser) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); - struct disasm_line *cursor = ab->selection, *target; - struct browser_disasm_line *btarget, *bcursor; + struct disasm_line *cursor = disasm_line(ab->selection); + struct annotation_line *target; unsigned int from, to; struct map_symbol *ms = ab->b.priv; struct symbol *sym = ms->sym; - u8 pcnt_width = 7; + struct annotation *notes = symbol__annotation(sym); + u8 pcnt_width = annotation__pcnt_width(notes); + u8 cntr_width = annotation__br_cntr_width(); + int width; + int diff = 0; /* PLT symbols contain external offsets */ if (strstr(sym->name, "@plt")) return; - if (!disasm_line__is_valid_jump(cursor, sym)) + if (!disasm_line__is_valid_local_jump(cursor, sym)) return; - target = ab->offsets[cursor->ops.target.offset]; - if (!target) + /* + * This first was seen with a gcc function, _cpp_lex_token, that + * has the usual jumps: + * + * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> + * + * I.e. jumps to a label inside that function (_cpp_lex_token), and + * those works, but also this kind: + * + * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> + * + * I.e. jumps to another function, outside _cpp_lex_token, which + * are not being correctly handled generating as a side effect references + * to ab->offset[] entries that are set to NULL, so to make this code + * more robust, check that here. + * + * A proper fix for will be put in place, looking at the function + * name right after the '<' token and probably treating this like a + * 'call' instruction. + */ + target = annotated_source__get_line(notes->src, cursor->ops.target.offset); + if (target == NULL) { + ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n", + cursor->ops.target.offset); return; + } - bcursor = disasm_line__browser(cursor); - btarget = disasm_line__browser(target); - - if (annotate_browser__opts.hide_src_code) { - from = bcursor->idx_asm; - to = btarget->idx_asm; + if (annotate_opts.hide_src_code) { + from = cursor->al.idx_asm; + to = target->idx_asm; } else { - from = (u64)bcursor->idx; - to = (u64)btarget->idx; + from = (u64)cursor->al.idx; + to = (u64)target->idx; } - pcnt_width *= ab->nr_events; + width = annotation__cycles_width(notes); - ui_browser__set_color(browser, HE_COLORSET_CODE); - __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, + ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); + __ui_browser__line_arrow(browser, + pcnt_width + 2 + notes->src->widths.addr + width + cntr_width, from, to); + + diff = is_fused(ab, cursor); + if (diff > 0) { + ui_browser__mark_fused(browser, + pcnt_width + 3 + notes->src->widths.addr + width + cntr_width, + from - diff, diff, to > from); + } } static unsigned int annotate_browser__refresh(struct ui_browser *browser) { - struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct annotation *notes = browser__annotation(browser); int ret = ui_browser__list_head_refresh(browser); - int pcnt_width; + int pcnt_width = annotation__pcnt_width(notes); - pcnt_width = 7 * ab->nr_events; - - if (annotate_browser__opts.jump_arrows) + if (annotate_opts.jump_arrows) annotate_browser__draw_current_jump(browser); ui_browser__set_color(browser, HE_COLORSET_NORMAL); - __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1); + __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1); return ret; } -static int disasm__cmp(struct browser_disasm_line *a, - struct browser_disasm_line *b, int nr_pcnt) +static double disasm__cmp(struct annotation_line *a, struct annotation_line *b, + int percent_type) { int i; - for (i = 0; i < nr_pcnt; i++) { - if (a->percent[i] == b->percent[i]) + for (i = 0; i < a->data_nr; i++) { + if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type]) continue; - return a->percent[i] < b->percent[i]; + return a->data[i].percent[percent_type] - + b->data[i].percent[percent_type]; } return 0; } -static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl, - int nr_events) +static void disasm_rb_tree__insert(struct annotate_browser *browser, + struct annotation_line *al) { + struct rb_root *root = &browser->entries; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - struct browser_disasm_line *l; + struct annotation_line *l; while (*p != NULL) { parent = *p; - l = rb_entry(parent, struct browser_disasm_line, rb_node); + l = rb_entry(parent, struct annotation_line, rb_node); - if (disasm__cmp(bdl, l, nr_events)) + if (disasm__cmp(al, l, annotate_opts.percent_type) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } - rb_link_node(&bdl->rb_node, parent, p); - rb_insert_color(&bdl->rb_node, root); + rb_link_node(&al->rb_node, parent, p); + rb_insert_color(&al->rb_node, root); } static void annotate_browser__set_top(struct annotate_browser *browser, - struct disasm_line *pos, u32 idx) + struct annotation_line *pos, u32 idx) { unsigned back; @@ -305,9 +310,9 @@ static void annotate_browser__set_top(struct annotate_browser *browser, browser->b.top_idx = browser->b.index = idx; while (browser->b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct disasm_line, node); + pos = list_entry(pos->node.prev, struct annotation_line, node); - if (disasm_line__filter(&browser->b, &pos->node)) + if (annotation_line__filter(pos)) continue; --browser->b.top_idx; @@ -321,147 +326,293 @@ static void annotate_browser__set_top(struct annotate_browser *browser, static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd) { - struct browser_disasm_line *bpos; - struct disasm_line *pos; - u32 idx; + struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); + u32 idx = pos->idx; - bpos = rb_entry(nd, struct browser_disasm_line, rb_node); - pos = ((struct disasm_line *)bpos) - 1; - idx = bpos->idx; - if (annotate_browser__opts.hide_src_code) - idx = bpos->idx_asm; + if (annotate_opts.hide_src_code) + idx = pos->idx_asm; annotate_browser__set_top(browser, pos, idx); browser->curr_hot = nd; } static void annotate_browser__calc_percent(struct annotate_browser *browser, - struct perf_evsel *evsel) + struct evsel *evsel) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); - struct disasm_line *pos, *next; - s64 len = symbol__size(sym); + struct disasm_line *pos; browser->entries = RB_ROOT; - pthread_mutex_lock(¬es->lock); + annotation__lock(notes); + + symbol__calc_percent(sym, evsel); - list_for_each_entry(pos, ¬es->src->source, node) { - struct browser_disasm_line *bpos = disasm_line__browser(pos); - const char *path = NULL; + list_for_each_entry(pos, ¬es->src->source, al.node) { double max_percent = 0.0; int i; - if (pos->offset == -1) { - RB_CLEAR_NODE(&bpos->rb_node); + if (pos->al.offset == -1) { + RB_CLEAR_NODE(&pos->al.rb_node); continue; } - next = disasm__get_next_ip_line(¬es->src->source, pos); + for (i = 0; i < pos->al.data_nr; i++) { + double percent; - for (i = 0; i < browser->nr_events; i++) { - bpos->percent[i] = disasm__calc_percent(notes, - evsel->idx + i, - pos->offset, - next ? next->offset : len, - &path); + percent = annotation_data__percent(&pos->al.data[i], + annotate_opts.percent_type); - if (max_percent < bpos->percent[i]) - max_percent = bpos->percent[i]; + if (max_percent < percent) + max_percent = percent; } - if (max_percent < 0.01) { - RB_CLEAR_NODE(&bpos->rb_node); + if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) { + RB_CLEAR_NODE(&pos->al.rb_node); continue; } - disasm_rb_tree__insert(&browser->entries, bpos, - browser->nr_events); + disasm_rb_tree__insert(browser, &pos->al); } - pthread_mutex_unlock(¬es->lock); + annotation__unlock(notes); browser->curr_hot = rb_last(&browser->entries); } -static bool annotate_browser__toggle_source(struct annotate_browser *browser) +static struct annotation_line *annotate_browser__find_new_asm_line( + struct annotate_browser *browser, + int idx_asm) +{ + struct annotation_line *al; + struct list_head *head = browser->b.entries; + + /* find an annotation line in the new list with the same idx_asm */ + list_for_each_entry(al, head, node) { + if (al->idx_asm == idx_asm) + return al; + } + + /* There are no asm lines */ + return NULL; +} + +static struct annotation_line *annotate_browser__find_next_asm_line( + struct annotate_browser *browser, + struct annotation_line *al) +{ + struct annotation_line *it = al; + + /* find next asm line */ + list_for_each_entry_continue(it, browser->b.entries, node) { + if (it->idx_asm >= 0) + return it; + } + + /* no asm line found forwards, try backwards */ + it = al; + list_for_each_entry_continue_reverse(it, browser->b.entries, node) { + if (it->idx_asm >= 0) + return it; + } + + /* There are no asm lines */ + return NULL; +} + +static bool annotation__has_source(struct annotation *notes) +{ + struct annotation_line *al; + bool found_asm = false; + + /* Let's skip the first non-asm lines which present regardless of source. */ + list_for_each_entry(al, ¬es->src->source, node) { + if (al->offset >= 0) { + found_asm = true; + break; + } + } + + if (found_asm) { + /* After assembly lines, any line without offset means source. */ + list_for_each_entry_continue(al, ¬es->src->source, node) { + if (al->offset == -1) + return true; + } + } + return false; +} + +static bool annotate_browser__toggle_source(struct annotate_browser *browser, + struct evsel *evsel) { - struct disasm_line *dl; - struct browser_disasm_line *bdl; + struct annotation *notes = browser__annotation(&browser->b); + struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; browser->b.seek(&browser->b, offset, SEEK_CUR); - dl = list_entry(browser->b.top, struct disasm_line, node); - bdl = disasm_line__browser(dl); + al = list_entry(browser->b.top, struct annotation_line, node); - if (annotate_browser__opts.hide_src_code) { - if (bdl->idx_asm < offset) - offset = bdl->idx; + if (!annotate_opts.annotate_src) + annotate_opts.annotate_src = true; - browser->b.nr_entries = browser->nr_entries; - annotate_browser__opts.hide_src_code = false; + /* + * It's about to get source code annotation for the first time. + * Drop the existing annotation_lines and get the new one with source. + * And then move to the original line at the same asm index. + */ + if (annotate_opts.hide_src_code && !notes->src->tried_source) { + struct map_symbol *ms = browser->b.priv; + int orig_idx_asm = al->idx_asm; + + /* annotate again with source code info */ + annotate_opts.hide_src_code = false; + annotated_source__purge(notes->src); + symbol__annotate2(ms, evsel, &browser->arch); + annotate_opts.hide_src_code = true; + + /* should be after annotated_source__purge() */ + notes->src->tried_source = true; + + if (!annotation__has_source(notes)) + ui__warning("Annotation has no source code."); + + browser->b.entries = ¬es->src->source; + al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); + if (unlikely(al == NULL)) { + al = list_first_entry(¬es->src->source, + struct annotation_line, node); + } + browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); + } + + if (annotate_opts.hide_src_code) { + if (al->idx_asm < offset) + offset = al->idx; + + browser->b.nr_entries = notes->src->nr_entries; + annotate_opts.hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = bdl->idx - offset; - browser->b.index = bdl->idx; + browser->b.top_idx = al->idx - offset; + browser->b.index = al->idx; } else { - if (bdl->idx_asm < 0) { - ui_helpline__puts("Only available for assembly lines."); - browser->b.seek(&browser->b, -offset, SEEK_CUR); - return false; + if (al->idx_asm < 0) { + /* move cursor to next asm line */ + al = annotate_browser__find_next_asm_line(browser, al); + if (!al) { + browser->b.seek(&browser->b, -offset, SEEK_CUR); + return false; + } } - if (bdl->idx_asm < offset) - offset = bdl->idx_asm; + if (al->idx_asm < offset) + offset = al->idx_asm; - browser->b.nr_entries = browser->nr_asm_entries; - annotate_browser__opts.hide_src_code = true; + browser->b.nr_entries = notes->src->nr_asm_entries; + annotate_opts.hide_src_code = true; browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = bdl->idx_asm - offset; - browser->b.index = bdl->idx_asm; + browser->b.top_idx = al->idx_asm - offset; + browser->b.index = al->idx_asm; } + if (annotate_opts.hide_src_code_on_title) + annotate_opts.hide_src_code_on_title = false; + return true; } -static void annotate_browser__init_asm_mode(struct annotate_browser *browser) +#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) + +static void annotate_browser__show_full_location(struct ui_browser *browser) { - ui_browser__reset_index(&browser->b); - browser->b.nr_entries = browser->nr_asm_entries; + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct disasm_line *cursor = disasm_line(ab->selection); + struct annotation_line *al = &cursor->al; + + if (al->offset != -1) + ui_helpline__puts("Only available for source code lines."); + else if (al->fileloc == NULL) + ui_helpline__puts("No source file location."); + else { + char help_line[SYM_TITLE_MAX_SIZE]; + sprintf (help_line, "Source file location: %s", al->fileloc); + ui_helpline__puts(help_line); + } } +static void ui_browser__init_asm_mode(struct ui_browser *browser) +{ + struct annotation *notes = browser__annotation(browser); + ui_browser__reset_index(browser); + browser->nr_entries = notes->src->nr_asm_entries; +} + +static int sym_title(struct symbol *sym, struct map *map, char *title, + size_t sz, int percent_type) +{ + return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name, + dso__long_name(map__dso(map)), + percent_type_str(percent_type), + annotate_opts.code_with_type ? "[Type]" : ""); +} + +static void annotate_browser__show_function_title(struct annotate_browser *browser) +{ + struct ui_browser *b = &browser->b; + struct map_symbol *ms = b->priv; + struct symbol *sym = ms->sym; + char title[SYM_TITLE_MAX_SIZE]; + + sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type); + + ui_browser__gotorc_title(b, 0, 0); + ui_browser__set_color(b, HE_COLORSET_ROOT); + ui_browser__write_nstring(b, title, b->width + 1); +} + +/* + * This can be called from external jumps, i.e. jumps from one function + * to another, like from the kernel's entry_SYSCALL_64 function to the + * swapgs_restore_regs_and_return_to_usermode() function. + * + * So all we check here is that dl->ops.target.sym is set, if it is, just + * go to that function and when exiting from its disassembly, come back + * to the calling function. + */ static bool annotate_browser__callq(struct annotate_browser *browser, - struct perf_evsel *evsel, + struct evsel *evsel, struct hist_browser_timer *hbt) { - struct map_symbol *ms = browser->b.priv; - struct disasm_line *dl = browser->selection; - struct symbol *sym = ms->sym; + struct map_symbol *ms = browser->b.priv, target_ms; + struct disasm_line *dl = disasm_line(browser->selection); struct annotation *notes; - struct symbol *target; - u64 ip; - if (!ins__is_call(dl->ins)) - return false; - - ip = ms->map->map_ip(ms->map, dl->ops.target.addr); - target = map__find_symbol(ms->map, ip, NULL); - if (target == NULL) { + if (!dl->ops.target.sym) { ui_helpline__puts("The called function was not found."); return true; } - notes = symbol__annotation(target); - pthread_mutex_lock(¬es->lock); + notes = symbol__annotation(dl->ops.target.sym); + annotation__lock(notes); - if (notes->src == NULL && symbol__alloc_hist(target) < 0) { - pthread_mutex_unlock(¬es->lock); + if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { + annotation__unlock(notes); ui__warning("Not enough memory for annotating '%s' symbol!\n", - target->name); + dl->ops.target.sym->name); return true; } - pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target, ms->map, evsel, hbt); - ui_browser__show_title(&browser->b, sym->name); + target_ms.maps = ms->maps; + target_ms.map = ms->map; + target_ms.sym = dl->ops.target.sym; + annotation__unlock(notes); + __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR); + + /* + * The annotate_browser above changed the title with the target function + * and now it's back to the original function. Refresh the header line + * for the original function again. + */ + annotate_browser__show_function_title(browser); return true; } @@ -469,59 +620,64 @@ 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 annotation *notes = browser__annotation(&browser->b); struct disasm_line *pos; *idx = 0; - list_for_each_entry(pos, ¬es->src->source, node) { - if (pos->offset == offset) + list_for_each_entry(pos, ¬es->src->source, al.node) { + if (pos->al.offset == offset) return pos; - if (!disasm_line__filter(&browser->b, &pos->node)) + if (!annotation_line__filter(&pos->al)) ++*idx; } return NULL; } -static bool annotate_browser__jump(struct annotate_browser *browser) +static bool annotate_browser__jump(struct annotate_browser *browser, + struct evsel *evsel, + struct hist_browser_timer *hbt) { - struct disasm_line *dl = browser->selection; + struct disasm_line *dl = disasm_line(browser->selection); + u64 offset; s64 idx; - if (!ins__is_jump(dl->ins)) + if (!ins__is_jump(&dl->ins)) return false; - dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); + if (dl->ops.target.outside) { + annotate_browser__callq(browser, evsel, hbt); + return true; + } + + offset = dl->ops.target.offset; + dl = annotate_browser__find_offset(browser, offset, &idx); if (dl == NULL) { - ui_helpline__puts("Invallid jump offset"); + ui_helpline__printf("Invalid jump offset: %" PRIx64, offset); return true; } - annotate_browser__set_top(browser, dl, idx); - + annotate_browser__set_top(browser, &dl->al, idx); + return true; } static -struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, +struct annotation_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 disasm_line *pos = browser->selection; + struct annotation *notes = browser__annotation(&browser->b); + struct annotation_line *al = browser->selection; *idx = browser->b.index; - list_for_each_entry_continue(pos, ¬es->src->source, node) { - if (disasm_line__filter(&browser->b, &pos->node)) + list_for_each_entry_continue(al, ¬es->src->source, node) { + if (annotation_line__filter(al)) continue; ++*idx; - if (pos->line && strstr(pos->line, s) != NULL) - return pos; + if (al->line && strstr(al->line, s) != NULL) + return al; } return NULL; @@ -529,38 +685,36 @@ struct disasm_line *annotate_browser__find_string(struct annotate_browser *brows static bool __annotate_browser__search(struct annotate_browser *browser) { - struct disasm_line *dl; + struct annotation_line *al; s64 idx; - dl = annotate_browser__find_string(browser, browser->search_bf, &idx); - if (dl == NULL) { + al = annotate_browser__find_string(browser, browser->search_bf, &idx); + if (al == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, dl, idx); + annotate_browser__set_top(browser, al, idx); browser->searching_backwards = false; return true; } static -struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, +struct annotation_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 disasm_line *pos = browser->selection; + struct annotation *notes = browser__annotation(&browser->b); + struct annotation_line *al = browser->selection; *idx = browser->b.index; - list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { - if (disasm_line__filter(&browser->b, &pos->node)) + list_for_each_entry_continue_reverse(al, ¬es->src->source, node) { + if (annotation_line__filter(al)) continue; --*idx; - if (pos->line && strstr(pos->line, s) != NULL) - return pos; + if (al->line && strstr(al->line, s) != NULL) + return al; } return NULL; @@ -568,16 +722,16 @@ struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browse static bool __annotate_browser__search_reverse(struct annotate_browser *browser) { - struct disasm_line *dl; + struct annotation_line *al; s64 idx; - dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); - if (dl == NULL) { + al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); + if (al == NULL) { ui_helpline__puts("String not found!"); return false; } - annotate_browser__set_top(browser, dl, idx); + annotate_browser__set_top(browser, al, idx); browser->searching_backwards = true; return true; } @@ -630,35 +784,122 @@ bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, return __annotate_browser__search_reverse(browser); } -static void annotate_browser__update_addr_width(struct annotate_browser *browser) +static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help) { - if (annotate_browser__opts.use_offset) - browser->target_width = browser->min_addr_width; - else - browser->target_width = browser->max_addr_width; + if (ui_browser__show(&browser->b, title, help) < 0) + return -1; + + annotate_browser__show_function_title(browser); + return 0; +} + +static void +switch_percent_type(struct annotation_options *opts, bool base) +{ + switch (opts->percent_type) { + case PERCENT_HITS_LOCAL: + if (base) + opts->percent_type = PERCENT_PERIOD_LOCAL; + else + opts->percent_type = PERCENT_HITS_GLOBAL; + break; + case PERCENT_HITS_GLOBAL: + if (base) + opts->percent_type = PERCENT_PERIOD_GLOBAL; + else + opts->percent_type = PERCENT_HITS_LOCAL; + break; + case PERCENT_PERIOD_LOCAL: + if (base) + opts->percent_type = PERCENT_HITS_LOCAL; + else + opts->percent_type = PERCENT_PERIOD_GLOBAL; + break; + case PERCENT_PERIOD_GLOBAL: + if (base) + opts->percent_type = PERCENT_HITS_GLOBAL; + else + opts->percent_type = PERCENT_PERIOD_LOCAL; + break; + default: + WARN_ON(1); + } +} - browser->addr_width = browser->target_width; +static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size) +{ + int printed = hists__scnprintf_title(hists, bf, size); - if (annotate_browser__opts.show_nr_jumps) - browser->addr_width += browser->jumps_width + 1; + if (!annotate_opts.hide_src_code_on_title) { + printed += scnprintf(bf + printed, size - printed, " [source: %s]", + annotate_opts.hide_src_code ? "OFF" : "On"); + } + + return printed; +} + +static void annotate_browser__debuginfo_warning(struct annotate_browser *browser) +{ + struct map_symbol *ms = browser->b.priv; + struct dso *dso = map__dso(ms->map); + + if (browser->dbg == NULL && annotate_opts.code_with_type && + !dso__debuginfo_warned(dso)) { + ui__warning("DWARF debuginfo not found.\n\n" + "Data-type in this DSO will not be displayed.\n" + "Please make sure to have debug information."); + dso__set_debuginfo_warned(dso); + } +} + +static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser) +{ + struct annotation_line *al = NULL; + + if (browser->curr_hot) + al = rb_entry(browser->curr_hot, struct annotation_line, rb_node); + + return al ? al->offset : 0; +} + +static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err) +{ + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + struct dso *dso = map__dso(ms->map); + char msg[BUFSIZ]; + + dso__set_annotate_warned(dso); + symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); + ui__error("Couldn't annotate %s:\n%s", sym->name, msg); } static int annotate_browser__run(struct annotate_browser *browser, - struct perf_evsel *evsel, + struct evsel *evsel, struct hist_browser_timer *hbt) { struct rb_node *nd = NULL; + struct hists *hists = evsel__hists(evsel); struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(ms->sym); const char *help = "Press 'h' for help on key bindings"; int delay_secs = hbt ? hbt->refresh : 0; + char *br_cntr_text = NULL; + char title[256]; int key; - if (ui_browser__show(&browser->b, sym->name, help) < 0) + annotate__scnprintf_title(hists, title, sizeof(title)); + if (annotate_browser__show(browser, title, help) < 0) return -1; annotate_browser__calc_percent(browser, evsel); + if (browser->selection != NULL) { + browser->curr_hot = &browser->selection->rb_node; + browser->b.use_navkeypressed = false; + } + if (browser->curr_hot) { annotate_browser__set_rb_top(browser, browser->curr_hot); browser->b.navkeypressed = false; @@ -666,6 +907,10 @@ static int annotate_browser__run(struct annotate_browser *browser, nd = browser->curr_hot; + annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); + + annotate_browser__debuginfo_warning(browser); + while (1) { key = ui_browser__run(&browser->b, delay_secs); @@ -685,8 +930,11 @@ static int annotate_browser__run(struct annotate_browser *browser, if (hbt) hbt->timer(hbt->arg); - if (delay_secs != 0) - symbol__annotate_decay_histogram(sym, evsel->idx); + if (delay_secs != 0) { + symbol__annotate_decay_histogram(sym, evsel); + annotate__scnprintf_title(hists, title, sizeof(title)); + annotate_browser__show(browser, title, help); + } continue; case K_TAB: if (nd != NULL) { @@ -697,11 +945,11 @@ static int annotate_browser__run(struct annotate_browser *browser, nd = browser->curr_hot; break; case K_UNTAB: - if (nd != NULL) + if (nd != NULL) { nd = rb_next(nd); if (nd == NULL) nd = rb_first(&browser->entries); - else + } else nd = browser->curr_hot; break; case K_F1: @@ -709,41 +957,76 @@ static int annotate_browser__run(struct annotate_browser *browser, ui_browser__help_window(&browser->b, "UP/DOWN/PGUP\n" "PGDN/SPACE Navigate\n" + "</> Move to prev/next symbol\n" "q/ESC/CTRL+C Exit\n\n" - "-> Go to target\n" - "<- Exit\n" - "H Cycle thru hottest instructions\n" + "ENTER Go to target\n" + "H Go to hottest instruction\n" + "TAB/shift+TAB Cycle thru hottest instructions\n" "j Toggle showing jump to target arrows\n" "J Toggle showing number of jump sources on targets\n" "n Search next string\n" "o Toggle disassembler output/simplified view\n" + "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n" "s Toggle source code view\n" + "t Circulate percent, total period, samples view\n" + "c Show min/max cycle\n" "/ Search string\n" + "k Toggle line numbers\n" + "l Show full source file location\n" + "P Print to [symbol_name].annotation file.\n" "r Run available scripts\n" - "? Search previous string\n"); + "p Toggle percent type [local/global]\n" + "b Toggle percent base [period/hits]\n" + "B Branch counter abbr list (Optional)\n" + "? Search string backwards\n" + "f Toggle showing offsets to full address\n" + "T Toggle data type display\n"); continue; case 'r': - { - script_browse(NULL); - continue; - } + script_browse(NULL, NULL); + annotate_browser__show(browser, title, help); + continue; + case 'k': + annotate_opts.show_linenr = !annotate_opts.show_linenr; + continue; + case 'l': + annotate_browser__show_full_location (&browser->b); + continue; case 'H': nd = browser->curr_hot; break; - case 's': - if (annotate_browser__toggle_source(browser)) + case 's': { + struct annotation_line *al = NULL; + s64 offset = annotate_browser__curr_hot_offset(browser); + + if (annotate_browser__toggle_source(browser, evsel)) ui_helpline__puts(help); + + /* Update the annotation browser's rb_tree, and reset the nd */ + annotate_browser__calc_percent(browser, evsel); + /* Try to find the same asm line as before */ + al = annotated_source__get_line(notes->src, offset); + browser->curr_hot = al ? &al->rb_node : NULL; + nd = browser->curr_hot; + + annotate__scnprintf_title(hists, title, sizeof(title)); + annotate_browser__show(browser, title, help); continue; + } case 'o': - annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset; - annotate_browser__update_addr_width(browser); + annotate_opts.use_offset = !annotate_opts.use_offset; + annotation__update_column_widths(notes); + continue; + case 'O': + if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL) + annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL; continue; case 'j': - annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows; + annotate_opts.jump_arrows = !annotate_opts.jump_arrows; continue; case 'J': - annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps; - annotate_browser__update_addr_width(browser); + annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps; + annotation__update_column_widths(notes); continue; case '/': if (annotate_browser__search(browser, delay_secs)) { @@ -769,31 +1052,86 @@ show_help: browser->b.height, browser->b.index, browser->b.top_idx, - browser->nr_asm_entries); + notes->src->nr_asm_entries); } continue; case K_ENTER: case K_RIGHT: + { + struct disasm_line *dl = disasm_line(browser->selection); + if (browser->selection == NULL) ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); else if (browser->selection->offset == -1) ui_helpline__puts("Actions are only available for assembly lines."); - else if (!browser->selection->ins) { - if (strcmp(browser->selection->name, "retq")) - goto show_sup_ins; + else if (!dl->ins.ops) + goto show_sup_ins; + else if (ins__is_ret(&dl->ins)) goto out; - } else if (!(annotate_browser__jump(browser) || + else if (!(annotate_browser__jump(browser, evsel, hbt) || annotate_browser__callq(browser, evsel, hbt))) { show_sup_ins: - ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); + ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions."); } continue; + } + case 'P': + map_symbol__annotation_dump(ms, evsel, browser->he); + continue; + case 't': + if (symbol_conf.show_total_period) { + symbol_conf.show_total_period = false; + symbol_conf.show_nr_samples = true; + } else if (symbol_conf.show_nr_samples) + symbol_conf.show_nr_samples = false; + else + symbol_conf.show_total_period = true; + annotation__update_column_widths(notes); + continue; + case 'c': + if (annotate_opts.show_minmax_cycle) + annotate_opts.show_minmax_cycle = false; + else + annotate_opts.show_minmax_cycle = true; + annotation__update_column_widths(notes); + continue; + case 'p': + case 'b': + switch_percent_type(&annotate_opts, key == 'b'); + annotate__scnprintf_title(hists, title, sizeof(title)); + annotate_browser__show(browser, title, help); + continue; + case 'B': + if (br_cntr_text) + ui_browser__help_window(&browser->b, br_cntr_text); + else { + ui_browser__help_window(&browser->b, + "\n The branch counter is not available.\n"); + } + continue; + case 'f': + annotation__toggle_full_addr(notes, ms); + continue; + case 'T': + annotate_opts.code_with_type ^= 1; + if (browser->dbg == NULL) + browser->dbg = dso__debuginfo(map__dso(ms->map)); + if (browser->type_hash == NULL) { + browser->type_hash = hashmap__new(type_hash, type_equal, + /*ctx=*/NULL); + } + annotate_browser__show(browser, title, help); + annotate_browser__debuginfo_warning(browser); + continue; case K_LEFT: + case '<': + case '>': case K_ESC: case 'q': case CTRL('c'): goto out; default: + ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions"); continue; } @@ -802,208 +1140,130 @@ show_sup_ins: } out: ui_browser__hide(&browser->b); + free(br_cntr_text); return key; } -int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, - struct hist_browser_timer *hbt) +int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel, + struct hist_browser_timer *hbt, u64 al_addr) { - return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt); -} - -static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, - size_t size) -{ - u64 offset; - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - - /* PLT symbols contain external offsets */ - if (strstr(sym->name, "@plt")) - return; - - for (offset = 0; offset < size; ++offset) { - struct disasm_line *dl = browser->offsets[offset], *dlt; - struct browser_disasm_line *bdlt; - - if (!disasm_line__is_valid_jump(dl, sym)) - continue; + /* reset abort key so that it can get Ctrl-C as a key */ + SLang_reset_tty(); + SLang_init_tty(0, 0, 0); + SLtty_set_suspend_state(true); - 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); - if (++bdlt->jump_sources > browser->max_jump_sources) - browser->max_jump_sources = bdlt->jump_sources; - - ++browser->nr_jumps; - } - + return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr); } -static inline int width_jumps(int n) +int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms, + struct evsel *evsel, + struct hist_browser_timer *hbt, u64 al_addr) { - if (n >= 100) - return 5; - if (n / 10) - return 2; - return 1; -} - -int symbol__tui_annotate(struct symbol *sym, struct map *map, - struct perf_evsel *evsel, - struct hist_browser_timer *hbt) -{ - struct disasm_line *pos, *n; - struct annotation *notes; - size_t size; - struct map_symbol ms = { - .map = map, - .sym = sym, - }; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); struct annotate_browser browser = { .b = { .refresh = annotate_browser__refresh, .seek = ui_browser__list_head_seek, .write = annotate_browser__write, .filter = disasm_line__filter, - .priv = &ms, + .extra_title_lines = 1, /* for hists__scnprintf_title() */ + .priv = ms, .use_navkeypressed = true, }, + .he = he, + .evsel = evsel, }; - int ret = -1; - int nr_pcnt = 1; - size_t sizeof_bdl = sizeof(struct browser_disasm_line); + struct dso *dso; + int ret = -1, err; + int not_annotated = list_empty(¬es->src->source); if (sym == NULL) return -1; - size = symbol__size(sym); - - if (map->dso->annotate_warned) + dso = map__dso(ms->map); + if (dso__annotate_warned(dso)) return -1; - browser.offsets = zalloc(size * sizeof(struct disasm_line *)); - if (browser.offsets == NULL) { - ui__error("Not enough memory!"); - return -1; - } + if (not_annotated || !sym->annotate2) { + err = symbol__annotate2(ms, evsel, &browser.arch); + if (err) { + annotate_browser__symbol_annotate_error(&browser, err); + return -1; + } - if (perf_evsel__is_group_event(evsel)) { - nr_pcnt = evsel->nr_members; - sizeof_bdl += sizeof(double) * (nr_pcnt - 1); + if (!annotate_opts.hide_src_code) { + notes->src->tried_source = true; + if (!annotation__has_source(notes)) + ui__warning("Annotation has no source code."); + } + } else { + err = evsel__get_arch(evsel, &browser.arch); + if (err) { + annotate_browser__symbol_annotate_error(&browser, err); + return -1; + } } - if (symbol__annotate(sym, map, sizeof_bdl) < 0) { - ui__error("%s", ui_helpline__last_msg); - goto out_free_offsets; - } + /* Copy necessary information when it's called from perf top */ + if (hbt != NULL && he != &annotate_he) { + annotate_he.hists = he->hists; + annotate_he.thread = thread__get(he->thread); + annotate_he.cpumode = he->cpumode; + map_symbol__copy(&annotate_he.ms, ms); - ui_helpline__push("Press <- or ESC to exit"); + browser.he = &annotate_he; + } - notes = symbol__annotation(sym); - browser.start = map__rip_2objdump(map, sym->start); + ui_helpline__push("Press ESC to exit"); - list_for_each_entry(pos, ¬es->src->source, node) { - struct browser_disasm_line *bpos; - size_t line_len = strlen(pos->line); + if (annotate_opts.code_with_type) { + browser.dbg = dso__debuginfo(dso); + browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL); + } - if (browser.b.width < line_len) - browser.b.width = line_len; - 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.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); - browser.max_addr_width = hex_width(sym->end); - browser.jumps_width = width_jumps(browser.max_jump_sources); - browser.nr_events = nr_pcnt; - browser.b.nr_entries = browser.nr_entries; - browser.b.entries = ¬es->src->source, + browser.b.width = notes->src->widths.max_line_len; + browser.b.nr_entries = notes->src->nr_entries; + browser.b.entries = ¬es->src->source; browser.b.width += 18; /* Percentage */ - if (annotate_browser__opts.hide_src_code) - annotate_browser__init_asm_mode(&browser); + if (annotate_opts.hide_src_code) + ui_browser__init_asm_mode(&browser.b); - annotate_browser__update_addr_width(&browser); + /* + * If al_addr is set, it means that there should be a line + * intentionally selected, not based on the percentages + * which caculated by the event sampling. In this case, we + * convey this information into the browser selection, where + * the selection in other cases should be empty. + */ + if (al_addr != NO_ADDR) { + struct annotation_line *al = annotated_source__get_line(notes->src, + al_addr - sym->start); - ret = annotate_browser__run(&browser, evsel, hbt); - list_for_each_entry_safe(pos, n, ¬es->src->source, node) { - list_del(&pos->node); - disasm_line__free(pos); + browser.selection = al; } -out_free_offsets: - free(browser.offsets); - return ret; -} - -#define ANNOTATE_CFG(n) \ - { .name = #n, .value = &annotate_browser__opts.n, } - -/* - * Keep the entries sorted, they are bsearch'ed - */ -static struct annotate_config { - const char *name; - bool *value; -} annotate__configs[] = { - ANNOTATE_CFG(hide_src_code), - ANNOTATE_CFG(jump_arrows), - ANNOTATE_CFG(show_nr_jumps), - ANNOTATE_CFG(use_offset), -}; - -#undef ANNOTATE_CFG - -static int annotate_config__cmp(const void *name, const void *cfgp) -{ - const struct annotate_config *cfg = cfgp; - - return strcmp(name, cfg->name); -} + ret = annotate_browser__run(&browser, evsel, hbt); -static int annotate__config(const char *var, const char *value, - void *data __maybe_unused) -{ - struct annotate_config *cfg; - const char *name; + debuginfo__delete(browser.dbg); - if (prefixcmp(var, "annotate.") != 0) - return 0; + if (!IS_ERR_OR_NULL(browser.type_hash)) { + struct hashmap_entry *cur; + size_t bkt; - name = var + 9; - cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), - sizeof(struct annotate_config), annotate_config__cmp); + hashmap__for_each_entry(browser.type_hash, cur, bkt) + zfree(&cur->pvalue); + hashmap__free(browser.type_hash); + } - if (cfg == NULL) - return -1; + if (not_annotated && !notes->src->tried_source) + annotated_source__purge(notes->src); - *cfg->value = perf_config_bool(name, value); - return 0; -} + if (hbt != NULL && he != &annotate_he) { + thread__zput(annotate_he.thread); + map_symbol__exit(&annotate_he.ms); + } -void annotate_browser__init(void) -{ - perf_config(annotate__config, NULL); + return ret; } |
