Skip to content

Commit

Permalink
Python BPF disassembler and map layout parser
Browse files Browse the repository at this point in the history
Debugging eBPF programs can be tricky. The clang debug flags are not
supported in all the code-loading branches yet - e.g., only load_prog()
supports BPF_DEBUG or DEBUG_BPF_REGISTER_STATE, but compiling a kprobe
with BPF(...) doesn't.

This built-in disassembler can disassemble and print the BPF code in a
similar syntax than the kernel, whenever and the number of times the
user needs it. The BPF ISA is relatively stable so it doesn't require
much maintenance.

In addition, this parser is agnostic from the original source language
(C, B, Go, etc.), and doesn't depend on a particular compiler.

Example output for trace_pid_start() in biotop:

Disassemble of BPF program trace_pid_start:
   0: (79) r1 = *(u64*)(r1 +112)
   1: (7b) *(u64*)(r10 -8) = r1
   2: (b7) r1 = 0
   3: (63) *(u32*)(r10 -16) = r1
   4: (7b) *(u64*)(r10 -24) = r1
   5: (7b) *(u64*)(r10 -32) = r1
   6: (bf) r1 = r10
   7: (07) r1 += -28
   8: (b7) r2 = 16
   9: (85) call bpf_get_current_comm#16
  10: (67) r0 <<= 32
  11: (77) r0 >>= 32
  12: (55) if r0 != 0 goto +10 <23>
  13: (85) call bpf_get_current_pid_tgid#14
  14: (63) *(u32*)(r10 -32) = r0
  15: (18) r1 = <map at fd iovisor#3>
  17:      (64-bit upper word)
  17: (bf) r2 = r10
  18: (07) r2 += -8
  19: (bf) r3 = r10
  20: (07) r3 += -32
  21: (b7) r4 = 0
  22: (85) call bpf_map_update_elem#2
  23: (b7) r0 = 0
  24: (95) exit

The fields, types and memory layouts of maps can also be printed, which
is something that can be really helpful when dealing with unaligned
accesses or packed vs unpacked structures, and currently not supported
by clang.

For a map with key:

  struct {int a; short b; struct {int c:4; int d:8;};});

and value u64 the example output is:

Layout of BPF type HASH map test_map (ID 0):
  struct {
    [0 +4] int a;
    [4 +2] short b;
    [6 +2] char[2] __pad_2;
    [8 +4] struct {
      int c:4;
      int d:8;
    } __anon0;
  } key;
  unsigned long long value;

The [X +Y] is optional and denotes the offset and the size of each
field. Note that bit-fields and padding fields are shown.
  • Loading branch information
Oriol Arcas committed Feb 15, 2019
1 parent 51d62d3 commit 58d5515
Show file tree
Hide file tree
Showing 3 changed files with 477 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/python/bcc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from .syscall import syscall_name
from .utils import get_online_cpus, printb, _assert_is_bytes, ArgString
from .version import __version__
from .disassembler import disassemble_prog, decode_map

_probe_limit = 1000
_num_open_probes = 0
Expand Down Expand Up @@ -400,6 +401,15 @@ def dump_func(self, func_name):
size, = lib.bpf_function_size(self.module, func_name),
return ct.string_at(start, size)

def disassemble_prog(self, func_name):
bpfstr = self.dump_func(func_name)
disassemble_prog(func_name, bpfstr)

def decode_table(self, table_name, sizeinfo=False):
table_obj = self[table_name]
table_type = lib.bpf_table_type_id(self.module, table_obj.map_id)
decode_map(table_name, table_obj, table_type, sizeinfo=sizeinfo)

str2ctype = {
u"_Bool": ct.c_bool,
u"char": ct.c_char,
Expand Down
Loading

0 comments on commit 58d5515

Please sign in to comment.