https://github.com/tw4452852/zbpf
Writing eBPF in Zig
https://github.com/tw4452852/zbpf
bpf ebpf tracing zig
Last synced: 9 days ago
JSON representation
Writing eBPF in Zig
- Host: GitHub
- URL: https://github.com/tw4452852/zbpf
- Owner: tw4452852
- License: gpl-3.0
- Created: 2023-04-11T01:46:45.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-03-31T02:21:05.000Z (22 days ago)
- Last Synced: 2025-03-31T03:24:40.466Z (22 days ago)
- Topics: bpf, ebpf, tracing, zig
- Language: Zig
- Homepage: https://tw4452852.github.io/zbpf/
- Size: 4.79 MB
- Stars: 152
- Watchers: 4
- Forks: 8
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-zig - tw4452852/zbpf
README
# zbpf
Writing eBPF in Zig. Thanks to Zig's comptime and BTF, we can equip eBPF with strong type system both at comptime and runtime!## Notable advantages when writing eBPF program with `zbpf`
### Different available methods based on the type of program's context
Suppose you want to trace the kernel function [path_listxattr](https://github.com/torvalds/linux/blob/7475e51b87969e01a6812eac713a1c8310372e8a/fs/xattr.c#L856-L857),
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`.### No more tedious error handling
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
```## How to use
## Prerequisite
- Make sure the linux kernel is built with `CONFIG_DEBUG_INFO_BTF=y`.
## Build
- Download the [lastest Zig](https://ziglang.org/download/).
- Clone this repostory.
- Build with `zig build zbpf -Dbpf=/path/to/your/bpf/prog.zig -Dmain=/path/to/your/main.zig`.For cross-compiling, you could specify the target with `-Dtarget=`,
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](https://tw4452852.github.io/zbpf)
for you reference.## Tools/trace
`trace` is a tool built on top of `zbpf` framework to trace kernel functions, syscalls and userspace functions.
It's heavily inspired by [retsnoop](https://github.com/anakryiko/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 and userspace program.
For more details, you could check the implementation: [BPF side](https://github.com/tw4452852/zbpf/blob/main/src/tools/trace/trace.bpf.zig)
and [Host side](https://github.com/tw4452852/zbpf/blob/main/src/tools/trace/trace.zig).You could specify the kernel functions you want to trace with: `zig build trace -Dkprobe= -Dkprobe=...`
And for system calls: `zig build trace -Dsyscall= -Dsyscall=...`.
Or userspace function: `zig build trace -Duprobe=/path/to/binary[function_name]`.
Moreover, if you also want to capture the function's arguments, append the argument specifier, something like this:
`-Dkprobe=:arg0,arg1...`, it also supports access to the deeper field if the argument is a pointer to a struct:
`-Dkprobe=: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=:arg0.field1.field0/x` will show `arg0.field1.field0` in hexadecimal notation.
Capturing call stack is also supported, append keyword `stack`, for example `-Dkprobe=:arg0,stack`.And here's a quick demo:
[](https://asciinema.org/a/675689)
For reference:
- Syscalls: https://tw4452852.github.io/zbpf/#vmlinux.kernel_syscalls
- Kernel functions: https://tw4452852.github.io/zbpf/#vmlinux.kernel_funcsWant to use your local specific linux kernel? No problem, you could set up the documentation locally with:
```
zig build docs [-Dvmlinux=/path/your/vmlinux]
```
Then browse the generated page which is located at `./zig-out/docs/index.html`.
Search for `vmlinux.kernel_syscalls` and `vmlinux.kernel_funcs` for syscalls and kernel functions respectively.## Samples
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=` and run it with `sudo zig-out/bin/test`.Name | BPF side | Host side
--- | --- | ---
exit | [source](samples/exit.zig) | [source](src/tests/exit.zig)
panic | [source](samples/panic.zig) | [source](src/tests/panic.zig)
trace_printk | [source](samples/trace_printk.zig) | [source](src/tests/trace_printk.zig)
array | [source](samples/array.zig) | [source](src/tests/array.zig)
hash | [source](samples/hash.zig) | [source](src/tests/hash.zig)
perf_event | [source](samples/perf_event.zig) | [source](src/tests/perf_event.zig)
ringbuf | [source](samples/ringbuf.zig) | [source](src/tests/ringbuf.zig)
tracepoint | [source](samples/tracepoint.zig) | [source](src/tests/tracepoint.zig)
iterator | [source](samples/iterator.zig) | [source](src/tests/iterator.zig)
fentry | [source](samples/fentry.zig) | [source](src/tests/fentry.zig)
kprobe | [source](samples/kprobe.zig) | [source](src/tests/kprobe.zig)
kmulprobe | [source](samples/kmulprobe.zig) | [source](src/tests/kmulprobe.zig)
xdp ping | [source](samples/xdp_ping.zig) | [source](src/tests/xdp_ping.zig)
kfunc | [source](samples/kfunc.zig) | [source](src/tests/kfunc.zig)
stack_trace | [source](samples/stacktrace.zig) | [source](src/tests/stacktrace.zig)
uprobe | [source](samples/uprobe.zig) | [source](src/tests/uprobe.zig)**Have fun!**