]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blob - tools/perf/ui/browsers/annotate.c
pinctrl: sh-pfc: r8a77965: Add DRIF pins, groups and functions
[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 int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
228 {
229 int i;
230
231 for (i = 0; i < a->data_nr; i++) {
232 if (a->data[i].percent == b->data[i].percent)
233 continue;
234 return a->data[i].percent < b->data[i].percent;
235 }
236 return 0;
237 }
238
239 static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
240 {
241 struct rb_node **p = &root->rb_node;
242 struct rb_node *parent = NULL;
243 struct annotation_line *l;
244
245 while (*p != NULL) {
246 parent = *p;
247 l = rb_entry(parent, struct annotation_line, rb_node);
248
249 if (disasm__cmp(al, l))
250 p = &(*p)->rb_left;
251 else
252 p = &(*p)->rb_right;
253 }
254 rb_link_node(&al->rb_node, parent, p);
255 rb_insert_color(&al->rb_node, root);
256 }
257
258 static void annotate_browser__set_top(struct annotate_browser *browser,
259 struct annotation_line *pos, u32 idx)
260 {
261 struct annotation *notes = browser__annotation(&browser->b);
262 unsigned back;
263
264 ui_browser__refresh_dimensions(&browser->b);
265 back = browser->b.height / 2;
266 browser->b.top_idx = browser->b.index = idx;
267
268 while (browser->b.top_idx != 0 && back != 0) {
269 pos = list_entry(pos->node.prev, struct annotation_line, node);
270
271 if (annotation_line__filter(pos, notes))
272 continue;
273
274 --browser->b.top_idx;
275 --back;
276 }
277
278 browser->b.top = pos;
279 browser->b.navkeypressed = true;
280 }
281
282 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
283 struct rb_node *nd)
284 {
285 struct annotation *notes = browser__annotation(&browser->b);
286 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
287 u32 idx = pos->idx;
288
289 if (notes->options->hide_src_code)
290 idx = pos->idx_asm;
291 annotate_browser__set_top(browser, pos, idx);
292 browser->curr_hot = nd;
293 }
294
295 static void annotate_browser__calc_percent(struct annotate_browser *browser,
296 struct perf_evsel *evsel)
297 {
298 struct map_symbol *ms = browser->b.priv;
299 struct symbol *sym = ms->sym;
300 struct annotation *notes = symbol__annotation(sym);
301 struct disasm_line *pos;
302
303 browser->entries = RB_ROOT;
304
305 pthread_mutex_lock(&notes->lock);
306
307 symbol__calc_percent(sym, evsel);
308
309 list_for_each_entry(pos, &notes->src->source, al.node) {
310 double max_percent = 0.0;
311 int i;
312
313 if (pos->al.offset == -1) {
314 RB_CLEAR_NODE(&pos->al.rb_node);
315 continue;
316 }
317
318 for (i = 0; i < pos->al.data_nr; i++) {
319 double percent;
320
321 percent = annotation_data__percent(&pos->al.data[i],
322 browser->opts->percent_type);
323
324 if (max_percent < percent)
325 max_percent = percent;
326 }
327
328 if (max_percent < 0.01 && pos->al.ipc == 0) {
329 RB_CLEAR_NODE(&pos->al.rb_node);
330 continue;
331 }
332 disasm_rb_tree__insert(&browser->entries, &pos->al);
333 }
334 pthread_mutex_unlock(&notes->lock);
335
336 browser->curr_hot = rb_last(&browser->entries);
337 }
338
339 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
340 {
341 struct annotation *notes = browser__annotation(&browser->b);
342 struct annotation_line *al;
343 off_t offset = browser->b.index - browser->b.top_idx;
344
345 browser->b.seek(&browser->b, offset, SEEK_CUR);
346 al = list_entry(browser->b.top, struct annotation_line, node);
347
348 if (notes->options->hide_src_code) {
349 if (al->idx_asm < offset)
350 offset = al->idx;
351
352 browser->b.nr_entries = notes->nr_entries;
353 notes->options->hide_src_code = false;
354 browser->b.seek(&browser->b, -offset, SEEK_CUR);
355 browser->b.top_idx = al->idx - offset;
356 browser->b.index = al->idx;
357 } else {
358 if (al->idx_asm < 0) {
359 ui_helpline__puts("Only available for assembly lines.");
360 browser->b.seek(&browser->b, -offset, SEEK_CUR);
361 return false;
362 }
363
364 if (al->idx_asm < offset)
365 offset = al->idx_asm;
366
367 browser->b.nr_entries = notes->nr_asm_entries;
368 notes->options->hide_src_code = true;
369 browser->b.seek(&browser->b, -offset, SEEK_CUR);
370 browser->b.top_idx = al->idx_asm - offset;
371 browser->b.index = al->idx_asm;
372 }
373
374 return true;
375 }
376
377 static void ui_browser__init_asm_mode(struct ui_browser *browser)
378 {
379 struct annotation *notes = browser__annotation(browser);
380 ui_browser__reset_index(browser);
381 browser->nr_entries = notes->nr_asm_entries;
382 }
383
384 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
385
386 static int sym_title(struct symbol *sym, struct map *map, char *title,
387 size_t sz, int percent_type)
388 {
389 return snprintf(title, sz, "%s %s [Percent: %s]", sym->name, map->dso->long_name,
390 percent_type_str(percent_type));
391 }
392
393 /*
394 * This can be called from external jumps, i.e. jumps from one functon
395 * to another, like from the kernel's entry_SYSCALL_64 function to the
396 * swapgs_restore_regs_and_return_to_usermode() function.
397 *
398 * So all we check here is that dl->ops.target.sym is set, if it is, just
399 * go to that function and when exiting from its disassembly, come back
400 * to the calling function.
401 */
402 static bool annotate_browser__callq(struct annotate_browser *browser,
403 struct perf_evsel *evsel,
404 struct hist_browser_timer *hbt)
405 {
406 struct map_symbol *ms = browser->b.priv;
407 struct disasm_line *dl = disasm_line(browser->selection);
408 struct annotation *notes;
409 char title[SYM_TITLE_MAX_SIZE];
410
411 if (!dl->ops.target.sym) {
412 ui_helpline__puts("The called function was not found.");
413 return true;
414 }
415
416 notes = symbol__annotation(dl->ops.target.sym);
417 pthread_mutex_lock(&notes->lock);
418
419 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->nr_entries)) {
420 pthread_mutex_unlock(&notes->lock);
421 ui__warning("Not enough memory for annotating '%s' symbol!\n",
422 dl->ops.target.sym->name);
423 return true;
424 }
425
426 pthread_mutex_unlock(&notes->lock);
427 symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt, browser->opts);
428 sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type);
429 ui_browser__show_title(&browser->b, title);
430 return true;
431 }
432
433 static
434 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
435 s64 offset, s64 *idx)
436 {
437 struct annotation *notes = browser__annotation(&browser->b);
438 struct disasm_line *pos;
439
440 *idx = 0;
441 list_for_each_entry(pos, &notes->src->source, al.node) {
442 if (pos->al.offset == offset)
443 return pos;
444 if (!annotation_line__filter(&pos->al, notes))
445 ++*idx;
446 }
447
448 return NULL;
449 }
450
451 static bool annotate_browser__jump(struct annotate_browser *browser,
452 struct perf_evsel *evsel,
453 struct hist_browser_timer *hbt)
454 {
455 struct disasm_line *dl = disasm_line(browser->selection);
456 u64 offset;
457 s64 idx;
458
459 if (!ins__is_jump(&dl->ins))
460 return false;
461
462 if (dl->ops.target.outside) {
463 annotate_browser__callq(browser, evsel, hbt);
464 return true;
465 }
466
467 offset = dl->ops.target.offset;
468 dl = annotate_browser__find_offset(browser, offset, &idx);
469 if (dl == NULL) {
470 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
471 return true;
472 }
473
474 annotate_browser__set_top(browser, &dl->al, idx);
475
476 return true;
477 }
478
479 static
480 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
481 char *s, s64 *idx)
482 {
483 struct annotation *notes = browser__annotation(&browser->b);
484 struct annotation_line *al = browser->selection;
485
486 *idx = browser->b.index;
487 list_for_each_entry_continue(al, &notes->src->source, node) {
488 if (annotation_line__filter(al, notes))
489 continue;
490
491 ++*idx;
492
493 if (al->line && strstr(al->line, s) != NULL)
494 return al;
495 }
496
497 return NULL;
498 }
499
500 static bool __annotate_browser__search(struct annotate_browser *browser)
501 {
502 struct annotation_line *al;
503 s64 idx;
504
505 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
506 if (al == NULL) {
507 ui_helpline__puts("String not found!");
508 return false;
509 }
510
511 annotate_browser__set_top(browser, al, idx);
512 browser->searching_backwards = false;
513 return true;
514 }
515
516 static
517 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
518 char *s, s64 *idx)
519 {
520 struct annotation *notes = browser__annotation(&browser->b);
521 struct annotation_line *al = browser->selection;
522
523 *idx = browser->b.index;
524 list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
525 if (annotation_line__filter(al, notes))
526 continue;
527
528 --*idx;
529
530 if (al->line && strstr(al->line, s) != NULL)
531 return al;
532 }
533
534 return NULL;
535 }
536
537 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
538 {
539 struct annotation_line *al;
540 s64 idx;
541
542 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
543 if (al == NULL) {
544 ui_helpline__puts("String not found!");
545 return false;
546 }
547
548 annotate_browser__set_top(browser, al, idx);
549 browser->searching_backwards = true;
550 return true;
551 }
552
553 static bool annotate_browser__search_window(struct annotate_browser *browser,
554 int delay_secs)
555 {
556 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
557 "ENTER: OK, ESC: Cancel",
558 delay_secs * 2) != K_ENTER ||
559 !*browser->search_bf)
560 return false;
561
562 return true;
563 }
564
565 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
566 {
567 if (annotate_browser__search_window(browser, delay_secs))
568 return __annotate_browser__search(browser);
569
570 return false;
571 }
572
573 static bool annotate_browser__continue_search(struct annotate_browser *browser,
574 int delay_secs)
575 {
576 if (!*browser->search_bf)
577 return annotate_browser__search(browser, delay_secs);
578
579 return __annotate_browser__search(browser);
580 }
581
582 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
583 int delay_secs)
584 {
585 if (annotate_browser__search_window(browser, delay_secs))
586 return __annotate_browser__search_reverse(browser);
587
588 return false;
589 }
590
591 static
592 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
593 int delay_secs)
594 {
595 if (!*browser->search_bf)
596 return annotate_browser__search_reverse(browser, delay_secs);
597
598 return __annotate_browser__search_reverse(browser);
599 }
600
601 static int annotate_browser__show(struct ui_browser *browser, char *title, const char *help)
602 {
603 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
604 struct map_symbol *ms = browser->priv;
605 struct symbol *sym = ms->sym;
606 char symbol_dso[SYM_TITLE_MAX_SIZE];
607
608 if (ui_browser__show(browser, title, help) < 0)
609 return -1;
610
611 sym_title(sym, ms->map, symbol_dso, sizeof(symbol_dso), ab->opts->percent_type);
612
613 ui_browser__gotorc_title(browser, 0, 0);
614 ui_browser__set_color(browser, HE_COLORSET_ROOT);
615 ui_browser__write_nstring(browser, symbol_dso, browser->width + 1);
616 return 0;
617 }
618
619 static void
620 switch_percent_type(struct annotation_options *opts, bool base)
621 {
622 switch (opts->percent_type) {
623 case PERCENT_HITS_LOCAL:
624 if (base)
625 opts->percent_type = PERCENT_PERIOD_LOCAL;
626 else
627 opts->percent_type = PERCENT_HITS_GLOBAL;
628 break;
629 case PERCENT_HITS_GLOBAL:
630 if (base)
631 opts->percent_type = PERCENT_PERIOD_GLOBAL;
632 else
633 opts->percent_type = PERCENT_HITS_LOCAL;
634 break;
635 case PERCENT_PERIOD_LOCAL:
636 if (base)
637 opts->percent_type = PERCENT_HITS_LOCAL;
638 else
639 opts->percent_type = PERCENT_PERIOD_GLOBAL;
640 break;
641 case PERCENT_PERIOD_GLOBAL:
642 if (base)
643 opts->percent_type = PERCENT_HITS_GLOBAL;
644 else
645 opts->percent_type = PERCENT_PERIOD_LOCAL;
646 break;
647 default:
648 WARN_ON(1);
649 }
650 }
651
652 static int annotate_browser__run(struct annotate_browser *browser,
653 struct perf_evsel *evsel,
654 struct hist_browser_timer *hbt)
655 {
656 struct rb_node *nd = NULL;
657 struct hists *hists = evsel__hists(evsel);
658 struct map_symbol *ms = browser->b.priv;
659 struct symbol *sym = ms->sym;
660 struct annotation *notes = symbol__annotation(ms->sym);
661 const char *help = "Press 'h' for help on key bindings";
662 int delay_secs = hbt ? hbt->refresh : 0;
663 char title[256];
664 int key;
665
666 hists__scnprintf_title(hists, title, sizeof(title));
667 if (annotate_browser__show(&browser->b, title, help) < 0)
668 return -1;
669
670 annotate_browser__calc_percent(browser, evsel);
671
672 if (browser->curr_hot) {
673 annotate_browser__set_rb_top(browser, browser->curr_hot);
674 browser->b.navkeypressed = false;
675 }
676
677 nd = browser->curr_hot;
678
679 while (1) {
680 key = ui_browser__run(&browser->b, delay_secs);
681
682 if (delay_secs != 0) {
683 annotate_browser__calc_percent(browser, evsel);
684 /*
685 * Current line focus got out of the list of most active
686 * lines, NULL it so that if TAB|UNTAB is pressed, we
687 * move to curr_hot (current hottest line).
688 */
689 if (nd != NULL && RB_EMPTY_NODE(nd))
690 nd = NULL;
691 }
692
693 switch (key) {
694 case K_TIMER:
695 if (hbt)
696 hbt->timer(hbt->arg);
697
698 if (delay_secs != 0) {
699 symbol__annotate_decay_histogram(sym, evsel->idx);
700 hists__scnprintf_title(hists, title, sizeof(title));
701 annotate_browser__show(&browser->b, title, help);
702 }
703 continue;
704 case K_TAB:
705 if (nd != NULL) {
706 nd = rb_prev(nd);
707 if (nd == NULL)
708 nd = rb_last(&browser->entries);
709 } else
710 nd = browser->curr_hot;
711 break;
712 case K_UNTAB:
713 if (nd != NULL) {
714 nd = rb_next(nd);
715 if (nd == NULL)
716 nd = rb_first(&browser->entries);
717 } else
718 nd = browser->curr_hot;
719 break;
720 case K_F1:
721 case 'h':
722 ui_browser__help_window(&browser->b,
723 "UP/DOWN/PGUP\n"
724 "PGDN/SPACE Navigate\n"
725 "q/ESC/CTRL+C Exit\n\n"
726 "ENTER Go to target\n"
727 "ESC Exit\n"
728 "H Go to hottest instruction\n"
729 "TAB/shift+TAB Cycle thru hottest instructions\n"
730 "j Toggle showing jump to target arrows\n"
731 "J Toggle showing number of jump sources on targets\n"
732 "n Search next string\n"
733 "o Toggle disassembler output/simplified view\n"
734 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
735 "s Toggle source code view\n"
736 "t Circulate percent, total period, samples view\n"
737 "c Show min/max cycle\n"
738 "/ Search string\n"
739 "k Toggle line numbers\n"
740 "P Print to [symbol_name].annotation file.\n"
741 "r Run available scripts\n"
742 "p Toggle percent type [local/global]\n"
743 "b Toggle percent base [period/hits]\n"
744 "? Search string backwards\n");
745 continue;
746 case 'r':
747 {
748 script_browse(NULL);
749 continue;
750 }
751 case 'k':
752 notes->options->show_linenr = !notes->options->show_linenr;
753 break;
754 case 'H':
755 nd = browser->curr_hot;
756 break;
757 case 's':
758 if (annotate_browser__toggle_source(browser))
759 ui_helpline__puts(help);
760 continue;
761 case 'o':
762 notes->options->use_offset = !notes->options->use_offset;
763 annotation__update_column_widths(notes);
764 continue;
765 case 'O':
766 if (++notes->options->offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
767 notes->options->offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
768 continue;
769 case 'j':
770 notes->options->jump_arrows = !notes->options->jump_arrows;
771 continue;
772 case 'J':
773 notes->options->show_nr_jumps = !notes->options->show_nr_jumps;
774 annotation__update_column_widths(notes);
775 continue;
776 case '/':
777 if (annotate_browser__search(browser, delay_secs)) {
778 show_help:
779 ui_helpline__puts(help);
780 }
781 continue;
782 case 'n':
783 if (browser->searching_backwards ?
784 annotate_browser__continue_search_reverse(browser, delay_secs) :
785 annotate_browser__continue_search(browser, delay_secs))
786 goto show_help;
787 continue;
788 case '?':
789 if (annotate_browser__search_reverse(browser, delay_secs))
790 goto show_help;
791 continue;
792 case 'D': {
793 static int seq;
794 ui_helpline__pop();
795 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
796 seq++, browser->b.nr_entries,
797 browser->b.height,
798 browser->b.index,
799 browser->b.top_idx,
800 notes->nr_asm_entries);
801 }
802 continue;
803 case K_ENTER:
804 case K_RIGHT:
805 {
806 struct disasm_line *dl = disasm_line(browser->selection);
807
808 if (browser->selection == NULL)
809 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
810 else if (browser->selection->offset == -1)
811 ui_helpline__puts("Actions are only available for assembly lines.");
812 else if (!dl->ins.ops)
813 goto show_sup_ins;
814 else if (ins__is_ret(&dl->ins))
815 goto out;
816 else if (!(annotate_browser__jump(browser, evsel, hbt) ||
817 annotate_browser__callq(browser, evsel, hbt))) {
818 show_sup_ins:
819 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
820 }
821 continue;
822 }
823 case 'P':
824 map_symbol__annotation_dump(ms, evsel, browser->opts);
825 continue;
826 case 't':
827 if (notes->options->show_total_period) {
828 notes->options->show_total_period = false;
829 notes->options->show_nr_samples = true;
830 } else if (notes->options->show_nr_samples)
831 notes->options->show_nr_samples = false;
832 else
833 notes->options->show_total_period = true;
834 annotation__update_column_widths(notes);
835 continue;
836 case 'c':
837 if (notes->options->show_minmax_cycle)
838 notes->options->show_minmax_cycle = false;
839 else
840 notes->options->show_minmax_cycle = true;
841 annotation__update_column_widths(notes);
842 continue;
843 case 'p':
844 case 'b':
845 switch_percent_type(browser->opts, key == 'b');
846 hists__scnprintf_title(hists, title, sizeof(title));
847 annotate_browser__show(&browser->b, title, help);
848 continue;
849 case K_LEFT:
850 case K_ESC:
851 case 'q':
852 case CTRL('c'):
853 goto out;
854 default:
855 continue;
856 }
857
858 if (nd != NULL)
859 annotate_browser__set_rb_top(browser, nd);
860 }
861 out:
862 ui_browser__hide(&browser->b);
863 return key;
864 }
865
866 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
867 struct hist_browser_timer *hbt,
868 struct annotation_options *opts)
869 {
870 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt, opts);
871 }
872
873 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
874 struct hist_browser_timer *hbt,
875 struct annotation_options *opts)
876 {
877 /* reset abort key so that it can get Ctrl-C as a key */
878 SLang_reset_tty();
879 SLang_init_tty(0, 0, 0);
880
881 return map_symbol__tui_annotate(&he->ms, evsel, hbt, opts);
882 }
883
884 int symbol__tui_annotate(struct symbol *sym, struct map *map,
885 struct perf_evsel *evsel,
886 struct hist_browser_timer *hbt,
887 struct annotation_options *opts)
888 {
889 struct annotation *notes = symbol__annotation(sym);
890 struct map_symbol ms = {
891 .map = map,
892 .sym = sym,
893 };
894 struct annotate_browser browser = {
895 .b = {
896 .refresh = annotate_browser__refresh,
897 .seek = ui_browser__list_head_seek,
898 .write = annotate_browser__write,
899 .filter = disasm_line__filter,
900 .extra_title_lines = 1, /* for hists__scnprintf_title() */
901 .priv = &ms,
902 .use_navkeypressed = true,
903 },
904 .opts = opts,
905 };
906 int ret = -1, err;
907
908 if (sym == NULL)
909 return -1;
910
911 if (map->dso->annotate_warned)
912 return -1;
913
914 err = symbol__annotate2(sym, map, evsel, opts, &browser.arch);
915 if (err) {
916 char msg[BUFSIZ];
917 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
918 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
919 goto out_free_offsets;
920 }
921
922 ui_helpline__push("Press ESC to exit");
923
924 browser.b.width = notes->max_line_len;
925 browser.b.nr_entries = notes->nr_entries;
926 browser.b.entries = &notes->src->source,
927 browser.b.width += 18; /* Percentage */
928
929 if (notes->options->hide_src_code)
930 ui_browser__init_asm_mode(&browser.b);
931
932 ret = annotate_browser__run(&browser, evsel, hbt);
933
934 annotated_source__purge(notes->src);
935
936 out_free_offsets:
937 zfree(&notes->offsets);
938 return ret;
939 }