1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.h"
5 #include "../ui.h"
6 #include "../util.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include "../../util/evsel.h"
12 #include "../../util/config.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 <sys/ttydefaults.h>
19
20 struct disasm_line_samples {
21 double percent;
22 struct sym_hist_entry he;
23 };
24
25 #define IPC_WIDTH 6
26 #define CYCLES_WIDTH 6
27
28 struct browser_disasm_line {
29 struct rb_node rb_node;
30 u32 idx;
31 int idx_asm;
32 int jump_sources;
33 /*
34 * actual length of this array is saved on the nr_events field
35 * of the struct annotate_browser
36 */
37 struct disasm_line_samples samples[1];
38 };
39
40 static struct annotate_browser_opt {
41 bool hide_src_code,
42 use_offset,
43 jump_arrows,
44 show_linenr,
45 show_nr_jumps,
46 show_nr_samples,
47 show_total_period;
48 } annotate_browser__opts = {
49 .use_offset = true,
50 .jump_arrows = true,
51 };
52
53 struct arch;
54
55 struct annotate_browser {
56 struct ui_browser b;
57 struct rb_root entries;
58 struct rb_node *curr_hot;
59 struct disasm_line *selection;
60 struct disasm_line **offsets;
61 struct arch *arch;
62 int nr_events;
63 u64 start;
64 int nr_asm_entries;
65 int nr_entries;
66 int max_jump_sources;
67 int nr_jumps;
68 bool searching_backwards;
69 bool have_cycles;
70 u8 addr_width;
71 u8 jumps_width;
72 u8 target_width;
73 u8 min_addr_width;
74 u8 max_addr_width;
75 char search_bf[128];
76 };
77
disasm_line__browser(struct disasm_line * dl)78 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
79 {
80 return (struct browser_disasm_line *)(dl + 1);
81 }
82
disasm_line__filter(struct ui_browser * browser __maybe_unused,void * entry)83 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
84 void *entry)
85 {
86 if (annotate_browser__opts.hide_src_code) {
87 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
88 return dl->offset == -1;
89 }
90
91 return false;
92 }
93
annotate_browser__jumps_percent_color(struct annotate_browser * browser,int nr,bool current)94 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
95 int nr, bool current)
96 {
97 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
98 return HE_COLORSET_SELECTED;
99 if (nr == browser->max_jump_sources)
100 return HE_COLORSET_TOP;
101 if (nr > 1)
102 return HE_COLORSET_MEDIUM;
103 return HE_COLORSET_NORMAL;
104 }
105
annotate_browser__set_jumps_percent_color(struct annotate_browser * browser,int nr,bool current)106 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
107 int nr, bool current)
108 {
109 int color = annotate_browser__jumps_percent_color(browser, nr, current);
110 return ui_browser__set_color(&browser->b, color);
111 }
112
annotate_browser__pcnt_width(struct annotate_browser * ab)113 static int annotate_browser__pcnt_width(struct annotate_browser *ab)
114 {
115 return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
116 }
117
annotate_browser__cycles_width(struct annotate_browser * ab)118 static int annotate_browser__cycles_width(struct annotate_browser *ab)
119 {
120 return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
121 }
122
annotate_browser__write(struct ui_browser * browser,void * entry,int row)123 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
124 {
125 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
126 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
127 struct browser_disasm_line *bdl = disasm_line__browser(dl);
128 bool current_entry = ui_browser__is_current_entry(browser, row);
129 bool change_color = (!annotate_browser__opts.hide_src_code &&
130 (!current_entry || (browser->use_navkeypressed &&
131 !browser->navkeypressed)));
132 int width = browser->width, printed;
133 int i, pcnt_width = annotate_browser__pcnt_width(ab),
134 cycles_width = annotate_browser__cycles_width(ab);
135 double percent_max = 0.0;
136 char bf[256];
137 bool show_title = false;
138
139 for (i = 0; i < ab->nr_events; i++) {
140 if (bdl->samples[i].percent > percent_max)
141 percent_max = bdl->samples[i].percent;
142 }
143
144 if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
145 if (ab->have_cycles) {
146 if (dl->ipc == 0.0 && dl->cycles == 0)
147 show_title = true;
148 } else
149 show_title = true;
150 }
151
152 if (dl->offset != -1 && percent_max != 0.0) {
153 for (i = 0; i < ab->nr_events; i++) {
154 ui_browser__set_percent_color(browser,
155 bdl->samples[i].percent,
156 current_entry);
157 if (annotate_browser__opts.show_total_period) {
158 ui_browser__printf(browser, "%11" PRIu64 " ",
159 bdl->samples[i].he.period);
160 } else if (annotate_browser__opts.show_nr_samples) {
161 ui_browser__printf(browser, "%6" PRIu64 " ",
162 bdl->samples[i].he.nr_samples);
163 } else {
164 ui_browser__printf(browser, "%6.2f ",
165 bdl->samples[i].percent);
166 }
167 }
168 } else {
169 ui_browser__set_percent_color(browser, 0, current_entry);
170
171 if (!show_title)
172 ui_browser__write_nstring(browser, " ", pcnt_width);
173 else {
174 ui_browser__printf(browser, "%*s", pcnt_width,
175 annotate_browser__opts.show_total_period ? "Period" :
176 annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
177 }
178 }
179 if (ab->have_cycles) {
180 if (dl->ipc)
181 ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
182 else if (!show_title)
183 ui_browser__write_nstring(browser, " ", IPC_WIDTH);
184 else
185 ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");
186
187 if (dl->cycles)
188 ui_browser__printf(browser, "%*" PRIu64 " ",
189 CYCLES_WIDTH - 1, dl->cycles);
190 else if (!show_title)
191 ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
192 else
193 ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
194 }
195
196 SLsmg_write_char(' ');
197
198 /* The scroll bar isn't being used */
199 if (!browser->navkeypressed)
200 width += 1;
201
202 if (!*dl->line)
203 ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
204 else if (dl->offset == -1) {
205 if (dl->line_nr && annotate_browser__opts.show_linenr)
206 printed = scnprintf(bf, sizeof(bf), "%-*d ",
207 ab->addr_width + 1, dl->line_nr);
208 else
209 printed = scnprintf(bf, sizeof(bf), "%*s ",
210 ab->addr_width, " ");
211 ui_browser__write_nstring(browser, bf, printed);
212 ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width - cycles_width + 1);
213 } else {
214 u64 addr = dl->offset;
215 int color = -1;
216
217 if (!annotate_browser__opts.use_offset)
218 addr += ab->start;
219
220 if (!annotate_browser__opts.use_offset) {
221 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
222 } else {
223 if (bdl->jump_sources) {
224 if (annotate_browser__opts.show_nr_jumps) {
225 int prev;
226 printed = scnprintf(bf, sizeof(bf), "%*d ",
227 ab->jumps_width,
228 bdl->jump_sources);
229 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
230 current_entry);
231 ui_browser__write_nstring(browser, bf, printed);
232 ui_browser__set_color(browser, prev);
233 }
234
235 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
236 ab->target_width, addr);
237 } else {
238 printed = scnprintf(bf, sizeof(bf), "%*s ",
239 ab->addr_width, " ");
240 }
241 }
242
243 if (change_color)
244 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
245 ui_browser__write_nstring(browser, bf, printed);
246 if (change_color)
247 ui_browser__set_color(browser, color);
248 if (dl->ins.ops && dl->ins.ops->scnprintf) {
249 if (ins__is_jump(&dl->ins)) {
250 bool fwd = dl->ops.target.offset > dl->offset;
251
252 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
253 SLSMG_UARROW_CHAR);
254 SLsmg_write_char(' ');
255 } else if (ins__is_call(&dl->ins)) {
256 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
257 SLsmg_write_char(' ');
258 } else if (ins__is_ret(&dl->ins)) {
259 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
260 SLsmg_write_char(' ');
261 } else {
262 ui_browser__write_nstring(browser, " ", 2);
263 }
264 } else {
265 ui_browser__write_nstring(browser, " ", 2);
266 }
267
268 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
269 ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
270 }
271
272 if (current_entry)
273 ab->selection = dl;
274 }
275
disasm_line__is_valid_jump(struct disasm_line * dl,struct symbol * sym)276 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
277 {
278 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
279 || !disasm_line__has_offset(dl)
280 || dl->ops.target.offset < 0
281 || dl->ops.target.offset >= (s64)symbol__size(sym))
282 return false;
283
284 return true;
285 }
286
is_fused(struct annotate_browser * ab,struct disasm_line * cursor)287 static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
288 {
289 struct disasm_line *pos = list_prev_entry(cursor, node);
290 const char *name;
291
292 if (!pos)
293 return false;
294
295 if (ins__is_lock(&pos->ins))
296 name = pos->ops.locked.ins.name;
297 else
298 name = pos->ins.name;
299
300 if (!name || !cursor->ins.name)
301 return false;
302
303 return ins__is_fused(ab->arch, name, cursor->ins.name);
304 }
305
annotate_browser__draw_current_jump(struct ui_browser * browser)306 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
307 {
308 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
309 struct disasm_line *cursor = ab->selection, *target;
310 struct browser_disasm_line *btarget, *bcursor;
311 unsigned int from, to;
312 struct map_symbol *ms = ab->b.priv;
313 struct symbol *sym = ms->sym;
314 u8 pcnt_width = annotate_browser__pcnt_width(ab);
315 int width = 0;
316
317 /* PLT symbols contain external offsets */
318 if (strstr(sym->name, "@plt"))
319 return;
320
321 if (!disasm_line__is_valid_jump(cursor, sym))
322 return;
323
324 target = ab->offsets[cursor->ops.target.offset];
325 if (!target)
326 return;
327
328 bcursor = disasm_line__browser(cursor);
329 btarget = disasm_line__browser(target);
330
331 if (annotate_browser__opts.hide_src_code) {
332 from = bcursor->idx_asm;
333 to = btarget->idx_asm;
334 } else {
335 from = (u64)bcursor->idx;
336 to = (u64)btarget->idx;
337 }
338
339 if (ab->have_cycles)
340 width = IPC_WIDTH + CYCLES_WIDTH;
341
342 ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
343 __ui_browser__line_arrow(browser,
344 pcnt_width + 2 + ab->addr_width + width,
345 from, to);
346
347 if (is_fused(ab, cursor)) {
348 ui_browser__mark_fused(browser,
349 pcnt_width + 3 + ab->addr_width + width,
350 from - 1,
351 to > from ? true : false);
352 }
353 }
354
annotate_browser__refresh(struct ui_browser * browser)355 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
356 {
357 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
358 int ret = ui_browser__list_head_refresh(browser);
359 int pcnt_width = annotate_browser__pcnt_width(ab);
360
361 if (annotate_browser__opts.jump_arrows)
362 annotate_browser__draw_current_jump(browser);
363
364 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
365 __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
366 return ret;
367 }
368
disasm__cmp(struct browser_disasm_line * a,struct browser_disasm_line * b,int nr_pcnt)369 static int disasm__cmp(struct browser_disasm_line *a,
370 struct browser_disasm_line *b, int nr_pcnt)
371 {
372 int i;
373
374 for (i = 0; i < nr_pcnt; i++) {
375 if (a->samples[i].percent == b->samples[i].percent)
376 continue;
377 return a->samples[i].percent < b->samples[i].percent;
378 }
379 return 0;
380 }
381
disasm_rb_tree__insert(struct rb_root * root,struct browser_disasm_line * bdl,int nr_events)382 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
383 int nr_events)
384 {
385 struct rb_node **p = &root->rb_node;
386 struct rb_node *parent = NULL;
387 struct browser_disasm_line *l;
388
389 while (*p != NULL) {
390 parent = *p;
391 l = rb_entry(parent, struct browser_disasm_line, rb_node);
392
393 if (disasm__cmp(bdl, l, nr_events))
394 p = &(*p)->rb_left;
395 else
396 p = &(*p)->rb_right;
397 }
398 rb_link_node(&bdl->rb_node, parent, p);
399 rb_insert_color(&bdl->rb_node, root);
400 }
401
annotate_browser__set_top(struct annotate_browser * browser,struct disasm_line * pos,u32 idx)402 static void annotate_browser__set_top(struct annotate_browser *browser,
403 struct disasm_line *pos, u32 idx)
404 {
405 unsigned back;
406
407 ui_browser__refresh_dimensions(&browser->b);
408 back = browser->b.height / 2;
409 browser->b.top_idx = browser->b.index = idx;
410
411 while (browser->b.top_idx != 0 && back != 0) {
412 pos = list_entry(pos->node.prev, struct disasm_line, node);
413
414 if (disasm_line__filter(&browser->b, &pos->node))
415 continue;
416
417 --browser->b.top_idx;
418 --back;
419 }
420
421 browser->b.top = pos;
422 browser->b.navkeypressed = true;
423 }
424
annotate_browser__set_rb_top(struct annotate_browser * browser,struct rb_node * nd)425 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
426 struct rb_node *nd)
427 {
428 struct browser_disasm_line *bpos;
429 struct disasm_line *pos;
430 u32 idx;
431
432 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
433 pos = ((struct disasm_line *)bpos) - 1;
434 idx = bpos->idx;
435 if (annotate_browser__opts.hide_src_code)
436 idx = bpos->idx_asm;
437 annotate_browser__set_top(browser, pos, idx);
438 browser->curr_hot = nd;
439 }
440
annotate_browser__calc_percent(struct annotate_browser * browser,struct perf_evsel * evsel)441 static void annotate_browser__calc_percent(struct annotate_browser *browser,
442 struct perf_evsel *evsel)
443 {
444 struct map_symbol *ms = browser->b.priv;
445 struct symbol *sym = ms->sym;
446 struct annotation *notes = symbol__annotation(sym);
447 struct disasm_line *pos, *next;
448 s64 len = symbol__size(sym);
449
450 browser->entries = RB_ROOT;
451
452 pthread_mutex_lock(¬es->lock);
453
454 list_for_each_entry(pos, ¬es->src->source, node) {
455 struct browser_disasm_line *bpos = disasm_line__browser(pos);
456 const char *path = NULL;
457 double max_percent = 0.0;
458 int i;
459
460 if (pos->offset == -1) {
461 RB_CLEAR_NODE(&bpos->rb_node);
462 continue;
463 }
464
465 next = disasm__get_next_ip_line(¬es->src->source, pos);
466
467 for (i = 0; i < browser->nr_events; i++) {
468 struct sym_hist_entry sample;
469
470 bpos->samples[i].percent = disasm__calc_percent(notes,
471 evsel->idx + i,
472 pos->offset,
473 next ? next->offset : len,
474 &path, &sample);
475 bpos->samples[i].he = sample;
476
477 if (max_percent < bpos->samples[i].percent)
478 max_percent = bpos->samples[i].percent;
479 }
480
481 if (max_percent < 0.01 && pos->ipc == 0) {
482 RB_CLEAR_NODE(&bpos->rb_node);
483 continue;
484 }
485 disasm_rb_tree__insert(&browser->entries, bpos,
486 browser->nr_events);
487 }
488 pthread_mutex_unlock(¬es->lock);
489
490 browser->curr_hot = rb_last(&browser->entries);
491 }
492
annotate_browser__toggle_source(struct annotate_browser * browser)493 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
494 {
495 struct disasm_line *dl;
496 struct browser_disasm_line *bdl;
497 off_t offset = browser->b.index - browser->b.top_idx;
498
499 browser->b.seek(&browser->b, offset, SEEK_CUR);
500 dl = list_entry(browser->b.top, struct disasm_line, node);
501 bdl = disasm_line__browser(dl);
502
503 if (annotate_browser__opts.hide_src_code) {
504 if (bdl->idx_asm < offset)
505 offset = bdl->idx;
506
507 browser->b.nr_entries = browser->nr_entries;
508 annotate_browser__opts.hide_src_code = false;
509 browser->b.seek(&browser->b, -offset, SEEK_CUR);
510 browser->b.top_idx = bdl->idx - offset;
511 browser->b.index = bdl->idx;
512 } else {
513 if (bdl->idx_asm < 0) {
514 ui_helpline__puts("Only available for assembly lines.");
515 browser->b.seek(&browser->b, -offset, SEEK_CUR);
516 return false;
517 }
518
519 if (bdl->idx_asm < offset)
520 offset = bdl->idx_asm;
521
522 browser->b.nr_entries = browser->nr_asm_entries;
523 annotate_browser__opts.hide_src_code = true;
524 browser->b.seek(&browser->b, -offset, SEEK_CUR);
525 browser->b.top_idx = bdl->idx_asm - offset;
526 browser->b.index = bdl->idx_asm;
527 }
528
529 return true;
530 }
531
annotate_browser__init_asm_mode(struct annotate_browser * browser)532 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
533 {
534 ui_browser__reset_index(&browser->b);
535 browser->b.nr_entries = browser->nr_asm_entries;
536 }
537
538 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
539
sym_title(struct symbol * sym,struct map * map,char * title,size_t sz)540 static int sym_title(struct symbol *sym, struct map *map, char *title,
541 size_t sz)
542 {
543 return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name);
544 }
545
annotate_browser__callq(struct annotate_browser * browser,struct perf_evsel * evsel,struct hist_browser_timer * hbt)546 static bool annotate_browser__callq(struct annotate_browser *browser,
547 struct perf_evsel *evsel,
548 struct hist_browser_timer *hbt)
549 {
550 struct map_symbol *ms = browser->b.priv;
551 struct disasm_line *dl = browser->selection;
552 struct annotation *notes;
553 struct addr_map_symbol target = {
554 .map = ms->map,
555 .addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
556 };
557 char title[SYM_TITLE_MAX_SIZE];
558
559 if (!ins__is_call(&dl->ins))
560 return false;
561
562 if (map_groups__find_ams(&target) ||
563 map__rip_2objdump(target.map, target.map->map_ip(target.map,
564 target.addr)) !=
565 dl->ops.target.addr) {
566 ui_helpline__puts("The called function was not found.");
567 return true;
568 }
569
570 notes = symbol__annotation(target.sym);
571 pthread_mutex_lock(¬es->lock);
572
573 if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
574 pthread_mutex_unlock(¬es->lock);
575 ui__warning("Not enough memory for annotating '%s' symbol!\n",
576 target.sym->name);
577 return true;
578 }
579
580 pthread_mutex_unlock(¬es->lock);
581 symbol__tui_annotate(target.sym, target.map, evsel, hbt);
582 sym_title(ms->sym, ms->map, title, sizeof(title));
583 ui_browser__show_title(&browser->b, title);
584 return true;
585 }
586
587 static
annotate_browser__find_offset(struct annotate_browser * browser,s64 offset,s64 * idx)588 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
589 s64 offset, s64 *idx)
590 {
591 struct map_symbol *ms = browser->b.priv;
592 struct symbol *sym = ms->sym;
593 struct annotation *notes = symbol__annotation(sym);
594 struct disasm_line *pos;
595
596 *idx = 0;
597 list_for_each_entry(pos, ¬es->src->source, node) {
598 if (pos->offset == offset)
599 return pos;
600 if (!disasm_line__filter(&browser->b, &pos->node))
601 ++*idx;
602 }
603
604 return NULL;
605 }
606
annotate_browser__jump(struct annotate_browser * browser)607 static bool annotate_browser__jump(struct annotate_browser *browser)
608 {
609 struct disasm_line *dl = browser->selection;
610 u64 offset;
611 s64 idx;
612
613 if (!ins__is_jump(&dl->ins))
614 return false;
615
616 offset = dl->ops.target.offset;
617 dl = annotate_browser__find_offset(browser, offset, &idx);
618 if (dl == NULL) {
619 ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
620 return true;
621 }
622
623 annotate_browser__set_top(browser, dl, idx);
624
625 return true;
626 }
627
628 static
annotate_browser__find_string(struct annotate_browser * browser,char * s,s64 * idx)629 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
630 char *s, s64 *idx)
631 {
632 struct map_symbol *ms = browser->b.priv;
633 struct symbol *sym = ms->sym;
634 struct annotation *notes = symbol__annotation(sym);
635 struct disasm_line *pos = browser->selection;
636
637 *idx = browser->b.index;
638 list_for_each_entry_continue(pos, ¬es->src->source, node) {
639 if (disasm_line__filter(&browser->b, &pos->node))
640 continue;
641
642 ++*idx;
643
644 if (pos->line && strstr(pos->line, s) != NULL)
645 return pos;
646 }
647
648 return NULL;
649 }
650
__annotate_browser__search(struct annotate_browser * browser)651 static bool __annotate_browser__search(struct annotate_browser *browser)
652 {
653 struct disasm_line *dl;
654 s64 idx;
655
656 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
657 if (dl == NULL) {
658 ui_helpline__puts("String not found!");
659 return false;
660 }
661
662 annotate_browser__set_top(browser, dl, idx);
663 browser->searching_backwards = false;
664 return true;
665 }
666
667 static
annotate_browser__find_string_reverse(struct annotate_browser * browser,char * s,s64 * idx)668 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
669 char *s, s64 *idx)
670 {
671 struct map_symbol *ms = browser->b.priv;
672 struct symbol *sym = ms->sym;
673 struct annotation *notes = symbol__annotation(sym);
674 struct disasm_line *pos = browser->selection;
675
676 *idx = browser->b.index;
677 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
678 if (disasm_line__filter(&browser->b, &pos->node))
679 continue;
680
681 --*idx;
682
683 if (pos->line && strstr(pos->line, s) != NULL)
684 return pos;
685 }
686
687 return NULL;
688 }
689
__annotate_browser__search_reverse(struct annotate_browser * browser)690 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
691 {
692 struct disasm_line *dl;
693 s64 idx;
694
695 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
696 if (dl == NULL) {
697 ui_helpline__puts("String not found!");
698 return false;
699 }
700
701 annotate_browser__set_top(browser, dl, idx);
702 browser->searching_backwards = true;
703 return true;
704 }
705
annotate_browser__search_window(struct annotate_browser * browser,int delay_secs)706 static bool annotate_browser__search_window(struct annotate_browser *browser,
707 int delay_secs)
708 {
709 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
710 "ENTER: OK, ESC: Cancel",
711 delay_secs * 2) != K_ENTER ||
712 !*browser->search_bf)
713 return false;
714
715 return true;
716 }
717
annotate_browser__search(struct annotate_browser * browser,int delay_secs)718 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
719 {
720 if (annotate_browser__search_window(browser, delay_secs))
721 return __annotate_browser__search(browser);
722
723 return false;
724 }
725
annotate_browser__continue_search(struct annotate_browser * browser,int delay_secs)726 static bool annotate_browser__continue_search(struct annotate_browser *browser,
727 int delay_secs)
728 {
729 if (!*browser->search_bf)
730 return annotate_browser__search(browser, delay_secs);
731
732 return __annotate_browser__search(browser);
733 }
734
annotate_browser__search_reverse(struct annotate_browser * browser,int delay_secs)735 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
736 int delay_secs)
737 {
738 if (annotate_browser__search_window(browser, delay_secs))
739 return __annotate_browser__search_reverse(browser);
740
741 return false;
742 }
743
744 static
annotate_browser__continue_search_reverse(struct annotate_browser * browser,int delay_secs)745 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
746 int delay_secs)
747 {
748 if (!*browser->search_bf)
749 return annotate_browser__search_reverse(browser, delay_secs);
750
751 return __annotate_browser__search_reverse(browser);
752 }
753
annotate_browser__update_addr_width(struct annotate_browser * browser)754 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
755 {
756 if (annotate_browser__opts.use_offset)
757 browser->target_width = browser->min_addr_width;
758 else
759 browser->target_width = browser->max_addr_width;
760
761 browser->addr_width = browser->target_width;
762
763 if (annotate_browser__opts.show_nr_jumps)
764 browser->addr_width += browser->jumps_width + 1;
765 }
766
annotate_browser__run(struct annotate_browser * browser,struct perf_evsel * evsel,struct hist_browser_timer * hbt)767 static int annotate_browser__run(struct annotate_browser *browser,
768 struct perf_evsel *evsel,
769 struct hist_browser_timer *hbt)
770 {
771 struct rb_node *nd = NULL;
772 struct map_symbol *ms = browser->b.priv;
773 struct symbol *sym = ms->sym;
774 const char *help = "Press 'h' for help on key bindings";
775 int delay_secs = hbt ? hbt->refresh : 0;
776 int key;
777 char title[SYM_TITLE_MAX_SIZE];
778
779 sym_title(sym, ms->map, title, sizeof(title));
780 if (ui_browser__show(&browser->b, title, help) < 0)
781 return -1;
782
783 annotate_browser__calc_percent(browser, evsel);
784
785 if (browser->curr_hot) {
786 annotate_browser__set_rb_top(browser, browser->curr_hot);
787 browser->b.navkeypressed = false;
788 }
789
790 nd = browser->curr_hot;
791
792 while (1) {
793 key = ui_browser__run(&browser->b, delay_secs);
794
795 if (delay_secs != 0) {
796 annotate_browser__calc_percent(browser, evsel);
797 /*
798 * Current line focus got out of the list of most active
799 * lines, NULL it so that if TAB|UNTAB is pressed, we
800 * move to curr_hot (current hottest line).
801 */
802 if (nd != NULL && RB_EMPTY_NODE(nd))
803 nd = NULL;
804 }
805
806 switch (key) {
807 case K_TIMER:
808 if (hbt)
809 hbt->timer(hbt->arg);
810
811 if (delay_secs != 0)
812 symbol__annotate_decay_histogram(sym, evsel->idx);
813 continue;
814 case K_TAB:
815 if (nd != NULL) {
816 nd = rb_prev(nd);
817 if (nd == NULL)
818 nd = rb_last(&browser->entries);
819 } else
820 nd = browser->curr_hot;
821 break;
822 case K_UNTAB:
823 if (nd != NULL) {
824 nd = rb_next(nd);
825 if (nd == NULL)
826 nd = rb_first(&browser->entries);
827 } else
828 nd = browser->curr_hot;
829 break;
830 case K_F1:
831 case 'h':
832 ui_browser__help_window(&browser->b,
833 "UP/DOWN/PGUP\n"
834 "PGDN/SPACE Navigate\n"
835 "q/ESC/CTRL+C Exit\n\n"
836 "ENTER Go to target\n"
837 "ESC Exit\n"
838 "H Go to hottest instruction\n"
839 "TAB/shift+TAB Cycle thru hottest instructions\n"
840 "j Toggle showing jump to target arrows\n"
841 "J Toggle showing number of jump sources on targets\n"
842 "n Search next string\n"
843 "o Toggle disassembler output/simplified view\n"
844 "s Toggle source code view\n"
845 "t Circulate percent, total period, samples view\n"
846 "/ Search string\n"
847 "k Toggle line numbers\n"
848 "r Run available scripts\n"
849 "? Search string backwards\n");
850 continue;
851 case 'r':
852 {
853 script_browse(NULL);
854 continue;
855 }
856 case 'k':
857 annotate_browser__opts.show_linenr =
858 !annotate_browser__opts.show_linenr;
859 break;
860 case 'H':
861 nd = browser->curr_hot;
862 break;
863 case 's':
864 if (annotate_browser__toggle_source(browser))
865 ui_helpline__puts(help);
866 continue;
867 case 'o':
868 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
869 annotate_browser__update_addr_width(browser);
870 continue;
871 case 'j':
872 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
873 continue;
874 case 'J':
875 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
876 annotate_browser__update_addr_width(browser);
877 continue;
878 case '/':
879 if (annotate_browser__search(browser, delay_secs)) {
880 show_help:
881 ui_helpline__puts(help);
882 }
883 continue;
884 case 'n':
885 if (browser->searching_backwards ?
886 annotate_browser__continue_search_reverse(browser, delay_secs) :
887 annotate_browser__continue_search(browser, delay_secs))
888 goto show_help;
889 continue;
890 case '?':
891 if (annotate_browser__search_reverse(browser, delay_secs))
892 goto show_help;
893 continue;
894 case 'D': {
895 static int seq;
896 ui_helpline__pop();
897 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
898 seq++, browser->b.nr_entries,
899 browser->b.height,
900 browser->b.index,
901 browser->b.top_idx,
902 browser->nr_asm_entries);
903 }
904 continue;
905 case K_ENTER:
906 case K_RIGHT:
907 if (browser->selection == NULL)
908 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
909 else if (browser->selection->offset == -1)
910 ui_helpline__puts("Actions are only available for assembly lines.");
911 else if (!browser->selection->ins.ops)
912 goto show_sup_ins;
913 else if (ins__is_ret(&browser->selection->ins))
914 goto out;
915 else if (!(annotate_browser__jump(browser) ||
916 annotate_browser__callq(browser, evsel, hbt))) {
917 show_sup_ins:
918 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
919 }
920 continue;
921 case 't':
922 if (annotate_browser__opts.show_total_period) {
923 annotate_browser__opts.show_total_period = false;
924 annotate_browser__opts.show_nr_samples = true;
925 } else if (annotate_browser__opts.show_nr_samples)
926 annotate_browser__opts.show_nr_samples = false;
927 else
928 annotate_browser__opts.show_total_period = true;
929 annotate_browser__update_addr_width(browser);
930 continue;
931 case K_LEFT:
932 case K_ESC:
933 case 'q':
934 case CTRL('c'):
935 goto out;
936 default:
937 continue;
938 }
939
940 if (nd != NULL)
941 annotate_browser__set_rb_top(browser, nd);
942 }
943 out:
944 ui_browser__hide(&browser->b);
945 return key;
946 }
947
map_symbol__tui_annotate(struct map_symbol * ms,struct perf_evsel * evsel,struct hist_browser_timer * hbt)948 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
949 struct hist_browser_timer *hbt)
950 {
951 /* Set default value for show_total_period and show_nr_samples */
952 annotate_browser__opts.show_total_period =
953 symbol_conf.show_total_period;
954 annotate_browser__opts.show_nr_samples =
955 symbol_conf.show_nr_samples;
956
957 return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
958 }
959
hist_entry__tui_annotate(struct hist_entry * he,struct perf_evsel * evsel,struct hist_browser_timer * hbt)960 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
961 struct hist_browser_timer *hbt)
962 {
963 /* reset abort key so that it can get Ctrl-C as a key */
964 SLang_reset_tty();
965 SLang_init_tty(0, 0, 0);
966
967 return map_symbol__tui_annotate(&he->ms, evsel, hbt);
968 }
969
970
count_insn(struct annotate_browser * browser,u64 start,u64 end)971 static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
972 {
973 unsigned n_insn = 0;
974 u64 offset;
975
976 for (offset = start; offset <= end; offset++) {
977 if (browser->offsets[offset])
978 n_insn++;
979 }
980 return n_insn;
981 }
982
count_and_fill(struct annotate_browser * browser,u64 start,u64 end,struct cyc_hist * ch)983 static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
984 struct cyc_hist *ch)
985 {
986 unsigned n_insn;
987 u64 offset;
988
989 n_insn = count_insn(browser, start, end);
990 if (n_insn && ch->num && ch->cycles) {
991 float ipc = n_insn / ((double)ch->cycles / (double)ch->num);
992
993 /* Hide data when there are too many overlaps. */
994 if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
995 return;
996
997 for (offset = start; offset <= end; offset++) {
998 struct disasm_line *dl = browser->offsets[offset];
999
1000 if (dl)
1001 dl->ipc = ipc;
1002 }
1003 }
1004 }
1005
1006 /*
1007 * This should probably be in util/annotate.c to share with the tty
1008 * annotate, but right now we need the per byte offsets arrays,
1009 * which are only here.
1010 */
annotate__compute_ipc(struct annotate_browser * browser,size_t size,struct symbol * sym)1011 static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
1012 struct symbol *sym)
1013 {
1014 u64 offset;
1015 struct annotation *notes = symbol__annotation(sym);
1016
1017 if (!notes->src || !notes->src->cycles_hist)
1018 return;
1019
1020 pthread_mutex_lock(¬es->lock);
1021 for (offset = 0; offset < size; ++offset) {
1022 struct cyc_hist *ch;
1023
1024 ch = ¬es->src->cycles_hist[offset];
1025 if (ch && ch->cycles) {
1026 struct disasm_line *dl;
1027
1028 if (ch->have_start)
1029 count_and_fill(browser, ch->start, offset, ch);
1030 dl = browser->offsets[offset];
1031 if (dl && ch->num_aggr)
1032 dl->cycles = ch->cycles_aggr / ch->num_aggr;
1033 browser->have_cycles = true;
1034 }
1035 }
1036 pthread_mutex_unlock(¬es->lock);
1037 }
1038
annotate_browser__mark_jump_targets(struct annotate_browser * browser,size_t size)1039 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
1040 size_t size)
1041 {
1042 u64 offset;
1043 struct map_symbol *ms = browser->b.priv;
1044 struct symbol *sym = ms->sym;
1045
1046 /* PLT symbols contain external offsets */
1047 if (strstr(sym->name, "@plt"))
1048 return;
1049
1050 for (offset = 0; offset < size; ++offset) {
1051 struct disasm_line *dl = browser->offsets[offset], *dlt;
1052 struct browser_disasm_line *bdlt;
1053
1054 if (!disasm_line__is_valid_jump(dl, sym))
1055 continue;
1056
1057 dlt = browser->offsets[dl->ops.target.offset];
1058 /*
1059 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1060 * have to adjust to the previous offset?
1061 */
1062 if (dlt == NULL)
1063 continue;
1064
1065 bdlt = disasm_line__browser(dlt);
1066 if (++bdlt->jump_sources > browser->max_jump_sources)
1067 browser->max_jump_sources = bdlt->jump_sources;
1068
1069 ++browser->nr_jumps;
1070 }
1071 }
1072
width_jumps(int n)1073 static inline int width_jumps(int n)
1074 {
1075 if (n >= 100)
1076 return 5;
1077 if (n / 10)
1078 return 2;
1079 return 1;
1080 }
1081
symbol__tui_annotate(struct symbol * sym,struct map * map,struct perf_evsel * evsel,struct hist_browser_timer * hbt)1082 int symbol__tui_annotate(struct symbol *sym, struct map *map,
1083 struct perf_evsel *evsel,
1084 struct hist_browser_timer *hbt)
1085 {
1086 struct disasm_line *pos, *n;
1087 struct annotation *notes;
1088 size_t size;
1089 struct map_symbol ms = {
1090 .map = map,
1091 .sym = sym,
1092 };
1093 struct annotate_browser browser = {
1094 .b = {
1095 .refresh = annotate_browser__refresh,
1096 .seek = ui_browser__list_head_seek,
1097 .write = annotate_browser__write,
1098 .filter = disasm_line__filter,
1099 .priv = &ms,
1100 .use_navkeypressed = true,
1101 },
1102 };
1103 int ret = -1, err;
1104 int nr_pcnt = 1;
1105 size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1106
1107 if (sym == NULL)
1108 return -1;
1109
1110 size = symbol__size(sym);
1111
1112 if (map->dso->annotate_warned)
1113 return -1;
1114
1115 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
1116 if (browser.offsets == NULL) {
1117 ui__error("Not enough memory!");
1118 return -1;
1119 }
1120
1121 if (perf_evsel__is_group_event(evsel)) {
1122 nr_pcnt = evsel->nr_members;
1123 sizeof_bdl += sizeof(struct disasm_line_samples) *
1124 (nr_pcnt - 1);
1125 }
1126
1127 err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1128 sizeof_bdl, &browser.arch,
1129 perf_evsel__env_cpuid(evsel));
1130 if (err) {
1131 char msg[BUFSIZ];
1132 symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
1133 ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1134 goto out_free_offsets;
1135 }
1136
1137 ui_helpline__push("Press ESC to exit");
1138
1139 notes = symbol__annotation(sym);
1140 browser.start = map__rip_2objdump(map, sym->start);
1141
1142 list_for_each_entry(pos, ¬es->src->source, node) {
1143 struct browser_disasm_line *bpos;
1144 size_t line_len = strlen(pos->line);
1145
1146 if (browser.b.width < line_len)
1147 browser.b.width = line_len;
1148 bpos = disasm_line__browser(pos);
1149 bpos->idx = browser.nr_entries++;
1150 if (pos->offset != -1) {
1151 bpos->idx_asm = browser.nr_asm_entries++;
1152 /*
1153 * FIXME: short term bandaid to cope with assembly
1154 * routines that comes with labels in the same column
1155 * as the address in objdump, sigh.
1156 *
1157 * E.g. copy_user_generic_unrolled
1158 */
1159 if (pos->offset < (s64)size)
1160 browser.offsets[pos->offset] = pos;
1161 } else
1162 bpos->idx_asm = -1;
1163 }
1164
1165 annotate_browser__mark_jump_targets(&browser, size);
1166 annotate__compute_ipc(&browser, size, sym);
1167
1168 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1169 browser.max_addr_width = hex_width(sym->end);
1170 browser.jumps_width = width_jumps(browser.max_jump_sources);
1171 browser.nr_events = nr_pcnt;
1172 browser.b.nr_entries = browser.nr_entries;
1173 browser.b.entries = ¬es->src->source,
1174 browser.b.width += 18; /* Percentage */
1175
1176 if (annotate_browser__opts.hide_src_code)
1177 annotate_browser__init_asm_mode(&browser);
1178
1179 annotate_browser__update_addr_width(&browser);
1180
1181 ret = annotate_browser__run(&browser, evsel, hbt);
1182 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
1183 list_del(&pos->node);
1184 disasm_line__free(pos);
1185 }
1186
1187 out_free_offsets:
1188 free(browser.offsets);
1189 return ret;
1190 }
1191
1192 #define ANNOTATE_CFG(n) \
1193 { .name = #n, .value = &annotate_browser__opts.n, }
1194
1195 /*
1196 * Keep the entries sorted, they are bsearch'ed
1197 */
1198 static struct annotate_config {
1199 const char *name;
1200 bool *value;
1201 } annotate__configs[] = {
1202 ANNOTATE_CFG(hide_src_code),
1203 ANNOTATE_CFG(jump_arrows),
1204 ANNOTATE_CFG(show_linenr),
1205 ANNOTATE_CFG(show_nr_jumps),
1206 ANNOTATE_CFG(show_nr_samples),
1207 ANNOTATE_CFG(show_total_period),
1208 ANNOTATE_CFG(use_offset),
1209 };
1210
1211 #undef ANNOTATE_CFG
1212
annotate_config__cmp(const void * name,const void * cfgp)1213 static int annotate_config__cmp(const void *name, const void *cfgp)
1214 {
1215 const struct annotate_config *cfg = cfgp;
1216
1217 return strcmp(name, cfg->name);
1218 }
1219
annotate__config(const char * var,const char * value,void * data __maybe_unused)1220 static int annotate__config(const char *var, const char *value,
1221 void *data __maybe_unused)
1222 {
1223 struct annotate_config *cfg;
1224 const char *name;
1225
1226 if (!strstarts(var, "annotate."))
1227 return 0;
1228
1229 name = var + 9;
1230 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1231 sizeof(struct annotate_config), annotate_config__cmp);
1232
1233 if (cfg == NULL)
1234 ui__warning("%s variable unknown, ignoring...", var);
1235 else
1236 *cfg->value = perf_config_bool(name, value);
1237 return 0;
1238 }
1239
annotate_browser__init(void)1240 void annotate_browser__init(void)
1241 {
1242 perf_config(annotate__config, NULL);
1243 }
1244