Skip to content

Commit 6987561

Browse files
liu-song-6acmel
authored andcommitted
perf annotate: Enable annotation of BPF programs
In symbol__disassemble(), DSO_BINARY_TYPE__BPF_PROG_INFO dso calls into a new function symbol__disassemble_bpf(), where annotation line information is filled based on the bpf_prog_info and btf data saved in given perf_env. symbol__disassemble_bpf() uses binutils's libopcodes to disassemble bpf programs. Committer testing: After fixing this: - u64 *addrs = (u64 *)(info_linear->info.jited_ksyms); + u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms); Detected when crossbuilding to a 32-bit arch. And making all this dependent on HAVE_LIBBFD_SUPPORT and HAVE_LIBBPF_SUPPORT: 1) Have a BPF program running, one that has BTF info, etc, I used the tools/perf/examples/bpf/augmented_raw_syscalls.c put in place by 'perf trace'. # grep -B1 augmented_raw ~/.perfconfig [trace] add_events = /home/acme/git/perf/tools/perf/examples/bpf/augmented_raw_syscalls.c # # perf trace -e *mmsg dnf/6245 sendmmsg(20, 0x7f5485a88030, 2, MSG_NOSIGNAL) = 2 NetworkManager/10055 sendmmsg(22<socket:[1056822]>, 0x7f8126ad1bb0, 2, MSG_NOSIGNAL) = 2 2) Then do a 'perf record' system wide for a while: # perf record -a ^C[ perf record: Woken up 68 times to write data ] [ perf record: Captured and wrote 19.427 MB perf.data (366891 samples) ] # 3) Check that we captured BPF and BTF info in the perf.data file: # perf report --header-only | grep 'b[pt]f' # event : name = cycles:ppp, , id = { 294789, 294790, 294791, 294792, 294793, 294794, 294795, 294796 }, size = 112, { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|CPU|PERIOD, read_format = ID, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, task = 1, precise_ip = 3, sample_id_all = 1, exclude_guest = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1 # bpf_prog_info of id 13 # bpf_prog_info of id 14 # bpf_prog_info of id 15 # bpf_prog_info of id 16 # bpf_prog_info of id 17 # bpf_prog_info of id 18 # bpf_prog_info of id 21 # bpf_prog_info of id 22 # bpf_prog_info of id 41 # bpf_prog_info of id 42 # btf info of id 2 # 4) Check which programs got recorded: # perf report | grep bpf_prog | head 0.16% exe bpf_prog_819967866022f1e1_sys_enter [k] bpf_prog_819967866022f1e1_sys_enter 0.14% exe bpf_prog_c1bd85c092d6e4aa_sys_exit [k] bpf_prog_c1bd85c092d6e4aa_sys_exit 0.08% fuse-overlayfs bpf_prog_819967866022f1e1_sys_enter [k] bpf_prog_819967866022f1e1_sys_enter 0.07% fuse-overlayfs bpf_prog_c1bd85c092d6e4aa_sys_exit [k] bpf_prog_c1bd85c092d6e4aa_sys_exit 0.01% clang-4.0 bpf_prog_c1bd85c092d6e4aa_sys_exit [k] bpf_prog_c1bd85c092d6e4aa_sys_exit 0.01% clang-4.0 bpf_prog_819967866022f1e1_sys_enter [k] bpf_prog_819967866022f1e1_sys_enter 0.00% clang bpf_prog_c1bd85c092d6e4aa_sys_exit [k] bpf_prog_c1bd85c092d6e4aa_sys_exit 0.00% runc bpf_prog_819967866022f1e1_sys_enter [k] bpf_prog_819967866022f1e1_sys_enter 0.00% clang bpf_prog_819967866022f1e1_sys_enter [k] bpf_prog_819967866022f1e1_sys_enter 0.00% sh bpf_prog_c1bd85c092d6e4aa_sys_exit [k] bpf_prog_c1bd85c092d6e4aa_sys_exit # This was with the default --sort order for 'perf report', which is: --sort comm,dso,symbol If we just look for the symbol, for instance: # perf report --sort symbol | grep bpf_prog | head 0.26% [k] bpf_prog_819967866022f1e1_sys_enter - - 0.24% [k] bpf_prog_c1bd85c092d6e4aa_sys_exit - - # or the DSO: # perf report --sort dso | grep bpf_prog | head 0.26% bpf_prog_819967866022f1e1_sys_enter 0.24% bpf_prog_c1bd85c092d6e4aa_sys_exit # We'll see the two BPF programs that augmented_raw_syscalls.o puts in place, one attached to the raw_syscalls:sys_enter and another to the raw_syscalls:sys_exit tracepoints, as expected. Now we can finally do, from the command line, annotation for one of those two symbols, with the original BPF program source coude intermixed with the disassembled JITed code: # perf annotate --stdio2 bpf_prog_819967866022f1e1_sys_enter Samples: 950 of event 'cycles:ppp', 4000 Hz, Event count (approx.): 553756947, [percent: local period] bpf_prog_819967866022f1e1_sys_enter() bpf_prog_819967866022f1e1_sys_enter Percent int sys_enter(struct syscall_enter_args *args) 53.41 push %rbp 0.63 mov %rsp,%rbp 0.31 sub $0x170,%rsp 1.93 sub $0x28,%rbp 7.02 mov %rbx,0x0(%rbp) 3.20 mov %r13,0x8(%rbp) 1.07 mov %r14,0x10(%rbp) 0.61 mov %r15,0x18(%rbp) 0.11 xor %eax,%eax 1.29 mov %rax,0x20(%rbp) 0.11 mov %rdi,%rbx return bpf_get_current_pid_tgid(); 2.02 → callq *ffffffffda6776d9 2.76 mov %eax,-0x148(%rbp) mov %rbp,%rsi int sys_enter(struct syscall_enter_args *args) add $0xfffffffffffffeb8,%rsi return bpf_map_lookup_elem(pids, &pid) != NULL; movabs $0xffff975ac2607800,%rdi 1.26 → callq *ffffffffda6789e9 cmp $0x0,%rax 2.43 → je 0 add $0x38,%rax 0.21 xor %r13d,%r13d if (pid_filter__has(&pids_filtered, getpid())) 0.81 cmp $0x0,%rax → jne 0 mov %rbp,%rdi probe_read(&augmented_args.args, sizeof(augmented_args.args), args); 2.22 add $0xfffffffffffffeb8,%rdi 0.11 mov $0x40,%esi 0.32 mov %rbx,%rdx 2.74 → callq *ffffffffda658409 syscall = bpf_map_lookup_elem(&syscalls, &augmented_args.args.syscall_nr); 0.22 mov %rbp,%rsi 1.69 add $0xfffffffffffffec0,%rsi syscall = bpf_map_lookup_elem(&syscalls, &augmented_args.args.syscall_nr); movabs $0xffff975bfcd36000,%rdi add $0xd0,%rdi 0.21 mov 0x0(%rsi),%eax 0.93 cmp $0x200,%rax → jae 0 0.10 shl $0x3,%rax 0.11 add %rdi,%rax 0.11 → jmp 0 xor %eax,%eax if (syscall == NULL || !syscall->enabled) 1.07 cmp $0x0,%rax → je 0 if (syscall == NULL || !syscall->enabled) 6.57 movzbq 0x0(%rax),%rdi if (syscall == NULL || !syscall->enabled) cmp $0x0,%rdi 0.95 → je 0 mov $0x40,%r8d switch (augmented_args.args.syscall_nr) { mov -0x140(%rbp),%rdi switch (augmented_args.args.syscall_nr) { cmp $0x2,%rdi → je 0 cmp $0x101,%rdi → je 0 cmp $0x15,%rdi → jne 0 case SYS_OPEN: filename_arg = (const void *)args->args[0]; mov 0x10(%rbx),%rdx → jmp 0 case SYS_OPENAT: filename_arg = (const void *)args->args[1]; mov 0x18(%rbx),%rdx if (filename_arg != NULL) { cmp $0x0,%rdx → je 0 xor %edi,%edi augmented_args.filename.reserved = 0; mov %edi,-0x104(%rbp) augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, mov %rbp,%rdi add $0xffffffffffffff00,%rdi augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, mov $0x100,%esi → callq *ffffffffda658499 mov $0x148,%r8d augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, mov %eax,-0x108(%rbp) augmented_args.filename.size = probe_read_str(&augmented_args.filename.value, mov %rax,%rdi shl $0x20,%rdi shr $0x20,%rdi if (augmented_args.filename.size < sizeof(augmented_args.filename.value)) { cmp $0xff,%rdi → ja 0 len -= sizeof(augmented_args.filename.value) - augmented_args.filename.size; add $0x48,%rax len &= sizeof(augmented_args.filename.value) - 1; and $0xff,%rax mov %rax,%r8 mov %rbp,%rcx return perf_event_output(args, &__augmented_syscalls__, BPF_F_CURRENT_CPU, &augmented_args, len); add $0xfffffffffffffeb8,%rcx mov %rbx,%rdi movabs $0xffff975fbd72d800,%rsi mov $0xffffffff,%edx → callq *ffffffffda658ad9 mov %rax,%r13 } mov %r13,%rax 0.72 mov 0x0(%rbp),%rbx mov 0x8(%rbp),%r13 1.16 mov 0x10(%rbp),%r14 0.10 mov 0x18(%rbp),%r15 0.42 add $0x28,%rbp 0.54 leaveq 0.54 ← retq # Please see 'man perf-config' to see how to control what should be seen, via ~/.perfconfig [annotate] section, for instance, one can suppress the source code and see just the disassembly, etc. Alternatively, use the TUI bu just using 'perf annotate', press '/bpf_prog' to see the bpf symbols, press enter and do the interactive annotation, which allows for dumping to a file after selecting the the various output tunables, for instance, the above without source code intermixed, plus showing all the instruction offsets: # perf annotate bpf_prog_819967866022f1e1_sys_enter Then press: 's' to hide the source code + 'O' twice to show all instruction offsets, then 'P' to print to the bpf_prog_819967866022f1e1_sys_enter.annotation file, which will have: # cat bpf_prog_819967866022f1e1_sys_enter.annotation bpf_prog_819967866022f1e1_sys_enter() bpf_prog_819967866022f1e1_sys_enter Event: cycles:ppp 53.41 0: push %rbp 0.63 1: mov %rsp,%rbp 0.31 4: sub $0x170,%rsp 1.93 b: sub $0x28,%rbp 7.02 f: mov %rbx,0x0(%rbp) 3.20 13: mov %r13,0x8(%rbp) 1.07 17: mov %r14,0x10(%rbp) 0.61 1b: mov %r15,0x18(%rbp) 0.11 1f: xor %eax,%eax 1.29 21: mov %rax,0x20(%rbp) 0.11 25: mov %rdi,%rbx 2.02 28: → callq *ffffffffda6776d9 2.76 2d: mov %eax,-0x148(%rbp) 33: mov %rbp,%rsi 36: add $0xfffffffffffffeb8,%rsi 3d: movabs $0xffff975ac2607800,%rdi 1.26 47: → callq *ffffffffda6789e9 4c: cmp $0x0,%rax 2.43 50: → je 0 52: add $0x38,%rax 0.21 56: xor %r13d,%r13d 0.81 59: cmp $0x0,%rax 5d: → jne 0 63: mov %rbp,%rdi 2.22 66: add $0xfffffffffffffeb8,%rdi 0.11 6d: mov $0x40,%esi 0.32 72: mov %rbx,%rdx 2.74 75: → callq *ffffffffda658409 0.22 7a: mov %rbp,%rsi 1.69 7d: add $0xfffffffffffffec0,%rsi 84: movabs $0xffff975bfcd36000,%rdi 8e: add $0xd0,%rdi 0.21 95: mov 0x0(%rsi),%eax 0.93 98: cmp $0x200,%rax 9f: → jae 0 0.10 a1: shl $0x3,%rax 0.11 a5: add %rdi,%rax 0.11 a8: → jmp 0 aa: xor %eax,%eax 1.07 ac: cmp $0x0,%rax b0: → je 0 6.57 b6: movzbq 0x0(%rax),%rdi bb: cmp $0x0,%rdi 0.95 bf: → je 0 c5: mov $0x40,%r8d cb: mov -0x140(%rbp),%rdi d2: cmp $0x2,%rdi d6: → je 0 d8: cmp $0x101,%rdi df: → je 0 e1: cmp $0x15,%rdi e5: → jne 0 e7: mov 0x10(%rbx),%rdx eb: → jmp 0 ed: mov 0x18(%rbx),%rdx f1: cmp $0x0,%rdx f5: → je 0 f7: xor %edi,%edi f9: mov %edi,-0x104(%rbp) ff: mov %rbp,%rdi 102: add $0xffffffffffffff00,%rdi 109: mov $0x100,%esi 10e: → callq *ffffffffda658499 113: mov $0x148,%r8d 119: mov %eax,-0x108(%rbp) 11f: mov %rax,%rdi 122: shl $0x20,%rdi 126: shr $0x20,%rdi 12a: cmp $0xff,%rdi 131: → ja 0 133: add $0x48,%rax 137: and $0xff,%rax 13d: mov %rax,%r8 140: mov %rbp,%rcx 143: add $0xfffffffffffffeb8,%rcx 14a: mov %rbx,%rdi 14d: movabs $0xffff975fbd72d800,%rsi 157: mov $0xffffffff,%edx 15c: → callq *ffffffffda658ad9 161: mov %rax,%r13 164: mov %r13,%rax 0.72 167: mov 0x0(%rbp),%rbx 16b: mov 0x8(%rbp),%r13 1.16 16f: mov 0x10(%rbp),%r14 0.10 173: mov 0x18(%rbp),%r15 0.42 177: add $0x28,%rbp 0.54 17b: leaveq 0.54 17c: ← retq Another cool way to test all this is to symple use 'perf top' look for those symbols, go there and press enter, annotate it live :-) Signed-off-by: Song Liu <songliubraving@fb.com> Reviewed-by: Jiri Olsa <jolsa@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stanislav Fomichev <sdf@google.com> Link: http://lkml.kernel.org/r/20190312053051.2690567-13-songliubraving@fb.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent 8a1b171 commit 6987561

