1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include "../../util/evsel.h"
12 #include "../../util/evlist.h"
15 #include <linux/kernel.h>
16 #include <linux/string.h>
17 #include <sys/ttydefaults.h>
19 struct disasm_line_samples {
21 struct sym_hist_entry he;
26 struct annotate_browser {
28 struct rb_root entries;
29 struct rb_node *curr_hot;
30 struct annotation_line *selection;
32 bool searching_backwards;
36 static inline struct annotation *browser__annotation(struct ui_browser *browser)
38 struct map_symbol *ms = browser->priv;
39 return symbol__annotation(ms->sym);
42 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
44 struct annotation *notes = browser__annotation(browser);
45 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
46 return annotation_line__filter(al, notes);
49 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
51 struct annotation *notes = browser__annotation(browser);
53 if (current && (!browser->use_navkeypressed || browser->navkeypressed))
54 return HE_COLORSET_SELECTED;
55 if (nr == notes->max_jump_sources)
56 return HE_COLORSET_TOP;
58 return HE_COLORSET_MEDIUM;
59 return HE_COLORSET_NORMAL;
62 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
64 int color = ui_browser__jumps_percent_color(browser, nr, current);
65 return ui_browser__set_color(browser, color);
68 static int annotate_browser__set_color(void *browser, int color)
70 return ui_browser__set_color(browser, color);
73 static void annotate_browser__write_graph(void *browser, int graph)
75 ui_browser__write_graph(browser, graph);
78 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
80 ui_browser__set_percent_color(browser, percent, current);
83 static void annotate_browser__printf(void *browser, const char *fmt, ...)
88 ui_browser__vprintf(browser, fmt, args);
92 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
94 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
95 struct annotation *notes = browser__annotation(browser);
96 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
97 struct annotation_write_ops ops = {
98 .first_line = row == 0,
99 .current_entry = ui_browser__is_current_entry(browser, row),
100 .change_color = (!notes->options->hide_src_code &&
101 (!ops.current_entry ||
102 (browser->use_navkeypressed &&
103 !browser->navkeypressed))),
104 .width = browser->width,
106 .set_color = annotate_browser__set_color,
107 .set_percent_color = annotate_browser__set_percent_color,
108 .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
109 .printf = annotate_browser__printf,
110 .write_graph = annotate_browser__write_graph,
113 /* The scroll bar isn't being used */
114 if (!browser->navkeypressed)
117 annotation_line__write(al, notes, &ops);
119 if (ops.current_entry)
123 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
125 struct disasm_line *pos = list_prev_entry(cursor, al.node);
131 if (ins__is_lock(&pos->ins))
132 name = pos->ops.locked.ins.name;
134 name = pos->ins.name;
136 if (!name || !cursor->ins.name)
139 return ins__is_fused(ab->arch, name, cursor->ins.name);
142 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
144 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
145 struct disasm_line *cursor = disasm_line(ab->selection);
146 struct annotation_line *target;
147 unsigned int from, to;
148 struct map_symbol *ms = ab->b.priv;
149 struct symbol *sym = ms->sym;
150 struct annotation *notes = symbol__annotation(sym);
151 u8 pcnt_width = annotation__pcnt_width(notes);
154 /* PLT symbols contain external offsets */
155 if (strstr(sym->name, "@plt"))
158 if (!disasm_line__is_valid_local_jump(cursor, sym))
162 * This first was seen with a gcc function, _cpp_lex_token, that
163 * has the usual jumps:
165 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
167 * I.e. jumps to a label inside that function (_cpp_lex_token), and
168 * those works, but also this kind:
170 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
172 * I.e. jumps to another function, outside _cpp_lex_token, which
173 * are not being correctly handled generating as a side effect references
174 * to ab->offset[] entries that are set to NULL, so to make this code
175 * more robust, check that here.
177 * A proper fix for will be put in place, looking at the function
178 * name right after the '<' token and probably treating this like a
179 * 'call' instruction.
181 target = notes->offsets[cursor->ops.target.offset];
182 if (target == NULL) {
183 ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
184 cursor->ops.target.offset);
188 if (notes->options->hide_src_code) {
189 from = cursor->al.idx_asm;
190 to = target->idx_asm;
192 from = (u64)cursor->al.idx;
193 to = (u64)target->idx;
196 width = annotation__cycles_width(notes);
198 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
199 __ui_browser__line_arrow(browser,
200 pcnt_width + 2 + notes->widths.addr + width,
203 if (is_fused(ab, cursor)) {
204 ui_browser__mark_fused(browser,
205 pcnt_width + 3 + notes->widths.addr + width,
207 to > from ? true : false);
211 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
213 struct annotation *notes = browser__annotation(browser);
214 int ret = ui_browser__list_head_refresh(browser);
215 int pcnt_width = annotation__pcnt_width(notes);
217 if (notes->options->jump_arrows)
218 annotate_browser__draw_current_jump(browser);
220 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
221 __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
225 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
229 for (i = 0; i < a->samples_nr; i++) {
230 if (a->samples[i].percent == b->samples[i].percent)
232 return a->samples[i].percent < b->samples[i].percent;
237 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
239 struct rb_node **p = &root->rb_node;
240 struct rb_node *parent = NULL;
241 struct annotation_line *l;
245 l = rb_entry(parent, struct annotation_line, rb_node);
247 if (disasm__cmp(al, l))
252 rb_link_node(&al->rb_node, parent, p);
253 rb_insert_color(&al->rb_node, root);
256 static void annotate_browser__set_top(struct annotate_browser *browser,
257 struct annotation_line *pos, u32 idx)
259 struct annotation *notes = browser__annotation(&browser->b);
262 ui_browser__refresh_dimensions(&browser->b);
263 back = browser->b.height / 2;
264 browser->b.top_idx = browser->b.index = idx;
266 while (browser->b.top_idx != 0 && back != 0) {
267 pos = list_entry(pos->node.prev, struct annotation_line, node);
269 if (annotation_line__filter(pos, notes))
272 --browser->b.top_idx;
276 browser->b.top = pos;
277 browser->b.navkeypressed = true;
280 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
283 struct annotation *notes = browser__annotation(&browser->b);
284 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
287 if (notes->options->hide_src_code)
289 annotate_browser__set_top(browser, pos, idx);
290 browser->curr_hot = nd;
293 static void annotate_browser__calc_percent(struct annotate_browser *browser,
294 struct perf_evsel *evsel)
296 struct map_symbol *ms = browser->b.priv;
297 struct symbol *sym = ms->sym;
298 struct annotation *notes = symbol__annotation(sym);
299 struct disasm_line *pos;
301 browser->entries = RB_ROOT;
303 pthread_mutex_lock(¬es->lock);
305 symbol__calc_percent(sym, evsel);
307 list_for_each_entry(pos, ¬es->src->source, al.node) {
308 double max_percent = 0.0;
311 if (pos->al.offset == -1) {
312 RB_CLEAR_NODE(&pos->al.rb_node);
316 for (i = 0; i < pos->al.samples_nr; i++) {
317 struct annotation_data *sample = &pos->al.samples[i];
319 if (max_percent < sample->percent)
320 max_percent = sample->percent;
323 if (max_percent < 0.01 && pos->al.ipc == 0) {
324 RB_CLEAR_NODE(&pos->al.rb_node);
327 disasm_rb_tree__insert(&browser->entries, &pos->al);
329 pthread_mutex_unlock(¬es->lock);
331 browser->curr_hot = rb_last(&browser->entries);
334 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
336 struct annotation *notes = browser__annotation(&browser->b);
337 struct annotation_line *al;
338 off_t offset = browser->b.index - browser->b.top_idx;
340 browser->b.seek(&browser->b, offset, SEEK_CUR);
341 al = list_entry(browser->b.top, struct annotation_line, node);
343 if (notes->options->hide_src_code) {
344 if (al->idx_asm < offset)
347 browser->b.nr_entries = notes->nr_entries;
348 notes->options->hide_src_code = false;
349 browser->b.seek(&browser->b, -offset, SEEK_CUR);
350 browser->b.top_idx = al->idx - offset;
351 browser->b.index = al->idx;
353 if (al->idx_asm < 0) {
354 ui_helpline__puts("Only available for assembly lines.");
355 browser->b.seek(&browser->b, -offset, SEEK_CUR);
359 if (al->idx_asm < offset)
360 offset = al->idx_asm;
362 browser->b.nr_entries = notes->nr_asm_entries;
363 notes->options->hide_src_code = true;
364 browser->b.seek(&browser->b, -offset, SEEK_CUR);
365 browser->b.top_idx = al->idx_asm - offset;
366 browser->b.index = al->idx_asm;
372 static void ui_browser__init_asm_mode(struct ui_browser *browser)
374 struct annotation *notes = browser__annotation(browser);
375 ui_browser__reset_index(browser);
376 browser->nr_entries = notes->nr_asm_entries;
379 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
381 static int sym_title(struct symbol *sym, struct map *map, char *title,
384 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
388 * This can be called from external jumps, i.e. jumps from one functon
389 * to another, like from the kernel's entry_SYSCALL_64 function to the
390 * swapgs_restore_regs_and_return_to_usermode() function.
392 * So all we check here is that dl->ops.target.sym is set, if it is, just
393 * go to that function and when exiting from its disassembly, come back
394 * to the calling function.
396 static bool annotate_browser__callq(struct annotate_browser *browser,
397 struct perf_evsel *evsel,
398 struct hist_browser_timer *hbt)
400 struct map_symbol *ms = browser->b.priv;
401 struct disasm_line *dl = disasm_line(browser->selection);
402 struct annotation *notes;
403 char title[SYM_TITLE_MAX_SIZE];
405 if (!dl->ops.target.sym) {
406 ui_helpline__puts("The called function was not found.");
410 notes = symbol__annotation(dl->ops.target.sym);
411 pthread_mutex_lock(¬es->lock);
413 if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
414 pthread_mutex_unlock(¬es->lock);
415 ui__warning("Not enough memory for annotating '%s' symbol!\n",
416 dl->ops.target.sym->name);
420 pthread_mutex_unlock(¬es->lock);
421 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
422 sym_title(ms->sym, ms->map, title, sizeof(title));
423 ui_browser__show_title(&browser->b, title);
428 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
429 s64 offset, s64 *idx)
431 struct annotation *notes = browser__annotation(&browser->b);
432 struct disasm_line *pos;
435 list_for_each_entry(pos, ¬es->src->source, al.node) {
436 if (pos->al.offset == offset)
438 if (!annotation_line__filter(&pos->al, notes))
445 static bool annotate_browser__jump(struct annotate_browser *browser,
446 struct perf_evsel *evsel,
447 struct hist_browser_timer *hbt)
449 struct disasm_line *dl = disasm_line(browser->selection);
453 if (!ins__is_jump(&dl->ins))
456 if (dl->ops.target.outside) {
457 annotate_browser__callq(browser, evsel, hbt);
461 offset = dl->ops.target.offset;
462 dl = annotate_browser__find_offset(browser, offset, &idx);
464 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
468 annotate_browser__set_top(browser, &dl->al, idx);
474 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
477 struct annotation *notes = browser__annotation(&browser->b);
478 struct annotation_line *al = browser->selection;
480 *idx = browser->b.index;
481 list_for_each_entry_continue(al, ¬es->src->source, node) {
482 if (annotation_line__filter(al, notes))
487 if (al->line && strstr(al->line, s) != NULL)
494 static bool __annotate_browser__search(struct annotate_browser *browser)
496 struct annotation_line *al;
499 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
501 ui_helpline__puts("String not found!");
505 annotate_browser__set_top(browser, al, idx);
506 browser->searching_backwards = false;
511 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
514 struct annotation *notes = browser__annotation(&browser->b);
515 struct annotation_line *al = browser->selection;
517 *idx = browser->b.index;
518 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
519 if (annotation_line__filter(al, notes))
524 if (al->line && strstr(al->line, s) != NULL)
531 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
533 struct annotation_line *al;
536 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
538 ui_helpline__puts("String not found!");
542 annotate_browser__set_top(browser, al, idx);
543 browser->searching_backwards = true;
547 static bool annotate_browser__search_window(struct annotate_browser *browser,
550 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
551 "ENTER: OK, ESC: Cancel",
552 delay_secs * 2) != K_ENTER ||
553 !*browser->search_bf)
559 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
561 if (annotate_browser__search_window(browser, delay_secs))
562 return __annotate_browser__search(browser);
567 static bool annotate_browser__continue_search(struct annotate_browser *browser,
570 if (!*browser->search_bf)
571 return annotate_browser__search(browser, delay_secs);
573 return __annotate_browser__search(browser);
576 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
579 if (annotate_browser__search_window(browser, delay_secs))
580 return __annotate_browser__search_reverse(browser);
586 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
589 if (!*browser->search_bf)
590 return annotate_browser__search_reverse(browser, delay_secs);
592 return __annotate_browser__search_reverse(browser);
595 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
597 struct map_symbol *ms = browser->priv;
598 struct symbol *sym = ms->sym;
599 char symbol_dso[SYM_TITLE_MAX_SIZE];
601 if (ui_browser__show(browser, title, help) < 0)
604 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso));
606 ui_browser__gotorc_title(browser, 0, 0);
607 ui_browser__set_color(browser, HE_COLORSET_ROOT);
608 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
612 static int annotate_browser__run(struct annotate_browser *browser,
613 struct perf_evsel *evsel,
614 struct hist_browser_timer *hbt)
616 struct rb_node *nd = NULL;
617 struct hists *hists = evsel__hists(evsel);
618 struct map_symbol *ms = browser->b.priv;
619 struct symbol *sym = ms->sym;
620 struct annotation *notes = symbol__annotation(ms->sym);
621 const char *help = "Press 'h' for help on key bindings";
622 int delay_secs = hbt ? hbt->refresh : 0;
626 annotation__scnprintf_samples_period(notes, title, sizeof(title), evsel);
628 if (annotate_browser__show(&browser->b, title, help) < 0)
631 annotate_browser__calc_percent(browser, evsel);
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, evsel);
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);
659 if (delay_secs != 0) {
660 symbol__annotate_decay_histogram(sym, evsel->idx);
661 hists__scnprintf_title(hists, title, sizeof(title));
662 annotate_browser__show(&browser->b, title, help);
669 nd = rb_last(&browser->entries);
671 nd = browser->curr_hot;
677 nd = rb_first(&browser->entries);
679 nd = browser->curr_hot;
683 ui_browser__help_window(&browser->b,
685 "PGDN/SPACE Navigate\n"
686 "q/ESC/CTRL+C Exit\n\n"
687 "ENTER Go to target\n"
689 "H Go to hottest instruction\n"
690 "TAB/shift+TAB Cycle thru hottest instructions\n"
691 "j Toggle showing jump to target arrows\n"
692 "J Toggle showing number of jump sources on targets\n"
693 "n Search next string\n"
694 "o Toggle disassembler output/simplified view\n"
695 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
696 "s Toggle source code view\n"
697 "t Circulate percent, total period, samples view\n"
699 "k Toggle line numbers\n"
700 "P Print to [symbol_name].annotation file.\n"
701 "r Run available scripts\n"
702 "? Search string backwards\n");
710 notes->options->show_linenr = !notes->options->show_linenr;
713 nd = browser->curr_hot;
716 if (annotate_browser__toggle_source(browser))
717 ui_helpline__puts(help);
720 notes->options->use_offset = !notes->options->use_offset;
721 annotation__update_column_widths(notes);
724 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
725 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
728 notes->options->jump_arrows = !notes->options->jump_arrows;
731 notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
732 annotation__update_column_widths(notes);
735 if (annotate_browser__search(browser, delay_secs)) {
737 ui_helpline__puts(help);
741 if (browser->searching_backwards ?
742 annotate_browser__continue_search_reverse(browser, delay_secs) :
743 annotate_browser__continue_search(browser, delay_secs))
747 if (annotate_browser__search_reverse(browser, delay_secs))
753 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
754 seq++, browser->b.nr_entries,
758 notes->nr_asm_entries);
764 struct disasm_line *dl = disasm_line(browser->selection);
766 if (browser->selection == NULL)
767 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
768 else if (browser->selection->offset == -1)
769 ui_helpline__puts("Actions are only available for assembly lines.");
770 else if (!dl->ins.ops)
772 else if (ins__is_ret(&dl->ins))
774 else if (!(annotate_browser__jump(browser, evsel, hbt) ||
775 annotate_browser__callq(browser, evsel, hbt))) {
777 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
782 map_symbol__annotation_dump(ms, evsel);
785 if (notes->options->show_total_period) {
786 notes->options->show_total_period = false;
787 notes->options->show_nr_samples = true;
788 } else if (notes->options->show_nr_samples)
789 notes->options->show_nr_samples = false;
791 notes->options->show_total_period = true;
792 annotation__update_column_widths(notes);
804 annotate_browser__set_rb_top(browser, nd);
807 ui_browser__hide(&browser->b);
811 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
812 struct hist_browser_timer *hbt)
814 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
817 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
818 struct hist_browser_timer *hbt)
820 /* reset abort key so that it can get Ctrl-C as a key */
822 SLang_init_tty(0, 0, 0);
824 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
827 int symbol__tui_annotate(struct symbol *sym, struct map *map,
828 struct perf_evsel *evsel,
829 struct hist_browser_timer *hbt)
831 struct annotation *notes = symbol__annotation(sym);
832 struct map_symbol ms = {
836 struct annotate_browser browser = {
838 .refresh = annotate_browser__refresh,
839 .seek = ui_browser__list_head_seek,
840 .write = annotate_browser__write,
841 .filter = disasm_line__filter,
842 .extra_title_lines = 1, /* for hists__scnprintf_title() */
844 .use_navkeypressed = true,
852 if (map->dso->annotate_warned)
855 err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
858 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
859 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
860 goto out_free_offsets;
863 ui_helpline__push("Press ESC to exit");
865 browser.b.width = notes->max_line_len;
866 browser.b.nr_entries = notes->nr_entries;
867 browser.b.entries = ¬es->src->source,
868 browser.b.width += 18; /* Percentage */
870 if (notes->options->hide_src_code)
871 ui_browser__init_asm_mode(&browser.b);
873 ret = annotate_browser__run(&browser, evsel, hbt);
875 annotated_source__purge(notes->src);
878 zfree(¬es->offsets);