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 ui_browser__write_graph(self
, SLSMG_VLINE_CHAR
);
75 SLsmg_write_char(' ');
77 /* The scroll bar isn't being used */
78 if (!self
->navkeypressed
)
81 if (dl
->offset
!= -1 && change_color
)
82 ui_browser__set_color(self
, HE_COLORSET_CODE
);
85 slsmg_write_nstring(" ", width
- 10);
86 else if (dl
->offset
== -1)
87 slsmg_write_nstring(dl
->line
, width
- 10);
90 u64 addr
= dl
->offset
;
91 int printed
, color
= -1;
96 if (!ab
->use_offset
) {
97 printed
= scnprintf(bf
, sizeof(bf
), " %" PRIx64
":", addr
);
99 if (bdl
->jump_target
) {
100 printed
= scnprintf(bf
, sizeof(bf
), " %*" PRIx64
":",
101 ab
->offset_width
, addr
);
103 printed
= scnprintf(bf
, sizeof(bf
), " %*s ",
104 ab
->offset_width
, " ");
109 color
= ui_browser__set_color(self
, HE_COLORSET_ADDR
);
110 slsmg_write_nstring(bf
, printed
);
112 ui_browser__set_color(self
, color
);
113 if (dl
->ins
&& dl
->ins
->ops
->scnprintf
) {
114 if (ins__is_jump(dl
->ins
)) {
115 bool fwd
= dl
->ops
.target
> (u64
)dl
->offset
;
117 ui_browser__write_graph(self
, fwd
? SLSMG_DARROW_CHAR
:
119 SLsmg_write_char(' ');
121 slsmg_write_nstring(" ", 2);
124 dl
->ins
->ops
->scnprintf(dl
->ins
, bf
, sizeof(bf
), &dl
->ops
,
127 if (strcmp(dl
->name
, "retq")) {
128 slsmg_write_nstring(" ", 2);
130 ui_browser__write_graph(self
, SLSMG_LARROW_CHAR
);
131 SLsmg_write_char(' ');
134 scnprintf(bf
, sizeof(bf
), "%-6.6s %s", dl
->name
, dl
->ops
.raw
);
137 slsmg_write_nstring(bf
, width
- 12 - printed
);
144 static void annotate_browser__draw_current_loop(struct ui_browser
*browser
)
146 struct annotate_browser
*ab
= container_of(browser
, struct annotate_browser
, b
);
147 struct map_symbol
*ms
= browser
->priv
;
148 struct symbol
*sym
= ms
->sym
;
149 struct annotation
*notes
= symbol__annotation(sym
);
150 struct disasm_line
*cursor
= ab
->selection
, *pos
= cursor
, *target
;
151 struct browser_disasm_line
*bcursor
= disasm_line__browser(cursor
),
153 unsigned int from
, to
, start_width
= 2;
155 list_for_each_entry_from(pos
, ¬es
->src
->source
, node
) {
156 if (!pos
->ins
|| !ins__is_jump(pos
->ins
))
159 target
= ab
->offsets
[pos
->ops
.target
];
163 btarget
= disasm_line__browser(target
);
164 if (btarget
->idx
<= bcursor
->idx
)
171 bpos
= disasm_line__browser(pos
);
172 if (ab
->hide_src_code
) {
173 from
= bpos
->idx_asm
;
174 to
= btarget
->idx_asm
;
176 from
= (u64
)bpos
->idx
;
177 to
= (u64
)btarget
->idx
;
180 ui_browser__set_color(browser
, HE_COLORSET_CODE
);
182 if (!bpos
->jump_target
)
183 start_width
+= ab
->offset_width
+ 1;
185 __ui_browser__line_arrow_up(browser
, 10, from
, to
, start_width
);
188 static unsigned int annotate_browser__refresh(struct ui_browser
*browser
)
190 int ret
= ui_browser__list_head_refresh(browser
);
192 annotate_browser__draw_current_loop(browser
);
197 static double disasm_line__calc_percent(struct disasm_line
*dl
, struct symbol
*sym
, int evidx
)
199 double percent
= 0.0;
201 if (dl
->offset
!= -1) {
202 int len
= sym
->end
- sym
->start
;
203 unsigned int hits
= 0;
204 struct annotation
*notes
= symbol__annotation(sym
);
205 struct source_line
*src_line
= notes
->src
->lines
;
206 struct sym_hist
*h
= annotation__histogram(notes
, evidx
);
207 s64 offset
= dl
->offset
;
208 struct disasm_line
*next
;
210 next
= disasm__get_next_ip_line(¬es
->src
->source
, dl
);
211 while (offset
< (s64
)len
&&
212 (next
== NULL
|| offset
< next
->offset
)) {
214 percent
+= src_line
[offset
].percent
;
216 hits
+= h
->addr
[offset
];
221 * If the percentage wasn't already calculated in
222 * symbol__get_source_line, do it now:
224 if (src_line
== NULL
&& h
->sum
)
225 percent
= 100.0 * hits
/ h
->sum
;
231 static void disasm_rb_tree__insert(struct rb_root
*root
, struct browser_disasm_line
*bdl
)
233 struct rb_node
**p
= &root
->rb_node
;
234 struct rb_node
*parent
= NULL
;
235 struct browser_disasm_line
*l
;
239 l
= rb_entry(parent
, struct browser_disasm_line
, rb_node
);
240 if (bdl
->percent
< l
->percent
)
245 rb_link_node(&bdl
->rb_node
, parent
, p
);
246 rb_insert_color(&bdl
->rb_node
, root
);
249 static void annotate_browser__set_top(struct annotate_browser
*self
,
250 struct disasm_line
*pos
, u32 idx
)
254 ui_browser__refresh_dimensions(&self
->b
);
255 back
= self
->b
.height
/ 2;
256 self
->b
.top_idx
= self
->b
.index
= idx
;
258 while (self
->b
.top_idx
!= 0 && back
!= 0) {
259 pos
= list_entry(pos
->node
.prev
, struct disasm_line
, node
);
261 if (disasm_line__filter(&self
->b
, &pos
->node
))
269 self
->b
.navkeypressed
= true;
272 static void annotate_browser__set_rb_top(struct annotate_browser
*browser
,
275 struct browser_disasm_line
*bpos
;
276 struct disasm_line
*pos
;
278 bpos
= rb_entry(nd
, struct browser_disasm_line
, rb_node
);
279 pos
= ((struct disasm_line
*)bpos
) - 1;
280 annotate_browser__set_top(browser
, pos
, bpos
->idx
);
281 browser
->curr_hot
= nd
;
284 static void annotate_browser__calc_percent(struct annotate_browser
*browser
,
287 struct map_symbol
*ms
= browser
->b
.priv
;
288 struct symbol
*sym
= ms
->sym
;
289 struct annotation
*notes
= symbol__annotation(sym
);
290 struct disasm_line
*pos
;
292 browser
->entries
= RB_ROOT
;
294 pthread_mutex_lock(¬es
->lock
);
296 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
297 struct browser_disasm_line
*bpos
= disasm_line__browser(pos
);
298 bpos
->percent
= disasm_line__calc_percent(pos
, sym
, evidx
);
299 if (bpos
->percent
< 0.01) {
300 RB_CLEAR_NODE(&bpos
->rb_node
);
303 disasm_rb_tree__insert(&browser
->entries
, bpos
);
305 pthread_mutex_unlock(¬es
->lock
);
307 browser
->curr_hot
= rb_last(&browser
->entries
);
310 static bool annotate_browser__toggle_source(struct annotate_browser
*browser
)
312 struct disasm_line
*dl
;
313 struct browser_disasm_line
*bdl
;
314 off_t offset
= browser
->b
.index
- browser
->b
.top_idx
;
316 browser
->b
.seek(&browser
->b
, offset
, SEEK_CUR
);
317 dl
= list_entry(browser
->b
.top
, struct disasm_line
, node
);
318 bdl
= disasm_line__browser(dl
);
320 if (browser
->hide_src_code
) {
321 if (bdl
->idx_asm
< offset
)
324 browser
->b
.nr_entries
= browser
->nr_entries
;
325 browser
->hide_src_code
= false;
326 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
327 browser
->b
.top_idx
= bdl
->idx
- offset
;
328 browser
->b
.index
= bdl
->idx
;
330 if (bdl
->idx_asm
< 0) {
331 ui_helpline__puts("Only available for assembly lines.");
332 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
336 if (bdl
->idx_asm
< offset
)
337 offset
= bdl
->idx_asm
;
339 browser
->b
.nr_entries
= browser
->nr_asm_entries
;
340 browser
->hide_src_code
= true;
341 browser
->b
.seek(&browser
->b
, -offset
, SEEK_CUR
);
342 browser
->b
.top_idx
= bdl
->idx_asm
- offset
;
343 browser
->b
.index
= bdl
->idx_asm
;
349 static bool annotate_browser__callq(struct annotate_browser
*browser
,
350 int evidx
, void (*timer
)(void *arg
),
351 void *arg
, int delay_secs
)
353 struct map_symbol
*ms
= browser
->b
.priv
;
354 struct disasm_line
*dl
= browser
->selection
;
355 struct symbol
*sym
= ms
->sym
;
356 struct annotation
*notes
;
357 struct symbol
*target
;
360 if (!ins__is_call(dl
->ins
))
363 ip
= ms
->map
->map_ip(ms
->map
, dl
->ops
.target
);
364 target
= map__find_symbol(ms
->map
, ip
, NULL
);
365 if (target
== NULL
) {
366 ui_helpline__puts("The called function was not found.");
370 notes
= symbol__annotation(target
);
371 pthread_mutex_lock(¬es
->lock
);
373 if (notes
->src
== NULL
&& symbol__alloc_hist(target
) < 0) {
374 pthread_mutex_unlock(¬es
->lock
);
375 ui__warning("Not enough memory for annotating '%s' symbol!\n",
380 pthread_mutex_unlock(¬es
->lock
);
381 symbol__tui_annotate(target
, ms
->map
, evidx
, timer
, arg
, delay_secs
);
382 ui_browser__show_title(&browser
->b
, sym
->name
);
387 struct disasm_line
*annotate_browser__find_offset(struct annotate_browser
*browser
,
388 s64 offset
, s64
*idx
)
390 struct map_symbol
*ms
= browser
->b
.priv
;
391 struct symbol
*sym
= ms
->sym
;
392 struct annotation
*notes
= symbol__annotation(sym
);
393 struct disasm_line
*pos
;
396 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
397 if (pos
->offset
== offset
)
399 if (!disasm_line__filter(&browser
->b
, &pos
->node
))
406 static bool annotate_browser__jump(struct annotate_browser
*browser
)
408 struct disasm_line
*dl
= browser
->selection
;
411 if (!ins__is_jump(dl
->ins
))
414 dl
= annotate_browser__find_offset(browser
, dl
->ops
.target
, &idx
);
416 ui_helpline__puts("Invallid jump offset");
420 annotate_browser__set_top(browser
, dl
, idx
);
426 struct disasm_line
*annotate_browser__find_string(struct annotate_browser
*browser
,
429 struct map_symbol
*ms
= browser
->b
.priv
;
430 struct symbol
*sym
= ms
->sym
;
431 struct annotation
*notes
= symbol__annotation(sym
);
432 struct disasm_line
*pos
= browser
->selection
;
434 *idx
= browser
->b
.index
;
435 list_for_each_entry_continue(pos
, ¬es
->src
->source
, node
) {
436 if (disasm_line__filter(&browser
->b
, &pos
->node
))
441 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
448 static bool __annotate_browser__search(struct annotate_browser
*browser
)
450 struct disasm_line
*dl
;
453 dl
= annotate_browser__find_string(browser
, browser
->search_bf
, &idx
);
455 ui_helpline__puts("String not found!");
459 annotate_browser__set_top(browser
, dl
, idx
);
460 browser
->searching_backwards
= false;
465 struct disasm_line
*annotate_browser__find_string_reverse(struct annotate_browser
*browser
,
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
= browser
->selection
;
473 *idx
= browser
->b
.index
;
474 list_for_each_entry_continue_reverse(pos
, ¬es
->src
->source
, node
) {
475 if (disasm_line__filter(&browser
->b
, &pos
->node
))
480 if (pos
->line
&& strstr(pos
->line
, s
) != NULL
)
487 static bool __annotate_browser__search_reverse(struct annotate_browser
*browser
)
489 struct disasm_line
*dl
;
492 dl
= annotate_browser__find_string_reverse(browser
, browser
->search_bf
, &idx
);
494 ui_helpline__puts("String not found!");
498 annotate_browser__set_top(browser
, dl
, idx
);
499 browser
->searching_backwards
= true;
503 static bool annotate_browser__search_window(struct annotate_browser
*browser
,
506 if (ui_browser__input_window("Search", "String: ", browser
->search_bf
,
507 "ENTER: OK, ESC: Cancel",
508 delay_secs
* 2) != K_ENTER
||
509 !*browser
->search_bf
)
515 static bool annotate_browser__search(struct annotate_browser
*browser
, int delay_secs
)
517 if (annotate_browser__search_window(browser
, delay_secs
))
518 return __annotate_browser__search(browser
);
523 static bool annotate_browser__continue_search(struct annotate_browser
*browser
,
526 if (!*browser
->search_bf
)
527 return annotate_browser__search(browser
, delay_secs
);
529 return __annotate_browser__search(browser
);
532 static bool annotate_browser__search_reverse(struct annotate_browser
*browser
,
535 if (annotate_browser__search_window(browser
, delay_secs
))
536 return __annotate_browser__search_reverse(browser
);
542 bool annotate_browser__continue_search_reverse(struct annotate_browser
*browser
,
545 if (!*browser
->search_bf
)
546 return annotate_browser__search_reverse(browser
, delay_secs
);
548 return __annotate_browser__search_reverse(browser
);
551 static int annotate_browser__run(struct annotate_browser
*self
, int evidx
,
552 void(*timer
)(void *arg
),
553 void *arg
, int delay_secs
)
555 struct rb_node
*nd
= NULL
;
556 struct map_symbol
*ms
= self
->b
.priv
;
557 struct symbol
*sym
= ms
->sym
;
558 const char *help
= "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
559 "H: Go to hottest line, ->/ENTER: Line action, "
560 "O: Toggle offset view, "
561 "S: Toggle source code view";
564 if (ui_browser__show(&self
->b
, sym
->name
, help
) < 0)
567 annotate_browser__calc_percent(self
, evidx
);
569 if (self
->curr_hot
) {
570 annotate_browser__set_rb_top(self
, self
->curr_hot
);
571 self
->b
.navkeypressed
= false;
577 key
= ui_browser__run(&self
->b
, delay_secs
);
579 if (delay_secs
!= 0) {
580 annotate_browser__calc_percent(self
, evidx
);
582 * Current line focus got out of the list of most active
583 * lines, NULL it so that if TAB|UNTAB is pressed, we
584 * move to curr_hot (current hottest line).
586 if (nd
!= NULL
&& RB_EMPTY_NODE(nd
))
596 symbol__annotate_decay_histogram(sym
, evidx
);
602 nd
= rb_last(&self
->entries
);
610 nd
= rb_first(&self
->entries
);
620 if (annotate_browser__toggle_source(self
))
621 ui_helpline__puts(help
);
625 self
->use_offset
= !self
->use_offset
;
628 if (annotate_browser__search(self
, delay_secs
)) {
630 ui_helpline__puts(help
);
634 if (self
->searching_backwards
?
635 annotate_browser__continue_search_reverse(self
, delay_secs
) :
636 annotate_browser__continue_search(self
, delay_secs
))
640 if (annotate_browser__search_reverse(self
, delay_secs
))
645 if (self
->selection
== NULL
)
646 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
647 else if (self
->selection
->offset
== -1)
648 ui_helpline__puts("Actions are only available for assembly lines.");
649 else if (!self
->selection
->ins
) {
650 if (strcmp(self
->selection
->name
, "retq"))
653 } else if (!(annotate_browser__jump(self
) ||
654 annotate_browser__callq(self
, evidx
, timer
, arg
, delay_secs
))) {
656 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
669 annotate_browser__set_rb_top(self
, nd
);
672 ui_browser__hide(&self
->b
);
676 int hist_entry__tui_annotate(struct hist_entry
*he
, int evidx
,
677 void(*timer
)(void *arg
), void *arg
, int delay_secs
)
679 return symbol__tui_annotate(he
->ms
.sym
, he
->ms
.map
, evidx
,
680 timer
, arg
, delay_secs
);
683 static void annotate_browser__mark_jump_targets(struct annotate_browser
*browser
,
688 for (offset
= 0; offset
< size
; ++offset
) {
689 struct disasm_line
*dl
= browser
->offsets
[offset
], *dlt
;
690 struct browser_disasm_line
*bdlt
;
692 if (!dl
|| !dl
->ins
|| !ins__is_jump(dl
->ins
))
695 if (dl
->ops
.target
>= size
) {
696 ui__error("jump to after symbol!\n"
697 "size: %zx, jump target: %" PRIx64
,
698 size
, dl
->ops
.target
);
702 dlt
= browser
->offsets
[dl
->ops
.target
];
704 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
705 * have to adjust to the previous offset?
710 bdlt
= disasm_line__browser(dlt
);
711 bdlt
->jump_target
= true;
716 int symbol__tui_annotate(struct symbol
*sym
, struct map
*map
, int evidx
,
717 void(*timer
)(void *arg
), void *arg
,
720 struct disasm_line
*pos
, *n
;
721 struct annotation
*notes
;
722 const size_t size
= symbol__size(sym
);
723 struct map_symbol ms
= {
727 struct annotate_browser browser
= {
729 .refresh
= annotate_browser__refresh
,
730 .seek
= ui_browser__list_head_seek
,
731 .write
= annotate_browser__write
,
732 .filter
= disasm_line__filter
,
734 .use_navkeypressed
= true,
743 if (map
->dso
->annotate_warned
)
746 browser
.offsets
= zalloc(size
* sizeof(struct disasm_line
*));
747 if (browser
.offsets
== NULL
) {
748 ui__error("Not enough memory!");
752 if (symbol__annotate(sym
, map
, sizeof(struct browser_disasm_line
)) < 0) {
753 ui__error("%s", ui_helpline__last_msg
);
754 goto out_free_offsets
;
757 ui_helpline__push("Press <- or ESC to exit");
759 notes
= symbol__annotation(sym
);
760 browser
.start
= map__rip_2objdump(map
, sym
->start
);
762 list_for_each_entry(pos
, ¬es
->src
->source
, node
) {
763 struct browser_disasm_line
*bpos
;
764 size_t line_len
= strlen(pos
->line
);
766 if (browser
.b
.width
< line_len
)
767 browser
.b
.width
= line_len
;
768 bpos
= disasm_line__browser(pos
);
769 bpos
->idx
= browser
.nr_entries
++;
770 if (pos
->offset
!= -1) {
771 bpos
->idx_asm
= browser
.nr_asm_entries
++;
773 * FIXME: short term bandaid to cope with assembly
774 * routines that comes with labels in the same column
775 * as the address in objdump, sigh.
777 * E.g. copy_user_generic_unrolled
779 if (pos
->offset
< (s64
)size
)
780 browser
.offsets
[pos
->offset
] = pos
;
785 annotate_browser__mark_jump_targets(&browser
, size
);
787 browser
.offset_width
= hex_width(size
);
788 browser
.b
.nr_entries
= browser
.nr_entries
;
789 browser
.b
.entries
= ¬es
->src
->source
,
790 browser
.b
.width
+= 18; /* Percentage */
791 ret
= annotate_browser__run(&browser
, evidx
, timer
, arg
, delay_secs
);
792 list_for_each_entry_safe(pos
, n
, ¬es
->src
->source
, node
) {
793 list_del(&pos
->node
);
794 disasm_line__free(pos
);
798 free(browser
.offsets
);