Fixes on top of the previous perf/annotate pull request

. Sometimes a jump points to an offset with no instructions, make the
   mark jump targets function handle that, for now just ignoring such
   jump targets, more investigation is needed to figure out how to cope
   with that.
 
 . Handle jump targets that are outside the function, for now just don't
   try to draw the connector arrow, right thing seems to be to mark this
   jump with a -> (right arrow) and handle it like a callq.
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJPmWGLAAoJENZQFvNTUqpAPwYP/1HLjBtDkh9IvyQdixHQsrsK
 D3SUajTPYVOwUMMtLKerpcEWgl8TvyBYiciYDTlVF3vPkIifKPV6VC3AUWfPuuOq
 RRZK8fwTiU8SBzBs0TbDs3uSMUh6HyICd8NrsCKVvydtAvtCErIPFZYQRcUbsLcS
 i5om0HTyeDUmvGMFZldrpu7rkaM5VoWp5cMCUFLHOFxg4b3dZV5Xuejn3M4vrtnN
 UNH5Koy6jBb+B9Y1QV28PUxs56nab2n+/04ruvGVwcpxjk939/eZKTxda4VkeRZs
 rev4yh9yf3xnvnDC2T/6hNM+gaapsSx5ho26qjh2Wn1Ty3OOlNo0dl5OygNCF0iX
 W0XrBzCh8O5V7Wyd8enY94tt97S8OYUhQmNXz1ee+1C284+vWNteIIhHK7MKOWn4
 Xy0RGU4qLQCKX/xZFmlRhlyoil5lS7R/zVUoimOaI3P4cjqsXHFZ+PT+8QkHu6Wk
 nisD2XX/v/gdkNuZ5ItW/gseAulp9JdvzHzTcrfPBnG+z1NqOvnPTmW3tlnvqmsb
 S4S0try6tbH2hBblP5Z4n7LiEudiD0tsdvqbnD0FvyhCTJkSP84q7rP6tUpqmLbH
 4EbRBRNQafjN08c4kZk8085C86iKSMQ0LKOJ+TOjJLsEHZzYtghNgOOVktKrnh8r
 n6cLlI0wE2iYFhZ92Oc9
 =NPh2
 -----END PGP SIGNATURE-----

Merge tag 'perf-annotate-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Annotation improvements:

Now the default annotate browser uses a much more compact format, implementing
suggestions made made by several people, notably Linus.

Here is part of the new __list_del_entry() annotation:

__list_del_entry
    8.47 │      push   %rbp
    8.47 │      mov    (%rdi),%rdx
   20.34 │      mov    $0xdead000000100100,%rcx
    3.39 │      mov    0x8(%rdi),%rax
    0.00 │      mov    %rsp,%rbp
    1.69 │      cmp    %rcx,%rdx
    0.00 │      je     43
    1.69 │      mov    $0xdead000000200200,%rcx
    3.39 │      cmp    %rcx,%rax
    0.00 │      je     a3
    5.08 │      mov    (%rax),%r8
   18.64 │      cmp    %r8,%rdi
    0.00 │      jne    84
    1.69 │      mov    0x8(%rdx),%r8
   25.42 │      cmp    %r8,%rdi
    0.00 │      jne    65
    1.69 │      mov    %rax,0x8(%rdx)
    0.00 │      mov    %rdx,(%rax)
    0.00 │      leaveq
    0.00 │      retq
    0.00 │ 43:  mov    %rdx,%r8
    0.00 │      mov    %rdi,%rcx
    0.00 │      mov    $0xffffffff817cd6a8,%rdx
    0.00 │      mov    $0x31,%esi
    0.00 │      mov    $0xffffffff817cd6e0,%rdi
    0.00 │      xor    %eax,%eax
    0.00 │      callq  ffffffff8104eab0 <warn_slowpath_fmt>
    0.00 │      leaveq
    0.00 │      retq
    0.00 │ 65:  mov    %rdi,%rcx
    0.00 │      mov    $0xffffffff817cd780,%rdx
    0.00 │      mov    $0x3a,%esi
    0.00 │      mov    $0xffffffff817cd6e0,%rdi
    0.00 │      xor    %eax,%eax
    0.00 │      callq  ffffffff8104eab0 <warn_slowpath_fmt>
    0.00 │      leaveq
    0.00 │      retq

The infrastructure is there to provide formatters for any instruction,
like the one I'll do for call functions to elide the address.

Further fixes on top of the first iteration:

- Sometimes a jump points to an offset with no instructions, make the
  mark jump targets function handle that, for now just ignoring such
  jump targets, more investigation is needed to figure out how to cope
  with that.

- Handle jump targets that are outside the function, for now just don't
  try to draw the connector arrow, right thing seems to be to mark this
  jump with a -> (right arrow) and handle it like a callq.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2012-04-27 09:00:50 +02:00
commit 1fa2e84db3
8 changed files with 645 additions and 195 deletions

View File

