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