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