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 struct annotation_options *opts;
33 bool searching_backwards;
37 static inline struct annotation *browser__annotation(struct ui_browser *browser)
39 struct map_symbol *ms = browser->priv;
40 return symbol__annotation(ms->sym);
43 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
45 struct annotation *notes = browser__annotation(browser);
46 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
47 return annotation_line__filter(al, notes);
50 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
52 struct annotation *notes = browser__annotation(browser);
54 if (current && (!browser->use_navkeypressed || browser->navkeypressed))
55 return HE_COLORSET_SELECTED;
56 if (nr == notes->max_jump_sources)
57 return HE_COLORSET_TOP;
59 return HE_COLORSET_MEDIUM;
60 return HE_COLORSET_NORMAL;
63 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
65 int color = ui_browser__jumps_percent_color(browser, nr, current);
66 return ui_browser__set_color(browser, color);
69 static int annotate_browser__set_color(void *browser, int color)
71 return ui_browser__set_color(browser, color);
74 static void annotate_browser__write_graph(void *browser, int graph)
76 ui_browser__write_graph(browser, graph);
79 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
81 ui_browser__set_percent_color(browser, percent, current);
84 static void annotate_browser__printf(void *browser, const char *fmt, ...)
89 ui_browser__vprintf(browser, fmt, args);
93 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
95 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
96 struct annotation *notes = browser__annotation(browser);
97 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
98 struct annotation_write_ops ops = {
99 .first_line = row == 0,
100 .current_entry = ui_browser__is_current_entry(browser, row),
101 .change_color = (!notes->options->hide_src_code &&
102 (!ops.current_entry ||
103 (browser->use_navkeypressed &&
104 !browser->navkeypressed))),
105 .width = browser->width,
107 .set_color = annotate_browser__set_color,
108 .set_percent_color = annotate_browser__set_percent_color,
109 .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
110 .printf = annotate_browser__printf,
111 .write_graph = annotate_browser__write_graph,
114 /* The scroll bar isn't being used */
115 if (!browser->navkeypressed)
118 annotation_line__write(al, notes, &ops);
120 if (ops.current_entry)
124 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
126 struct disasm_line *pos = list_prev_entry(cursor, al.node);
132 if (ins__is_lock(&pos->ins))
133 name = pos->ops.locked.ins.name;
135 name = pos->ins.name;
137 if (!name || !cursor->ins.name)
140 return ins__is_fused(ab->arch, name, cursor->ins.name);
143 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
145 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
146 struct disasm_line *cursor = disasm_line(ab->selection);
147 struct annotation_line *target;
148 unsigned int from, to;
149 struct map_symbol *ms = ab->b.priv;
150 struct symbol *sym = ms->sym;
151 struct annotation *notes = symbol__annotation(sym);
152 u8 pcnt_width = annotation__pcnt_width(notes);
155 /* PLT symbols contain external offsets */
156 if (strstr(sym->name, "@plt"))
159 if (!disasm_line__is_valid_local_jump(cursor, sym))
163 * This first was seen with a gcc function, _cpp_lex_token, that
164 * has the usual jumps:
166 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
168 * I.e. jumps to a label inside that function (_cpp_lex_token), and
169 * those works, but also this kind:
171 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
173 * I.e. jumps to another function, outside _cpp_lex_token, which
174 * are not being correctly handled generating as a side effect references
175 * to ab->offset[] entries that are set to NULL, so to make this code
176 * more robust, check that here.
178 * A proper fix for will be put in place, looking at the function
179 * name right after the '<' token and probably treating this like a
180 * 'call' instruction.
182 target = notes->offsets[cursor->ops.target.offset];
183 if (target == NULL) {
184 ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
185 cursor->ops.target.offset);
189 if (notes->options->hide_src_code) {
190 from = cursor->al.idx_asm;
191 to = target->idx_asm;
193 from = (u64)cursor->al.idx;
194 to = (u64)target->idx;
197 width = annotation__cycles_width(notes);
199 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
200 __ui_browser__line_arrow(browser,
201 pcnt_width + 2 + notes->widths.addr + width,
204 if (is_fused(ab, cursor)) {
205 ui_browser__mark_fused(browser,
206 pcnt_width + 3 + notes->widths.addr + width,
208 to > from ? true : false);
212 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
214 struct annotation *notes = browser__annotation(browser);
215 int ret = ui_browser__list_head_refresh(browser);
216 int pcnt_width = annotation__pcnt_width(notes);
218 if (notes->options->jump_arrows)
219 annotate_browser__draw_current_jump(browser);
221 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
222 __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
226 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
230 for (i = 0; i < a->samples_nr; i++) {
231 if (a->samples[i].percent == b->samples[i].percent)
233 return a->samples[i].percent < b->samples[i].percent;
238 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
240 struct rb_node **p = &root->rb_node;
241 struct rb_node *parent = NULL;
242 struct annotation_line *l;
246 l = rb_entry(parent, struct annotation_line, rb_node);
248 if (disasm__cmp(al, l))
253 rb_link_node(&al->rb_node, parent, p);
254 rb_insert_color(&al->rb_node, root);
257 static void annotate_browser__set_top(struct annotate_browser *browser,
258 struct annotation_line *pos, u32 idx)
260 struct annotation *notes = browser__annotation(&browser->b);
263 ui_browser__refresh_dimensions(&browser->b);
264 back = browser->b.height / 2;
265 browser->b.top_idx = browser->b.index = idx;
267 while (browser->b.top_idx != 0 && back != 0) {
268 pos = list_entry(pos->node.prev, struct annotation_line, node);
270 if (annotation_line__filter(pos, notes))
273 --browser->b.top_idx;
277 browser->b.top = pos;
278 browser->b.navkeypressed = true;
281 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
284 struct annotation *notes = browser__annotation(&browser->b);
285 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
288 if (notes->options->hide_src_code)
290 annotate_browser__set_top(browser, pos, idx);
291 browser->curr_hot = nd;
294 static void annotate_browser__calc_percent(struct annotate_browser *browser,
295 struct perf_evsel *evsel)
297 struct map_symbol *ms = browser->b.priv;
298 struct symbol *sym = ms->sym;
299 struct annotation *notes = symbol__annotation(sym);
300 struct disasm_line *pos;
302 browser->entries = RB_ROOT;
304 pthread_mutex_lock(¬es->lock);
306 symbol__calc_percent(sym, evsel);
308 list_for_each_entry(pos, ¬es->src->source, al.node) {
309 double max_percent = 0.0;
312 if (pos->al.offset == -1) {
313 RB_CLEAR_NODE(&pos->al.rb_node);
317 for (i = 0; i < pos->al.samples_nr; i++) {
318 struct annotation_data *sample = &pos->al.samples[i];
320 if (max_percent < sample->percent)
321 max_percent = sample->percent;
324 if (max_percent < 0.01 && pos->al.ipc == 0) {
325 RB_CLEAR_NODE(&pos->al.rb_node);
328 disasm_rb_tree__insert(&browser->entries, &pos->al);
330 pthread_mutex_unlock(¬es->lock);
332 browser->curr_hot = rb_last(&browser->entries);
335 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
337 struct annotation *notes = browser__annotation(&browser->b);
338 struct annotation_line *al;
339 off_t offset = browser->b.index - browser->b.top_idx;
341 browser->b.seek(&browser->b, offset, SEEK_CUR);
342 al = list_entry(browser->b.top, struct annotation_line, node);
344 if (notes->options->hide_src_code) {
345 if (al->idx_asm < offset)
348 browser->b.nr_entries = notes->nr_entries;
349 notes->options->hide_src_code = false;
350 browser->b.seek(&browser->b, -offset, SEEK_CUR);
351 browser->b.top_idx = al->idx - offset;
352 browser->b.index = al->idx;
354 if (al->idx_asm < 0) {
355 ui_helpline__puts("Only available for assembly lines.");
356 browser->b.seek(&browser->b, -offset, SEEK_CUR);
360 if (al->idx_asm < offset)
361 offset = al->idx_asm;
363 browser->b.nr_entries = notes->nr_asm_entries;
364 notes->options->hide_src_code = true;
365 browser->b.seek(&browser->b, -offset, SEEK_CUR);
366 browser->b.top_idx = al->idx_asm - offset;
367 browser->b.index = al->idx_asm;
373 static void ui_browser__init_asm_mode(struct ui_browser *browser)
375 struct annotation *notes = browser__annotation(browser);
376 ui_browser__reset_index(browser);
377 browser->nr_entries = notes->nr_asm_entries;
380 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
382 static int sym_title(struct symbol *sym, struct map *map, char *title,
385 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
389 * This can be called from external jumps, i.e. jumps from one functon
390 * to another, like from the kernel's entry_SYSCALL_64 function to the
391 * swapgs_restore_regs_and_return_to_usermode() function.
393 * So all we check here is that dl->ops.target.sym is set, if it is, just
394 * go to that function and when exiting from its disassembly, come back
395 * to the calling function.
397 static bool annotate_browser__callq(struct annotate_browser *browser,
398 struct perf_evsel *evsel,
399 struct hist_browser_timer *hbt)
401 struct map_symbol *ms = browser->b.priv;
402 struct disasm_line *dl = disasm_line(browser->selection);
403 struct annotation *notes;
404 char title[SYM_TITLE_MAX_SIZE];
406 if (!dl->ops.target.sym) {
407 ui_helpline__puts("The called function was not found.");
411 notes = symbol__annotation(dl->ops.target.sym);
412 pthread_mutex_lock(¬es->lock);
414 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
415 pthread_mutex_unlock(¬es->lock);
416 ui__warning("Not enough memory for annotating '%s' symbol!\n",
417 dl->ops.target.sym->name);
421 pthread_mutex_unlock(¬es->lock);
422 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
423 sym_title(ms->sym, ms->map, title, sizeof(title));
424 ui_browser__show_title(&browser->b, title);
429 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
430 s64 offset, s64 *idx)
432 struct annotation *notes = browser__annotation(&browser->b);
433 struct disasm_line *pos;
436 list_for_each_entry(pos, ¬es->src->source, al.node) {
437 if (pos->al.offset == offset)
439 if (!annotation_line__filter(&pos->al, notes))
446 static bool annotate_browser__jump(struct annotate_browser *browser,
447 struct perf_evsel *evsel,
448 struct hist_browser_timer *hbt)
450 struct disasm_line *dl = disasm_line(browser->selection);
454 if (!ins__is_jump(&dl->ins))
457 if (dl->ops.target.outside) {
458 annotate_browser__callq(browser, evsel, hbt);
462 offset = dl->ops.target.offset;
463 dl = annotate_browser__find_offset(browser, offset, &idx);
465 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
469 annotate_browser__set_top(browser, &dl->al, idx);
475 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
478 struct annotation *notes = browser__annotation(&browser->b);
479 struct annotation_line *al = browser->selection;
481 *idx = browser->b.index;
482 list_for_each_entry_continue(al, ¬es->src->source, node) {
483 if (annotation_line__filter(al, notes))
488 if (al->line && strstr(al->line, s) != NULL)
495 static bool __annotate_browser__search(struct annotate_browser *browser)
497 struct annotation_line *al;
500 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
502 ui_helpline__puts("String not found!");
506 annotate_browser__set_top(browser, al, idx);
507 browser->searching_backwards = false;
512 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
515 struct annotation *notes = browser__annotation(&browser->b);
516 struct annotation_line *al = browser->selection;
518 *idx = browser->b.index;
519 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
520 if (annotation_line__filter(al, notes))
525 if (al->line && strstr(al->line, s) != NULL)
532 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
534 struct annotation_line *al;
537 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
539 ui_helpline__puts("String not found!");
543 annotate_browser__set_top(browser, al, idx);
544 browser->searching_backwards = true;
548 static bool annotate_browser__search_window(struct annotate_browser *browser,
551 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
552 "ENTER: OK, ESC: Cancel",
553 delay_secs * 2) != K_ENTER ||
554 !*browser->search_bf)
560 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
562 if (annotate_browser__search_window(browser, delay_secs))
563 return __annotate_browser__search(browser);
568 static bool annotate_browser__continue_search(struct annotate_browser *browser,
571 if (!*browser->search_bf)
572 return annotate_browser__search(browser, delay_secs);
574 return __annotate_browser__search(browser);
577 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
580 if (annotate_browser__search_window(browser, delay_secs))
581 return __annotate_browser__search_reverse(browser);
587 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
590 if (!*browser->search_bf)
591 return annotate_browser__search_reverse(browser, delay_secs);
593 return __annotate_browser__search_reverse(browser);
596 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
598 struct map_symbol *ms = browser->priv;
599 struct symbol *sym = ms->sym;
600 char symbol_dso[SYM_TITLE_MAX_SIZE];
602 if (ui_browser__show(browser, title, help) < 0)
605 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso));
607 ui_browser__gotorc_title(browser, 0, 0);
608 ui_browser__set_color(browser, HE_COLORSET_ROOT);
609 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
613 static int annotate_browser__run(struct annotate_browser *browser,
614 struct perf_evsel *evsel,
615 struct hist_browser_timer *hbt)
617 struct rb_node *nd = NULL;
618 struct hists *hists = evsel__hists(evsel);
619 struct map_symbol *ms = browser->b.priv;
620 struct symbol *sym = ms->sym;
621 struct annotation *notes = symbol__annotation(ms->sym);
622 const char *help = "Press 'h' for help on key bindings";
623 int delay_secs = hbt ? hbt->refresh : 0;
627 annotation__scnprintf_samples_period(notes, title, sizeof(title), evsel);
629 if (annotate_browser__show(&browser->b, title, help) < 0)
632 annotate_browser__calc_percent(browser, evsel);
634 if (browser->curr_hot) {
635 annotate_browser__set_rb_top(browser, browser->curr_hot);
636 browser->b.navkeypressed = false;
639 nd = browser->curr_hot;
642 key = ui_browser__run(&browser->b, delay_secs);
644 if (delay_secs != 0) {
645 annotate_browser__calc_percent(browser, evsel);
647 * Current line focus got out of the list of most active
648 * lines, NULL it so that if TAB|UNTAB is pressed, we
649 * move to curr_hot (current hottest line).
651 if (nd != NULL && RB_EMPTY_NODE(nd))
658 hbt->timer(hbt->arg);
660 if (delay_secs != 0) {
661 symbol__annotate_decay_histogram(sym, evsel->idx);
662 hists__scnprintf_title(hists, title, sizeof(title));
663 annotate_browser__show(&browser->b, title, help);
670 nd = rb_last(&browser->entries);
672 nd = browser->curr_hot;
678 nd = rb_first(&browser->entries);
680 nd = browser->curr_hot;
684 ui_browser__help_window(&browser->b,
686 "PGDN/SPACE Navigate\n"
687 "q/ESC/CTRL+C Exit\n\n"
688 "ENTER Go to target\n"
690 "H Go to hottest instruction\n"
691 "TAB/shift+TAB Cycle thru hottest instructions\n"
692 "j Toggle showing jump to target arrows\n"
693 "J Toggle showing number of jump sources on targets\n"
694 "n Search next string\n"
695 "o Toggle disassembler output/simplified view\n"
696 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
697 "s Toggle source code view\n"
698 "t Circulate percent, total period, samples view\n"
699 "c Show min/max cycle\n"
701 "k Toggle line numbers\n"
702 "P Print to [symbol_name].annotation file.\n"
703 "r Run available scripts\n"
704 "? Search string backwards\n");
712 notes->options->show_linenr = !notes->options->show_linenr;
715 nd = browser->curr_hot;
718 if (annotate_browser__toggle_source(browser))
719 ui_helpline__puts(help);
722 notes->options->use_offset = !notes->options->use_offset;
723 annotation__update_column_widths(notes);
726 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
727 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
730 notes->options->jump_arrows = !notes->options->jump_arrows;
733 notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
734 annotation__update_column_widths(notes);
737 if (annotate_browser__search(browser, delay_secs)) {
739 ui_helpline__puts(help);
743 if (browser->searching_backwards ?
744 annotate_browser__continue_search_reverse(browser, delay_secs) :
745 annotate_browser__continue_search(browser, delay_secs))
749 if (annotate_browser__search_reverse(browser, delay_secs))
755 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
756 seq++, browser->b.nr_entries,
760 notes->nr_asm_entries);
766 struct disasm_line *dl = disasm_line(browser->selection);
768 if (browser->selection == NULL)
769 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
770 else if (browser->selection->offset == -1)
771 ui_helpline__puts("Actions are only available for assembly lines.");
772 else if (!dl->ins.ops)
774 else if (ins__is_ret(&dl->ins))
776 else if (!(annotate_browser__jump(browser, evsel, hbt) ||
777 annotate_browser__callq(browser, evsel, hbt))) {
779 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
784 map_symbol__annotation_dump(ms, evsel);
787 if (notes->options->show_total_period) {
788 notes->options->show_total_period = false;
789 notes->options->show_nr_samples = true;
790 } else if (notes->options->show_nr_samples)
791 notes->options->show_nr_samples = false;
793 notes->options->show_total_period = true;
794 annotation__update_column_widths(notes);
797 if (notes->options->show_minmax_cycle)
798 notes->options->show_minmax_cycle = false;
800 notes->options->show_minmax_cycle = true;
801 annotation__update_column_widths(notes);
813 annotate_browser__set_rb_top(browser, nd);
816 ui_browser__hide(&browser->b);
820 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
821 struct hist_browser_timer *hbt,
822 struct annotation_options *opts)
824 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
827 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
828 struct hist_browser_timer *hbt,
829 struct annotation_options *opts)
831 /* reset abort key so that it can get Ctrl-C as a key */
833 SLang_init_tty(0, 0, 0);
835 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
838 int symbol__tui_annotate(struct symbol *sym, struct map *map,
839 struct perf_evsel *evsel,
840 struct hist_browser_timer *hbt,
841 struct annotation_options *opts)
843 struct annotation *notes = symbol__annotation(sym);
844 struct map_symbol ms = {
848 struct annotate_browser browser = {
850 .refresh = annotate_browser__refresh,
851 .seek = ui_browser__list_head_seek,
852 .write = annotate_browser__write,
853 .filter = disasm_line__filter,
854 .extra_title_lines = 1, /* for hists__scnprintf_title() */
856 .use_navkeypressed = true,
865 if (map->dso->annotate_warned)
868 err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
871 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
872 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
873 goto out_free_offsets;
876 ui_helpline__push("Press ESC to exit");
878 browser.b.width = notes->max_line_len;
879 browser.b.nr_entries = notes->nr_entries;
880 browser.b.entries = ¬es->src->source,
881 browser.b.width += 18; /* Percentage */
883 if (notes->options->hide_src_code)
884 ui_browser__init_asm_mode(&browser.b);
886 ret = annotate_browser__run(&browser, evsel, hbt);
888 annotated_source__purge(notes->src);
891 zfree(¬es->offsets);