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