]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/perf/ui/browsers/annotate.c
3bfe17e176fe9ff2dd1944327dd990a6a742d158
[linux.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 (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
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                 "c             Show min/max cycle\n"
699                 "/             Search string\n"
700                 "k             Toggle line numbers\n"
701                 "P             Print to [symbol_name].annotation file.\n"
702                 "r             Run available scripts\n"
703                 "?             Search string backwards\n");
704                         continue;
705                 case 'r':
706                         {
707                                 script_browse(NULL);
708                                 continue;
709                         }
710                 case 'k':
711                         notes->options->show_linenr = !notes->options->show_linenr;
712                         break;
713                 case 'H':
714                         nd = browser->curr_hot;
715                         break;
716                 case 's':
717                         if (annotate_browser__toggle_source(browser))
718                                 ui_helpline__puts(help);
719                         continue;
720                 case 'o':
721                         notes->options->use_offset = !notes->options->use_offset;
722                         annotation__update_column_widths(notes);
723                         continue;
724                 case 'O':
725                         if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
726                                 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
727                         continue;
728                 case 'j':
729                         notes->options->jump_arrows = !notes->options->jump_arrows;
730                         continue;
731                 case 'J':
732                         notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
733                         annotation__update_column_widths(notes);
734                         continue;
735                 case '/':
736                         if (annotate_browser__search(browser, delay_secs)) {
737 show_help:
738                                 ui_helpline__puts(help);
739                         }
740                         continue;
741                 case 'n':
742                         if (browser->searching_backwards ?
743                             annotate_browser__continue_search_reverse(browser, delay_secs) :
744                             annotate_browser__continue_search(browser, delay_secs))
745                                 goto show_help;
746                         continue;
747                 case '?':
748                         if (annotate_browser__search_reverse(browser, delay_secs))
749                                 goto show_help;
750                         continue;
751                 case 'D': {
752                         static int seq;
753                         ui_helpline__pop();
754                         ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
755                                            seq++, browser->b.nr_entries,
756                                            browser->b.height,
757                                            browser->b.index,
758                                            browser->b.top_idx,
759                                            notes->nr_asm_entries);
760                 }
761                         continue;
762                 case K_ENTER:
763                 case K_RIGHT:
764                 {
765                         struct disasm_line *dl = disasm_line(browser->selection);
766
767                         if (browser->selection == NULL)
768                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
769                         else if (browser->selection->offset == -1)
770                                 ui_helpline__puts("Actions are only available for assembly lines.");
771                         else if (!dl->ins.ops)
772                                 goto show_sup_ins;
773                         else if (ins__is_ret(&dl->ins))
774                                 goto out;
775                         else if (!(annotate_browser__jump(browser, evsel, hbt) ||
776                                      annotate_browser__callq(browser, evsel, hbt))) {
777 show_sup_ins:
778                                 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
779                         }
780                         continue;
781                 }
782                 case 'P':
783                         map_symbol__annotation_dump(ms, evsel);
784                         continue;
785                 case 't':
786                         if (notes->options->show_total_period) {
787                                 notes->options->show_total_period = false;
788                                 notes->options->show_nr_samples = true;
789                         } else if (notes->options->show_nr_samples)
790                                 notes->options->show_nr_samples = false;
791                         else
792                                 notes->options->show_total_period = true;
793                         annotation__update_column_widths(notes);
794                         continue;
795                 case 'c':
796                         if (notes->options->show_minmax_cycle)
797                                 notes->options->show_minmax_cycle = false;
798                         else
799                                 notes->options->show_minmax_cycle = true;
800                         annotation__update_column_widths(notes);
801                         continue;
802                 case K_LEFT:
803                 case K_ESC:
804                 case 'q':
805                 case CTRL('c'):
806                         goto out;
807                 default:
808                         continue;
809                 }
810
811                 if (nd != NULL)
812                         annotate_browser__set_rb_top(browser, nd);
813         }
814 out:
815         ui_browser__hide(&browser->b);
816         return key;
817 }
818
819 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
820                              struct hist_browser_timer *hbt)
821 {
822         return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
823 }
824
825 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
826                              struct hist_browser_timer *hbt)
827 {
828         /* reset abort key so that it can get Ctrl-C as a key */
829         SLang_reset_tty();
830         SLang_init_tty(0, 0, 0);
831
832         return map_symbol__tui_annotate(&he->ms, evsel, hbt);
833 }
834
835 int symbol__tui_annotate(struct symbol *sym, struct map *map,
836                          struct perf_evsel *evsel,
837                          struct hist_browser_timer *hbt)
838 {
839         struct annotation *notes = symbol__annotation(sym);
840         struct map_symbol ms = {
841                 .map = map,
842                 .sym = sym,
843         };
844         struct annotate_browser browser = {
845                 .b = {
846                         .refresh = annotate_browser__refresh,
847                         .seek    = ui_browser__list_head_seek,
848                         .write   = annotate_browser__write,
849                         .filter  = disasm_line__filter,
850                         .extra_title_lines = 1, /* for hists__scnprintf_title() */
851                         .priv    = &ms,
852                         .use_navkeypressed = true,
853                 },
854         };
855         int ret = -1, err;
856
857         if (sym == NULL)
858                 return -1;
859
860         if (map->dso->annotate_warned)
861                 return -1;
862
863         err = symbol__annotate2(sym, map, evsel, &annotation__default_options, &browser.arch);
864         if (err) {
865                 char msg[BUFSIZ];
866                 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
867                 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
868                 goto out_free_offsets;
869         }
870
871         ui_helpline__push("Press ESC to exit");
872
873         browser.b.width = notes->max_line_len;
874         browser.b.nr_entries = notes->nr_entries;
875         browser.b.entries = &notes->src->source,
876         browser.b.width += 18; /* Percentage */
877
878         if (notes->options->hide_src_code)
879                 ui_browser__init_asm_mode(&browser.b);
880
881         ret = annotate_browser__run(&browser, evsel, hbt);
882
883         annotated_source__purge(notes->src);
884
885 out_free_offsets:
886         zfree(&notes->offsets);
887         return ret;
888 }