diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
| -rw-r--r-- | tools/perf/util/annotate.c | 214 |
1 files changed, 170 insertions, 44 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index db475e44f42f..ba988a13dacb 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -32,12 +32,14 @@ #include "block-range.h" #include "string2.h" #include "util/event.h" +#include "util/sharded_mutex.h" #include "arch/common.h" #include "namespaces.h" #include <regex.h> #include <linux/bitops.h> #include <linux/kernel.h> #include <linux/string.h> +#include <linux/zalloc.h> #include <subcmd/parse-options.h> #include <subcmd/run-command.h> @@ -55,19 +57,15 @@ #include <linux/ctype.h> -struct annotation_options annotation__default_options = { - .use_offset = true, - .jump_arrows = true, - .annotate_src = true, - .offset_level = ANNOTATION__OFFSET_JUMP_TARGETS, - .percent_type = PERCENT_PERIOD_LOCAL, -}; - static regex_t file_lineno; static struct ins_ops *ins__find(struct arch *arch, const char *name); static void ins__sort(struct arch *arch); static int disasm_line__parse(char *line, const char **namep, char **rawp); +static int call__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, int max_ins_name); +static int jump__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, int max_ins_name); struct arch { const char *name; @@ -77,6 +75,7 @@ struct arch { struct ins_ops *(*associate_instruction_ops)(struct arch *arch, const char *name); bool sorted_instructions; bool initialized; + const char *insn_suffix; void *priv; unsigned int model; unsigned int family; @@ -149,6 +148,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i #include "arch/arm/annotate/instructions.c" #include "arch/arm64/annotate/instructions.c" #include "arch/csky/annotate/instructions.c" +#include "arch/loongarch/annotate/instructions.c" #include "arch/mips/annotate/instructions.c" #include "arch/x86/annotate/instructions.c" #include "arch/powerpc/annotate/instructions.c" @@ -185,6 +185,7 @@ static struct arch architectures[] = { .init = x86__annotate_init, .instructions = x86__instructions, .nr_instructions = ARRAY_SIZE(x86__instructions), + .insn_suffix = "bwlq", .objdump = { .comment_char = '#', }, @@ -211,6 +212,13 @@ static struct arch architectures[] = { .comment_char = '#', }, }, + { + .name = "loongarch", + .init = loongarch__annotate_init, + .objdump = { + .comment_char = '#', + }, + }, }; static void ins__delete(struct ins_operands *ops) @@ -280,7 +288,7 @@ find_target: target.addr = map__objdump_2mem(map, ops->target.addr); if (maps__find_ams(ms->maps, &target) == 0 && - map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) + map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) ops->target.sym = target.ms.sym; return 0; @@ -320,7 +328,7 @@ static struct ins_ops call_ops = { bool ins__is_call(const struct ins *ins) { - return ins->ops == &call_ops || ins->ops == &s390_call_ops; + return ins->ops == &call_ops || ins->ops == &s390_call_ops || ins->ops == &loongarch_call_ops; } /* @@ -384,8 +392,8 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s } target.addr = map__objdump_2mem(map, ops->target.addr); - start = map->unmap_ip(map, sym->start), - end = map->unmap_ip(map, sym->end); + start = map__unmap_ip(map, sym->start); + end = map__unmap_ip(map, sym->end); ops->target.outside = target.addr < start || target.addr > end; @@ -408,7 +416,7 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s * the symbol searching and disassembly should be done. */ if (maps__find_ams(ms->maps, &target) == 0 && - map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) + map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) ops->target.sym = target.ms.sym; if (!ops->target.outside) { @@ -461,7 +469,7 @@ static struct ins_ops jump_ops = { bool ins__is_jump(const struct ins *ins) { - return ins->ops == &jump_ops; + return ins->ops == &jump_ops || ins->ops == &loongarch_jump_ops; } static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) @@ -557,13 +565,26 @@ static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_sy return -1; *s = '\0'; + + /* + * x86 SIB addressing has something like 0x8(%rax, %rcx, 1) + * then it needs to have the closing parenthesis. + */ + if (strchr(ops->raw, '(')) { + *s = ','; + s = strchr(ops->raw, ')'); + if (s == NULL || s[1] != ',') + return -1; + *++s = '\0'; + } + ops->source.raw = strdup(ops->raw); *s = ','; if (ops->source.raw == NULL) return -1; - target = ++s; + target = skip_spaces(++s); comment = strchr(s, arch->objdump.comment_char); if (comment != NULL) @@ -706,6 +727,26 @@ static struct ins_ops *__ins__find(struct arch *arch, const char *name) } ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); + if (ins) + return ins->ops; + + if (arch->insn_suffix) { + char tmp[32]; + char suffix; + size_t len = strlen(name); + + if (len == 0 || len >= sizeof(tmp)) + return NULL; + + suffix = name[len - 1]; + if (strchr(arch->insn_suffix, suffix) == NULL) + return NULL; + + strcpy(tmp, name); + tmp[len - 1] = '\0'; /* remove the suffix and check again */ + + ins = bsearch(tmp, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); + } return ins ? ins->ops : NULL; } @@ -820,7 +861,7 @@ void symbol__annotate_zero_histograms(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); - mutex_lock(¬es->lock); + annotation__lock(notes); if (notes->src != NULL) { memset(notes->src->histograms, 0, notes->src->nr_histograms * notes->src->sizeof_sym_hist); @@ -828,7 +869,7 @@ void symbol__annotate_zero_histograms(struct symbol *sym) memset(notes->src->cycles_hist, 0, symbol__size(sym) * sizeof(struct cyc_hist)); } - mutex_unlock(¬es->lock); + annotation__unlock(notes); } static int __symbol__account_cycles(struct cyc_hist *ch, @@ -889,7 +930,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms, unsigned offset; struct sym_hist *h; - pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, ms->map->unmap_ip(ms->map, addr)); + pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr)); if ((addr < sym->start || addr >= sym->end) && (addr != sym->end || sym->start != sym->end)) { @@ -1016,13 +1057,13 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, if (start && (start->ms.sym == ams->ms.sym || (ams->ms.sym && - start->addr == ams->ms.sym->start + ams->ms.map->start))) + start->addr == ams->ms.sym->start + map__start(ams->ms.map)))) saddr = start->al_addr; if (saddr == 0) pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n", ams->addr, start ? start->addr : 0, - ams->ms.sym ? ams->ms.sym->start + ams->ms.map->start : 0, + ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0, saddr); err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles); if (err) @@ -1085,7 +1126,7 @@ void annotation__compute_ipc(struct annotation *notes, size_t size) notes->hit_insn = 0; notes->cover_insn = 0; - mutex_lock(¬es->lock); + annotation__lock(notes); for (offset = size - 1; offset >= 0; --offset) { struct cyc_hist *ch; @@ -1104,7 +1145,7 @@ void annotation__compute_ipc(struct annotation *notes, size_t size) notes->have_cycles = true; } } - mutex_unlock(¬es->lock); + annotation__unlock(notes); } int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, @@ -1182,7 +1223,7 @@ static void annotation_line__init(struct annotation_line *al, static void annotation_line__exit(struct annotation_line *al) { - free_srcline(al->path); + zfree_srcline(&al->path); zfree(&al->line); } @@ -1255,17 +1296,64 @@ int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool r return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name); } -void annotation__init(struct annotation *notes) +void annotation__exit(struct annotation *notes) { - mutex_init(¬es->lock); + annotated_source__delete(notes->src); } -void annotation__exit(struct annotation *notes) +static struct sharded_mutex *sharded_mutex; + +static void annotation__init_sharded_mutex(void) { - annotated_source__delete(notes->src); - mutex_destroy(¬es->lock); + /* As many mutexes as there are CPUs. */ + sharded_mutex = sharded_mutex__new(cpu__max_present_cpu().cpu); +} + +static size_t annotation__hash(const struct annotation *notes) +{ + return (size_t)notes; +} + +static struct mutex *annotation__get_mutex(const struct annotation *notes) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + + pthread_once(&once, annotation__init_sharded_mutex); + if (!sharded_mutex) + return NULL; + + return sharded_mutex__get_mutex(sharded_mutex, annotation__hash(notes)); +} + +void annotation__lock(struct annotation *notes) + NO_THREAD_SAFETY_ANALYSIS +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (mutex) + mutex_lock(mutex); +} + +void annotation__unlock(struct annotation *notes) + NO_THREAD_SAFETY_ANALYSIS +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (mutex) + mutex_unlock(mutex); +} + +bool annotation__trylock(struct annotation *notes) +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (!mutex) + return false; + + return mutex_trylock(mutex); } + static void annotation_line__add(struct annotation_line *al, struct list_head *head) { list_add_tail(&al->node, head); @@ -1510,6 +1598,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, /* /filename:linenr ? Save line number and ignore. */ if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) { *line_nr = atoi(parsed_line + match[1].rm_so); + free(*fileloc); *fileloc = strdup(parsed_line); return 0; } @@ -1558,7 +1647,6 @@ static int symbol__parse_objdump_line(struct symbol *sym, } annotation_line__add(&dl->al, ¬es->src->source); - return 0; } @@ -1593,7 +1681,7 @@ static void delete_last_nop(struct symbol *sym) int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen) { - struct dso *dso = ms->map->dso; + struct dso *dso = map__dso(ms->map); BUG_ON(buflen == 0); @@ -1695,12 +1783,14 @@ fallback: * cache, or is just a kallsyms file, well, lets hope that this * DSO is the same as when 'perf record' ran. */ - __symbol__join_symfs(filename, filename_size, dso->long_name); + if (dso->kernel && dso->long_name[0] == '/') + snprintf(filename, filename_size, "%s", dso->long_name); + else + __symbol__join_symfs(filename, filename_size, dso->long_name); mutex_lock(&dso->lock); if (access(filename, R_OK) && errno == ENOENT && dso->nsinfo) { - char *new_name = filename_with_chroot(dso->nsinfo->pid, - filename); + char *new_name = dso__filename_with_chroot(dso, filename); if (new_name) { strlcpy(filename, new_name, filename_size); free(new_name); @@ -1735,7 +1825,7 @@ static int symbol__disassemble_bpf(struct symbol *sym, struct map *map = args->ms.map; struct perf_bpil *info_linear; struct disassemble_info info; - struct dso *dso = map->dso; + struct dso *dso = map__dso(map); int pc = 0, count, sub_id; struct btf *btf = NULL; char tpath[PATH_MAX]; @@ -1889,7 +1979,7 @@ symbol__disassemble_bpf_image(struct symbol *sym, if (dl) annotation_line__add(&dl->al, ¬es->src->source); - free(args->line); + zfree(&args->line); return 0; } @@ -1958,7 +2048,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) { struct annotation_options *opts = args->options; struct map *map = args->ms.map; - struct dso *dso = map->dso; + struct dso *dso = map__dso(map); char *command; FILE *file; char symfs_filename[PATH_MAX]; @@ -1985,8 +2075,8 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) return err; pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, - symfs_filename, sym->name, map->unmap_ip(map, sym->start), - map->unmap_ip(map, sym->end)); + symfs_filename, sym->name, map__unmap_ip(map, sym->start), + map__unmap_ip(map, sym->end)); pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); @@ -2101,6 +2191,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) nline++; } free(line); + free(fileloc); err = finish_command(&objdump_process); if (err) @@ -2403,7 +2494,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel, { struct map *map = ms->map; struct symbol *sym = ms->sym; - struct dso *dso = map->dso; + struct dso *dso = map__dso(map); char *filename; const char *d_filename; const char *evsel_name = evsel__name(evsel); @@ -2586,7 +2677,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel, } fprintf(fp, "%s() %s\nEvent: %s\n\n", - ms->sym->name, ms->map->dso->long_name, ev_name); + ms->sym->name, map__dso(ms->map)->long_name, ev_name); symbol__annotate_fprintf2(ms->sym, fp, opts); fclose(fp); @@ -2812,7 +2903,7 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map, if (percent_max <= 0.5) continue; - al->path = get_srcline(map->dso, notes->start + al->offset, NULL, + al->path = get_srcline(map__dso(map), notes->start + al->offset, NULL, false, true, notes->start + al->offset); insert_source_line(&tmp_root, al, opts); } @@ -2831,7 +2922,7 @@ static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root, int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel, struct annotation_options *opts) { - struct dso *dso = ms->map->dso; + struct dso *dso = map__dso(ms->map); struct symbol *sym = ms->sym; struct rb_root source_line = RB_ROOT; struct hists *hists = evsel__hists(evsel); @@ -2867,7 +2958,7 @@ int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel, int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel, struct annotation_options *opts) { - struct dso *dso = ms->map->dso; + struct dso *dso = map__dso(ms->map); struct symbol *sym = ms->sym; struct rb_root source_line = RB_ROOT; int err; @@ -3214,7 +3305,23 @@ static int annotation__config(const char *var, const char *value, void *data) } else if (!strcmp(var, "annotate.use_offset")) { opt->use_offset = perf_config_bool("use_offset", value); } else if (!strcmp(var, "annotate.disassembler_style")) { - opt->disassembler_style = value; + opt->disassembler_style = strdup(value); + if (!opt->disassembler_style) { + pr_err("Not enough memory for annotate.disassembler_style\n"); + return -1; + } + } else if (!strcmp(var, "annotate.objdump")) { + opt->objdump_path = strdup(value); + if (!opt->objdump_path) { + pr_err("Not enough memory for annotate.objdump\n"); + return -1; + } + } else if (!strcmp(var, "annotate.addr2line")) { + symbol_conf.addr2line_path = strdup(value); + if (!symbol_conf.addr2line_path) { + pr_err("Not enough memory for annotate.addr2line\n"); + return -1; + } } else if (!strcmp(var, "annotate.demangle")) { symbol_conf.demangle = perf_config_bool("demangle", value); } else if (!strcmp(var, "annotate.demangle_kernel")) { @@ -3226,6 +3333,25 @@ static int annotation__config(const char *var, const char *value, void *data) return 0; } +void annotation_options__init(struct annotation_options *opt) +{ + memset(opt, 0, sizeof(*opt)); + + /* Default values. */ + opt->use_offset = true; + opt->jump_arrows = true; + opt->annotate_src = true; + opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS; + opt->percent_type = PERCENT_PERIOD_LOCAL; +} + + +void annotation_options__exit(struct annotation_options *opt) +{ + zfree(&opt->disassembler_style); + zfree(&opt->objdump_path); +} + void annotation_config__init(struct annotation_options *opt) { perf_config(annotation__config, opt); |