@ -593,6 +593,52 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
return row;
}
void ui_browser__write_graph(struct ui_browser *browser __used, int graph)
{
SLsmg_set_char_set(1);
SLsmg_write_char(graph);
SLsmg_set_char_set(0);
}
void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column,
u64 start, u64 end, int start_width)
{
unsigned int row, end_row;
SLsmg_set_char_set(1);
if (start < browser->top_idx + browser->height) {
row = start - browser->top_idx;
ui_browser__gotorc(browser, row, column);
SLsmg_write_char(SLSMG_LLCORN_CHAR);
ui_browser__gotorc(browser, row, column + 1);
SLsmg_draw_hline(start_width);
if (row-- == 0)
goto out;
} else
row = browser->height - 1;
if (end > browser->top_idx)
end_row = end - browser->top_idx;
else
end_row = 0;
ui_browser__gotorc(browser, end_row, column);
SLsmg_draw_vline(row - end_row + 1);
ui_browser__gotorc(browser, end_row, column);
if (end >= browser->top_idx) {
SLsmg_write_char(SLSMG_ULCORN_CHAR);
ui_browser__gotorc(browser, end_row, column + 1);
SLsmg_write_char(SLSMG_HLINE_CHAR);
ui_browser__gotorc(browser, end_row, column + 2);
SLsmg_write_char(SLSMG_RARROW_CHAR);
}
out:
SLsmg_set_char_set(0);
}
void ui_browser__init(void)
{
int i = 0;

View File

@ -37,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self);
void ui_browser__reset_index(struct ui_browser *self);
void ui_browser__gotorc(struct ui_browser *self, int y, int x);
void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column,
u64 start, u64 end, int start_width);
void __ui_browser__show_title(struct ui_browser *browser, const char *title);
void ui_browser__show_title(struct ui_browser *browser, const char *title);
int ui_browser__show(struct ui_browser *self, const char *title,

View File

@ -11,40 +11,42 @@
#include <pthread.h>
#include <newt.h>
struct browser_disasm_line {
struct rb_node rb_node;
double percent;
u32 idx;
int idx_asm;
bool jump_target;
};
struct annotate_browser {
struct ui_browser b;
struct rb_root entries;
struct rb_node *curr_hot;
struct objdump_line *selection;
struct disasm_line *selection;
struct disasm_line **offsets;
u64 start;
int nr_asm_entries;
int nr_entries;
bool hide_src_code;
bool use_offset;
bool searching_backwards;
u8 offset_width;
char search_bf[128];
};
struct objdump_line_rb_node {
struct rb_node rb_node;
double percent;
u32 idx;
int idx_asm;
};
static inline
struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
{
return (struct objdump_line_rb_node *)(self + 1);
return (struct browser_disasm_line *)(dl + 1);
}
static bool objdump_line__filter(struct ui_browser *browser, void *entry)
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
if (ab->hide_src_code) {
struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
return ol->offset == -1;
struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
return dl->offset == -1;
}
return false;
@ -53,72 +55,160 @@ static bool objdump_line__filter(struct ui_browser *browser, void *entry)
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
struct browser_disasm_line *bdl = disasm_line__browser(dl);
bool current_entry = ui_browser__is_current_entry(self, row);
bool change_color = (!ab->hide_src_code &&
(!current_entry || (self->use_navkeypressed &&
!self->navkeypressed)));
int width = self->width;
if (ol->offset != -1) {
struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
ui_browser__set_percent_color(self, olrb->percent, current_entry);
slsmg_printf(" %7.2f ", olrb->percent);
if (dl->offset != -1) {
ui_browser__set_percent_color(self, bdl->percent, current_entry);
slsmg_printf(" %7.2f ", bdl->percent);
} else {
ui_browser__set_percent_color(self, 0, current_entry);
slsmg_write_nstring(" ", 9);
}
SLsmg_write_char(':');
slsmg_write_nstring(" ", 8);
ui_browser__write_graph(self, SLSMG_VLINE_CHAR);
SLsmg_write_char(' ');
/* The scroll bar isn't being used */
if (!self->navkeypressed)
width += 1;
if (ol->offset != -1 && change_color)
if (dl->offset != -1 && change_color)
ui_browser__set_color(self, HE_COLORSET_CODE);
if (!*ol->line)
slsmg_write_nstring(" ", width - 18);
else if (ol->offset == -1)
slsmg_write_nstring(ol->line, width - 18);
if (!*dl->line)
slsmg_write_nstring(" ", width - 10);
else if (dl->offset == -1)
slsmg_write_nstring(dl->line, width - 10);
else {
char bf[64];
u64 addr = ol->offset;
char bf[256];
u64 addr = dl->offset;
int printed, color = -1;
if (!ab->use_offset)
addr += ab->start;
printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
if (!ab->use_offset) {
printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
} else {
if (bdl->jump_target) {
printed = scnprintf(bf, sizeof(bf), " %*" PRIx64 ":",
ab->offset_width, addr);
} else {
printed = scnprintf(bf, sizeof(bf), " %*s ",
ab->offset_width, " ");
}
}
if (change_color)
color = ui_browser__set_color(self, HE_COLORSET_ADDR);
slsmg_write_nstring(bf, printed);
if (change_color)
ui_browser__set_color(self, color);
slsmg_write_nstring(ol->line, width - 18 - printed);
if (dl->ins && dl->ins->ops->scnprintf) {
if (ins__is_jump(dl->ins)) {
bool fwd = dl->ops.target.offset > (u64)dl->offset;
ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
SLSMG_UARROW_CHAR);
SLsmg_write_char(' ');
} else {
slsmg_write_nstring(" ", 2);
}
dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
!ab->use_offset);
} else {
if (strcmp(dl->name, "retq")) {
slsmg_write_nstring(" ", 2);
} else {
ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
SLsmg_write_char(' ');
}
scnprintf(bf, sizeof(bf), "%-6.6s %s", dl->name, dl->ops.raw);
}
slsmg_write_nstring(bf, width - 12 - printed);
}
if (current_entry)
ab->selection = ol;
ab->selection = dl;
}
static double objdump_line__calc_percent(struct objdump_line *self,
struct symbol *sym, int evidx)
static void annotate_browser__draw_current_loop(struct ui_browser *browser)
{
struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
struct map_symbol *ms = browser->priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *cursor = ab->selection, *pos = cursor, *target;
struct browser_disasm_line *bcursor = disasm_line__browser(cursor),
*btarget, *bpos;
unsigned int from, to, start_width = 2;
list_for_each_entry_from(pos, &notes->src->source, node) {
if (!pos->ins || !ins__is_jump(pos->ins) ||
!disasm_line__has_offset(pos))
continue;
target = ab->offsets[pos->ops.target.offset];
if (!target)
continue;
btarget = disasm_line__browser(target);
if (btarget->idx <= bcursor->idx)
goto found;
}
return;
found:
bpos = disasm_line__browser(pos);
if (ab->hide_src_code) {
from = bpos->idx_asm;
to = btarget->idx_asm;
} else {
from = (u64)bpos->idx;
to = (u64)btarget->idx;
}
ui_browser__set_color(browser, HE_COLORSET_CODE);
if (!bpos->jump_target)
start_width += ab->offset_width + 1;
__ui_browser__line_arrow_up(browser, 10, from, to, start_width);
}
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
int ret = ui_browser__list_head_refresh(browser);
annotate_browser__draw_current_loop(browser);
return ret;
}
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
{
double percent = 0.0;
if (self->offset != -1) {
if (dl->offset != -1) {
int len = sym->end - sym->start;
unsigned int hits = 0;
struct annotation *notes = symbol__annotation(sym);
struct source_line *src_line = notes->src->lines;
struct sym_hist *h = annotation__histogram(notes, evidx);
s64 offset = self->offset;
struct objdump_line *next;
s64 offset = dl->offset;
struct disasm_line *next;
next = objdump__get_next_ip_line(&notes->src->source, self);
next = disasm__get_next_ip_line(&notes->src->source, dl);
while (offset < (s64)len &&
(next == NULL || offset < next->offset)) {
if (src_line) {
@ -139,27 +229,26 @@ static double objdump_line__calc_percent(struct objdump_line *self,
return percent;
}
static void objdump__insert_line(struct rb_root *self,
struct objdump_line_rb_node *line)
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
{
struct rb_node **p = &self->rb_node;
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct objdump_line_rb_node *l;
struct browser_disasm_line *l;
while (*p != NULL) {
parent = *p;
l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
if (line->percent < l->percent)
l = rb_entry(parent, struct browser_disasm_line, rb_node);
if (bdl->percent < l->percent)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&line->rb_node, parent, p);
rb_insert_color(&line->rb_node, self);
rb_link_node(&bdl->rb_node, parent, p);
rb_insert_color(&bdl->rb_node, root);
}
static void annotate_browser__set_top(struct annotate_browser *self,
struct objdump_line *pos, u32 idx)
struct disasm_line *pos, u32 idx)
{
unsigned back;
@ -168,9 +257,9 @@ static void annotate_browser__set_top(struct annotate_browser *self,
self->b.top_idx = self->b.index = idx;
while (self->b.top_idx != 0 && back != 0) {
pos = list_entry(pos->node.prev, struct objdump_line, node);
pos = list_entry(pos->node.prev, struct disasm_line, node);
if (objdump_line__filter(&self->b, &pos->node))
if (disasm_line__filter(&self->b, &pos->node))
continue;
--self->b.top_idx;
@ -184,12 +273,12 @@ static void annotate_browser__set_top(struct annotate_browser *self,
static void annotate_browser__set_rb_top(struct annotate_browser *browser,
struct rb_node *nd)
{
struct objdump_line_rb_node *rbpos;
struct objdump_line *pos;
struct browser_disasm_line *bpos;
struct disasm_line *pos;
rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
pos = ((struct objdump_line *)rbpos) - 1;
annotate_browser__set_top(browser, pos, rbpos->idx);
bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
pos = ((struct disasm_line *)bpos) - 1;
annotate_browser__set_top(browser, pos, bpos->idx);
browser->curr_hot = nd;
}
@ -199,20 +288,20 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos;
struct disasm_line *pos;
browser->entries = RB_ROOT;
pthread_mutex_lock(&notes->lock);
list_for_each_entry(pos, &notes->src->source, node) {
struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
if (rbpos->percent < 0.01) {
RB_CLEAR_NODE(&rbpos->rb_node);
struct browser_disasm_line *bpos = disasm_line__browser(pos);
bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
if (bpos->percent < 0.01) {
RB_CLEAR_NODE(&bpos->rb_node);
continue;
}
objdump__insert_line(&browser->entries, rbpos);
disasm_rb_tree__insert(&browser->entries, bpos);
}
pthread_mutex_unlock(&notes->lock);
@ -221,38 +310,38 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
struct objdump_line *ol;
struct objdump_line_rb_node *olrb;
struct disasm_line *dl;
struct browser_disasm_line *bdl;
off_t offset = browser->b.index - browser->b.top_idx;
browser->b.seek(&browser->b, offset, SEEK_CUR);
ol = list_entry(browser->b.top, struct objdump_line, node);
olrb = objdump_line__rb(ol);
dl = list_entry(browser->b.top, struct disasm_line, node);
bdl = disasm_line__browser(dl);
if (browser->hide_src_code) {
if (olrb->idx_asm < offset)
offset = olrb->idx;
if (bdl->idx_asm < offset)
offset = bdl->idx;
browser->b.nr_entries = browser->nr_entries;
browser->hide_src_code = false;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
browser->b.top_idx = olrb->idx - offset;
browser->b.index = olrb->idx;
browser->b.top_idx = bdl->idx - offset;
browser->b.index = bdl->idx;
} else {
if (olrb->idx_asm < 0) {
if (bdl->idx_asm < 0) {
ui_helpline__puts("Only available for assembly lines.");
browser->b.seek(&browser->b, -offset, SEEK_CUR);
return false;
}
if (olrb->idx_asm < offset)
offset = olrb->idx_asm;
if (bdl->idx_asm < offset)
offset = bdl->idx_asm;
browser->b.nr_entries = browser->nr_asm_entries;
browser->hide_src_code = true;
browser->b.seek(&browser->b, -offset, SEEK_CUR);
browser->b.top_idx = olrb->idx_asm - offset;
browser->b.index = olrb->idx_asm;
browser->b.top_idx = bdl->idx_asm - offset;
browser->b.index = bdl->idx_asm;
}
return true;
@ -263,23 +352,16 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
void *arg, int delay_secs)
{
struct map_symbol *ms = browser->b.priv;
struct disasm_line *dl = browser->selection;
struct symbol *sym = ms->sym;
struct annotation *notes;
struct symbol *target;
char *s = strstr(browser->selection->line, "callq ");
u64 ip;
if (s == NULL)
if (!ins__is_call(dl->ins))
return false;
s = strchr(s, ' ');
if (s++ == NULL) {
ui_helpline__puts("Invallid callq instruction.");
return true;
}
ip = strtoull(s, NULL, 16);
ip = ms->map->map_ip(ms->map, ip);
ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
target = map__find_symbol(ms->map, ip, NULL);
if (target == NULL) {
ui_helpline__puts("The called function was not found.");
@ -302,20 +384,20 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
return true;
}
static struct objdump_line *
annotate_browser__find_offset(struct annotate_browser *browser,
s64 offset, s64 *idx)
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
s64 offset, s64 *idx)
{
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos;
struct disasm_line *pos;
*idx = 0;
list_for_each_entry(pos, &notes->src->source, node) {
if (pos->offset == offset)
return pos;
if (!objdump_line__filter(&browser->b, &pos->node))
if (!disasm_line__filter(&browser->b, &pos->node))
++*idx;
}
@ -324,51 +406,35 @@ static struct objdump_line *
static bool annotate_browser__jump(struct annotate_browser *browser)
{
const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
struct objdump_line *line;
s64 idx, offset;
char *s = NULL;
int i = 0;
struct disasm_line *dl = browser->selection;
s64 idx;
while (jumps[i]) {
s = strstr(browser->selection->line, jumps[i++]);
if (s)
break;
}
if (s == NULL)
if (!ins__is_jump(dl->ins))
return false;
s = strchr(s, '+');
if (s++ == NULL) {
ui_helpline__puts("Invallid jump instruction.");
return true;
}
offset = strtoll(s, NULL, 16);
line = annotate_browser__find_offset(browser, offset, &idx);
if (line == NULL) {
dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
if (dl == NULL) {
ui_helpline__puts("Invallid jump offset");
return true;
}
annotate_browser__set_top(browser, line, idx);
annotate_browser__set_top(browser, dl, idx);
return true;
}
static struct objdump_line *
annotate_browser__find_string(struct annotate_browser *browser,
char *s, s64 *idx)
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
char *s, s64 *idx)
{
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos = browser->selection;
struct disasm_line *pos = browser->selection;
*idx = browser->b.index;
list_for_each_entry_continue(pos, &notes->src->source, node) {
if (objdump_line__filter(&browser->b, &pos->node))
if (disasm_line__filter(&browser->b, &pos->node))
continue;
++*idx;
@ -382,32 +448,32 @@ static struct objdump_line *
static bool __annotate_browser__search(struct annotate_browser *browser)
{
struct objdump_line *line;
struct disasm_line *dl;
s64 idx;
line = annotate_browser__find_string(browser, browser->search_bf, &idx);
if (line == NULL) {
dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
if (dl == NULL) {
ui_helpline__puts("String not found!");
return false;
}
annotate_browser__set_top(browser, line, idx);
annotate_browser__set_top(browser, dl, idx);
browser->searching_backwards = false;
return true;
}
static struct objdump_line *
annotate_browser__find_string_reverse(struct annotate_browser *browser,
char *s, s64 *idx)
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
char *s, s64 *idx)
{
struct map_symbol *ms = browser->b.priv;
struct symbol *sym = ms->sym;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos = browser->selection;
struct disasm_line *pos = browser->selection;
*idx = browser->b.index;
list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
if (objdump_line__filter(&browser->b, &pos->node))
if (disasm_line__filter(&browser->b, &pos->node))
continue;
--*idx;
@ -421,16 +487,16 @@ static struct objdump_line *
static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
struct objdump_line *line;
struct disasm_line *dl;
s64 idx;
line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
if (line == NULL) {
dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
if (dl == NULL) {
ui_helpline__puts("String not found!");
return false;
}
annotate_browser__set_top(browser, line, idx);
annotate_browser__set_top(browser, dl, idx);
browser->searching_backwards = true;
return true;
}
@ -581,9 +647,15 @@ show_help:
ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
else if (self->selection->offset == -1)
ui_helpline__puts("Actions are only available for assembly lines.");
else if (!(annotate_browser__jump(self) ||
annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
else if (!self->selection->ins) {
if (strcmp(self->selection->name, "retq"))
goto show_sup_ins;
goto out;
} else if (!(annotate_browser__jump(self) ||
annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
show_sup_ins:
ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
}
continue;
case K_LEFT:
case K_ESC:
@ -609,27 +681,63 @@ int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
timer, arg, delay_secs);
}
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
size_t size)
{
u64 offset;
for (offset = 0; offset < size; ++offset) {
struct disasm_line *dl = browser->offsets[offset], *dlt;
struct browser_disasm_line *bdlt;
if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
!disasm_line__has_offset(dl))
continue;
if (dl->ops.target.offset >= size) {
ui__error("jump to after symbol!\n"
"size: %zx, jump target: %" PRIx64,
size, dl->ops.target.offset);
continue;
}
dlt = browser->offsets[dl->ops.target.offset];
/*
* FIXME: Oops, no jump target? Buggy disassembler? Or do we
* have to adjust to the previous offset?
*/
if (dlt == NULL)
continue;
bdlt = disasm_line__browser(dlt);
bdlt->jump_target = true;
}
}
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
void(*timer)(void *arg), void *arg,
int delay_secs)
{
struct objdump_line *pos, *n;
struct disasm_line *pos, *n;
struct annotation *notes;
const size_t size = symbol__size(sym);
struct map_symbol ms = {
.map = map,
.sym = sym,
};
struct annotate_browser browser = {
.b = {
.refresh = ui_browser__list_head_refresh,
.refresh = annotate_browser__refresh,
.seek = ui_browser__list_head_seek,
.write = annotate_browser__write,
.filter = objdump_line__filter,
.filter = disasm_line__filter,
.priv = &ms,
.use_navkeypressed = true,
},
.use_offset = true,
};
int ret;
int ret = -1;
if (sym == NULL)
return -1;
@ -637,37 +745,58 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
if (map->dso->annotate_warned)
return -1;
if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
ui__error("%s", ui_helpline__last_msg);
browser.offsets = zalloc(size * sizeof(struct disasm_line *));
if (browser.offsets == NULL) {
ui__error("Not enough memory!");
return -1;
}
if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
ui__error("%s", ui_helpline__last_msg);
goto out_free_offsets;
}
ui_helpline__push("Press <- or ESC to exit");
notes = symbol__annotation(sym);
browser.start = map__rip_2objdump(map, sym->start);
list_for_each_entry(pos, &notes->src->source, node) {
struct objdump_line_rb_node *rbpos;
struct browser_disasm_line *bpos;
size_t line_len = strlen(pos->line);
if (browser.b.width < line_len)
browser.b.width = line_len;
rbpos = objdump_line__rb(pos);
rbpos->idx = browser.nr_entries++;
if (pos->offset != -1)
rbpos->idx_asm = browser.nr_asm_entries++;
else
rbpos->idx_asm = -1;
bpos = disasm_line__browser(pos);
bpos->idx = browser.nr_entries++;
if (pos->offset != -1) {
bpos->idx_asm = browser.nr_asm_entries++;
/*
* FIXME: short term bandaid to cope with assembly
* routines that comes with labels in the same column
* as the address in objdump, sigh.
*
* E.g. copy_user_generic_unrolled
*/
if (pos->offset < (s64)size)
browser.offsets[pos->offset] = pos;
} else
bpos->idx_asm = -1;
}
annotate_browser__mark_jump_targets(&browser, size);
browser.offset_width = hex_width(size);
browser.b.nr_entries = browser.nr_entries;
browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */
ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
list_for_each_entry_safe(pos, n, &notes->src->source, node) {
list_del(&pos->node);
objdump_line__free(pos);
disasm_line__free(pos);
}
out_free_offsets:
free(browser.offsets);
return ret;
}

View File

@ -18,6 +18,149 @@
const char *disassembler_style;
static int call__parse(struct ins_operands *ops)
{
char *endptr, *tok, *name;
ops->target.addr = strtoull(ops->raw, &endptr, 16);
name = strchr(endptr, '<');
if (name == NULL)
goto indirect_call;
name++;
tok = strchr(name, '>');
if (tok == NULL)
return -1;
*tok = '\0';
ops->target.name = strdup(name);
*tok = '>';
return ops->target.name == NULL ? -1 : 0;
indirect_call:
tok = strchr(endptr, '*');
if (tok == NULL)
return -1;
ops->target.addr = strtoull(tok + 1, NULL, 16);
return 0;
}
static int call__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, bool addrs)
{
if (addrs)
return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
if (ops->target.name)
return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);
return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
}
static struct ins_ops call_ops = {
.parse = call__parse,
.scnprintf = call__scnprintf,
};
bool ins__is_call(const struct ins *ins)
{
return ins->ops == &call_ops;
}
static int jump__parse(struct ins_operands *ops)
{
const char *s = strchr(ops->raw, '+');
ops->target.addr = strtoll(ops->raw, NULL, 16);
if (s++ != NULL)
ops->target.offset = strtoll(s, NULL, 16);
else
ops->target.offset = UINT64_MAX;
return 0;
}
static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, bool addrs)
{
if (addrs)
return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset);
}
static struct ins_ops jump_ops = {
.parse = jump__parse,
.scnprintf = jump__scnprintf,
};
bool ins__is_jump(const struct ins *ins)
{
return ins->ops == &jump_ops;
}
/*
* Must be sorted by name!
*/
static struct ins instructions[] = {
{ .name = "call", .ops = &call_ops, },
{ .name = "callq", .ops = &call_ops, },
{ .name = "ja", .ops = &jump_ops, },
{ .name = "jae", .ops = &jump_ops, },
{ .name = "jb", .ops = &jump_ops, },
{ .name = "jbe", .ops = &jump_ops, },
{ .name = "jc", .ops = &jump_ops, },
{ .name = "jcxz", .ops = &jump_ops, },
{ .name = "je", .ops = &jump_ops, },
{ .name = "jecxz", .ops = &jump_ops, },
{ .name = "jg", .ops = &jump_ops, },
{ .name = "jge", .ops = &jump_ops, },
{ .name = "jl", .ops = &jump_ops, },
{ .name = "jle", .ops = &jump_ops, },
{ .name = "jmp", .ops = &jump_ops, },
{ .name = "jmpq", .ops = &jump_ops, },
{ .name = "jna", .ops = &jump_ops, },
{ .name = "jnae", .ops = &jump_ops, },
{ .name = "jnb", .ops = &jump_ops, },
{ .name = "jnbe", .ops = &jump_ops, },
{ .name = "jnc", .ops = &jump_ops, },
{ .name = "jne", .ops = &jump_ops, },
{ .name = "jng", .ops = &jump_ops, },
{ .name = "jnge", .ops = &jump_ops, },
{ .name = "jnl", .ops = &jump_ops, },
{ .name = "jnle", .ops = &jump_ops, },
{ .name = "jno", .ops = &jump_ops, },
{ .name = "jnp", .ops = &jump_ops, },
{ .name = "jns", .ops = &jump_ops, },
{ .name = "jnz", .ops = &jump_ops, },
{ .name = "jo", .ops = &jump_ops, },
{ .name = "jp", .ops = &jump_ops, },
{ .name = "jpe", .ops = &jump_ops, },
{ .name = "jpo", .ops = &jump_ops, },
{ .name = "jrcxz", .ops = &jump_ops, },
{ .name = "js", .ops = &jump_ops, },
{ .name = "jz", .ops = &jump_ops, },
};
static int ins__cmp(const void *name, const void *insp)
{
const struct ins *ins = insp;
return strcmp(name, ins->name);
}
static struct ins *ins__find(const char *name)
{
const int nmemb = ARRAY_SIZE(instructions);
return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);
}
int symbol__annotate_init(struct map *map __used, struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
@ -28,7 +171,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
int symbol__alloc_hist(struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
const size_t size = sym->end - sym->start + 1;
const size_t size = symbol__size(sym);
size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64));
notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
@ -78,36 +221,87 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
return 0;
}
static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
static void disasm_line__init_ins(struct disasm_line *dl)
{
struct objdump_line *self = malloc(sizeof(*self) + privsize);
dl->ins = ins__find(dl->name);
if (self != NULL) {
self->offset = offset;
self->line = strdup(line);
if (self->line == NULL)
if (dl->ins == NULL)
return;
if (!dl->ins->ops)
return;
if (dl->ins->ops->parse)
dl->ins->ops->parse(&dl->ops);
}
static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
{
struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
if (dl != NULL) {
dl->offset = offset;
dl->line = strdup(line);
if (dl->line == NULL)
goto out_delete;
if (offset != -1) {
char *name = dl->line, tmp;
while (isspace(name[0]))
++name;
if (name[0] == '\0')
goto out_delete;
dl->ops.raw = name + 1;
while (dl->ops.raw[0] != '\0' &&
!isspace(dl->ops.raw[0]))
++dl->ops.raw;
tmp = dl->ops.raw[0];
dl->ops.raw[0] = '\0';
dl->name = strdup(name);
if (dl->name == NULL)
goto out_free_line;
dl->ops.raw[0] = tmp;
if (dl->ops.raw[0] != '\0') {
dl->ops.raw++;
while (isspace(dl->ops.raw[0]))
++dl->ops.raw;
}
disasm_line__init_ins(dl);
}
}
return self;
return dl;
out_free_line:
free(dl->line);
out_delete:
free(self);
free(dl);
return NULL;
}
void objdump_line__free(struct objdump_line *self)
void disasm_line__free(struct disasm_line *dl)
{
free(self->line);
free(self);
free(dl->line);
free(dl->name);
free(dl->ops.target.name);
free(dl);
}
static void objdump__add_line(struct list_head *head, struct objdump_line *line)
static void disasm__add(struct list_head *head, struct disasm_line *line)
{
list_add_tail(&line->node, head);
}
struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
struct objdump_line *pos)
struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos)
{
list_for_each_entry_continue(pos, head, node)
if (pos->offset >= 0)
@ -116,15 +310,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
return NULL;
}
static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
u64 start, int evidx, u64 len, int min_pcnt,
int printed, int max_lines,
struct objdump_line *queue)
static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start,
int evidx, u64 len, int min_pcnt, int printed,
int max_lines, struct disasm_line *queue)
{
static const char *prev_line;
static const char *prev_color;
if (oline->offset != -1) {
if (dl->offset != -1) {
const char *path = NULL;
unsigned int hits = 0;
double percent = 0.0;
@ -132,11 +325,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
struct annotation *notes = symbol__annotation(sym);
struct source_line *src_line = notes->src->lines;
struct sym_hist *h = annotation__histogram(notes, evidx);
s64 offset = oline->offset;
s64 offset = dl->offset;
const u64 addr = start + offset;
struct objdump_line *next;
struct disasm_line *next;
next = objdump__get_next_ip_line(&notes->src->source, oline);
next = disasm__get_next_ip_line(&notes->src->source, dl);
while (offset < (s64)len &&
(next == NULL || offset < next->offset)) {
@ -161,9 +354,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
if (queue != NULL) {
list_for_each_entry_from(queue, &notes->src->source, node) {
if (queue == oline)
if (queue == dl)
break;
objdump_line__print(queue, sym, start, evidx, len,
disasm_line__print(queue, sym, start, evidx, len,
0, 0, 1, NULL);
}
}
@ -187,17 +380,17 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym,
color_fprintf(stdout, color, " %7.2f", percent);
printf(" : ");
color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr);
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line);
color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line);
} else if (max_lines && printed >= max_lines)
return 1;
else {
if (queue)
return -1;
if (!*oline->line)
if (!*dl->line)
printf(" :\n");
else
printf(" : %s\n", oline->line);
printf(" : %s\n", dl->line);
}
return 0;
@ -207,7 +400,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
FILE *file, size_t privsize)
{
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *objdump_line;
struct disasm_line *dl;
char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
size_t line_len;
s64 line_ip, offset = -1;
@ -258,13 +451,13 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
parsed_line = tmp2 + 1;
}
objdump_line = objdump_line__new(offset, parsed_line, privsize);
dl = disasm_line__new(offset, parsed_line, privsize);
free(line);
if (objdump_line == NULL)
if (dl == NULL)
return -1;
objdump__add_line(&notes->src->source, objdump_line);
disasm__add(&notes->src->source, dl);
return 0;
}
@ -487,7 +680,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx)
{
struct annotation *notes = symbol__annotation(sym);
struct sym_hist *h = annotation__histogram(notes, evidx);
u64 len = sym->end - sym->start, offset;
u64 len = symbol__size(sym), offset;
for (offset = 0; offset < len; ++offset)
if (h->addr[offset] != 0)
@ -503,7 +696,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
struct dso *dso = map->dso;
const char *filename = dso->long_name, *d_filename;
struct annotation *notes = symbol__annotation(sym);
struct objdump_line *pos, *queue = NULL;
struct disasm_line *pos, *queue = NULL;
u64 start = map__rip_2objdump(map, sym->start);
int printed = 2, queue_len = 0;
int more = 0;
@ -514,7 +707,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
else
d_filename = basename(filename);
len = sym->end - sym->start;
len = symbol__size(sym);
printf(" Percent | Source code & Disassembly of %s\n", d_filename);
printf("------------------------------------------------\n");
@ -528,7 +721,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
queue_len = 0;
}
switch (objdump_line__print(pos, sym, start, evidx, len,
switch (disasm_line__print(pos, sym, start, evidx, len,
min_pcnt, printed, max_lines,
queue)) {
case 0:
@ -574,7 +767,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
{
struct annotation *notes = symbol__annotation(sym);
struct sym_hist *h = annotation__histogram(notes, evidx);
int len = sym->end - sym->start, offset;
int len = symbol__size(sym), offset;
h->sum = 0;
for (offset = 0; offset < len; ++offset) {
@ -583,16 +776,44 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx)
}
}
void objdump_line_list__purge(struct list_head *head)
void disasm__purge(struct list_head *head)
{
struct objdump_line *pos, *n;
struct disasm_line *pos, *n;
list_for_each_entry_safe(pos, n, head, node) {
list_del(&pos->node);
objdump_line__free(pos);
disasm_line__free(pos);
}
}
static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
{
size_t printed;
if (dl->offset == -1)
return fprintf(fp, "%s\n", dl->line);
printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);
if (dl->ops.raw[0] != '\0') {
printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
dl->ops.raw);
}
return printed + fprintf(fp, "\n");
}
size_t disasm__fprintf(struct list_head *head, FILE *fp)
{
struct disasm_line *pos;
size_t printed = 0;
list_for_each_entry(pos, head, node)
printed += disasm_line__fprintf(pos, fp);
return printed;
}
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
bool print_lines, bool full_paths, int min_pcnt,
int max_lines)
@ -605,7 +826,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
if (symbol__annotate(sym, map, 0) < 0)
return -1;
len = sym->end - sym->start;
len = symbol__size(sym);
if (print_lines) {
symbol__get_source_line(sym, map, evidx, &source_line,
@ -618,7 +839,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
if (print_lines)
symbol__free_source_line(sym, len);
objdump_line_list__purge(&symbol__annotation(sym)->src->source);
disasm__purge(&symbol__annotation(sym)->src->source);
return 0;
}

View File

@ -2,20 +2,54 @@
#define __PERF_ANNOTATE_H
#include <stdbool.h>
#include <stdint.h>
#include "types.h"
#include "symbol.h"
#include <linux/list.h>
#include <linux/rbtree.h>
struct objdump_line {
struct list_head node;
s64 offset;
char *line;
struct ins;
struct ins_operands {
char *raw;
struct {
char *name;
u64 offset;
u64 addr;
} target;
};
void objdump_line__free(struct objdump_line *self);
struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
struct objdump_line *pos);
struct ins_ops {
int (*parse)(struct ins_operands *ops);
int (*scnprintf)(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops, bool addrs);
};
struct ins {
const char *name;
struct ins_ops *ops;
};
bool ins__is_jump(const struct ins *ins);
bool ins__is_call(const struct ins *ins);
struct disasm_line {
struct list_head node;
s64 offset;
char *line;
char *name;
struct ins *ins;
struct ins_operands ops;
};
static inline bool disasm_line__has_offset(const struct disasm_line *dl)
{
return dl->ops.target.offset != UINT64_MAX;
}
void disasm_line__free(struct disasm_line *dl);
struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos);
size_t disasm__fprintf(struct list_head *head, FILE *fp);
struct sym_hist {
u64 sum;
@ -32,7 +66,7 @@ struct source_line {
*
* @histogram: Array of addr hit histograms per event being monitored
* @lines: If 'print_lines' is specified, per source code line percentages
* @source: source parsed from objdump -dS
* @source: source parsed from a disassembler like objdump -dS
*
* lines is allocated, percentages calculated and all sorted by percentage
* when the annotation is about to be presented, so the percentages are for
@ -82,7 +116,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
int context);
void symbol__annotate_zero_histogram(struct symbol *sym, int evidx);
void symbol__annotate_decay_histogram(struct symbol *sym, int evidx);
void objdump_line_list__purge(struct list_head *head);
void disasm__purge(struct list_head *head);
int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
bool print_lines, bool full_paths, int min_pcnt,

View File

@ -65,6 +65,11 @@ struct symbol {
void symbol__delete(struct symbol *sym);
static inline size_t symbol__size(const struct symbol *sym)
{
return sym->end - sym->start + 1;
}
struct strlist;
struct symbol_conf {

View File

@ -148,3 +148,13 @@ int readn(int fd, void *buf, size_t n)
return buf - buf_start;
}
size_t hex_width(u64 v)
{
size_t n = 1;
while ((v >>= 4))
++n;
return n;
}

View File

@ -265,4 +265,6 @@ bool is_power_of_2(unsigned long n)
return (n != 0 && ((n & (n - 1)) == 0));
}
size_t hex_width(u64 v);
#endif