File tree

3 files changed

+164
-2
lines changed

3 files changed

+164
-2
lines changed

tools/perf/util/annotate.c

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#include <errno.h>
1111
#include <inttypes.h>
1212
#include <libgen.h>
13+
#include <bpf/bpf.h>
14+
#include <bpf/btf.h>
15+
#include <bpf/libbpf.h>
16+
#include <linux/btf.h>
1317
#include "util.h"
1418
#include "ui/ui.h"
1519
#include "sort.h"
@@ -24,13 +28,15 @@
2428
#include "annotate.h"
2529
#include "evsel.h"
2630
#include "evlist.h"
31+
#include "bpf-event.h"
2732
#include "block-range.h"
2833
#include "string2.h"
2934
#include "arch/common.h"
3035
#include <regex.h>
3136
#include <pthread.h>
3237
#include <linux/bitops.h>
3338
#include <linux/kernel.h>
39+
#include <bpf/libbpf.h>
3440

3541
/* FIXME: For the HE_COLORSET */
3642
#include "ui/browser.h"
@@ -1615,6 +1621,9 @@ int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *
16151621
" --vmlinux vmlinux\n", build_id_msg ?: "");
16161622
}
16171623
break;
1624+
case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF:
1625+
scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation");
1626+
break;
16181627
default:
16191628
scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum);
16201629
break;
@@ -1674,6 +1683,156 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
16741683
return 0;
16751684
}
16761685

