Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2#include "../../util/util.h"
3#include "../browser.h"
4#include "../helpline.h"
5#include "../ui.h"
6#include "../util.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"
13#include <inttypes.h>
14#include <pthread.h>
15#include <linux/kernel.h>
16#include <linux/string.h>
17#include <sys/ttydefaults.h>
18
19struct disasm_line_samples {
20 double percent;
21 struct sym_hist_entry he;
22};
23
24struct arch;
25
26struct annotate_browser {
27 struct ui_browser b;
28 struct rb_root entries;
29 struct rb_node *curr_hot;
30 struct annotation_line *selection;
31 struct arch *arch;
32 struct annotation_options *opts;
33 bool searching_backwards;
34 char search_bf[128];
35};
36
37static inline struct annotation *browser__annotation(struct ui_browser *browser)
38{
39 struct map_symbol *ms = browser->priv;
40 return symbol__annotation(ms->sym);
41}
42
43static bool disasm_line__filter(struct ui_browser *browser, void *entry)
44{
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);
48}
49
50static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
51{
52 struct annotation *notes = browser__annotation(browser);
53
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;
58 if (nr > 1)
59 return HE_COLORSET_MEDIUM;
60 return HE_COLORSET_NORMAL;
61}
62
63static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
64{
65 int color = ui_browser__jumps_percent_color(browser, nr, current);
66 return ui_browser__set_color(browser, color);
67}
68
69static int annotate_browser__set_color(void *browser, int color)
70{
71 return ui_browser__set_color(browser, color);
72}
73
74static void annotate_browser__write_graph(void *browser, int graph)
75{
76 ui_browser__write_graph(browser, graph);
77}
78
79static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
80{
81 ui_browser__set_percent_color(browser, percent, current);
82}
83
84static void annotate_browser__printf(void *browser, const char *fmt, ...)
85{
86 va_list args;
87
88 va_start(args, fmt);
89 ui_browser__vprintf(browser, fmt, args);
90 va_end(args);
91}
92
93static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
94{
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,
106 .obj = browser,
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,
112 };
113
114 /* The scroll bar isn't being used */
115 if (!browser->navkeypressed)
116 ops.width += 1;
117
118 annotation_line__write(al, notes, &ops);
119
120 if (ops.current_entry)
121 ab->selection = al;
122}
123
124static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
125{
126 struct disasm_line *pos = list_prev_entry(cursor, al.node);
127 const char *name;
128
129 if (!pos)
130 return false;
131
132 if (ins__is_lock(&pos->ins))
133 name = pos->ops.locked.ins.name;
134 else
135 name = pos->ins.name;
136
137 if (!name || !cursor->ins.name)
138 return false;
139
140 return ins__is_fused(ab->arch, name, cursor->ins.name);
141}
142
143static void annotate_browser__draw_current_jump(struct ui_browser *browser)
144{
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);
153 int width;
154
155 /* PLT symbols contain external offsets */
156 if (strstr(sym->name, "@plt"))
157 return;
158
159 if (!disasm_line__is_valid_local_jump(cursor, sym))
160 return;
161
162 /*
163 * This first was seen with a gcc function, _cpp_lex_token, that
164 * has the usual jumps:
165 *
166 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
167 *
168 * I.e. jumps to a label inside that function (_cpp_lex_token), and
169 * those works, but also this kind:
170 *
171 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
172 *
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.
177 *
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.
181 */
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);
186 return;
187 }
188
189 if (notes->options->hide_src_code) {
190 from = cursor->al.idx_asm;
191 to = target->idx_asm;
192 } else {
193 from = (u64)cursor->al.idx;
194 to = (u64)target->idx;
195 }
196
197 width = annotation__cycles_width(notes);
198
199 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
200 __ui_browser__line_arrow(browser,
201 pcnt_width + 2 + notes->widths.addr + width,
202 from, to);
203
204 if (is_fused(ab, cursor)) {
205 ui_browser__mark_fused(browser,
206 pcnt_width + 3 + notes->widths.addr + width,
207 from - 1,
208 to > from ? true : false);
209 }
210}
211
212static unsigned int annotate_browser__refresh(struct ui_browser *browser)
213{
214 struct annotation *notes = browser__annotation(browser);
215 int ret = ui_browser__list_head_refresh(browser);
216 int pcnt_width = annotation__pcnt_width(notes);
217
218 if (notes->options->jump_arrows)
219 annotate_browser__draw_current_jump(browser);
220
221 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
222 __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
223 return ret;
224}
225
226static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
227{
228 int i;
229
230 for (i = 0; i < a->samples_nr; i++) {
231 if (a->samples[i].percent == b->samples[i].percent)
232 continue;
233 return a->samples[i].percent < b->samples[i].percent;
234 }
235 return 0;
236}
237
238static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
239{
240 struct rb_node **p = &root->rb_node;
241 struct rb_node *parent = NULL;
242 struct annotation_line *l;
243
244 while (*p != NULL) {
245 parent = *p;
246 l = rb_entry(parent, struct annotation_line, rb_node);
247
248 if (disasm__cmp(al, l))
249 p = &(*p)->rb_left;
250 else
251 p = &(*p)->rb_right;
252 }
253 rb_link_node(&al->rb_node, parent, p);
254 rb_insert_color(&al->rb_node, root);
255}
256
257static void annotate_browser__set_top(struct annotate_browser *browser,
258 struct annotation_line *pos, u32 idx)
259{
260 struct annotation *notes = browser__annotation(&browser->b);
261 unsigned back;
262
263 ui_browser__refresh_dimensions(&browser->b);
264 back = browser->b.height / 2;
265 browser->b.top_idx = browser->b.index = idx;
266
267 while (browser->b.top_idx != 0 && back != 0) {
268 pos = list_entry(pos->node.prev, struct annotation_line, node);
269
270 if (annotation_line__filter(pos, notes))
271 continue;
272
273 --browser->b.top_idx;
274 --back;
275 }
276
277 browser->b.top = pos;
278 browser->b.navkeypressed = true;
279}
280
281static void annotate_browser__set_rb_top(struct annotate_browser *browser,
282 struct rb_node *nd)
283{
284 struct annotation *notes = browser__annotation(&browser->b);
285 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
286 u32 idx = pos->idx;
287
288 if (notes->options->hide_src_code)
289 idx = pos->idx_asm;
290 annotate_browser__set_top(browser, pos, idx);
291 browser->curr_hot = nd;
292}
293
294static void annotate_browser__calc_percent(struct annotate_browser *browser,
295 struct perf_evsel *evsel)
296{
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;
301
302 browser->entries = RB_ROOT;
303
304 pthread_mutex_lock(¬es->lock);
305
306 symbol__calc_percent(sym, evsel);
307
308 list_for_each_entry(pos, ¬es->src->source, al.node) {
309 double max_percent = 0.0;
310 int i;
311
312 if (pos->al.offset == -1) {
313 RB_CLEAR_NODE(&pos->al.rb_node);
314 continue;
315 }
316
317 for (i = 0; i < pos->al.samples_nr; i++) {
318 struct annotation_data *sample = &pos->al.samples[i];
319
320 if (max_percent < sample->percent)
321 max_percent = sample->percent;
322 }
323
324 if (max_percent < 0.01 && pos->al.ipc == 0) {
325 RB_CLEAR_NODE(&pos->al.rb_node);
326 continue;
327 }
328 disasm_rb_tree__insert(&browser->entries, &pos->al);
329 }
330 pthread_mutex_unlock(¬es->lock);
331
332 browser->curr_hot = rb_last(&browser->entries);
333}
334
335static bool annotate_browser__toggle_source(struct annotate_browser *browser)
336{
337 struct annotation *notes = browser__annotation(&browser->b);
338 struct annotation_line *al;
339 off_t offset = browser->b.index - browser->b.top_idx;
340
341 browser->b.seek(&browser->b, offset, SEEK_CUR);
342 al = list_entry(browser->b.top, struct annotation_line, node);
343
344 if (notes->options->hide_src_code) {
345 if (al->idx_asm < offset)
346 offset = al->idx;
347
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;
353 } else {
354 if (al->idx_asm < 0) {
355 ui_helpline__puts("Only available for assembly lines.");
356 browser->b.seek(&browser->b, -offset, SEEK_CUR);
357 return false;
358 }
359
360 if (al->idx_asm < offset)
361 offset = al->idx_asm;
362
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;
368 }
369
370 return true;
371}
372
373static void ui_browser__init_asm_mode(struct ui_browser *browser)
374{
375 struct annotation *notes = browser__annotation(browser);
376 ui_browser__reset_index(browser);
377 browser->nr_entries = notes->nr_asm_entries;
378}
379
380#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
381
382static int sym_title(struct symbol *sym, struct map *map, char *title,
383 size_t sz)
384{
385 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
386}
387
388/*
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.
392 *
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.
396 */
397static bool annotate_browser__callq(struct annotate_browser *browser,
398 struct perf_evsel *evsel,
399 struct hist_browser_timer *hbt)
400{
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];
405
406 if (!dl->ops.target.sym) {
407 ui_helpline__puts("The called function was not found.");
408 return true;
409 }
410
411 notes = symbol__annotation(dl->ops.target.sym);
412 pthread_mutex_lock(¬es->lock);
413
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);
418 return true;
419 }
420
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);
425 return true;
426}
427
428static
429struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
430 s64 offset, s64 *idx)
431{
432 struct annotation *notes = browser__annotation(&browser->b);
433 struct disasm_line *pos;
434
435 *idx = 0;
436 list_for_each_entry(pos, ¬es->src->source, al.node) {
437 if (pos->al.offset == offset)
438 return pos;
439 if (!annotation_line__filter(&pos->al, notes))
440 ++*idx;
441 }
442
443 return NULL;
444}
445
446static bool annotate_browser__jump(struct annotate_browser *browser,
447 struct perf_evsel *evsel,
448 struct hist_browser_timer *hbt)
449{
450 struct disasm_line *dl = disasm_line(browser->selection);
451 u64 offset;
452 s64 idx;
453
454 if (!ins__is_jump(&dl->ins))
455 return false;
456
457 if (dl->ops.target.outside) {
458 annotate_browser__callq(browser, evsel, hbt);
459 return true;
460 }
461
462 offset = dl->ops.target.offset;
463 dl = annotate_browser__find_offset(browser, offset, &idx);
464 if (dl == NULL) {
465 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
466 return true;
467 }
468
469 annotate_browser__set_top(browser, &dl->al, idx);
470
471 return true;
472}
473
474static
475struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
476 char *s, s64 *idx)
477{
478 struct annotation *notes = browser__annotation(&browser->b);
479 struct annotation_line *al = browser->selection;
480
481 *idx = browser->b.index;
482 list_for_each_entry_continue(al, ¬es->src->source, node) {
483 if (annotation_line__filter(al, notes))
484 continue;
485
486 ++*idx;
487
488 if (al->line && strstr(al->line, s) != NULL)
489 return al;
490 }
491
492 return NULL;
493}
494
495static bool __annotate_browser__search(struct annotate_browser *browser)
496{
497 struct annotation_line *al;
498 s64 idx;
499
500 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
501 if (al == NULL) {
502 ui_helpline__puts("String not found!");
503 return false;
504 }
505
506 annotate_browser__set_top(browser, al, idx);
507 browser->searching_backwards = false;
508 return true;
509}
510
511static
512struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
513 char *s, s64 *idx)
514{
515 struct annotation *notes = browser__annotation(&browser->b);
516 struct annotation_line *al = browser->selection;
517
518 *idx = browser->b.index;
519 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
520 if (annotation_line__filter(al, notes))
521 continue;
522
523 --*idx;
524
525 if (al->line && strstr(al->line, s) != NULL)
526 return al;
527 }
528
529 return NULL;
530}
531
532static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
533{
534 struct annotation_line *al;
535 s64 idx;
536
537 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
538 if (al == NULL) {
539 ui_helpline__puts("String not found!");
540 return false;
541 }
542
543 annotate_browser__set_top(browser, al, idx);
544 browser->searching_backwards = true;
545 return true;
546}
547
548static bool annotate_browser__search_window(struct annotate_browser *browser,
549 int delay_secs)
550{
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)
555 return false;
556
557 return true;
558}
559
560static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
561{
562 if (annotate_browser__search_window(browser, delay_secs))
563 return __annotate_browser__search(browser);
564
565 return false;
566}
567
568static bool annotate_browser__continue_search(struct annotate_browser *browser,
569 int delay_secs)
570{
571 if (!*browser->search_bf)
572 return annotate_browser__search(browser, delay_secs);
573
574 return __annotate_browser__search(browser);
575}
576
577static bool annotate_browser__search_reverse(struct annotate_browser *browser,
578 int delay_secs)
579{
580 if (annotate_browser__search_window(browser, delay_secs))
581 return __annotate_browser__search_reverse(browser);
582
583 return false;
584}
585
586static
587bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
588 int delay_secs)
589{
590 if (!*browser->search_bf)
591 return annotate_browser__search_reverse(browser, delay_secs);
592
593 return __annotate_browser__search_reverse(browser);
594}
595
596static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
597{
598 struct map_symbol *ms = browser->priv;
599 struct symbol *sym = ms->sym;
600 char symbol_dso[SYM_TITLE_MAX_SIZE];
601
602 if (ui_browser__show(browser, title, help) < 0)
603 return -1;
604
605 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso));
606
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);
610 return 0;
611}
612
613static int annotate_browser__run(struct annotate_browser *browser,
614 struct perf_evsel *evsel,
615 struct hist_browser_timer *hbt)
616{
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;
624 char title[256];
625 int key;
626
627 annotation__scnprintf_samples_period(notes, title, sizeof(title), evsel);
628
629 if (annotate_browser__show(&browser->b, title, help) < 0)
630 return -1;
631
632 annotate_browser__calc_percent(browser, evsel);
633
634 if (browser->curr_hot) {
635 annotate_browser__set_rb_top(browser, browser->curr_hot);
636 browser->b.navkeypressed = false;
637 }
638
639 nd = browser->curr_hot;
640
641 while (1) {
642 key = ui_browser__run(&browser->b, delay_secs);
643
644 if (delay_secs != 0) {
645 annotate_browser__calc_percent(browser, evsel);
646 /*
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).
650 */
651 if (nd != NULL && RB_EMPTY_NODE(nd))
652 nd = NULL;
653 }
654
655 switch (key) {
656 case K_TIMER:
657 if (hbt)
658 hbt->timer(hbt->arg);
659
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);
664 }
665 continue;
666 case K_TAB:
667 if (nd != NULL) {
668 nd = rb_prev(nd);
669 if (nd == NULL)
670 nd = rb_last(&browser->entries);
671 } else
672 nd = browser->curr_hot;
673 break;
674 case K_UNTAB:
675 if (nd != NULL) {
676 nd = rb_next(nd);
677 if (nd == NULL)
678 nd = rb_first(&browser->entries);
679 } else
680 nd = browser->curr_hot;
681 break;
682 case K_F1:
683 case 'h':
684 ui_browser__help_window(&browser->b,
685 "UP/DOWN/PGUP\n"
686 "PGDN/SPACE Navigate\n"
687 "q/ESC/CTRL+C Exit\n\n"
688 "ENTER Go to target\n"
689 "ESC Exit\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"
700 "/ Search string\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");
705 continue;
706 case 'r':
707 {
708 script_browse(NULL);
709 continue;
710 }
711 case 'k':
712 notes->options->show_linenr = !notes->options->show_linenr;
713 break;
714 case 'H':
715 nd = browser->curr_hot;
716 break;
717 case 's':
718 if (annotate_browser__toggle_source(browser))
719 ui_helpline__puts(help);
720 continue;
721 case 'o':
722 notes->options->use_offset = !notes->options->use_offset;
723 annotation__update_column_widths(notes);
724 continue;
725 case 'O':
726 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
727 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
728 continue;
729 case 'j':
730 notes->options->jump_arrows = !notes->options->jump_arrows;
731 continue;
732 case 'J':
733 notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
734 annotation__update_column_widths(notes);
735 continue;
736 case '/':
737 if (annotate_browser__search(browser, delay_secs)) {
738show_help:
739 ui_helpline__puts(help);
740 }
741 continue;
742 case 'n':
743 if (browser->searching_backwards ?
744 annotate_browser__continue_search_reverse(browser, delay_secs) :
745 annotate_browser__continue_search(browser, delay_secs))
746 goto show_help;
747 continue;
748 case '?':
749 if (annotate_browser__search_reverse(browser, delay_secs))
750 goto show_help;
751 continue;
752 case 'D': {
753 static int seq;
754 ui_helpline__pop();
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,
757 browser->b.height,
758 browser->b.index,
759 browser->b.top_idx,
760 notes->nr_asm_entries);
761 }
762 continue;
763 case K_ENTER:
764 case K_RIGHT:
765 {
766 struct disasm_line *dl = disasm_line(browser->selection);
767
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)
773 goto show_sup_ins;
774 else if (ins__is_ret(&dl->ins))
775 goto out;
776 else if (!(annotate_browser__jump(browser, evsel, hbt) ||
777 annotate_browser__callq(browser, evsel, hbt))) {
778show_sup_ins:
779 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
780 }
781 continue;
782 }
783 case 'P':
784 map_symbol__annotation_dump(ms, evsel);
785 continue;
786 case 't':
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;
792 else
793 notes->options->show_total_period = true;
794 annotation__update_column_widths(notes);
795 continue;
796 case 'c':
797 if (notes->options->show_minmax_cycle)
798 notes->options->show_minmax_cycle = false;
799 else
800 notes->options->show_minmax_cycle = true;
801 annotation__update_column_widths(notes);
802 continue;
803 case K_LEFT:
804 case K_ESC:
805 case 'q':
806 case CTRL('c'):
807 goto out;
808 default:
809 continue;
810 }
811
812 if (nd != NULL)
813 annotate_browser__set_rb_top(browser, nd);
814 }
815out:
816 ui_browser__hide(&browser->b);
817 return key;
818}
819
820int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
821 struct hist_browser_timer *hbt,
822 struct annotation_options *opts)
823{
824 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
825}
826
827int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
828 struct hist_browser_timer *hbt,
829 struct annotation_options *opts)
830{
831 /* reset abort key so that it can get Ctrl-C as a key */
832 SLang_reset_tty();
833 SLang_init_tty(0, 0, 0);
834
835 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
836}
837
838int 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)
842{
843 struct annotation *notes = symbol__annotation(sym);
844 struct map_symbol ms = {
845 .map = map,
846 .sym = sym,
847 };
848 struct annotate_browser browser = {
849 .b = {
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() */
855 .priv = &ms,
856 .use_navkeypressed = true,
857 },
858 .opts = opts,
859 };
860 int ret = -1, err;
861
862 if (sym == NULL)
863 return -1;
864
865 if (map->dso->annotate_warned)
866 return -1;
867
868 err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
869 if (err) {
870 char msg[BUFSIZ];
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;
874 }
875
876 ui_helpline__push("Press ESC to exit");
877
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 */
882
883 if (notes->options->hide_src_code)
884 ui_browser__init_asm_mode(&browser.b);
885
886 ret = annotate_browser__run(&browser, evsel, hbt);
887
888 annotated_source__purge(notes->src);
889
890out_free_offsets:
891 zfree(¬es->offsets);
892 return ret;
893}