diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 06367c1df720..6e0ef79be169 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -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 = ¬es->src->source, browser.b.width += 18; /* Percentage */ diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 6b4146b40a20..8069dfb5ba77 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -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); } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index bb0a9f27165b..78a5692dd718 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -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);