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