diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
| -rw-r--r-- | tools/perf/util/annotate.c | 428 |
1 files changed, 323 insertions, 105 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d33fe937e6f..809b4c50bea 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -8,12 +8,15 @@ */ #include "util.h" +#include "ui/ui.h" +#include "sort.h" #include "build-id.h" #include "color.h" #include "cache.h" #include "symbol.h" #include "debug.h" #include "annotate.h" +#include "evsel.h" #include <pthread.h> #include <linux/bitops.h> @@ -25,10 +28,10 @@ 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); + zfree(&ops->source.raw); + zfree(&ops->source.name); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, @@ -109,10 +112,10 @@ static int jump__parse(struct ins_operands *ops) { const char *s = strchr(ops->raw, '+'); - ops->target.addr = strtoll(ops->raw, NULL, 16); + ops->target.addr = strtoull(ops->raw, NULL, 16); if (s++ != NULL) - ops->target.offset = strtoll(s, NULL, 16); + ops->target.offset = strtoull(s, NULL, 16); else ops->target.offset = UINT64_MAX; @@ -184,8 +187,7 @@ static int lock__parse(struct ins_operands *ops) return 0; out_free_ops: - free(ops->locked.ops); - ops->locked.ops = NULL; + zfree(&ops->locked.ops); return 0; } @@ -204,9 +206,9 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size, static void lock__delete(struct ins_operands *ops) { - free(ops->locked.ops); - free(ops->target.raw); - free(ops->target.name); + zfree(&ops->locked.ops); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static struct ins_ops lock_ops = { @@ -255,8 +257,7 @@ static int mov__parse(struct ins_operands *ops) return 0; out_free_source: - free(ops->source.raw); - ops->source.raw = NULL; + zfree(&ops->source.raw); return -1; } @@ -463,17 +464,12 @@ void symbol__annotate_zero_histograms(struct symbol *sym) pthread_mutex_unlock(¬es->lock); } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, - int evidx, u64 addr) +static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, + struct annotation *notes, int evidx, u64 addr) { unsigned offset; - struct annotation *notes; struct sym_hist *h; - notes = symbol__annotation(sym); - if (notes->src == NULL) - return -ENOMEM; - pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); if (addr < sym->start || addr > sym->end) @@ -490,6 +486,33 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, return 0; } +static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, + int evidx, u64 addr) +{ + struct annotation *notes; + + if (sym == NULL) + return 0; + + notes = symbol__annotation(sym); + if (notes->src == NULL) { + if (symbol__alloc_hist(sym) < 0) + return -ENOMEM; + } + + return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); +} + +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) +{ + return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); +} + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) +{ + return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); +} + static void disasm_line__init_ins(struct disasm_line *dl) { dl->ins = ins__find(dl->name); @@ -537,8 +560,7 @@ static int disasm_line__parse(char *line, char **namep, char **rawp) return 0; out_free_name: - free(*namep); - *namep = NULL; + zfree(namep); return -1; } @@ -563,7 +585,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs return dl; out_free_line: - free(dl->line); + zfree(&dl->line); out_delete: free(dl); return NULL; @@ -571,8 +593,8 @@ out_delete: void disasm_line__free(struct disasm_line *dl) { - free(dl->line); - free(dl->name); + zfree(&dl->line); + zfree(&dl->name); if (dl->ins && dl->ins->ops->free) dl->ins->ops->free(&dl->ops); else @@ -602,8 +624,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa return NULL; } +double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, + s64 end, const char **path) +{ + struct source_line *src_line = notes->src->lines; + double percent = 0.0; + + if (src_line) { + size_t sizeof_src_line = sizeof(*src_line) + + sizeof(src_line->p) * (src_line->nr_pcnt - 1); + + while (offset < end) { + src_line = (void *)notes->src->lines + + (sizeof_src_line * offset); + + if (*path == NULL) + *path = src_line->path; + + percent += src_line->p[evidx].percent; + offset++; + } + } else { + struct sym_hist *h = annotation__histogram(notes, evidx); + unsigned int hits = 0; + + while (offset < end) + hits += h->addr[offset++]; + + if (h->sum) + percent = 100.0 * hits / h->sum; + } + + return percent; +} + static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, - int evidx, u64 len, int min_pcnt, int printed, + struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, int max_lines, struct disasm_line *queue) { static const char *prev_line; @@ -611,34 +667,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st if (dl->offset != -1) { const char *path = NULL; - unsigned int hits = 0; - double percent = 0.0; + double percent, max_percent = 0.0; + double *ppercents = &percent; + int i, nr_percent = 1; const char *color; struct annotation *notes = symbol__annotation(sym); - struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evidx); s64 offset = dl->offset; const u64 addr = start + offset; struct disasm_line *next; next = disasm__get_next_ip_line(¬es->src->source, dl); - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (src_line) { - if (path == NULL) - path = src_line[offset].path; - percent += src_line[offset].percent; - } else - hits += h->addr[offset]; - - ++offset; + if (perf_evsel__is_group_event(evsel)) { + nr_percent = evsel->nr_members; + ppercents = calloc(nr_percent, sizeof(double)); + if (ppercents == NULL) + return -1; } - if (src_line == NULL && h->sum) - percent = 100.0 * hits / h->sum; + for (i = 0; i < nr_percent; i++) { + percent = disasm__calc_percent(notes, + notes->src->lines ? i : evsel->idx + i, + offset, + next ? next->offset : (s64) len, + &path); + + ppercents[i] = percent; + if (percent > max_percent) + max_percent = percent; + } - if (percent < min_pcnt) + if (max_percent < min_pcnt) return -1; if (max_lines && printed >= max_lines) @@ -648,12 +707,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st list_for_each_entry_from(queue, ¬es->src->source, node) { if (queue == dl) break; - disasm_line__print(queue, sym, start, evidx, len, + disasm_line__print(queue, sym, start, evsel, len, 0, 0, 1, NULL); } } - color = get_percent_color(percent); + color = get_percent_color(max_percent); /* * Also color the filename and line if needed, with @@ -669,25 +728,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st } } - color_fprintf(stdout, color, " %7.2f", percent); + for (i = 0; i < nr_percent; i++) { + percent = ppercents[i]; + color = get_percent_color(percent); + color_fprintf(stdout, color, " %7.2f", percent); + } + printf(" : "); color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); + + if (ppercents != &percent) + free(ppercents); + } else if (max_lines && printed >= max_lines) return 1; else { + int width = 8; + if (queue) return -1; + if (perf_evsel__is_group_event(evsel)) + width *= evsel->nr_members; + if (!*dl->line) - printf(" :\n"); + printf(" %*s:\n", width, " "); else - printf(" : %s\n", dl->line); + printf(" %*s: %s\n", width, " ", dl->line); } return 0; } +/* + * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) + * which looks like following + * + * 0000000000415500 <_init>: + * 415500: sub $0x8,%rsp + * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> + * 41550b: test %rax,%rax + * 41550e: je 415515 <_init+0x15> + * 415510: callq 416e70 <__gmon_start__@plt> + * 415515: add $0x8,%rsp + * 415519: retq + * + * it will be parsed and saved into struct disasm_line as + * <offset> <name> <ops.raw> + * + * The offset will be a relative offset from the start of the symbol and -1 + * means that it's not a disassembly line so should be treated differently. + * The ops.raw part will be parsed further according to type of the instruction. + */ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, size_t privsize) { @@ -737,7 +830,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, end = map__rip_2objdump(map, sym->end); offset = line_ip - start; - if (offset < 0 || (u64)line_ip > end) + if ((u64)line_ip < start || (u64)line_ip > end) offset = -1; else parsed_line = tmp2 + 1; @@ -749,11 +842,51 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, if (dl == NULL) return -1; + if (dl->ops.target.offset == UINT64_MAX) + dl->ops.target.offset = dl->ops.target.addr - + map__rip_2objdump(map, sym->start); + + /* kcore has no symbols, so add the call target name */ + if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { + struct addr_map_symbol target = { + .map = map, + .addr = dl->ops.target.addr, + }; + + if (!map_groups__find_ams(&target, NULL) && + target.sym->start == target.al_addr) + dl->ops.target.name = strdup(target.sym->name); + } + disasm__add(¬es->src->source, dl); return 0; } +static void delete_last_nop(struct symbol *sym) +{ + struct annotation *notes = symbol__annotation(sym); + struct list_head *list = ¬es->src->source; + struct disasm_line *dl; + + while (!list_empty(list)) { + dl = list_entry(list->prev, struct disasm_line, node); + + if (dl->ins && dl->ins->ops) { + if (dl->ins->ops != &nop_ops) + return; + } else { + if (!strstr(dl->line, " nop ") && + !strstr(dl->line, " nopl ") && + !strstr(dl->line, " nopw ")) + return; + } + + list_del(&dl->node); + disasm_line__free(dl); + } +} + int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) { struct dso *dso = map->dso; @@ -763,6 +896,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) FILE *file; int err = 0; char symfs_filename[PATH_MAX]; + struct kcore_extract kce; + bool delete_extract = false; if (filename) { snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", @@ -786,13 +921,14 @@ fallback: * cache, or is just a kallsyms file, well, lets hope that this * DSO is the same as when 'perf record' ran. */ - filename = dso->long_name; + filename = (char *)dso->long_name; snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", symbol_conf.symfs, filename); free_filename = false; } - if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { + if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && + !dso__is_kcore(dso)) { char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; char *build_id_msg = NULL; @@ -823,10 +959,27 @@ fallback: pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); + if (dso__is_kcore(dso)) { + kce.kcore_filename = symfs_filename; + kce.addr = map__rip_2objdump(map, sym->start); + kce.offs = sym->start; + kce.len = sym->end + 1 - sym->start; + if (!kcore_extract__create(&kce)) { + delete_extract = true; + strlcpy(symfs_filename, kce.extract_filename, + sizeof(symfs_filename)); + if (free_filename) { + free(filename); + free_filename = false; + } + filename = symfs_filename; + } + } + snprintf(command, sizeof(command), "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -d %s %s -C %s|grep -v %s|expand", + " -d %s %s -C %s 2>/dev/null|grep -v %s|expand", objdump_path ? objdump_path : "objdump", disassembler_style ? "-M " : "", disassembler_style ? disassembler_style : "", @@ -846,8 +999,17 @@ fallback: if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) break; + /* + * kallsyms does not have symbol sizes so there may a nop at the end. + * Remove it. + */ + if (dso__is_kcore(dso)) + delete_last_nop(sym); + pclose(file); out_free_filename: + if (delete_extract) + kcore_extract__delete(&kce); if (free_filename) free(filename); return err; @@ -858,7 +1020,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin struct source_line *iter; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; - int ret; + int i, ret; while (*p != NULL) { parent = *p; @@ -866,7 +1028,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin ret = strcmp(iter->path, src_line->path); if (ret == 0) { - iter->percent_sum += src_line->percent; + for (i = 0; i < src_line->nr_pcnt; i++) + iter->p[i].percent_sum += src_line->p[i].percent; return; } @@ -876,12 +1039,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin p = &(*p)->rb_right; } - src_line->percent_sum = src_line->percent; + for (i = 0; i < src_line->nr_pcnt; i++) + src_line->p[i].percent_sum = src_line->p[i].percent; rb_link_node(&src_line->node, parent, p); rb_insert_color(&src_line->node, root); } +static int cmp_source_line(struct source_line *a, struct source_line *b) +{ + int i; + + for (i = 0; i < a->nr_pcnt; i++) { + if (a->p[i].percent_sum == b->p[i].percent_sum) + continue; + return a->p[i].percent_sum > b->p[i].percent_sum; + } + + return 0; +} + static void __resort_source_line(struct rb_root *root, struct source_line *src_line) { struct source_line *iter; @@ -892,7 +1069,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l parent = *p; iter = rb_entry(parent, struct source_line, node); - if (src_line->percent_sum > iter->percent_sum) + if (cmp_source_line(src_line, iter)) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -924,65 +1101,77 @@ static void symbol__free_source_line(struct symbol *sym, int len) { struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; + size_t sizeof_src_line; int i; - for (i = 0; i < len; i++) - free(src_line[i].path); + sizeof_src_line = sizeof(*src_line) + + (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); - free(src_line); - notes->src->lines = NULL; + for (i = 0; i < len; i++) { + free_srcline(src_line->path); + src_line = (void *)src_line + sizeof_src_line; + } + + zfree(¬es->src->lines); } /* Get the filename:line for the colored entries */ static int symbol__get_source_line(struct symbol *sym, struct map *map, - int evidx, struct rb_root *root, int len, - const char *filename) + struct perf_evsel *evsel, + struct rb_root *root, int len) { u64 start; - int i; - char cmd[PATH_MAX * 2]; + int i, k; + int evidx = evsel->idx; struct source_line *src_line; struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); struct rb_root tmp_root = RB_ROOT; + int nr_pcnt = 1; + u64 h_sum = h->sum; + size_t sizeof_src_line = sizeof(struct source_line); + + if (perf_evsel__is_group_event(evsel)) { + for (i = 1; i < evsel->nr_members; i++) { + h = annotation__histogram(notes, evidx + i); + h_sum += h->sum; + } + nr_pcnt = evsel->nr_members; + sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); + } - if (!h->sum) + if (!h_sum) return 0; - src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); + src_line = notes->src->lines = calloc(len, sizeof_src_line); if (!notes->src->lines) return -1; start = map__rip_2objdump(map, sym->start); for (i = 0; i < len; i++) { - char *path = NULL; - size_t line_len; u64 offset; - FILE *fp; + double percent_max = 0.0; - src_line[i].percent = 100.0 * h->addr[i] / h->sum; - if (src_line[i].percent <= 0.5) - continue; + src_line->nr_pcnt = nr_pcnt; - offset = start + i; - sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); - fp = popen(cmd, "r"); - if (!fp) - continue; + for (k = 0; k < nr_pcnt; k++) { + h = annotation__histogram(notes, evidx + k); + src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; - if (getline(&path, &line_len, fp) < 0 || !line_len) - goto next; + if (src_line->p[k].percent > percent_max) + percent_max = src_line->p[k].percent; + } - src_line[i].path = malloc(sizeof(char) * line_len + 1); - if (!src_line[i].path) + if (percent_max <= 0.5) goto next; - strcpy(src_line[i].path, path); - insert_source_line(&tmp_root, &src_line[i]); + offset = start + i; + src_line->path = get_srcline(map->dso, offset); + insert_source_line(&tmp_root, src_line); next: - pclose(fp); + src_line = (void *)src_line + sizeof_src_line; } resort_source_line(root, &tmp_root); @@ -1004,24 +1193,33 @@ static void print_summary(struct rb_root *root, const char *filename) node = rb_first(root); while (node) { - double percent; + double percent, percent_max = 0.0; const char *color; char *path; + int i; src_line = rb_entry(node, struct source_line, node); - percent = src_line->percent_sum; - color = get_percent_color(percent); + for (i = 0; i < src_line->nr_pcnt; i++) { + percent = src_line->p[i].percent_sum; + color = get_percent_color(percent); + color_fprintf(stdout, color, " %7.2f", percent); + + if (percent > percent_max) + percent_max = percent; + } + path = src_line->path; + color = get_percent_color(percent_max); + color_fprintf(stdout, color, " %s\n", path); - color_fprintf(stdout, color, " %7.2f %s", percent, path); node = rb_next(node); } } -static void symbol__annotate_hits(struct symbol *sym, int evidx) +static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) { struct annotation *notes = symbol__annotation(sym); - struct sym_hist *h = annotation__histogram(notes, evidx); + struct sym_hist *h = annotation__histogram(notes, evsel->idx); u64 len = symbol__size(sym), offset; for (offset = 0; offset < len; ++offset) @@ -1031,19 +1229,22 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); } -int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, - bool full_paths, int min_pcnt, int max_lines, - int context) +int symbol__annotate_printf(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool full_paths, + int min_pcnt, int max_lines, int context) { struct dso *dso = map->dso; char *filename; const char *d_filename; + const char *evsel_name = perf_evsel__name(evsel); struct annotation *notes = symbol__annotation(sym); struct disasm_line *pos, *queue = NULL; u64 start = map__rip_2objdump(map, sym->start); int printed = 2, queue_len = 0; int more = 0; u64 len; + int width = 8; + int namelen, evsel_name_len, graph_dotted_len; filename = strdup(dso->long_name); if (!filename) @@ -1055,12 +1256,21 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, d_filename = basename(filename); len = symbol__size(sym); + namelen = strlen(d_filename); + evsel_name_len = strlen(evsel_name); + + if (perf_evsel__is_group_event(evsel)) + width *= evsel->nr_members; + + printf(" %-*.*s| Source code & Disassembly of %s for %s\n", + width, width, "Percent", d_filename, evsel_name); - printf(" Percent | Source code & Disassembly of %s\n", d_filename); - printf("------------------------------------------------\n"); + graph_dotted_len = width + namelen + evsel_name_len; + printf("-%-*.*s-----------------------------------------\n", + graph_dotted_len, graph_dotted_len, graph_dotted_line); if (verbose) - symbol__annotate_hits(sym, evidx); + symbol__annotate_hits(sym, evsel); list_for_each_entry(pos, ¬es->src->source, node) { if (context && queue == NULL) { @@ -1068,7 +1278,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, queue_len = 0; } - switch (disasm_line__print(pos, sym, start, evidx, len, + switch (disasm_line__print(pos, sym, start, evsel, len, min_pcnt, printed, max_lines, queue)) { case 0: @@ -1163,12 +1373,11 @@ size_t disasm__fprintf(struct list_head *head, FILE *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) +int symbol__tty_annotate(struct symbol *sym, struct map *map, + struct perf_evsel *evsel, bool print_lines, + bool full_paths, int min_pcnt, int max_lines) { struct dso *dso = map->dso; - const char *filename = dso->long_name; struct rb_root source_line = RB_ROOT; u64 len; @@ -1178,12 +1387,11 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, len = symbol__size(sym); if (print_lines) { - symbol__get_source_line(sym, map, evidx, &source_line, - len, filename); - print_summary(&source_line, filename); + symbol__get_source_line(sym, map, evsel, &source_line, len); + print_summary(&source_line, dso->long_name); } - symbol__annotate_printf(sym, map, evidx, full_paths, + symbol__annotate_printf(sym, map, evsel, full_paths, min_pcnt, max_lines, 0); if (print_lines) symbol__free_source_line(sym, len); @@ -1192,3 +1400,13 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, return 0; } + +int hist_entry__annotate(struct hist_entry *he, size_t privsize) +{ + return symbol__annotate(he->ms.sym, he->ms.map, privsize); +} + +bool ui__has_annotation(void) +{ + return use_browser == 1 && sort__has_sym; +} |
