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/config.h"
13 #include "../../util/evlist.h"
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
20 struct disasm_line_samples {
22 struct sym_hist_entry he;
26 #define CYCLES_WIDTH 6
34 static struct annotate_browser_opt {
42 } annotate_browser__opts = {
49 struct annotate_browser {
51 struct rb_root entries;
52 struct rb_node *curr_hot;
53 struct annotation_line *selection;
54 struct annotation_line **offsets;
62 bool searching_backwards;
72 static inline struct browser_line *browser_line(struct annotation_line *al)
76 ptr = container_of(al, struct disasm_line, al);
77 return ptr - sizeof(struct browser_line);
80 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
83 if (annotate_browser__opts.hide_src_code) {
84 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
86 return al->offset == -1;
92 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
95 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
96 return HE_COLORSET_SELECTED;
97 if (nr == browser->max_jump_sources)
98 return HE_COLORSET_TOP;
100 return HE_COLORSET_MEDIUM;
101 return HE_COLORSET_NORMAL;
104 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
105 int nr, bool current)
107 int color = annotate_browser__jumps_percent_color(browser, nr, current);
108 return ui_browser__set_color(&browser->b, color);
111 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
113 return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
116 static int annotate_browser__cycles_width(struct annotate_browser *ab)
118 return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
121 static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
122 char *bf, size_t size)
124 if (dl->ins.ops && dl->ins.ops->scnprintf) {
125 if (ins__is_jump(&dl->ins)) {
126 bool fwd = dl->ops.target.offset > dl->al.offset;
128 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
130 SLsmg_write_char(' ');
131 } else if (ins__is_call(&dl->ins)) {
132 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
133 SLsmg_write_char(' ');
134 } else if (ins__is_ret(&dl->ins)) {
135 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
136 SLsmg_write_char(' ');
138 ui_browser__write_nstring(browser, " ", 2);
141 ui_browser__write_nstring(browser, " ", 2);
144 disasm_line__scnprintf(dl, bf, size, !annotate_browser__opts.use_offset);
147 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
149 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
150 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
151 struct browser_line *bl = browser_line(al);
152 bool current_entry = ui_browser__is_current_entry(browser, row);
153 bool change_color = (!annotate_browser__opts.hide_src_code &&
154 (!current_entry || (browser->use_navkeypressed &&
155 !browser->navkeypressed)));
156 int width = browser->width, printed;
157 int i, pcnt_width = annotate_browser__pcnt_width(ab),
158 cycles_width = annotate_browser__cycles_width(ab);
159 double percent_max = 0.0;
161 bool show_title = false;
163 for (i = 0; i < ab->nr_events; i++) {
164 if (al->samples[i].percent > percent_max)
165 percent_max = al->samples[i].percent;
168 if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
169 if (ab->have_cycles) {
170 if (al->ipc == 0.0 && al->cycles == 0)
176 if (al->offset != -1 && percent_max != 0.0) {
177 for (i = 0; i < ab->nr_events; i++) {
178 ui_browser__set_percent_color(browser,
179 al->samples[i].percent,
181 if (annotate_browser__opts.show_total_period) {
182 ui_browser__printf(browser, "%11" PRIu64 " ",
183 al->samples[i].he.period);
184 } else if (annotate_browser__opts.show_nr_samples) {
185 ui_browser__printf(browser, "%6" PRIu64 " ",
186 al->samples[i].he.nr_samples);
188 ui_browser__printf(browser, "%6.2f ",
189 al->samples[i].percent);
193 ui_browser__set_percent_color(browser, 0, current_entry);
196 ui_browser__write_nstring(browser, " ", pcnt_width);
198 ui_browser__printf(browser, "%*s", pcnt_width,
199 annotate_browser__opts.show_total_period ? "Period" :
200 annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
203 if (ab->have_cycles) {
205 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, al->ipc);
206 else if (!show_title)
207 ui_browser__write_nstring(browser, " ", IPC_WIDTH);
209 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
212 ui_browser__printf(browser, "%*" PRIu64 " ",
213 CYCLES_WIDTH - 1, al->cycles);
214 else if (!show_title)
215 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
217 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
220 SLsmg_write_char(' ');
222 /* The scroll bar isn't being used */
223 if (!browser->navkeypressed)
227 ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
228 else if (al->offset == -1) {
229 if (al->line_nr && annotate_browser__opts.show_linenr)
230 printed = scnprintf(bf, sizeof(bf), "%-*d ",
231 ab->addr_width + 1, al->line_nr);
233 printed = scnprintf(bf, sizeof(bf), "%*s ",
234 ab->addr_width, " ");
235 ui_browser__write_nstring(browser, bf, printed);
236 ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
238 u64 addr = al->offset;
241 if (!annotate_browser__opts.use_offset)
244 if (!annotate_browser__opts.use_offset) {
245 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
247 if (bl->jump_sources) {
248 if (annotate_browser__opts.show_nr_jumps) {
250 printed = scnprintf(bf, sizeof(bf), "%*d ",
253 prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
255 ui_browser__write_nstring(browser, bf, printed);
256 ui_browser__set_color(browser, prev);
259 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
260 ab->target_width, addr);
262 printed = scnprintf(bf, sizeof(bf), "%*s ",
263 ab->addr_width, " ");
268 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
269 ui_browser__write_nstring(browser, bf, printed);
271 ui_browser__set_color(browser, color);
273 disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));
275 ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
282 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
284 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
285 || !disasm_line__has_offset(dl)
286 || dl->ops.target.offset < 0
287 || dl->ops.target.offset >= (s64)symbol__size(sym))
293 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
295 struct disasm_line *pos = list_prev_entry(cursor, al.node);
301 if (ins__is_lock(&pos->ins))
302 name = pos->ops.locked.ins.name;
304 name = pos->ins.name;
306 if (!name || !cursor->ins.name)
309 return ins__is_fused(ab->arch, name, cursor->ins.name);
312 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
314 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315 struct disasm_line *cursor = disasm_line(ab->selection);
316 struct annotation_line *target;
317 struct browser_line *btarget, *bcursor;
318 unsigned int from, to;
319 struct map_symbol *ms = ab->b.priv;
320 struct symbol *sym = ms->sym;
321 u8 pcnt_width = annotate_browser__pcnt_width(ab);
324 /* PLT symbols contain external offsets */
325 if (strstr(sym->name, "@plt"))
328 if (!disasm_line__is_valid_jump(cursor, sym))
332 * This first was seen with a gcc function, _cpp_lex_token, that
333 * has the usual jumps:
335 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
337 * I.e. jumps to a label inside that function (_cpp_lex_token), and
338 * those works, but also this kind:
340 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
342 * I.e. jumps to another function, outside _cpp_lex_token, which
343 * are not being correctly handled generating as a side effect references
344 * to ab->offset[] entries that are set to NULL, so to make this code
345 * more robust, check that here.
347 * A proper fix for will be put in place, looking at the function
348 * name right after the '<' token and probably treating this like a
349 * 'call' instruction.
351 target = ab->offsets[cursor->ops.target.offset];
352 if (target == NULL) {
353 ui_helpline__printf("WARN: jump target inconsistency, press 'o', ab->offsets[%#x] = NULL\n",
354 cursor->ops.target.offset);
358 bcursor = browser_line(&cursor->al);
359 btarget = browser_line(target);
361 if (annotate_browser__opts.hide_src_code) {
362 from = bcursor->idx_asm;
363 to = btarget->idx_asm;
365 from = (u64)bcursor->idx;
366 to = (u64)btarget->idx;
370 width = IPC_WIDTH + CYCLES_WIDTH;
372 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
373 __ui_browser__line_arrow(browser,
374 pcnt_width + 2 + ab->addr_width + width,
377 if (is_fused(ab, cursor)) {
378 ui_browser__mark_fused(browser,
379 pcnt_width + 3 + ab->addr_width + width,
381 to > from ? true : false);
385 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
387 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
388 int ret = ui_browser__list_head_refresh(browser);
389 int pcnt_width = annotate_browser__pcnt_width(ab);
391 if (annotate_browser__opts.jump_arrows)
392 annotate_browser__draw_current_jump(browser);
394 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
395 __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
399 static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
403 for (i = 0; i < a->samples_nr; i++) {
404 if (a->samples[i].percent == b->samples[i].percent)
406 return a->samples[i].percent < b->samples[i].percent;
411 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
413 struct rb_node **p = &root->rb_node;
414 struct rb_node *parent = NULL;
415 struct annotation_line *l;
419 l = rb_entry(parent, struct annotation_line, rb_node);
421 if (disasm__cmp(al, l))
426 rb_link_node(&al->rb_node, parent, p);
427 rb_insert_color(&al->rb_node, root);
430 static void annotate_browser__set_top(struct annotate_browser *browser,
431 struct annotation_line *pos, u32 idx)
435 ui_browser__refresh_dimensions(&browser->b);
436 back = browser->b.height / 2;
437 browser->b.top_idx = browser->b.index = idx;
439 while (browser->b.top_idx != 0 && back != 0) {
440 pos = list_entry(pos->node.prev, struct annotation_line, node);
442 if (disasm_line__filter(&browser->b, &pos->node))
445 --browser->b.top_idx;
449 browser->b.top = pos;
450 browser->b.navkeypressed = true;
453 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
456 struct browser_line *bpos;
457 struct annotation_line *pos;
460 pos = rb_entry(nd, struct annotation_line, rb_node);
461 bpos = browser_line(pos);
464 if (annotate_browser__opts.hide_src_code)
466 annotate_browser__set_top(browser, pos, idx);
467 browser->curr_hot = nd;
470 static void annotate_browser__calc_percent(struct annotate_browser *browser,
471 struct perf_evsel *evsel)
473 struct map_symbol *ms = browser->b.priv;
474 struct symbol *sym = ms->sym;
475 struct annotation *notes = symbol__annotation(sym);
476 struct disasm_line *pos;
478 browser->entries = RB_ROOT;
480 pthread_mutex_lock(¬es->lock);
482 symbol__calc_percent(sym, evsel);
484 list_for_each_entry(pos, ¬es->src->source, al.node) {
485 double max_percent = 0.0;
488 if (pos->al.offset == -1) {
489 RB_CLEAR_NODE(&pos->al.rb_node);
493 for (i = 0; i < pos->al.samples_nr; i++) {
494 struct annotation_data *sample = &pos->al.samples[i];
496 if (max_percent < sample->percent)
497 max_percent = sample->percent;
500 if (max_percent < 0.01 && pos->al.ipc == 0) {
501 RB_CLEAR_NODE(&pos->al.rb_node);
504 disasm_rb_tree__insert(&browser->entries, &pos->al);
506 pthread_mutex_unlock(¬es->lock);
508 browser->curr_hot = rb_last(&browser->entries);
511 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
513 struct annotation_line *al;
514 struct browser_line *bl;
515 off_t offset = browser->b.index - browser->b.top_idx;
517 browser->b.seek(&browser->b, offset, SEEK_CUR);
518 al = list_entry(browser->b.top, struct annotation_line, node);
519 bl = browser_line(al);
521 if (annotate_browser__opts.hide_src_code) {
522 if (bl->idx_asm < offset)
525 browser->b.nr_entries = browser->nr_entries;
526 annotate_browser__opts.hide_src_code = false;
527 browser->b.seek(&browser->b, -offset, SEEK_CUR);
528 browser->b.top_idx = bl->idx - offset;
529 browser->b.index = bl->idx;
531 if (bl->idx_asm < 0) {
532 ui_helpline__puts("Only available for assembly lines.");
533 browser->b.seek(&browser->b, -offset, SEEK_CUR);
537 if (bl->idx_asm < offset)
538 offset = bl->idx_asm;
540 browser->b.nr_entries = browser->nr_asm_entries;
541 annotate_browser__opts.hide_src_code = true;
542 browser->b.seek(&browser->b, -offset, SEEK_CUR);
543 browser->b.top_idx = bl->idx_asm - offset;
544 browser->b.index = bl->idx_asm;
550 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
552 ui_browser__reset_index(&browser->b);
553 browser->b.nr_entries = browser->nr_asm_entries;
556 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
558 static int sym_title(struct symbol *sym, struct map *map, char *title,
561 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
564 static bool annotate_browser__callq(struct annotate_browser *browser,
565 struct perf_evsel *evsel,
566 struct hist_browser_timer *hbt)
568 struct map_symbol *ms = browser->b.priv;
569 struct disasm_line *dl = disasm_line(browser->selection);
570 struct annotation *notes;
571 char title[SYM_TITLE_MAX_SIZE];
573 if (!ins__is_call(&dl->ins))
576 if (!dl->ops.target.sym) {
577 ui_helpline__puts("The called function was not found.");
581 notes = symbol__annotation(dl->ops.target.sym);
582 pthread_mutex_lock(¬es->lock);
584 if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
585 pthread_mutex_unlock(¬es->lock);
586 ui__warning("Not enough memory for annotating '%s' symbol!\n",
587 dl->ops.target.sym->name);
591 pthread_mutex_unlock(¬es->lock);
592 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
593 sym_title(ms->sym, ms->map, title, sizeof(title));
594 ui_browser__show_title(&browser->b, title);
599 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
600 s64 offset, s64 *idx)
602 struct map_symbol *ms = browser->b.priv;
603 struct symbol *sym = ms->sym;
604 struct annotation *notes = symbol__annotation(sym);
605 struct disasm_line *pos;
608 list_for_each_entry(pos, ¬es->src->source, al.node) {
609 if (pos->al.offset == offset)
611 if (!disasm_line__filter(&browser->b, &pos->al.node))
618 static bool annotate_browser__jump(struct annotate_browser *browser)
620 struct disasm_line *dl = disasm_line(browser->selection);
624 if (!ins__is_jump(&dl->ins))
627 offset = dl->ops.target.offset;
628 dl = annotate_browser__find_offset(browser, offset, &idx);
630 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
634 annotate_browser__set_top(browser, &dl->al, idx);
640 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
643 struct map_symbol *ms = browser->b.priv;
644 struct symbol *sym = ms->sym;
645 struct annotation *notes = symbol__annotation(sym);
646 struct annotation_line *al = browser->selection;
648 *idx = browser->b.index;
649 list_for_each_entry_continue(al, ¬es->src->source, node) {
650 if (disasm_line__filter(&browser->b, &al->node))
655 if (al->line && strstr(al->line, s) != NULL)
662 static bool __annotate_browser__search(struct annotate_browser *browser)
664 struct annotation_line *al;
667 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
669 ui_helpline__puts("String not found!");
673 annotate_browser__set_top(browser, al, idx);
674 browser->searching_backwards = false;
679 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
682 struct map_symbol *ms = browser->b.priv;
683 struct symbol *sym = ms->sym;
684 struct annotation *notes = symbol__annotation(sym);
685 struct annotation_line *al = browser->selection;
687 *idx = browser->b.index;
688 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
689 if (disasm_line__filter(&browser->b, &al->node))
694 if (al->line && strstr(al->line, s) != NULL)
701 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
703 struct annotation_line *al;
706 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
708 ui_helpline__puts("String not found!");
712 annotate_browser__set_top(browser, al, idx);
713 browser->searching_backwards = true;
717 static bool annotate_browser__search_window(struct annotate_browser *browser,
720 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
721 "ENTER: OK, ESC: Cancel",
722 delay_secs * 2) != K_ENTER ||
723 !*browser->search_bf)
729 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
731 if (annotate_browser__search_window(browser, delay_secs))
732 return __annotate_browser__search(browser);
737 static bool annotate_browser__continue_search(struct annotate_browser *browser,
740 if (!*browser->search_bf)
741 return annotate_browser__search(browser, delay_secs);
743 return __annotate_browser__search(browser);
746 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
749 if (annotate_browser__search_window(browser, delay_secs))
750 return __annotate_browser__search_reverse(browser);
756 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
759 if (!*browser->search_bf)
760 return annotate_browser__search_reverse(browser, delay_secs);
762 return __annotate_browser__search_reverse(browser);
765 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
767 if (annotate_browser__opts.use_offset)
768 browser->target_width = browser->min_addr_width;
770 browser->target_width = browser->max_addr_width;
772 browser->addr_width = browser->target_width;
774 if (annotate_browser__opts.show_nr_jumps)
775 browser->addr_width += browser->jumps_width + 1;
778 static int annotate_browser__run(struct annotate_browser *browser,
779 struct perf_evsel *evsel,
780 struct hist_browser_timer *hbt)
782 struct rb_node *nd = NULL;
783 struct map_symbol *ms = browser->b.priv;
784 struct symbol *sym = ms->sym;
785 const char *help = "Press 'h' for help on key bindings";
786 int delay_secs = hbt ? hbt->refresh : 0;
788 char title[SYM_TITLE_MAX_SIZE];
790 sym_title(sym, ms->map, title, sizeof(title));
791 if (ui_browser__show(&browser->b, title, help) < 0)
794 annotate_browser__calc_percent(browser, evsel);
796 if (browser->curr_hot) {
797 annotate_browser__set_rb_top(browser, browser->curr_hot);
798 browser->b.navkeypressed = false;
801 nd = browser->curr_hot;
804 key = ui_browser__run(&browser->b, delay_secs);
806 if (delay_secs != 0) {
807 annotate_browser__calc_percent(browser, evsel);
809 * Current line focus got out of the list of most active
810 * lines, NULL it so that if TAB|UNTAB is pressed, we
811 * move to curr_hot (current hottest line).
813 if (nd != NULL && RB_EMPTY_NODE(nd))
820 hbt->timer(hbt->arg);
823 symbol__annotate_decay_histogram(sym, evsel->idx);
829 nd = rb_last(&browser->entries);
831 nd = browser->curr_hot;
837 nd = rb_first(&browser->entries);
839 nd = browser->curr_hot;
843 ui_browser__help_window(&browser->b,
845 "PGDN/SPACE Navigate\n"
846 "q/ESC/CTRL+C Exit\n\n"
847 "ENTER Go to target\n"
849 "H Go to hottest instruction\n"
850 "TAB/shift+TAB Cycle thru hottest instructions\n"
851 "j Toggle showing jump to target arrows\n"
852 "J Toggle showing number of jump sources on targets\n"
853 "n Search next string\n"
854 "o Toggle disassembler output/simplified view\n"
855 "s Toggle source code view\n"
856 "t Circulate percent, total period, samples view\n"
858 "k Toggle line numbers\n"
859 "r Run available scripts\n"
860 "? Search string backwards\n");
868 annotate_browser__opts.show_linenr =
869 !annotate_browser__opts.show_linenr;
872 nd = browser->curr_hot;
875 if (annotate_browser__toggle_source(browser))
876 ui_helpline__puts(help);
879 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
880 annotate_browser__update_addr_width(browser);
883 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
886 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
887 annotate_browser__update_addr_width(browser);
890 if (annotate_browser__search(browser, delay_secs)) {
892 ui_helpline__puts(help);
896 if (browser->searching_backwards ?
897 annotate_browser__continue_search_reverse(browser, delay_secs) :
898 annotate_browser__continue_search(browser, delay_secs))
902 if (annotate_browser__search_reverse(browser, delay_secs))
908 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
909 seq++, browser->b.nr_entries,
913 browser->nr_asm_entries);
919 struct disasm_line *dl = disasm_line(browser->selection);
921 if (browser->selection == NULL)
922 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
923 else if (browser->selection->offset == -1)
924 ui_helpline__puts("Actions are only available for assembly lines.");
925 else if (!dl->ins.ops)
927 else if (ins__is_ret(&dl->ins))
929 else if (!(annotate_browser__jump(browser) ||
930 annotate_browser__callq(browser, evsel, hbt))) {
932 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
937 if (annotate_browser__opts.show_total_period) {
938 annotate_browser__opts.show_total_period = false;
939 annotate_browser__opts.show_nr_samples = true;
940 } else if (annotate_browser__opts.show_nr_samples)
941 annotate_browser__opts.show_nr_samples = false;
943 annotate_browser__opts.show_total_period = true;
944 annotate_browser__update_addr_width(browser);
956 annotate_browser__set_rb_top(browser, nd);
959 ui_browser__hide(&browser->b);
963 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
964 struct hist_browser_timer *hbt)
966 /* Set default value for show_total_period and show_nr_samples */
967 annotate_browser__opts.show_total_period =
968 symbol_conf.show_total_period;
969 annotate_browser__opts.show_nr_samples =
970 symbol_conf.show_nr_samples;
972 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
975 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
976 struct hist_browser_timer *hbt)
978 /* reset abort key so that it can get Ctrl-C as a key */
980 SLang_init_tty(0, 0, 0);
982 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
986 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
991 for (offset = start; offset <= end; offset++) {
992 if (browser->offsets[offset])
998 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
1004 n_insn = count_insn(browser, start, end);
1005 if (n_insn && ch->num && ch->cycles) {
1006 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
1008 /* Hide data when there are too many overlaps. */
1009 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
1012 for (offset = start; offset <= end; offset++) {
1013 struct annotation_line *al = browser->offsets[offset];
1022 * This should probably be in util/annotate.c to share with the tty
1023 * annotate, but right now we need the per byte offsets arrays,
1024 * which are only here.
1026 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
1030 struct annotation *notes = symbol__annotation(sym);
1032 if (!notes->src || !notes->src->cycles_hist)
1035 pthread_mutex_lock(¬es->lock);
1036 for (offset = 0; offset < size; ++offset) {
1037 struct cyc_hist *ch;
1039 ch = ¬es->src->cycles_hist[offset];
1040 if (ch && ch->cycles) {
1041 struct annotation_line *al;
1044 count_and_fill(browser, ch->start, offset, ch);
1045 al = browser->offsets[offset];
1046 if (al && ch->num_aggr)
1047 al->cycles = ch->cycles_aggr / ch->num_aggr;
1048 browser->have_cycles = true;
1051 pthread_mutex_unlock(¬es->lock);
1054 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
1058 struct map_symbol *ms = browser->b.priv;
1059 struct symbol *sym = ms->sym;
1061 /* PLT symbols contain external offsets */
1062 if (strstr(sym->name, "@plt"))
1065 for (offset = 0; offset < size; ++offset) {
1066 struct annotation_line *al = browser->offsets[offset];
1067 struct disasm_line *dl;
1068 struct browser_line *blt;
1070 dl = disasm_line(al);
1072 if (!disasm_line__is_valid_jump(dl, sym))
1075 al = browser->offsets[dl->ops.target.offset];
1078 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1079 * have to adjust to the previous offset?
1084 blt = browser_line(al);
1085 if (++blt->jump_sources > browser->max_jump_sources)
1086 browser->max_jump_sources = blt->jump_sources;
1088 ++browser->nr_jumps;
1092 static inline int width_jumps(int n)
1101 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1102 struct perf_evsel *evsel,
1103 struct hist_browser_timer *hbt)
1105 struct annotation_line *al;
1106 struct annotation *notes;
1108 struct map_symbol ms = {
1112 struct annotate_browser browser = {
1114 .refresh = annotate_browser__refresh,
1115 .seek = ui_browser__list_head_seek,
1116 .write = annotate_browser__write,
1117 .filter = disasm_line__filter,
1119 .use_navkeypressed = true,
1128 size = symbol__size(sym);
1130 if (map->dso->annotate_warned)
1133 browser.offsets = zalloc(size * sizeof(struct annotation_line *));
1134 if (browser.offsets == NULL) {
1135 ui__error("Not enough memory!");
1139 if (perf_evsel__is_group_event(evsel))
1140 nr_pcnt = evsel->nr_members;
1142 err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1145 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1146 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1147 goto out_free_offsets;
1150 symbol__calc_percent(sym, evsel);
1152 ui_helpline__push("Press ESC to exit");
1154 notes = symbol__annotation(sym);
1155 browser.start = map__rip_2objdump(map, sym->start);
1157 list_for_each_entry(al, ¬es->src->source, node) {
1158 struct browser_line *bpos;
1159 size_t line_len = strlen(al->line);
1161 if (browser.b.width < line_len)
1162 browser.b.width = line_len;
1163 bpos = browser_line(al);
1164 bpos->idx = browser.nr_entries++;
1165 if (al->offset != -1) {
1166 bpos->idx_asm = browser.nr_asm_entries++;
1168 * FIXME: short term bandaid to cope with assembly
1169 * routines that comes with labels in the same column
1170 * as the address in objdump, sigh.
1172 * E.g. copy_user_generic_unrolled
1174 if (al->offset < (s64)size)
1175 browser.offsets[al->offset] = al;
1180 annotate_browser__mark_jump_targets(&browser, size);
1181 annotate__compute_ipc(&browser, size, sym);
1183 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1184 browser.max_addr_width = hex_width(sym->end);
1185 browser.jumps_width = width_jumps(browser.max_jump_sources);
1186 browser.nr_events = nr_pcnt;
1187 browser.b.nr_entries = browser.nr_entries;
1188 browser.b.entries = ¬es->src->source,
1189 browser.b.width += 18; /* Percentage */
1191 if (annotate_browser__opts.hide_src_code)
1192 annotate_browser__init_asm_mode(&browser);
1194 annotate_browser__update_addr_width(&browser);
1196 ret = annotate_browser__run(&browser, evsel, hbt);
1198 annotated_source__purge(notes->src);
1201 free(browser.offsets);
1205 #define ANNOTATE_CFG(n) \
1206 { .name = #n, .value = &annotate_browser__opts.n, }
1209 * Keep the entries sorted, they are bsearch'ed
1211 static struct annotate_config {
1214 } annotate__configs[] = {
1215 ANNOTATE_CFG(hide_src_code),
1216 ANNOTATE_CFG(jump_arrows),
1217 ANNOTATE_CFG(show_linenr),
1218 ANNOTATE_CFG(show_nr_jumps),
1219 ANNOTATE_CFG(show_nr_samples),
1220 ANNOTATE_CFG(show_total_period),
1221 ANNOTATE_CFG(use_offset),
1226 static int annotate_config__cmp(const void *name, const void *cfgp)
1228 const struct annotate_config *cfg = cfgp;
1230 return strcmp(name, cfg->name);
1233 static int annotate__config(const char *var, const char *value,
1234 void *data __maybe_unused)
1236 struct annotate_config *cfg;
1239 if (!strstarts(var, "annotate."))
1243 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1244 sizeof(struct annotate_config), annotate_config__cmp);
1247 ui__warning("%s variable unknown, ignoring...", var);
1249 *cfg->value = perf_config_bool(name, value);
1253 void annotate_browser__init(void)
1255 perf_config(annotate__config, NULL);