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