Commit 0c5a0f96 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 5dcefda0 54e7a4e8
...@@ -16,7 +16,7 @@ struct browser_disasm_line { ...@@ -16,7 +16,7 @@ struct browser_disasm_line {
double percent; double percent;
u32 idx; u32 idx;
int idx_asm; int idx_asm;
bool jump_target; int jump_sources;
}; };
struct annotate_browser { struct annotate_browser {
...@@ -28,11 +28,16 @@ struct annotate_browser { ...@@ -28,11 +28,16 @@ struct annotate_browser {
u64 start; u64 start;
int nr_asm_entries; int nr_asm_entries;
int nr_entries; int nr_entries;
int max_jump_sources;
int nr_jumps;
bool hide_src_code; bool hide_src_code;
bool use_offset; bool use_offset;
bool jump_arrows; bool jump_arrows;
bool show_nr_jumps;
bool searching_backwards; bool searching_backwards;
u8 addr_width; u8 addr_width;
u8 jumps_width;
u8 target_width;
u8 min_addr_width; u8 min_addr_width;
u8 max_addr_width; u8 max_addr_width;
char search_bf[128]; char search_bf[128];
...@@ -55,6 +60,25 @@ static bool disasm_line__filter(struct ui_browser *browser, void *entry) ...@@ -55,6 +60,25 @@ static bool disasm_line__filter(struct ui_browser *browser, void *entry)
return false; 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) 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 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 ...@@ -98,9 +122,20 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
if (!ab->use_offset) { if (!ab->use_offset) {
printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
} else { } 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 ": ", printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
ab->addr_width, addr); ab->target_width, addr);
} else { } else {
printed = scnprintf(bf, sizeof(bf), "%*s ", printed = scnprintf(bf, sizeof(bf), "%*s ",
ab->addr_width, " "); ab->addr_width, " ");
...@@ -546,10 +581,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, ...@@ -546,10 +581,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
struct rb_node *nd = NULL; struct rb_node *nd = NULL;
struct map_symbol *ms = self->b.priv; struct map_symbol *ms = self->b.priv;
struct symbol *sym = ms->sym; struct symbol *sym = ms->sym;
const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, " const char *help = "Press 'h' for help on key bindings";
"H: Hottest line, ->/ENTER: Line action, "
"O: Offset view, "
"S: Source view";
int key; int key;
if (ui_browser__show(&self->b, sym->name, help) < 0) 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, ...@@ -602,26 +634,47 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
else else
nd = self->curr_hot; nd = self->curr_hot;
break; break;
case 'H': case K_F1:
case 'h': 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; nd = self->curr_hot;
break; break;
case 'S':
case 's': case 's':
if (annotate_browser__toggle_source(self)) if (annotate_browser__toggle_source(self))
ui_helpline__puts(help); ui_helpline__puts(help);
continue; continue;
case 'O':
case 'o': case 'o':
self->use_offset = !self->use_offset; self->use_offset = !self->use_offset;
if (self->use_offset) if (self->use_offset)
self->addr_width = self->min_addr_width; self->target_width = self->min_addr_width;
else 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; continue;
case 'j': case 'j':
self->jump_arrows = !self->jump_arrows; self->jump_arrows = !self->jump_arrows;
continue; continue;
case 'J':
self->show_nr_jumps = !self->show_nr_jumps;
goto update_addr_width;
case '/': case '/':
if (annotate_browser__search(self, delay_secs)) { if (annotate_browser__search(self, delay_secs)) {
show_help: show_help:
...@@ -707,11 +760,23 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser ...@@ -707,11 +760,23 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
continue; continue;
bdlt = disasm_line__browser(dlt); 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, int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
void(*timer)(void *arg), void *arg, void(*timer)(void *arg), void *arg,
int delay_secs) int delay_secs)
...@@ -784,8 +849,9 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, ...@@ -784,8 +849,9 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
annotate_browser__mark_jump_targets(&browser, size); 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.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.nr_entries = browser.nr_entries;
browser.b.entries = &notes->src->source, browser.b.entries = &notes->src->source,
browser.b.width += 18; /* Percentage */ browser.b.width += 18; /* Percentage */
......
...@@ -18,6 +18,17 @@ ...@@ -18,6 +18,17 @@
const char *disassembler_style; 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, static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops) struct ins_operands *ops)
{ {
...@@ -56,6 +67,12 @@ static int call__parse(struct ins_operands *ops) ...@@ -56,6 +67,12 @@ static int call__parse(struct ins_operands *ops)
return ops->target.name == NULL ? -1 : 0; return ops->target.name == NULL ? -1 : 0;
indirect_call: indirect_call:
tok = strchr(endptr, '(');
if (tok != NULL) {
ops->target.addr = 0;
return 0;
}
tok = strchr(endptr, '*'); tok = strchr(endptr, '*');
if (tok == NULL) if (tok == NULL)
return -1; return -1;
...@@ -70,6 +87,9 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size, ...@@ -70,6 +87,9 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size,
if (ops->target.name) if (ops->target.name)
return scnprintf(bf, size, "%-6.6s %s", ins->name, 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); return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr);
} }
...@@ -113,6 +133,185 @@ bool ins__is_jump(const struct ins *ins) ...@@ -113,6 +133,185 @@ bool ins__is_jump(const struct ins *ins)
return ins->ops == &jump_ops; 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, static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size,
struct ins_operands *ops __used) struct ins_operands *ops __used)
{ {
...@@ -127,8 +326,25 @@ static struct ins_ops nop_ops = { ...@@ -127,8 +326,25 @@ static struct ins_ops nop_ops = {
* Must be sorted by name! * Must be sorted by name!
*/ */
static struct ins instructions[] = { 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 = "call", .ops = &call_ops, },
{ .name = "callq", .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 = "ja", .ops = &jump_ops, },
{ .name = "jae", .ops = &jump_ops, }, { .name = "jae", .ops = &jump_ops, },
{ .name = "jb", .ops = &jump_ops, }, { .name = "jb", .ops = &jump_ops, },
...@@ -164,9 +380,25 @@ static struct ins instructions[] = { ...@@ -164,9 +380,25 @@ static struct ins instructions[] = {
{ .name = "jrcxz", .ops = &jump_ops, }, { .name = "jrcxz", .ops = &jump_ops, },
{ .name = "js", .ops = &jump_ops, }, { .name = "js", .ops = &jump_ops, },
{ .name = "jz", .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 = "nop", .ops = &nop_ops, },
{ .name = "nopl", .ops = &nop_ops, }, { .name = "nopl", .ops = &nop_ops, },
{ .name = "nopw", .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) static int ins__cmp(const void *name, const void *insp)
...@@ -257,6 +489,44 @@ static void disasm_line__init_ins(struct disasm_line *dl) ...@@ -257,6 +489,44 @@ static void disasm_line__init_ins(struct disasm_line *dl)
dl->ins->ops->parse(&dl->ops); 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) static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize)
{ {
struct disasm_line *dl = zalloc(sizeof(*dl) + 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 ...@@ -268,35 +538,9 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs
goto out_delete; goto out_delete;
if (offset != -1) { if (offset != -1) {
char *name = dl->line, tmp; if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
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; 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); disasm_line__init_ins(dl);
} }
} }
...@@ -314,7 +558,10 @@ void disasm_line__free(struct disasm_line *dl) ...@@ -314,7 +558,10 @@ void disasm_line__free(struct disasm_line *dl)
{ {
free(dl->line); free(dl->line);
free(dl->name); 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); free(dl);
} }
......
...@@ -13,13 +13,26 @@ struct ins; ...@@ -13,13 +13,26 @@ struct ins;
struct ins_operands { struct ins_operands {
char *raw; char *raw;
struct { struct {
char *raw;
char *name; char *name;
u64 offset;
u64 addr; u64 addr;
u64 offset;
} target; } target;
union {
struct {
char *raw;
char *name;
u64 addr;
} source;
struct {
struct ins *ins;
struct ins_operands *ops;
} locked;
};
}; };
struct ins_ops { struct ins_ops {
void (*free)(struct ins_operands *ops);
int (*parse)(struct ins_operands *ops); int (*parse)(struct ins_operands *ops);
int (*scnprintf)(struct ins *ins, char *bf, size_t size, int (*scnprintf)(struct ins *ins, char *bf, size_t size,
struct ins_operands *ops); struct ins_operands *ops);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment