Arjan & Linus Annotation Edition

More like Linus', but hey at least an attempt at implementing a suggestion by
 Arjan made this time around!
 
 . Fix indirect calls beautifier, reported by Linus.
 
 . Use the objdump comments to nuke specificities about how access to a well
   know variable is encoded, suggested by Linus.
 
 . Show the number of places that jump to a target, requested by Arjan.
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJPrr3pAAoJENZQFvNTUqpAUZUP/jbBfYzJPqFWNvpPrTIeJXAP
 WVX8bOHCjSQId9dWrc/Nlf/25JUbZOnDE8FV9weBGjJfgc6Y7awfuVtFW/QNjZ7w
 2Hggn5242J+CSJ8jwLFFV3gJH3jQUE7m6egkkCQBdPI/ruarO01FKfydHvkTeSNt
 f418A+S2Bx6oEjZa+DO8g/bhJ9d4FbS5hqubWvqC6iTf5Z9+6fTNnR0dJG1GTdy6
 F8x7SXNG3AXKHNbqVzdXExtOYS2bum9yQNI/O4sjUgSoOdg7GzPYom08WglxnOkO
 K8BXj8stbEW3mZJANoNxUzR7xvfvcaBsEWmz8gF/LkZa51VnbtSsrqKkKZU8LuJ6
 MRBW9ue2BUrtLb+U9sfFSs1toRT8WfIetK7OXJIow6m+5TZBepWhZt+Gx0c1LCrt
 AOGvGSq1u0troomv52JYqNukI+w39quWBPk4C7/kKGY3SRjrriKyCyo1spJxH5en
 UVFQ8vEHu4gEUspXphKqctT4RAlBO2YdeiJv7t5h2TJgz9wnUwjnvrB1xpIvc6Yo
 2gNjSMReNupT5mL8peh0eYS4+4Dcx14Layl3JeYPZ+azIf2mJrfpGucEv/s1b3yr
 XK2HAQboNF/eZSsuJsmbvq/BIOHaUjFMNqLw8ORwK0WnPNNO0iAuYnu45KcfXhTx
 I+9JzyG3WYJtZp+R7Zqg
 =cE1R
 -----END PGP SIGNATURE-----

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

Arjan & Linus Annotation Edition

 - Fix indirect calls beautifier, reported by Linus.

 - Use the objdump comments to nuke specificities about how access to a well
   know variable is encoded, suggested by Linus.

 - Show the number of places that jump to a target, requested by Arjan.

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-05-14 10:45:01 +02:00
commit 0c5a0f96e8
3 changed files with 369 additions and 43 deletions

View File

