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