diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r-- | tools/perf/util/annotate.c | 237 |
1 files changed, 208 insertions, 29 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 11a8a447a3af..c8b01176c9e1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -10,6 +10,10 @@ #include <errno.h> #include <inttypes.h> #include <libgen.h> +#include <bpf/bpf.h> +#include <bpf/btf.h> +#include <bpf/libbpf.h> +#include <linux/btf.h> #include "util.h" #include "ui/ui.h" #include "sort.h" @@ -24,6 +28,7 @@ #include "annotate.h" #include "evsel.h" #include "evlist.h" +#include "bpf-event.h" #include "block-range.h" #include "string2.h" #include "arch/common.h" @@ -31,6 +36,7 @@ #include <pthread.h> #include <linux/bitops.h> #include <linux/kernel.h> +#include <bpf/libbpf.h> /* FIXME: For the HE_COLORSET */ #include "ui/browser.h" @@ -198,18 +204,18 @@ static void ins__delete(struct ins_operands *ops) } static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { - return scnprintf(bf, size, "%-6s %s", ins->name, ops->raw); + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw); } int ins__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { if (ins->ops->scnprintf) - return ins->ops->scnprintf(ins, bf, size, ops); + return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name); - return ins__raw_scnprintf(ins, bf, size, ops); + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); } bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) @@ -273,18 +279,18 @@ indirect_call: } static int call__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { if (ops->target.sym) - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); if (ops->target.addr == 0) - return ins__raw_scnprintf(ins, bf, size, ops); + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); if (ops->target.name) - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name); + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name); - return scnprintf(bf, size, "%-6s *%" PRIx64, ins->name, ops->target.addr); + return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr); } static struct ins_ops call_ops = { @@ -388,15 +394,15 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s } static int jump__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { const char *c; if (!ops->target.addr || ops->target.offset < 0) - return ins__raw_scnprintf(ins, bf, size, ops); + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); if (ops->target.outside && ops->target.sym != NULL) - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); c = strchr(ops->raw, ','); c = validate_comma(c, ops); @@ -415,7 +421,7 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size, c++; } - return scnprintf(bf, size, "%-6s %.*s%" PRIx64, + return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name, ins->name, c ? c - ops->raw : 0, ops->raw, ops->target.offset); } @@ -483,16 +489,16 @@ out_free_ops: } static int lock__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { int printed; if (ops->locked.ins.ops == NULL) - return ins__raw_scnprintf(ins, bf, size, ops); + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); - printed = scnprintf(bf, size, "%-6s ", ins->name); + printed = scnprintf(bf, size, "%-*s ", max_ins_name, ins->name); return printed + ins__scnprintf(&ops->locked.ins, bf + printed, - size - printed, ops->locked.ops); + size - printed, ops->locked.ops, max_ins_name); } static void lock__delete(struct ins_operands *ops) @@ -564,9 +570,9 @@ out_free_source: } static int mov__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { - return scnprintf(bf, size, "%-6s %s,%s", ins->name, + return scnprintf(bf, size, "%-*s %s,%s", max_ins_name, ins->name, ops->source.name ?: ops->source.raw, ops->target.name ?: ops->target.raw); } @@ -604,9 +610,9 @@ static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops } static int dec__scnprintf(struct ins *ins, char *bf, size_t size, - struct ins_operands *ops) + struct ins_operands *ops, int max_ins_name) { - return scnprintf(bf, size, "%-6s %s", ins->name, + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name ?: ops->target.raw); } @@ -616,9 +622,9 @@ static struct ins_ops dec_ops = { }; static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, - struct ins_operands *ops __maybe_unused) + struct ins_operands *ops __maybe_unused, int max_ins_name) { - return scnprintf(bf, size, "%-6s", "nop"); + return scnprintf(bf, size, "%-*s", max_ins_name, "nop"); } static struct ins_ops nop_ops = { @@ -1232,12 +1238,12 @@ void disasm_line__free(struct disasm_line *dl) annotation_line__delete(&dl->al); } -int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name) { if (raw || !dl->ins.ops) - return scnprintf(bf, size, "%-6s %s", dl->ins.name, dl->ops.raw); + return scnprintf(bf, size, "%-*s %s", max_ins_name, dl->ins.name, dl->ops.raw); - return ins__scnprintf(&dl->ins, bf, size, &dl->ops); + return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name); } static void annotation_line__add(struct annotation_line *al, struct list_head *head) @@ -1615,6 +1621,9 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map * " --vmlinux vmlinux\n", build_id_msg ?: ""); } break; + case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF: + scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation"); + break; default: scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); break; @@ -1674,6 +1683,156 @@ fallback: return 0; } +#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +#define PACKAGE "perf" +#include <bfd.h> +#include <dis-asm.h> + +static int symbol__disassemble_bpf(struct symbol *sym, + struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct annotation_options *opts = args->options; + struct bpf_prog_info_linear *info_linear; + struct bpf_prog_linfo *prog_linfo = NULL; + struct bpf_prog_info_node *info_node; + int len = sym->end - sym->start; + disassembler_ftype disassemble; + struct map *map = args->ms.map; + struct disassemble_info info; + struct dso *dso = map->dso; + int pc = 0, count, sub_id; + struct btf *btf = NULL; + char tpath[PATH_MAX]; + size_t buf_size; + int nr_skip = 0; + int ret = -1; + char *buf; + bfd *bfdf; + FILE *s; + + if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO) + return -1; + + pr_debug("%s: handling sym %s addr %lx len %lx\n", __func__, + sym->name, sym->start, sym->end - sym->start); + + memset(tpath, 0, sizeof(tpath)); + perf_exe(tpath, sizeof(tpath)); + + bfdf = bfd_openr(tpath, NULL); + assert(bfdf); + assert(bfd_check_format(bfdf, bfd_object)); + + s = open_memstream(&buf, &buf_size); + if (!s) + goto out; + init_disassemble_info(&info, s, + (fprintf_ftype) fprintf); + + info.arch = bfd_get_arch(bfdf); + info.mach = bfd_get_mach(bfdf); + + info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, + dso->bpf_prog.id); + if (!info_node) + goto out; + info_linear = info_node->info_linear; + sub_id = dso->bpf_prog.sub_id; + + info.buffer = (void *)(info_linear->info.jited_prog_insns); + info.buffer_length = info_linear->info.jited_prog_len; + + if (info_linear->info.nr_line_info) + prog_linfo = bpf_prog_linfo__new(&info_linear->info); + + if (info_linear->info.btf_id) { + struct btf_node *node; + + node = perf_env__find_btf(dso->bpf_prog.env, + info_linear->info.btf_id); + if (node) + btf = btf__new((__u8 *)(node->data), + node->data_size); + } + + disassemble_init_for_target(&info); + +#ifdef DISASM_FOUR_ARGS_SIGNATURE + disassemble = disassembler(info.arch, + bfd_big_endian(bfdf), + info.mach, + bfdf); +#else + disassemble = disassembler(bfdf); +#endif + assert(disassemble); + + fflush(s); + do { + const struct bpf_line_info *linfo = NULL; + struct disasm_line *dl; + size_t prev_buf_size; + const char *srcline; + u64 addr; + + addr = pc + ((u64 *)(info_linear->info.jited_ksyms))[sub_id]; + count = disassemble(pc, &info); + + if (prog_linfo) + linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, + addr, sub_id, + nr_skip); + + if (linfo && btf) { + srcline = btf__name_by_offset(btf, linfo->line_off); + nr_skip++; + } else + srcline = NULL; + + fprintf(s, "\n"); + prev_buf_size = buf_size; + fflush(s); + + if (!opts->hide_src_code && srcline) { + args->offset = -1; + args->line = strdup(srcline); + args->line_nr = 0; + args->ms.sym = sym; + dl = disasm_line__new(args); + if (dl) { + annotation_line__add(&dl->al, + ¬es->src->source); + } + } + + args->offset = pc; + args->line = buf + prev_buf_size; + args->line_nr = 0; + args->ms.sym = sym; + dl = disasm_line__new(args); + if (dl) + annotation_line__add(&dl->al, ¬es->src->source); + + pc += count; + } while (count > 0 && pc < len); + + ret = 0; +out: + free(prog_linfo); + free(btf); + fclose(s); + bfd_close(bfdf); + return ret; +} +#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, + struct annotate_args *args __maybe_unused) +{ + return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; +} +#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) + static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) { struct annotation_options *opts = args->options; @@ -1701,7 +1860,9 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); - if (dso__is_kcore(dso)) { + if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) { + return symbol__disassemble_bpf(sym, args); + } else if (dso__is_kcore(dso)) { kce.kcore_filename = symfs_filename; kce.addr = map__rip_2objdump(map, sym->start); kce.offs = sym->start; @@ -2414,12 +2575,30 @@ static inline int width_jumps(int n) return 1; } +static int annotation__max_ins_name(struct annotation *notes) +{ + int max_name = 0, len; + struct annotation_line *al; + + list_for_each_entry(al, ¬es->src->source, node) { + if (al->offset == -1) + continue; + + len = strlen(disasm_line(al)->ins.name); + if (max_name < len) + max_name = len; + } + + return max_name; +} + void annotation__init_column_widths(struct annotation *notes, struct symbol *sym) { notes->widths.addr = notes->widths.target = notes->widths.min_addr = hex_width(symbol__size(sym)); notes->widths.max_addr = hex_width(sym->end); notes->widths.jumps = width_jumps(notes->max_jump_sources); + notes->widths.max_ins_name = annotation__max_ins_name(notes); } void annotation__update_column_widths(struct annotation *notes) @@ -2583,7 +2762,7 @@ call_like: obj__printf(obj, " "); } - disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset); + disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset, notes->widths.max_ins_name); } static void ipc_coverage_string(char *bf, int size, struct annotation *notes) |