@ -16,7 +16,7 @@ struct browser_disasm_line {
double percent;
u32 idx;
int idx_asm;
bool jump_target;
int jump_sources;
};
struct annotate_browser {
@ -28,11 +28,16 @@ struct annotate_browser {
u64 start;
int nr_asm_entries;
int nr_entries;
int max_jump_sources;
int nr_jumps;
bool hide_src_code;
bool use_offset;
bool jump_arrows;
bool show_nr_jumps;
bool searching_backwards;
u8 addr_width;
u8 jumps_width;
u8 target_width;
u8 min_addr_width;
u8 max_addr_width;
char search_bf[128];
@ -55,6 +60,25 @@ static bool disasm_line__filter(struct ui_browser *browser, void *entry)
return false;
}
static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
int nr, bool current)
{
if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
return HE_COLORSET_SELECTED;
if (nr == browser->max_jump_sources)
return HE_COLORSET_TOP;
if (nr > 1)
return HE_COLORSET_MEDIUM;
return HE_COLORSET_NORMAL;
}
static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
int nr, bool current)
{
int color = annotate_browser__jumps_percent_color(browser, nr, current);
return ui_browser__set_color(&browser->b, color);
}
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
@ -98,9 +122,20 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if (!ab->use_offset) {
printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
} else {
if (bdl->jump_target) {
if (bdl->jump_sources) {
if (ab->show_nr_jumps) {
int prev;
printed = scnprintf(bf, sizeof(bf), "%*d ",
ab->jumps_width,
bdl->jump_sources);
prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
current_entry);
slsmg_write_nstring(bf, printed);
ui_browser__set_color(self, prev);
}
printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
ab->addr_width, addr);
ab->target_width, addr);
} else {
printed = scnprintf(bf, sizeof(bf), "%*s ",
ab->addr_width, " ");
@ -546,10 +581,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
struct rb_node *nd = NULL;
struct map_symbol *ms = self->b.priv;
struct symbol *sym = ms->sym;
const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
"H: Hottest line, ->/ENTER: Line action, "
"O: Offset view, "
"S: Source view";
const char *help = "Press 'h' for help on key bindings";
int key;
if (ui_browser__show(&self->b, sym->name, help) < 0)
@ -602,26 +634,47 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
else
nd = self->curr_hot;
break;
case 'H':
case K_F1:
case 'h':
ui_browser__help_window(&self->b,
"UP/DOWN/PGUP\n"
"PGDN/SPACE Navigate\n"
"q/ESC/CTRL+C Exit\n\n"
"-> Go to target\n"
"<- Exit\n"
"h Cycle thru hottest instructions\n"
"j Toggle showing jump to target arrows\n"
"J Toggle showing number of jump sources on targets\n"
"n Search next string\n"
"o Toggle disassembler output/simplified view\n"
"s Toggle source code view\n"
"/ Search string\n"
"? Search previous string\n");
continue;
case 'H':
nd = self->curr_hot;
break;
case 'S':
case 's':
if (annotate_browser__toggle_source(self))
ui_helpline__puts(help);
continue;
case 'O':
case 'o':
self->use_offset = !self->use_offset;
if (self->use_offset)
self->addr_width = self->min_addr_width;
self->target_width = self->min_addr_width;
else
self->addr_width = self->max_addr_width;
self->target_width = self->max_addr_width;
update_addr_width:
self->addr_width = self->target_width;
if (self->show_nr_jumps)
self->addr_width += self->jumps_width + 1;
continue;
case 'j':
self->jump_arrows = !self->jump_arrows;
continue;
case 'J':
self->show_nr_jumps = !self->show_nr_jumps;
goto update_addr_width;
case '/':
if (annotate_browser__search(self, delay_secs)) {
show_help:
@ -707,11 +760,23 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
continue;
bdlt = disasm_line__browser(dlt);
bdlt->jump_target = true;
if (++bdlt->jump_sources > browser->max_jump_sources)
browser->max_jump_sources = bdlt->jump_sources;
++browser->nr_jumps;
}
}
static inline int width_jumps(int n)
{
if (n >= 100)
return 5;
if (n / 10)
return 2;
return 1;
}
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
void(*timer)(void *arg), void *arg,
int delay_secs)
@ -784,8 +849,9 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
annotate_browser__mark_jump_targets(&browser, size);
browser.addr_width = browser.min_addr_width = hex_width(size);
browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
browser.max_addr_width = hex_width(sym->end);
browser.jumps_width = width_jumps(browser.max_jump_sources);
browser.b.nr_entries = browser.nr_entries;
browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */

View File

