1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../libslang.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
14 struct browser_disasm_line
{
15 struct rb_node rb_node
;
22 struct annotate_browser
{
24 struct rb_root entries
;
25 struct rb_node
*curr_hot
;
26 struct disasm_line
*selection
;
27 struct disasm_line
**offsets
;
33 bool searching_backwards
;
38 static inline struct browser_disasm_line
*disasm_line__browser(struct disasm_line
*dl
)
40 return (struct browser_disasm_line
*)(dl
+ 1);
43 static bool disasm_line__filter(struct ui_browser
*browser
, void *entry
)
45 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
47 if (ab
->hide_src_code
) {
48 struct disasm_line
*dl
= list_entry(entry
, struct disasm_line
, node
);
49 return dl
->offset
== -1;
55 static void annotate_browser__write(struct ui_browser
*self
, void *entry
, int row
)
57 struct annotate_browser
*ab
= container_of(self
, struct annotate_browser
, b
);
58 struct disasm_line
*dl
= list_entry(entry
, struct disasm_line
, node
);
59 struct browser_disasm_line
*bdl
= disasm_line__browser(dl
);
60 bool current_entry
= ui_browser__is_current_entry(self
, row
);
61 bool change_color
= (!ab
->hide_src_code
&&
62 (!current_entry
|| (self
->use_navkeypressed
&&
63 !self
->navkeypressed
)));
64 int width
= self
->width
;
66 if (dl
->offset
!= -1) {
67 ui_browser__set_percent_color(self
, bdl
->percent
, current_entry
);
68 slsmg_printf(" %7.2f ", bdl
->percent
);
70 ui_browser__set_percent_color(self
, 0, current_entry
);
71 slsmg_write_nstring(" ", 9);
74 SLsmg_set_char_set(1);
75 SLsmg_write_char(SLSMG_VLINE_CHAR
);
76 SLsmg_set_char_set(0);
77 SLsmg_write_char(' ');
79 /* The scroll bar isn't being used */
80 if (!self
->navkeypressed
)
83 if (dl
->offset
!= -1 && change_color
)
84 ui_browser__set_color(self
, HE_COLORSET_CODE
);
87 slsmg_write_nstring(" ", width
- 10);
88 else if (dl
->offset
== -1)
89 slsmg_write_nstring(dl
->line
, width
- 10);
92 u64 addr
= dl
->offset
;
93 int printed
, color
= -1;
98 if (!ab
->use_offset
) {
99 printed
= scnprintf(bf
, sizeof(bf
), "%" PRIx64
":", addr
);
101 if (bdl
->jump_target
) {
102 printed
= scnprintf(bf
, sizeof(bf
), "%*" PRIx64
":",
103 ab
->offset_width
, addr
);
105 printed
= scnprintf(bf
, sizeof(bf
), "%*s ",
106 ab
->offset_width
, " ");
111 color
= ui_browser__set_color(self
, HE_COLORSET_ADDR
);
112 slsmg_write_nstring(bf
, printed
);
114 ui_browser__set_color(self
, color
);
115 if (dl
->ins
&& dl
->ins
->ops
->scnprintf
) {
116 if (ins__is_jump(dl
->ins
)) {
117 bool fwd
= dl
->ops
.target
> (u64
)dl
->offset
;
119 SLsmg_set_char_set(1);
120 SLsmg_write_char(fwd
? SLSMG_DARROW_CHAR
:
122 SLsmg_set_char_set(0);
123 SLsmg_write_char(' ');
125 slsmg_write_nstring(" ", 2);
128 dl
->ins
->ops
->scnprintf(dl
->ins
, bf
, sizeof(bf
), &dl
->ops
,
131 if (strcmp(dl
->name
, "retq")) {
132 slsmg_write_nstring(" ", 2);
134 SLsmg_set_char_set(1);
135 SLsmg_write_char(SLSMG_LARROW_CHAR
);
136 SLsmg_set_char_set(0);
137 SLsmg_write_char(' ');
140 scnprintf(bf
, sizeof(bf
), "%-6.6s %s", dl
->name
, dl
->ops
.raw
);
143 slsmg_write_nstring(bf
, width
- 12 - printed
);
150 static double disasm_line__calc_percent(struct disasm_line
*dl
, struct symbol
*sym
, int evidx
)
152 double percent
= 0.0;
154 if (dl
->offset
!= -1) {
155 int len
= sym
->end
- sym
->start
;
156 unsigned int hits
= 0;
157 struct annotation
*notes
= symbol__annotation(sym
);
158 struct source_line
*src_line
= notes
->src
->lines
;
159 struct sym_hist
*h
= annotation__histogram(notes
, evidx
);
160 s64 offset
= dl
->offset
;
161 struct disasm_line
*next
;
163 next
= disasm__get_next_ip_line(¬es
->src
->source
, dl
);
164 while (offset
< (s64
)len
&&
165 (next
== NULL
|| offset
< next
->offset
)) {
167 percent
+= src_line
[offset
].percent
;
169 hits
+= h
->addr
[offset
];
174 * If the percentage wasn't already calculated in
175 * symbol__get_source_line, do it now:
177 if (src_line
== NULL
&& h
->sum
)
178 percent
= 100.0 * hits
/ h
->sum
;
184 static void disasm_rb_tree__insert(struct rb_root
*root
, struct browser_disasm_line
*bdl
)
186 struct rb_node
**p
= &root
->rb_node
;
187 struct rb_node
*parent
= NULL
;
188 struct browser_disasm_line
*l
;
192 l
= rb_entry(parent
, struct browser_disasm_line
, rb_node
);
193 if (bdl
->percent
< l
->percent
)
198 rb_link_node(&bdl
->rb_node
, parent
, p
);
199 rb_insert_color(&bdl
->rb_node
, root
);
202 static void annotate_browser__set_top(struct annotate_browser
*self
,
203 struct disasm_line
*pos
, u32 idx
)
207 ui_browser__refresh_dimensions(&self
->b
);
208 back
= self
->b
.height
/ 2;
209 self
->b
.top_idx
= self
->b
.index
= idx
;
211 while (self
->b
.top_idx
!= 0 && back
!= 0) {
212 pos
= list_entry(pos
->node
.prev
, struct disasm_line
, node
);
214 if (disasm_line__filter(&self
->b
, &pos
->node
))
222 self
->b
.navkeypressed
= true;
225 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
228 struct browser_disasm_line
*bpos
;
229 struct disasm_line
*pos
;
231 bpos
= rb_entry(nd
, struct browser_disasm_line
, rb_node
);
232 pos
= ((struct disasm_line
*)bpos
) - 1;
233 annotate_browser__set_top(browser
, pos
, bpos
->idx
);
234 browser
->curr_hot
= nd
;
237 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
240 struct map_symbol
*ms
= browser
->b
.priv
;
241 struct symbol
*sym
= ms
->sym
;
242 struct annotation
*notes
= symbol__annotation(sym
);
243 struct disasm_line
*pos
;
245 browser
->entries
= RB_ROOT
;
247 pthread_mutex_lock(¬es
->lock
);
249 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
250 struct browser_disasm_line
*bpos
= disasm_line__browser(pos
);
251 bpos
->percent
= disasm_line__calc_percent(pos
, sym
, evidx
);
252 if (bpos
->percent
< 0.01) {
253 RB_CLEAR_NODE(&bpos
->rb_node
);
256 disasm_rb_tree__insert(&browser
->entries
, bpos
);
258 pthread_mutex_unlock(¬es
->lock
);
260 browser
->curr_hot
= rb_last(&browser
->entries
);
263 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
265 struct disasm_line
*dl
;
266 struct browser_disasm_line
*bdl
;
267 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
269 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
270 dl
= list_entry(browser
->b
.top
, struct disasm_line
, node
);
271 bdl
= disasm_line__browser(dl
);
273 if (browser
->hide_src_code
) {
274 if (bdl
->idx_asm
< offset
)
277 browser
->b
.nr_entries
= browser
->nr_entries
;
278 browser
->hide_src_code
= false;
279 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
280 browser
->b
.top_idx
= bdl
->idx
- offset
;
281 browser
->b
.index
= bdl
->idx
;
283 if (bdl
->idx_asm
< 0) {
284 ui_helpline__puts("Only available for assembly lines.");
285 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
289 if (bdl
->idx_asm
< offset
)
290 offset
= bdl
->idx_asm
;
292 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
293 browser
->hide_src_code
= true;
294 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
295 browser
->b
.top_idx
= bdl
->idx_asm
- offset
;
296 browser
->b
.index
= bdl
->idx_asm
;
302 static bool annotate_browser__callq(struct annotate_browser
*browser
,
303 int evidx
, void (*timer
)(void *arg
),
304 void *arg
, int delay_secs
)
306 struct map_symbol
*ms
= browser
->b
.priv
;
307 struct disasm_line
*dl
= browser
->selection
;
308 struct symbol
*sym
= ms
->sym
;
309 struct annotation
*notes
;
310 struct symbol
*target
;
313 if (!ins__is_call(dl
->ins
))
316 ip
= ms
->map
->map_ip(ms
->map
, dl
->ops
.target
);
317 target
= map__find_symbol(ms
->map
, ip
, NULL
);
318 if (target
== NULL
) {
319 ui_helpline__puts("The called function was not found.");
323 notes
= symbol__annotation(target
);
324 pthread_mutex_lock(¬es
->lock
);
326 if (notes
->src
== NULL
&& symbol__alloc_hist(target
) < 0) {
327 pthread_mutex_unlock(¬es
->lock
);
328 ui__warning("Not enough memory for annotating '%s' symbol!\n",
333 pthread_mutex_unlock(¬es
->lock
);
334 symbol__tui_annotate(target
, ms
->map
, evidx
, timer
, arg
, delay_secs
);
335 ui_browser__show_title(&browser
->b
, sym
->name
);
340 struct disasm_line
*annotate_browser__find_offset(struct annotate_browser
*browser
,
341 s64 offset
, s64
*idx
)
343 struct map_symbol
*ms
= browser
->b
.priv
;
344 struct symbol
*sym
= ms
->sym
;
345 struct annotation
*notes
= symbol__annotation(sym
);
346 struct disasm_line
*pos
;
349 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
350 if (pos
->offset
== offset
)
352 if (!disasm_line__filter(&browser
->b
, &pos
->node
))
359 static bool annotate_browser__jump(struct annotate_browser
*browser
)
361 struct disasm_line
*dl
= browser
->selection
;
364 if (!ins__is_jump(dl
->ins
))
367 dl
= annotate_browser__find_offset(browser
, dl
->ops
.target
, &idx
);
369 ui_helpline__puts("Invallid jump offset");
373 annotate_browser__set_top(browser
, dl
, idx
);
379 struct disasm_line
*annotate_browser__find_string(struct annotate_browser
*browser
,
382 struct map_symbol
*ms
= browser
->b
.priv
;
383 struct symbol
*sym
= ms
->sym
;
384 struct annotation
*notes
= symbol__annotation(sym
);
385 struct disasm_line
*pos
= browser
->selection
;
387 *idx
= browser
->b
.index
;
388 list_for_each_entry_continue(pos
, ¬es
->src
->source
, node
) {
389 if (disasm_line__filter(&browser
->b
, &pos
->node
))
394 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
401 static bool __annotate_browser__search(struct annotate_browser
*browser
)
403 struct disasm_line
*dl
;
406 dl
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
408 ui_helpline__puts("String not found!");
412 annotate_browser__set_top(browser
, dl
, idx
);
413 browser
->searching_backwards
= false;
418 struct disasm_line
*annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
421 struct map_symbol
*ms
= browser
->b
.priv
;
422 struct symbol
*sym
= ms
->sym
;
423 struct annotation
*notes
= symbol__annotation(sym
);
424 struct disasm_line
*pos
= browser
->selection
;
426 *idx
= browser
->b
.index
;
427 list_for_each_entry_continue_reverse(pos
, ¬es
->src
->source
, node
) {
428 if (disasm_line__filter(&browser
->b
, &pos
->node
))
433 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
440 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
442 struct disasm_line
*dl
;
445 dl
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
447 ui_helpline__puts("String not found!");
451 annotate_browser__set_top(browser
, dl
, idx
);
452 browser
->searching_backwards
= true;
456 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
459 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
460 "ENTER: OK, ESC: Cancel",
461 delay_secs
* 2) != K_ENTER
||
462 !*browser
->search_bf
)
468 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
470 if (annotate_browser__search_window(browser
, delay_secs
))
471 return __annotate_browser__search(browser
);
476 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
479 if (!*browser
->search_bf
)
480 return annotate_browser__search(browser
, delay_secs
);
482 return __annotate_browser__search(browser
);
485 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
488 if (annotate_browser__search_window(browser
, delay_secs
))
489 return __annotate_browser__search_reverse(browser
);
495 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
498 if (!*browser
->search_bf
)
499 return annotate_browser__search_reverse(browser
, delay_secs
);
501 return __annotate_browser__search_reverse(browser
);
504 static int annotate_browser__run(struct annotate_browser
*self
, int evidx
,
505 void(*timer
)(void *arg
),
506 void *arg
, int delay_secs
)
508 struct rb_node
*nd
= NULL
;
509 struct map_symbol
*ms
= self
->b
.priv
;
510 struct symbol
*sym
= ms
->sym
;
511 const char *help
= "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
512 "H: Go to hottest line, ->/ENTER: Line action, "
513 "O: Toggle offset view, "
514 "S: Toggle source code view";
517 if (ui_browser__show(&self
->b
, sym
->name
, help
) < 0)
520 annotate_browser__calc_percent(self
, evidx
);
522 if (self
->curr_hot
) {
523 annotate_browser__set_rb_top(self
, self
->curr_hot
);
524 self
->b
.navkeypressed
= false;
530 key
= ui_browser__run(&self
->b
, delay_secs
);
532 if (delay_secs
!= 0) {
533 annotate_browser__calc_percent(self
, evidx
);
535 * Current line focus got out of the list of most active
536 * lines, NULL it so that if TAB|UNTAB is pressed, we
537 * move to curr_hot (current hottest line).
539 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
549 symbol__annotate_decay_histogram(sym
, evidx
);
555 nd
= rb_last(&self
->entries
);
563 nd
= rb_first(&self
->entries
);
573 if (annotate_browser__toggle_source(self
))
574 ui_helpline__puts(help
);
578 self
->use_offset
= !self
->use_offset
;
581 if (annotate_browser__search(self
, delay_secs
)) {
583 ui_helpline__puts(help
);
587 if (self
->searching_backwards
?
588 annotate_browser__continue_search_reverse(self
, delay_secs
) :
589 annotate_browser__continue_search(self
, delay_secs
))
593 if (annotate_browser__search_reverse(self
, delay_secs
))
598 if (self
->selection
== NULL
)
599 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
600 else if (self
->selection
->offset
== -1)
601 ui_helpline__puts("Actions are only available for assembly lines.");
602 else if (!self
->selection
->ins
||
603 !(annotate_browser__jump(self
) ||
604 annotate_browser__callq(self
, evidx
, timer
, arg
, delay_secs
)))
605 ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
617 annotate_browser__set_rb_top(self
, nd
);
620 ui_browser__hide(&self
->b
);
624 int hist_entry__tui_annotate(struct hist_entry
*he
, int evidx
,
625 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
627 return symbol__tui_annotate(he
->ms
.sym
, he
->ms
.map
, evidx
,
628 timer
, arg
, delay_secs
);
631 static void annotate_browser__mark_jump_targets(struct annotate_browser
*browser
,
636 for (offset
= 0; offset
< size
; ++offset
) {
637 struct disasm_line
*dl
= browser
->offsets
[offset
], *dlt
;
638 struct browser_disasm_line
*bdlt
;
640 if (!dl
|| !dl
->ins
|| !ins__is_jump(dl
->ins
))
643 if (dl
->ops
.target
>= size
) {
644 ui__error("jump to after symbol!\n"
645 "size: %zx, jump target: %" PRIx64
,
646 size
, dl
->ops
.target
);
650 dlt
= browser
->offsets
[dl
->ops
.target
];
651 bdlt
= disasm_line__browser(dlt
);
652 bdlt
->jump_target
= true;
657 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
, int evidx
,
658 void(*timer
)(void *arg
), void *arg
,
661 struct disasm_line
*pos
, *n
;
662 struct annotation
*notes
;
663 const size_t size
= symbol__size(sym
);
664 struct map_symbol ms
= {
668 struct annotate_browser browser
= {
670 .refresh
= ui_browser__list_head_refresh
,
671 .seek
= ui_browser__list_head_seek
,
672 .write
= annotate_browser__write
,
673 .filter
= disasm_line__filter
,
675 .use_navkeypressed
= true,
684 if (map
->dso
->annotate_warned
)
687 browser
.offsets
= zalloc(size
* sizeof(struct disasm_line
*));
688 if (browser
.offsets
== NULL
) {
689 ui__error("Not enough memory!");
693 if (symbol__annotate(sym
, map
, sizeof(struct browser_disasm_line
)) < 0) {
694 ui__error("%s", ui_helpline__last_msg
);
695 goto out_free_offsets
;
698 ui_helpline__push("Press <- or ESC to exit");
700 notes
= symbol__annotation(sym
);
701 browser
.start
= map__rip_2objdump(map
, sym
->start
);
703 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
704 struct browser_disasm_line
*bpos
;
705 size_t line_len
= strlen(pos
->line
);
707 if (browser
.b
.width
< line_len
)
708 browser
.b
.width
= line_len
;
709 bpos
= disasm_line__browser(pos
);
710 bpos
->idx
= browser
.nr_entries
++;
711 if (pos
->offset
!= -1) {
712 bpos
->idx_asm
= browser
.nr_asm_entries
++;
714 * FIXME: short term bandaid to cope with assembly
715 * routines that comes with labels in the same column
716 * as the address in objdump, sigh.
718 * E.g. copy_user_generic_unrolled
720 if (pos
->offset
< (s64
)size
)
721 browser
.offsets
[pos
->offset
] = pos
;
726 annotate_browser__mark_jump_targets(&browser
, size
);
728 browser
.offset_width
= hex_width(size
);
729 browser
.b
.nr_entries
= browser
.nr_entries
;
730 browser
.b
.entries
= ¬es
->src
->source
,
731 browser
.b
.width
+= 18; /* Percentage */
732 ret
= annotate_browser__run(&browser
, evidx
, timer
, arg
, delay_secs
);
733 list_for_each_entry_safe(pos
, n
, ¬es
->src
->source
, node
) {
734 list_del(&pos
->node
);
735 disasm_line__free(pos
);
739 free(browser
.offsets
);