Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[platform/kernel/linux-starfive.git] / tools / perf / ui / browsers / annotate.c
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/map.h"
11 #include "../../util/symbol.h"
12 #include "../../util/evsel.h"
13 #include "../../util/evlist.h"
14 #include <inttypes.h>
15 #include <pthread.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
19 #include <asm/bug.h>
20
21 struct disasm_line_samples {
22         double                percent;
23         struct sym_hist_entry he;
24 };
25
26 struct arch;
27
28 struct annotate_browser {
29         struct ui_browser           b;
30         struct rb_root              entries;
31         struct rb_node             *curr_hot;
32         struct annotation_line     *selection;
33         struct arch                *arch;
34         struct annotation_options  *opts;
35         bool                        searching_backwards;
36         char                        search_bf[128];
37 };
38
39 static inline struct annotation *browser__annotation(struct ui_browser *browser)
40 {
41         struct map_symbol *ms = browser->priv;
42         return symbol__annotation(ms->sym);
43 }
44
45 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
46 {
47         struct annotation *notes = browser__annotation(browser);
48         struct annotation_line *al = list_entry(entry, struct annotation_line, node);
49         return annotation_line__filter(al, notes);
50 }
51
52 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
53 {
54         struct annotation *notes = browser__annotation(browser);
55
56         if (current && (!browser->use_navkeypressed || browser->navkeypressed))
57                 return HE_COLORSET_SELECTED;
58         if (nr == notes->max_jump_sources)
59                 return HE_COLORSET_TOP;
60         if (nr > 1)
61                 return HE_COLORSET_MEDIUM;
62         return HE_COLORSET_NORMAL;
63 }
64
65 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
66 {
67          int color = ui_browser__jumps_percent_color(browser, nr, current);
68          return ui_browser__set_color(browser, color);
69 }
70
71 static int annotate_browser__set_color(void *browser, int color)
72 {
73         return ui_browser__set_color(browser, color);
74 }
75
76 static void annotate_browser__write_graph(void *browser, int graph)
77 {
78         ui_browser__write_graph(browser, graph);
79 }
80
81 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
82 {
83         ui_browser__set_percent_color(browser, percent, current);
84 }
85
86 static void annotate_browser__printf(void *browser, const char *fmt, ...)
87 {
88         va_list args;
89
90         va_start(args, fmt);
91         ui_browser__vprintf(browser, fmt, args);
92         va_end(args);
93 }
94
95 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
96 {
97         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
98         struct annotation *notes = browser__annotation(browser);
99         struct annotation_line *al = list_entry(entry, struct annotation_line, node);
100         struct annotation_write_ops ops = {
101                 .first_line              = row == 0,
102                 .current_entry           = ui_browser__is_current_entry(browser, row),
103                 .change_color            = (!notes->options->hide_src_code &&
104                                             (!ops.current_entry ||
105                                              (browser->use_navkeypressed &&
106                                               !browser->navkeypressed))),
107                 .width                   = browser->width,
108                 .obj                     = browser,
109                 .set_color               = annotate_browser__set_color,
110                 .set_percent_color       = annotate_browser__set_percent_color,
111                 .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
112                 .printf                  = annotate_browser__printf,
113                 .write_graph             = annotate_browser__write_graph,
114         };
115
116         /* The scroll bar isn't being used */
117         if (!browser->navkeypressed)
118                 ops.width += 1;
119
120         annotation_line__write(al, notes, &ops, ab->opts);
121
122         if (ops.current_entry)
123                 ab->selection = al;
124 }
125
126 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
127 {
128         struct disasm_line *pos = list_prev_entry(cursor, al.node);
129         const char *name;
130
131         if (!pos)
132                 return false;
133
134         if (ins__is_lock(&pos->ins))
135                 name = pos->ops.locked.ins.name;
136         else
137                 name = pos->ins.name;
138
139         if (!name || !cursor->ins.name)
140                 return false;
141
142         return ins__is_fused(ab->arch, name, cursor->ins.name);
143 }
144
145 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
146 {
147         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
148         struct disasm_line *cursor = disasm_line(ab->selection);
149         struct annotation_line *target;
150         unsigned int from, to;
151         struct map_symbol *ms = ab->b.priv;
152         struct symbol *sym = ms->sym;
153         struct annotation *notes = symbol__annotation(sym);
154         u8 pcnt_width = annotation__pcnt_width(notes);
155         int width;
156
157         /* PLT symbols contain external offsets */
158         if (strstr(sym->name, "@plt"))
159                 return;
160
161         if (!disasm_line__is_valid_local_jump(cursor, sym))
162                 return;
163
164         /*
165          * This first was seen with a gcc function, _cpp_lex_token, that
166          * has the usual jumps:
167          *
168          *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
169          *
170          * I.e. jumps to a label inside that function (_cpp_lex_token), and
171          * those works, but also this kind:
172          *
173          *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
174          *
175          *  I.e. jumps to another function, outside _cpp_lex_token, which
176          *  are not being correctly handled generating as a side effect references
177          *  to ab->offset[] entries that are set to NULL, so to make this code
178          *  more robust, check that here.
179          *
180          *  A proper fix for will be put in place, looking at the function
181          *  name right after the '<' token and probably treating this like a
182          *  'call' instruction.
183          */
184         target = notes->offsets[cursor->ops.target.offset];
185         if (target == NULL) {
186                 ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
187                                     cursor->ops.target.offset);
188                 return;
189         }
190
191         if (notes->options->hide_src_code) {
192                 from = cursor->al.idx_asm;
193                 to = target->idx_asm;
194         } else {
195                 from = (u64)cursor->al.idx;
196                 to = (u64)target->idx;
197         }
198
199         width = annotation__cycles_width(notes);
200
201         ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
202         __ui_browser__line_arrow(browser,
203                                  pcnt_width + 2 + notes->widths.addr + width,
204                                  from, to);
205
206         if (is_fused(ab, cursor)) {
207                 ui_browser__mark_fused(browser,
208                                        pcnt_width + 3 + notes->widths.addr + width,
209                                        from - 1,
210                                        to > from ? true : false);
211         }
212 }
213
214 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
215 {
216         struct annotation *notes = browser__annotation(browser);
217         int ret = ui_browser__list_head_refresh(browser);
218         int pcnt_width = annotation__pcnt_width(notes);
219
220         if (notes->options->jump_arrows)
221                 annotate_browser__draw_current_jump(browser);
222
223         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
224         __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
225         return ret;
226 }
227
228 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
229                                                   int percent_type)
230 {
231         int i;
232
233         for (i = 0; i < a->data_nr; i++) {
234                 if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
235                         continue;
236                 return a->data[i].percent[percent_type] -
237                            b->data[i].percent[percent_type];
238         }
239         return 0;
240 }
241
242 static void disasm_rb_tree__insert(struct annotate_browser *browser,
243                                 struct annotation_line *al)
244 {
245         struct rb_root *root = &browser->entries;
246         struct rb_node **p = &root->rb_node;
247         struct rb_node *parent = NULL;
248         struct annotation_line *l;
249
250         while (*p != NULL) {
251                 parent = *p;
252                 l = rb_entry(parent, struct annotation_line, rb_node);
253
254                 if (disasm__cmp(al, l, browser->opts->percent_type) < 0)
255                         p = &(*p)->rb_left;
256                 else
257                         p = &(*p)->rb_right;
258         }
259         rb_link_node(&al->rb_node, parent, p);
260         rb_insert_color(&al->rb_node, root);
261 }
262
263 static void annotate_browser__set_top(struct annotate_browser *browser,
264                                       struct annotation_line *pos, u32 idx)
265 {
266         struct annotation *notes = browser__annotation(&browser->b);
267         unsigned back;
268
269         ui_browser__refresh_dimensions(&browser->b);
270         back = browser->b.height / 2;
271         browser->b.top_idx = browser->b.index = idx;
272
273         while (browser->b.top_idx != 0 && back != 0) {
274                 pos = list_entry(pos->node.prev, struct annotation_line, node);
275
276                 if (annotation_line__filter(pos, notes))
277                         continue;
278
279                 --browser->b.top_idx;
280                 --back;
281         }
282
283         browser->b.top = pos;
284         browser->b.navkeypressed = true;
285 }
286
287 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
288                                          struct rb_node *nd)
289 {
290         struct annotation *notes = browser__annotation(&browser->b);
291         struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
292         u32 idx = pos->idx;
293
294         if (notes->options->hide_src_code)
295                 idx = pos->idx_asm;
296         annotate_browser__set_top(browser, pos, idx);
297         browser->curr_hot = nd;
298 }
299
300 static void annotate_browser__calc_percent(struct annotate_browser *browser,
301                                            struct perf_evsel *evsel)
302 {
303         struct map_symbol *ms = browser->b.priv;
304         struct symbol *sym = ms->sym;
305         struct annotation *notes = symbol__annotation(sym);
306         struct disasm_line *pos;
307
308         browser->entries = RB_ROOT;
309
310         pthread_mutex_lock(&notes->lock);
311
312         symbol__calc_percent(sym, evsel);
313
314         list_for_each_entry(pos, &notes->src->source, al.node) {
315                 double max_percent = 0.0;
316                 int i;
317
318                 if (pos->al.offset == -1) {
319                         RB_CLEAR_NODE(&pos->al.rb_node);
320                         continue;
321                 }
322
323                 for (i = 0; i < pos->al.data_nr; i++) {
324                         double percent;
325
326                         percent = annotation_data__percent(&pos->al.data[i],
327                                                            browser->opts->percent_type);
328
329                         if (max_percent < percent)
330                                 max_percent = percent;
331                 }
332
333                 if (max_percent < 0.01 && pos->al.ipc == 0) {
334                         RB_CLEAR_NODE(&pos->al.rb_node);
335                         continue;
336                 }
337                 disasm_rb_tree__insert(browser, &pos->al);
338         }
339         pthread_mutex_unlock(&notes->lock);
340
341         browser->curr_hot = rb_last(&browser->entries);
342 }
343
344 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
345 {
346         struct annotation *notes = browser__annotation(&browser->b);
347         struct annotation_line *al;
348         off_t offset = browser->b.index - browser->b.top_idx;
349
350         browser->b.seek(&browser->b, offset, SEEK_CUR);
351         al = list_entry(browser->b.top, struct annotation_line, node);
352
353         if (notes->options->hide_src_code) {
354                 if (al->idx_asm < offset)
355                         offset = al->idx;
356
357                 browser->b.nr_entries = notes->nr_entries;
358                 notes->options->hide_src_code = false;
359                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
360                 browser->b.top_idx = al->idx - offset;
361                 browser->b.index = al->idx;
362         } else {
363                 if (al->idx_asm < 0) {
364                         ui_helpline__puts("Only available for assembly lines.");
365                         browser->b.seek(&browser->b, -offset, SEEK_CUR);
366                         return false;
367                 }
368
369                 if (al->idx_asm < offset)
370                         offset = al->idx_asm;
371
372                 browser->b.nr_entries = notes->nr_asm_entries;
373                 notes->options->hide_src_code = true;
374                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
375                 browser->b.top_idx = al->idx_asm - offset;
376                 browser->b.index = al->idx_asm;
377         }
378
379         return true;
380 }
381
382 static void ui_browser__init_asm_mode(struct ui_browser *browser)
383 {
384         struct annotation *notes = browser__annotation(browser);
385         ui_browser__reset_index(browser);
386         browser->nr_entries = notes->nr_asm_entries;
387 }
388
389 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
390
391 static int sym_title(struct symbol *sym, struct map *map, char *title,
392                      size_t sz, int percent_type)
393 {
394         return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
395                         percent_type_str(percent_type));
396 }
397
398 /*
399  * This can be called from external jumps, i.e. jumps from one functon
400  * to another, like from the kernel's entry_SYSCALL_64 function to the
401  * swapgs_restore_regs_and_return_to_usermode() function.
402  *
403  * So all we check here is that dl->ops.target.sym is set, if it is, just
404  * go to that function and when exiting from its disassembly, come back
405  * to the calling function.
406  */
407 static bool annotate_browser__callq(struct annotate_browser *browser,
408                                     struct perf_evsel *evsel,
409                                     struct hist_browser_timer *hbt)
410 {
411         struct map_symbol *ms = browser->b.priv;
412         struct disasm_line *dl = disasm_line(browser->selection);
413         struct annotation *notes;
414         char title[SYM_TITLE_MAX_SIZE];
415
416         if (!dl->ops.target.sym) {
417                 ui_helpline__puts("The called function was not found.");
418                 return true;
419         }
420
421         notes = symbol__annotation(dl->ops.target.sym);
422         pthread_mutex_lock(&notes->lock);
423
424         if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
425                 pthread_mutex_unlock(&notes->lock);
426                 ui__warning("Not enough memory for annotating '%s' symbol!\n",
427                             dl->ops.target.sym->name);
428                 return true;
429         }
430
431         pthread_mutex_unlock(&notes->lock);
432         symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
433         sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
434         ui_browser__show_title(&browser->b, title);
435         return true;
436 }
437
438 static
439 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
440                                           s64 offset, s64 *idx)
441 {
442         struct annotation *notes = browser__annotation(&browser->b);
443         struct disasm_line *pos;
444
445         *idx = 0;
446         list_for_each_entry(pos, &notes->src->source, al.node) {
447                 if (pos->al.offset == offset)
448                         return pos;
449                 if (!annotation_line__filter(&pos->al, notes))
450                         ++*idx;
451         }
452
453         return NULL;
454 }
455
456 static bool annotate_browser__jump(struct annotate_browser *browser,
457                                    struct perf_evsel *evsel,
458                                    struct hist_browser_timer *hbt)
459 {
460         struct disasm_line *dl = disasm_line(browser->selection);
461         u64 offset;
462         s64 idx;
463
464         if (!ins__is_jump(&dl->ins))
465                 return false;
466
467         if (dl->ops.target.outside) {
468                 annotate_browser__callq(browser, evsel, hbt);
469                 return true;
470         }
471
472         offset = dl->ops.target.offset;
473         dl = annotate_browser__find_offset(browser, offset, &idx);
474         if (dl == NULL) {
475                 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
476                 return true;
477         }
478
479         annotate_browser__set_top(browser, &dl->al, idx);
480
481         return true;
482 }
483
484 static
485 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
486                                           char *s, s64 *idx)
487 {
488         struct annotation *notes = browser__annotation(&browser->b);
489         struct annotation_line *al = browser->selection;
490
491         *idx = browser->b.index;
492         list_for_each_entry_continue(al, &notes->src->source, node) {
493                 if (annotation_line__filter(al, notes))
494                         continue;
495
496                 ++*idx;
497
498                 if (al->line && strstr(al->line, s) != NULL)
499                         return al;
500         }
501
502         return NULL;
503 }
504
505 static bool __annotate_browser__search(struct annotate_browser *browser)
506 {
507         struct annotation_line *al;
508         s64 idx;
509
510         al = annotate_browser__find_string(browser, browser->search_bf, &idx);
511         if (al == NULL) {
512                 ui_helpline__puts("String not found!");
513                 return false;
514         }
515
516         annotate_browser__set_top(browser, al, idx);
517         browser->searching_backwards = false;
518         return true;
519 }
520
521 static
522 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
523                                                   char *s, s64 *idx)
524 {
525         struct annotation *notes = browser__annotation(&browser->b);
526         struct annotation_line *al = browser->selection;
527
528         *idx = browser->b.index;
529         list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
530                 if (annotation_line__filter(al, notes))
531                         continue;
532
533                 --*idx;
534
535                 if (al->line && strstr(al->line, s) != NULL)
536                         return al;
537         }
538
539         return NULL;
540 }
541
542 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
543 {
544         struct annotation_line *al;
545         s64 idx;
546
547         al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
548         if (al == NULL) {
549                 ui_helpline__puts("String not found!");
550                 return false;
551         }
552
553         annotate_browser__set_top(browser, al, idx);
554         browser->searching_backwards = true;
555         return true;
556 }
557
558 static bool annotate_browser__search_window(struct annotate_browser *browser,
559                                             int delay_secs)
560 {
561         if (ui_browser__input_window("Search", "String: ", browser->search_bf,
562                                      "ENTER: OK, ESC: Cancel",
563                                      delay_secs * 2) != K_ENTER ||
564             !*browser->search_bf)
565                 return false;
566
567         return true;
568 }
569
570 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
571 {
572         if (annotate_browser__search_window(browser, delay_secs))
573                 return __annotate_browser__search(browser);
574
575         return false;
576 }
577
578 static bool annotate_browser__continue_search(struct annotate_browser *browser,
579                                               int delay_secs)
580 {
581         if (!*browser->search_bf)
582                 return annotate_browser__search(browser, delay_secs);
583
584         return __annotate_browser__search(browser);
585 }
586
587 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
588                                            int delay_secs)
589 {
590         if (annotate_browser__search_window(browser, delay_secs))
591                 return __annotate_browser__search_reverse(browser);
592
593         return false;
594 }
595
596 static
597 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
598                                                int delay_secs)
599 {
600         if (!*browser->search_bf)
601                 return annotate_browser__search_reverse(browser, delay_secs);
602
603         return __annotate_browser__search_reverse(browser);
604 }
605
606 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
607 {
608         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
609         struct map_symbol *ms = browser->priv;
610         struct symbol *sym = ms->sym;
611         char symbol_dso[SYM_TITLE_MAX_SIZE];
612
613         if (ui_browser__show(browser, title, help) < 0)
614                 return -1;
615
616         sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
617
618         ui_browser__gotorc_title(browser, 0, 0);
619         ui_browser__set_color(browser, HE_COLORSET_ROOT);
620         ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
621         return 0;
622 }
623
624 static void
625 switch_percent_type(struct annotation_options *opts, bool base)
626 {
627         switch (opts->percent_type) {
628         case PERCENT_HITS_LOCAL:
629                 if (base)
630                         opts->percent_type = PERCENT_PERIOD_LOCAL;
631                 else
632                         opts->percent_type = PERCENT_HITS_GLOBAL;
633                 break;
634         case PERCENT_HITS_GLOBAL:
635                 if (base)
636                         opts->percent_type = PERCENT_PERIOD_GLOBAL;
637                 else
638                         opts->percent_type = PERCENT_HITS_LOCAL;
639                 break;
640         case PERCENT_PERIOD_LOCAL:
641                 if (base)
642                         opts->percent_type = PERCENT_HITS_LOCAL;
643                 else
644                         opts->percent_type = PERCENT_PERIOD_GLOBAL;
645                 break;
646         case PERCENT_PERIOD_GLOBAL:
647                 if (base)
648                         opts->percent_type = PERCENT_HITS_GLOBAL;
649                 else
650                         opts->percent_type = PERCENT_PERIOD_LOCAL;
651                 break;
652         default:
653                 WARN_ON(1);
654         }
655 }
656
657 static int annotate_browser__run(struct annotate_browser *browser,
658                                  struct perf_evsel *evsel,
659                                  struct hist_browser_timer *hbt)
660 {
661         struct rb_node *nd = NULL;
662         struct hists *hists = evsel__hists(evsel);
663         struct map_symbol *ms = browser->b.priv;
664         struct symbol *sym = ms->sym;
665         struct annotation *notes = symbol__annotation(ms->sym);
666         const char *help = "Press 'h' for help on key bindings";
667         int delay_secs = hbt ? hbt->refresh : 0;
668         char title[256];
669         int key;
670
671         hists__scnprintf_title(hists, title, sizeof(title));
672         if (annotate_browser__show(&browser->b, title, help) < 0)
673                 return -1;
674
675         annotate_browser__calc_percent(browser, evsel);
676
677         if (browser->curr_hot) {
678                 annotate_browser__set_rb_top(browser, browser->curr_hot);
679                 browser->b.navkeypressed = false;
680         }
681
682         nd = browser->curr_hot;
683
684         while (1) {
685                 key = ui_browser__run(&browser->b, delay_secs);
686
687                 if (delay_secs != 0) {
688                         annotate_browser__calc_percent(browser, evsel);
689                         /*
690                          * Current line focus got out of the list of most active
691                          * lines, NULL it so that if TAB|UNTAB is pressed, we
692                          * move to curr_hot (current hottest line).
693                          */
694                         if (nd != NULL && RB_EMPTY_NODE(nd))
695                                 nd = NULL;
696                 }
697
698                 switch (key) {
699                 case K_TIMER:
700                         if (hbt)
701                                 hbt->timer(hbt->arg);
702
703                         if (delay_secs != 0) {
704                                 symbol__annotate_decay_histogram(sym, evsel->idx);
705                                 hists__scnprintf_title(hists, title, sizeof(title));
706                                 annotate_browser__show(&browser->b, title, help);
707                         }
708                         continue;
709                 case K_TAB:
710                         if (nd != NULL) {
711                                 nd = rb_prev(nd);
712                                 if (nd == NULL)
713                                         nd = rb_last(&browser->entries);
714                         } else
715                                 nd = browser->curr_hot;
716                         break;
717                 case K_UNTAB:
718                         if (nd != NULL) {
719                                 nd = rb_next(nd);
720                                 if (nd == NULL)
721                                         nd = rb_first(&browser->entries);
722                         } else
723                                 nd = browser->curr_hot;
724                         break;
725                 case K_F1:
726                 case 'h':
727                         ui_browser__help_window(&browser->b,
728                 "UP/DOWN/PGUP\n"
729                 "PGDN/SPACE    Navigate\n"
730                 "q/ESC/CTRL+C  Exit\n\n"
731                 "ENTER         Go to target\n"
732                 "ESC           Exit\n"
733                 "H             Go to hottest instruction\n"
734                 "TAB/shift+TAB Cycle thru hottest instructions\n"
735                 "j             Toggle showing jump to target arrows\n"
736                 "J             Toggle showing number of jump sources on targets\n"
737                 "n             Search next string\n"
738                 "o             Toggle disassembler output/simplified view\n"
739                 "O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
740                 "s             Toggle source code view\n"
741                 "t             Circulate percent, total period, samples view\n"
742                 "c             Show min/max cycle\n"
743                 "/             Search string\n"
744                 "k             Toggle line numbers\n"
745                 "P             Print to [symbol_name].annotation file.\n"
746                 "r             Run available scripts\n"
747                 "p             Toggle percent type [local/global]\n"
748                 "b             Toggle percent base [period/hits]\n"
749                 "?             Search string backwards\n");
750                         continue;
751                 case 'r':
752                         {
753                                 script_browse(NULL, NULL);
754                                 continue;
755                         }
756                 case 'k':
757                         notes->options->show_linenr = !notes->options->show_linenr;
758                         break;
759                 case 'H':
760                         nd = browser->curr_hot;
761                         break;
762                 case 's':
763                         if (annotate_browser__toggle_source(browser))
764                                 ui_helpline__puts(help);
765                         continue;
766                 case 'o':
767                         notes->options->use_offset = !notes->options->use_offset;
768                         annotation__update_column_widths(notes);
769                         continue;
770                 case 'O':
771                         if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
772                                 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
773                         continue;
774                 case 'j':
775                         notes->options->jump_arrows = !notes->options->jump_arrows;
776                         continue;
777                 case 'J':
778                         notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
779                         annotation__update_column_widths(notes);
780                         continue;
781                 case '/':
782                         if (annotate_browser__search(browser, delay_secs)) {
783 show_help:
784                                 ui_helpline__puts(help);
785                         }
786                         continue;
787                 case 'n':
788                         if (browser->searching_backwards ?
789                             annotate_browser__continue_search_reverse(browser, delay_secs) :
790                             annotate_browser__continue_search(browser, delay_secs))
791                                 goto show_help;
792                         continue;
793                 case '?':
794                         if (annotate_browser__search_reverse(browser, delay_secs))
795                                 goto show_help;
796                         continue;
797                 case 'D': {
798                         static int seq;
799                         ui_helpline__pop();
800                         ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
801                                            seq++, browser->b.nr_entries,
802                                            browser->b.height,
803                                            browser->b.index,
804                                            browser->b.top_idx,
805                                            notes->nr_asm_entries);
806                 }
807                         continue;
808                 case K_ENTER:
809                 case K_RIGHT:
810                 {
811                         struct disasm_line *dl = disasm_line(browser->selection);
812
813                         if (browser->selection == NULL)
814                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
815                         else if (browser->selection->offset == -1)
816                                 ui_helpline__puts("Actions are only available for assembly lines.");
817                         else if (!dl->ins.ops)
818                                 goto show_sup_ins;
819                         else if (ins__is_ret(&dl->ins))
820                                 goto out;
821                         else if (!(annotate_browser__jump(browser, evsel, hbt) ||
822                                      annotate_browser__callq(browser, evsel, hbt))) {
823 show_sup_ins:
824                                 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
825                         }
826                         continue;
827                 }
828                 case 'P':
829                         map_symbol__annotation_dump(ms, evsel, browser->opts);
830                         continue;
831                 case 't':
832                         if (notes->options->show_total_period) {
833                                 notes->options->show_total_period = false;
834                                 notes->options->show_nr_samples = true;
835                         } else if (notes->options->show_nr_samples)
836                                 notes->options->show_nr_samples = false;
837                         else
838                                 notes->options->show_total_period = true;
839                         annotation__update_column_widths(notes);
840                         continue;
841                 case 'c':
842                         if (notes->options->show_minmax_cycle)
843                                 notes->options->show_minmax_cycle = false;
844                         else
845                                 notes->options->show_minmax_cycle = true;
846                         annotation__update_column_widths(notes);
847                         continue;
848                 case 'p':
849                 case 'b':
850                         switch_percent_type(browser->opts, key == 'b');
851                         hists__scnprintf_title(hists, title, sizeof(title));
852                         annotate_browser__show(&browser->b, title, help);
853                         continue;
854                 case K_LEFT:
855                 case K_ESC:
856                 case 'q':
857                 case CTRL('c'):
858                         goto out;
859                 default:
860                         continue;
861                 }
862
863                 if (nd != NULL)
864                         annotate_browser__set_rb_top(browser, nd);
865         }
866 out:
867         ui_browser__hide(&browser->b);
868         return key;
869 }
870
871 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
872                              struct hist_browser_timer *hbt,
873                              struct annotation_options *opts)
874 {
875         return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
876 }
877
878 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
879                              struct hist_browser_timer *hbt,
880                              struct annotation_options *opts)
881 {
882         /* reset abort key so that it can get Ctrl-C as a key */
883         SLang_reset_tty();
884         SLang_init_tty(0, 0, 0);
885
886         return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
887 }
888
889 int symbol__tui_annotate(struct symbol *sym, struct map *map,
890                          struct perf_evsel *evsel,
891                          struct hist_browser_timer *hbt,
892                          struct annotation_options *opts)
893 {
894         struct annotation *notes = symbol__annotation(sym);
895         struct map_symbol ms = {
896                 .map = map,
897                 .sym = sym,
898         };
899         struct annotate_browser browser = {
900                 .b = {
901                         .refresh = annotate_browser__refresh,
902                         .seek    = ui_browser__list_head_seek,
903                         .write   = annotate_browser__write,
904                         .filter  = disasm_line__filter,
905                         .extra_title_lines = 1, /* for hists__scnprintf_title() */
906                         .priv    = &ms,
907                         .use_navkeypressed = true,
908                 },
909                 .opts = opts,
910         };
911         int ret = -1, err;
912
913         if (sym == NULL)
914                 return -1;
915
916         if (map->dso->annotate_warned)
917                 return -1;
918
919         err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
920         if (err) {
921                 char msg[BUFSIZ];
922                 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
923                 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
924                 goto out_free_offsets;
925         }
926
927         ui_helpline__push("Press ESC to exit");
928
929         browser.b.width = notes->max_line_len;
930         browser.b.nr_entries = notes->nr_entries;
931         browser.b.entries = &notes->src->source,
932         browser.b.width += 18; /* Percentage */
933
934         if (notes->options->hide_src_code)
935                 ui_browser__init_asm_mode(&browser.b);
936
937         ret = annotate_browser__run(&browser, evsel, hbt);
938
939         annotated_source__purge(notes->src);
940
941 out_free_offsets:
942         zfree(&notes->offsets);
943         return ret;
944 }