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/debuginfo.h"
8 #include "../../util/dso.h"
9 #include "../../util/hashmap.h"
10 #include "../../util/hist.h"
11 #include "../../util/sort.h"
12 #include "../../util/map.h"
13 #include "../../util/mutex.h"
14 #include "../../util/symbol.h"
15 #include "../../util/evsel.h"
16 #include "../../util/evlist.h"
17 #include "../../util/thread.h"
18 #include <inttypes.h>
19 #include <linux/err.h>
20 #include <linux/kernel.h>
21 #include <linux/string.h>
22 #include <linux/zalloc.h>
23 #include <sys/ttydefaults.h>
24 #include <asm/bug.h>
25
26 struct arch;
27
28 struct annotate_browser {
29 struct ui_browser b;
30 struct rb_root entries;
31 struct rb_node *curr_hot;
32 struct annotation_line *selection;
33 struct arch *arch;
34 /*
35 * perf top can delete hist_entry anytime. Callers should make sure
36 * its lifetime.
37 */
38 struct hist_entry *he;
39 struct debuginfo *dbg;
40 struct evsel *evsel;
41 struct hashmap *type_hash;
42 bool searching_backwards;
43 char search_bf[128];
44 };
45
46 /* A copy of target hist_entry for perf top. */
47 static struct hist_entry annotate_he;
48
type_hash(long key,void * ctx __maybe_unused)49 static size_t type_hash(long key, void *ctx __maybe_unused)
50 {
51 return key;
52 }
53
type_equal(long key1,long key2,void * ctx __maybe_unused)54 static bool type_equal(long key1, long key2, void *ctx __maybe_unused)
55 {
56 return key1 == key2;
57 }
58
browser__annotation(struct ui_browser * browser)59 static inline struct annotation *browser__annotation(struct ui_browser *browser)
60 {
61 struct map_symbol *ms = browser->priv;
62 return symbol__annotation(ms->sym);
63 }
64
disasm_line__filter(struct ui_browser * browser __maybe_unused,void * entry)65 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry)
66 {
67 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
68 return annotation_line__filter(al);
69 }
70
ui_browser__jumps_percent_color(struct ui_browser * browser,int nr,bool current)71 static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
72 {
73 struct annotation *notes = browser__annotation(browser);
74
75 if (current && (!browser->use_navkeypressed || browser->navkeypressed))
76 return HE_COLORSET_SELECTED;
77 if (nr == notes->src->max_jump_sources)
78 return HE_COLORSET_TOP;
79 if (nr > 1)
80 return HE_COLORSET_MEDIUM;
81 return HE_COLORSET_NORMAL;
82 }
83
ui_browser__set_jumps_percent_color(void * browser,int nr,bool current)84 static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current)
85 {
86 int color = ui_browser__jumps_percent_color(browser, nr, current);
87 return ui_browser__set_color(browser, color);
88 }
89
annotate_browser__set_color(void * browser,int color)90 static int annotate_browser__set_color(void *browser, int color)
91 {
92 return ui_browser__set_color(browser, color);
93 }
94
annotate_browser__write_graph(void * browser,int graph)95 static void annotate_browser__write_graph(void *browser, int graph)
96 {
97 ui_browser__write_graph(browser, graph);
98 }
99
annotate_browser__set_percent_color(void * browser,double percent,bool current)100 static void annotate_browser__set_percent_color(void *browser, double percent, bool current)
101 {
102 ui_browser__set_percent_color(browser, percent, current);
103 }
104
annotate_browser__printf(void * browser,const char * fmt,...)105 static void annotate_browser__printf(void *browser, const char *fmt, ...)
106 {
107 va_list args;
108
109 va_start(args, fmt);
110 ui_browser__vprintf(browser, fmt, args);
111 va_end(args);
112 }
113
annotate_browser__write(struct ui_browser * browser,void * entry,int row)114 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
115 {
116 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
117 struct annotation *notes = browser__annotation(browser);
118 struct annotation_line *al = list_entry(entry, struct annotation_line, node);
119 const bool is_current_entry = ui_browser__is_current_entry(browser, row);
120 struct annotation_write_ops ops = {
121 .first_line = row == 0,
122 .current_entry = is_current_entry,
123 .change_color = (!annotate_opts.hide_src_code &&
124 (!is_current_entry ||
125 (browser->use_navkeypressed &&
126 !browser->navkeypressed))),
127 .width = browser->width,
128 .obj = browser,
129 .set_color = annotate_browser__set_color,
130 .set_percent_color = annotate_browser__set_percent_color,
131 .set_jumps_percent_color = ui_browser__set_jumps_percent_color,
132 .printf = annotate_browser__printf,
133 .write_graph = annotate_browser__write_graph,
134 };
135 struct annotation_print_data apd = {
136 .he = ab->he,
137 .arch = ab->arch,
138 .evsel = ab->evsel,
139 .dbg = ab->dbg,
140 };
141
142 /* The scroll bar isn't being used */
143 if (!browser->navkeypressed)
144 ops.width += 1;
145
146 if (!IS_ERR_OR_NULL(ab->type_hash))
147 apd.type_hash = ab->type_hash;
148
149 annotation_line__write(al, notes, &ops, &apd);
150
151 if (ops.current_entry)
152 ab->selection = al;
153 }
154
is_fused(struct annotate_browser * ab,struct disasm_line * cursor)155 static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
156 {
157 struct disasm_line *pos = list_prev_entry(cursor, al.node);
158 const char *name;
159 int diff = 1;
160
161 while (pos && pos->al.offset == -1) {
162 pos = list_prev_entry(pos, al.node);
163 if (!annotate_opts.hide_src_code)
164 diff++;
165 }
166
167 if (!pos)
168 return 0;
169
170 if (ins__is_lock(&pos->ins))
171 name = pos->ops.locked.ins.name;
172 else
173 name = pos->ins.name;
174
175 if (!name || !cursor->ins.name)
176 return 0;
177
178 if (ins__is_fused(ab->arch, name, cursor->ins.name))
179 return diff;
180 return 0;
181 }
182
annotate_browser__draw_current_jump(struct ui_browser * browser)183 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
184 {
185 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
186 struct disasm_line *cursor = disasm_line(ab->selection);
187 struct annotation_line *target;
188 unsigned int from, to;
189 struct map_symbol *ms = ab->b.priv;
190 struct symbol *sym = ms->sym;
191 struct annotation *notes = symbol__annotation(sym);
192 u8 pcnt_width = annotation__pcnt_width(notes);
193 u8 cntr_width = annotation__br_cntr_width();
194 int width;
195 int diff = 0;
196
197 /* PLT symbols contain external offsets */
198 if (strstr(sym->name, "@plt"))
199 return;
200
201 if (!disasm_line__is_valid_local_jump(cursor, sym))
202 return;
203
204 /*
205 * This first was seen with a gcc function, _cpp_lex_token, that
206 * has the usual jumps:
207 *
208 * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92>
209 *
210 * I.e. jumps to a label inside that function (_cpp_lex_token), and
211 * those works, but also this kind:
212 *
213 * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72>
214 *
215 * I.e. jumps to another function, outside _cpp_lex_token, which
216 * are not being correctly handled generating as a side effect references
217 * to ab->offset[] entries that are set to NULL, so to make this code
218 * more robust, check that here.
219 *
220 * A proper fix for will be put in place, looking at the function
221 * name right after the '<' token and probably treating this like a
222 * 'call' instruction.
223 */
224 target = annotated_source__get_line(notes->src, cursor->ops.target.offset);
225 if (target == NULL) {
226 ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
227 cursor->ops.target.offset);
228 return;
229 }
230
231 if (annotate_opts.hide_src_code) {
232 from = cursor->al.idx_asm;
233 to = target->idx_asm;
234 } else {
235 from = (u64)cursor->al.idx;
236 to = (u64)target->idx;
237 }
238
239 width = annotation__cycles_width(notes);
240
241 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
242 __ui_browser__line_arrow(browser,
243 pcnt_width + 2 + notes->src->widths.addr + width + cntr_width,
244 from, to);
245
246 diff = is_fused(ab, cursor);
247 if (diff > 0) {
248 ui_browser__mark_fused(browser,
249 pcnt_width + 3 + notes->src->widths.addr + width + cntr_width,
250 from - diff, diff, to > from);
251 }
252 }
253
annotate_browser__refresh(struct ui_browser * browser)254 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
255 {
256 struct annotation *notes = browser__annotation(browser);
257 int ret = ui_browser__list_head_refresh(browser);
258 int pcnt_width = annotation__pcnt_width(notes);
259
260 if (annotate_opts.jump_arrows)
261 annotate_browser__draw_current_jump(browser);
262
263 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
264 __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1);
265 return ret;
266 }
267
disasm__cmp(struct annotation_line * a,struct annotation_line * b,int percent_type)268 static double disasm__cmp(struct annotation_line *a, struct annotation_line *b,
269 int percent_type)
270 {
271 int i;
272
273 for (i = 0; i < a->data_nr; i++) {
274 if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type])
275 continue;
276 return a->data[i].percent[percent_type] -
277 b->data[i].percent[percent_type];
278 }
279 return 0;
280 }
281
disasm_rb_tree__insert(struct annotate_browser * browser,struct annotation_line * al)282 static void disasm_rb_tree__insert(struct annotate_browser *browser,
283 struct annotation_line *al)
284 {
285 struct rb_root *root = &browser->entries;
286 struct rb_node **p = &root->rb_node;
287 struct rb_node *parent = NULL;
288 struct annotation_line *l;
289
290 while (*p != NULL) {
291 parent = *p;
292 l = rb_entry(parent, struct annotation_line, rb_node);
293
294 if (disasm__cmp(al, l, annotate_opts.percent_type) < 0)
295 p = &(*p)->rb_left;
296 else
297 p = &(*p)->rb_right;
298 }
299 rb_link_node(&al->rb_node, parent, p);
300 rb_insert_color(&al->rb_node, root);
301 }
302
annotate_browser__set_top(struct annotate_browser * browser,struct annotation_line * pos,u32 idx)303 static void annotate_browser__set_top(struct annotate_browser *browser,
304 struct annotation_line *pos, u32 idx)
305 {
306 unsigned back;
307
308 ui_browser__refresh_dimensions(&browser->b);
309 back = browser->b.height / 2;
310 browser->b.top_idx = browser->b.index = idx;
311
312 while (browser->b.top_idx != 0 && back != 0) {
313 pos = list_entry(pos->node.prev, struct annotation_line, node);
314
315 if (annotation_line__filter(pos))
316 continue;
317
318 --browser->b.top_idx;
319 --back;
320 }
321
322 browser->b.top = pos;
323 browser->b.navkeypressed = true;
324 }
325
annotate_browser__set_rb_top(struct annotate_browser * browser,struct rb_node * nd)326 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
327 struct rb_node *nd)
328 {
329 struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
330 u32 idx = pos->idx;
331
332 if (annotate_opts.hide_src_code)
333 idx = pos->idx_asm;
334 annotate_browser__set_top(browser, pos, idx);
335 browser->curr_hot = nd;
336 }
337
annotate_browser__calc_percent(struct annotate_browser * browser,struct evsel * evsel)338 static void annotate_browser__calc_percent(struct annotate_browser *browser,
339 struct evsel *evsel)
340 {
341 struct map_symbol *ms = browser->b.priv;
342 struct symbol *sym = ms->sym;
343 struct annotation *notes = symbol__annotation(sym);
344 struct disasm_line *pos;
345
346 browser->entries = RB_ROOT;
347
348 annotation__lock(notes);
349
350 symbol__calc_percent(sym, evsel);
351
352 list_for_each_entry(pos, ¬es->src->source, al.node) {
353 double max_percent = 0.0;
354 int i;
355
356 if (pos->al.offset == -1) {
357 RB_CLEAR_NODE(&pos->al.rb_node);
358 continue;
359 }
360
361 for (i = 0; i < pos->al.data_nr; i++) {
362 double percent;
363
364 percent = annotation_data__percent(&pos->al.data[i],
365 annotate_opts.percent_type);
366
367 if (max_percent < percent)
368 max_percent = percent;
369 }
370
371 if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) {
372 RB_CLEAR_NODE(&pos->al.rb_node);
373 continue;
374 }
375 disasm_rb_tree__insert(browser, &pos->al);
376 }
377 annotation__unlock(notes);
378
379 browser->curr_hot = rb_last(&browser->entries);
380 }
381
annotate_browser__find_new_asm_line(struct annotate_browser * browser,int idx_asm)382 static struct annotation_line *annotate_browser__find_new_asm_line(
383 struct annotate_browser *browser,
384 int idx_asm)
385 {
386 struct annotation_line *al;
387 struct list_head *head = browser->b.entries;
388
389 /* find an annotation line in the new list with the same idx_asm */
390 list_for_each_entry(al, head, node) {
391 if (al->idx_asm == idx_asm)
392 return al;
393 }
394
395 /* There are no asm lines */
396 return NULL;
397 }
398
annotate_browser__find_next_asm_line(struct annotate_browser * browser,struct annotation_line * al)399 static struct annotation_line *annotate_browser__find_next_asm_line(
400 struct annotate_browser *browser,
401 struct annotation_line *al)
402 {
403 struct annotation_line *it = al;
404
405 /* find next asm line */
406 list_for_each_entry_continue(it, browser->b.entries, node) {
407 if (it->idx_asm >= 0)
408 return it;
409 }
410
411 /* no asm line found forwards, try backwards */
412 it = al;
413 list_for_each_entry_continue_reverse(it, browser->b.entries, node) {
414 if (it->idx_asm >= 0)
415 return it;
416 }
417
418 /* There are no asm lines */
419 return NULL;
420 }
421
annotation__has_source(struct annotation * notes)422 static bool annotation__has_source(struct annotation *notes)
423 {
424 struct annotation_line *al;
425 bool found_asm = false;
426
427 /* Let's skip the first non-asm lines which present regardless of source. */
428 list_for_each_entry(al, ¬es->src->source, node) {
429 if (al->offset >= 0) {
430 found_asm = true;
431 break;
432 }
433 }
434
435 if (found_asm) {
436 /* After assembly lines, any line without offset means source. */
437 list_for_each_entry_continue(al, ¬es->src->source, node) {
438 if (al->offset == -1)
439 return true;
440 }
441 }
442 return false;
443 }
444
annotate_browser__toggle_source(struct annotate_browser * browser,struct evsel * evsel)445 static bool annotate_browser__toggle_source(struct annotate_browser *browser,
446 struct evsel *evsel)
447 {
448 struct annotation *notes = browser__annotation(&browser->b);
449 struct annotation_line *al;
450 off_t offset = browser->b.index - browser->b.top_idx;
451
452 browser->b.seek(&browser->b, offset, SEEK_CUR);
453 al = list_entry(browser->b.top, struct annotation_line, node);
454
455 if (!annotate_opts.annotate_src)
456 annotate_opts.annotate_src = true;
457
458 /*
459 * It's about to get source code annotation for the first time.
460 * Drop the existing annotation_lines and get the new one with source.
461 * And then move to the original line at the same asm index.
462 */
463 if (annotate_opts.hide_src_code && !notes->src->tried_source) {
464 struct map_symbol *ms = browser->b.priv;
465 int orig_idx_asm = al->idx_asm;
466
467 /* annotate again with source code info */
468 annotate_opts.hide_src_code = false;
469 annotated_source__purge(notes->src);
470 symbol__annotate2(ms, evsel, &browser->arch);
471 annotate_opts.hide_src_code = true;
472
473 /* should be after annotated_source__purge() */
474 notes->src->tried_source = true;
475
476 if (!annotation__has_source(notes))
477 ui__warning("Annotation has no source code.");
478
479 browser->b.entries = ¬es->src->source;
480 al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);
481 if (unlikely(al == NULL)) {
482 al = list_first_entry(¬es->src->source,
483 struct annotation_line, node);
484 }
485 browser->b.seek(&browser->b, al->idx_asm, SEEK_SET);
486 }
487
488 if (annotate_opts.hide_src_code) {
489 if (al->idx_asm < offset)
490 offset = al->idx;
491
492 browser->b.nr_entries = notes->src->nr_entries;
493 annotate_opts.hide_src_code = false;
494 browser->b.seek(&browser->b, -offset, SEEK_CUR);
495 browser->b.top_idx = al->idx - offset;
496 browser->b.index = al->idx;
497 } else {
498 if (al->idx_asm < 0) {
499 /* move cursor to next asm line */
500 al = annotate_browser__find_next_asm_line(browser, al);
501 if (!al) {
502 browser->b.seek(&browser->b, -offset, SEEK_CUR);
503 return false;
504 }
505 }
506
507 if (al->idx_asm < offset)
508 offset = al->idx_asm;
509
510 browser->b.nr_entries = notes->src->nr_asm_entries;
511 annotate_opts.hide_src_code = true;
512 browser->b.seek(&browser->b, -offset, SEEK_CUR);
513 browser->b.top_idx = al->idx_asm - offset;
514 browser->b.index = al->idx_asm;
515 }
516
517 if (annotate_opts.hide_src_code_on_title)
518 annotate_opts.hide_src_code_on_title = false;
519
520 return true;
521 }
522
523 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
524
annotate_browser__show_full_location(struct ui_browser * browser)525 static void annotate_browser__show_full_location(struct ui_browser *browser)
526 {
527 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
528 struct disasm_line *cursor = disasm_line(ab->selection);
529 struct annotation_line *al = &cursor->al;
530
531 if (al->offset != -1)
532 ui_helpline__puts("Only available for source code lines.");
533 else if (al->fileloc == NULL)
534 ui_helpline__puts("No source file location.");
535 else {
536 char help_line[SYM_TITLE_MAX_SIZE];
537 sprintf (help_line, "Source file location: %s", al->fileloc);
538 ui_helpline__puts(help_line);
539 }
540 }
541
ui_browser__init_asm_mode(struct ui_browser * browser)542 static void ui_browser__init_asm_mode(struct ui_browser *browser)
543 {
544 struct annotation *notes = browser__annotation(browser);
545 ui_browser__reset_index(browser);
546 browser->nr_entries = notes->src->nr_asm_entries;
547 }
548
sym_title(struct symbol * sym,struct map * map,char * title,size_t sz,int percent_type)549 static int sym_title(struct symbol *sym, struct map *map, char *title,
550 size_t sz, int percent_type)
551 {
552 return snprintf(title, sz, "%s %s [Percent: %s] %s", sym->name,
553 dso__long_name(map__dso(map)),
554 percent_type_str(percent_type),
555 annotate_opts.code_with_type ? "[Type]" : "");
556 }
557
annotate_browser__show_function_title(struct annotate_browser * browser)558 static void annotate_browser__show_function_title(struct annotate_browser *browser)
559 {
560 struct ui_browser *b = &browser->b;
561 struct map_symbol *ms = b->priv;
562 struct symbol *sym = ms->sym;
563 char title[SYM_TITLE_MAX_SIZE];
564
565 sym_title(sym, ms->map, title, sizeof(title), annotate_opts.percent_type);
566
567 ui_browser__gotorc_title(b, 0, 0);
568 ui_browser__set_color(b, HE_COLORSET_ROOT);
569 ui_browser__write_nstring(b, title, b->width + 1);
570 }
571
572 /*
573 * This can be called from external jumps, i.e. jumps from one function
574 * to another, like from the kernel's entry_SYSCALL_64 function to the
575 * swapgs_restore_regs_and_return_to_usermode() function.
576 *
577 * So all we check here is that dl->ops.target.sym is set, if it is, just
578 * go to that function and when exiting from its disassembly, come back
579 * to the calling function.
580 */
annotate_browser__callq(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)581 static bool annotate_browser__callq(struct annotate_browser *browser,
582 struct evsel *evsel,
583 struct hist_browser_timer *hbt)
584 {
585 struct map_symbol *ms = browser->b.priv, target_ms;
586 struct disasm_line *dl = disasm_line(browser->selection);
587 struct annotation *notes;
588
589 if (!dl->ops.target.sym) {
590 ui_helpline__puts("The called function was not found.");
591 return true;
592 }
593
594 notes = symbol__annotation(dl->ops.target.sym);
595 annotation__lock(notes);
596
597 if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) {
598 annotation__unlock(notes);
599 ui__warning("Not enough memory for annotating '%s' symbol!\n",
600 dl->ops.target.sym->name);
601 return true;
602 }
603
604 target_ms.maps = ms->maps;
605 target_ms.map = ms->map;
606 target_ms.sym = dl->ops.target.sym;
607 annotation__unlock(notes);
608 __hist_entry__tui_annotate(browser->he, &target_ms, evsel, hbt, NO_ADDR);
609
610 /*
611 * The annotate_browser above changed the title with the target function
612 * and now it's back to the original function. Refresh the header line
613 * for the original function again.
614 */
615 annotate_browser__show_function_title(browser);
616 return true;
617 }
618
619 static
annotate_browser__find_offset(struct annotate_browser * browser,s64 offset,s64 * idx)620 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
621 s64 offset, s64 *idx)
622 {
623 struct annotation *notes = browser__annotation(&browser->b);
624 struct disasm_line *pos;
625
626 *idx = 0;
627 list_for_each_entry(pos, ¬es->src->source, al.node) {
628 if (pos->al.offset == offset)
629 return pos;
630 if (!annotation_line__filter(&pos->al))
631 ++*idx;
632 }
633
634 return NULL;
635 }
636
annotate_browser__jump(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)637 static bool annotate_browser__jump(struct annotate_browser *browser,
638 struct evsel *evsel,
639 struct hist_browser_timer *hbt)
640 {
641 struct disasm_line *dl = disasm_line(browser->selection);
642 u64 offset;
643 s64 idx;
644
645 if (!ins__is_jump(&dl->ins))
646 return false;
647
648 if (dl->ops.target.outside) {
649 annotate_browser__callq(browser, evsel, hbt);
650 return true;
651 }
652
653 offset = dl->ops.target.offset;
654 dl = annotate_browser__find_offset(browser, offset, &idx);
655 if (dl == NULL) {
656 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
657 return true;
658 }
659
660 annotate_browser__set_top(browser, &dl->al, idx);
661
662 return true;
663 }
664
665 static
annotate_browser__find_string(struct annotate_browser * browser,char * s,s64 * idx)666 struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
667 char *s, s64 *idx)
668 {
669 struct annotation *notes = browser__annotation(&browser->b);
670 struct annotation_line *al = browser->selection;
671
672 *idx = browser->b.index;
673 list_for_each_entry_continue(al, ¬es->src->source, node) {
674 if (annotation_line__filter(al))
675 continue;
676
677 ++*idx;
678
679 if (al->line && strstr(al->line, s) != NULL)
680 return al;
681 }
682
683 return NULL;
684 }
685
__annotate_browser__search(struct annotate_browser * browser)686 static bool __annotate_browser__search(struct annotate_browser *browser)
687 {
688 struct annotation_line *al;
689 s64 idx;
690
691 al = annotate_browser__find_string(browser, browser->search_bf, &idx);
692 if (al == NULL) {
693 ui_helpline__puts("String not found!");
694 return false;
695 }
696
697 annotate_browser__set_top(browser, al, idx);
698 browser->searching_backwards = false;
699 return true;
700 }
701
702 static
annotate_browser__find_string_reverse(struct annotate_browser * browser,char * s,s64 * idx)703 struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
704 char *s, s64 *idx)
705 {
706 struct annotation *notes = browser__annotation(&browser->b);
707 struct annotation_line *al = browser->selection;
708
709 *idx = browser->b.index;
710 list_for_each_entry_continue_reverse(al, ¬es->src->source, node) {
711 if (annotation_line__filter(al))
712 continue;
713
714 --*idx;
715
716 if (al->line && strstr(al->line, s) != NULL)
717 return al;
718 }
719
720 return NULL;
721 }
722
__annotate_browser__search_reverse(struct annotate_browser * browser)723 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
724 {
725 struct annotation_line *al;
726 s64 idx;
727
728 al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
729 if (al == NULL) {
730 ui_helpline__puts("String not found!");
731 return false;
732 }
733
734 annotate_browser__set_top(browser, al, idx);
735 browser->searching_backwards = true;
736 return true;
737 }
738
annotate_browser__search_window(struct annotate_browser * browser,int delay_secs)739 static bool annotate_browser__search_window(struct annotate_browser *browser,
740 int delay_secs)
741 {
742 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
743 "ENTER: OK, ESC: Cancel",
744 delay_secs * 2) != K_ENTER ||
745 !*browser->search_bf)
746 return false;
747
748 return true;
749 }
750
annotate_browser__search(struct annotate_browser * browser,int delay_secs)751 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
752 {
753 if (annotate_browser__search_window(browser, delay_secs))
754 return __annotate_browser__search(browser);
755
756 return false;
757 }
758
annotate_browser__continue_search(struct annotate_browser * browser,int delay_secs)759 static bool annotate_browser__continue_search(struct annotate_browser *browser,
760 int delay_secs)
761 {
762 if (!*browser->search_bf)
763 return annotate_browser__search(browser, delay_secs);
764
765 return __annotate_browser__search(browser);
766 }
767
annotate_browser__search_reverse(struct annotate_browser * browser,int delay_secs)768 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
769 int delay_secs)
770 {
771 if (annotate_browser__search_window(browser, delay_secs))
772 return __annotate_browser__search_reverse(browser);
773
774 return false;
775 }
776
777 static
annotate_browser__continue_search_reverse(struct annotate_browser * browser,int delay_secs)778 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
779 int delay_secs)
780 {
781 if (!*browser->search_bf)
782 return annotate_browser__search_reverse(browser, delay_secs);
783
784 return __annotate_browser__search_reverse(browser);
785 }
786
annotate_browser__show(struct annotate_browser * browser,char * title,const char * help)787 static int annotate_browser__show(struct annotate_browser *browser, char *title, const char *help)
788 {
789 if (ui_browser__show(&browser->b, title, help) < 0)
790 return -1;
791
792 annotate_browser__show_function_title(browser);
793 return 0;
794 }
795
796 static void
switch_percent_type(struct annotation_options * opts,bool base)797 switch_percent_type(struct annotation_options *opts, bool base)
798 {
799 switch (opts->percent_type) {
800 case PERCENT_HITS_LOCAL:
801 if (base)
802 opts->percent_type = PERCENT_PERIOD_LOCAL;
803 else
804 opts->percent_type = PERCENT_HITS_GLOBAL;
805 break;
806 case PERCENT_HITS_GLOBAL:
807 if (base)
808 opts->percent_type = PERCENT_PERIOD_GLOBAL;
809 else
810 opts->percent_type = PERCENT_HITS_LOCAL;
811 break;
812 case PERCENT_PERIOD_LOCAL:
813 if (base)
814 opts->percent_type = PERCENT_HITS_LOCAL;
815 else
816 opts->percent_type = PERCENT_PERIOD_GLOBAL;
817 break;
818 case PERCENT_PERIOD_GLOBAL:
819 if (base)
820 opts->percent_type = PERCENT_HITS_GLOBAL;
821 else
822 opts->percent_type = PERCENT_PERIOD_LOCAL;
823 break;
824 default:
825 WARN_ON(1);
826 }
827 }
828
annotate__scnprintf_title(struct hists * hists,char * bf,size_t size)829 static int annotate__scnprintf_title(struct hists *hists, char *bf, size_t size)
830 {
831 int printed = hists__scnprintf_title(hists, bf, size);
832
833 if (!annotate_opts.hide_src_code_on_title) {
834 printed += scnprintf(bf + printed, size - printed, " [source: %s]",
835 annotate_opts.hide_src_code ? "OFF" : "On");
836 }
837
838 return printed;
839 }
840
annotate_browser__debuginfo_warning(struct annotate_browser * browser)841 static void annotate_browser__debuginfo_warning(struct annotate_browser *browser)
842 {
843 struct map_symbol *ms = browser->b.priv;
844 struct dso *dso = map__dso(ms->map);
845
846 if (browser->dbg == NULL && annotate_opts.code_with_type &&
847 !dso__debuginfo_warned(dso)) {
848 ui__warning("DWARF debuginfo not found.\n\n"
849 "Data-type in this DSO will not be displayed.\n"
850 "Please make sure to have debug information.");
851 dso__set_debuginfo_warned(dso);
852 }
853 }
854
annotate_browser__curr_hot_offset(struct annotate_browser * browser)855 static s64 annotate_browser__curr_hot_offset(struct annotate_browser *browser)
856 {
857 struct annotation_line *al = NULL;
858
859 if (browser->curr_hot)
860 al = rb_entry(browser->curr_hot, struct annotation_line, rb_node);
861
862 return al ? al->offset : 0;
863 }
864
annotate_browser__symbol_annotate_error(struct annotate_browser * browser,int err)865 static void annotate_browser__symbol_annotate_error(struct annotate_browser *browser, int err)
866 {
867 struct map_symbol *ms = browser->b.priv;
868 struct symbol *sym = ms->sym;
869 struct dso *dso = map__dso(ms->map);
870 char msg[BUFSIZ];
871
872 dso__set_annotate_warned(dso);
873 symbol__strerror_disassemble(ms, err, msg, sizeof(msg));
874 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
875 }
876
annotate_browser__run(struct annotate_browser * browser,struct evsel * evsel,struct hist_browser_timer * hbt)877 static int annotate_browser__run(struct annotate_browser *browser,
878 struct evsel *evsel,
879 struct hist_browser_timer *hbt)
880 {
881 struct rb_node *nd = NULL;
882 struct hists *hists = evsel__hists(evsel);
883 struct map_symbol *ms = browser->b.priv;
884 struct symbol *sym = ms->sym;
885 struct annotation *notes = symbol__annotation(ms->sym);
886 const char *help = "Press 'h' for help on key bindings";
887 int delay_secs = hbt ? hbt->refresh : 0;
888 char *br_cntr_text = NULL;
889 char title[256];
890 int key;
891
892 annotate__scnprintf_title(hists, title, sizeof(title));
893 if (annotate_browser__show(browser, title, help) < 0)
894 return -1;
895
896 annotate_browser__calc_percent(browser, evsel);
897
898 if (browser->selection != NULL) {
899 browser->curr_hot = &browser->selection->rb_node;
900 browser->b.use_navkeypressed = false;
901 }
902
903 if (browser->curr_hot) {
904 annotate_browser__set_rb_top(browser, browser->curr_hot);
905 browser->b.navkeypressed = false;
906 }
907
908 nd = browser->curr_hot;
909
910 annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false);
911
912 annotate_browser__debuginfo_warning(browser);
913
914 while (1) {
915 key = ui_browser__run(&browser->b, delay_secs);
916
917 if (delay_secs != 0) {
918 annotate_browser__calc_percent(browser, evsel);
919 /*
920 * Current line focus got out of the list of most active
921 * lines, NULL it so that if TAB|UNTAB is pressed, we
922 * move to curr_hot (current hottest line).
923 */
924 if (nd != NULL && RB_EMPTY_NODE(nd))
925 nd = NULL;
926 }
927
928 switch (key) {
929 case K_TIMER:
930 if (hbt)
931 hbt->timer(hbt->arg);
932
933 if (delay_secs != 0) {
934 symbol__annotate_decay_histogram(sym, evsel);
935 annotate__scnprintf_title(hists, title, sizeof(title));
936 annotate_browser__show(browser, title, help);
937 }
938 continue;
939 case K_TAB:
940 if (nd != NULL) {
941 nd = rb_prev(nd);
942 if (nd == NULL)
943 nd = rb_last(&browser->entries);
944 } else
945 nd = browser->curr_hot;
946 break;
947 case K_UNTAB:
948 if (nd != NULL) {
949 nd = rb_next(nd);
950 if (nd == NULL)
951 nd = rb_first(&browser->entries);
952 } else
953 nd = browser->curr_hot;
954 break;
955 case K_F1:
956 case 'h':
957 ui_browser__help_window(&browser->b,
958 "UP/DOWN/PGUP\n"
959 "PGDN/SPACE Navigate\n"
960 "</> Move to prev/next symbol\n"
961 "q/ESC/CTRL+C Exit\n\n"
962 "ENTER Go to target\n"
963 "H Go to hottest instruction\n"
964 "TAB/shift+TAB Cycle thru hottest instructions\n"
965 "j Toggle showing jump to target arrows\n"
966 "J Toggle showing number of jump sources on targets\n"
967 "n Search next string\n"
968 "o Toggle disassembler output/simplified view\n"
969 "O Bump offset level (jump targets -> +call -> all -> cycle thru)\n"
970 "s Toggle source code view\n"
971 "t Circulate percent, total period, samples view\n"
972 "c Show min/max cycle\n"
973 "/ Search string\n"
974 "k Toggle line numbers\n"
975 "l Show full source file location\n"
976 "P Print to [symbol_name].annotation file.\n"
977 "r Run available scripts\n"
978 "p Toggle percent type [local/global]\n"
979 "b Toggle percent base [period/hits]\n"
980 "B Branch counter abbr list (Optional)\n"
981 "? Search string backwards\n"
982 "f Toggle showing offsets to full address\n"
983 "T Toggle data type display\n");
984 continue;
985 case 'r':
986 script_browse(NULL, NULL);
987 annotate_browser__show(browser, title, help);
988 continue;
989 case 'k':
990 annotate_opts.show_linenr = !annotate_opts.show_linenr;
991 continue;
992 case 'l':
993 annotate_browser__show_full_location (&browser->b);
994 continue;
995 case 'H':
996 nd = browser->curr_hot;
997 break;
998 case 's': {
999 struct annotation_line *al = NULL;
1000 s64 offset = annotate_browser__curr_hot_offset(browser);
1001
1002 if (annotate_browser__toggle_source(browser, evsel))
1003 ui_helpline__puts(help);
1004
1005 /* Update the annotation browser's rb_tree, and reset the nd */
1006 annotate_browser__calc_percent(browser, evsel);
1007 /* Try to find the same asm line as before */
1008 al = annotated_source__get_line(notes->src, offset);
1009 browser->curr_hot = al ? &al->rb_node : NULL;
1010 nd = browser->curr_hot;
1011
1012 annotate__scnprintf_title(hists, title, sizeof(title));
1013 annotate_browser__show(browser, title, help);
1014 continue;
1015 }
1016 case 'o':
1017 annotate_opts.use_offset = !annotate_opts.use_offset;
1018 annotation__update_column_widths(notes);
1019 continue;
1020 case 'O':
1021 if (++annotate_opts.offset_level > ANNOTATION__MAX_OFFSET_LEVEL)
1022 annotate_opts.offset_level = ANNOTATION__MIN_OFFSET_LEVEL;
1023 continue;
1024 case 'j':
1025 annotate_opts.jump_arrows = !annotate_opts.jump_arrows;
1026 continue;
1027 case 'J':
1028 annotate_opts.show_nr_jumps = !annotate_opts.show_nr_jumps;
1029 annotation__update_column_widths(notes);
1030 continue;
1031 case '/':
1032 if (annotate_browser__search(browser, delay_secs)) {
1033 show_help:
1034 ui_helpline__puts(help);
1035 }
1036 continue;
1037 case 'n':
1038 if (browser->searching_backwards ?
1039 annotate_browser__continue_search_reverse(browser, delay_secs) :
1040 annotate_browser__continue_search(browser, delay_secs))
1041 goto show_help;
1042 continue;
1043 case '?':
1044 if (annotate_browser__search_reverse(browser, delay_secs))
1045 goto show_help;
1046 continue;
1047 case 'D': {
1048 static int seq;
1049 ui_helpline__pop();
1050 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
1051 seq++, browser->b.nr_entries,
1052 browser->b.height,
1053 browser->b.index,
1054 browser->b.top_idx,
1055 notes->src->nr_asm_entries);
1056 }
1057 continue;
1058 case K_ENTER:
1059 case K_RIGHT:
1060 {
1061 struct disasm_line *dl = disasm_line(browser->selection);
1062
1063 if (browser->selection == NULL)
1064 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
1065 else if (browser->selection->offset == -1)
1066 ui_helpline__puts("Actions are only available for assembly lines.");
1067 else if (!dl->ins.ops)
1068 goto show_sup_ins;
1069 else if (ins__is_ret(&dl->ins))
1070 goto out;
1071 else if (!(annotate_browser__jump(browser, evsel, hbt) ||
1072 annotate_browser__callq(browser, evsel, hbt))) {
1073 show_sup_ins:
1074 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
1075 }
1076 continue;
1077 }
1078 case 'P':
1079 map_symbol__annotation_dump(ms, evsel, browser->he);
1080 continue;
1081 case 't':
1082 if (symbol_conf.show_total_period) {
1083 symbol_conf.show_total_period = false;
1084 symbol_conf.show_nr_samples = true;
1085 } else if (symbol_conf.show_nr_samples)
1086 symbol_conf.show_nr_samples = false;
1087 else
1088 symbol_conf.show_total_period = true;
1089 annotation__update_column_widths(notes);
1090 continue;
1091 case 'c':
1092 if (annotate_opts.show_minmax_cycle)
1093 annotate_opts.show_minmax_cycle = false;
1094 else
1095 annotate_opts.show_minmax_cycle = true;
1096 annotation__update_column_widths(notes);
1097 continue;
1098 case 'p':
1099 case 'b':
1100 switch_percent_type(&annotate_opts, key == 'b');
1101 annotate__scnprintf_title(hists, title, sizeof(title));
1102 annotate_browser__show(browser, title, help);
1103 continue;
1104 case 'B':
1105 if (br_cntr_text)
1106 ui_browser__help_window(&browser->b, br_cntr_text);
1107 else {
1108 ui_browser__help_window(&browser->b,
1109 "\n The branch counter is not available.\n");
1110 }
1111 continue;
1112 case 'f':
1113 annotation__toggle_full_addr(notes, ms);
1114 continue;
1115 case 'T':
1116 annotate_opts.code_with_type ^= 1;
1117 if (browser->dbg == NULL)
1118 browser->dbg = dso__debuginfo(map__dso(ms->map));
1119 if (browser->type_hash == NULL) {
1120 browser->type_hash = hashmap__new(type_hash, type_equal,
1121 /*ctx=*/NULL);
1122 }
1123 annotate_browser__show(browser, title, help);
1124 annotate_browser__debuginfo_warning(browser);
1125 continue;
1126 case K_LEFT:
1127 case '<':
1128 case '>':
1129 case K_ESC:
1130 case 'q':
1131 case CTRL('c'):
1132 goto out;
1133 default:
1134 ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs, ", use 'h'/F1 to see actions");
1135 continue;
1136 }
1137
1138 if (nd != NULL)
1139 annotate_browser__set_rb_top(browser, nd);
1140 }
1141 out:
1142 ui_browser__hide(&browser->b);
1143 free(br_cntr_text);
1144 return key;
1145 }
1146
hist_entry__tui_annotate(struct hist_entry * he,struct evsel * evsel,struct hist_browser_timer * hbt,u64 al_addr)1147 int hist_entry__tui_annotate(struct hist_entry *he, struct evsel *evsel,
1148 struct hist_browser_timer *hbt, u64 al_addr)
1149 {
1150 /* reset abort key so that it can get Ctrl-C as a key */
1151 SLang_reset_tty();
1152 SLang_init_tty(0, 0, 0);
1153 SLtty_set_suspend_state(true);
1154
1155 return __hist_entry__tui_annotate(he, &he->ms, evsel, hbt, al_addr);
1156 }
1157
__hist_entry__tui_annotate(struct hist_entry * he,struct map_symbol * ms,struct evsel * evsel,struct hist_browser_timer * hbt,u64 al_addr)1158 int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
1159 struct evsel *evsel,
1160 struct hist_browser_timer *hbt, u64 al_addr)
1161 {
1162 struct symbol *sym = ms->sym;
1163 struct annotation *notes = symbol__annotation(sym);
1164 struct annotate_browser browser = {
1165 .b = {
1166 .refresh = annotate_browser__refresh,
1167 .seek = ui_browser__list_head_seek,
1168 .write = annotate_browser__write,
1169 .filter = disasm_line__filter,
1170 .extra_title_lines = 1, /* for hists__scnprintf_title() */
1171 .priv = ms,
1172 .use_navkeypressed = true,
1173 },
1174 .he = he,
1175 .evsel = evsel,
1176 };
1177 struct dso *dso;
1178 int ret = -1, err;
1179 int not_annotated = list_empty(¬es->src->source);
1180
1181 if (sym == NULL)
1182 return -1;
1183
1184 dso = map__dso(ms->map);
1185 if (dso__annotate_warned(dso))
1186 return -1;
1187
1188 if (not_annotated || !sym->annotate2) {
1189 err = symbol__annotate2(ms, evsel, &browser.arch);
1190 if (err) {
1191 annotate_browser__symbol_annotate_error(&browser, err);
1192 return -1;
1193 }
1194
1195 if (!annotate_opts.hide_src_code) {
1196 notes->src->tried_source = true;
1197 if (!annotation__has_source(notes))
1198 ui__warning("Annotation has no source code.");
1199 }
1200 } else {
1201 err = evsel__get_arch(evsel, &browser.arch);
1202 if (err) {
1203 annotate_browser__symbol_annotate_error(&browser, err);
1204 return -1;
1205 }
1206 }
1207
1208 /* Copy necessary information when it's called from perf top */
1209 if (hbt != NULL && he != &annotate_he) {
1210 annotate_he.hists = he->hists;
1211 annotate_he.thread = thread__get(he->thread);
1212 annotate_he.cpumode = he->cpumode;
1213 map_symbol__copy(&annotate_he.ms, ms);
1214
1215 browser.he = &annotate_he;
1216 }
1217
1218 ui_helpline__push("Press ESC to exit");
1219
1220 if (annotate_opts.code_with_type) {
1221 browser.dbg = dso__debuginfo(dso);
1222 browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL);
1223 }
1224
1225 browser.b.width = notes->src->widths.max_line_len;
1226 browser.b.nr_entries = notes->src->nr_entries;
1227 browser.b.entries = ¬es->src->source;
1228 browser.b.width += 18; /* Percentage */
1229
1230 if (annotate_opts.hide_src_code)
1231 ui_browser__init_asm_mode(&browser.b);
1232
1233 /*
1234 * If al_addr is set, it means that there should be a line
1235 * intentionally selected, not based on the percentages
1236 * which caculated by the event sampling. In this case, we
1237 * convey this information into the browser selection, where
1238 * the selection in other cases should be empty.
1239 */
1240 if (al_addr != NO_ADDR) {
1241 struct annotation_line *al = annotated_source__get_line(notes->src,
1242 al_addr - sym->start);
1243
1244 browser.selection = al;
1245 }
1246
1247 ret = annotate_browser__run(&browser, evsel, hbt);
1248
1249 debuginfo__delete(browser.dbg);
1250
1251 if (!IS_ERR_OR_NULL(browser.type_hash)) {
1252 struct hashmap_entry *cur;
1253 size_t bkt;
1254
1255 hashmap__for_each_entry(browser.type_hash, cur, bkt)
1256 zfree(&cur->pvalue);
1257 hashmap__free(browser.type_hash);
1258 }
1259
1260 if (not_annotated && !notes->src->tried_source)
1261 annotated_source__purge(notes->src);
1262
1263 if (hbt != NULL && he != &annotate_he) {
1264 thread__zput(annotate_he.thread);
1265 map_symbol__exit(&annotate_he.ms);
1266 }
1267
1268 return ret;
1269 }
1270