@ -18,6 +18,17 @@
const char *disassembler_style;
static struct ins *ins__find(const char *name);
static int disasm_line__parse(char *line, char **namep, char **rawp);
static void ins__delete(struct ins_operands *ops)
{
free(ops->source.raw);
free(ops->source.name);
free(ops->target.raw);
free(ops->target.name);
}
static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
@ -56,6 +67,12 @@ static int call__parse(struct ins_operands *ops)
return ops->target.name == NULL ? -1 : 0;
indirect_call:
tok = strchr(endptr, '(');
if (tok != NULL) {
ops->target.addr = 0;
return 0;
}
tok = strchr(endptr, '*');
if (tok == NULL)
return -1;
@ -70,6 +87,9 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size,
if (ops->target.name)
return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name);
if (ops->target.addr == 0)
return ins__raw_scnprintf(ins, bf, size, ops);
return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
}
@ -113,6 +133,185 @@ bool ins__is_jump(const struct ins *ins)
return ins->ops == &jump_ops;
}
static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
{
char *endptr, *name, *t;
if (strstr(raw, "(%rip)") == NULL)
return 0;
*addrp = strtoull(comment, &endptr, 16);
name = strchr(endptr, '<');
if (name == NULL)
return -1;
name++;
t = strchr(name, '>');
if (t == NULL)
return 0;
*t = '\0';
*namep = strdup(name);
*t = '>';
return 0;
}
static int lock__parse(struct ins_operands *ops)
{
char *name;
ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
if (ops->locked.ops == NULL)
return 0;
if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
goto out_free_ops;
ops->locked.ins = ins__find(name);
if (ops->locked.ins == NULL)
goto out_free_ops;
if (!ops->locked.ins->ops)
return 0;
if (ops->locked.ins->ops->parse)
ops->locked.ins->ops->parse(ops->locked.ops);
return 0;
out_free_ops:
free(ops->locked.ops);
ops->locked.ops = NULL;
return 0;
}
static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
int printed;
if (ops->locked.ins == NULL)
return ins__raw_scnprintf(ins, bf, size, ops);
printed = scnprintf(bf, size, "%-6.6s ", ins->name);
return printed + ins__scnprintf(ops->locked.ins, bf + printed,
size - printed, ops->locked.ops);
}
static void lock__delete(struct ins_operands *ops)
{
free(ops->locked.ops);
free(ops->target.raw);
free(ops->target.name);
}
static struct ins_ops lock_ops = {
.free = lock__delete,
.parse = lock__parse,
.scnprintf = lock__scnprintf,
};
static int mov__parse(struct ins_operands *ops)
{
char *s = strchr(ops->raw, ','), *target, *comment, prev;
if (s == NULL)
return -1;
*s = '\0';
ops->source.raw = strdup(ops->raw);
*s = ',';
if (ops->source.raw == NULL)
return -1;
target = ++s;
while (s[0] != '\0' && !isspace(s[0]))
++s;
prev = *s;
*s = '\0';
ops->target.raw = strdup(target);
*s = prev;
if (ops->target.raw == NULL)
goto out_free_source;
comment = strchr(s, '#');
if (comment == NULL)
return 0;
while (comment[0] != '\0' && isspace(comment[0]))
++comment;
comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name);
comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);
return 0;
out_free_source:
free(ops->source.raw);
ops->source.raw = NULL;
return -1;
}
static int mov__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
return scnprintf(bf, size, "%-6.6s %s,%s", ins->name,
ops->source.name ?: ops->source.raw,
ops->target.name ?: ops->target.raw);
}
static struct ins_ops mov_ops = {
.parse = mov__parse,
.scnprintf = mov__scnprintf,
};
static int dec__parse(struct ins_operands *ops)
{
char *target, *comment, *s, prev;
target = s = ops->raw;
while (s[0] != '\0' && !isspace(s[0]))
++s;
prev = *s;
*s = '\0';
ops->target.raw = strdup(target);
*s = prev;
if (ops->target.raw == NULL)
return -1;
comment = strchr(s, '#');
if (comment == NULL)
return 0;
while (comment[0] != '\0' && isspace(comment[0]))
++comment;
comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name);
return 0;
}
static int dec__scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops)
{
return scnprintf(bf, size, "%-6.6s %s", ins->name,
ops->target.name ?: ops->target.raw);
}
static struct ins_ops dec_ops = {
.parse = dec__parse,
.scnprintf = dec__scnprintf,
};
static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size,
struct ins_operands *ops __used)
{
@ -127,8 +326,25 @@ static struct ins_ops nop_ops = {
* Must be sorted by name!
*/
static struct ins instructions[] = {
{ .name = "add", .ops = &mov_ops, },
{ .name = "addl", .ops = &mov_ops, },
{ .name = "addq", .ops = &mov_ops, },
{ .name = "addw", .ops = &mov_ops, },
{ .name = "and", .ops = &mov_ops, },
{ .name = "bts", .ops = &mov_ops, },
{ .name = "call", .ops = &call_ops, },
{ .name = "callq", .ops = &call_ops, },
{ .name = "cmp", .ops = &mov_ops, },
{ .name = "cmpb", .ops = &mov_ops, },
{ .name = "cmpl", .ops = &mov_ops, },
{ .name = "cmpq", .ops = &mov_ops, },
{ .name = "cmpw", .ops = &mov_ops, },
{ .name = "cmpxch", .ops = &mov_ops, },
{ .name = "dec", .ops = &dec_ops, },
{ .name = "decl", .ops = &dec_ops, },
{ .name = "imul", .ops = &mov_ops, },
{ .name = "inc", .ops = &dec_ops, },
{ .name = "incl", .ops = &dec_ops, },
{ .name = "ja", .ops = &jump_ops, },
{ .name = "jae", .ops = &jump_ops, },
{ .name = "jb", .ops = &jump_ops, },
@ -164,9 +380,25 @@ static struct ins instructions[] = {
{ .name = "jrcxz", .ops = &jump_ops, },
{ .name = "js", .ops = &jump_ops, },
{ .name = "jz", .ops = &jump_ops, },
{ .name = "lea", .ops = &mov_ops, },
{ .name = "lock", .ops = &lock_ops, },
{ .name = "mov", .ops = &mov_ops, },
{ .name = "movb", .ops = &mov_ops, },
{ .name = "movdqa",.ops = &mov_ops, },
{ .name = "movl", .ops = &mov_ops, },
{ .name = "movq", .ops = &mov_ops, },
{ .name = "movslq", .ops = &mov_ops, },
{ .name = "movzbl", .ops = &mov_ops, },
{ .name = "movzwl", .ops = &mov_ops, },
{ .name = "nop", .ops = &nop_ops, },
{ .name = "nopl", .ops = &nop_ops, },
{ .name = "nopw", .ops = &nop_ops, },
{ .name = "or", .ops = &mov_ops, },
{ .name = "orl", .ops = &mov_ops, },
{ .name = "test", .ops = &mov_ops, },
{ .name = "testb", .ops = &mov_ops, },
{ .name = "testl", .ops = &mov_ops, },
{ .name = "xadd", .ops = &mov_ops, },
};
static int ins__cmp(const void *name, const void *insp)
@ -257,6 +489,44 @@ static void disasm_line__init_ins(struct disasm_line *dl)
dl->ins->ops->parse(&dl->ops);
}
static int disasm_line__parse(char *line, char **namep, char **rawp)
{
char *name = line, tmp;
while (isspace(name[0]))
++name;
if (name[0] == '\0')
return -1;
*rawp = name + 1;
while ((*rawp)[0] != '\0' && !isspace((*rawp)[0]))
++*rawp;
tmp = (*rawp)[0];
(*rawp)[0] = '\0';
*namep = strdup(name);
if (*namep == NULL)
goto out_free_name;
(*rawp)[0] = tmp;
if ((*rawp)[0] != '\0') {
(*rawp)++;
while (isspace((*rawp)[0]))
++(*rawp);
}
return 0;
out_free_name:
free(*namep);
*namep = NULL;
return -1;
}
static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
{
struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
@ -268,35 +538,9 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs
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)
if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
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);
}
}
@ -314,7 +558,10 @@ void disasm_line__free(struct disasm_line *dl)
{
free(dl->line);
free(dl->name);
free(dl->ops.target.name);
if (dl->ins && dl->ins->ops->free)
dl->ins->ops->free(&dl->ops);
else
ins__delete(&dl->ops);
free(dl);
}

View File

@ -13,13 +13,26 @@ struct ins;
struct ins_operands {
char *raw;
struct {
char *raw;
char *name;
u64 offset;
u64 addr;
u64 offset;
} target;
union {
struct {
char *raw;
char *name;
u64 addr;
} source;
struct {
struct ins *ins;
struct ins_operands *ops;
} locked;
};
};
struct ins_ops {
void (*free)(struct ins_operands *ops);
int (*parse)(struct ins_operands *ops);
int (*scnprintf)(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops);