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