Writing eBPF in Zig. Thanks to Zig's comptime and BTF, we can equip eBPF with strong type system both at comptime and runtime!
zbpf
Suppose you want to trace the kernel function path_listxattr, and here's its prototype:
static ssize_t path_listxattr(const char __user *pathname, char __user *list,
size_t size, unsigned int lookup_flags)
As you can see, it has 4 input parameters and return type is ssize_t
.
With ctx = bpf.Kprobe{.name = "path_listxattr"}.Ctx()
, you could retrieve
the input parameter with ctx.arg0()
, ctx.arg1()
, ctx.arg2()
and ctx.arg3()
respectively,
and return value with ctx.ret()
.
the type will be consistent with the above prototype. If you try to access a non-existing
parameter, e.g. ctx.arg4()
, you will get a compilation error.
This also applies to syscall
with bpf.Ksyscall
, tracepoint
with bpf.Tracepoint
and
fentry
with bpf.Fentry
.
When writing in C, you always have to check the error conditions
(the return value of the helper function, pointer validation, ...)
With zbpf
, you won't care about the these cases, we handle it under the hood for you,
just focus on the business logic.
The following are some examples:
bpf.Map
takes care BPF map's update
and delete
error.bpf.PerfEventArray
handles event output failure.bpf.RingBuffer
also handles space reservation.bpf.Xdp
validates the pointer for you.If some error happens, you could get all the information (file, line number, return value ...) you need to debug in the kernel trace buffer:
~> sudo bpftool prog tracelog
test-11717 [005] d..21 10990692.273976: bpf_trace_printk: error occur at src/bpf/map.zig:110 return -2
CONFIG_DEBUG_INFO_BTF=y
.libc
(this is not even necessary if you build with musl-libc).zig build -Dbpf=/path/to/your/bpf/prog.zig -Dmain=/path/to/your/main.zig
.For cross-compiling, you could specify the target with -Dtarget=<target>
,
the list of all supported targets could be retrieved by zig targets
.
Moreover, you could specify the target kernel with -Dvmlinux=/path/to/vmlinux
to extract BTF from it, otherwise, current kernel's BTF will be used.
That's all! The generated binary is located at ./zig-out/bin/zbpf
,
feel free to run it on your target machine.
Here's the Documentations generated by Zig's AutoDoc for you reference.
trace
is a tool built on top of zbpf
framework to trace kernel functions and syscalls.
It's heavily inspired by retsnoop.
One improvement I made (which is also what I feel when using retsnoop) is that trace
support
show parameters according its type (thanks to the Zig type system).
This is very helpful when debugging linux kernel.
For more details, you could check the implementation: BPF side
and Host side.
You could specify the kernel functions you want to trace with: zig build trace -Dkprobe=<kernel_function_name> -Dkprobe=...
And for system calls: zig build trace -Dsyscall=<syscall_name> -Dsyscall=...
.
Moreover, if you also want to capture the function's arguments, append the argument specifier, something like this:
-Dkprobe=<kernel_function_name>:arg0,arg1...
, it also supports access to the deeper field if the argument is a pointer to a struct:
-Dkprobe=<kernel_function_name>:arg0.field1.field0
.
You could even control how the argument is shown by using all the supported specifier by Zig's std.fmt
, something like this:
-Dkprobe=<kernel_function_name>:arg0.field1.field0/x
will show arg0.field1.field0
in hexadecimal notation.
Capturing call stack is also supported, append keyword stack
, for example -Dkprobe=<kernel_function_name>:arg0,stack
.
And here's a quick demo:
For each supported feature, we have the corresponding unit test.
You could find them under samples/
(BPF side) and src/tests
(Host side).
Build it with zig build test -Dtest=<name>
and run it with sudo zig-out/bin/test
.
Name | BPF side | Host side |
---|---|---|
exit | source | source |
panic | source | source |
trace_printk | source | source |
array | source | source |
hash | source | source |
perf_event | source | source |
ringbuf | source | source |
tracepoint | source | source |
iterator | source | source |
fentry | source | source |
kprobe | source | source |
kmulprobe | source | source |
xdp ping | source | source |
kfunc | source | source |
stack_trace | source | source |
Have fun!