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