1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../libslang.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
14 struct browser_disasm_line {
15 struct rb_node rb_node;
22 static struct annotate_browser_opt {
27 } annotate_browser__opts = {
32 struct annotate_browser {
34 struct rb_root entries;
35 struct rb_node *curr_hot;
36 struct disasm_line *selection;
37 struct disasm_line **offsets;
43 bool searching_backwards;
52 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
54 return (struct browser_disasm_line *)(dl + 1);
57 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
60 if (annotate_browser__opts.hide_src_code) {
61 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
62 return dl->offset == -1;
68 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
71 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
72 return HE_COLORSET_SELECTED;
73 if (nr == browser->max_jump_sources)
74 return HE_COLORSET_TOP;
76 return HE_COLORSET_MEDIUM;
77 return HE_COLORSET_NORMAL;
80 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
83 int color = annotate_browser__jumps_percent_color(browser, nr, current);
84 return ui_browser__set_color(&browser->b, color);
87 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
89 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
90 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
91 struct browser_disasm_line *bdl = disasm_line__browser(dl);
92 bool current_entry = ui_browser__is_current_entry(browser, row);
93 bool change_color = (!annotate_browser__opts.hide_src_code &&
94 (!current_entry || (browser->use_navkeypressed &&
95 !browser->navkeypressed)));
96 int width = browser->width, printed;
99 if (dl->offset != -1 && bdl->percent != 0.0) {
100 ui_browser__set_percent_color(browser, bdl->percent, current_entry);
101 slsmg_printf("%6.2f ", bdl->percent);
103 ui_browser__set_percent_color(browser, 0, current_entry);
104 slsmg_write_nstring(" ", 7);
107 SLsmg_write_char(' ');
109 /* The scroll bar isn't being used */
110 if (!browser->navkeypressed)
114 slsmg_write_nstring(" ", width - 7);
115 else if (dl->offset == -1) {
116 printed = scnprintf(bf, sizeof(bf), "%*s ",
117 ab->addr_width, " ");
118 slsmg_write_nstring(bf, printed);
119 slsmg_write_nstring(dl->line, width - printed - 6);
121 u64 addr = dl->offset;
124 if (!annotate_browser__opts.use_offset)
127 if (!annotate_browser__opts.use_offset) {
128 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
130 if (bdl->jump_sources) {
131 if (annotate_browser__opts.show_nr_jumps) {
133 printed = scnprintf(bf, sizeof(bf), "%*d ",
136 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
138 slsmg_write_nstring(bf, printed);
139 ui_browser__set_color(browser, prev);
142 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
143 ab->target_width, addr);
145 printed = scnprintf(bf, sizeof(bf), "%*s ",
146 ab->addr_width, " ");
151 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
152 slsmg_write_nstring(bf, printed);
154 ui_browser__set_color(browser, color);
155 if (dl->ins && dl->ins->ops->scnprintf) {
156 if (ins__is_jump(dl->ins)) {
157 bool fwd = dl->ops.target.offset > (u64)dl->offset;
159 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
161 SLsmg_write_char(' ');
162 } else if (ins__is_call(dl->ins)) {
163 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
164 SLsmg_write_char(' ');
166 slsmg_write_nstring(" ", 2);
169 if (strcmp(dl->name, "retq")) {
170 slsmg_write_nstring(" ", 2);
172 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
173 SLsmg_write_char(' ');
177 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
178 slsmg_write_nstring(bf, width - 10 - printed);
185 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
187 if (!dl || !dl->ins || !ins__is_jump(dl->ins)
188 || !disasm_line__has_offset(dl)
189 || dl->ops.target.offset >= symbol__size(sym))
195 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
197 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
198 struct disasm_line *cursor = ab->selection, *target;
199 struct browser_disasm_line *btarget, *bcursor;
200 unsigned int from, to;
201 struct map_symbol *ms = ab->b.priv;
202 struct symbol *sym = ms->sym;
204 /* PLT symbols contain external offsets */
205 if (strstr(sym->name, "@plt"))
208 if (!disasm_line__is_valid_jump(cursor, sym))
211 target = ab->offsets[cursor->ops.target.offset];
215 bcursor = disasm_line__browser(cursor);
216 btarget = disasm_line__browser(target);
218 if (annotate_browser__opts.hide_src_code) {
219 from = bcursor->idx_asm;
220 to = btarget->idx_asm;
222 from = (u64)bcursor->idx;
223 to = (u64)btarget->idx;
226 ui_browser__set_color(browser, HE_COLORSET_CODE);
227 __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
230 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
232 int ret = ui_browser__list_head_refresh(browser);
234 if (annotate_browser__opts.jump_arrows)
235 annotate_browser__draw_current_jump(browser);
237 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
238 __ui_browser__vline(browser, 7, 0, browser->height - 1);
242 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
244 double percent = 0.0;
246 if (dl->offset != -1) {
247 int len = sym->end - sym->start;
248 unsigned int hits = 0;
249 struct annotation *notes = symbol__annotation(sym);
250 struct source_line *src_line = notes->src->lines;
251 struct sym_hist *h = annotation__histogram(notes, evidx);
252 s64 offset = dl->offset;
253 struct disasm_line *next;
255 next = disasm__get_next_ip_line(¬es->src->source, dl);
256 while (offset < (s64)len &&
257 (next == NULL || offset < next->offset)) {
259 percent += src_line[offset].percent;
261 hits += h->addr[offset];
266 * If the percentage wasn't already calculated in
267 * symbol__get_source_line, do it now:
269 if (src_line == NULL && h->sum)
270 percent = 100.0 * hits / h->sum;
276 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
278 struct rb_node **p = &root->rb_node;
279 struct rb_node *parent = NULL;
280 struct browser_disasm_line *l;
284 l = rb_entry(parent, struct browser_disasm_line, rb_node);
285 if (bdl->percent < l->percent)
290 rb_link_node(&bdl->rb_node, parent, p);
291 rb_insert_color(&bdl->rb_node, root);
294 static void annotate_browser__set_top(struct annotate_browser *browser,
295 struct disasm_line *pos, u32 idx)
299 ui_browser__refresh_dimensions(&browser->b);
300 back = browser->b.height / 2;
301 browser->b.top_idx = browser->b.index = idx;
303 while (browser->b.top_idx != 0 && back != 0) {
304 pos = list_entry(pos->node.prev, struct disasm_line, node);
306 if (disasm_line__filter(&browser->b, &pos->node))
309 --browser->b.top_idx;
313 browser->b.top = pos;
314 browser->b.navkeypressed = true;
317 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
320 struct browser_disasm_line *bpos;
321 struct disasm_line *pos;
324 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
325 pos = ((struct disasm_line *)bpos) - 1;
327 if (annotate_browser__opts.hide_src_code)
329 annotate_browser__set_top(browser, pos, idx);
330 browser->curr_hot = nd;
333 static void annotate_browser__calc_percent(struct annotate_browser *browser,
336 struct map_symbol *ms = browser->b.priv;
337 struct symbol *sym = ms->sym;
338 struct annotation *notes = symbol__annotation(sym);
339 struct disasm_line *pos;
341 browser->entries = RB_ROOT;
343 pthread_mutex_lock(¬es->lock);
345 list_for_each_entry(pos, ¬es->src->source, node) {
346 struct browser_disasm_line *bpos = disasm_line__browser(pos);
347 bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
348 if (bpos->percent < 0.01) {
349 RB_CLEAR_NODE(&bpos->rb_node);
352 disasm_rb_tree__insert(&browser->entries, bpos);
354 pthread_mutex_unlock(¬es->lock);
356 browser->curr_hot = rb_last(&browser->entries);
359 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
361 struct disasm_line *dl;
362 struct browser_disasm_line *bdl;
363 off_t offset = browser->b.index - browser->b.top_idx;
365 browser->b.seek(&browser->b, offset, SEEK_CUR);
366 dl = list_entry(browser->b.top, struct disasm_line, node);
367 bdl = disasm_line__browser(dl);
369 if (annotate_browser__opts.hide_src_code) {
370 if (bdl->idx_asm < offset)
373 browser->b.nr_entries = browser->nr_entries;
374 annotate_browser__opts.hide_src_code = false;
375 browser->b.seek(&browser->b, -offset, SEEK_CUR);
376 browser->b.top_idx = bdl->idx - offset;
377 browser->b.index = bdl->idx;
379 if (bdl->idx_asm < 0) {
380 ui_helpline__puts("Only available for assembly lines.");
381 browser->b.seek(&browser->b, -offset, SEEK_CUR);
385 if (bdl->idx_asm < offset)
386 offset = bdl->idx_asm;
388 browser->b.nr_entries = browser->nr_asm_entries;
389 annotate_browser__opts.hide_src_code = true;
390 browser->b.seek(&browser->b, -offset, SEEK_CUR);
391 browser->b.top_idx = bdl->idx_asm - offset;
392 browser->b.index = bdl->idx_asm;
398 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
400 ui_browser__reset_index(&browser->b);
401 browser->b.nr_entries = browser->nr_asm_entries;
404 static bool annotate_browser__callq(struct annotate_browser *browser, int evidx,
405 struct hist_browser_timer *hbt)
407 struct map_symbol *ms = browser->b.priv;
408 struct disasm_line *dl = browser->selection;
409 struct symbol *sym = ms->sym;
410 struct annotation *notes;
411 struct symbol *target;
414 if (!ins__is_call(dl->ins))
417 ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
418 target = map__find_symbol(ms->map, ip, NULL);
419 if (target == NULL) {
420 ui_helpline__puts("The called function was not found.");
424 notes = symbol__annotation(target);
425 pthread_mutex_lock(¬es->lock);
427 if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
428 pthread_mutex_unlock(¬es->lock);
429 ui__warning("Not enough memory for annotating '%s' symbol!\n",
434 pthread_mutex_unlock(¬es->lock);
435 symbol__tui_annotate(target, ms->map, evidx, hbt);
436 ui_browser__show_title(&browser->b, sym->name);
441 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
442 s64 offset, s64 *idx)
444 struct map_symbol *ms = browser->b.priv;
445 struct symbol *sym = ms->sym;
446 struct annotation *notes = symbol__annotation(sym);
447 struct disasm_line *pos;
450 list_for_each_entry(pos, ¬es->src->source, node) {
451 if (pos->offset == offset)
453 if (!disasm_line__filter(&browser->b, &pos->node))
460 static bool annotate_browser__jump(struct annotate_browser *browser)
462 struct disasm_line *dl = browser->selection;
465 if (!ins__is_jump(dl->ins))
468 dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
470 ui_helpline__puts("Invallid jump offset");
474 annotate_browser__set_top(browser, dl, idx);
480 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
483 struct map_symbol *ms = browser->b.priv;
484 struct symbol *sym = ms->sym;
485 struct annotation *notes = symbol__annotation(sym);
486 struct disasm_line *pos = browser->selection;
488 *idx = browser->b.index;
489 list_for_each_entry_continue(pos, ¬es->src->source, node) {
490 if (disasm_line__filter(&browser->b, &pos->node))
495 if (pos->line && strstr(pos->line, s) != NULL)
502 static bool __annotate_browser__search(struct annotate_browser *browser)
504 struct disasm_line *dl;
507 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
509 ui_helpline__puts("String not found!");
513 annotate_browser__set_top(browser, dl, idx);
514 browser->searching_backwards = false;
519 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
522 struct map_symbol *ms = browser->b.priv;
523 struct symbol *sym = ms->sym;
524 struct annotation *notes = symbol__annotation(sym);
525 struct disasm_line *pos = browser->selection;
527 *idx = browser->b.index;
528 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
529 if (disasm_line__filter(&browser->b, &pos->node))
534 if (pos->line && strstr(pos->line, s) != NULL)
541 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
543 struct disasm_line *dl;
546 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
548 ui_helpline__puts("String not found!");
552 annotate_browser__set_top(browser, dl, idx);
553 browser->searching_backwards = true;
557 static bool annotate_browser__search_window(struct annotate_browser *browser,
560 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
561 "ENTER: OK, ESC: Cancel",
562 delay_secs * 2) != K_ENTER ||
563 !*browser->search_bf)
569 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
571 if (annotate_browser__search_window(browser, delay_secs))
572 return __annotate_browser__search(browser);
577 static bool annotate_browser__continue_search(struct annotate_browser *browser,
580 if (!*browser->search_bf)
581 return annotate_browser__search(browser, delay_secs);
583 return __annotate_browser__search(browser);
586 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
589 if (annotate_browser__search_window(browser, delay_secs))
590 return __annotate_browser__search_reverse(browser);
596 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
599 if (!*browser->search_bf)
600 return annotate_browser__search_reverse(browser, delay_secs);
602 return __annotate_browser__search_reverse(browser);
605 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
607 if (annotate_browser__opts.use_offset)
608 browser->target_width = browser->min_addr_width;
610 browser->target_width = browser->max_addr_width;
612 browser->addr_width = browser->target_width;
614 if (annotate_browser__opts.show_nr_jumps)
615 browser->addr_width += browser->jumps_width + 1;
618 static int annotate_browser__run(struct annotate_browser *browser, int evidx,
619 struct hist_browser_timer *hbt)
621 struct rb_node *nd = NULL;
622 struct map_symbol *ms = browser->b.priv;
623 struct symbol *sym = ms->sym;
624 const char *help = "Press 'h' for help on key bindings";
625 int delay_secs = hbt ? hbt->refresh : 0;
628 if (ui_browser__show(&browser->b, sym->name, help) < 0)
631 annotate_browser__calc_percent(browser, evidx);
633 if (browser->curr_hot) {
634 annotate_browser__set_rb_top(browser, browser->curr_hot);
635 browser->b.navkeypressed = false;
638 nd = browser->curr_hot;
641 key = ui_browser__run(&browser->b, delay_secs);
643 if (delay_secs != 0) {
644 annotate_browser__calc_percent(browser, evidx);
646 * Current line focus got out of the list of most active
647 * lines, NULL it so that if TAB|UNTAB is pressed, we
648 * move to curr_hot (current hottest line).
650 if (nd != NULL && RB_EMPTY_NODE(nd))
657 hbt->timer(hbt->arg);
660 symbol__annotate_decay_histogram(sym, evidx);
666 nd = rb_last(&browser->entries);
668 nd = browser->curr_hot;
674 nd = rb_first(&browser->entries);
676 nd = browser->curr_hot;
680 ui_browser__help_window(&browser->b,
682 "PGDN/SPACE Navigate\n"
683 "q/ESC/CTRL+C Exit\n\n"
686 "H Cycle thru hottest instructions\n"
687 "j Toggle showing jump to target arrows\n"
688 "J Toggle showing number of jump sources on targets\n"
689 "n Search next string\n"
690 "o Toggle disassembler output/simplified view\n"
691 "s Toggle source code view\n"
693 "r Run available scripts\n"
694 "? Search previous string\n");
702 nd = browser->curr_hot;
705 if (annotate_browser__toggle_source(browser))
706 ui_helpline__puts(help);
709 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
710 annotate_browser__update_addr_width(browser);
713 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
716 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
717 annotate_browser__update_addr_width(browser);
720 if (annotate_browser__search(browser, delay_secs)) {
722 ui_helpline__puts(help);
726 if (browser->searching_backwards ?
727 annotate_browser__continue_search_reverse(browser, delay_secs) :
728 annotate_browser__continue_search(browser, delay_secs))
732 if (annotate_browser__search_reverse(browser, delay_secs))
738 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
739 seq++, browser->b.nr_entries,
743 browser->nr_asm_entries);
748 if (browser->selection == NULL)
749 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
750 else if (browser->selection->offset == -1)
751 ui_helpline__puts("Actions are only available for assembly lines.");
752 else if (!browser->selection->ins) {
753 if (strcmp(browser->selection->name, "retq"))
756 } else if (!(annotate_browser__jump(browser) ||
757 annotate_browser__callq(browser, evidx, hbt))) {
759 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
772 annotate_browser__set_rb_top(browser, nd);
775 ui_browser__hide(&browser->b);
779 int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
780 struct hist_browser_timer *hbt)
782 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt);
785 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
789 struct map_symbol *ms = browser->b.priv;
790 struct symbol *sym = ms->sym;
792 /* PLT symbols contain external offsets */
793 if (strstr(sym->name, "@plt"))
796 for (offset = 0; offset < size; ++offset) {
797 struct disasm_line *dl = browser->offsets[offset], *dlt;
798 struct browser_disasm_line *bdlt;
800 if (!disasm_line__is_valid_jump(dl, sym))
803 dlt = browser->offsets[dl->ops.target.offset];
805 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
806 * have to adjust to the previous offset?
811 bdlt = disasm_line__browser(dlt);
812 if (++bdlt->jump_sources > browser->max_jump_sources)
813 browser->max_jump_sources = bdlt->jump_sources;
820 static inline int width_jumps(int n)
829 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
830 struct hist_browser_timer *hbt)
832 struct disasm_line *pos, *n;
833 struct annotation *notes;
835 struct map_symbol ms = {
839 struct annotate_browser browser = {
841 .refresh = annotate_browser__refresh,
842 .seek = ui_browser__list_head_seek,
843 .write = annotate_browser__write,
844 .filter = disasm_line__filter,
846 .use_navkeypressed = true,
854 size = symbol__size(sym);
856 if (map->dso->annotate_warned)
859 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
860 if (browser.offsets == NULL) {
861 ui__error("Not enough memory!");
865 if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
866 ui__error("%s", ui_helpline__last_msg);
867 goto out_free_offsets;
870 ui_helpline__push("Press <- or ESC to exit");
872 notes = symbol__annotation(sym);
873 browser.start = map__rip_2objdump(map, sym->start);
875 list_for_each_entry(pos, ¬es->src->source, node) {
876 struct browser_disasm_line *bpos;
877 size_t line_len = strlen(pos->line);
879 if (browser.b.width < line_len)
880 browser.b.width = line_len;
881 bpos = disasm_line__browser(pos);
882 bpos->idx = browser.nr_entries++;
883 if (pos->offset != -1) {
884 bpos->idx_asm = browser.nr_asm_entries++;
886 * FIXME: short term bandaid to cope with assembly
887 * routines that comes with labels in the same column
888 * as the address in objdump, sigh.
890 * E.g. copy_user_generic_unrolled
892 if (pos->offset < (s64)size)
893 browser.offsets[pos->offset] = pos;
898 annotate_browser__mark_jump_targets(&browser, size);
900 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
901 browser.max_addr_width = hex_width(sym->end);
902 browser.jumps_width = width_jumps(browser.max_jump_sources);
903 browser.b.nr_entries = browser.nr_entries;
904 browser.b.entries = ¬es->src->source,
905 browser.b.width += 18; /* Percentage */
907 if (annotate_browser__opts.hide_src_code)
908 annotate_browser__init_asm_mode(&browser);
910 annotate_browser__update_addr_width(&browser);
912 ret = annotate_browser__run(&browser, evidx, hbt);
913 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
914 list_del(&pos->node);
915 disasm_line__free(pos);
919 free(browser.offsets);
923 #define ANNOTATE_CFG(n) \
924 { .name = #n, .value = &annotate_browser__opts.n, }
927 * Keep the entries sorted, they are bsearch'ed
929 static struct annotate__config {
932 } annotate__configs[] = {
933 ANNOTATE_CFG(hide_src_code),
934 ANNOTATE_CFG(jump_arrows),
935 ANNOTATE_CFG(show_nr_jumps),
936 ANNOTATE_CFG(use_offset),
941 static int annotate_config__cmp(const void *name, const void *cfgp)
943 const struct annotate__config *cfg = cfgp;
945 return strcmp(name, cfg->name);
948 static int annotate__config(const char *var, const char *value,
949 void *data __maybe_unused)
951 struct annotate__config *cfg;
954 if (prefixcmp(var, "annotate.") != 0)
958 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
959 sizeof(struct annotate__config), annotate_config__cmp);
964 *cfg->value = perf_config_bool(name, value);
968 void annotate_browser__init(void)
970 perf_config(annotate__config, NULL);