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