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