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