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