perf annotate: Resolve symbols using objdump comment for single op ins
[firefly-linux-kernel-4.4.55.git] / tools / perf / util / annotate.c
index a72585ab52e80cd9ff3cf97e5504e66e6d22fdf7..a6109dc3a81eaac12fe27dded8e7a0e9046d41ac 100644 (file)
 
 const char     *disassembler_style;
 
+static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
+                             struct ins_operands *ops)
+{
+       return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw);
+}
+
+int ins__scnprintf(struct ins *ins, char *bf, size_t size,
+                 struct ins_operands *ops)
+{
+       if (ins->ops->scnprintf)
+               return ins->ops->scnprintf(ins, bf, size, ops);
+
+       return ins__raw_scnprintf(ins, bf, size, ops);
+}
+
+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) {
+               ops->target.addr = 0;
+               return 0;
+       }
+
+       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)
+{
+       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);
+}
+
+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)
+{
+       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;
+}
+
+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 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)
+{
+       return scnprintf(bf, size, "%-6.6s", "nop");
+}
+
+static struct ins_ops nop_ops = {
+       .scnprintf = nop__scnprintf,
+};
+
+/*
+ * 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 = "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, },
+       { .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, },
+       { .name = "lea",   .ops  = &mov_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, },
+};
+
+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 +356,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,6 +406,20 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
        return 0;
 }
 
+static void disasm_line__init_ins(struct disasm_line *dl)
+{
+       dl->ins = ins__find(dl->name);
+
+       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);
@@ -97,26 +439,28 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs
                        if (name[0] == '\0')
                                goto out_delete;
 
-                       dl->operands = name + 1;
+                       dl->ops.raw = name + 1;
 
-                       while (dl->operands[0] != '\0' &&
-                              !isspace(dl->operands[0]))
-                               ++dl->operands;
+                       while (dl->ops.raw[0] != '\0' &&
+                              !isspace(dl->ops.raw[0]))
+                               ++dl->ops.raw;
 
-                       tmp = dl->operands[0];
-                       dl->operands[0] = '\0';
+                       tmp = dl->ops.raw[0];
+                       dl->ops.raw[0] = '\0';
                        dl->name = strdup(name);
 
                        if (dl->name == NULL)
                                goto out_free_line;
 
-                       dl->operands[0] = tmp;
+                       dl->ops.raw[0] = tmp;
 
-                       if (dl->operands[0] != '\0') {
-                               dl->operands++;
-                               while (isspace(dl->operands[0]))
-                                       ++dl->operands;
+                       if (dl->ops.raw[0] != '\0') {
+                               dl->ops.raw++;
+                               while (isspace(dl->ops.raw[0]))
+                                       ++dl->ops.raw;
                        }
+
+                       disasm_line__init_ins(dl);
                }
        }
 
@@ -133,9 +477,21 @@ void disasm_line__free(struct disasm_line *dl)
 {
        free(dl->line);
        free(dl->name);
+       free(dl->ops.source.raw);
+       free(dl->ops.source.name);
+       free(dl->ops.target.raw);
+       free(dl->ops.target.name);
        free(dl);
 }
 
+int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw)
+{
+       if (raw || !dl->ins)
+               return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw);
+
+       return ins__scnprintf(dl->ins, bf, size, &dl->ops);
+}
+
 static void disasm__add(struct list_head *head, struct disasm_line *line)
 {
        list_add_tail(&line->node, head);
@@ -520,7 +876,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)
@@ -547,7 +903,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");
@@ -607,7 +963,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) {
@@ -635,9 +991,9 @@ static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
 
        printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);
 
-       if (dl->operands[0] != '\0') {
+       if (dl->ops.raw[0] != '\0') {
                printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
-                                  dl->operands);
+                                  dl->ops.raw);
        }
 
        return printed + fprintf(fp, "\n");
@@ -666,7 +1022,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,