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