1686+
#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
1687+
#define PACKAGE "perf"
1688+
#include <bfd.h>
1689+
#include <dis-asm.h>
1690+
1691+
static int symbol__disassemble_bpf(struct symbol *sym,
1692+
struct annotate_args *args)
1693+
{
1694+
struct annotation *notes = symbol__annotation(sym);
1695+
struct annotation_options *opts = args->options;
1696+
struct bpf_prog_info_linear *info_linear;
1697+
struct bpf_prog_linfo *prog_linfo = NULL;
1698+
struct bpf_prog_info_node *info_node;
1699+
int len = sym->end - sym->start;
1700+
disassembler_ftype disassemble;
1701+
struct map *map = args->ms.map;
1702+
struct disassemble_info info;
1703+
struct dso *dso = map->dso;
1704+
int pc = 0, count, sub_id;
1705+
struct btf *btf = NULL;
1706+
char tpath[PATH_MAX];
1707+
size_t buf_size;
1708+
int nr_skip = 0;
1709+
int ret = -1;
1710+
char *buf;
1711+
bfd *bfdf;
1712+
FILE *s;
1713+
1714+
if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO)
1715+
return -1;
1716+
1717+
pr_debug("%s: handling sym %s addr %lx len %lx\n", __func__,
1718+
sym->name, sym->start, sym->end - sym->start);
1719+
1720+
memset(tpath, 0, sizeof(tpath));
1721+
perf_exe(tpath, sizeof(tpath));
1722+
1723+
bfdf = bfd_openr(tpath, NULL);
1724+
assert(bfdf);
1725+
assert(bfd_check_format(bfdf, bfd_object));
1726+
1727+
s = open_memstream(&buf, &buf_size);
1728+
if (!s)
1729+
goto out;
1730+
init_disassemble_info(&info, s,
1731+
(fprintf_ftype) fprintf);
1732+
1733+
info.arch = bfd_get_arch(bfdf);
1734+
info.mach = bfd_get_mach(bfdf);
1735+
1736+
info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env,
1737+
dso->bpf_prog.id);
1738+
if (!info_node)
1739+
goto out;
1740+
info_linear = info_node->info_linear;
1741+
sub_id = dso->bpf_prog.sub_id;
1742+
1743+
info.buffer = (void *)(info_linear->info.jited_prog_insns);
1744+
info.buffer_length = info_linear->info.jited_prog_len;
1745+
1746+
if (info_linear->info.nr_line_info)
1747+
prog_linfo = bpf_prog_linfo__new(&info_linear->info);
1748+
1749+
if (info_linear->info.btf_id) {
1750+
struct btf_node *node;
1751+
1752+
node = perf_env__find_btf(dso->bpf_prog.env,
1753+
info_linear->info.btf_id);
1754+
if (node)
1755+
btf = btf__new((__u8 *)(node->data),
1756+
node->data_size);
1757+
}
1758+
1759+
disassemble_init_for_target(&info);
1760+
1761+
#ifdef DISASM_FOUR_ARGS_SIGNATURE
1762+
disassemble = disassembler(info.arch,
1763+
bfd_big_endian(bfdf),
1764+
info.mach,
1765+
bfdf);
1766+
#else
1767+
disassemble = disassembler(bfdf);
1768+
#endif
1769+
assert(disassemble);
1770+
1771+
fflush(s);
1772+
do {
1773+
const struct bpf_line_info *linfo = NULL;
1774+
struct disasm_line *dl;
1775+
size_t prev_buf_size;
1776+
const char *srcline;
1777+
u64 addr;
1778+
1779+
addr = pc + ((u64 *)(info_linear->info.jited_ksyms))[sub_id];
1780+
count = disassemble(pc, &info);
1781+
1782+
if (prog_linfo)
1783+
linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
1784+
addr, sub_id,
1785+
nr_skip);
1786+
1787+
if (linfo && btf) {
1788+
srcline = btf__name_by_offset(btf, linfo->line_off);
1789+
nr_skip++;
1790+
} else
1791+
srcline = NULL;
1792+
1793+
fprintf(s, "\n");
1794+
prev_buf_size = buf_size;
1795+
fflush(s);
1796+
1797+
if (!opts->hide_src_code && srcline) {
1798+
args->offset = -1;
1799+
args->line = strdup(srcline);
1800+
args->line_nr = 0;
1801+
args->ms.sym = sym;
1802+
dl = disasm_line__new(args);
1803+
if (dl) {
1804+
annotation_line__add(&dl->al,
1805+
&notes->src->source);
1806+
}
1807+
}
1808+
1809+
args->offset = pc;
1810+
args->line = buf + prev_buf_size;
1811+
args->line_nr = 0;
1812+
args->ms.sym = sym;
1813+
dl = disasm_line__new(args);
1814+
if (dl)
1815+
annotation_line__add(&dl->al, &notes->src->source);
1816+
1817+
pc += count;
1818+
} while (count > 0 && pc < len);
1819+
1820+
ret = 0;
1821+
out:
1822+
free(prog_linfo);
1823+
free(btf);
1824+
fclose(s);
1825+
bfd_close(bfdf);
1826+
return ret;
1827+
}
1828+
#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
1829+
static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused,
1830+
struct annotate_args *args __maybe_unused)
1831+
{
1832+
return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF;
1833+
}
1834+
#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT)
1835+
16771836
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
16781837
{
16791838
struct annotation_options *opts = args->options;
@@ -1701,7 +1860,9 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
17011860
pr_debug("annotating [%p] %30s : [%p] %30s\n",
17021861
dso, dso->long_name, sym, sym->name);
17031862

1704-
if (dso__is_kcore(dso)) {
1863+
if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) {
1864+
return symbol__disassemble_bpf(sym, args);
1865+
} else if (dso__is_kcore(dso)) {
17051866
kce.kcore_filename = symfs_filename;
17061867
kce.addr = map__rip_2objdump(map, sym->start);
17071868
kce.offs = sym->start;

tools/perf/util/annotate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ enum symbol_disassemble_errno {
369369
__SYMBOL_ANNOTATE_ERRNO__START = -10000,
370370

371371
SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX = __SYMBOL_ANNOTATE_ERRNO__START,
372+
SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF,
372373

373374
__SYMBOL_ANNOTATE_ERRNO__END,
374375
};

tools/perf/util/bpf-event.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static int machine__process_bpf_event_load(struct machine *machine,
4646
info_linear = info_node->info_linear;
4747

4848
for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) {
49-
u64 *addrs = (u64 *)(info_linear->info.jited_ksyms);
49+
u64 *addrs = (u64 *)(uintptr_t)(info_linear->info.jited_ksyms);
5050
u64 addr = addrs[i];
5151
struct map *map;
5252

0 commit comments

Comments
 (0)