perf annotate: Re-add annotate_warned functionality
[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 struct annotation_line *annotate_browser__find_next_asm_line(
347                                         struct annotate_browser *browser,
348                                         struct annotation_line *al)
349 {
350         struct annotation_line *it = al;
351
352         /* find next asm line */
353         list_for_each_entry_continue(it, browser->b.entries, node) {
354                 if (it->idx_asm >= 0)
355                         return it;
356         }
357
358         /* no asm line found forwards, try backwards */
359         it = al;
360         list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
361                 if (it->idx_asm >= 0)
362                         return it;
363         }
364
365         /* There are no asm lines */
366         return NULL;
367 }
368
369 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
370 {
371         struct annotation *notes = browser__annotation(&browser->b);
372         struct annotation_line *al;
373         off_t offset = browser->b.index - browser->b.top_idx;
374
375         browser->b.seek(&browser->b, offset, SEEK_CUR);
376         al = list_entry(browser->b.top, struct annotation_line, node);
377
378         if (notes->options->hide_src_code) {
379                 if (al->idx_asm < offset)
380                         offset = al->idx;
381
382                 browser->b.nr_entries = notes->nr_entries;
383                 notes->options->hide_src_code = false;
384                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
385                 browser->b.top_idx = al->idx - offset;
386                 browser->b.index = al->idx;
387         } else {
388                 if (al->idx_asm < 0) {
389                         /* move cursor to next asm line */
390                         al = annotate_browser__find_next_asm_line(browser, al);
391                         if (!al) {
392                                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
393                                 return false;
394                         }
395                 }
396
397                 if (al->idx_asm < offset)
398                         offset = al->idx_asm;
399
400                 browser->b.nr_entries = notes->nr_asm_entries;
401                 notes->options->hide_src_code = true;
402                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
403                 browser->b.top_idx = al->idx_asm - offset;
404                 browser->b.index = al->idx_asm;
405         }
406
407         return true;
408 }
409
410 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
411
412 static void annotate_browser__show_full_location(struct ui_browser *browser)
413 {
414         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
415         struct disasm_line *cursor = disasm_line(ab->selection);
416         struct annotation_line *al = &cursor->al;
417
418         if (al->offset != -1)
419                 ui_helpline__puts("Only available for source code lines.");
420         else if (al->fileloc == NULL)
421                 ui_helpline__puts("No source file location.");
422         else {
423                 char help_line[SYM_TITLE_MAX_SIZE];
424                 sprintf (help_line, "Source file location: %s", al->fileloc);
425                 ui_helpline__puts(help_line);
426         }
427 }
428
429 static void ui_browser__init_asm_mode(struct ui_browser *browser)
430 {
431         struct annotation *notes = browser__annotation(browser);
432         ui_browser__reset_index(browser);
433         browser->nr_entries = notes->nr_asm_entries;
434 }
435
436 static int sym_title(struct symbol *sym, struct map *map, char *title,
437                      size_t sz, int percent_type)
438 {
439         return snprintf(title, sz, "%s  %s [Percent: %s]", sym->name, map->dso->long_name,
440                         percent_type_str(percent_type));
441 }
442
443 /*
444  * This can be called from external jumps, i.e. jumps from one function
445  * to another, like from the kernel's entry_SYSCALL_64 function to the
446  * swapgs_restore_regs_and_return_to_usermode() function.
447  *
448  * So all we check here is that dl->ops.target.sym is set, if it is, just
449  * go to that function and when exiting from its disassembly, come back
450  * to the calling function.
451  */
452 static bool annotate_browser__callq(struct annotate_browser *browser,
453                                     struct evsel *evsel,
454                                     struct hist_browser_timer *hbt)
455 {
456         struct map_symbol *ms = browser->b.priv, target_ms;
457         struct disasm_line *dl = disasm_line(browser->selection);
458         struct annotation *notes;
459         char title[SYM_TITLE_MAX_SIZE];
460
461         if (!dl->ops.target.sym) {
462                 ui_helpline__puts("The called function was not found.");
463                 return true;
464         }
465
466         notes = symbol__annotation(dl->ops.target.sym);
467         pthread_mutex_lock(&notes->lock);
468
469         if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
470                 pthread_mutex_unlock(&notes->lock);
471                 ui__warning("Not enough memory for annotating '%s' symbol!\n",
472                             dl->ops.target.sym->name);
473                 return true;
474         }
475
476         target_ms.maps = ms->maps;
477         target_ms.map = ms->map;
478         target_ms.sym = dl->ops.target.sym;
479         pthread_mutex_unlock(&notes->lock);
480         symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts);
481         sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
482         ui_browser__show_title(&browser->b, title);
483         return true;
484 }
485
486 static
487 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
488                                           s64 offset, s64 *idx)
489 {
490         struct annotation *notes = browser__annotation(&browser->b);
491         struct disasm_line *pos;
492
493         *idx = 0;
494         list_for_each_entry(pos, &notes->src->source, al.node) {
495                 if (pos->al.offset == offset)
496                         return pos;
497                 if (!annotation_line__filter(&pos->al, notes))
498                         ++*idx;
499         }
500
501         return NULL;
502 }
503
504 static bool annotate_browser__jump(struct annotate_browser *browser,
505                                    struct evsel *evsel,
506                                    struct hist_browser_timer *hbt)
507 {
508         struct disasm_line *dl = disasm_line(browser->selection);
509         u64 offset;
510         s64 idx;
511
512         if (!ins__is_jump(&dl->ins))
513                 return false;
514
515         if (dl->ops.target.outside) {
516                 annotate_browser__callq(browser, evsel, hbt);
517                 return true;
518         }
519
520         offset = dl->ops.target.offset;
521         dl = annotate_browser__find_offset(browser, offset, &idx);
522         if (dl == NULL) {
523                 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
524                 return true;
525         }
526
527         annotate_browser__set_top(browser, &dl->al, idx);
528
529         return true;
530 }
531
532 static
533 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
534                                           char *s, s64 *idx)
535 {
536         struct annotation *notes = browser__annotation(&browser->b);
537         struct annotation_line *al = browser->selection;
538
539         *idx = browser->b.index;
540         list_for_each_entry_continue(al, &notes->src->source, node) {
541                 if (annotation_line__filter(al, notes))
542                         continue;
543
544                 ++*idx;
545
546                 if (al->line && strstr(al->line, s) != NULL)
547                         return al;
548         }
549
550         return NULL;
551 }
552
553 static bool __annotate_browser__search(struct annotate_browser *browser)
554 {
555         struct annotation_line *al;
556         s64 idx;
557
558         al = annotate_browser__find_string(browser, browser->search_bf, &idx);
559         if (al == NULL) {
560                 ui_helpline__puts("String not found!");
561                 return false;
562         }
563
564         annotate_browser__set_top(browser, al, idx);
565         browser->searching_backwards = false;
566         return true;
567 }
568
569 static
570 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
571                                                   char *s, s64 *idx)
572 {
573         struct annotation *notes = browser__annotation(&browser->b);
574         struct annotation_line *al = browser->selection;
575
576         *idx = browser->b.index;
577         list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
578                 if (annotation_line__filter(al, notes))
579                         continue;
580
581                 --*idx;
582
583                 if (al->line && strstr(al->line, s) != NULL)
584                         return al;
585         }
586
587         return NULL;
588 }
589
590 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
591 {
592         struct annotation_line *al;
593         s64 idx;
594
595         al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
596         if (al == NULL) {
597                 ui_helpline__puts("String not found!");
598                 return false;
599         }
600
601         annotate_browser__set_top(browser, al, idx);
602         browser->searching_backwards = true;
603         return true;
604 }
605
606 static bool annotate_browser__search_window(struct annotate_browser *browser,
607                                             int delay_secs)
608 {
609         if (ui_browser__input_window("Search", "String: ", browser->search_bf,
610                                      "ENTER: OK, ESC: Cancel",
611                                      delay_secs * 2) != K_ENTER ||
612             !*browser->search_bf)
613                 return false;
614
615         return true;
616 }
617
618 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
619 {
620         if (annotate_browser__search_window(browser, delay_secs))
621                 return __annotate_browser__search(browser);
622
623         return false;
624 }
625
626 static bool annotate_browser__continue_search(struct annotate_browser *browser,
627                                               int delay_secs)
628 {
629         if (!*browser->search_bf)
630                 return annotate_browser__search(browser, delay_secs);
631
632         return __annotate_browser__search(browser);
633 }
634
635 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
636                                            int delay_secs)
637 {
638         if (annotate_browser__search_window(browser, delay_secs))
639                 return __annotate_browser__search_reverse(browser);
640
641         return false;
642 }
643
644 static
645 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
646                                                int delay_secs)
647 {
648         if (!*browser->search_bf)
649                 return annotate_browser__search_reverse(browser, delay_secs);
650
651         return __annotate_browser__search_reverse(browser);
652 }
653
654 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
655 {
656         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
657         struct map_symbol *ms = browser->priv;
658         struct symbol *sym = ms->sym;
659         char symbol_dso[SYM_TITLE_MAX_SIZE];
660
661         if (ui_browser__show(browser, title, help) < 0)
662                 return -1;
663
664         sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
665
666         ui_browser__gotorc_title(browser, 0, 0);
667         ui_browser__set_color(browser, HE_COLORSET_ROOT);
668         ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
669         return 0;
670 }
671
672 static void
673 switch_percent_type(struct annotation_options *opts, bool base)
674 {
675         switch (opts->percent_type) {
676         case PERCENT_HITS_LOCAL:
677                 if (base)
678                         opts->percent_type = PERCENT_PERIOD_LOCAL;
679                 else
680                         opts->percent_type = PERCENT_HITS_GLOBAL;
681                 break;
682         case PERCENT_HITS_GLOBAL:
683                 if (base)
684                         opts->percent_type = PERCENT_PERIOD_GLOBAL;
685                 else
686                         opts->percent_type = PERCENT_HITS_LOCAL;
687                 break;
688         case PERCENT_PERIOD_LOCAL:
689                 if (base)
690                         opts->percent_type = PERCENT_HITS_LOCAL;
691                 else
692                         opts->percent_type = PERCENT_PERIOD_GLOBAL;
693                 break;
694         case PERCENT_PERIOD_GLOBAL:
695                 if (base)
696                         opts->percent_type = PERCENT_HITS_GLOBAL;
697                 else
698                         opts->percent_type = PERCENT_PERIOD_LOCAL;
699                 break;
700         default:
701                 WARN_ON(1);
702         }
703 }
704
705 static int annotate_browser__run(struct annotate_browser *browser,
706                                  struct evsel *evsel,
707                                  struct hist_browser_timer *hbt)
708 {
709         struct rb_node *nd = NULL;
710         struct hists *hists = evsel__hists(evsel);
711         struct map_symbol *ms = browser->b.priv;
712         struct symbol *sym = ms->sym;
713         struct annotation *notes = symbol__annotation(ms->sym);
714         const char *help = "Press 'h' for help on key bindings";
715         int delay_secs = hbt ? hbt->refresh : 0;
716         char title[256];
717         int key;
718
719         hists__scnprintf_title(hists, title, sizeof(title));
720         if (annotate_browser__show(&browser->b, title, help) < 0)
721                 return -1;
722
723         annotate_browser__calc_percent(browser, evsel);
724
725         if (browser->curr_hot) {
726                 annotate_browser__set_rb_top(browser, browser->curr_hot);
727                 browser->b.navkeypressed = false;
728         }
729
730         nd = browser->curr_hot;
731
732         while (1) {
733                 key = ui_browser__run(&browser->b, delay_secs);
734
735                 if (delay_secs != 0) {
736                         annotate_browser__calc_percent(browser, evsel);
737                         /*
738                          * Current line focus got out of the list of most active
739                          * lines, NULL it so that if TAB|UNTAB is pressed, we
740                          * move to curr_hot (current hottest line).
741                          */
742                         if (nd != NULL && RB_EMPTY_NODE(nd))
743                                 nd = NULL;
744                 }
745
746                 switch (key) {
747                 case K_TIMER:
748                         if (hbt)
749                                 hbt->timer(hbt->arg);
750
751                         if (delay_secs != 0) {
752                                 symbol__annotate_decay_histogram(sym, evsel->core.idx);
753                                 hists__scnprintf_title(hists, title, sizeof(title));
754                                 annotate_browser__show(&browser->b, title, help);
755                         }
756                         continue;
757                 case K_TAB:
758                         if (nd != NULL) {
759                                 nd = rb_prev(nd);
760                                 if (nd == NULL)
761                                         nd = rb_last(&browser->entries);
762                         } else
763                                 nd = browser->curr_hot;
764                         break;
765                 case K_UNTAB:
766                         if (nd != NULL) {
767                                 nd = rb_next(nd);
768                                 if (nd == NULL)
769                                         nd = rb_first(&browser->entries);
770                         } else
771                                 nd = browser->curr_hot;
772                         break;
773                 case K_F1:
774                 case 'h':
775                         ui_browser__help_window(&browser->b,
776                 "UP/DOWN/PGUP\n"
777                 "PGDN/SPACE    Navigate\n"
778                 "q/ESC/CTRL+C  Exit\n\n"
779                 "ENTER         Go to target\n"
780                 "ESC           Exit\n"
781                 "H             Go to hottest instruction\n"
782                 "TAB/shift+TAB Cycle thru hottest instructions\n"
783                 "j             Toggle showing jump to target arrows\n"
784                 "J             Toggle showing number of jump sources on targets\n"
785                 "n             Search next string\n"
786                 "o             Toggle disassembler output/simplified view\n"
787                 "O             Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
788                 "s             Toggle source code view\n"
789                 "t             Circulate percent, total period, samples view\n"
790                 "c             Show min/max cycle\n"
791                 "/             Search string\n"
792                 "k             Toggle line numbers\n"
793                 "l             Show full source file location\n"
794                 "P             Print to [symbol_name].annotation file.\n"
795                 "r             Run available scripts\n"
796                 "p             Toggle percent type [local/global]\n"
797                 "b             Toggle percent base [period/hits]\n"
798                 "?             Search string backwards\n");
799                         continue;
800                 case 'r':
801                         script_browse(NULL, NULL);
802                         annotate_browser__show(&browser->b, title, help);
803                         continue;
804                 case 'k':
805                         notes->options->show_linenr = !notes->options->show_linenr;
806                         continue;
807                 case 'l':
808                         annotate_browser__show_full_location (&browser->b);
809                         continue;
810                 case 'H':
811                         nd = browser->curr_hot;
812                         break;
813                 case 's':
814                         if (annotate_browser__toggle_source(browser))
815                                 ui_helpline__puts(help);
816                         continue;
817                 case 'o':
818                         notes->options->use_offset = !notes->options->use_offset;
819                         annotation__update_column_widths(notes);
820                         continue;
821                 case 'O':
822                         if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
823                                 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
824                         continue;
825                 case 'j':
826                         notes->options->jump_arrows = !notes->options->jump_arrows;
827                         continue;
828                 case 'J':
829                         notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
830                         annotation__update_column_widths(notes);
831                         continue;
832                 case '/':
833                         if (annotate_browser__search(browser, delay_secs)) {
834 show_help:
835                                 ui_helpline__puts(help);
836                         }
837                         continue;
838                 case 'n':
839                         if (browser->searching_backwards ?
840                             annotate_browser__continue_search_reverse(browser, delay_secs) :
841                             annotate_browser__continue_search(browser, delay_secs))
842                                 goto show_help;
843                         continue;
844                 case '?':
845                         if (annotate_browser__search_reverse(browser, delay_secs))
846                                 goto show_help;
847                         continue;
848                 case 'D': {
849                         static int seq;
850                         ui_helpline__pop();
851                         ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
852                                            seq++, browser->b.nr_entries,
853                                            browser->b.height,
854                                            browser->b.index,
855                                            browser->b.top_idx,
856                                            notes->nr_asm_entries);
857                 }
858                         continue;
859                 case K_ENTER:
860                 case K_RIGHT:
861                 {
862                         struct disasm_line *dl = disasm_line(browser->selection);
863
864                         if (browser->selection == NULL)
865                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
866                         else if (browser->selection->offset == -1)
867                                 ui_helpline__puts("Actions are only available for assembly lines.");
868                         else if (!dl->ins.ops)
869                                 goto show_sup_ins;
870                         else if (ins__is_ret(&dl->ins))
871                                 goto out;
872                         else if (!(annotate_browser__jump(browser, evsel, hbt) ||
873                                      annotate_browser__callq(browser, evsel, hbt))) {
874 show_sup_ins:
875                                 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
876                         }
877                         continue;
878                 }
879                 case 'P':
880                         map_symbol__annotation_dump(ms, evsel, browser->opts);
881                         continue;
882                 case 't':
883                         if (symbol_conf.show_total_period) {
884                                 symbol_conf.show_total_period = false;
885                                 symbol_conf.show_nr_samples = true;
886                         } else if (symbol_conf.show_nr_samples)
887                                 symbol_conf.show_nr_samples = false;
888                         else
889                                 symbol_conf.show_total_period = true;
890                         annotation__update_column_widths(notes);
891                         continue;
892                 case 'c':
893                         if (notes->options->show_minmax_cycle)
894                                 notes->options->show_minmax_cycle = false;
895                         else
896                                 notes->options->show_minmax_cycle = true;
897                         annotation__update_column_widths(notes);
898                         continue;
899                 case 'p':
900                 case 'b':
901                         switch_percent_type(browser->opts, key == 'b');
902                         hists__scnprintf_title(hists, title, sizeof(title));
903                         annotate_browser__show(&browser->b, title, help);
904                         continue;
905                 case K_LEFT:
906                 case K_ESC:
907                 case 'q':
908                 case CTRL('c'):
909                         goto out;
910                 default:
911                         continue;
912                 }
913
914                 if (nd != NULL)
915                         annotate_browser__set_rb_top(browser, nd);
916         }
917 out:
918         ui_browser__hide(&browser->b);
919         return key;
920 }
921
922 int map_symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
923                              struct hist_browser_timer *hbt,
924                              struct annotation_options *opts)
925 {
926         return symbol__tui_annotate(ms, evsel, hbt, opts);
927 }
928
929 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
930                              struct hist_browser_timer *hbt,
931                              struct annotation_options *opts)
932 {
933         /* reset abort key so that it can get Ctrl-C as a key */
934         SLang_reset_tty();
935         SLang_init_tty(0, 0, 0);
936
937         return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
938 }
939
940 int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel,
941                          struct hist_browser_timer *hbt,
942                          struct annotation_options *opts)
943 {
944         struct symbol *sym = ms->sym;
945         struct annotation *notes = symbol__annotation(sym);
946         struct annotate_browser browser = {
947                 .b = {
948                         .refresh = annotate_browser__refresh,
949                         .seek    = ui_browser__list_head_seek,
950                         .write   = annotate_browser__write,
951                         .filter  = disasm_line__filter,
952                         .extra_title_lines = 1, /* for hists__scnprintf_title() */
953                         .priv    = ms,
954                         .use_navkeypressed = true,
955                 },
956                 .opts = opts,
957         };
958         int ret = -1, err;
959
960         if (sym == NULL)
961                 return -1;
962
963         if (ms->map->dso->annotate_warned)
964                 return -1;
965
966         err = symbol__annotate2(ms, evsel, opts, &browser.arch);
967         if (err) {
968                 char msg[BUFSIZ];
969                 ms->map->dso->annotate_warned = true;
970                 symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
971                 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
972                 goto out_free_offsets;
973         }
974
975         ui_helpline__push("Press ESC to exit");
976
977         browser.b.width = notes->max_line_len;
978         browser.b.nr_entries = notes->nr_entries;
979         browser.b.entries = &notes->src->source,
980         browser.b.width += 18; /* Percentage */
981
982         if (notes->options->hide_src_code)
983                 ui_browser__init_asm_mode(&browser.b);
984
985         ret = annotate_browser__run(&browser, evsel, hbt);
986
987         annotated_source__purge(notes->src);
988
989 out_free_offsets:
990         zfree(&notes->offsets);
991         return ret;
992 }