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