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 struct annotate_browser {
24 struct rb_root entries;
25 struct rb_node *curr_hot;
26 struct disasm_line *selection;
27 struct disasm_line **offsets;
33 bool searching_backwards;
38 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
40 return (struct browser_disasm_line *)(dl + 1);
43 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
45 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
47 if (ab->hide_src_code) {
48 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
49 return dl->offset == -1;
55 static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
57 struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
58 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
59 struct browser_disasm_line *bdl = disasm_line__browser(dl);
60 bool current_entry = ui_browser__is_current_entry(self, row);
61 bool change_color = (!ab->hide_src_code &&
62 (!current_entry || (self->use_navkeypressed &&
63 !self->navkeypressed)));
64 int width = self->width;
66 if (dl->offset != -1) {
67 ui_browser__set_percent_color(self, bdl->percent, current_entry);
68 slsmg_printf(" %7.2f ", bdl->percent);
70 ui_browser__set_percent_color(self, 0, current_entry);
71 slsmg_write_nstring(" ", 9);
74 SLsmg_set_char_set(1);
75 SLsmg_write_char(SLSMG_VLINE_CHAR);
76 SLsmg_set_char_set(0);
77 SLsmg_write_char(' ');
79 /* The scroll bar isn't being used */
80 if (!self->navkeypressed)
83 if (dl->offset != -1 && change_color)
84 ui_browser__set_color(self, HE_COLORSET_CODE);
87 slsmg_write_nstring(" ", width - 10);
88 else if (dl->offset == -1)
89 slsmg_write_nstring(dl->line, width - 10);
92 u64 addr = dl->offset;
93 int printed, color = -1;
98 if (!ab->use_offset) {
99 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ":", addr);
101 if (bdl->jump_target) {
102 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ":",
103 ab->offset_width, addr);
105 printed = scnprintf(bf, sizeof(bf), "%*s ",
106 ab->offset_width, " ");
111 color = ui_browser__set_color(self, HE_COLORSET_ADDR);
112 slsmg_write_nstring(bf, printed);
114 ui_browser__set_color(self, color);
115 if (dl->ins && dl->ins->ops->scnprintf) {
116 dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
118 slsmg_write_nstring(" ", 2);
121 scnprintf(bf, sizeof(bf), " %-6.6s %s", dl->name, dl->ops.raw);
123 slsmg_write_nstring(bf, width - 10 - printed);
130 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
132 double percent = 0.0;
134 if (dl->offset != -1) {
135 int len = sym->end - sym->start;
136 unsigned int hits = 0;
137 struct annotation *notes = symbol__annotation(sym);
138 struct source_line *src_line = notes->src->lines;
139 struct sym_hist *h = annotation__histogram(notes, evidx);
140 s64 offset = dl->offset;
141 struct disasm_line *next;
143 next = disasm__get_next_ip_line(¬es->src->source, dl);
144 while (offset < (s64)len &&
145 (next == NULL || offset < next->offset)) {
147 percent += src_line[offset].percent;
149 hits += h->addr[offset];
154 * If the percentage wasn't already calculated in
155 * symbol__get_source_line, do it now:
157 if (src_line == NULL && h->sum)
158 percent = 100.0 * hits / h->sum;
164 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
166 struct rb_node **p = &root->rb_node;
167 struct rb_node *parent = NULL;
168 struct browser_disasm_line *l;
172 l = rb_entry(parent, struct browser_disasm_line, rb_node);
173 if (bdl->percent < l->percent)
178 rb_link_node(&bdl->rb_node, parent, p);
179 rb_insert_color(&bdl->rb_node, root);
182 static void annotate_browser__set_top(struct annotate_browser *self,
183 struct disasm_line *pos, u32 idx)
187 ui_browser__refresh_dimensions(&self->b);
188 back = self->b.height / 2;
189 self->b.top_idx = self->b.index = idx;
191 while (self->b.top_idx != 0 && back != 0) {
192 pos = list_entry(pos->node.prev, struct disasm_line, node);
194 if (disasm_line__filter(&self->b, &pos->node))
202 self->b.navkeypressed = true;
205 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
208 struct browser_disasm_line *bpos;
209 struct disasm_line *pos;
211 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
212 pos = ((struct disasm_line *)bpos) - 1;
213 annotate_browser__set_top(browser, pos, bpos->idx);
214 browser->curr_hot = nd;
217 static void annotate_browser__calc_percent(struct annotate_browser *browser,
220 struct map_symbol *ms = browser->b.priv;
221 struct symbol *sym = ms->sym;
222 struct annotation *notes = symbol__annotation(sym);
223 struct disasm_line *pos;
225 browser->entries = RB_ROOT;
227 pthread_mutex_lock(¬es->lock);
229 list_for_each_entry(pos, ¬es->src->source, node) {
230 struct browser_disasm_line *bpos = disasm_line__browser(pos);
231 bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
232 if (bpos->percent < 0.01) {
233 RB_CLEAR_NODE(&bpos->rb_node);
236 disasm_rb_tree__insert(&browser->entries, bpos);
238 pthread_mutex_unlock(¬es->lock);
240 browser->curr_hot = rb_last(&browser->entries);
243 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
245 struct disasm_line *dl;
246 struct browser_disasm_line *bdl;
247 off_t offset = browser->b.index - browser->b.top_idx;
249 browser->b.seek(&browser->b, offset, SEEK_CUR);
250 dl = list_entry(browser->b.top, struct disasm_line, node);
251 bdl = disasm_line__browser(dl);
253 if (browser->hide_src_code) {
254 if (bdl->idx_asm < offset)
257 browser->b.nr_entries = browser->nr_entries;
258 browser->hide_src_code = false;
259 browser->b.seek(&browser->b, -offset, SEEK_CUR);
260 browser->b.top_idx = bdl->idx - offset;
261 browser->b.index = bdl->idx;
263 if (bdl->idx_asm < 0) {
264 ui_helpline__puts("Only available for assembly lines.");
265 browser->b.seek(&browser->b, -offset, SEEK_CUR);
269 if (bdl->idx_asm < offset)
270 offset = bdl->idx_asm;
272 browser->b.nr_entries = browser->nr_asm_entries;
273 browser->hide_src_code = true;
274 browser->b.seek(&browser->b, -offset, SEEK_CUR);
275 browser->b.top_idx = bdl->idx_asm - offset;
276 browser->b.index = bdl->idx_asm;
282 static bool annotate_browser__callq(struct annotate_browser *browser,
283 int evidx, void (*timer)(void *arg),
284 void *arg, int delay_secs)
286 struct map_symbol *ms = browser->b.priv;
287 struct disasm_line *dl = browser->selection;
288 struct symbol *sym = ms->sym;
289 struct annotation *notes;
290 struct symbol *target;
293 if (!ins__is_call(dl->ins))
296 ip = ms->map->map_ip(ms->map, dl->ops.target);
297 target = map__find_symbol(ms->map, ip, NULL);
298 if (target == NULL) {
299 ui_helpline__puts("The called function was not found.");
303 notes = symbol__annotation(target);
304 pthread_mutex_lock(¬es->lock);
306 if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
307 pthread_mutex_unlock(¬es->lock);
308 ui__warning("Not enough memory for annotating '%s' symbol!\n",
313 pthread_mutex_unlock(¬es->lock);
314 symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
315 ui_browser__show_title(&browser->b, sym->name);
320 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
321 s64 offset, s64 *idx)
323 struct map_symbol *ms = browser->b.priv;
324 struct symbol *sym = ms->sym;
325 struct annotation *notes = symbol__annotation(sym);
326 struct disasm_line *pos;
329 list_for_each_entry(pos, ¬es->src->source, node) {
330 if (pos->offset == offset)
332 if (!disasm_line__filter(&browser->b, &pos->node))
339 static bool annotate_browser__jump(struct annotate_browser *browser)
341 struct disasm_line *dl = browser->selection;
344 if (!ins__is_jump(dl->ins))
347 dl = annotate_browser__find_offset(browser, dl->ops.target, &idx);
349 ui_helpline__puts("Invallid jump offset");
353 annotate_browser__set_top(browser, dl, idx);
359 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
362 struct map_symbol *ms = browser->b.priv;
363 struct symbol *sym = ms->sym;
364 struct annotation *notes = symbol__annotation(sym);
365 struct disasm_line *pos = browser->selection;
367 *idx = browser->b.index;
368 list_for_each_entry_continue(pos, ¬es->src->source, node) {
369 if (disasm_line__filter(&browser->b, &pos->node))
374 if (pos->line && strstr(pos->line, s) != NULL)
381 static bool __annotate_browser__search(struct annotate_browser *browser)
383 struct disasm_line *dl;
386 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
388 ui_helpline__puts("String not found!");
392 annotate_browser__set_top(browser, dl, idx);
393 browser->searching_backwards = false;
398 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
401 struct map_symbol *ms = browser->b.priv;
402 struct symbol *sym = ms->sym;
403 struct annotation *notes = symbol__annotation(sym);
404 struct disasm_line *pos = browser->selection;
406 *idx = browser->b.index;
407 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
408 if (disasm_line__filter(&browser->b, &pos->node))
413 if (pos->line && strstr(pos->line, s) != NULL)
420 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
422 struct disasm_line *dl;
425 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
427 ui_helpline__puts("String not found!");
431 annotate_browser__set_top(browser, dl, idx);
432 browser->searching_backwards = true;
436 static bool annotate_browser__search_window(struct annotate_browser *browser,
439 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
440 "ENTER: OK, ESC: Cancel",
441 delay_secs * 2) != K_ENTER ||
442 !*browser->search_bf)
448 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
450 if (annotate_browser__search_window(browser, delay_secs))
451 return __annotate_browser__search(browser);
456 static bool annotate_browser__continue_search(struct annotate_browser *browser,
459 if (!*browser->search_bf)
460 return annotate_browser__search(browser, delay_secs);
462 return __annotate_browser__search(browser);
465 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
468 if (annotate_browser__search_window(browser, delay_secs))
469 return __annotate_browser__search_reverse(browser);
475 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
478 if (!*browser->search_bf)
479 return annotate_browser__search_reverse(browser, delay_secs);
481 return __annotate_browser__search_reverse(browser);
484 static int annotate_browser__run(struct annotate_browser *self, int evidx,
485 void(*timer)(void *arg),
486 void *arg, int delay_secs)
488 struct rb_node *nd = NULL;
489 struct map_symbol *ms = self->b.priv;
490 struct symbol *sym = ms->sym;
491 const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
492 "H: Go to hottest line, ->/ENTER: Line action, "
493 "O: Toggle offset view, "
494 "S: Toggle source code view";
497 if (ui_browser__show(&self->b, sym->name, help) < 0)
500 annotate_browser__calc_percent(self, evidx);
502 if (self->curr_hot) {
503 annotate_browser__set_rb_top(self, self->curr_hot);
504 self->b.navkeypressed = false;
510 key = ui_browser__run(&self->b, delay_secs);
512 if (delay_secs != 0) {
513 annotate_browser__calc_percent(self, evidx);
515 * Current line focus got out of the list of most active
516 * lines, NULL it so that if TAB|UNTAB is pressed, we
517 * move to curr_hot (current hottest line).
519 if (nd != NULL && RB_EMPTY_NODE(nd))
529 symbol__annotate_decay_histogram(sym, evidx);
535 nd = rb_last(&self->entries);
543 nd = rb_first(&self->entries);
553 if (annotate_browser__toggle_source(self))
554 ui_helpline__puts(help);
558 self->use_offset = !self->use_offset;
561 if (annotate_browser__search(self, delay_secs)) {
563 ui_helpline__puts(help);
567 if (self->searching_backwards ?
568 annotate_browser__continue_search_reverse(self, delay_secs) :
569 annotate_browser__continue_search(self, delay_secs))
573 if (annotate_browser__search_reverse(self, delay_secs))
578 if (self->selection == NULL)
579 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
580 else if (self->selection->offset == -1)
581 ui_helpline__puts("Actions are only available for assembly lines.");
582 else if (!self->selection->ins ||
583 !(annotate_browser__jump(self) ||
584 annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
585 ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
597 annotate_browser__set_rb_top(self, nd);
600 ui_browser__hide(&self->b);
604 int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
605 void(*timer)(void *arg), void *arg, int delay_secs)
607 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
608 timer, arg, delay_secs);
611 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
616 for (offset = 0; offset < size; ++offset) {
617 struct disasm_line *dl = browser->offsets[offset], *dlt;
618 struct browser_disasm_line *bdlt;
620 if (!dl || !dl->ins || !ins__is_jump(dl->ins))
623 if (dl->ops.target >= size) {
624 ui__error("jump to after symbol!\n"
625 "size: %zx, jump target: %" PRIx64,
626 size, dl->ops.target);
630 dlt = browser->offsets[dl->ops.target];
631 bdlt = disasm_line__browser(dlt);
632 bdlt->jump_target = true;
637 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
638 void(*timer)(void *arg), void *arg,
641 struct disasm_line *pos, *n;
642 struct annotation *notes;
643 const size_t size = symbol__size(sym);
644 struct map_symbol ms = {
648 struct annotate_browser browser = {
650 .refresh = ui_browser__list_head_refresh,
651 .seek = ui_browser__list_head_seek,
652 .write = annotate_browser__write,
653 .filter = disasm_line__filter,
655 .use_navkeypressed = true,
664 if (map->dso->annotate_warned)
667 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
668 if (browser.offsets == NULL) {
669 ui__error("Not enough memory!");
673 if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
674 ui__error("%s", ui_helpline__last_msg);
675 goto out_free_offsets;
678 ui_helpline__push("Press <- or ESC to exit");
680 notes = symbol__annotation(sym);
681 browser.start = map__rip_2objdump(map, sym->start);
683 list_for_each_entry(pos, ¬es->src->source, node) {
684 struct browser_disasm_line *bpos;
685 size_t line_len = strlen(pos->line);
687 if (browser.b.width < line_len)
688 browser.b.width = line_len;
689 bpos = disasm_line__browser(pos);
690 bpos->idx = browser.nr_entries++;
691 if (pos->offset != -1) {
692 bpos->idx_asm = browser.nr_asm_entries++;
693 browser.offsets[pos->offset] = pos;
698 annotate_browser__mark_jump_targets(&browser, size);
700 browser.offset_width = hex_width(size);
701 browser.b.nr_entries = browser.nr_entries;
702 browser.b.entries = ¬es->src->source,
703 browser.b.width += 18; /* Percentage */
704 ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
705 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
706 list_del(&pos->node);
707 disasm_line__free(pos);
711 free(browser.offsets);