1 // SPDX-License-Identifier: GPL-2.0
2 #include "../../util/util.h"
3 #include "../browser.h"
4 #include "../helpline.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"
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <sys/ttydefaults.h>
20 struct disasm_line_samples
{
22 struct sym_hist_entry he
;
26 #define CYCLES_WIDTH 6
28 struct browser_disasm_line
{
29 struct rb_node rb_node
;
34 * actual length of this array is saved on the nr_events field
35 * of the struct annotate_browser
37 struct disasm_line_samples samples
[1];
40 static struct annotate_browser_opt
{
48 } annotate_browser__opts
= {
55 struct annotate_browser
{
57 struct rb_root entries
;
58 struct rb_node
*curr_hot
;
59 struct disasm_line
*selection
;
60 struct disasm_line
**offsets
;
68 bool searching_backwards
;
78 static inline struct browser_disasm_line
*disasm_line__browser(struct disasm_line
*dl
)
80 return (struct browser_disasm_line
*)(dl
+ 1);
83 static bool disasm_line__filter(struct ui_browser
*browser __maybe_unused
,
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;
94 static int annotate_browser__jumps_percent_color(struct annotate_browser
*browser
,
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
;
102 return HE_COLORSET_MEDIUM
;
103 return HE_COLORSET_NORMAL
;
106 static int annotate_browser__set_jumps_percent_color(struct annotate_browser
*browser
,
107 int nr
, bool current
)
109 int color
= annotate_browser__jumps_percent_color(browser
, nr
, current
);
110 return ui_browser__set_color(&browser
->b
, color
);
113 static int annotate_browser__pcnt_width(struct annotate_browser
*ab
)
115 return (annotate_browser__opts
.show_total_period
? 12 : 7) * ab
->nr_events
;
118 static int annotate_browser__cycles_width(struct annotate_browser
*ab
)
120 return ab
->have_cycles
? IPC_WIDTH
+ CYCLES_WIDTH
: 0;
123 static void annotate_browser__write(struct ui_browser
*browser
, void *entry
, int row
)
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;
137 bool show_title
= false;
139 for (i
= 0; i
< ab
->nr_events
; i
++) {
140 if (bdl
->samples
[i
].percent
> percent_max
)
141 percent_max
= bdl
->samples
[i
].percent
;
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)
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
,
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
);
164 ui_browser__printf(browser
, "%6.2f ",
165 bdl
->samples
[i
].percent
);
169 ui_browser__set_percent_color(browser
, 0, current_entry
);
172 ui_browser__write_nstring(browser
, " ", pcnt_width
);
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");
179 if (ab
->have_cycles
) {
181 ui_browser__printf(browser
, "%*.2f ", IPC_WIDTH
- 1, dl
->ipc
);
182 else if (!show_title
)
183 ui_browser__write_nstring(browser
, " ", IPC_WIDTH
);
185 ui_browser__printf(browser
, "%*s ", IPC_WIDTH
- 1, "IPC");
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
);
193 ui_browser__printf(browser
, "%*s ", CYCLES_WIDTH
- 1, "Cycle");
196 SLsmg_write_char(' ');
198 /* The scroll bar isn't being used */
199 if (!browser
->navkeypressed
)
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
);
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);
214 u64 addr
= dl
->offset
;
217 if (!annotate_browser__opts
.use_offset
)
220 if (!annotate_browser__opts
.use_offset
) {
221 printed
= scnprintf(bf
, sizeof(bf
), "%" PRIx64
": ", addr
);
223 if (bdl
->jump_sources
) {
224 if (annotate_browser__opts
.show_nr_jumps
) {
226 printed
= scnprintf(bf
, sizeof(bf
), "%*d ",
229 prev
= annotate_browser__set_jumps_percent_color(ab
, bdl
->jump_sources
,
231 ui_browser__write_nstring(browser
, bf
, printed
);
232 ui_browser__set_color(browser
, prev
);
235 printed
= scnprintf(bf
, sizeof(bf
), "%*" PRIx64
": ",
236 ab
->target_width
, addr
);
238 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
239 ab
->addr_width
, " ");
244 color
= ui_browser__set_color(browser
, HE_COLORSET_ADDR
);
245 ui_browser__write_nstring(browser
, bf
, printed
);
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
;
252 ui_browser__write_graph(browser
, fwd
? SLSMG_DARROW_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(' ');
262 ui_browser__write_nstring(browser
, " ", 2);
265 ui_browser__write_nstring(browser
, " ", 2);
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
);
276 static bool disasm_line__is_valid_jump(struct disasm_line
*dl
, struct symbol
*sym
)
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
))
287 static bool is_fused(struct annotate_browser
*ab
, struct disasm_line
*cursor
)
289 struct disasm_line
*pos
= list_prev_entry(cursor
, node
);
295 if (ins__is_lock(&pos
->ins
))
296 name
= pos
->ops
.locked
.ins
.name
;
298 name
= pos
->ins
.name
;
300 if (!name
|| !cursor
->ins
.name
)
303 return ins__is_fused(ab
->arch
, name
, cursor
->ins
.name
);
306 static void annotate_browser__draw_current_jump(struct ui_browser
*browser
)
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
);
316 /* PLT symbols contain external offsets */
317 if (strstr(sym
->name
, "@plt"))
320 if (!disasm_line__is_valid_jump(cursor
, sym
))
323 target
= ab
->offsets
[cursor
->ops
.target
.offset
];
327 bcursor
= disasm_line__browser(cursor
);
328 btarget
= disasm_line__browser(target
);
330 if (annotate_browser__opts
.hide_src_code
) {
331 from
= bcursor
->idx_asm
;
332 to
= btarget
->idx_asm
;
334 from
= (u64
)bcursor
->idx
;
335 to
= (u64
)btarget
->idx
;
338 ui_browser__set_color(browser
, HE_COLORSET_JUMP_ARROWS
);
339 __ui_browser__line_arrow(browser
, pcnt_width
+ 2 + ab
->addr_width
,
342 if (is_fused(ab
, cursor
)) {
343 ui_browser__mark_fused(browser
,
344 pcnt_width
+ 3 + ab
->addr_width
,
346 to
> from
? true : false);
350 static unsigned int annotate_browser__refresh(struct ui_browser
*browser
)
352 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
353 int ret
= ui_browser__list_head_refresh(browser
);
354 int pcnt_width
= annotate_browser__pcnt_width(ab
);
356 if (annotate_browser__opts
.jump_arrows
)
357 annotate_browser__draw_current_jump(browser
);
359 ui_browser__set_color(browser
, HE_COLORSET_NORMAL
);
360 __ui_browser__vline(browser
, pcnt_width
, 0, browser
->height
- 1);
364 static int disasm__cmp(struct browser_disasm_line
*a
,
365 struct browser_disasm_line
*b
, int nr_pcnt
)
369 for (i
= 0; i
< nr_pcnt
; i
++) {
370 if (a
->samples
[i
].percent
== b
->samples
[i
].percent
)
372 return a
->samples
[i
].percent
< b
->samples
[i
].percent
;
377 static void disasm_rb_tree__insert(struct rb_root
*root
, struct browser_disasm_line
*bdl
,
380 struct rb_node
**p
= &root
->rb_node
;
381 struct rb_node
*parent
= NULL
;
382 struct browser_disasm_line
*l
;
386 l
= rb_entry(parent
, struct browser_disasm_line
, rb_node
);
388 if (disasm__cmp(bdl
, l
, nr_events
))
393 rb_link_node(&bdl
->rb_node
, parent
, p
);
394 rb_insert_color(&bdl
->rb_node
, root
);
397 static void annotate_browser__set_top(struct annotate_browser
*browser
,
398 struct disasm_line
*pos
, u32 idx
)
402 ui_browser__refresh_dimensions(&browser
->b
);
403 back
= browser
->b
.height
/ 2;
404 browser
->b
.top_idx
= browser
->b
.index
= idx
;
406 while (browser
->b
.top_idx
!= 0 && back
!= 0) {
407 pos
= list_entry(pos
->node
.prev
, struct disasm_line
, node
);
409 if (disasm_line__filter(&browser
->b
, &pos
->node
))
412 --browser
->b
.top_idx
;
416 browser
->b
.top
= pos
;
417 browser
->b
.navkeypressed
= true;
420 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
423 struct browser_disasm_line
*bpos
;
424 struct disasm_line
*pos
;
427 bpos
= rb_entry(nd
, struct browser_disasm_line
, rb_node
);
428 pos
= ((struct disasm_line
*)bpos
) - 1;
430 if (annotate_browser__opts
.hide_src_code
)
432 annotate_browser__set_top(browser
, pos
, idx
);
433 browser
->curr_hot
= nd
;
436 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
437 struct perf_evsel
*evsel
)
439 struct map_symbol
*ms
= browser
->b
.priv
;
440 struct symbol
*sym
= ms
->sym
;
441 struct annotation
*notes
= symbol__annotation(sym
);
442 struct disasm_line
*pos
, *next
;
443 s64 len
= symbol__size(sym
);
445 browser
->entries
= RB_ROOT
;
447 pthread_mutex_lock(¬es
->lock
);
449 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
450 struct browser_disasm_line
*bpos
= disasm_line__browser(pos
);
451 const char *path
= NULL
;
452 double max_percent
= 0.0;
455 if (pos
->offset
== -1) {
456 RB_CLEAR_NODE(&bpos
->rb_node
);
460 next
= disasm__get_next_ip_line(¬es
->src
->source
, pos
);
462 for (i
= 0; i
< browser
->nr_events
; i
++) {
463 struct sym_hist_entry sample
;
465 bpos
->samples
[i
].percent
= disasm__calc_percent(notes
,
468 next
? next
->offset
: len
,
470 bpos
->samples
[i
].he
= sample
;
472 if (max_percent
< bpos
->samples
[i
].percent
)
473 max_percent
= bpos
->samples
[i
].percent
;
476 if (max_percent
< 0.01 && pos
->ipc
== 0) {
477 RB_CLEAR_NODE(&bpos
->rb_node
);
480 disasm_rb_tree__insert(&browser
->entries
, bpos
,
483 pthread_mutex_unlock(¬es
->lock
);
485 browser
->curr_hot
= rb_last(&browser
->entries
);
488 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
490 struct disasm_line
*dl
;
491 struct browser_disasm_line
*bdl
;
492 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
494 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
495 dl
= list_entry(browser
->b
.top
, struct disasm_line
, node
);
496 bdl
= disasm_line__browser(dl
);
498 if (annotate_browser__opts
.hide_src_code
) {
499 if (bdl
->idx_asm
< offset
)
502 browser
->b
.nr_entries
= browser
->nr_entries
;
503 annotate_browser__opts
.hide_src_code
= false;
504 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
505 browser
->b
.top_idx
= bdl
->idx
- offset
;
506 browser
->b
.index
= bdl
->idx
;
508 if (bdl
->idx_asm
< 0) {
509 ui_helpline__puts("Only available for assembly lines.");
510 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
514 if (bdl
->idx_asm
< offset
)
515 offset
= bdl
->idx_asm
;
517 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
518 annotate_browser__opts
.hide_src_code
= true;
519 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
520 browser
->b
.top_idx
= bdl
->idx_asm
- offset
;
521 browser
->b
.index
= bdl
->idx_asm
;
527 static void annotate_browser__init_asm_mode(struct annotate_browser
*browser
)
529 ui_browser__reset_index(&browser
->b
);
530 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
533 #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
535 static int sym_title(struct symbol
*sym
, struct map
*map
, char *title
,
538 return snprintf(title
, sz
, "%s %s", sym
->name
, map
->dso
->long_name
);
541 static bool annotate_browser__callq(struct annotate_browser
*browser
,
542 struct perf_evsel
*evsel
,
543 struct hist_browser_timer
*hbt
)
545 struct map_symbol
*ms
= browser
->b
.priv
;
546 struct disasm_line
*dl
= browser
->selection
;
547 struct annotation
*notes
;
548 struct addr_map_symbol target
= {
550 .addr
= map__objdump_2mem(ms
->map
, dl
->ops
.target
.addr
),
552 char title
[SYM_TITLE_MAX_SIZE
];
554 if (!ins__is_call(&dl
->ins
))
557 if (map_groups__find_ams(&target
) ||
558 map__rip_2objdump(target
.map
, target
.map
->map_ip(target
.map
,
560 dl
->ops
.target
.addr
) {
561 ui_helpline__puts("The called function was not found.");
565 notes
= symbol__annotation(target
.sym
);
566 pthread_mutex_lock(¬es
->lock
);
568 if (notes
->src
== NULL
&& symbol__alloc_hist(target
.sym
) < 0) {
569 pthread_mutex_unlock(¬es
->lock
);
570 ui__warning("Not enough memory for annotating '%s' symbol!\n",
575 pthread_mutex_unlock(¬es
->lock
);
576 symbol__tui_annotate(target
.sym
, target
.map
, evsel
, hbt
);
577 sym_title(ms
->sym
, ms
->map
, title
, sizeof(title
));
578 ui_browser__show_title(&browser
->b
, title
);
583 struct disasm_line
*annotate_browser__find_offset(struct annotate_browser
*browser
,
584 s64 offset
, s64
*idx
)
586 struct map_symbol
*ms
= browser
->b
.priv
;
587 struct symbol
*sym
= ms
->sym
;
588 struct annotation
*notes
= symbol__annotation(sym
);
589 struct disasm_line
*pos
;
592 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
593 if (pos
->offset
== offset
)
595 if (!disasm_line__filter(&browser
->b
, &pos
->node
))
602 static bool annotate_browser__jump(struct annotate_browser
*browser
)
604 struct disasm_line
*dl
= browser
->selection
;
608 if (!ins__is_jump(&dl
->ins
))
611 offset
= dl
->ops
.target
.offset
;
612 dl
= annotate_browser__find_offset(browser
, offset
, &idx
);
614 ui_helpline__printf("Invalid jump offset: %" PRIx64
, offset
);
618 annotate_browser__set_top(browser
, dl
, idx
);
624 struct disasm_line
*annotate_browser__find_string(struct annotate_browser
*browser
,
627 struct map_symbol
*ms
= browser
->b
.priv
;
628 struct symbol
*sym
= ms
->sym
;
629 struct annotation
*notes
= symbol__annotation(sym
);
630 struct disasm_line
*pos
= browser
->selection
;
632 *idx
= browser
->b
.index
;
633 list_for_each_entry_continue(pos
, ¬es
->src
->source
, node
) {
634 if (disasm_line__filter(&browser
->b
, &pos
->node
))
639 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
646 static bool __annotate_browser__search(struct annotate_browser
*browser
)
648 struct disasm_line
*dl
;
651 dl
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
653 ui_helpline__puts("String not found!");
657 annotate_browser__set_top(browser
, dl
, idx
);
658 browser
->searching_backwards
= false;
663 struct disasm_line
*annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
666 struct map_symbol
*ms
= browser
->b
.priv
;
667 struct symbol
*sym
= ms
->sym
;
668 struct annotation
*notes
= symbol__annotation(sym
);
669 struct disasm_line
*pos
= browser
->selection
;
671 *idx
= browser
->b
.index
;
672 list_for_each_entry_continue_reverse(pos
, ¬es
->src
->source
, node
) {
673 if (disasm_line__filter(&browser
->b
, &pos
->node
))
678 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
685 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
687 struct disasm_line
*dl
;
690 dl
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
692 ui_helpline__puts("String not found!");
696 annotate_browser__set_top(browser
, dl
, idx
);
697 browser
->searching_backwards
= true;
701 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
704 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
705 "ENTER: OK, ESC: Cancel",
706 delay_secs
* 2) != K_ENTER
||
707 !*browser
->search_bf
)
713 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
715 if (annotate_browser__search_window(browser
, delay_secs
))
716 return __annotate_browser__search(browser
);
721 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
724 if (!*browser
->search_bf
)
725 return annotate_browser__search(browser
, delay_secs
);
727 return __annotate_browser__search(browser
);
730 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
733 if (annotate_browser__search_window(browser
, delay_secs
))
734 return __annotate_browser__search_reverse(browser
);
740 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
743 if (!*browser
->search_bf
)
744 return annotate_browser__search_reverse(browser
, delay_secs
);
746 return __annotate_browser__search_reverse(browser
);
749 static void annotate_browser__update_addr_width(struct annotate_browser
*browser
)
751 if (annotate_browser__opts
.use_offset
)
752 browser
->target_width
= browser
->min_addr_width
;
754 browser
->target_width
= browser
->max_addr_width
;
756 browser
->addr_width
= browser
->target_width
;
758 if (annotate_browser__opts
.show_nr_jumps
)
759 browser
->addr_width
+= browser
->jumps_width
+ 1;
762 static int annotate_browser__run(struct annotate_browser
*browser
,
763 struct perf_evsel
*evsel
,
764 struct hist_browser_timer
*hbt
)
766 struct rb_node
*nd
= NULL
;
767 struct map_symbol
*ms
= browser
->b
.priv
;
768 struct symbol
*sym
= ms
->sym
;
769 const char *help
= "Press 'h' for help on key bindings";
770 int delay_secs
= hbt
? hbt
->refresh
: 0;
772 char title
[SYM_TITLE_MAX_SIZE
];
774 sym_title(sym
, ms
->map
, title
, sizeof(title
));
775 if (ui_browser__show(&browser
->b
, title
, help
) < 0)
778 annotate_browser__calc_percent(browser
, evsel
);
780 if (browser
->curr_hot
) {
781 annotate_browser__set_rb_top(browser
, browser
->curr_hot
);
782 browser
->b
.navkeypressed
= false;
785 nd
= browser
->curr_hot
;
788 key
= ui_browser__run(&browser
->b
, delay_secs
);
790 if (delay_secs
!= 0) {
791 annotate_browser__calc_percent(browser
, evsel
);
793 * Current line focus got out of the list of most active
794 * lines, NULL it so that if TAB|UNTAB is pressed, we
795 * move to curr_hot (current hottest line).
797 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
804 hbt
->timer(hbt
->arg
);
807 symbol__annotate_decay_histogram(sym
, evsel
->idx
);
813 nd
= rb_last(&browser
->entries
);
815 nd
= browser
->curr_hot
;
821 nd
= rb_first(&browser
->entries
);
823 nd
= browser
->curr_hot
;
827 ui_browser__help_window(&browser
->b
,
829 "PGDN/SPACE Navigate\n"
830 "q/ESC/CTRL+C Exit\n\n"
831 "ENTER Go to target\n"
833 "H Go to hottest instruction\n"
834 "TAB/shift+TAB Cycle thru hottest instructions\n"
835 "j Toggle showing jump to target arrows\n"
836 "J Toggle showing number of jump sources on targets\n"
837 "n Search next string\n"
838 "o Toggle disassembler output/simplified view\n"
839 "s Toggle source code view\n"
840 "t Circulate percent, total period, samples view\n"
842 "k Toggle line numbers\n"
843 "r Run available scripts\n"
844 "? Search string backwards\n");
852 annotate_browser__opts
.show_linenr
=
853 !annotate_browser__opts
.show_linenr
;
856 nd
= browser
->curr_hot
;
859 if (annotate_browser__toggle_source(browser
))
860 ui_helpline__puts(help
);
863 annotate_browser__opts
.use_offset
= !annotate_browser__opts
.use_offset
;
864 annotate_browser__update_addr_width(browser
);
867 annotate_browser__opts
.jump_arrows
= !annotate_browser__opts
.jump_arrows
;
870 annotate_browser__opts
.show_nr_jumps
= !annotate_browser__opts
.show_nr_jumps
;
871 annotate_browser__update_addr_width(browser
);
874 if (annotate_browser__search(browser
, delay_secs
)) {
876 ui_helpline__puts(help
);
880 if (browser
->searching_backwards
?
881 annotate_browser__continue_search_reverse(browser
, delay_secs
) :
882 annotate_browser__continue_search(browser
, delay_secs
))
886 if (annotate_browser__search_reverse(browser
, delay_secs
))
892 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
893 seq
++, browser
->b
.nr_entries
,
897 browser
->nr_asm_entries
);
902 if (browser
->selection
== NULL
)
903 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
904 else if (browser
->selection
->offset
== -1)
905 ui_helpline__puts("Actions are only available for assembly lines.");
906 else if (!browser
->selection
->ins
.ops
)
908 else if (ins__is_ret(&browser
->selection
->ins
))
910 else if (!(annotate_browser__jump(browser
) ||
911 annotate_browser__callq(browser
, evsel
, hbt
))) {
913 ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
917 if (annotate_browser__opts
.show_total_period
) {
918 annotate_browser__opts
.show_total_period
= false;
919 annotate_browser__opts
.show_nr_samples
= true;
920 } else if (annotate_browser__opts
.show_nr_samples
)
921 annotate_browser__opts
.show_nr_samples
= false;
923 annotate_browser__opts
.show_total_period
= true;
924 annotate_browser__update_addr_width(browser
);
936 annotate_browser__set_rb_top(browser
, nd
);
939 ui_browser__hide(&browser
->b
);
943 int map_symbol__tui_annotate(struct map_symbol
*ms
, struct perf_evsel
*evsel
,
944 struct hist_browser_timer
*hbt
)
946 /* Set default value for show_total_period and show_nr_samples */
947 annotate_browser__opts
.show_total_period
=
948 symbol_conf
.show_total_period
;
949 annotate_browser__opts
.show_nr_samples
=
950 symbol_conf
.show_nr_samples
;
952 return symbol__tui_annotate(ms
->sym
, ms
->map
, evsel
, hbt
);
955 int hist_entry__tui_annotate(struct hist_entry
*he
, struct perf_evsel
*evsel
,
956 struct hist_browser_timer
*hbt
)
958 /* reset abort key so that it can get Ctrl-C as a key */
960 SLang_init_tty(0, 0, 0);
962 return map_symbol__tui_annotate(&he
->ms
, evsel
, hbt
);
966 static unsigned count_insn(struct annotate_browser
*browser
, u64 start
, u64 end
)
971 for (offset
= start
; offset
<= end
; offset
++) {
972 if (browser
->offsets
[offset
])
978 static void count_and_fill(struct annotate_browser
*browser
, u64 start
, u64 end
,
984 n_insn
= count_insn(browser
, start
, end
);
985 if (n_insn
&& ch
->num
&& ch
->cycles
) {
986 float ipc
= n_insn
/ ((double)ch
->cycles
/ (double)ch
->num
);
988 /* Hide data when there are too many overlaps. */
989 if (ch
->reset
>= 0x7fff || ch
->reset
>= ch
->num
/ 2)
992 for (offset
= start
; offset
<= end
; offset
++) {
993 struct disasm_line
*dl
= browser
->offsets
[offset
];
1002 * This should probably be in util/annotate.c to share with the tty
1003 * annotate, but right now we need the per byte offsets arrays,
1004 * which are only here.
1006 static void annotate__compute_ipc(struct annotate_browser
*browser
, size_t size
,
1010 struct annotation
*notes
= symbol__annotation(sym
);
1012 if (!notes
->src
|| !notes
->src
->cycles_hist
)
1015 pthread_mutex_lock(¬es
->lock
);
1016 for (offset
= 0; offset
< size
; ++offset
) {
1017 struct cyc_hist
*ch
;
1019 ch
= ¬es
->src
->cycles_hist
[offset
];
1020 if (ch
&& ch
->cycles
) {
1021 struct disasm_line
*dl
;
1024 count_and_fill(browser
, ch
->start
, offset
, ch
);
1025 dl
= browser
->offsets
[offset
];
1026 if (dl
&& ch
->num_aggr
)
1027 dl
->cycles
= ch
->cycles_aggr
/ ch
->num_aggr
;
1028 browser
->have_cycles
= true;
1031 pthread_mutex_unlock(¬es
->lock
);
1034 static void annotate_browser__mark_jump_targets(struct annotate_browser
*browser
,
1038 struct map_symbol
*ms
= browser
->b
.priv
;
1039 struct symbol
*sym
= ms
->sym
;
1041 /* PLT symbols contain external offsets */
1042 if (strstr(sym
->name
, "@plt"))
1045 for (offset
= 0; offset
< size
; ++offset
) {
1046 struct disasm_line
*dl
= browser
->offsets
[offset
], *dlt
;
1047 struct browser_disasm_line
*bdlt
;
1049 if (!disasm_line__is_valid_jump(dl
, sym
))
1052 dlt
= browser
->offsets
[dl
->ops
.target
.offset
];
1054 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
1055 * have to adjust to the previous offset?
1060 bdlt
= disasm_line__browser(dlt
);
1061 if (++bdlt
->jump_sources
> browser
->max_jump_sources
)
1062 browser
->max_jump_sources
= bdlt
->jump_sources
;
1064 ++browser
->nr_jumps
;
1068 static inline int width_jumps(int n
)
1077 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
,
1078 struct perf_evsel
*evsel
,
1079 struct hist_browser_timer
*hbt
)
1081 struct disasm_line
*pos
, *n
;
1082 struct annotation
*notes
;
1084 struct map_symbol ms
= {
1088 struct annotate_browser browser
= {
1090 .refresh
= annotate_browser__refresh
,
1091 .seek
= ui_browser__list_head_seek
,
1092 .write
= annotate_browser__write
,
1093 .filter
= disasm_line__filter
,
1095 .use_navkeypressed
= true,
1100 size_t sizeof_bdl
= sizeof(struct browser_disasm_line
);
1105 size
= symbol__size(sym
);
1107 if (map
->dso
->annotate_warned
)
1110 browser
.offsets
= zalloc(size
* sizeof(struct disasm_line
*));
1111 if (browser
.offsets
== NULL
) {
1112 ui__error("Not enough memory!");
1116 if (perf_evsel__is_group_event(evsel
)) {
1117 nr_pcnt
= evsel
->nr_members
;
1118 sizeof_bdl
+= sizeof(struct disasm_line_samples
) *
1122 err
= symbol__disassemble(sym
, map
, perf_evsel__env_arch(evsel
),
1123 sizeof_bdl
, &browser
.arch
,
1124 perf_evsel__env_cpuid(evsel
));
1127 symbol__strerror_disassemble(sym
, map
, err
, msg
, sizeof(msg
));
1128 ui__error("Couldn't annotate %s:\n%s", sym
->name
, msg
);
1129 goto out_free_offsets
;
1132 ui_helpline__push("Press ESC to exit");
1134 notes
= symbol__annotation(sym
);
1135 browser
.start
= map__rip_2objdump(map
, sym
->start
);
1137 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
1138 struct browser_disasm_line
*bpos
;
1139 size_t line_len
= strlen(pos
->line
);
1141 if (browser
.b
.width
< line_len
)
1142 browser
.b
.width
= line_len
;
1143 bpos
= disasm_line__browser(pos
);
1144 bpos
->idx
= browser
.nr_entries
++;
1145 if (pos
->offset
!= -1) {
1146 bpos
->idx_asm
= browser
.nr_asm_entries
++;
1148 * FIXME: short term bandaid to cope with assembly
1149 * routines that comes with labels in the same column
1150 * as the address in objdump, sigh.
1152 * E.g. copy_user_generic_unrolled
1154 if (pos
->offset
< (s64
)size
)
1155 browser
.offsets
[pos
->offset
] = pos
;
1160 annotate_browser__mark_jump_targets(&browser
, size
);
1161 annotate__compute_ipc(&browser
, size
, sym
);
1163 browser
.addr_width
= browser
.target_width
= browser
.min_addr_width
= hex_width(size
);
1164 browser
.max_addr_width
= hex_width(sym
->end
);
1165 browser
.jumps_width
= width_jumps(browser
.max_jump_sources
);
1166 browser
.nr_events
= nr_pcnt
;
1167 browser
.b
.nr_entries
= browser
.nr_entries
;
1168 browser
.b
.entries
= ¬es
->src
->source
,
1169 browser
.b
.width
+= 18; /* Percentage */
1171 if (annotate_browser__opts
.hide_src_code
)
1172 annotate_browser__init_asm_mode(&browser
);
1174 annotate_browser__update_addr_width(&browser
);
1176 ret
= annotate_browser__run(&browser
, evsel
, hbt
);
1177 list_for_each_entry_safe(pos
, n
, ¬es
->src
->source
, node
) {
1178 list_del(&pos
->node
);
1179 disasm_line__free(pos
);
1183 free(browser
.offsets
);
1187 #define ANNOTATE_CFG(n) \
1188 { .name = #n, .value = &annotate_browser__opts.n, }
1191 * Keep the entries sorted, they are bsearch'ed
1193 static struct annotate_config
{
1196 } annotate__configs
[] = {
1197 ANNOTATE_CFG(hide_src_code
),
1198 ANNOTATE_CFG(jump_arrows
),
1199 ANNOTATE_CFG(show_linenr
),
1200 ANNOTATE_CFG(show_nr_jumps
),
1201 ANNOTATE_CFG(show_nr_samples
),
1202 ANNOTATE_CFG(show_total_period
),
1203 ANNOTATE_CFG(use_offset
),
1208 static int annotate_config__cmp(const void *name
, const void *cfgp
)
1210 const struct annotate_config
*cfg
= cfgp
;
1212 return strcmp(name
, cfg
->name
);
1215 static int annotate__config(const char *var
, const char *value
,
1216 void *data __maybe_unused
)
1218 struct annotate_config
*cfg
;
1221 if (!strstarts(var
, "annotate."))
1225 cfg
= bsearch(name
, annotate__configs
, ARRAY_SIZE(annotate__configs
),
1226 sizeof(struct annotate_config
), annotate_config__cmp
);
1229 ui__warning("%s variable unknown, ignoring...", var
);
1231 *cfg
->value
= perf_config_bool(name
, value
);
1235 void annotate_browser__init(void)
1237 perf_config(annotate__config
, NULL
);