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