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