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